diff --git a/Ide/Code/DocEdit.h b/Ide/Code/DocEdit.h --- a/Ide/Code/DocEdit.h +++ b/Ide/Code/DocEdit.h @@ -1,204 +1,204 @@ #ifndef _DOC_EDIT_H_ #define _DOC_EDIT_H_ #include "lgi/common/TextView3.h" #include "IdeDoc.h" #define IDM_FILE_COMMENT 100 #define IDM_FUNC_COMMENT 101 enum SourceType { SrcUnknown, SrcPlainText, SrcCpp, SrcPython, SrcXml, SrcHtml, }; class DocEdit; class DocEditStyling : public LThread, public LMutex { public: enum DocType { CodeHtml, CodePhp, CodeCss, CodeComment, CodePre, CodeJavascript }; protected: SourceType FileType; DocEdit *View = NULL; enum WordType { KNone, KLang, KType }; enum ThreadState { KWaiting, KStyling, KCancel, KExiting, }; struct Node { const int Captials = 26; const int Numbers = Captials + 26; const int Symbols = Numbers + 10; Node *Next[26 + 26 + 10 + 1]; WordType Type; int Map(char16 c) { if (c >= 'a' && c <= 'z') return c - 'a'; if (c >= 'A' && c <= 'Z') return c - 'A' + Captials; if (IsDigit(c)) return c - '0' + Numbers; if (c == '_') return Symbols; // LAssert(0); return -1; } Node() { ZeroObj(Next); Type = KNone; } ~Node() { for (int i=0; i Text; LString FileName; LUnrolledList Styles; LTextView3::LStyle Visible; LTextView3::LStyle Dirty; StylingParams(LTextView3 *view) : Dirty(LTextView3::STYLE_NONE) { View = view; } void StyleString(char16 *&s, char16 *e, LColour c, LUnrolledList *Out = NULL) { if (!Out) Out = &Styles; auto &st = Out->New().Construct(View, LTextView3::STYLE_IDE); auto pText = Text.AddressOf(); st.Start = s - pText; st.Font = View->GetFont(); char16 Delim = *s++; while (s < e && *s != Delim) { if (*s == '\\') s++; s++; } st.Len = (s - pText) - st.Start + 1; st.Fore = c; } }; // Styling data... LThreadEvent Event; ThreadState ParentState, WorkerState; // Lock before access: StylingParams Params; // EndLock // Full refresh triggers int RefreshSize; const char **RefreshEdges; void AddKeywords(const char **keys, bool IsType); private: // Thread only // Styling functions.. int Main(); void StyleCpp(StylingParams &p); void StylePython(StylingParams &p); void StyleDefault(StylingParams &p); void StyleXml(StylingParams &p); void StyleHtml(StylingParams &p); Node Root; LUnrolledList PrevStyle; // End Thread only public: DocEditStyling(DocEdit *view); LColour ColourFromType(DocType t); }; class DocEdit : public LTextView3, public LDocumentEnv, public DocEditStyling { IdeDoc *Doc; int CurLine; LPoint MsClick; void OnApplyStyles(); int CountRefreshEdges(size_t At, ssize_t Len); public: static int LeftMarginPx; DocEdit(IdeDoc *d, LFontType *f); ~DocEdit(); const char *GetClass() override { return "DocEdit"; } const char *Name() override { return LTextView3::Name(); } bool Name(const char *s) override { return LTextView3::Name(s); } bool SetPourEnabled(bool b); int GetTopPaddingPx(); void InvalidateLine(int Idx); char *TemplateMerge(const char *Template, const char *Name, List *Params); bool GetVisible(LStyle &s); void OnCreate() override; // Overrides bool AppendItems(LSubMenu *Menu, const char *Param, int Base) override; void DoGoto(std::function Callback) override; void OnPaintLeftMargin(LSurface *pDC, LRect &r, LColour &colour) override; void OnMouseClick(LMouse &m) override; bool OnKey(LKey &k) override; bool OnMenu(LDocView *View, int Id, void *Context) override; LMessage::Result OnEvent(LMessage *m) override; void SetCaret(size_t i, bool Select, bool ForceFullUpdate = false) override; void PourStyle(size_t Start, ssize_t EditSize) override; bool Pour(LRegion &r) override; bool Insert(size_t At, const char16 *Data, ssize_t Len) override; bool Delete(size_t At, ssize_t Len) override; - void OnPaint(LSurface *pDC); + void OnPaint(LSurface *pDC) override; }; #endif diff --git a/Ide/Code/DocEditStyling.cpp b/Ide/Code/DocEditStyling.cpp --- a/Ide/Code/DocEditStyling.cpp +++ b/Ide/Code/DocEditStyling.cpp @@ -1,1248 +1,1248 @@ #include "lgi/common/Lgi.h" #include "LgiIde.h" #include "DocEdit.h" #define COMP_STYLE 1 #define LOG_STYLE 0 #define PROFILE_STYLE 0 #if PROFILE_STYLE #define PROF(str) Prof.Add(str) #else #define PROF(str) #endif #define ColourComment LColour(0, 140, 0) #define ColourHashDef LColour(0, 0, 222) #define ColourLiteral LColour(192, 0, 0) #define ColourKeyword LColour::Black #define ColourType LColour(0, 0, 222) #define ColourCode LColour(140, 140, 180) #define ColourHtml LColour(80, 80, 255) #define ColourPre LColour(150, 110, 110) #define ColourStyle LColour(110, 110, 150) #define IsSymbolChar(ch) (IsAlpha(ch) || (ch) == '_') #define DetectKeyword() \ Node *n = &Root; \ char16 *e = s; \ do \ { \ int idx = n->Map(*e); \ if (idx < 0) \ break; \ n = n->Next[idx]; \ e++; \ } \ while (n); struct LanguageParams { const char **Keywords; const char **Types; const char **Edges; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CPP const char *DefaultKeywords[] = {"if", "elseif", "endif", "else", "ifeq", "ifdef", "ifndef", "ifneq", "include", NULL}; const char *CppKeywords[] = {"extern", "class", "struct", "static", "default", "case", "break", "switch", "new", "delete", "sizeof", "return", "enum", "else", "if", "for", "while", "do", "continue", "public", "private", "virtual", "protected", "friend", "union", "template", "typedef", "dynamic_cast", "inline", NULL}; const char *CppTypes[] = { "int", "char", "short", "long", "signed", "unsigned", "double", "float", "bool", "const", "void", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "char16", "wchar_t", "LArray", "GHashTbl", "List", "LString", "LAutoString", "LAutoWString", "LAutoPtr", "LHashTbl", NULL}; const char *CppEdges[] = { "/*", "*/", "\"", NULL }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Python const char *PythonKeywords[] = {"def", "try", "except", "import", "if", "for", "elif", "else", "class", NULL}; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // XML /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HTML const char *HtmlEdges[] = { "", "/*", "*/", "", "", "\"", "\'", NULL }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LanguageParams LangParam[] = { // Unknown {NULL, NULL, NULL}, // Plain text {DefaultKeywords, NULL, NULL}, // C/C++ {CppKeywords, CppTypes, CppEdges}, // Python {PythonKeywords, NULL, NULL}, // Xml {NULL, NULL, NULL}, // Html/Php {NULL, NULL, HtmlEdges} }; DocEditStyling::DocEditStyling(DocEdit *view) : LThread("DocEditStyling.Thread"), LMutex("DocEditStyling.Lock"), View(view), Event("DocEditStyling.Event"), ParentState(KWaiting), WorkerState(KWaiting), Params(view) { } void DocEdit::OnApplyStyles() { #if PROFILE_STYLE LProfile Prof("OnApplyStyles"); #endif PROF("Lock"); LTextView3::LStyle Vis(STYLE_NONE); GetVisible(Vis); if (DocEditStyling::Lock(_FL)) { PROF("Insert"); if (Params.Styles.Length()) { Style.Swap(Params.Styles); #if LOG_STYLE LgiTrace("Swapped in %i styles.\n", (int)Style.Length()); #endif PROF("Inval"); if (Params.Dirty.Start >= 0) { #if LOG_STYLE LgiTrace("Visible rgn: %i + %i = %i\n", Vis.Start, Vis.Len, Vis.End()); LgiTrace("Dirty rgn: %i + %i = %i\n", Params.Dirty.Start, Params.Dirty.Len, Params.Dirty.End()); #endif ssize_t CurLine = -1, DirtyStartLine = -1, DirtyEndLine = -1; GetTextLine(Cursor, &CurLine); LTextLine *Start = GetTextLine(Params.Dirty.Start, &DirtyStartLine); LTextLine *End = GetTextLine(MIN(Size, Params.Dirty.End()), &DirtyEndLine); if (CurLine >= 0 && DirtyStartLine >= 0 && DirtyEndLine >= 0) { #if LOG_STYLE LgiTrace("Dirty lines %i, %i, %i\n", CurLine, DirtyStartLine, DirtyEndLine); #endif if (DirtyStartLine != CurLine || DirtyEndLine != CurLine) { LRect c = GetClient(); LRect r(c.x1, Start->r.Valid() ? DocToScreen(Start->r).y1 : c.y1, c.x2, Params.Dirty.End() >= Vis.End() ? c.y2 : DocToScreen(End->r).y2); #if LOG_STYLE LgiTrace("Cli: %s, Start rgn: %s, End rgn: %s, Update: %s\n", c.GetStr(), Start->r.GetStr(), End->r.GetStr(), r.GetStr()); #endif Invalidate(&r); } } else { #if LOG_STYLE LgiTrace("No Change: %i, %i, %i\n", CurLine, DirtyStartLine, DirtyEndLine); #endif } } else { #if LOG_STYLE LgiTrace("Invalidate everything\n"); #endif Invalidate(); } } PROF("Unlock"); DocEditStyling::Unlock(); } } int DocEditStyling::Main() { LThreadEvent::WaitStatus s; while (ParentState != KExiting && (s = Event.Wait()) == LThreadEvent::WaitSignaled) { StylingParams p(View); if (Lock(_FL)) { Params.Dirty.Empty(); Params.Styles.Empty(); p = Params; Unlock(); } WorkerState = KStyling; #if LOG_STYLE LgiTrace("DocEdit.Worker starting style...\n"); #endif switch (FileType) { case SrcCpp: StyleCpp(p); break; case SrcPython: StylePython(p); break; case SrcXml: StyleXml(p); break; case SrcHtml: StyleHtml(p); break; default: StyleDefault(p); break; } if (LMutex::Lock(_FL)) { Params.Dirty = p.Dirty; Params.Styles.Swap(p.Styles); LMutex::Unlock(); } WorkerState = KWaiting; if (ParentState != KCancel && ParentState != KExiting) { #if LOG_STYLE LgiTrace("DocEdit.Worker finished style... Items=%i ParentState=%i\n", (int)p.Styles.Length(), ParentState); #endif - auto r = View->PostEvent(M_STYLING_DONE); + View->PostEvent(M_STYLING_DONE); } } return 0; } void DocEditStyling::StyleCpp(StylingParams &p) { #if PROFILE_STYLE LProfile Prof("DocEdit::StyleCpp"); #endif if (!p.Text.Length()) return; char16 *Text = p.Text.AddressOf(); char16 *s = Text; char16 *e = s + p.Text.Length(); PROF("Scan"); LUnrolledList Out; for (; ParentState != KCancel && s < e; s++) { switch (*s) { case '\"': case '\'': p.StyleString(s, e, ColourLiteral, &Out); break; case '#': { // Check that the '#' is the first non-whitespace on the line bool IsWhite = true; for (char16 *w = s - 1; w >= Text && *w != '\n'; w--) { if (!IsWhiteSpace(*w)) { IsWhite = false; break; } } if (IsWhite) { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); char LastNonWhite = 0; while (s < e) { if ( // Break at end of line (*s == '\n' && LastNonWhite != '\\') || // Or the start of a comment (*s == '/' && s[1] == '/') || (*s == '/' && s[1] == '*') ) break; if (!IsWhiteSpace(*s)) LastNonWhite = *s; s++; } st.Len = (s - Text) - st.Start; st.Fore = ColourHashDef; s--; } break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } case '/': { if (s[1] == '/') { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); while (s < e && *s != '\n') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; } else if (s[1] == '*') { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); s += 2; while (s < e && !(s[-2] == '*' && s[-1] == '/')) s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; } break; } default: { wchar_t Ch = ToLower(*s); if (Ch >= 'a' && Ch <= 'z') { DetectKeyword(); if (n && n->Type) { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; #ifdef MAC #warning "Weird mac bold issues fixme.") st.Font = View->GetFont(); #else st.Font = n->Type == KType ? View->GetFont() : View->GetBold(); #endif st.Len = e - s; st.Fore = n->Type == KType ? ColourType : ColourKeyword; } else { while ( IsAlpha(*e) || IsDigit(*e) || *e == '_') e++; } s = e - 1; } } } } #if COMP_STYLE PROF("Compare"); auto &Vis = p.Visible; if (Vis.Valid() && ParentState != KCancel && PrevStyle.Length()) { LArray Old, Cur; for (auto s : PrevStyle) { if (s.Overlap(Vis)) Old.Add(&s); else if (Old.Length()) break; } for (auto s : Out) { if (s.Overlap(Vis)) Cur.Add(&s); else if (Cur.Length()) break; } for (int o=0; oGetFont(); while (s < e && *s != '\n') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } default: { wchar_t Ch = ToLower(*s); if (Ch >= 'a' && Ch <= 'z') { DetectKeyword(); if (n && n->Type) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = n->Type == KType ? View->GetFont() : View->GetBold(); st.Len = e - s; st.Fore = n->Type == KType ? ColourType : ColourKeyword; } s = e - 1; } break; } } } } void DocEditStyling::StyleDefault(StylingParams &p) { char16 *Text = p.Text.AddressOf(); char16 *e = Text + p.Text.Length(); auto &Style = p.Styles; for (char16 *s = Text; s < e && ParentState < KCancel; s++) { switch (*s) { case '\"': case '\'': case '`': { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); bool Quoted = s > Text && s[-1] == '\\'; st.Start = s - Text - Quoted; st.Font = View->GetFont(); char16 Delim = *s++; while ( s < e && *s != Delim && !(Delim == '`' && *s == '\'') ) { if (*s == '\\') { if (!Quoted || s[1] != Delim) s++; } else if (*s == '\n') break; s++; } st.Len = (s - Text) - st.Start + 1; st.Fore = ColourLiteral; break; } case '#': { // Single line comment auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); while (s < e && *s != '\n') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text - ((s > Text && strchr("-+", s[-1])) ? 1 : 0); st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } if (*s == '%') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } default: { if (*s >= 'a' && *s <= 'z') { /* if (HasKeyword[*s - 'a']) { static char16 buf[64], *o = buf; char16 *e = s; while (IsSymbolChar(*e)) *o++ = *e++; *o = 0; KeyworkType type = Keywords.Find(buf); if (type != KNone) { LAutoPtr st(new LTextView3::LStyle(STYLE_IDE)); if (st) { st->View = this; st->Start = s - Text; st->Font = Bold; st->Len = e - s; st->Fore = ColourKeyword; InsertStyle(st); s = e - 1; } } } */ } break; } } } } void DocEditStyling::StyleXml(StylingParams &p) { char16 *Text = p.Text.AddressOf(); char16 *e = Text + p.Text.Length(); auto &Style = p.Styles; for (char16 *s = Text; s < e; s++) { switch (*s) { case '\"': case '\'': p.StyleString(s, e, ColourLiteral); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } case '<': { if (s[1] == '!' && s[2] == '-' && s[3] == '-') { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); s += 4; while ( s < e && ! ( s[-3] == '-' && s[-2] == '-' && s[-1] == '>' ) ) s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; } break; } default: { if (*s >= 'a' && *s <= 'z') { /* if (HasKeyword[*s - 'a']) { static char16 buf[64], *o = buf; char16 *e = s; while (IsSymbolChar(*e)) *o++ = *e++; *o = 0; KeyworkType type = Keywords.Find(buf); if (type != KNone) { LAutoPtr st(new LTextView3::LStyle(STYLE_IDE)); if (st) { st->View = this; st->Start = s - Text; st->Font = Bold; st->Len = e - s; st->Fore = ColourKeyword; InsertStyle(st); s = e - 1; } } } */ } break; } } } } LColour DocEditStyling::ColourFromType(DocType t) { switch (t) { default: return LColour::Black; case CodePhp: case CodeJavascript: return ColourCode; case CodeCss: return ColourStyle; case CodePre: return ColourPre; case CodeComment: return ColourComment; } } void DocEditStyling::StyleHtml(StylingParams &p) { char16 *Text = p.Text.AddressOf(); char16 *e = Text + p.Text.Length(); auto &Style = p.Styles; LString Ext = LGetExtension(p.FileName); DocType Type = CodeHtml; LTextView3::LStyle *Cur = NULL; #define START_CODE() \ if (Type != CodeHtml) \ { \ Cur = &Style.New().Construct(View, LTextView3::STYLE_IDE); \ Cur->Start = s - Text; \ Cur->Font = View->GetFont(); \ Cur->Fore = ColourFromType(Type); \ } #define END_CODE() \ if (Cur) \ { \ Cur->Len = (s - Text) - Cur->Start; \ if (Cur->Len <= 0) \ Style.Delete(Style.rbegin()); \ Cur = NULL; \ } char16 *s = Text; bool IsJavascript = Ext.Equals("js"); bool IsPHP = Ext.Equals("php"); bool IsCSS = Ext.Equals("css"); if (IsJavascript) { Type = CodeJavascript; START_CODE(); } else if (IsPHP) { Type = CodePhp; START_CODE(); } else if (IsCSS) { Type = CodeCss; START_CODE(); } for (s = Text; s < e; s++) { switch (*s) { case '`': { if (Type == CodeJavascript) { END_CODE(); p.StyleString(s, e, ColourLiteral); s++; START_CODE(); s--; } break; } case '\'': { if (s > Text && IsAlpha(s[-1])) break; // else fall through } case '\"': { if (Type != CodeComment) { END_CODE(); p.StyleString(s, e, ColourLiteral); s++; START_CODE(); s--; } break; } case '/': { if (Type == CodeJavascript || Type == CodePhp || Type == CodeCss) { if (s[1] == '/') { END_CODE(); char16 *nl = Strchr(s, '\n'); if (!nl) nl = s + Strlen(s); auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourComment; st.Len = nl - s; s = nl; START_CODE(); } else if (s[1] == '*') { END_CODE(); char16 *end_comment = Stristr(s, L"*/"); if (!end_comment) end_comment = s + Strlen(s); else end_comment += 2; auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourComment; st.Len = end_comment - s; s = end_comment; START_CODE(); } } break; } case '<': { #define IS_TAG(name) \ ((tmp = strlen(#name)) && \ len == tmp && \ !Strnicmp(tag, L ## #name, tmp)) #define SCAN_TAG() \ char16 *tag = s + 1; \ while (tag < e && strchr(WhiteSpace, *tag)) tag++; \ char16 *c = tag; \ while (c < e && (IsAlpha(*c) || strchr("!_/0123456789", *c))) c++; \ size_t len = c - tag, tmp; if (Type == CodeHtml) { if (s[1] == '?' && s[2] == 'p' && s[3] == 'h' && s[4] == 'p') { // Start PHP block Type = CodePhp; START_CODE(); s += 4; } else if ( s[1] == '!' && s[2] == '-' && s[3] == '-') { // Start comment Type = CodeComment; START_CODE(); s += 3; } else { // Html element SCAN_TAG(); bool start = false; if (IS_TAG(pre)) { Type = CodePre; while (*c && *c != '>') c++; if (*c) c++; start = true; } else if (IS_TAG(style)) { Type = CodeCss; while (*c && *c != '>') c++; if (*c) c++; start = true; } else if (IS_TAG(script)) { Type = CodeJavascript; while (c < e && *c != '>') c++; if (c < e) c++; start = true; } auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourHtml; st.Len = c - s; s = c - 1; if (start) { s++; START_CODE(); s--; } } } else if (Type == CodePre) { SCAN_TAG(); if (IS_TAG(/pre)) { END_CODE(); Type = CodeHtml; s--; } } else if (Type == CodeCss) { SCAN_TAG(); if (IS_TAG(/style)) { END_CODE(); Type = CodeHtml; s--; } } break; } case '>': { if (Type == CodeHtml) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourHtml; st.Len = 1; } else if (Type == CodeComment) { if (s - 2 >= Text && s[-1] == '-' && s[-2] == '-') { s++; END_CODE(); Type = CodeHtml; } } else if (Type == CodeJavascript) { // Check for if (!Strnicmp(s - 8, L"", 9)) { s -= 8; END_CODE(); auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourHtml; st.Len = 9; s += 9; Type = CodeHtml; } } break; } case '?': { if (Type == CodePhp && s[1] == '>') { Type = CodeHtml; s += 2; END_CODE(); s--; } break; } } } END_CODE(); } void DocEditStyling::AddKeywords(const char **keys, bool IsType) { for (const char **k = keys; *k; k++) { Node *n = &Root; for (const char *c = *k; *c && n; c++) { int idx = n->Map(*c); LAssert(idx >= 0); if (!n->Next[idx]) n->Next[idx] = new Node; n = n->Next[idx]; } if (n) n->Type = IsType ? KType : KLang; } } void DocEdit::PourStyle(size_t Start, ssize_t EditSize) { if (FileType == SrcUnknown) { char *Ext = LGetExtension(Doc->GetFileName()); if (!Ext) FileType = SrcPlainText; else if (!stricmp(Ext, "c") || !stricmp(Ext, "cpp") || !stricmp(Ext, "mm") || !stricmp(Ext, "cc") || !stricmp(Ext, "h") || !stricmp(Ext, "hpp") || !stricmp(Ext, "java") ) FileType = SrcCpp; else if (!stricmp(Ext, "py")) FileType = SrcPython; else if (!stricmp(Ext, "xml")) FileType = SrcXml; else if (!stricmp(Ext, "html") || !stricmp(Ext, "htm") || !stricmp(Ext, "php") || !stricmp(Ext, "js")) FileType = SrcHtml; else FileType = SrcPlainText; if (LangParam[FileType].Keywords) AddKeywords(LangParam[FileType].Keywords, false); if (LangParam[FileType].Types) AddKeywords(LangParam[FileType].Types, true); if (LangParam[FileType].Edges) { RefreshEdges = LangParam[FileType].Edges; RefreshSize = 0; for (const char **e = RefreshEdges; *e; e++) RefreshSize = MAX((int)strlen(*e), RefreshSize); } } // Get into the waiting mode (so the worker is not using 'StyleIn' data) if (WorkerState == KStyling) { #ifdef _DEBUG uint64 Start = LgiMicroTime(); #endif ParentState = KCancel; while (WorkerState != KWaiting) LSleep(1); #ifdef _DEBUG uint64 Tm = LgiMicroTime() - Start; if (Tm >= 10000) LgiTrace("DocEdit: Styling->Waiting time: %.1g ms\n", (double) Tm / 1000.0); #endif } // Create the StyleIn array from the current document // Lock the object and give the text to the worker #if LOG_STYLE LgiTrace("DocEdit starting style...\n"); #endif ParentState = KStyling; if (DocEditStyling::Lock(_FL)) { Params.PourStart = Start; Params.PourSize = EditSize; Params.Dirty.Empty(); Params.Text.Length(Size); Params.FileName = Doc->GetFileName(); GetVisible(Params.Visible); memcpy(Params.Text.AddressOf(), Text, sizeof(*Text) * Size); DocEditStyling::Unlock(); Event.Signal(); } } int DocEdit::CountRefreshEdges(size_t At, ssize_t Len) { if (!RefreshEdges) return 0; size_t s = MAX(0, At - (RefreshSize - 1)); bool t[256] = {0}; for (const char **Edge = RefreshEdges; *Edge; Edge++) { const char *e = *Edge; t[(int)e[0]] = true; } int Edges = 0; for (size_t i = s; i <= At; i++) { if (Text[i] < 256 && t[Text[i]]) { for (const char **Edge = RefreshEdges; *Edge; Edge++) { auto n = i; const char *e; for (e = *Edge; *e; e++) if (Text[n++] != *e) break; if (!*e) Edges++; } } } return Edges; -} \ No newline at end of file +} diff --git a/Ide/Code/LgiIde.cpp b/Ide/Code/LgiIde.cpp --- a/Ide/Code/LgiIde.cpp +++ b/Ide/Code/LgiIde.cpp @@ -1,4680 +1,4682 @@ #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Mdi.h" #include "lgi/common/Token.h" #include "lgi/common/XmlTree.h" #include "lgi/common/Panel.h" #include "lgi/common/Button.h" #include "lgi/common/TabView.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/Box.h" #include "lgi/common/TextLog.h" #include "lgi/common/Edit.h" #include "lgi/common/TableLayout.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Combo.h" #include "lgi/common/CheckBox.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Box.h" #include "lgi/common/SubProcess.h" #include "lgi/common/About.h" #include "lgi/common/Menu.h" #include "lgi/common/ToolBar.h" #include "lgi/common/FileSelect.h" #include "lgi/common/SubProcess.h" #include "LgiIde.h" #include "FtpThread.h" #include "FindSymbol.h" #include "Debugger.h" #include "ProjectNode.h" #define IDM_RECENT_FILE 1000 #define IDM_RECENT_PROJECT 1100 #define IDM_WINDOWS 1200 #define IDM_MAKEFILE_BASE 1300 #define USE_HAIKU_PULSE_HACK 0 #define OPT_ENTIRE_SOLUTION "SearchSolution" #define OPT_SPLIT_PX "SplitPos" #define OPT_OUTPUT_PX "OutputPx" #define OPT_FIX_RENAMED "FixRenamed" #define OPT_RENAMED_SYM "RenamedSym" #define IsSymbolChar(c) ( IsDigit(c) || IsAlpha(c) || strchr("-_", c) ) ////////////////////////////////////////////////////////////////////////////////////////// class FindInProject : public LDialog { AppWnd *App; LList *Lst; public: FindInProject(AppWnd *app) { Lst = NULL; App = app; if (LoadFromResource(IDC_FIND_PROJECT_FILE)) { MoveSameScreen(App); LViewI *v; if (GetViewById(IDC_TEXT, v)) v->Focus(true); if (!GetViewById(IDC_FILES, Lst)) return; RegisterHook(this, LKeyEvents, 0); } } bool OnViewKey(LView *v, LKey &k) { switch (k.vkey) { case LK_UP: case LK_DOWN: case LK_PAGEDOWN: case LK_PAGEUP: { return Lst->OnKey(k); break; } case LK_RETURN: { if (k.Down()) { LListItem *i = Lst->GetSelected(); if (i) { const char *Ref = i->GetText(0); App->GotoReference(Ref, 1, false); } EndModal(1); return true; } break; } case LK_ESCAPE: { if (k.Down()) { EndModal(0); return true; } break; } } return false; } void Search(const char *s) { IdeProject *p = App->RootProject(); if (!p || !s) return; LArray Matches, Nodes; List All; p->GetChildProjects(All); All.Insert(p); for (auto p: All) { p->GetAllNodes(Nodes); } FilterFiles(Matches, Nodes, s); Lst->Empty(); for (auto m: Matches) { LListItem *li = new LListItem; LString Fn = m->GetFileName(); #ifdef WINDOWS Fn = Fn.Replace("/","\\"); #else Fn = Fn.Replace("\\","/"); #endif m->GetProject()->CheckExists(Fn); li->SetText(Fn); Lst->Insert(li); } Lst->ResizeColumnsToContent(); } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_FILES: if (n.Type == LNotifyItemDoubleClick) { LListItem *i = Lst->GetSelected(); if (i) { App->GotoReference(i->GetText(0), 1, false); EndModal(1); } } break; case IDC_TEXT: if (n.Type != LNotifyReturnKey) Search(c->Name()); break; case IDCANCEL: EndModal(0); break; } return 0; } }; ////////////////////////////////////////////////////////////////////////////////////////// -char AppName[] = "LgiIde"; +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; LMdiParent *Mdi; LOptionsFile Options; LBox *HBox, *VBox; List Docs; List Projects; LImageList *Icons; LTree *Tree; IdeOutput *Output; bool Debugging; bool Running; bool Building; bool FixBuildWait = false; int RebuildWait = 0; LSubMenu *WindowsMenu; LSubMenu *CreateMakefileMenu; LAutoPtr FindSym; LArray SystemIncludePaths; LArray BreakPoints; // Debugging LDebugContext *DbgContext; // Cursor history tracking int HistoryLoc; LArray CursorHistory; bool InHistorySeek; void SeekHistory(int Direction) { if (CursorHistory.Length()) { int Loc = HistoryLoc + Direction; if (Loc >= 0 && Loc < CursorHistory.Length()) { HistoryLoc = Loc; FileLoc &Loc = CursorHistory[HistoryLoc]; App->GotoReference(Loc.File, Loc.Line, false, false); App->DumpHistory(); } } } // Find in files LAutoPtr FindParameters; LAutoPtr Finder; int AppHnd; // Mru LString::Array RecentFiles; LSubMenu *RecentFilesMenu = NULL; LString::Array RecentProjects; LSubMenu *RecentProjectsMenu = NULL; // Object AppWndPrivate(AppWnd *a) : Options(LOptionsFile::DesktopMode, AppName), AppHnd(LEventSinkMap::Dispatch.AddSink(a)) { FindSym.Reset(new FindSymbolSystem(AppHnd)); HistoryLoc = 0; InHistorySeek = false; WindowsMenu = 0; App = a; HBox = VBox = NULL; Tree = 0; Mdi = NULL; DbgContext = NULL; Output = 0; Debugging = false; Running = false; Building = false; Icons = LLoadImageList("icons.png", 16, 16); Options.SerializeFile(false); App->SerializeState(&Options, "WndPos", true); SerializeStringList("RecentFiles", &RecentFiles, false); SerializeStringList("RecentProjects", &RecentProjects, false); } ~AppWndPrivate() { FindSym.Reset(); Finder.Reset(); if (Output) Output->Save(); App->SerializeState(&Options, "WndPos", false); SerializeStringList("RecentFiles", &RecentFiles, true); SerializeStringList("RecentProjects", &RecentProjects, true); Options.SerializeFile(true); while (Docs.Length()) { auto len = Docs.Length(); delete Docs[0]; LAssert(Docs.Length() != len); // doc must delete itself... } auto root = App->RootProject(); if (root) { // printf("Deleting proj %s\n", root->GetFileName()); delete root; } LAssert(!Projects.Length()); DeleteObj(Icons); } bool FindSource(LAutoString &Full, char *File, char *Context) { if (!LIsRelativePath(File)) { Full.Reset(NewStr(File)); } char *ContextPath = 0; if (Context && !Full) { char *Dir = strrchr(Context, DIR_CHAR); for (auto p: Projects) { ContextPath = p->FindFullPath(Dir?Dir+1:Context); if (ContextPath) break; } if (ContextPath) { LTrimDir(ContextPath); char p[300]; LMakePath(p, sizeof(p), ContextPath, File); if (LFileExists(p)) { Full.Reset(NewStr(p)); } } else { LgiTrace("%s:%i - Context '%s' not found in project.\n", _FL, Context); } } if (!Full) { List::I Projs = Projects.begin(); for (IdeProject *p=*Projs; p; p=*++Projs) { LAutoString Base = p->GetBasePath(); if (Base) { char Path[MAX_PATH_LEN]; LMakePath(Path, sizeof(Path), Base, File); if (LFileExists(Path)) { Full.Reset(NewStr(Path)); break; } } } } if (!Full) { char *Dir = dirchar(File, true); for (auto p: Projects) { if (Full.Reset(p->FindFullPath(Dir?Dir+1:File))) break; } if (!Full) { if (LFileExists(File)) { Full.Reset(NewStr(File)); } } } return ValidStr(Full); } void ViewMsg(char *File, int Line, char *Context) { LAutoString Full; if (FindSource(Full, File, Context)) { App->GotoReference(Full, Line, false); } } #if 1 #define LOG_SEEK_MSG(...) printf(__VA_ARGS__) #else #define LOG_SEEK_MSG(...) #endif void GetContext(const char16 *Txt, ssize_t &i, char16 *&Context) { static char16 NMsg[] = L"In file included "; static char16 FromMsg[] = L"from "; auto NMsgLen = StrlenW(NMsg); if (Txt[i] != '\n') return; if (StrncmpW(Txt + i + 1, NMsg, NMsgLen)) return; i += NMsgLen + 1; while (Txt[i]) { // Skip whitespace while (Txt[i] && strchr(" \t\r\n", Txt[i])) i++; // Check for 'from' if (StrncmpW(FromMsg, Txt + i, 5)) break; i += 5; auto Start = Txt + i; // Skip to end of doc or line const char16 *Colon = 0; while (Txt[i] && Txt[i] != '\n') { if (Txt[i] == ':' && Txt[i+1] != '\n') { Colon = Txt + i; } i++; } if (Colon) { DeleteArray(Context); Context = NewStrW(Start, Colon-Start); } } } template bool IsTimeStamp(T *s, ssize_t i) { while (i > 0 && s[i-1] != '\n') i--; auto start = i; while (s[i] && (IsDigit(s[i]) || strchr(" :-", s[i]))) i++; LString txt(s + start, i - start); auto parts = txt.SplitDelimit(" :-"); return parts.Length() == 6 && parts[0].Length() == 4; } template bool IsContext(T *s, ssize_t i) { auto key = L"In file included"; auto end = i; while (i > 0 && s[i-1] != '\n') i--; if (Strnistr(s + i, key, end - i)) return true; return false; } #define PossibleLineSep(ch) \ ( (ch) == ':' || (ch) == '(' ) void SeekMsg(int Direction) { LString Comp; IdeProject *p = App->RootProject(); if (p) p ->GetSettings()->GetStr(ProjCompiler); // bool IsIAR = Comp.Equals("IAR"); if (!Output) return; int64 Current = Output->Value(); LTextView3 *o = Current < CountOf(Output->Txt) ? Output->Txt[Current] : 0; if (!o) return; auto Txt = o->NameW(); if (!Txt) return; ssize_t Cur = o->GetCaret(); char16 *Context = NULL; // Scan forward to the end of file for the next filename/line number separator. ssize_t i; for (i=Cur; Txt[i]; i++) { GetContext(Txt, i, Context); if ( PossibleLineSep(Txt[i]) && isdigit(Txt[i+1]) && !IsTimeStamp(Txt, i) && !IsContext(Txt, i) ) { break; } } // If not found then scan from the start of the file for the next filename/line number separator. if (!PossibleLineSep(Txt[i])) { for (i=0; i 0 && !strchr("\n>", Txt[Line-1])) { Line--; } // Store the filename LString File(Txt+Line, i-Line); if (!File) return; #if DIR_CHAR == '\\' File = File.Replace("/", "\\"); #else File = File.Replace("\\", "/"); #endif // Scan over the line number.. auto NumIndex = ++i; while (isdigit(Txt[NumIndex])) NumIndex++; // Store the line number LString NumStr(Txt + i, NumIndex - i); if (!NumStr) return; // Convert it to an integer auto LineNumber = (int)NumStr.Int(); o->SetCaret(Line, false); o->SetCaret(NumIndex + 1, true); LString Context8 = Context; ViewMsg(File, LineNumber, Context8); } void UpdateMenus() { static const char *None = "(none)"; if (!App->GetMenu()) return; // This happens in GTK during window destruction if (RecentFilesMenu) { RecentFilesMenu->Empty(); if (RecentFiles.Length() == 0) RecentFilesMenu->AppendItem(None, 0, false); else { int n=0; char *f; for (auto It = RecentFiles.begin(); (f = *It); f=*(++It)) { for (; f; f=*(++It)) { if (LIsUtf8(f)) RecentFilesMenu->AppendItem(f, IDM_RECENT_FILE+n++, true); else RecentFiles.Delete(It); } } } } if (RecentProjectsMenu) { RecentProjectsMenu->Empty(); if (RecentProjects.Length() == 0) RecentProjectsMenu->AppendItem(None, 0, false); else { int n=0; char *f; for (auto It = RecentProjects.begin(); (f = *It); f=*(++It)) { if (LIsUtf8(f)) RecentProjectsMenu->AppendItem(f, IDM_RECENT_PROJECT+n++, true); else RecentProjects.Delete(It); } } } if (WindowsMenu) { WindowsMenu->Empty(); Docs.Sort(DocSorter); int n=0; for (auto d: Docs) { const char *File = d->GetFileName(); if (!File) File = "(untitled)"; char *Dir = strrchr((char*)File, DIR_CHAR); WindowsMenu->AppendItem(Dir?Dir+1:File, IDM_WINDOWS+n++, true); } if (!Docs.Length()) { WindowsMenu->AppendItem(None, 0, false); } } } void Dump(LString::Array &a) { for (auto i: a) printf(" %s\n", i.Get()); } void OnFile(const char *File, bool IsProject = false) { if (!File) return; auto *Recent = IsProject ? &RecentProjects : &RecentFiles; for (auto &f: *Recent) { if (f && LFileCompare(f, File) == 0) { f = File; UpdateMenus(); return; } } Recent->AddAt(0, File); if (Recent->Length() > 10) Recent->Length(10); UpdateMenus(); } void RemoveRecent(const char *File) { if (File) { LString::Array *Recent[3] = { &RecentProjects, &RecentFiles, 0 }; for (int i=0; Recent[i]; i++) { auto &a = *Recent[i]; for (size_t n=0; nIsFile(File)) { return Doc; } } // LgiTrace("%s:%i - '%s' not found in %i docs.\n", _FL, File, Docs.Length()); return 0; } IdeProject *IsProjectOpen(const char *File) { if (File) { for (auto p: Projects) { if (p->GetFileName() && stricmp(p->GetFileName(), File) == 0) { return p; } } } return 0; } void SerializeStringList(const char *Opt, LString::Array *Lst, bool Write) { LVariant v; LString Sep = OptFileSeparator; if (Write) { if (Lst->Length() > 0) { auto s = Sep.Join(*Lst); Options.SetValue(Opt, v = s.Get()); // printf("Saving '%s' to %s\n", s.Get(), Opt); } else Options.DeleteValue(Opt); } else if (Options.GetValue(Opt, v)) { auto files = LString(v.Str()).Split(Sep); Lst->Length(0); for (auto f: files) if (f.Length() > 0) Lst->Add(f); // printf("Reading '%s' to %s, file.len=%i %s\n", v.Str(), Opt, (int)files.Length(), v.Str()); } // else printf("%s:%i - No option '%s' to read.\n", _FL, Opt); } }; AppWnd::AppWnd() { #ifdef __GTK_H__ LgiGetResObj(true, AppName); #endif LRect r(0, 0, 1000, 760); SetPos(r); MoveToCenter(); d = new AppWndPrivate(this); Name(AppName); SetQuitOnClose(true); #if WINNATIVE SetIcon((char*)MAKEINTRESOURCE(IDI_APP)); #else SetIcon("icon64.png"); #endif if (!Attach(0)) { LgiTrace("%s:%i - Attach failed.\n", _FL); return; } if ((Menu = new LMenu)) { Menu->Attach(this); bool Loaded = Menu->Load(this, "IDM_MENU"); LAssert(Loaded); if (Loaded) { Menu->SetPrefAndAboutItems(IDM_OPTIONS, IDM_ABOUT); d->RecentFilesMenu = Menu->FindSubMenu(IDM_RECENT_FILES); d->RecentProjectsMenu = Menu->FindSubMenu(IDM_RECENT_PROJECTS); d->WindowsMenu = Menu->FindSubMenu(IDM_WINDOW_LST); d->CreateMakefileMenu = Menu->FindSubMenu(IDM_CREATE_MAKEFILE); if (d->CreateMakefileMenu) { d->CreateMakefileMenu->Empty(); for (int i=0; PlatformNames[i]; i++) { d->CreateMakefileMenu->AppendItem(PlatformNames[i], IDM_MAKEFILE_BASE + i); } } else LgiTrace("%s:%i - FindSubMenu failed.\n", _FL); LMenuItem *Debug = GetMenu()->FindItem(IDM_DEBUG_MODE); if (Debug) Debug->Checked(true); else LgiTrace("%s:%i - FindSubMenu failed.\n", _FL); d->UpdateMenus(); } } LToolBar *Tools = NULL; if (GdcD->Y() > 1200) Tools = LgiLoadToolbar(this, "cmds-32px.png", 32, 32); else Tools = LgiLoadToolbar(this, "cmds-16px.png", 16, 16); if (Tools) { Tools->AppendButton("New", IDM_NEW, TBT_PUSH, true, CMD_NEW); Tools->AppendButton("Open", IDM_OPEN, TBT_PUSH, true, CMD_OPEN); Tools->AppendButton("Save", IDM_SAVE_ALL, TBT_PUSH, true, CMD_SAVE_ALL); Tools->AppendSeparator(); Tools->AppendButton("Cut", IDM_CUT, TBT_PUSH, true, CMD_CUT); Tools->AppendButton("Copy", IDM_COPY, TBT_PUSH, true, CMD_COPY); Tools->AppendButton("Paste", IDM_PASTE, TBT_PUSH, true, CMD_PASTE); Tools->AppendSeparator(); Tools->AppendButton("Compile", IDM_COMPILE, TBT_PUSH, true, CMD_COMPILE); Tools->AppendButton("Build", IDM_BUILD, TBT_PUSH, true, CMD_BUILD); Tools->AppendButton("Stop", IDM_STOP_BUILD, TBT_PUSH, true, CMD_STOP_BUILD); // Tools->AppendButton("Execute", IDM_EXECUTE, TBT_PUSH, true, CMD_EXECUTE); Tools->AppendSeparator(); Tools->AppendButton("Debug", IDM_START_DEBUG, TBT_PUSH, true, CMD_DEBUG); Tools->AppendButton("Pause", IDM_PAUSE_DEBUG, TBT_PUSH, true, CMD_PAUSE); Tools->AppendButton("Restart", IDM_RESTART_DEBUGGING, TBT_PUSH, true, CMD_RESTART); Tools->AppendButton("Kill", IDM_STOP_DEBUG, TBT_PUSH, true, CMD_KILL); Tools->AppendButton("Step Into", IDM_STEP_INTO, TBT_PUSH, true, CMD_STEP_INTO); Tools->AppendButton("Step Over", IDM_STEP_OVER, TBT_PUSH, true, CMD_STEP_OVER); Tools->AppendButton("Step Out", IDM_STEP_OUT, TBT_PUSH, true, CMD_STEP_OUT); Tools->AppendButton("Run To", IDM_RUN_TO, TBT_PUSH, true, CMD_RUN_TO); Tools->AppendSeparator(); Tools->AppendButton("Find In Files", IDM_FIND_IN_FILES, TBT_PUSH, true, CMD_FIND_IN_FILES); Tools->GetCss(true)->Padding("4px"); Tools->Attach(this); } else LgiTrace("%s:%i - No tools obj?", _FL); LVariant v = 270, OutPx = 250; d->Options.GetValue(OPT_SPLIT_PX, v); d->Options.GetValue(OPT_OUTPUT_PX, OutPx); AddView(d->VBox = new LBox); d->VBox->SetVertical(true); d->HBox = new LBox; d->VBox->AddView(d->HBox); d->VBox->AddView(d->Output = new IdeOutput(this)); d->HBox->AddView(d->Tree = new IdeTree); if (d->Tree) { d->Tree->SetImageList(d->Icons, false); d->Tree->Sunken(false); } d->HBox->AddView(d->Mdi = new LMdiParent); if (d->Mdi) { d->Mdi->HasButton(true); } d->HBox->Value(MAX(v.CastInt32(), 20)); LRect c = GetClient(); if (c.Y() > OutPx.CastInt32()) { auto Px = OutPx.CastInt32(); LCss::Len y(LCss::LenPx, (float)MAX(Px, 120)); d->Output->GetCss(true)->Height(y); } AttachChildren(); OnPosChange(); UpdateState(); Visible(true); DropTarget(true); SetPulse(1000); #ifdef LINUX LFinishXWindowsStartup(this); #endif #if USE_HAIKU_PULSE_HACK if (d->Output) d->Output->SetPulse(1000); #endif OnCommand(IDM_NEW, 0, NULL); } AppWnd::~AppWnd() { LAssert(IsClean()); + #ifdef HAIKU WaitThread(); + #endif if (d->HBox) { LVariant v = d->HBox->Value(); d->Options.SetValue(OPT_SPLIT_PX, v); } if (d->Output) { LVariant v = d->Output->Y(); d->Options.SetValue(OPT_OUTPUT_PX, v); } ShutdownFtpThread(); LAppInst->AppWnd = NULL; DeleteObj(d); } void AppWnd::OnPulse() { IdeDoc *Top = TopDoc(); if (Top) Top->OnPulse(); if (d->FixBuildWait) { d->FixBuildWait = false; if (OnFixBuildErrors() > 0) d->RebuildWait = 3; } else if (d->RebuildWait > 0) { if (--d->RebuildWait == 0) Build(); } 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(""); } } 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; } FindInFiles Dlg(this, d->FindParameters); LViewI *Focus = GetFocus(); if (Focus) { LTextView3 *Edit = dynamic_cast(Focus); if (Edit && Edit->HasSelection()) { LAutoString a(Edit->GetSelection()); Dlg.Params->Text = a; } } IdeProject *p = RootProject(); if (p) { LAutoString Base = p->GetBasePath(); if (Base) Dlg.Params->Dir = Base; } Dlg.DoModal([&](auto dlg, auto code) { if (p && Dlg.Params->Type == FifSearchSolution) { Dlg.Params->ProjectFiles.Length(0); List Projects; Projects.Insert(p); p->GetChildProjects(Projects); LArray Nodes; for (auto p: Projects) p->GetAllNodes(Nodes); for (unsigned i=0; iGetFullPath(); if (s) Dlg.Params->ProjectFiles.Add(s); } } LVariant var = d->FindParameters->Type == FifSearchSolution; GetOptions()->SetValue(OPT_ENTIRE_SOLUTION, var); d->Finder->Stop(); if (d->FindParameters->Text) d->Finder->PostEvent(FindInFilesThread::M_START_SEARCH, (LMessage::Param) new FindParams(d->FindParameters)); }); } break; } case IDM_FIND_SYMBOL: { IdeDoc *Doc = FocusDoc(); if (Doc) { Doc->GotoSearch(IDC_SYMBOL_SEARCH); } else { d->FindSym->OpenSearchDlg(this, [&](auto r) { if (r.File) GotoReference(r.File, r.Line, false); }); } break; } case IDM_GOTO_SYMBOL: { IdeDoc *Doc = FocusDoc(); if (Doc) { Doc->SearchSymbol(); } break; } case IDM_FIND_PROJECT_FILE: { IdeDoc *Doc = FocusDoc(); if (Doc) { Doc->SearchFile(); } else { auto d = new FindInProject(this); d->DoModal([](auto dlg, auto ctrlId){ delete dlg; }); } break; } case IDM_FIND_REFERENCES: { LViewI *f = LAppInst->GetFocus(); LDocView *doc = dynamic_cast(f); if (!doc) break; ssize_t c = doc->GetCaret(); if (c < 0) break; LString Txt = doc->Name(); char *s = Txt.Get() + c; char *e = s; while ( s > Txt.Get() && IsSymbolChar(s[-1])) s--; while (*e && IsSymbolChar(*e)) e++; if (e <= s) break; LString Word(s, e - s); if (!d->Finder) d->Finder.Reset(new FindInFilesThread(d->AppHnd)); if (!d->Finder) break; IdeProject *p = RootProject(); if (!p) break; List Projects; Projects.Insert(p); p->GetChildProjects(Projects); LArray Nodes; for (auto p: Projects) p->GetAllNodes(Nodes); LAutoPtr Params(new FindParams); Params->Type = FifSearchSolution; Params->MatchWord = true; Params->Text = Word; for (unsigned i = 0; i < Nodes.Length(); i++) { Params->ProjectFiles.New() = Nodes[i]->GetFullPath(); } d->Finder->Stop(); d->Finder->PostEvent(FindInFilesThread::M_START_SEARCH, (LMessage::Param) Params.Release()); break; } case IDM_PREV_LOCATION: { d->SeekHistory(-1); break; } case IDM_NEXT_LOCATION: { d->SeekHistory(1); break; } // // Project // case IDM_NEW_PROJECT: { CloseAll(); IdeProject *p; d->Projects.Insert(p = new IdeProject(this)); if (p) { p->CreateProject(); } break; } case IDM_NEW_PROJECT_TEMPLATE: { NewProjectFromTemplate(this); break; } case IDM_OPEN_PROJECT: { LFileSelect *s = new LFileSelect; s->Parent(this); s->Type("Projects", "*.xml"); s->Open([&](auto s, auto ok) { if (ok) { CloseAll(); OpenProject(s->Name(), NULL, Cmd == IDM_NEW_PROJECT); if (d->Tree) { d->Tree->Focus(true); } } delete s; }); break; } case IDM_IMPORT_DSP: { IdeProject *p = RootProject(); if (p) { LFileSelect *s = new LFileSelect; s->Parent(this); s->Type("Developer Studio Project", "*.dsp"); s->Open([&](auto s, auto ok) { if (ok) p->ImportDsp(s->Name()); delete s; }); } break; } case IDM_RUN: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (p) p->Execute(); }); break; } case IDM_VALGRIND: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (p) p->Execute(ExeValgrind); }); break; } case IDM_FIX_MISSING_FILES: { IdeProject *p = RootProject(); if (p) p->FixMissingFiles(); else LgiMsg(this, "No project loaded.", AppName); break; } case IDM_FIND_DUPE_SYM: { IdeProject *p = RootProject(); if (p) p->FindDuplicateSymbols(); else LgiMsg(this, "No project loaded.", AppName); break; } case IDM_RENAME_SYM: { if (!RenameDlg::Inst) new RenameDlg(this); break; } case IDM_START_DEBUG: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (!p) { LgiMsg(this, "No project loaded.", "Error"); return; } LString ErrMsg; if (d->DbgContext) { d->DbgContext->OnCommand(IDM_CONTINUE); } else if ((d->DbgContext = p->Execute(ExeDebug, &ErrMsg))) { d->DbgContext->DebuggerLog = d->Output->DebuggerLog; d->DbgContext->Watch = d->Output->Watch; d->DbgContext->Locals = d->Output->Locals; d->DbgContext->CallStack = d->Output->CallStack; d->DbgContext->Threads = d->Output->Threads; d->DbgContext->ObjectDump = d->Output->ObjectDump; d->DbgContext->Registers = d->Output->Registers; d->DbgContext->MemoryDump = d->Output->MemoryDump; d->DbgContext->OnCommand(IDM_START_DEBUG); d->Output->Value(AppWnd::DebugTab); d->Output->DebugEdit->Focus(true); } else if (ErrMsg) { LgiMsg(this, "Error: %s", AppName, MB_OK, ErrMsg.Get()); } }); break; } case IDM_TOGGLE_BREAKPOINT: { IdeDoc *Cur = GetCurrentDoc(); if (Cur) ToggleBreakpoint(Cur->GetFileName(), Cur->GetLine()); break; } case IDM_ATTACH_TO_PROCESS: case IDM_PAUSE_DEBUG: case IDM_RESTART_DEBUGGING: case IDM_RUN_TO: case IDM_STEP_INTO: case IDM_STEP_OVER: case IDM_STEP_OUT: { if (d->DbgContext) d->DbgContext->OnCommand(Cmd); break; } case IDM_STOP_DEBUG: { if (d->DbgContext && d->DbgContext->OnCommand(Cmd)) { DeleteObj(d->DbgContext); } break; } case IDM_BUILD: { Build(); break; } case IDM_STOP_BUILD: { IdeProject *p = RootProject(); if (p) p->StopBuild(); break; } case IDM_CLEAN: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (p) p->Clean(true, GetBuildMode()); }); break; } case IDM_NEXT_MSG: { d->SeekMsg(1); break; } case IDM_PREV_MSG: { d->SeekMsg(-1); break; } case IDM_DEBUG_MODE: { LMenuItem *Debug = GetMenu()->FindItem(IDM_DEBUG_MODE); LMenuItem *Release = GetMenu()->FindItem(IDM_RELEASE_MODE); if (Debug && Release) { Debug->Checked(true); Release->Checked(false); } break; } case IDM_RELEASE_MODE: { LMenuItem *Debug = GetMenu()->FindItem(IDM_DEBUG_MODE); LMenuItem *Release = GetMenu()->FindItem(IDM_RELEASE_MODE); if (Debug && Release) { Debug->Checked(false); Release->Checked(true); } break; } // // Other // case IDM_LOOKUP_SYMBOLS: { IdeDoc *Cur = GetCurrentDoc(); if (Cur) { // LookupSymbols(Cur->Read()); } break; } case IDM_DEPENDS: { IdeProject *p = RootProject(); if (p) { LString Exe = p->GetExecutable(GetCurrentPlatform()); if (LFileExists(Exe)) { Depends Dlg(this, Exe); } else { LgiMsg(this, "Couldn't find '%s'\n", AppName, MB_OK, Exe ? Exe.Get() : ""); } } break; } case IDM_SP_TO_TAB: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->ConvertWhiteSpace(true); break; } case IDM_TAB_TO_SP: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->ConvertWhiteSpace(false); break; } case IDM_ESCAPE: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->EscapeSelection(true); break; } case IDM_DESCAPE: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->EscapeSelection(false); break; } case IDM_SPLIT: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; LInput *i = new LInput(this, "", "Separator:", AppName); i->DoModal([&](auto dlg, auto ok) { if (ok) Doc->SplitSelection(i->GetStr()); delete i; }); break; } case IDM_JOIN: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; LInput *i = new LInput(this, "", "Separator:", AppName); i->DoModal([&](auto dlg, auto ok) { if (ok) Doc->JoinSelection(i->GetStr()); delete i; }); break; } case IDM_EOL_LF: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; Doc->SetCrLf(false); break; } case IDM_EOL_CRLF: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; Doc->SetCrLf(true); break; } case IDM_LOAD_MEMDUMP: { NewMemDumpViewer(this); break; } case IDM_SYS_CHAR_SUPPORT: { new SysCharSupport(this); break; } default: { int index = Cmd - IDM_RECENT_FILE; auto r = d->RecentFiles.IdxCheck(index)? d->RecentFiles[index] : LString(); if (r) { auto idx = Cmd - IDM_RECENT_FILE; if (idx > 0) { d->RecentFiles.DeleteAt(idx, true); d->RecentFiles.AddAt(0, r); } IdeDoc *f = d->IsFileOpen(r); if (f) f->Raise(); else OpenFile(r); } index = Cmd - IDM_RECENT_PROJECT; auto p = d->RecentProjects.IdxCheck(index) ? d->RecentProjects[index] : LString(); if (p) { auto idx = Cmd - IDM_RECENT_PROJECT; if (idx > 0) { d->RecentProjects.DeleteAt(idx, true); d->RecentProjects.AddAt(0, p); } CloseAll(); OpenProject(p, NULL, false); if (d->Tree) { d->Tree->Focus(true); } } IdeDoc *Doc = d->Docs[Cmd - IDM_WINDOWS]; if (Doc) { Doc->Raise(); } IdePlatform PlatIdx = (IdePlatform) (Cmd - IDM_MAKEFILE_BASE); const char *Platform = PlatIdx >= 0 && PlatIdx < PlatformMax ? PlatformNames[Cmd - IDM_MAKEFILE_BASE] : NULL; if (Platform) { IdeProject *p = RootProject(); if (p) { p->CreateMakefile(PlatIdx, false); } } break; } } return 0; } LTree *AppWnd::GetTree() { return d->Tree; } IdeDoc *AppWnd::TopDoc() { return d->Mdi ? dynamic_cast(d->Mdi->GetTop()) : NULL; } LTextView3 *AppWnd::FocusEdit() { return dynamic_cast(GetWindow()->GetFocus()); } IdeDoc *AppWnd::FocusDoc() { IdeDoc *Doc = TopDoc(); if (Doc) { if (Doc->HasFocus()) { return Doc; } else { LViewI *f = GetFocus(); LgiTrace("%s:%i - Edit doesn't have focus, f=%p %s doc.edit=%s\n", _FL, f, f ? f->GetClass() : 0, Doc->Name()); } } return 0; } void AppWnd::OnProjectDestroy(IdeProject *Proj) { if (d) { auto locked = Lock(_FL); // printf("OnProjectDestroy(%s) %i\n", Proj->GetFileName(), locked); d->Projects.Delete(Proj); if (locked) Unlock(); } else LAssert(!"No priv"); } void AppWnd::OnProjectChange() { LArray Views; if (d->Mdi->GetChildren(Views)) { for (unsigned i=0; i(Views[i]); if (Doc) Doc->OnProjectChange(); } } } void AppWnd::OnDocDestroy(IdeDoc *Doc) { if (d) { auto locked = Lock(_FL); d->Docs.Delete(Doc); d->UpdateMenus(); if (locked) Unlock(); } else { LAssert(!"OnDocDestroy no priv...\n"); } } BuildConfig AppWnd::GetBuildMode() { LMenuItem *Release = GetMenu()->FindItem(IDM_RELEASE_MODE); if (Release && Release->Checked()) { return BuildRelease; } return BuildDebug; } LList *AppWnd::GetFtpLog() { return d->Output->FtpLog; } LStream *AppWnd::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) { d->FindSym->Search(ResultsSinkHnd, Sym, AllPlatforms); } bool AppWnd::GetSystemIncludePaths(::LArray &Paths) { if (d->SystemIncludePaths.Length() == 0) { #if !defined(WINNATIVE) // echo | gcc -v -x c++ -E - LSubProcess sp1("echo"); LSubProcess sp2("gcc", "-v -x c++ -E -"); sp1.Connect(&sp2); sp1.Start(true, false); char Buf[256]; ssize_t r; LStringPipe p; while ((r = sp1.Read(Buf, sizeof(Buf))) > 0) { p.Write(Buf, r); } bool InIncludeList = false; while (p.Pop(Buf, sizeof(Buf))) { if (stristr(Buf, "#include")) { InIncludeList = true; } else if (stristr(Buf, "End of search")) { InIncludeList = false; } else if (InIncludeList) { LAutoString a(TrimStr(Buf)); d->SystemIncludePaths.New() = a; } } #else char p[MAX_PATH_LEN]; LGetSystemPath(LSP_USER_DOCUMENTS, p, sizeof(p)); LMakePath(p, sizeof(p), p, "Visual Studio 2008\\Settings\\CurrentSettings.xml"); if (LFileExists(p)) { LFile f; if (f.Open(p, O_READ)) { LXmlTree t; LXmlTag r; if (t.Read(&r, &f)) { LXmlTag *Opts = r.GetChildTag("ToolsOptions"); if (Opts) { LXmlTag *Projects = NULL; char *Name; for (auto c: Opts->Children) { if (c->IsTag("ToolsOptionsCategory") && (Name = c->GetAttr("Name")) && !stricmp(Name, "Projects")) { Projects = c; break; } } LXmlTag *VCDirectories = NULL; if (Projects) for (auto c: Projects->Children) { if (c->IsTag("ToolsOptionsSubCategory") && (Name = c->GetAttr("Name")) && !stricmp(Name, "VCDirectories")) { VCDirectories = c; break; } } if (VCDirectories) for (auto prop: VCDirectories->Children) { if (prop->IsTag("PropertyValue") && (Name = prop->GetAttr("Name")) && !stricmp(Name, "IncludeDirectories")) { char *Bar = strchr(prop->GetContent(), '|'); LToken t(Bar ? Bar + 1 : prop->GetContent(), ";"); for (int i=0; iSystemIncludePaths.New().Reset(NewStr(s)); } } } } } } } #endif } for (int i=0; iSystemIncludePaths.Length(); i++) { Paths.Add(NewStr(d->SystemIncludePaths[i])); } return true; } /* class SocketTest : public LWindow, public LThread { LTextLog *Log; public: SocketTest() : LThread("SocketTest") { Log = new LTextLog(100); SetPos(LRect(200, 200, 900, 800)); Attach(0); Visible(true); Log->Attach(this); Run(); } int Main() { LSocket s; s.SetTimeout(15000); Log->Print("Starting...\n"); auto r = s.Open("192.168.1.30", 7000); Log->Print("Open =%i\n", r); return 0; } }; */ class TestView : public LView { public: TestView() { LRect r(10, 10, 110, 210); SetPos(r); Sunken(true); printf("_BorderSize=%i\n", _BorderSize); } void OnPaint(LSurface *pdc) { auto c = GetClient(); pdc->Colour(LColour::Red); pdc->Line(c.x1, c.y1, c.x2, c.y2); pdc->Ellipse(c.x1+(c.X()/2)+10, c.y1+(c.Y()/2), c.X()/2, c.Y()/2); } }; #include "lgi/common/Tree.h" #include "lgi/common/List.h" class Test : public LWindow { public: Test() { LRect r(100, 100, 800, 700); SetPos(r); Name("Test"); SetQuitOnClose(true); if (Attach(0)) { // AddView(new TestView); // auto t = new LTree(10, 10, 10, 100, 200); auto t = new LTextLabel(10, 10, 10, 100, 35, "Text"); AddView(t); AttachChildren(); Visible(true); } } }; int LgiMain(OsAppArguments &AppArgs) { printf("LgiIde v%s\n", APP_VER); LApp a(AppArgs, "LgiIde"); if (a.IsOk()) { 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,346 @@ #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 char AppName[]; +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"; } 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); 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/Code/ProjectNode.cpp b/Ide/Code/ProjectNode.cpp --- a/Ide/Code/ProjectNode.cpp +++ b/Ide/Code/ProjectNode.cpp @@ -1,1577 +1,1577 @@ #include "lgi/common/Lgi.h" #include "lgi/common/Token.h" #include "lgi/common/Combo.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Menu.h" #include "lgi/common/FileSelect.h" #include "lgi/common/Charset.h" #include "LgiIde.h" #include "IdeProject.h" #include "ProjectNode.h" #include "AddFtpFile.h" #include "WebFldDlg.h" #define DEBUG_SHOW_NODE_COUNTS 0 const char *TypeNames[NodeMax] = { "None", "Folder", "Source", "Header", "Dependancy", "Resources", "Graphic", "Web", "Text", "MakeFile", }; ////////////////////////////////////////////////////////////////////////////////// class FileProps : public LDialog { public: NodeType Type; LString Charset; int Platforms; FileProps(LView *p, char *m, NodeType t, int plat, const char *charset) { Platforms = plat; Type = t; SetParent(p); if (LoadFromResource(IDD_FILE_PROPS)) { MoveToCenter(); SetCtrlName(IDC_MSG, m); LCombo *c; if (GetViewById(IDC_TYPE, c)) { for (int i=NodeNone; iInsert(TypeNames[i]); } c->Value(Type); } else LgiTrace("%s:%i - Failed to get Type combo.\n", _FL); if (GetViewById(IDC_CHARSET, c)) { if (!charset) charset = "utf-8"; for (LCharset *cs = LGetCsList(); cs->Charset; cs++) { c->Insert(cs->Charset); if (!Stricmp(charset, cs->Charset)) c->Value(c->Length()-1); } } for (int i=0; PlatformNames[i]; i++) { SetCtrlValue(PlatformCtrlId[i], ((1 << i) & Platforms) != 0); } OnPosChange(); // Make sure the dialog can display the whole table... LTableLayout *t; if (GetViewById(IDC_TABLE, t)) { LRect u = t->GetUsedArea(); u.Inset(-LTableLayout::CellSpacing, -LTableLayout::CellSpacing); LRect p = GetPos(); if (u.X() < p.X() || u.Y() < p.Y()) { p.SetSize(MAX(u.X(), p.X()), MAX(u.Y(), p.Y())); SetPos(p); } } } } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDOK: { int64 t = GetCtrlValue(IDC_TYPE); if (t >= NodeSrc) { Type = (NodeType) t; } Platforms = 0; for (int i=0; PlatformNames[i]; i++) { if (GetCtrlValue(PlatformCtrlId[i])) { Platforms |= 1 << i; } } Charset = GetCtrlName(IDC_CHARSET); if (!Stricmp(Charset.Get(), "utf-8")) Charset.Empty(); // fall thru } case IDCANCEL: case IDC_COPY_PATH: { EndModal(c->GetId()); break; } } return 0; } }; //////////////////////////////////////////////////////////////////// ProjectNode::ProjectNode(IdeProject *p) : IdeCommon(p) { Platforms = PLATFORM_ALL; NodeId = 0; Type = NodeNone; ChildCount = -1; IgnoreExpand = false; Dep = 0; Tag = NewStr("Node"); } ProjectNode::~ProjectNode() { NeedsPulse(false); if (sFile && Project) Project->OnNode(sFile, this, false); if (Dep) DeleteObj(Dep); } void ProjectNode::NeedsPulse(bool yes) { if (!Project) { LAssert(!"No project."); return; } auto app = Project->GetApp(); if (!app) { LAssert(!"No app."); return; } app->NeedsPulse.Delete(this); if (yes) app->NeedsPulse.Add(this); } void ProjectNode::OpenLocalCache(IdeDoc *&Doc) { if (sLocalCache) { Doc = Project->GetApp()->OpenFile(sLocalCache, this); if (Doc) { Doc->SetProject(Project); IdeProjectSettings *Settings = Project->GetSettings(); Doc->SetEditorParams(Settings->GetInt(ProjEditorIndentSize), Settings->GetInt(ProjEditorTabSize), Settings->GetInt(ProjEditorUseHardTabs), Settings->GetInt(ProjEditorShowWhiteSpace)); } else { LgiMsg(Tree, "Couldn't open file '%s'", AppName, MB_OK, sLocalCache.Get()); } } } int64 ProjectNode::CountNodes() { int64 n = 1; for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; n += c->CountNodes(); } return n; } void ProjectNode::OnCmdComplete(FtpCmd *Cmd) { if (!Cmd) return; if (Cmd->Status && Cmd->File) { if (Cmd->Cmd == FtpRead) { sLocalCache = Cmd->File; IdeDoc *Doc; OpenLocalCache(Doc); } else if (Cmd->Cmd == FtpWrite) { Cmd->View->OnSaveComplete(Cmd->Status); } } } int ProjectNode::GetPlatforms() { return Platforms; } const char *ProjectNode::GetLocalCache() { return sLocalCache; } bool ProjectNode::Load(LDocView *Edit, NodeView *Callback) { bool Status = false; if (IsWeb()) { if (sLocalCache) Status = Edit->Open(sLocalCache); else LAssert(!"No LocalCache"); } else { auto Full = GetFullPath(); Status = Edit->Open(Full, Charset); } return Status; } bool ProjectNode::Save(LDocView *Edit, NodeView *Callback) { bool Status = false; if (IsWeb()) { if (sLocalCache) { if (Edit->Save(sLocalCache)) { FtpThread *t = GetFtpThread(); if (t) { FtpCmd *c = new FtpCmd(FtpWrite, this); if (c) { c->View = Callback; c->Watch = Project->GetApp()->GetFtpLog(); c->Uri = NewStr(sFile); c->File = NewStr(sLocalCache); t->Post(c); } } } else LAssert(!"Editbox save failed."); } else LAssert(!"No LocalCache"); } else { auto f = GetFullPath(); if (Project) Project->CheckExists(f); Status = Edit->Save(f, Charset); if (Callback) Callback->OnSaveComplete(Status); } return Status; } int ProjectNode::GetId() { if (!NodeId && Project) NodeId = Project->AllocateId(); return NodeId; } bool ProjectNode::IsWeb() { char *Www = GetAttr(OPT_Www); char *Ftp = GetAttr(OPT_Ftp); if ( Www || Ftp || (sFile && strnicmp(sFile, "ftp://", 6) == 0) ) return true; return false; } bool ProjectNode::HasNode(ProjectNode *Node) { printf("Has %s %s %p\n", sFile.Get(), sName.Get(), Dep); if (this == Node) return true; if (Dep && Dep->HasNode(Node)) return true; for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; if (c->HasNode(Node)) return true; } return false; } void ProjectNode::AddNodes(LArray &Nodes) { Nodes.Add(this); for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; c->AddNodes(Nodes); } } bool ProjectNode::GetClean() { if (Dep) return Dep->GetClean(); return true; } void ProjectNode::SetClean() { auto CleanProj = [&]() { for (auto i: *this) { ProjectNode *p = dynamic_cast(i); if (p) p->SetClean(); } }; if (Dep) Dep->SetClean([&](bool ok) { if (ok) CleanProj(); }); else CleanProj(); } IdeProject *ProjectNode::GetDep() { return Dep; } IdeProject *ProjectNode::GetProject() { return Project; } bool ProjectNode::GetFormats(LDragFormats &Formats) { Formats.Supports(NODE_DROP_FORMAT); return true; } bool ProjectNode::GetData(LArray &Data) { for (unsigned i=0; iOnNode(sFile, this, false); if (Project->RelativePath(Rel, f, true)) sFile = Rel; else sFile = f; if (sFile) { auto FullPath = GetFullPath(); if (Project) Project->OnNode(FullPath, this, true); AutoDetectType(); } Project->SetDirty(); } char *ProjectNode::GetName() { return sName; } void ProjectNode::SetName(const char *f) { sName = f; Type = NodeDir; } NodeType ProjectNode::GetType() { return Type; } void ProjectNode::SetType(NodeType t) { Type = t; } int ProjectNode::GetImage(int f) { if (IsWeb()) { return sFile ? ICON_SOURCE : ICON_WEB; } switch (Type) { default: break; case NodeDir: return ICON_FOLDER; case NodeSrc: return ICON_SOURCE; case NodeDependancy: return ICON_DEPENDANCY; case NodeGraphic: return ICON_GRAPHIC; case NodeResources: return ICON_RESOURCE; } return ICON_HEADER; } const char *ProjectNode::GetText(int c) { if (sFile) { char *d = 0; if (IsWeb()) { d = sFile ? strrchr(sFile, '/') : 0; } else { #ifdef WIN32 char Other = '/'; #else char Other = '\\'; #endif char *s; while ((s = strchr(sFile, Other))) { *s = DIR_CHAR; } d = strrchr(sFile, DIR_CHAR); } if (d) return d + 1; else return sFile; } #if DEBUG_SHOW_NODE_COUNTS if (Type == NodeDir) { if (ChildCount < 0) ChildCount = CountNodes(); Label.Printf("%s ("LGI_PrintfInt64")", Name, ChildCount); return Label; } #endif return sName ? sName.Get() : Untitled; } void ProjectNode::OnExpand(bool b) { if (!IgnoreExpand) { Project->SetExpanded(GetId(), b); } } bool ProjectNode::Serialize(bool Write) { if (!Write && sFile) Project->OnNode(sFile, this, false); SerializeAttr("File", sFile); SerializeAttr("Name", sName); SerializeAttr("Charset", Charset); SerializeAttr("Type", (int&)Type); SerializeAttr("Platforms", (int&)Platforms); if (!Write && sFile) Project->OnNode(sFile, this, true); if (Type == NodeNone) { AutoDetectType(); } if (Type == NodeDir) { if (Write && !NodeId) GetId(); // Make sure we have an ID SerializeAttr("Id", NodeId); if (Write) Project->SetExpanded(GetId(), Expanded()); else { IgnoreExpand = true; Expanded(Project->GetExpanded(GetId())); IgnoreExpand = false; } } else if (Type == NodeDependancy) { SerializeAttr("LinkAgainst", LinkAgainst); if (!Write) { auto Full = GetFullPath(); Dep = Project->GetApp()->OpenProject(Full, Project, false, true); } } else { #if 0 if (!Write) { // Check that file exists. auto p = GetFullPath(); if (p) { if (LFileExists(p)) { if (!LIsRelativePath(File)) { // Try and fix up any non-relative paths that have crept in... char Rel[MAX_PATH_LEN]; if (Project->RelativePath(Rel, File)) { if (File) Project->OnNode(File, this, false); DeleteArray(File); File = NewStr(Rel); Project->SetDirty(); Project->OnNode(File, this, true); } } } else { // File doesn't exist... has it moved??? LAutoString Path = Project->GetBasePath(); if (Path) { // Find the file. char *d = strrchr(p, DIR_CHAR); LArray Files; LArray Ext; Ext.Add(d ? d + 1 : p.Get()); if (LRecursiveFileSearch(Path, &Ext, &Files)) { if (Files.Length()) { LStringPipe Buf; Buf.Print( "The file:\n" "\n" "\t%s\n" "\n" "doesn't exist. Is this the right file:\n" "\n" "\t%s", p.Get(), Files[0]); char *Msg = Buf.NewStr(); if (Msg) { LAlert a(Project->GetApp(), "Missing File", Msg, "Yes", "No", "Browse..."); switch (a.DoModal()) { case 1: // Yes { SetFileName(Files[0]); break; } case 2: // No { break; } case 3: // Browse { LFileSelect s; s.Parent(Project->GetApp()); s.Type("Code", SourcePatterns); if (s.Open()) { SetFileName(s.Name()); } break; } } DeleteArray(Msg); } } else { LStringPipe Buf; Buf.Print( "The file:\n" "\n" "\t%s\n" "\n" "doesn't exist.", p.Get()); char *Msg = Buf.NewStr(); if (Msg) { LAlert a(Project->GetApp(), "Missing File", Msg, "Skip", "Delete", "Browse..."); switch (a.DoModal()) { case 1: // Skip { break; } case 2: // Delete { Project->SetDirty(); delete this; return false; break; } case 3: // Browse { LFileSelect s; s.Parent(Project->GetApp()); s.Type("Code", SourcePatterns); if (s.Open()) { SetFileName(s.Name()); } break; } } DeleteArray(Msg); } } } } else { LgiTrace("%s:%i - Project::GetBasePath failed.\n", _FL); } } } } #endif } return true; } LString ProjectNode::GetFullPath() { LString FullPath; if (LIsRelativePath(sFile)) { // Relative path auto Path = Project->GetBasePath(); if (Path) { char p[MAX_PATH_LEN]; LMakePath(p, sizeof(p), Path, sFile); FullPath = p; } } else { // Absolute path FullPath = sFile; } return FullPath; } IdeDoc *ProjectNode::Open() { static bool Processing = false; IdeDoc *Doc = 0; if (Processing) { LAssert(!"Not recursive"); } if (!Processing) { Processing = true; if (IsWeb()) { if (sFile) { if (sLocalCache) { OpenLocalCache(Doc); } else { FtpThread *t = GetFtpThread(); if (t) { FtpCmd *c = new FtpCmd(FtpRead, this); if (c) { c->Watch = Project->GetApp()->GetFtpLog(); c->Uri = NewStr(sFile); t->Post(c); } } } } } else { switch (Type) { case NodeDir: { Expanded(true); break; } case NodeResources: { auto FullPath = GetFullPath(); if (FullPath) { char Exe[MAX_PATH_LEN]; LMakePath(Exe, sizeof(Exe), LGetExePath(), ".."); #if defined WIN32 LMakePath(Exe, sizeof(Exe), Exe, "Debug\\LgiRes.exe"); #elif defined LINUX LMakePath(Exe, sizeof(Exe), Exe, "LgiRes/lgires"); #endif if (LFileExists(Exe)) { LExecute(Exe, FullPath); } } else { LgiMsg(Tree, "No Path", AppName); } break; } case NodeGraphic: { auto FullPath = GetFullPath(); if (FullPath) { LExecute(FullPath); } else { LgiMsg(Tree, "No Path", AppName); } break; } default: { auto FullPath = GetFullPath(); if (Project->CheckExists(FullPath)) { Doc = Project->GetApp()->FindOpenFile(FullPath); if (!Doc) { Doc = Project->GetApp()->NewDocWnd(0, this); if (Doc) { if (Doc->OpenFile(FullPath)) { IdeProjectSettings *Settings = Project->GetSettings(); Doc->SetProject(Project); Doc->SetEditorParams(Settings->GetInt(ProjEditorIndentSize), Settings->GetInt(ProjEditorTabSize), Settings->GetInt(ProjEditorUseHardTabs), Settings->GetInt(ProjEditorShowWhiteSpace)); } else { LgiMsg(Tree, "Couldn't open file '%s'", AppName, MB_OK, FullPath.Get()); } } } else { Doc->Raise(); Doc->Focus(true); } } else { LgiMsg(Tree, "No Path", AppName); } break; } } } Processing = false; } return Doc; } void ProjectNode::Delete() { if (Select()) { LTreeItem *s = GetNext(); if (s || (s = GetParent())) s->Select(true); } if (nView) { nView->OnDelete(); nView = NULL; } Project->SetDirty(); LXmlTag::RemoveTag(); delete this; } bool ProjectNode::OnKey(LKey &k) { if (k.Down()) { if (k.vkey == LK_RETURN && k.IsChar) { Open(); return true; } else if (k.vkey == LK_DELETE) { Delete(); return true; } } return false; } void ProjectNode::OnPulse() { auto StartTs = LCurrentTime(); int TimeSlice = 700; //ms auto Start = strlen(ImportPath) + 1; while (ImportFiles.Length()) { LAutoString f(ImportFiles[0]); // Take ownership of the string ImportFiles.DeleteAt(0); if (ImportProg) (*ImportProg)++; LToken p(f + Start, DIR_STR); ProjectNode *Insert = this; // Find sub nodes, and drill into directory heirarchy, // creating the nodes if needed. for (int i=0; Insert && i(it); if (!c) break; if (c->GetType() == NodeDir && c->GetName() && stricmp(c->GetName(), p[i]) == 0) { Insert = c; Found = true; break; } } if (!Found) { // Create the node IdeCommon *Com = Insert->GetSubFolder(Project, p[i], true); Insert = dynamic_cast(Com); LAssert(Insert); } } // Insert the file into the tree... if (Insert) { ProjectNode *New = new ProjectNode(Project); if (New) { New->SetFileName(f); Insert->InsertTag(New); Insert->SortChildren(); Project->SetDirty(); } } if (LCurrentTime() - StartTs >= TimeSlice) break; // Yeild to the message loop } if (ImportFiles.Length() == 0) NeedsPulse(false); } void ProjectNode::OnMouseClick(LMouse &m) { LTreeItem::OnMouseClick(m); if (m.IsContextMenu()) { LSubMenu Sub; Select(true); if (Type == NodeDir) { if (IsWeb()) { Sub.AppendItem("Insert FTP File", IDM_INSERT_FTP, true); } else { Sub.AppendItem("Insert File", IDM_INSERT, true); } Sub.AppendItem("New Folder", IDM_NEW_FOLDER, true); Sub.AppendItem("Import Folder", IDM_IMPORT_FOLDER, true); Sub.AppendSeparator(); if (!IsWeb()) { Sub.AppendItem("Rename", IDM_RENAME, true); } } Sub.AppendItem("Remove", IDM_DELETE, true); Sub.AppendItem("Sort", IDM_SORT_CHILDREN, true); Sub.AppendSeparator(); Sub.AppendItem("Browse Folder", IDM_BROWSE_FOLDER, ValidStr(sFile)); Sub.AppendItem("Open Terminal", IDM_OPEN_TERM, ValidStr(sFile)); Sub.AppendItem("Properties", IDM_PROPERTIES, true); m.ToScreen(); LPoint c = _ScrollPos(); m.x -= c.x; m.y -= c.y; switch (Sub.Float(Tree, m.x, m.y)) { case IDM_INSERT_FTP: { AddFtpFile *Add = new AddFtpFile(Tree, GetAttr(OPT_Ftp)); Add->DoModal([&](auto dlg, auto code) { if (code) { for (int i=0; iUris.Length(); i++) { ProjectNode *New = new ProjectNode(Project); if (New) { New->SetFileName(Add->Uris[i]); InsertTag(New); SortChildren(); Project->SetDirty(); } } } delete Add; }); break; } case IDM_INSERT: { LFileSelect *s = new LFileSelect; s->Parent(Tree); s->Type("Source", SourcePatterns); s->Type("Makefiles", "*makefile"); s->Type("All Files", LGI_ALL_FILES); s->MultiSelect(true); LAutoString Dir = Project->GetBasePath(); if (Dir) { s->InitialDir(Dir); } - s->Open([&](auto s, auto ok) + s->Open([this](auto s, auto ok) { if (ok) { for (int i=0; iLength(); i++) { if (!Project->InProject(false, (*s)[i], false)) { ProjectNode *New = new ProjectNode(Project); if (New) { New->SetFileName((*s)[i]); InsertTag(New); SortChildren(); Project->SetDirty(); } } else { - LgiMsg(Tree, "'%s' is already in the project.\n", AppName, MB_OK, s[i]); + LgiMsg(Tree, "'%s' is already in the project.\n", AppName, MB_OK, (*s)[i]); } } } delete s; }); break; } case IDM_IMPORT_FOLDER: { LFileSelect *s = new LFileSelect; s->Parent(Tree); auto Dir = Project->GetBasePath(); if (Dir) s->InitialDir(Dir); s->OpenFolder([this](auto s, auto ok) { if (ok) { ImportPath = s->Name(); LArray Ext; LToken e(SourcePatterns, ";"); for (auto i: e) { Ext.Add(i); } if (LRecursiveFileSearch(ImportPath, &Ext, &ImportFiles)) { ImportProg.Reset(new LProgressDlg(GetTree(), 300)); ImportProg->SetDescription("Importing..."); ImportProg->SetRange(ImportFiles.Length()); NeedsPulse(true); } } delete s; }); break; } case IDM_SORT_CHILDREN: { SortChildren(); Project->SetDirty(); break; } case IDM_NEW_FOLDER: { LInput *Name = new LInput(Tree, "", "Name:", AppName); Name->DoModal([&](auto dlg, auto ok) { if (ok) GetSubFolder(Project, Name->GetStr(), true); delete Name; }); break; } case IDM_RENAME: { LInput *Name = new LInput(Tree, GetName(), "Name:", AppName); Name->DoModal([&](auto dlg, auto ok) { if (ok) { SetName(Name->GetStr()); Project->SetDirty(); Update(); } delete Name; }); break; } case IDM_DELETE: { Delete(); return; break; } case IDM_BROWSE_FOLDER: { auto Path = GetFullPath(); if (Path) LBrowseToFile(Path); break; } case IDM_OPEN_TERM: { if (Type == NodeDir) { } else { auto Path = GetFullPath(); if (Path) { LTrimDir(Path); #if defined LINUX char *Term = 0; char *Format = 0; switch (LGetWindowManager()) { case WM_Kde: Term = "konsole"; Format = "--workdir "; break; case WM_Gnome: Term = "gnome-terminal"; Format = "--working-directory="; break; } if (Term) { char s[256]; strcpy_s(s, sizeof(s), Format); char *o = s + strlen(s), *i = Path; *o++ = '\"'; while (*i) { if (*i == ' ') { *o++ = '\\'; } *o++ = *i++; } *o++ = '\"'; *o++ = 0; LExecute(Term, s); } #elif defined WIN32 LExecute("cmd", 0, Path); #endif } } break; } case IDM_PROPERTIES: { OnProperties(); break; } } } else if (m.Left()) { if (Type != NodeDir && m.Double()) { if ( ( IsWeb() || Type != NodeDir ) && ValidStr(sFile) ) { Open(); } else { LAssert(!"Unknown file type"); } } } } #if 0 #define LOG_FIND_FILE(...) printf(__VA_ARGS__) #else #define LOG_FIND_FILE(...) #endif ProjectNode *ProjectNode::FindFile(const char *In, char **Full) { LOG_FIND_FILE("%s:%i Node='%s' '%s'\n", _FL, sFile.Get(), sName.Get()); if (sFile) { bool Match = false; const char *AnyDir = "\\/"; if (IsWeb()) { Match = sFile ? stricmp(In, sFile) == 0 : false; } else if (strchr(In, DIR_CHAR)) { // Match partial or full path char Full[MAX_PATH_LEN] = ""; if (LIsRelativePath(sFile)) { auto Base = Project->GetBasePath(); if (Base) LMakePath(Full, sizeof(Full), Base, sFile); else LgiTrace("%s,%i - Couldn't get full IDoc path.\n", _FL); } else { strcpy_s(Full, sizeof(Full), sFile); } LOG_FIND_FILE("%s:%i Full='%s'\n", _FL, Full); LString MyPath(Full); LString::Array MyArr = MyPath.SplitDelimit(AnyDir); LString InPath(In); LString::Array InArr = InPath.SplitDelimit(AnyDir); auto Common = MIN(MyArr.Length(), InArr.Length()); Match = true; for (int i = 0; i < Common; i++) { char *a = MyArr[MyArr.Length()-(i+1)]; char *b = InArr[InArr.Length()-(i+1)]; auto res = _stricmp(a, b); LOG_FIND_FILE("%s:%i cmp '%s','%s'=%i\n", _FL, a, b, res); if (res) { Match = false; break; } } } else { // Match file name only auto p = sFile.SplitDelimit(AnyDir); Match = p.Last().Equals(In); LOG_FIND_FILE("%s:%i cmp '%s','%s'=%i\n", _FL, p.Last().Get(), In, Match); } if (Match) { if (Full) { if (sFile(0) == '.') { LAutoString Base = Project->GetBasePath(); if (Base) { char f[256]; LMakePath(f, sizeof(f), Base, sFile); *Full = NewStr(f); } } else { *Full = NewStr(sFile); } } return this; } } for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) { LAssert(!"Not a node?"); break; } ProjectNode *n = c->FindFile(In, Full); if (n) { return n; } } return 0; } struct DepDlg : public LDialog { ProjectNode *Node; DepDlg(ProjectNode *node) : Node(node) { auto proj = node->GetProject(); auto app = proj ? proj->GetApp() : NULL; if (!app) LAssert(!"Can't get app ptr."); else { SetParent(app); MoveSameScreen(app); } if (!LoadFromResource(IDD_DEP_NODE)) LAssert(!"Resource missing."); else { auto Path = node->GetFullPath(); if (Path) SetCtrlName(IDC_PATH, Path); SetCtrlValue(IDC_LINK_AGAINST, node->GetLinkAgainst()); } } int OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDOK: { Node->SetLinkAgainst(GetCtrlValue(IDC_LINK_AGAINST)); break; } } return LDialog::OnNotify(Ctrl, n); } }; void ProjectNode::OnProperties() { if (IsWeb()) { bool IsFolder = sFile.IsEmpty(); WebFldDlg *Dlg = new WebFldDlg(Tree, sName, IsFolder ? GetAttr(OPT_Ftp) : sFile.Get(), GetAttr(OPT_Www)); Dlg->DoModal([&](auto dlg, auto ok) { if (ok) { if (IsFolder) { SetName(Dlg->Name); SetAttr(OPT_Ftp, Dlg->Ftp); SetAttr(OPT_Www, Dlg->Www); } else { sFile = Dlg->Ftp; } Project->SetDirty(); Update(); } delete Dlg; }); } else if (Type == NodeDir) { } else if (Type == NodeDependancy) { DepDlg dlg(this); dlg.DoModal(NULL); } else { auto Path = GetFullPath(); if (Path) { char Size[32]; int64 FSize = LFileSize(Path); LFormatSize(Size, sizeof(Size), FSize); char Msg[512]; sprintf(Msg, "Source Code:\n\n\t%s\n\nSize: %s (%i bytes)", Path.Get(), Size, (int32)FSize); FileProps *Dlg = new FileProps(Tree, Msg, Type, Platforms, Charset); Dlg->DoModal([this, Dlg, Path](auto dlg, auto code) { switch (code) { case IDOK: { if (Type != Dlg->Type) { Type = Dlg->Type; Project->SetDirty(); } if (Platforms != Dlg->Platforms) { Platforms = Dlg->Platforms; Project->SetDirty(); } if (Charset != Dlg->Charset) { Charset = Dlg->Charset; Project->SetDirty(); } Update(); break; } case IDC_COPY_PATH: { LClipBoard Clip(Tree); Clip.Text(Path); break; } } delete Dlg; }); } } } diff --git a/Ide/Code/ProjectNode.h b/Ide/Code/ProjectNode.h --- a/Ide/Code/ProjectNode.h +++ b/Ide/Code/ProjectNode.h @@ -1,108 +1,108 @@ #ifndef _PROJECT_NODE_H_ #define _PROJECT_NODE_H_ #include "FtpThread.h" // Linked to 'TypeNames' global variable, update that too. enum NodeType { NodeNone, NodeDir, NodeSrc, NodeHeader, NodeDependancy, NodeResources, NodeGraphic, NodeWeb, NodeText, NodeMakeFile, NodeMax, // Always last }; extern int NodeSort(LTreeItem *a, LTreeItem *b, NativeInt d = 0); class ProjectNode : public IdeCommon, public LDragDropSource, public FtpCallback, public NodeSource { NodeType Type; int NodeId; int Platforms; LString sFile; LString sLocalCache; LString sName; LString Charset; bool IgnoreExpand; int64 ChildCount; LString Label; IdeProject *Dep; int LinkAgainst = true; // Import process LArray ImportFiles; LString ImportPath; LAutoPtr ImportProg; void OpenLocalCache(IdeDoc *&Doc); void OnCmdComplete(FtpCmd *Cmd) override; int64 CountNodes(); void NeedsPulse(bool yes); public: ProjectNode(IdeProject *p); ~ProjectNode(); // Actions IdeDoc *Open(); void Delete(); bool GetClean(); void SetClean(); void AddNodes(LArray &Nodes); bool HasNode(ProjectNode *Node); // Props int GetId(); int GetLinkAgainst() { return LinkAgainst; } void SetLinkAgainst(bool b) { LinkAgainst = b; } bool IsWeb() override; IdeProject *GetDep(); IdeProject *GetProject() override; const char *GetFileName() override; void SetFileName(const char *f); char *GetName(); void SetName(const char *f); NodeType GetType(); void SetType(NodeType t); int GetImage(int f) override; const char *GetText(int c) override; LString GetFullPath() override; ProjectNode *FindFile(const char *In, char **Full); /// \sa Some combination of PLATFORM_WIN32, PLATFORM_LINUX, PLATFORM_MAC, PLATFORM_HAIKU or PLATFORM_ALL int GetPlatforms() override; const char *GetLocalCache() override; void AutoDetectType(); const char *GetCharset() override { return Charset; } // Dnd bool GetFormats(LDragFormats &Formats) override; bool GetData(LArray &Data) override; // Ui events bool OnBeginDrag(LMouse &m) override; bool OnKey(LKey &k) override; void OnExpand(bool b) override; void OnMouseClick(LMouse &m) override; void OnProperties(); - void OnPulse(); + void OnPulse() override; // Serialization bool Load(LDocView *Edit, NodeView *Callback) override; bool Save(LDocView *Edit, NodeView *Callback) override; bool Serialize(bool Write) override; }; #endif diff --git a/Ide/MacCocoa/LgiIde.xcodeproj/project.pbxproj b/Ide/MacCocoa/LgiIde.xcodeproj/project.pbxproj --- a/Ide/MacCocoa/LgiIde.xcodeproj/project.pbxproj +++ b/Ide/MacCocoa/LgiIde.xcodeproj/project.pbxproj @@ -1,828 +1,815 @@ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXBuildFile section */ 34148AFC2349ECA200D5E495 /* LgiCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B20D1217968820079E3C9 /* LgiCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 341C7AC821A752DC0083C9CE /* About.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 341C7AC721A752DC0083C9CE /* About.cpp */; }; 345B20D42179688E0079E3C9 /* LgiCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 345B20D1217968820079E3C9 /* LgiCocoa.framework */; }; 345B2101217969700079E3C9 /* PythonParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20D52179696B0079E3C9 /* PythonParser.cpp */; }; 345B2102217969700079E3C9 /* MemDumpViewer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DA2179696B0079E3C9 /* MemDumpViewer.cpp */; }; 345B2103217969700079E3C9 /* FindInFiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DB2179696B0079E3C9 /* FindInFiles.cpp */; }; 345B2104217969700079E3C9 /* DocEditStyling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DC2179696B0079E3C9 /* DocEditStyling.cpp */; }; 345B2105217969700079E3C9 /* ProjectNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DD2179696B0079E3C9 /* ProjectNode.cpp */; }; 345B2106217969700079E3C9 /* SysCharSupport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20DE2179696B0079E3C9 /* SysCharSupport.cpp */; }; 345B2107217969700079E3C9 /* IdeDoc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E12179696C0079E3C9 /* IdeDoc.cpp */; }; 345B2108217969700079E3C9 /* SpaceTabConv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E22179696C0079E3C9 /* SpaceTabConv.cpp */; }; 345B2109217969700079E3C9 /* FindSymbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E32179696C0079E3C9 /* FindSymbol.cpp */; }; 345B210A217969700079E3C9 /* WebFldDlg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E42179696C0079E3C9 /* WebFldDlg.cpp */; }; 345B210B217969700079E3C9 /* SimpleCppParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E52179696C0079E3C9 /* SimpleCppParser.cpp */; }; 345B210C217969700079E3C9 /* DocEdit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20E72179696C0079E3C9 /* DocEdit.cpp */; }; 345B210D217969700079E3C9 /* AddFtpFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20EA2179696D0079E3C9 /* AddFtpFile.cpp */; }; - 345B210E217969700079E3C9 /* GHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20EE2179696D0079E3C9 /* GHistory.cpp */; }; + 345B210E217969700079E3C9 /* History.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20EE2179696D0079E3C9 /* History.cpp */; }; 345B210F217969700079E3C9 /* IdeProject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20EF2179696E0079E3C9 /* IdeProject.cpp */; }; 345B2110217969700079E3C9 /* levenshtein.c in Sources */ = {isa = PBXBuildFile; fileRef = 345B20F12179696E0079E3C9 /* levenshtein.c */; }; 345B2112217969700079E3C9 /* LgiUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20F52179696E0079E3C9 /* LgiUtils.cpp */; }; 345B2113217969700079E3C9 /* LgiIde.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20F62179696E0079E3C9 /* LgiIde.cpp */; }; 345B2114217969700079E3C9 /* IdeCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20F72179696F0079E3C9 /* IdeCommon.cpp */; }; 345B2115217969700079E3C9 /* FtpThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FB2179696F0079E3C9 /* FtpThread.cpp */; }; - 345B2116217969700079E3C9 /* GDebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FC2179696F0079E3C9 /* GDebugger.cpp */; }; - 345B2117217969700079E3C9 /* GDebugContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FD2179696F0079E3C9 /* GDebugContext.cpp */; }; + 345B2116217969700079E3C9 /* Debugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FC2179696F0079E3C9 /* Debugger.cpp */; }; + 345B2117217969700079E3C9 /* DebugContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FD2179696F0079E3C9 /* DebugContext.cpp */; }; 345B2118217969700079E3C9 /* IdeProjectSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FE217969700079E3C9 /* IdeProjectSettings.cpp */; }; 345B2119217969700079E3C9 /* MissingFiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 345B20FF217969700079E3C9 /* MissingFiles.cpp */; }; + 348C6BA329A814A4002E235F /* libpng16.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 348C6BA029A8149E002E235F /* libpng16.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34C4FD2A22FD7EB800C4BECB /* icons.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B211D217969D30079E3C9 /* icons.png */; }; 34C4FD2B22FD7EC100C4BECB /* LgiIde.lr8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B211C217969D30079E3C9 /* LgiIde.lr8 */; }; 34C4FD2C22FD7EC700C4BECB /* mac-icon.icns in CopyFiles */ = {isa = PBXBuildFile; fileRef = 345B211B217969D30079E3C9 /* mac-icon.icns */; }; 34C4FD3422FDB58B00C4BECB /* cmds-16px.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34C4FD3222FDB50D00C4BECB /* cmds-16px.png */; }; 34C4FD3522FDB58B00C4BECB /* cmds-32px.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34C4FD3322FDB50D00C4BECB /* cmds-32px.png */; }; 34C75D9323A0D89A006DE6D1 /* NewProjectFromTemplate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34C75D9223A0D89A006DE6D1 /* NewProjectFromTemplate.cpp */; }; 34D21CD821797815003D1EA6 /* ExeCheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CD721797815003D1EA6 /* ExeCheck.cpp */; }; 34D21CDA21797833003D1EA6 /* LexCpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CD921797833003D1EA6 /* LexCpp.cpp */; }; 34D21CDE21797856003D1EA6 /* OptionsFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CDB21797856003D1EA6 /* OptionsFile.cpp */; }; 34D21CE021797856003D1EA6 /* Mdi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CDD21797856003D1EA6 /* Mdi.cpp */; }; 34D21CE42179788B003D1EA6 /* Ftp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE12179788B003D1EA6 /* Ftp.cpp */; }; 34D21CE52179788B003D1EA6 /* OpenSSLSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE22179788B003D1EA6 /* OpenSSLSocket.cpp */; }; 34D21CE62179788B003D1EA6 /* Http.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE32179788B003D1EA6 /* Http.cpp */; }; 34D21CE8217978C4003D1EA6 /* LgiMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE7217978C4003D1EA6 /* LgiMain.cpp */; }; 34D21CEA217978DA003D1EA6 /* Base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CE9217978DA003D1EA6 /* Base64.cpp */; }; 34D21CEE21798240003D1EA6 /* Png.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D21CED21798240003D1EA6 /* Png.cpp */; }; - 34E44DE0279FBBEF00855194 /* libpng15.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34E44DCF279FB65600855194 /* libpng15.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 34F566AB237BD55F0027133A /* JavascriptParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34F566A0237BD55F0027133A /* JavascriptParser.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 345B20D0217968820079E3C9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */; proxyType = 2; remoteGlobalIDString = 3477C2681CBF020F0028B84B; remoteInfo = LgiCocoa; }; 345B20D2217968890079E3C9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */; proxyType = 1; remoteGlobalIDString = 3477C2671CBF020F0028B84B; remoteInfo = LgiCocoa; }; + 348C6B9F29A8149E002E235F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9D1B005B4C804977BD5DC316; + remoteInfo = libpng16; + }; + 348C6BA129A8149E002E235F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 5BBB106F0CF040E99654EA52; + remoteInfo = libpng16_static; + }; 34E44DCC279FB65600855194 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; proxyType = 2; remoteGlobalIDString = 228A010349D347BEADC408F8; remoteInfo = example; }; - 34E44DCE279FB65600855194 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = A86AA39F3CD14012BB654B02; - remoteInfo = libpng15; - }; - 34E44DD0279FB65600855194 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 1286F870F6A24F77B02F0866; - remoteInfo = libpng15_static; - }; 34E44DD2279FB65600855194 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; proxyType = 2; remoteGlobalIDString = 2BAFBD9C0B06410A93146583; remoteInfo = minigzip; }; 34E44DD4279FB65600855194 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; proxyType = 2; remoteGlobalIDString = 5E165C6692104427B1760FAC; remoteInfo = pngtest; }; 34E44DD6279FB65600855194 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; proxyType = 2; remoteGlobalIDString = 85787DCB21984E60AFB434AC; remoteInfo = zlib; }; 34E44DD8279FB65600855194 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; proxyType = 2; remoteGlobalIDString = 308342A9F1B047B0917875BB; remoteInfo = zlib_static; }; - 34E44DDB279FB72800855194 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 6E0E6C089FA643D996341259; - remoteInfo = libpng15; - }; 34E44DEE279FBD2700855194 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; proxyType = 1; - remoteGlobalIDString = 5DB72C35B3F241A8BBD29DC8; + remoteGlobalIDString = D84CFE826B9D4F5BA52AE2A5; remoteInfo = zlib; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 34148AF12349EC9500D5E495 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 34E44DE0279FBBEF00855194 /* libpng15.dylib in CopyFiles */, + 348C6BA329A814A4002E235F /* libpng16.dylib in CopyFiles */, 34148AFC2349ECA200D5E495 /* LgiCocoa.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; 34CAF47E22D3771500BE529A /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 7; files = ( 34C4FD3422FDB58B00C4BECB /* cmds-16px.png in CopyFiles */, 34C4FD3522FDB58B00C4BECB /* cmds-32px.png in CopyFiles */, 34C4FD2C22FD7EC700C4BECB /* mac-icon.icns in CopyFiles */, 34C4FD2B22FD7EC100C4BECB /* LgiIde.lr8 in CopyFiles */, 34C4FD2A22FD7EB800C4BECB /* icons.png in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 341C7AC721A752DC0083C9CE /* About.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = About.cpp; path = ../../src/common/Lgi/About.cpp; sourceTree = ""; }; 345B206C217965E80079E3C9 /* LgiIde.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LgiIde.app; sourceTree = BUILT_PRODUCTS_DIR; }; 345B2077217965EA0079E3C9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 345B207A217965EA0079E3C9 /* LgiIde.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LgiIde.entitlements; sourceTree = ""; }; 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LgiCocoa.xcodeproj; path = ../../src/mac/cocoa/LgiCocoa.xcodeproj; sourceTree = ""; }; 345B20D52179696B0079E3C9 /* PythonParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PythonParser.cpp; path = ../Code/PythonParser.cpp; sourceTree = ""; }; 345B20D62179696B0079E3C9 /* SpaceTabConv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpaceTabConv.h; path = ../Code/SpaceTabConv.h; sourceTree = ""; }; 345B20D72179696B0079E3C9 /* IdeDocPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeDocPrivate.h; path = ../Code/IdeDocPrivate.h; sourceTree = ""; }; 345B20D82179696B0079E3C9 /* FtpThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FtpThread.h; path = ../Code/FtpThread.h; sourceTree = ""; }; 345B20D92179696B0079E3C9 /* WebFldDlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebFldDlg.h; path = ../Code/WebFldDlg.h; sourceTree = ""; }; 345B20DA2179696B0079E3C9 /* MemDumpViewer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MemDumpViewer.cpp; path = ../Code/MemDumpViewer.cpp; sourceTree = ""; }; 345B20DB2179696B0079E3C9 /* FindInFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FindInFiles.cpp; path = ../Code/FindInFiles.cpp; sourceTree = ""; }; 345B20DC2179696B0079E3C9 /* DocEditStyling.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DocEditStyling.cpp; path = ../Code/DocEditStyling.cpp; sourceTree = ""; }; 345B20DD2179696B0079E3C9 /* ProjectNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProjectNode.cpp; path = ../Code/ProjectNode.cpp; sourceTree = ""; }; 345B20DE2179696B0079E3C9 /* SysCharSupport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SysCharSupport.cpp; path = ../Code/SysCharSupport.cpp; sourceTree = ""; }; 345B20DF2179696C0079E3C9 /* IdeProjectSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeProjectSettings.h; path = ../Code/IdeProjectSettings.h; sourceTree = ""; }; 345B20E02179696C0079E3C9 /* FindSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FindSymbol.h; path = ../Code/FindSymbol.h; sourceTree = ""; }; 345B20E12179696C0079E3C9 /* IdeDoc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeDoc.cpp; path = ../Code/IdeDoc.cpp; sourceTree = ""; }; 345B20E22179696C0079E3C9 /* SpaceTabConv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SpaceTabConv.cpp; path = ../Code/SpaceTabConv.cpp; sourceTree = ""; }; 345B20E32179696C0079E3C9 /* FindSymbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FindSymbol.cpp; path = ../Code/FindSymbol.cpp; sourceTree = ""; }; 345B20E42179696C0079E3C9 /* WebFldDlg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebFldDlg.cpp; path = ../Code/WebFldDlg.cpp; sourceTree = ""; }; 345B20E52179696C0079E3C9 /* SimpleCppParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SimpleCppParser.cpp; path = ../Code/SimpleCppParser.cpp; sourceTree = ""; }; 345B20E62179696C0079E3C9 /* IdeDoc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeDoc.h; path = ../Code/IdeDoc.h; sourceTree = ""; }; 345B20E72179696C0079E3C9 /* DocEdit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DocEdit.cpp; path = ../Code/DocEdit.cpp; sourceTree = ""; }; 345B20E82179696C0079E3C9 /* DocEdit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DocEdit.h; path = ../Code/DocEdit.h; sourceTree = ""; }; 345B20E92179696D0079E3C9 /* FindInFiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FindInFiles.h; path = ../Code/FindInFiles.h; sourceTree = ""; }; 345B20EA2179696D0079E3C9 /* AddFtpFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddFtpFile.cpp; path = ../Code/AddFtpFile.cpp; sourceTree = ""; }; 345B20EB2179696D0079E3C9 /* AddFtpFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddFtpFile.h; path = ../Code/AddFtpFile.h; sourceTree = ""; }; 345B20EC2179696D0079E3C9 /* FtpFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FtpFile.h; path = ../Code/FtpFile.h; sourceTree = ""; }; - 345B20ED2179696D0079E3C9 /* GDebugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GDebugger.h; path = ../Code/GDebugger.h; sourceTree = ""; }; - 345B20EE2179696D0079E3C9 /* GHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GHistory.cpp; path = ../Code/GHistory.cpp; sourceTree = ""; }; + 345B20ED2179696D0079E3C9 /* Debugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Debugger.h; path = ../Code/Debugger.h; sourceTree = ""; }; + 345B20EE2179696D0079E3C9 /* History.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = History.cpp; path = ../Code/History.cpp; sourceTree = ""; }; 345B20EF2179696E0079E3C9 /* IdeProject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeProject.cpp; path = ../Code/IdeProject.cpp; sourceTree = ""; }; - 345B20F02179696E0079E3C9 /* GDebugContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GDebugContext.h; path = ../Code/GDebugContext.h; sourceTree = ""; }; + 345B20F02179696E0079E3C9 /* DebugContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DebugContext.h; path = ../Code/DebugContext.h; sourceTree = ""; }; 345B20F12179696E0079E3C9 /* levenshtein.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = levenshtein.c; path = ../Code/levenshtein.c; sourceTree = ""; }; - 345B20F22179696E0079E3C9 /* GHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GHistory.h; path = ../Code/GHistory.h; sourceTree = ""; }; + 345B20F22179696E0079E3C9 /* History.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = History.h; path = ../Code/History.h; sourceTree = ""; }; 345B20F32179696E0079E3C9 /* levenshtein.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = levenshtein.h; path = ../Code/levenshtein.h; sourceTree = ""; }; 345B20F52179696E0079E3C9 /* LgiUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiUtils.cpp; path = ../Code/LgiUtils.cpp; sourceTree = ""; }; 345B20F62179696E0079E3C9 /* LgiIde.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = LgiIde.cpp; path = ../Code/LgiIde.cpp; sourceTree = ""; }; 345B20F72179696F0079E3C9 /* IdeCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeCommon.cpp; path = ../Code/IdeCommon.cpp; sourceTree = ""; }; 345B20F82179696F0079E3C9 /* IdeProject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IdeProject.h; path = ../Code/IdeProject.h; sourceTree = ""; }; 345B20F92179696F0079E3C9 /* LgiIde.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiIde.h; path = ../Code/LgiIde.h; sourceTree = ""; }; 345B20FA2179696F0079E3C9 /* ProjectNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProjectNode.h; path = ../Code/ProjectNode.h; sourceTree = ""; }; 345B20FB2179696F0079E3C9 /* FtpThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FtpThread.cpp; path = ../Code/FtpThread.cpp; sourceTree = ""; }; - 345B20FC2179696F0079E3C9 /* GDebugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GDebugger.cpp; path = ../Code/GDebugger.cpp; sourceTree = ""; }; - 345B20FD2179696F0079E3C9 /* GDebugContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GDebugContext.cpp; path = ../Code/GDebugContext.cpp; sourceTree = ""; }; + 345B20FC2179696F0079E3C9 /* Debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Debugger.cpp; path = ../Code/Debugger.cpp; sourceTree = ""; }; + 345B20FD2179696F0079E3C9 /* DebugContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DebugContext.cpp; path = ../Code/DebugContext.cpp; sourceTree = ""; }; 345B20FE217969700079E3C9 /* IdeProjectSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IdeProjectSettings.cpp; path = ../Code/IdeProjectSettings.cpp; sourceTree = ""; }; 345B20FF217969700079E3C9 /* MissingFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MissingFiles.cpp; path = ../Code/MissingFiles.cpp; sourceTree = ""; }; 345B2100217969700079E3C9 /* ParserCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ParserCommon.h; path = ../Code/ParserCommon.h; sourceTree = ""; }; 345B211B217969D30079E3C9 /* mac-icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = "mac-icon.icns"; path = "../Resources/mac-icon.icns"; sourceTree = ""; }; 345B211C217969D30079E3C9 /* LgiIde.lr8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = LgiIde.lr8; path = ../Resources/LgiIde.lr8; sourceTree = ""; }; 345B211D217969D30079E3C9 /* icons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icons.png; path = ../Resources/icons.png; sourceTree = ""; }; 346A86722325CBA000FBC545 /* libpng.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libpng.xcodeproj; path = ../../../../../codelib/libpng/build/libpng.xcodeproj; sourceTree = ""; }; 34C4FD3222FDB50D00C4BECB /* cmds-16px.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cmds-16px.png"; sourceTree = ""; }; 34C4FD3322FDB50D00C4BECB /* cmds-32px.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cmds-32px.png"; sourceTree = ""; }; 34C75D9223A0D89A006DE6D1 /* NewProjectFromTemplate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NewProjectFromTemplate.cpp; path = ../Code/NewProjectFromTemplate.cpp; sourceTree = ""; }; 34D21CD721797815003D1EA6 /* ExeCheck.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ExeCheck.cpp; path = ../../src/common/General/ExeCheck.cpp; sourceTree = ""; }; 34D21CD921797833003D1EA6 /* LexCpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LexCpp.cpp; path = ../../src/common/Coding/LexCpp.cpp; sourceTree = ""; }; 34D21CDB21797856003D1EA6 /* OptionsFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptionsFile.cpp; path = ../../src/common/Lgi/OptionsFile.cpp; sourceTree = ""; }; 34D21CDD21797856003D1EA6 /* Mdi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mdi.cpp; path = ../../src/common/Lgi/Mdi.cpp; sourceTree = ""; }; 34D21CE12179788B003D1EA6 /* Ftp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ftp.cpp; path = ../../src/common/Net/Ftp.cpp; sourceTree = ""; }; 34D21CE22179788B003D1EA6 /* OpenSSLSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OpenSSLSocket.cpp; path = ../../src/common/Net/OpenSSLSocket.cpp; sourceTree = ""; }; 34D21CE32179788B003D1EA6 /* Http.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Http.cpp; path = ../../src/common/Net/Http.cpp; sourceTree = ""; }; 34D21CE7217978C4003D1EA6 /* LgiMain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiMain.cpp; path = ../../src/common/Lgi/LgiMain.cpp; sourceTree = ""; }; 34D21CE9217978DA003D1EA6 /* Base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Base64.cpp; path = ../../src/common/Net/Base64.cpp; sourceTree = ""; }; 34D21CED21798240003D1EA6 /* Png.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Png.cpp; path = ../../src/common/Gdc2/Filters/Png.cpp; sourceTree = ""; }; 34F566A0237BD55F0027133A /* JavascriptParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JavascriptParser.cpp; path = ../Code/JavascriptParser.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 345B2069217965E80079E3C9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 345B20D42179688E0079E3C9 /* LgiCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 345B2063217965E80079E3C9 = { isa = PBXGroup; children = ( 345B206E217965E80079E3C9 /* LgiIde */, 345B211A217969C50079E3C9 /* Resources */, 345B206D217965E80079E3C9 /* Products */, 345B20CB217967B70079E3C9 /* Frameworks */, ); sourceTree = ""; }; 345B206D217965E80079E3C9 /* Products */ = { isa = PBXGroup; children = ( 345B206C217965E80079E3C9 /* LgiIde.app */, ); name = Products; sourceTree = ""; }; 345B206E217965E80079E3C9 /* LgiIde */ = { isa = PBXGroup; children = ( 34D21CD621797809003D1EA6 /* Lgi */, 345B212521796A2D0079E3C9 /* Search */, 345B212421796A1E0079E3C9 /* Debugging */, 345B212321796A180079E3C9 /* Ui */, 345B212221796A040079E3C9 /* Docs */, 345B2121217969F10079E3C9 /* Projects */, 345B20F62179696E0079E3C9 /* LgiIde.cpp */, 34C75D9223A0D89A006DE6D1 /* NewProjectFromTemplate.cpp */, 345B20F92179696F0079E3C9 /* LgiIde.h */, 345B20F52179696E0079E3C9 /* LgiUtils.cpp */, 345B2077217965EA0079E3C9 /* Info.plist */, 345B207A217965EA0079E3C9 /* LgiIde.entitlements */, ); name = LgiIde; sourceTree = ""; }; 345B20CB217967B70079E3C9 /* Frameworks */ = { isa = PBXGroup; children = ( 346A86722325CBA000FBC545 /* libpng.xcodeproj */, 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */, ); name = Frameworks; sourceTree = ""; }; 345B20CD217968820079E3C9 /* Products */ = { isa = PBXGroup; children = ( 345B20D1217968820079E3C9 /* LgiCocoa.framework */, ); name = Products; sourceTree = ""; }; 345B211A217969C50079E3C9 /* Resources */ = { isa = PBXGroup; children = ( 34C4FD3222FDB50D00C4BECB /* cmds-16px.png */, 34C4FD3322FDB50D00C4BECB /* cmds-32px.png */, 345B211D217969D30079E3C9 /* icons.png */, 345B211C217969D30079E3C9 /* LgiIde.lr8 */, 345B211B217969D30079E3C9 /* mac-icon.icns */, ); name = Resources; path = ../Resources; sourceTree = ""; }; 345B2121217969F10079E3C9 /* Projects */ = { isa = PBXGroup; children = ( 345B20DD2179696B0079E3C9 /* ProjectNode.cpp */, 345B20FA2179696F0079E3C9 /* ProjectNode.h */, 345B20D72179696B0079E3C9 /* IdeDocPrivate.h */, 345B20EF2179696E0079E3C9 /* IdeProject.cpp */, 345B20F82179696F0079E3C9 /* IdeProject.h */, 345B20FE217969700079E3C9 /* IdeProjectSettings.cpp */, 345B20DF2179696C0079E3C9 /* IdeProjectSettings.h */, ); name = Projects; sourceTree = ""; }; 345B212221796A040079E3C9 /* Docs */ = { isa = PBXGroup; children = ( 345B20E12179696C0079E3C9 /* IdeDoc.cpp */, 345B20E62179696C0079E3C9 /* IdeDoc.h */, 345B20E72179696C0079E3C9 /* DocEdit.cpp */, 345B20E82179696C0079E3C9 /* DocEdit.h */, 345B20DC2179696B0079E3C9 /* DocEditStyling.cpp */, ); name = Docs; sourceTree = ""; }; 345B212321796A180079E3C9 /* Ui */ = { isa = PBXGroup; children = ( 345B20E42179696C0079E3C9 /* WebFldDlg.cpp */, 345B20D92179696B0079E3C9 /* WebFldDlg.h */, 345B20DE2179696B0079E3C9 /* SysCharSupport.cpp */, 345B20E22179696C0079E3C9 /* SpaceTabConv.cpp */, 345B20D62179696B0079E3C9 /* SpaceTabConv.h */, 345B20EC2179696D0079E3C9 /* FtpFile.h */, 345B20FB2179696F0079E3C9 /* FtpThread.cpp */, 345B20D82179696B0079E3C9 /* FtpThread.h */, - 345B20EE2179696D0079E3C9 /* GHistory.cpp */, - 345B20F22179696E0079E3C9 /* GHistory.h */, + 345B20EE2179696D0079E3C9 /* History.cpp */, + 345B20F22179696E0079E3C9 /* History.h */, 345B20F72179696F0079E3C9 /* IdeCommon.cpp */, 345B20DA2179696B0079E3C9 /* MemDumpViewer.cpp */, 345B20FF217969700079E3C9 /* MissingFiles.cpp */, 345B20EA2179696D0079E3C9 /* AddFtpFile.cpp */, 345B20EB2179696D0079E3C9 /* AddFtpFile.h */, ); name = Ui; sourceTree = ""; }; 345B212421796A1E0079E3C9 /* Debugging */ = { isa = PBXGroup; children = ( - 345B20FD2179696F0079E3C9 /* GDebugContext.cpp */, - 345B20F02179696E0079E3C9 /* GDebugContext.h */, - 345B20FC2179696F0079E3C9 /* GDebugger.cpp */, - 345B20ED2179696D0079E3C9 /* GDebugger.h */, + 345B20FD2179696F0079E3C9 /* DebugContext.cpp */, + 345B20F02179696E0079E3C9 /* DebugContext.h */, + 345B20FC2179696F0079E3C9 /* Debugger.cpp */, + 345B20ED2179696D0079E3C9 /* Debugger.h */, ); name = Debugging; sourceTree = ""; }; 345B212521796A2D0079E3C9 /* Search */ = { isa = PBXGroup; children = ( 34F566A0237BD55F0027133A /* JavascriptParser.cpp */, 34D21CD921797833003D1EA6 /* LexCpp.cpp */, 345B2100217969700079E3C9 /* ParserCommon.h */, 345B20D52179696B0079E3C9 /* PythonParser.cpp */, 345B20E52179696C0079E3C9 /* SimpleCppParser.cpp */, 345B20F12179696E0079E3C9 /* levenshtein.c */, 345B20F32179696E0079E3C9 /* levenshtein.h */, 345B20DB2179696B0079E3C9 /* FindInFiles.cpp */, 345B20E92179696D0079E3C9 /* FindInFiles.h */, 345B20E32179696C0079E3C9 /* FindSymbol.cpp */, 345B20E02179696C0079E3C9 /* FindSymbol.h */, ); name = Search; sourceTree = ""; }; 34D21CD621797809003D1EA6 /* Lgi */ = { isa = PBXGroup; children = ( 341C7AC721A752DC0083C9CE /* About.cpp */, 34D21CED21798240003D1EA6 /* Png.cpp */, 34D21CE9217978DA003D1EA6 /* Base64.cpp */, 34D21CE7217978C4003D1EA6 /* LgiMain.cpp */, 34D21CE12179788B003D1EA6 /* Ftp.cpp */, 34D21CE32179788B003D1EA6 /* Http.cpp */, 34D21CE22179788B003D1EA6 /* OpenSSLSocket.cpp */, 34D21CDD21797856003D1EA6 /* Mdi.cpp */, 34D21CDB21797856003D1EA6 /* OptionsFile.cpp */, 34D21CD721797815003D1EA6 /* ExeCheck.cpp */, ); name = Lgi; sourceTree = ""; }; 34E44DBF279FB65600855194 /* Products */ = { isa = PBXGroup; children = ( 34E44DCD279FB65600855194 /* example */, - 34E44DCF279FB65600855194 /* libpng15.dylib */, - 34E44DD1279FB65600855194 /* libpng15_static.a */, + 348C6BA029A8149E002E235F /* libpng16.dylib */, + 348C6BA229A8149E002E235F /* libpng16_static.a */, 34E44DD3279FB65600855194 /* minigzip */, 34E44DD5279FB65600855194 /* pngtest */, 34E44DD7279FB65600855194 /* libz_local.dylib */, 34E44DD9279FB65600855194 /* libzlib_static.a */, ); name = Products; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 345B206B217965E80079E3C9 /* LgiIde */ = { isa = PBXNativeTarget; buildConfigurationList = 345B207D217965EA0079E3C9 /* Build configuration list for PBXNativeTarget "LgiIde" */; buildPhases = ( 345B2068217965E80079E3C9 /* Sources */, 345B2069217965E80079E3C9 /* Frameworks */, 34CAF47E22D3771500BE529A /* CopyFiles */, 34148AF12349EC9500D5E495 /* CopyFiles */, ); buildRules = ( ); dependencies = ( 34E44DEF279FBD2700855194 /* PBXTargetDependency */, - 34E44DDC279FB72800855194 /* PBXTargetDependency */, 345B20D3217968890079E3C9 /* PBXTargetDependency */, ); name = LgiIde; productName = LgiIde; productReference = 345B206C217965E80079E3C9 /* LgiIde.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 345B2064217965E80079E3C9 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1240; ORGANIZATIONNAME = "Matthew Allen"; TargetAttributes = { 345B206B217965E80079E3C9 = { CreatedOnToolsVersion = 9.3; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.Sandbox = { enabled = 0; }; }; }; }; }; buildConfigurationList = 345B2067217965E80079E3C9 /* Build configuration list for PBXProject "LgiIde" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 345B2063217965E80079E3C9; productRefGroup = 345B206D217965E80079E3C9 /* Products */; projectDirPath = ""; projectReferences = ( { ProductGroup = 345B20CD217968820079E3C9 /* Products */; ProjectRef = 345B20C3217967760079E3C9 /* LgiCocoa.xcodeproj */; }, { ProductGroup = 34E44DBF279FB65600855194 /* Products */; ProjectRef = 346A86722325CBA000FBC545 /* libpng.xcodeproj */; }, ); projectRoot = ""; targets = ( 345B206B217965E80079E3C9 /* LgiIde */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 345B20D1217968820079E3C9 /* LgiCocoa.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = LgiCocoa.framework; remoteRef = 345B20D0217968820079E3C9 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 348C6BA029A8149E002E235F /* libpng16.dylib */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.dylib"; + name = libpng16.dylib; + path = libpng16.15.29.0.dylib; + remoteRef = 348C6B9F29A8149E002E235F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 348C6BA229A8149E002E235F /* libpng16_static.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libpng16_static.a; + remoteRef = 348C6BA129A8149E002E235F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 34E44DCD279FB65600855194 /* example */ = { isa = PBXReferenceProxy; fileType = "compiled.mach-o.executable"; path = example; remoteRef = 34E44DCC279FB65600855194 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 34E44DCF279FB65600855194 /* libpng15.dylib */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.dylib"; - name = libpng15.dylib; - path = libpng15.15.4.0.dylib; - remoteRef = 34E44DCE279FB65600855194 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 34E44DD1279FB65600855194 /* libpng15_static.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libpng15_static.a; - remoteRef = 34E44DD0279FB65600855194 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 34E44DD3279FB65600855194 /* minigzip */ = { isa = PBXReferenceProxy; fileType = "compiled.mach-o.executable"; path = minigzip; remoteRef = 34E44DD2279FB65600855194 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 34E44DD5279FB65600855194 /* pngtest */ = { isa = PBXReferenceProxy; fileType = "compiled.mach-o.executable"; path = pngtest; remoteRef = 34E44DD4279FB65600855194 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 34E44DD7279FB65600855194 /* libz_local.dylib */ = { isa = PBXReferenceProxy; fileType = "compiled.mach-o.dylib"; name = libz_local.dylib; path = libz_local.1.2.5.dylib; remoteRef = 34E44DD6279FB65600855194 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 34E44DD9279FB65600855194 /* libzlib_static.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libzlib_static.a; remoteRef = 34E44DD8279FB65600855194 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXSourcesBuildPhase section */ 345B2068217965E80079E3C9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 34D21CD821797815003D1EA6 /* ExeCheck.cpp in Sources */, 345B2115217969700079E3C9 /* FtpThread.cpp in Sources */, 345B210F217969700079E3C9 /* IdeProject.cpp in Sources */, 34D21CE62179788B003D1EA6 /* Http.cpp in Sources */, 345B210C217969700079E3C9 /* DocEdit.cpp in Sources */, 345B2107217969700079E3C9 /* IdeDoc.cpp in Sources */, 345B2110217969700079E3C9 /* levenshtein.c in Sources */, 34D21CEE21798240003D1EA6 /* Png.cpp in Sources */, 345B2101217969700079E3C9 /* PythonParser.cpp in Sources */, 345B2119217969700079E3C9 /* MissingFiles.cpp in Sources */, 345B2114217969700079E3C9 /* IdeCommon.cpp in Sources */, 345B2108217969700079E3C9 /* SpaceTabConv.cpp in Sources */, 345B2104217969700079E3C9 /* DocEditStyling.cpp in Sources */, 345B210B217969700079E3C9 /* SimpleCppParser.cpp in Sources */, 34D21CE8217978C4003D1EA6 /* LgiMain.cpp in Sources */, - 345B2116217969700079E3C9 /* GDebugger.cpp in Sources */, + 345B2116217969700079E3C9 /* Debugger.cpp in Sources */, 341C7AC821A752DC0083C9CE /* About.cpp in Sources */, - 345B210E217969700079E3C9 /* GHistory.cpp in Sources */, + 345B210E217969700079E3C9 /* History.cpp in Sources */, 345B2113217969700079E3C9 /* LgiIde.cpp in Sources */, 345B2112217969700079E3C9 /* LgiUtils.cpp in Sources */, 345B2103217969700079E3C9 /* FindInFiles.cpp in Sources */, 34D21CEA217978DA003D1EA6 /* Base64.cpp in Sources */, 34D21CE021797856003D1EA6 /* Mdi.cpp in Sources */, 34D21CDA21797833003D1EA6 /* LexCpp.cpp in Sources */, 34F566AB237BD55F0027133A /* JavascriptParser.cpp in Sources */, 345B2118217969700079E3C9 /* IdeProjectSettings.cpp in Sources */, 345B2106217969700079E3C9 /* SysCharSupport.cpp in Sources */, 345B2109217969700079E3C9 /* FindSymbol.cpp in Sources */, 345B2102217969700079E3C9 /* MemDumpViewer.cpp in Sources */, 34C75D9323A0D89A006DE6D1 /* NewProjectFromTemplate.cpp in Sources */, 34D21CE42179788B003D1EA6 /* Ftp.cpp in Sources */, 345B2105217969700079E3C9 /* ProjectNode.cpp in Sources */, 34D21CDE21797856003D1EA6 /* OptionsFile.cpp in Sources */, 34D21CE52179788B003D1EA6 /* OpenSSLSocket.cpp in Sources */, 345B210D217969700079E3C9 /* AddFtpFile.cpp in Sources */, 345B210A217969700079E3C9 /* WebFldDlg.cpp in Sources */, - 345B2117217969700079E3C9 /* GDebugContext.cpp in Sources */, + 345B2117217969700079E3C9 /* DebugContext.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 345B20D3217968890079E3C9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = LgiCocoa; targetProxy = 345B20D2217968890079E3C9 /* PBXContainerItemProxy */; }; - 34E44DDC279FB72800855194 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = libpng15; - targetProxy = 34E44DDB279FB72800855194 /* PBXContainerItemProxy */; - }; 34E44DEF279FBD2700855194 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = zlib; targetProxy = 34E44DEE279FBD2700855194 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 345B207B217965EA0079E3C9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 345B207C217965EA0079E3C9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; name = Release; }; 345B207E217965EA0079E3C9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES_NONAGGRESSIVE; CLANG_ENABLE_OBJC_ARC = NO; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = c11; HEADER_SEARCH_PATHS = ( - ../../include, - ../../include/lgi/mac/cocoa, ../Code, ../Resources, - /opt/local/include, - ../../../../../CodeLib/libpng, - ../../../../../CodeLib/libpng/build, + ../../include, + ../../include/lgi/mac/cocoa, + ../../../../../codelib/libpng, + ../../../../../codelib/libpng/build, + ../../../../../codelib/openssl/include, ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; OTHER_CFLAGS = "-DNCURSES_UNCTRL_H_incl=1"; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DMAC", "-DPOSIX", "-DLGI_COCOA", "-D_DEBUG", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.LgiIde; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; VALID_ARCHS = x86_64; WARNING_CFLAGS = "-Wno-nullability-completeness"; }; name = Debug; }; 345B207F217965EA0079E3C9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES_NONAGGRESSIVE; CLANG_ENABLE_OBJC_ARC = NO; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = c11; HEADER_SEARCH_PATHS = ( - ../../include, - ../../include/lgi/mac/cocoa, ../Code, ../Resources, - /opt/local/include, - ../../../../../CodeLib/libpng, - ../../../../../CodeLib/libpng/build, + ../../include, + ../../include/lgi/mac/cocoa, + ../../../../../codelib/libpng, + ../../../../../codelib/libpng/build, + ../../../../../codelib/openssl/include, ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; OTHER_CFLAGS = "-DNCURSES_UNCTRL_H_incl=1"; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-DMAC", "-DPOSIX", "-DLGI_COCOA", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.LgiIde; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; VALID_ARCHS = x86_64; WARNING_CFLAGS = "-Wno-nullability-completeness"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 345B2067217965E80079E3C9 /* Build configuration list for PBXProject "LgiIde" */ = { isa = XCConfigurationList; buildConfigurations = ( 345B207B217965EA0079E3C9 /* Debug */, 345B207C217965EA0079E3C9 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 345B207D217965EA0079E3C9 /* Build configuration list for PBXNativeTarget "LgiIde" */ = { isa = XCConfigurationList; buildConfigurations = ( 345B207E217965EA0079E3C9 /* Debug */, 345B207F217965EA0079E3C9 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 345B2064217965E80079E3C9 /* Project object */; } diff --git a/ResourceEditor/Code/LgiResApp.cpp b/ResourceEditor/Code/LgiResApp.cpp --- a/ResourceEditor/Code/LgiResApp.cpp +++ b/ResourceEditor/Code/LgiResApp.cpp @@ -1,4656 +1,4657 @@ /* ** FILE: LgiRes.cpp ** AUTHOR: Matthew Allen ** DATE: 3/8/99 ** DESCRIPTION: Lgi Resource Editor ** ** Copyright (C) 1999, Matthew Allen ** fret@memecode.com */ #include #include "LgiResEdit.h" #include "LgiRes_Dialog.h" #include "LgiRes_Menu.h" #include "lgi/common/About.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Edit.h" #include "lgi/common/CheckBox.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/TextView3.h" #include "resdefs.h" #include "lgi/common/Token.h" #include "lgi/common/DataDlg.h" #include "lgi/common/Button.h" #include "lgi/common/Menu.h" #include "lgi/common/StatusBar.h" char AppName[] = "Lgi Resource Editor"; char HelpFile[] = "Help.html"; char OptionsFileName[] = "Options.r"; char TranslationStrMagic[] = "LgiRes.String"; #define VIEW_PULSE_RATE 100 #ifndef DIALOG_X #define DIALOG_X 1.56 #define DIALOG_Y 1.85 #define CTRL_X 1.50 #define CTRL_Y 1.64 #endif enum Ctrls { IDC_HBOX = 100, IDC_VBOX, }; const char *TypeNames[] = { "", "Css", "Dialog", "String", "Menu", 0}; ////////////////////////////////////////////////////////////////////////////// ResFileFormat GetFormat(const char *File) { ResFileFormat Format = Lr8File; char *Ext = LGetExtension(File); if (Ext) { if (stricmp(Ext, "lr") == 0) Format = CodepageFile; else if (stricmp(Ext, "xml") == 0) Format = XmlFile; } return Format; } char *EncodeXml(const char *Str, int Len) { char *Ret = 0; if (Str) { LStringPipe p; const char *s = Str; for (const char *e = Str; e && *e && (Len < 0 || ((e-Str) < Len)); ) { switch (*e) { case '<': { p.Push(s, e-s); p.Push("<"); s = ++e; break; } case '>': { p.Push(s, e-s); p.Push(">"); s = ++e; break; } case '&': { p.Push(s, e-s); p.Push("&"); s = ++e; break; } case '\\': { if (e[1] == 'n') { // Newline p.Push(s, e-s); p.Push("\n"); s = (e += 2); break; } // fall thru } case '\'': case '\"': case '/': { // Convert to entity p.Push(s, e-s); char b[32]; sprintf(b, "&#%i;", *e); p.Push(b); s = ++e; break; } default: { // Regular character e++; break; } } } p.Push(s); Ret = p.NewStr(); } return Ret; } char *DecodeXml(const char *Str, int Len) { if (Str) { LStringPipe p; const char *s = Str; for (const char *e = Str; e && *e && (Len < 0 || ((e-Str) < Len)); ) { switch (*e) { case '&': { // Store string up to here p.Push(s, e-s); e++; if (*e == '#') { // Numerical e++; if (*e == 'x' || *e == 'X') { // Hex e++; char16 c = htoi(e); char *c8 = WideToUtf8(&c, 1); if (c8) { p.Push(c8); DeleteArray(c8); } } else if (isdigit(*e)) { // Decimal char16 c = atoi(e); char *c8 = WideToUtf8(&c, 1); if (c8) { p.Push(c8); DeleteArray(c8); } } else { LAssert(0); } while (*e && *e != ';') e++; } else if (isalpha(*e)) { // named entity const char *Name = e; while (*e && *e != ';') e++; auto Len = e - Name; if (Len == 3 && strnicmp(Name, "amp", Len) == 0) { p.Push("&"); } else if (Len == 2 && strnicmp(Name, "gt", Len) == 0) { p.Push(">"); } else if (Len == 2 && strnicmp(Name, "lt", Len) == 0) { p.Push("<"); } else { // Unsupported entity LAssert(0); } } else { LAssert(0); while (*e && *e != ';') e++; } s = ++e; break; } case '\n': { p.Push(s, e-s); p.Push("\\n"); s = ++e; break; } default: { e++; break; } } } p.Push(s); return p.NewStr(); } return 0; } ////////////////////////////////////////////////////////////////////////////// Resource::Resource(AppWnd *w, int t, bool enabled) { AppWindow = w; ResType = t; Item = 0; SysObject = false; LAssert(AppWindow); } Resource::~Resource() { AppWindow->OnResourceDelete(this); if (Item) { Item->Obj = 0; DeleteObj(Item); } } bool Resource::IsSelected() { return Item?Item->Select():false; } bool Resource::Attach(LViewI *Parent) { LView *w = Wnd(); if (w) { return w->Attach(Parent); } return false; } ////////////////////////////////////////////////////////////////////////////// ResFolder::ResFolder(AppWnd *w, int t, bool enabled) : Resource(w, t, enabled) { Wnd()->Name(""); Wnd()->Enabled(enabled); } ////////////////////////////////////////////////////////////////////////////// ObjTreeItem::ObjTreeItem(Resource *Object) { if ((Obj = Object)) { Obj->Item = this; if (dynamic_cast(Object)) SetImage(ICON_FOLDER); else { int t = Object->Type(); switch (t) { case TYPE_CSS: SetImage(ICON_CSS); break; case TYPE_DIALOG: SetImage(ICON_DIALOG); break; case TYPE_STRING: SetImage(ICON_STRING); break; case TYPE_MENU: SetImage(ICON_MENU); break; } } } } ObjTreeItem::~ObjTreeItem() { if (Obj) { Obj->Item = 0; DeleteObj(Obj); } } const char *ObjTreeItem::GetText(int i) { if (Obj) { int Type = Obj->Type(); if (Type > 0) return Obj->Wnd()->Name(); else return TypeNames[-Type]; } return "#NO_OBJ"; } void ObjTreeItem::OnSelect() { if (Obj) { Obj->App()->OnResourceSelect(Obj); } } void ObjTreeItem::OnMouseClick(LMouse &m) { if (!Obj) return; if (m.IsContextMenu()) { Tree->Select(this); LSubMenu RClick; if (Obj->Wnd()->Enabled()) { if (Obj->Type() > 0) { // Resource RClick.AppendItem("Delete", IDM_DELETE, !Obj->SystemObject()); RClick.AppendItem("Rename", IDM_RENAME, !Obj->SystemObject()); } else { // Folder RClick.AppendItem("New", IDM_NEW, true); RClick.AppendSeparator(); auto Insert = RClick.AppendSub("Import from..."); if (Insert) { Insert->AppendItem("Lgi File", IDM_IMPORT, true); Insert->AppendItem("Win32 Resource Script", IDM_IMPORT_WIN32, false); } } // Custom entries if (!Obj->SystemObject()) { Obj->OnRightClick(&RClick); } } else { RClick.AppendItem("Not implemented", 0, false); } if (Tree->GetMouse(m, true)) { int Cmd = 0; switch (Cmd = RClick.Float(Tree, m.x, m.y)) { case IDM_NEW: { SerialiseContext Ctx; Obj->App()->NewObject(Ctx, 0, -Obj->Type()); break; } case IDM_DELETE: { Obj->App()->SetDirty(true); Obj->App()->DelObject(Obj); break; } case IDM_RENAME: { auto Dlg = new LInput(Tree, GetText(), "Enter the name for the object", "Object Name"); Dlg->DoModal([&](auto dlg, auto id) { if (id) { Obj->Wnd()->Name(Dlg->GetStr()); Update(); Obj->App()->SetDirty(true); } delete dlg; }); break; } case IDM_IMPORT: { auto Select = new LFileSelect(Obj->App()); Select->Type("Text", "*.txt"); Select->Open([&](auto dlg, auto status) { if (status) { LFile F; if (F.Open(dlg->Name(), O_READ)) { SerialiseContext Ctx; Resource *Res = Obj->App()->NewObject(Ctx, 0, -Obj->Type()); if (Res) { // TODO // Res->Read(); } } else { LgiMsg(Obj->App(), "Couldn't open file for reading."); } } delete dlg; }); break; } case IDM_IMPORT_WIN32: { /* List l; if (ImportWin32Dialogs(l, MainWnd)) { for (ResDialog *r = l.First(); r; r = l.Next()) { Obj->App()->InsertObject(TYPE_DIALOG, r); } } */ break; } default: { Obj->OnCommand(Cmd); break; } } } } } ////////////////////////////////////////////////////////////////////////////// FieldView::FieldView(AppWnd *app) : Fields(NextId, true) { NextId = 100; App = app; Source = 0; Ignore = true; SetTabStop(true); Sunken(true); #ifdef WIN32 SetExStyle(GetExStyle() | WS_EX_CONTROLPARENT); #endif } FieldView::~FieldView() { } void FieldView::Serialize(bool Write) { if (!Source) return; Ignore = !Write; Fields.SetMode(Write ? FieldTree::UiToObj : FieldTree::ObjToUi); Fields.SetView(this); Source->Serialize(Fields); /* for (DataDlgField *f=Fields.First(); f; f=Fields.Next()) { LViewI *v; if (GetViewById(f->GetCtrl(), v)) { switch (f->GetType()) { case DATA_STR: { if (Write) // Ctrl -> Options { char *s = v->Name(); Options->Set(f->GetOption(), s); } else // Options -> Ctrl { char *s = 0; Options->Get(f->GetOption(), s); v->Name(s?s:(char*)""); } break; } case DATA_BOOL: case DATA_INT: { if (Write) // Ctrl -> Options { char *s = v->Name(); if (s && (s = strchr(s, '\''))) { s++; char *e = strchr(s, '\''); int i = 0; if (e - s == 4) { memcpy(&i, s, 4); i = LgiSwap32(i); Options->Set(f->GetOption(), i); } } else { int i = v->Value(); Options->Set(f->GetOption(), i); } } else // Options -> Ctrl { int i = 0; Options->Get(f->GetOption(), i); if (i != -1 && (i & 0xff000000) != 0) { char m[8]; i = LgiSwap32(i); sprintf(m, "'%04.4s'", &i); v->Name(m); } else { v->Value(i); } } break; } case DATA_FLOAT: case DATA_PASSWORD: case DATA_STR_SYSTEM: default: { LAssert(0); break; } } } else LAssert(0); } */ Ignore = false; } class TextViewEdit : public LTextView3 { public: bool Multiline; TextViewEdit( int Id, int x, int y, int cx, int cy, LFontType *FontInfo = 0) : LTextView3(Id, x, y, cx, cy, FontInfo) { Multiline = false; #ifdef WIN32 SetDlgCode(DLGC_WANTARROWS | DLGC_WANTCHARS); #endif } bool OnKey(LKey &k) { if (!Multiline && (k.c16 == '\t' || k.c16 == LK_RETURN)) { return false; } return LTextView3::OnKey(k); } }; class Hr : public LView { public: Hr(int x1, int y, int x2) { LRect r(x1, y, x2, y+1); SetPos(r); } void OnPaint(LSurface *pDC) { LRect c = GetClient(); LThinBorder(pDC, c, DefaultSunkenEdge); } bool OnLayout(LViewLayoutInfo &Inf) { if (Inf.Width.Min) Inf.Height.Min = Inf.Height.Max = 2; else Inf.Width.Min = Inf.Width.Max = -1; return true; } }; void FieldView::OnDelete(FieldSource *s) { if (Source != NULL && Source == s) { // Clear fields Source->_FieldView = 0; Fields.Empty(); // remove all children LViewI *c; while ((c = Children[0])) { c->Detach(); DeleteObj(c); } Source = NULL; } } void FieldView::OnSelect(FieldSource *s) { Ignore = true; OnDelete(Source); if (Source) { // Clear fields Source->_FieldView = 0; Fields.Empty(); // remove all children LViewI *c; while ((c = Children[0])) { c->Detach(); DeleteObj(c); } Source = 0; } if (s) { // Add new fields Source = s; Source->_FieldView = AddDispatch(); if (Source->GetFields(Fields)) { LFontType Sys; Sys.GetSystemFont("System"); LTableLayout *t = new LTableLayout(IDC_TABLE); int Row = 0; LLayoutCell *Cell; LArray a; Fields.GetAll(a); for (int i=0; iLength(); n++, Row++) { FieldTree::Field *c = (*b)[n]; switch (c->Type) { case DATA_STR: case DATA_FLOAT: case DATA_INT: case DATA_FILENAME: { Cell = t->GetCell(0, Row); Cell->VerticalAlign(LCss::VerticalMiddle); Cell->Add(new LTextLabel(-1, 0, 0, -1, -1, c->Label)); TextViewEdit *Tv; Cell = t->GetCell(1, Row, true, c->Type == DATA_FILENAME ? 1 : 2); Cell->Add(Tv = new TextViewEdit(c->Id, 0, 0, 100, 20, &Sys)); if (Tv) { Tv->Multiline = c->Multiline; Tv->GetCss(true)->Height(LCss::Len(LCss::LenPx, c->Multiline ? LSysFont->GetHeight() * 8 : LSysFont->GetHeight() + 8)); Tv->SetWrapType(TEXTED_WRAP_NONE); Tv->Sunken(true); } if (c->Type == DATA_FILENAME) { Cell = t->GetCell(2, Row); Cell->Add(new LButton(-c->Id, 0, 0, 21, 21, "...")); } break; } case DATA_BOOL: { Cell = t->GetCell(1, Row, true, 2); Cell->Add(new LCheckBox(c->Id, 0, 0, -1, -1, c->Label)); break; } default: LAssert(!"Impl me."); break; } } if (i < a.Length() - 1) { Cell = t->GetCell(0, Row++, true, 3); Cell->Add(new Hr(0, 0, X()-1)); } } AddView(t); OnPosChange(); AttachChildren(); Invalidate(); } Serialize(false); Ignore = false; } } void FieldView::OnPosChange() { LRect c = GetClient(); c.Inset(6, 6); LViewI *v; if (GetViewById(IDC_TABLE, v)) v->SetPos(c); } LMessage::Result FieldView::OnEvent(LMessage *m) { switch (m->Msg()) { case M_OBJECT_CHANGED: { FieldSource *Src = (FieldSource*)m->A(); if (Src == Source) { Fields.SetMode(FieldTree::ObjToUi); Fields.SetView(this); Serialize(false); } else LAssert(0); break; } } return LLayout::OnEvent(m); } int FieldView::OnNotify(LViewI *Ctrl, LNotification n) { if (!Ignore) { LTextView3 *Tv = dynamic_cast(Ctrl); if (Tv && n.Type == LNotifyCursorChanged) { return 0; } LArray a; Fields.GetAll(a); for (int i=0; iLength(); n++) { FieldTree::Field *c = (*b)[n]; if (c->Id == Ctrl->GetId()) { // Write the value back to the objects Fields.SetMode(FieldTree::UiToObj); Fields.SetView(this); Source->Serialize(Fields); return 0; } else if (c->Id == -Ctrl->GetId()) { auto s = new LFileSelect(this); s->Open([&](auto dlg, auto status) { if (status) { auto File = App->GetCurFile(); if (File) { LFile::Path p = File; p--; auto Rel = LMakeRelativePath(p, dlg->Name()); if (Rel) SetCtrlName(c->Id, Rel); else SetCtrlName(c->Id, dlg->Name()); } else SetCtrlName(c->Id, dlg->Name()); Fields.SetMode(FieldTree::UiToObj); Fields.SetView(this); Source->Serialize(Fields); } delete dlg; }); } } } } return 0; } void FieldView::OnPaint(LSurface *pDC) { pDC->Colour(L_MED); pDC->Rectangle(); } ////////////////////////////////////////////////////////////////////////////// ObjContainer::ObjContainer(AppWnd *w) : LTree(100, 0, 0, 100, 100, "LgiResObjTree") { Window = w; Sunken(true); Insert(Style = new ObjTreeItem( new ResFolder(Window, -TYPE_CSS))); Insert(Dialogs = new ObjTreeItem( new ResFolder(Window, -TYPE_DIALOG))); Insert(Strings = new ObjTreeItem( new ResFolder(Window, -TYPE_STRING))); Insert(Menus = new ObjTreeItem( new ResFolder(Window, -TYPE_MENU))); const char *IconFile = "_icons.gif"; auto f = LFindFile(IconFile); if (f) { Images = LLoadImageList(f, 16, 16); if (Images) SetImageList(Images, false); else LgiTrace("%s:%i - failed to load '%s'\n", _FL, IconFile); } } ObjContainer::~ObjContainer() { DeleteObj(Images); } bool ObjContainer::AppendChildren(ObjTreeItem *Res, List &Lst) { bool Status = true; if (Res) { LTreeItem *Item = Res->GetChild(); while (Item) { ObjTreeItem *i = dynamic_cast(Item); if (i) Lst.Insert(i->GetObj()); else Status = false; Item = Item->GetNext(); } } return Status; } Resource *ObjContainer::CurrentResource() { ObjTreeItem *Item = dynamic_cast(Selection()); if (!Item) return NULL; return Item->GetObj(); } bool ObjContainer::ListObjects(List &Lst) { bool Status = AppendChildren(Style, Lst); Status &= AppendChildren(Dialogs, Lst); Status &= AppendChildren(Strings, Lst); Status &= AppendChildren(Menus, Lst); return Status; } ////////////////////////////////////////////////////////////////////////////// #ifdef WIN32 int Icon = IDI_ICON1; #else const char *Icon = "icon64.png"; #endif AppWnd::AppWnd() : LDocApp(AppName, Icon) { LastRes = 0; Fields = 0; ViewMenu = 0; ContentView = NULL; VBox = NULL; HBox = NULL; ShortCuts = 0; CurLang = -1; ShowLanguages.Add("en", true); if (_Create()) { LVariant Langs; if (GetOptions()->GetValue(OPT_ShowLanguages, Langs)) { ShowLanguages.Empty(); LToken L(Langs.Str(), ","); for (int i=0; iEmpty(); _Destroy(); } void AppWnd::OnCreate() { if (_LoadMenu("IDM_MENU")) { if (_FileMenu) { int n = 6; _FileMenu->AppendSeparator(n++); _FileMenu->AppendItem("Import Win32 Script", IDM_IMPORT_WIN32, true, n++); _FileMenu->AppendItem("Import LgiRes Language", IDM_IMPORT_LANG, true, n++); _FileMenu->AppendItem("Compare To File...", IDM_COMPARE, true, n++); _FileMenu->AppendSeparator(n++); _FileMenu->AppendItem("Properties", IDM_PROPERTIES, true, n++); } ViewMenu = Menu->FindSubMenu(IDM_VIEW); LAssert(ViewMenu); } else LgiTrace("%s:%i - _LoadMenu failed.\n", _FL); Status = 0; StatusInfo[0] = StatusInfo[1] = 0; HBox = new LBox(IDC_HBOX); if (HBox) { HBox->GetCss(true)->Padding("5px"); VBox = new LBox(IDC_VBOX, true); if (VBox) { HBox->AddView(VBox); VBox->AddView(Objs = new ObjContainer(this)); if (Objs) { Objs->AskImage(true); Objs->AskText(true); } VBox->AddView(Fields = new FieldView(this)); VBox->Value(200); } HBox->Value(240); HBox->Attach(this); } DropTarget(true); LString Open; if (LAppInst->GetOption("o", Open)) LoadLgi(Open); } void AppWnd::OnLanguagesChange(LLanguageId Lang, bool Add, bool Update) { bool Change = false; if (Lang) { // Update the list.... bool Has = false; for (int i=0; iId, Lang) == 0) { Has = true; if (!Add) { Languages.DeleteAt(i); Change = true; } break; } } if (Add && !Has) { Change = true; Languages.Add(LFindLang(Lang)); } } // Update the menu... if (ViewMenu && (Change || Update)) { // Remove existing language menu items while (ViewMenu->RemoveItem(2)); // Add new ones int n = 0; for (int i=0; iAppendItem(Lang->Name, IDM_LANG_BASE + n, true); if (Item) { if (CurLang == i) { Item->Checked(true); } } } } } } bool AppWnd::ShowLang(LLanguageId Lang) { return ShowLanguages.Find(Lang) != 0; } void AppWnd::ShowLang(LLanguageId Lang, bool Show) { // Apply change if (Show) { OnLanguagesChange(Lang, true); ShowLanguages.Add(Lang, true); } else { ShowLanguages.Delete(Lang); } // Store the setting for next time LStringPipe p; // const char *L; // for (bool i = ShowLanguages.First(&L); i; i = ShowLanguages.Next(&L)) for (auto i : ShowLanguages) { if (p.GetSize()) p.Push(","); p.Push(i.key); } char *Langs = p.NewStr(); if (Langs) { LVariant v; GetOptions()->SetValue(OPT_ShowLanguages, v = Langs); DeleteArray(Langs); } // Update everything List res; if (ListObjects(res)) { for (auto r: res) { r->OnShowLanguages(); } } } LLanguage *AppWnd::GetCurLang() { if (CurLang >= 0 && CurLang < Languages.Length()) return Languages[CurLang]; return LFindLang("en"); } void AppWnd::SetCurLang(LLanguage *L) { for (int i=0; iId == L->Id) { // Set new current CurLang = i; // Update everything List res; if (ListObjects(res)) { for (auto r: res) { r->OnShowLanguages(); } } break; } } } LArray *AppWnd::GetLanguages() { return &Languages; } class Test : public LView { COLOUR c; public: Test(COLOUR col, int x1, int y1, int x2, int y2) { c = col; LRect r(x1, y1, x2, y2); SetPos(r); _BorderSize = 1; Sunken(true); } void OnPaint(LSurface *pDC) { pDC->Colour(c, 24); pDC->Rectangle(); } }; LMessage::Result AppWnd::OnEvent(LMessage *m) { LMru::OnEvent(m); switch (m->Msg()) { case M_CHANGE: { LAutoPtr note((LNotification*)m->B()); return OnNotify((LViewI*) m->A(), *note); } case M_DESCRIBE: { char *Text = (char*) m->A(); if (Text) { SetStatusText(Text, STATUS_NORMAL); } break; } } return LWindow::OnEvent(m); } -#include "lgi/common/Token.h" void _CountGroup(ResStringGroup *Grp, int &Words, int &Multi) { for (auto s: *Grp->GetStrs()) { if (s->Items.Length() > 1) { Multi++; char *e = s->Get("en"); if (e) { LToken t(e, " "); Words += t.Length(); } } } } int AppWnd::OnCommand(int Cmd, int Event, OsView Handle) { SerialiseContext Ctx; switch (Cmd) { case IDM_SHOW_LANG: { auto Dlg = new ShowLanguagesDlg(this); Dlg->DoModal([](auto dlg, auto ctrlId) { delete dlg; }); break; } case IDM_NEW_CSS: { NewObject(Ctx, 0, TYPE_CSS); break; } case IDM_NEW_DIALOG: { NewObject(Ctx, 0, TYPE_DIALOG); break; } case IDM_NEW_STRING_GRP: { NewObject(Ctx, 0, TYPE_STRING); break; } case IDM_NEW_MENU: { NewObject(Ctx, 0, TYPE_MENU); break; } case IDM_CLOSE: { Empty(); break; } case IDM_IMPORT_WIN32: { LoadWin32(); break; } case IDM_IMPORT_LANG: { ImportLang(); break; } case IDM_COMPARE: { Compare(); break; } case IDM_PROPERTIES: { List l; if (Objs->ListObjects(l)) { int Dialogs = 0; int Strings = 0; int Menus = 0; int Words = 0; int MultiLingual = 0; for (auto r: l) { switch (r->Type()) { case TYPE_DIALOG: { Dialogs++; break; } case TYPE_STRING: { ResStringGroup *Grp = dynamic_cast(r); if (Grp) { Strings += Grp->GetStrs()->Length(); _CountGroup(Grp, Words, MultiLingual); } break; } case TYPE_MENU: { Menus++; ResMenu *Menu = dynamic_cast(r); if (Menu) { if (Menu->Group) { Strings += Menu->Group->GetStrs()->Length(); _CountGroup(Menu->Group, Words, MultiLingual); } } break; } } } LgiMsg( this, "This file contains:\n" "\n" " Dialogs: %i\n" " Menus: %i\n" " Strings: %i\n" " Multi-lingual: %i\n" " Words: %i", AppName, MB_OK, Dialogs, Menus, Strings, MultiLingual, Words); } break; } case IDM_EXIT: { LCloseApp(); break; } case IDM_FIND: { auto s = new Search(this); s->DoModal([&](auto dlg, auto id) { if (id) new Results(this, s); delete dlg; }); break; } case IDM_NEXT: { LgiMsg(this, "Not implemented :(", AppName); break; } case IDM_CUT: { auto Focus = LAppInst->GetFocus(); if (Focus) { Focus->PostEvent(M_CUT); } else { Resource *r = Objs->CurrentResource(); if (r) r->Copy(true); } break; } case IDM_COPY: { auto Focus = LAppInst->GetFocus(); if (Focus) { Focus->PostEvent(M_COPY); } else { Resource *r = Objs->CurrentResource(); if (r) r->Copy(false); } break; } case IDM_PASTE: { auto Focus = LAppInst->GetFocus(); if (Focus) { Focus->PostEvent(M_PASTE); } else { Resource *r = Objs->CurrentResource(); if (r) r->Paste(); } break; } case IDM_TABLELAYOUT_TEST: { OpenTableLayoutTest(this); break; } case IDM_HELP: { char ExeName[MAX_PATH_LEN]; sprintf_s(ExeName, sizeof(ExeName), "%s", LGetExePath().Get()); while (strchr(ExeName, DIR_CHAR) && strlen(ExeName) > 3) { char p[256]; LMakePath(p, sizeof(p), ExeName, "index.html"); if (!LFileExists(p)) { LMakePath(p, sizeof(p), ExeName, "help"); LMakePath(p, sizeof(p), p, "index.html"); } if (LFileExists(p)) { LExecute(HelpFile, NULL, ExeName); break; } LTrimDir(ExeName); } break; } case IDM_SHOW_SHORTCUTS: { if (!ShortCuts) ShortCuts = new ShortCutView(this); break; } case IDM_ABOUT: { LAbout Dlg( this, AppName, APP_VER, "\nLgi Resource Editor (lr8 files).", "icon64.png", "http://www.memecode.com/lgi/res", "fret@memecode.com"); break; } default: { int Idx = Cmd - IDM_LANG_BASE; if (Idx >= 0 && Idx < Languages.Length()) { // Deselect the old lang auto Item = ViewMenu ? ViewMenu->ItemAt(CurLang + 2) : 0; if (Item) { Item->Checked(false); } // Set the current CurLang = Idx; // Set the new lang's menu item Item = ViewMenu ? ViewMenu->ItemAt(CurLang + 2) : 0; if (Item) { Item->Checked(true); } // Update everything List res; if (ListObjects(res)) { for (auto r: res) { r->OnShowLanguages(); } } } break; } } return LDocApp::OnCommand(Cmd, Event, Handle); } int AppWnd::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { default: { break; } } return 0; } void AppWnd::FindStrings(List &Strs, char *Define, int *CtrlId) { if (Objs) { List l; if (Objs->ListObjects(l)) { for (auto r: l) { StringList *s = r->GetStrs(); if (s) { for (auto Str: *s) { if (Define && ValidStr(Str->GetDefine())) { if (strcmp(Define, Str->GetDefine()) == 0) { Strs.Insert(Str); continue; } } if (CtrlId) { if (*CtrlId == Str->GetId()) { Strs.Insert(Str); continue; } } } } } } } } int AppWnd::GetUniqueCtrlId() { int Max = 0; if (Objs) { List l; if (Objs->ListObjects(l)) { LHashTbl, int> t; for (auto r: l) { StringList *sl = r->GetStrs(); if (sl) { for (auto s: *sl) { if (s->GetId() > 0 && !t.Find(s->GetId())) { t.Add(s->GetId(), s->GetId()); } Max = MAX(s->GetId(), Max); } } } int i = 500; while (true) { if (t.Find(i)) { i++; } else { return i; } } } } return Max + 1; } int AppWnd::GetUniqueStrRef(int Start) { if (!Objs) return -1; List l; if (!Objs->ListObjects(l)) return -1; LHashTbl, ResString*> Map; LArray Dupes; for (auto r: l) { ResStringGroup *Grp = r->GetStringGroup(); if (Grp) { List::I it = Grp->GetStrs()->begin(); for (ResString *s = *it; s; s = *++it) { if (s->GetRef()) { ResString *Existing = Map.Find(s->GetRef()); if (Existing) { // These get their ref's reset to a unique value as a side // effect of this function... Dupes.Add(s); } else { Map.Add(s->GetRef(), s); } } else { // auto Idx = Grp->GetStrs()->IndexOf(s); LAssert(!"No string ref?"); } } } } for (int i=Start; true; i++) { if (!Map.Find(i)) { if (Dupes.Length()) { ResString *s = Dupes[0]; Dupes.DeleteAt(0); s->SetRef(i); SetDirty(true); } else { return i; } } } return -1; } ResString *AppWnd::GetStrFromRef(int Ref) { ResString *Str = 0; if (Objs) { List l; if (Objs->ListObjects(l)) { for (auto r: l) { ResStringGroup *Grp = dynamic_cast(r); if (Grp) { if ((Str = Grp->FindRef(Ref))) break; } } } } return Str; } ResStringGroup *AppWnd::GetDialogSymbols() { if (Objs) { List l; if (Objs->ListObjects(l)) { for (auto r: l) { ResStringGroup *Grp = dynamic_cast(r); if (Grp) { auto ObjName = Grp->Wnd()->Name(); if (ObjName && stricmp(ObjName, StrDialogSymbols) == 0) { return Grp; } } } } } return NULL; } void AppWnd::OnReceiveFiles(LArray &Files) { auto f = Files.Length() ? Files[0] : 0; if (f) { _OpenFile(f, false); } } void AppWnd::SetStatusText(char *Text, int Pane) { if (Pane >= 0 && Pane < STATUS_MAX && StatusInfo[Pane]) { StatusInfo[Pane]->Name(Text); } } Resource *AppWnd::NewObject(SerialiseContext ctx, LXmlTag *load, int Type, bool Select) { Resource *r = 0; ObjTreeItem *Dir = 0; switch (Type) { case TYPE_CSS: { r = new ResCss(this); Dir = Objs->Style; break; } case TYPE_DIALOG: { r = new ResDialog(this); Dir = Objs->Dialogs; break; } case TYPE_STRING: { r = new ResStringGroup(this); Dir = Objs->Strings; break; } case TYPE_MENU: { r = new ResMenu(this); Dir = Objs->Menus; break; } } if (r) { ObjTreeItem *Item = new ObjTreeItem(r); if (Item) { Dir->Insert(Item); Dir->Update(); Dir->Expanded(true); if (Select) { Objs->Select(Item); } } r->Create(load, &ctx); if (Item) { Item->Update(); } SetDirty(true); } return r; } bool AppWnd::InsertObject(int Type, Resource *r, bool Select) { bool Status = false; if (r) { ObjTreeItem *Dir = 0; switch (Type) { case TYPE_CSS: { Dir = Objs->Style; break; } case TYPE_DIALOG: { Dir = Objs->Dialogs; break; } case TYPE_STRING: { Dir = Objs->Strings; break; } case TYPE_MENU: { Dir = Objs->Menus; break; } } if (Dir) { ObjTreeItem *Item = new ObjTreeItem(r); if (Item) { const char *Name = Item->GetText(); r->Item = Item; Dir->Insert(Item, (Name && Name[0] == '_') ? 0 : -1); Dir->Update(); Dir->Expanded(true); if (Select) { Objs->Select(Item); } Status = true; } } } return Status; } void AppWnd::DelObject(Resource *r) { OnResourceSelect(0); DeleteObj(r); } ObjTreeItem *GetTreeItem(LTreeItem *ti, Resource *r) { for (LTreeItem *i=ti->GetChild(); i; i=i->GetNext()) { ObjTreeItem *o = dynamic_cast(i); if (o) { if (o->GetObj() == r) return o; } o = GetTreeItem(i, r); if (o) return o; } return 0; } ObjTreeItem *GetTreeItem(LTree *ti, Resource *r) { for (LTreeItem *i=ti->GetChild(); i; i=i->GetNext()) { ObjTreeItem *o = dynamic_cast(i); if (o) { if (o->GetObj() == r) return o; } o = GetTreeItem(i, r); if (o) return o; } return 0; } void AppWnd::GotoObject(ResString *s, ResStringGroup *g, ResDialog *d, ResMenuItem *m, ResDialogCtrl *c) { if (s) { Resource *Res = 0; if (g) { Res = g; } else if (d) { Res = d; } else if (m) { Res = m->GetMenu(); } if (Res) { ObjTreeItem *ti = GetTreeItem(Objs, Res); if (ti) { ti->Select(true); if (g) { s->GetList()->Select(0); s->ScrollTo(); LYield(); s->Select(true); } else if (d) { LYield(); d->SelectCtrl(c); } else if (m) { for (LTreeItem *i=m; i; i=i->GetParent()) { i->Expanded(true); } m->Select(true); m->ScrollTo(); } } else { printf("%s:%i - couldn't find resources tree item\n", _FL); } } } } bool AppWnd::ListObjects(List &Lst) { if (Objs) { return Objs->ListObjects(Lst); } return false; } void AppWnd::OnObjChange(FieldSource *r) { if (Fields) { Fields->Serialize(false); SetDirty(true); } } void AppWnd::OnObjSelect(FieldSource *r) { if (Fields) Fields->OnSelect(r); } void AppWnd::OnObjDelete(FieldSource *r) { if (Fields) { Fields->OnDelete(r); } } void AppWnd::OnResourceDelete(Resource *r) { } void AppWnd::OnResourceSelect(Resource *r) { if (LastRes) { OnObjSelect(NULL); if (ContentView) { ContentView->Detach(); DeleteObj(ContentView); } LastRes = NULL; } if (r) { ContentView = r->CreateUI(); if (ContentView) { if (HBox) ContentView->Attach(HBox); LastRes = r; } } } char *TagName(LXmlTag *t) { static char Buf[1024]; LArray Tags; for (; t; t = t->Parent) { Tags.AddAt(0, t); } Buf[0] = 0; for (int i=0; iGetTag()); } return Buf; } class ResCompare : public LWindow, public LResourceLoad { LList *Lst; public: ResCompare(const char *File1, const char *File2) { Lst = 0; LRect p; LAutoString n; if (LoadFromResource(IDD_COMPARE, this, &p, &n)) { SetPos(p); Name(n); MoveToCenter(); GetViewById(IDC_DIFFS, Lst); if (Attach(0)) { Visible(true); AttachChildren(); LXmlTag *t1 = new LXmlTag; LXmlTag *t2 = new LXmlTag; if (t1 && File1) { LFile f; if (f.Open(File1, O_READ)) { LXmlTree x(GXT_NO_ENTITIES); if (!x.Read(t1, &f, 0)) { DeleteObj(t1); } } else { DeleteObj(t1); } } if (t2 && File2) { LFile f; if (f.Open(File2, O_READ)) { LXmlTree x(GXT_NO_ENTITIES); if (!x.Read(t2, &f, 0)) { DeleteObj(t2); } } else { DeleteObj(t2); } } if (Lst && t1 && t2) { Lst->Enabled(false); Compare(t1, t2); Lst->Enabled(true); } DeleteObj(t1); DeleteObj(t2); } } } void Compare(LXmlTag *t1, LXmlTag *t2) { char s[1024]; if (stricmp(t1->GetTag(), t2->GetTag()) != 0) { sprintf(s, "Different Tag: '%s' <-> '%s'", t1->GetTag(), t2->GetTag()); LListItem *i = new LListItem; if (i) { i->SetText(s); i->SetText(TagName(t1), 1); Lst->Insert(i); } } LHashTbl,LXmlAttr*> a; for (int i=0; iAttr.Length(); i++) { LXmlAttr *a1 = &t1->Attr[i]; a.Add(a1->GetName(), a1); } for (int n=0; nAttr.Length(); n++) { LXmlAttr *a2 = &t2->Attr[n]; LXmlAttr *a1 = (LXmlAttr*) a.Find(a2->GetName()); if (a1) { if (strcmp(a1->GetValue(), a2->GetValue()) != 0) { sprintf(s, "Different Attr Value: '%s' <-> '%s'", a1->GetValue(), a2->GetValue()); LListItem *i = new LListItem; if (i) { i->SetText(s); sprintf(s, "%s.%s", TagName(t1), a1->GetName()); i->SetText(s, 1); Lst->Insert(i); } } a.Delete(a2->GetName()); } else { sprintf(s, "[Right] Missing Attr: '%s' = '%s'", a2->GetName(), a2->GetValue()); LListItem *i = new LListItem; if (i) { i->SetText(s); i->SetText(TagName(t1), 1); Lst->Insert(i); } } } // char *Key; // for (void *v = a.First(&Key); v; v = a.Next(&Key)) for (auto v : a) { LXmlAttr *a1 = v.value; sprintf(s, "[Left] Missing Attr: '%s' = '%s'", a1->GetName(), a1->GetValue()); LListItem *i = new LListItem; if (i) { i->SetText(s); i->SetText(TagName(t1), 1); Lst->Insert(i); } } if (t1->IsTag("string-group")) { LArray r1, r2; for (auto t: t1->Children) { char *Ref; if ((Ref = t->GetAttr("ref"))) { int r = atoi(Ref); if (r) { r1[r] = t; } } } for (auto t: t2->Children) { char *Ref; if ((Ref = t->GetAttr("ref"))) { int r = atoi(Ref); if (r) { r2[r] = t; } } } auto Max = MAX(r1.Length(), r2.Length()); for (int i = 0; iGetAttr("ref"), r1[i]->GetAttr("Define")); LListItem *n = new LListItem; if (n) { n->SetText(s); n->SetText(TagName(r1[i]), 1); Lst->Insert(n); } } else if (r2[i]) { sprintf(s, "[Left] Missing String: Ref=%s, Def=%s", r2[i]->GetAttr("ref"), r2[i]->GetAttr("Define")); LListItem *n = new LListItem; if (n) { n->SetText(s); n->SetText(TagName(r2[i]), 1); Lst->Insert(n); } } } } else { LXmlTag *c1 = t1->Children[0]; LXmlTag *c2 = t2->Children[0]; while (c1 && c2) { Compare(c1, c2); c1 = t1->Children[0]; c2 = t2->Children[0]; } } LYield(); } void OnPosChange() { LRect c = GetClient(); if (Lst) { c.Inset(7, 7); Lst->SetPos(c); } } }; void AppWnd::Compare() { auto s = new LFileSelect(this); s->Type("Lgi Resource", "*.lr8"); s->Open([&](auto dlg, auto status) { if (status) new ResCompare(GetCurFile(), dlg->Name()); delete dlg; }); } void AppWnd::ImportLang() { // open dialog auto Select = new LFileSelect(this); Select->Type("Lgi Resources", "*.lr8;*.xml"); Select->Open([&](auto dlg, auto status) { if (status) { LFile F; if (F.Open(dlg->Name(), O_READ)) { SerialiseContext Ctx; Ctx.Format = GetFormat(dlg->Name()); // convert file to Xml objects LXmlTag *Root = new LXmlTag; if (Root) { LXmlTree Tree(GXT_NO_ENTITIES); if (Tree.Read(Root, &F, 0)) { List Menus; List Groups; for (auto t: Root->Children) { if (t->IsTag("menu")) { ResMenu *Menu = new ResMenu(this); if (Menu && Menu->Read(t, Ctx)) { Menus.Insert(Menu); } else break; } else if (t->IsTag("string-group")) { ResStringGroup *g = new ResStringGroup(this); if (g && g->Read(t, Ctx)) { Groups.Insert(g); } else break; } } Ctx.PostLoad(this); bool HasData = false; for (auto g: Groups) { g->SetLanguages(); if (g->GetStrs()->Length() > 0 && g->GetLanguages() > 0) { HasData = true; } } if (HasData) { List Langs; for (auto g: Groups) { for (int i=0; iGetLanguages(); i++) { LLanguage *Lang = g->GetLanguage(i); if (Lang) { bool Has = false; for (auto l: Langs) { if (stricmp((char*)l, (char*)Lang) == 0) { Has = true; break; } } if (!Has) { Langs.Insert(Lang); } } } } auto Dlg = new LangDlg(this, Langs); Dlg->DoModal([&](auto dlg, auto id) { if (id == IDOK && Dlg->Lang) { LStringPipe Errors; int Matches = 0; int NotFound = 0; int Imported = 0; int Different = 0; for (auto g: Groups) { List::I Strings = g->GetStrs()->begin(); for (ResString *s=*Strings; s; s=*++Strings) { ResString *d = GetStrFromRef(s->GetRef()); if (d) { Matches++; char *Str = s->Get(Dlg->Lang->Id); char *Dst = d->Get(Dlg->Lang->Id); if ( ( Str && Dst && strcmp(Dst, Str) != 0 ) || ( (Str != 0) ^ (Dst != 0) ) ) { Different++; d->Set(Str, Dlg->Lang->Id); Imported++; } } else { NotFound++; char e[256]; sprintf(e, "String ref=%i (%s)\n", s->GetRef(), s->GetDefine()); Errors.Push(e); } } } List Lst; if (ListObjects(Lst)) { for (auto m: Menus) { // find matching menu in our list ResMenu *Match = 0; for (auto r: Lst) { ResMenu *n = dynamic_cast(r); if (n && stricmp(n->Name(), m->Name()) == 0) { Match = n; break; } } if (Match) { // match strings List *Src = m->GetStrs(); List *Dst = Match->GetStrs(); for (auto s: *Src) { bool FoundRef = false; for (auto d: *Dst) { if (s->GetRef() == d->GetRef()) { FoundRef = true; char *Str = s->Get(Dlg->Lang->Id); if (Str) { char *Dst = d->Get(Dlg->Lang->Id); if (!Dst || strcmp(Dst, Str)) { Different++; } d->Set(Str, Dlg->Lang->Id); Imported++; } break; } } if (!FoundRef) { NotFound++; char e[256]; sprintf(e, "MenuString ref=%i (%s)\n", s->GetRef(), s->GetDefine()); Errors.Push(e); } } Match->SetLanguages(); } } for (auto r: Lst) { ResStringGroup *StrRes = dynamic_cast(r); if (StrRes) { StrRes->SetLanguages(); } } } char *ErrorStr = Errors.NewStr(); LgiMsg( this, "Imported: %i\n" "Matched: %i\n" "Not matched: %i\n" "Different: %i\n" "Total: %i\n" "\n" "Import complete.\n" "\n%s", AppName, MB_OK, Imported, Matches, NotFound, Different, Matches + NotFound, ErrorStr?ErrorStr:(char*)""); } delete dlg; }); } else { LgiMsg(this, "No language information to import", AppName, MB_OK); } // Groups.DeleteObjects(); // Menus.DeleteObjects(); } else { LgiMsg(this, "Failed to parse XML from file.\nError: %s", AppName, MB_OK, Tree.GetErrorMsg()); } DeleteObj(Root); } } } delete dlg; }); } bool AppWnd::Empty() { // Delete any existing objects List l; if (ListObjects(l)) { for (auto It = l.begin(); It != l.end(); ) { auto r = *It; if (r->SystemObject()) l.Delete(It); else It++; } for (auto r: l) { DelObject(r); } } return true; } bool AppWnd::OpenFile(const char *FileName, bool Ro) { if (stristr(FileName, ".lr8") || stristr(FileName, ".xml")) { return LoadLgi(FileName); } else if (stristr(FileName, ".rc")) { LoadWin32(FileName); return true; } return false; } bool AppWnd::SaveFile(const char *FileName) { if (stristr(FileName, ".lr8") || stristr(FileName, ".xml")) { return SaveLgi(FileName); } else if (stristr(FileName, ".rc")) { } return false; } void AppWnd::GetFileTypes(LFileSelect *Dlg, bool Write) { Dlg->Type("Lgi Resources", "*.lr8;*.xml"); if (!Write) { Dlg->Type("All Files", LGI_ALL_FILES); } } // Lgi load/save bool AppWnd::TestLgi(bool Quite) { bool Status = true; List l; if (ListObjects(l)) { ErrorCollection Errors; for (auto r: l) { Status &= r->Test(&Errors); } if (Errors.StrErr.Length() > 0) { LStringPipe Sample; for (int i=0; iGetRef(), s->GetDefine(), Errors.StrErr[i].Msg.Get()); } char *Sam = Sample.NewStr(); LgiMsg(this, "%i strings have errors.\n\n%s", AppName, MB_OK, Errors.StrErr.Length(), Sam); DeleteArray(Sam); } else if (!Quite) { LgiMsg(this, "Object are all ok.", AppName); } } return Status; } bool AppWnd::LoadLgi(const char *FileName) { bool Status = false; Empty(); if (FileName) { // ResFileFormat Format = GetFormat(FileName); LFile f; if (f.Open(FileName, O_READ)) { LProgressDlg Progress(this); Progress.SetDescription("Initializing..."); Progress.SetType("Tags"); LXmlTag *Root = new LXmlTag; if (Root) { // convert file to Xml objects LXmlTree Xml(0); Progress.SetDescription("Lexing..."); if (Xml.Read(Root, &f, 0)) { Progress.SetRange(Root->Children.Length()); // convert Xml list into objects int i=0; DoEvery Timer(500); SerialiseContext Ctx; for (auto t: Root->Children) { if (Timer.DoNow()) { Progress.Value(Root->Children.IndexOf(t)); LYield(); } int RType = 0; if (t->IsTag("dialog")) { RType = TYPE_DIALOG; } else if (t->IsTag("string-group")) { RType = TYPE_STRING; } else if (t->IsTag("menu")) { RType = TYPE_MENU; } else if (t->IsTag("style")) { RType = TYPE_CSS; } else { LAssert(!"Unexpected tag"); } if (RType > 0) { NewObject(Ctx, t, RType, false); } i++; } Ctx.PostLoad(this); SortDialogs(); TestLgi(); // Scan for languages and update the view lang menu Languages.Length(0); LHashTbl, LLanguage*> Langs; if (ViewMenu) { // Remove existing language menu items while (ViewMenu->RemoveItem(1)); ViewMenu->AppendSeparator(); // Enumerate all languages List res; if (ListObjects(res)) { for (auto r: res) { ResStringGroup *Sg = r->IsStringGroup(); if (Sg) { for (int i=0; iGetLanguages(); i++) { LLanguage *Lang = Sg->GetLanguage(i); if (Lang) { Langs.Add(Lang->Id, Lang); } } } } } // Update languages array int n = 0; for (auto i : Langs) { Languages.Add(i.value); auto Item = ViewMenu->AppendItem(i.value->Name, IDM_LANG_BASE + n, true); if (Item && i.value->IsEnglish()) { Item->Checked(true); CurLang = n; } n++; } if (Languages.Length() == 0) { ViewMenu->AppendItem("(none)", -1, false); } } Status = true; } else { LgiMsg(this, "Xml read failed: %s", AppName, MB_OK, Xml.GetErrorMsg()); } DeleteObj(Root); } } } return Status; } void SerialiseContext::PostLoad(AppWnd *App) { for (int i=0; iGetUniqueCtrlId(); s->SetId(Id); Log.Print("Repaired CtrlId of string ref %i to %i\n", s->GetRef(), Id); } LAutoString a(Log.NewStr()); if (ValidStr(a)) { LgiMsg(App, "%s", "Load Warnings", MB_OK, a.Get()); } } int DialogNameCompare(ResDialog *a, ResDialog *b, NativeInt Data) { const char *A = (a)?a->Name():0; const char *B = (b)?b->Name():0; if (A && B) return stricmp(A, B); return -1; } void AppWnd::SortDialogs() { List Lst; if (ListObjects(Lst)) { List Dlgs; for (auto r: Lst) { ResDialog *Dlg = dynamic_cast(r); if (Dlg) { Dlgs.Insert(Dlg); Dlg->Item->Remove(); } } Dlgs.Sort(DialogNameCompare); for (auto d: Dlgs) { Objs->Dialogs->Insert(d->Item); } } } class ResTreeNode { public: char *Str; ResTreeNode *a, *b; ResTreeNode(char *s) { a = b = 0; Str = s; } ~ResTreeNode() { DeleteArray(Str); DeleteObj(a); DeleteObj(b); } void Enum(List &l) { if (a) { a->Enum(l); } if (Str) { l.Insert(Str); } if (b) { b->Enum(l); } } bool Add(char *s) { int Comp = (Str && s) ? stricmp(Str, s) : -1; if (Comp == 0) { return false; } if (Comp < 0) { if (a) { return a->Add(s); } else { a = new ResTreeNode(s); } } if (Comp > 0) { if (b) { return b->Add(s); } else { b = new ResTreeNode(s); } } return true; } }; class ResTree { ResTreeNode *Root; public: ResTree() { Root = 0; } ~ResTree() { DeleteObj(Root); } bool Add(char *s) { if (s) { if (!Root) { Root = new ResTreeNode(NewStr(s)); return true; } else { return Root->Add(NewStr(s)); } } return false; } void Enum(List &l) { if (Root) { Root->Enum(l); } } }; const char *HeaderStr = "// This file generated by LgiRes\r\n\r\n"; struct DefinePair { char *Name; int Value; }; int PairCmp(DefinePair *a, DefinePair *b) { return a->Value - b->Value; } bool AppWnd::WriteDefines(LStream &Defs) { bool Status = false; ResTree Tree; // Empty file Defs.Write(HeaderStr, strlen(HeaderStr)); // make a unique list of #define's List Lst; if (ListObjects(Lst)) { LHashTbl,int> Def; LHashTbl,char*> Ident; for (auto r: Lst) { List *StrList = r->GetStrs(); if (StrList) { Status = true; List::I sl = StrList->begin(); for (ResString *s = *sl; s; s = *++sl) { if (ValidStr(s->GetDefine())) { if (stricmp(s->GetDefine(), "IDOK") == 0) { s->SetId(IDOK); } else if (stricmp(s->GetDefine(), "IDCANCEL") == 0) { s->SetId(IDCANCEL); } else if (stricmp(s->GetDefine(), "IDC_STATIC") == 0) { s->SetId(-1); } else if (stricmp(s->GetDefine(), "-1") == 0) { s->SetDefine(0); } else { // Remove dupe ID's char IdStr[32]; sprintf(IdStr, "%i", s->GetId()); char *Define; if ((Define = Ident.Find(IdStr))) { if (strcmp(Define, s->GetDefine())) { List n; FindStrings(n, s->GetDefine()); int NewId = GetUniqueCtrlId(); for (auto Ns: n) { Ns->SetId(NewId); } } } else { Ident.Add(IdStr, s->GetDefine()); } // Make all define's the same int CtrlId; if ((CtrlId = Def.Find(s->GetDefine()))) { // Already there... s->SetId(CtrlId); } else { // Add... LAssert(s->GetId()); if (s->GetId()) Def.Add(s->GetDefine(), s->GetId()); } } } } } } // write the list out LArray Pairs; // char *s = 0; // for (int i = Def.First(&s); i; i = Def.Next(&s)) for (auto i : Def) { if (ValidStr(i.key) && stricmp(i.key, "IDOK") != 0 && stricmp(i.key, "IDCANCEL") != 0 && stricmp(i.key, "IDC_STATIC") != 0 && stricmp(i.key, "-1") != 0) { DefinePair &p = Pairs.New(); p.Name = i.key; p.Value = i.value; } } Pairs.Sort(PairCmp); for (int n=0; n=' ' && (uint8_t)(c) <= 127) if (IsPrintable(s[0]) && IsPrintable(s[1]) && IsPrintable(s[2]) && IsPrintable(s[3])) { #ifndef __BIG_ENDIAN__ int32 i = LgiSwap32(p.Value); memcpy(s, &i, 4); #endif Defs.Print("#define %s%s'%04.4s'\r\n", p.Name, Tab, s); } else Defs.Print("#define %s%s%i\r\n", p.Name, Tab, p.Value); } } return Status; } bool AppWnd::SaveLgi(const char *FileName) { bool Status = false; if (!TestLgi()) { if (LgiMsg(this, "Do you want to save the file with errors?", AppName, MB_YESNO) == IDNO) return false; } // Rename the existing file to 'xxxxxx.bak' if (LFileExists(FileName)) { char Bak[MAX_PATH_LEN]; strcpy_s(Bak, sizeof(Bak), FileName); char *e = LGetExtension(Bak); if (e) { strcpy(e, "bak"); if (LFileExists(Bak)) FileDev->Delete(Bak, false); FileDev->Move(FileName, Bak); } } // Save the file to xml if (FileName) { LFile f; LFile::Path DefsName = FileName; DefsName += "../resdefs.h"; LStringPipe Defs; if (f.Open(FileName, O_WRITE)) { SerialiseContext Ctx; f.SetSize(0); Defs.SetSize(0); Defs.Print("// Generated by LgiRes\r\n\r\n"); List l; if (ListObjects(l)) { // Remove all duplicate symbol Id's from the dialogs for (auto r: l) { ResDialog *Dlg = dynamic_cast(r); if (Dlg) Dlg->CleanSymbols(); } // write defines WriteDefines(Defs); LXmlTag Root("resources"); // Write all string lists out first so that when we load objects // back in again the strings will already be loaded and can // be referenced for (auto r: l) { if (r->Type() == TYPE_STRING) { LXmlTag *c = new LXmlTag; if (c && r->Write(c, Ctx)) { Root.InsertTag(c); } else { LAssert(0); DeleteObj(c); } } } // now write the rest of the objects out for (auto r: l) { if (r->Type() != TYPE_STRING) { LXmlTag *c = new LXmlTag; if (c && r->Write(c, Ctx)) { Root.InsertTag(c); } else { r->Write(c, Ctx); LAssert(0); DeleteObj(c); } } } // Set the offset type. // // Older versions of LgiRes stored the dialog's controls at a fixed // offset (3,17) from where they should've been. That was fixed, but // to differentiate between the 2 systems, we store a tag at the // root element. Root.SetAttr("Offset", 1); LXmlTree Tree(GXT_NO_ENTITIES); Status = Tree.Write(&Root, &f); if (Status) { // Also write the header... but only if it's changed... auto DefsContent = Defs.NewGStr(); LAutoString OldDefsContent(LReadTextFile(DefsName)); if (Strcmp(DefsContent.Get(), OldDefsContent.Get())) { LFile DefsFile; if (!DefsFile.Open(DefsName, O_WRITE)) goto FileErrorMsg; DefsFile.SetSize(0); DefsFile.Write(DefsContent); } } } } else { FileErrorMsg: LgiMsg(this, "Couldn't open these files for output:\n" "\t%s\n" "\t%s\n" "\n" "Maybe they are read only or locked by another application.", AppName, MB_OK, FileName, DefsName.GetFull().Get()); } } return Status; } // Win32 load/save #define ADJUST_CTRLS_X 2 #define ADJUST_CTRLS_Y 12 #define IMP_MODE_SEARCH 0 #define IMP_MODE_DIALOG 1 #define IMP_MODE_DLG_CTRLS 2 #define IMP_MODE_STRINGS 3 #define IMP_MODE_MENU 4 #include "lgi/common/Token.h" class ImportDefine { public: char *Name; char *Value; ImportDefine() { Name = Value = 0; } ImportDefine(char *Line) { Name = Value = 0; if (Line && *Line == '#') { Line++; if (strnicmp(Line, "define", 6) == 0) { Line += 6; Line = LSkipDelim(Line); char *Start = Line; const char *WhiteSpace = " \r\n\t"; while (*Line && !strchr(WhiteSpace, *Line)) { Line++; } Name = NewStr(Start, Line-Start); Line = LSkipDelim(Line); Start = Line; while (*Line && !strchr(WhiteSpace, *Line)) { Line++; } if (Start != Line) { Value = NewStr(Start, Line-Start); } } } } ~ImportDefine() { DeleteArray(Name); DeleteArray(Value); } }; class DefineList : public List { int NestLevel; public: bool Defined; List IncludeDirs; DefineList() { Defined = true; NestLevel = 0; } ~DefineList() { for (auto i: *this) { DeleteObj(i); } for (auto c: IncludeDirs) { DeleteArray(c); } } void DefineSymbol(const char *Name, const char *Value = 0) { ImportDefine *Def = new ImportDefine; if (Def) { Def->Name = NewStr(Name); if (Value) Def->Value = NewStr(Value); Insert(Def); } } ImportDefine *GetDefine(char *Name) { if (Name) { for (auto i: *this) { if (i->Name && stricmp(i->Name, Name) == 0) { return i; } } } return NULL; } void ProcessLine(char *Line) { if (NestLevel > 16) { return; } if (Line && *Line == '#') { Line++; LToken T(Line); if (T.Length() > 0) { if (stricmp(T[0], "define") == 0) // #define { ImportDefine *Def = new ImportDefine(Line-1); if (Def) { if (Def->Name) { Insert(Def); } else { DeleteObj(Def); } } } else if (stricmp(T[0], "include") == 0) // #include { NestLevel++; LFile F; if (T.Length() > 1) { for (auto IncPath: IncludeDirs) { char FullPath[256]; strcpy(FullPath, IncPath); if (FullPath[strlen(FullPath)-1] != DIR_CHAR) { strcat(FullPath, DIR_STR); } strcat(FullPath, T[1]); if (F.Open(FullPath, O_READ)) { char Line[1024]; while (!F.Eof()) { F.ReadStr(Line, sizeof(Line)); char *p = LSkipDelim(Line); if (*p == '#') { ProcessLine(p); } } break; } } } NestLevel--; } else if (stricmp(T[0], "if") == 0) // #if { } else if (stricmp(T[0], "ifdef") == 0) // #if { if (T.Length() > 1) { Defined = GetDefine(T[1]) != 0; } } else if (stricmp(T[0], "endif") == 0) // #endif { Defined = true; } else if (stricmp(T[0], "pragma") == 0) { ImportDefine *Def = new ImportDefine; if (Def) { char *Str = Line + 7; char *First = strchr(Str, '('); char *Second = (First) ? strchr(First+1, ')') : 0; if (First && Second) { Insert(Def); Def->Name = NewStr(Str, First-Str); First++; Def->Value = NewStr(First, Second-First); } else { DeleteObj(Def); } } } else if (stricmp(T[0], "undef") == 0) { } } } // else it's not for us anyway } }; void TokLine(LArray &T, char *Line) { if (Line) { // Exclude comments for (int k=0; Line[k]; k++) { if (Line[k] == '/' && Line[k+1] == '/') { Line[k] = 0; break; } } // Break into tokens for (const char *s = Line; s && *s; ) { while (*s && strchr(" \t", *s)) s++; char *t = LTokStr(s); if (t) T.Add(t); else break; } } } void AppWnd::LoadWin32(const char *FileName) { bool Status = false; LHashTbl,bool> CtrlNames; CtrlNames.Add("LTEXT", true); CtrlNames.Add("EDITTEXT", true); CtrlNames.Add("COMBOBOX", true); CtrlNames.Add("SCROLLBAR", true); CtrlNames.Add("GROUPBOX", true); CtrlNames.Add("PUSHBUTTON", true); CtrlNames.Add("DEFPUSHBUTTON", true); CtrlNames.Add("CONTROL", true); CtrlNames.Add("ICON", true); CtrlNames.Add("LISTBOX", true); Empty(); auto Load = [&](const char *FileName) { if (!FileName) return; LProgressDlg Progress(this); Progress.SetDescription("Initializing..."); Progress.SetType("K"); Progress.SetScale(1.0/1024.0); char *FileTxt = LReadTextFile(FileName); if (FileTxt) { LToken Lines(FileTxt, "\r\n"); DeleteArray(FileTxt); DefineList Defines; ResStringGroup *String = new ResStringGroup(this); int Mode = IMP_MODE_SEARCH; // Language char *Language = 0; LLanguageId LanguageId = 0; // Dialogs List DlLList; CtrlDlg *Dlg = 0; // Menus ResDialog *Dialog = 0; ResMenu *Menu = 0; List Menus; ResMenuItem *MenuItem[32]; int MenuLevel = 0; bool MenuNewLang = false; int MenuNextItem = 0; // Include defines char IncPath[256]; strcpy(IncPath, FileName); LTrimDir(IncPath); Defines.IncludeDirs.Insert(NewStr(IncPath)); Defines.DefineSymbol("_WIN32"); Defines.DefineSymbol("IDC_STATIC", "-1"); DoEvery Ticker(200); Progress.SetDescription("Reading resources..."); Progress.SetRange(Lines.Length()); if (String) { InsertObject(TYPE_STRING, String, false); } for (int CurLine = 0; CurLine < Lines.Length(); CurLine++) { if (Ticker.DoNow()) { Progress.Value(CurLine); LYield(); } // Skip white space char *Line = Lines[CurLine]; char *p = LSkipDelim(Line); Defines.ProcessLine(Line); // Tokenize LArray T; TokLine(T, Line); // Process line if (Defines.Defined) { switch (Mode) { case IMP_MODE_SEARCH: { DeleteObj(Dialog); Dlg = 0; if (*p != '#') { if (T.Length() > 1 && (stricmp(T[1], "DIALOG") == 0 || stricmp(T[1], "DIALOGEX") == 0)) { Mode = IMP_MODE_DIALOG; Dialog = new ResDialog(this); if (Dialog) { Dialog->Create(NULL, NULL); Dialog->Name(T[0]); auto It = Dialog->IterateViews(); Dlg = dynamic_cast(It[0]); if (Dlg) { int Pos[4] = {0, 0, 0, 0}; int i = 0; for (; iResDialogCtrl::SetPos(r); Dlg->GetStr()->SetDefine(T[0]); ImportDefine *Def = Defines.GetDefine(T[0]); if (Def) { Dlg->GetStr()->SetId(atoi(Def->Value)); } } } break; } if (T.Length() > 1 && stricmp(T[1], "MENU") == 0) { ZeroObj(MenuItem); Mode = IMP_MODE_MENU; Menu = 0; // Check for preexisting menu in another language MenuNewLang = false; for (auto m: Menus) { if (stricmp(m->Name(), T[0]) == 0) { MenuNewLang = true; Menu = m; break; } } // If it doesn't preexist then create it if (!Menu) { Menu = new ResMenu(this); if (Menu) { Menus.Insert(Menu); Menu->Create(NULL, NULL); Menu->Name(T[0]); } } break; } if (T.Length() > 0 && stricmp(T[0], "STRINGTABLE") == 0) { Mode = IMP_MODE_STRINGS; if (String) { String->Name("_Win32 Imports_"); } break; } if (T.Length() > 2 && stricmp(T[0], "LANGUAGE") == 0) { LanguageId = 0; DeleteArray(Language); char *Language = NewStr(T[1]); if (Language) { LLanguage *Info = LFindLang(0, Language); if (Info) { LanguageId = Info->Id; ResDialog::AddLanguage(Info->Id); } } break; } } break; } case IMP_MODE_DIALOG: { if (T.Length() > 0 && Dlg) { if (stricmp(T[0], "CAPTION") == 0) { char *Caption = T[1]; if (Caption) { Dlg->GetStr()->Set(Caption, LanguageId); } } else if (stricmp(T[0], "BEGIN") == 0) { Mode = IMP_MODE_DLG_CTRLS; } } break; } case IMP_MODE_DLG_CTRLS: { char *Type = T[0]; if (!Type) break; if (stricmp(Type, "end") != 0) { // Add wrapped content to the token array. char *Next = Lines[CurLine+1]; if (Next) { Next = LSkipDelim(Next); char *NextTok = LTokStr((const char*&)Next); if (NextTok) { if (stricmp(NextTok, "END") != 0 && !CtrlNames.Find(NextTok)) { TokLine(T, Lines[++CurLine]); } DeleteArray(NextTok); } } } // Process controls if (stricmp(Type, "LTEXT") == 0) { if (T.Length() >= 7) { CtrlText *Ctrl = new CtrlText(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->Set(T[1], LanguageId); Ctrl->GetStr()->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "EDITTEXT") == 0) { if (T.Length() >= 7) { CtrlEditbox *Ctrl = new CtrlEditbox(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "COMBOBOX") == 0) { if (T.Length() >= 6) { CtrlComboBox *Ctrl = new CtrlComboBox(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "SCROLLBAR") == 0) { if (T.Length() == 6) { CtrlScrollBar *Ctrl = new CtrlScrollBar(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "GROUPBOX") == 0) { if (T.Length() >= 7) { CtrlGroup *Ctrl = new CtrlGroup(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->Set(T[1], LanguageId); Ctrl->GetStr()->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "PUSHBUTTON") == 0 || stricmp(Type, "DEFPUSHBUTTON") == 0) { if (T.Length() >= 7) { CtrlButton *Ctrl = new CtrlButton(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->Set(T[1], LanguageId); Ctrl->GetStr()->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "CONTROL") == 0) { if (T.Length() >= 7) { char *Caption = T[1]; char *Id = T[2]; char *Type = T[3]; bool Checkbox = false; bool Radio = false; bool Done = false; // loop through styles int i; for (i=4; !Done && iSetPos(r); if (Caption) Ctrl->GetStr()->Set(Caption, LanguageId); if (Id) Ctrl->GetStr()->SetDefine(Id); ImportDefine *Def = Defines.GetDefine(Id); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } Dlg->AddView(Ctrl->View()); } } } } else if (stricmp(Type, "ICON") == 0) { if (T.Length() >= 7) { CtrlBitmap *Ctrl = new CtrlBitmap(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl->View()); } } } else if (stricmp(Type, "LISTBOX") == 0) { if (T.Length() >= 7) { CtrlList *Ctrl = new CtrlList(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "END") == 0) { // search for an existing dialog resource in // another language ResDialog *Match = 0; CtrlDlg *MatchObj = 0; for (auto d: DlLList) { auto It = d->IterateViews(); LViewI *Wnd = It[0]; if (Wnd) { CtrlDlg *Obj = dynamic_cast(Wnd); if (Obj) { if (Obj->GetStr()->GetId() == Dlg->GetStr()->GetId()) { MatchObj = Obj; Match = d; break; } } } } if (Match) { // Merge the controls from "Dlg" to "MatchObj" List Old; List New; Dlg->ListChildren(New); MatchObj->ListChildren(Old); // add the language strings for the caption // without clobbering the languages already // present for (auto s: Dlg->GetStr()->Items) { if (!MatchObj->GetStr()->Get(s->GetLang())) { MatchObj->GetStr()->Set(s->GetStr(), s->GetLang()); } } for (auto c: New) { ResDialogCtrl *MatchCtrl = 0; // try matching by Id { for (auto Mc: Old) { if (Mc->GetStr()->GetId() == c->GetStr()->GetId() && Mc->GetStr()->GetId() > 0) { MatchCtrl = Mc; break; } } } // ok no Id match, match by location and type if (!MatchCtrl) { List Overlapping; for (auto Mc: Old) { LRect a = Mc->View()->GetPos(); LRect b = c->View()->GetPos(); LRect c = a; c.Bound(&b); if (c.Valid()) { int Sa = a.X() * a.Y(); int Sb = b.X() * b.Y(); int Sc = c.X() * c.Y(); int Total = Sa + Sb - Sc; double Amount = (double) Sc / (double) Total; if (Amount > 0.5) { // mostly similar in size Overlapping.Insert(Mc); } } } if (Overlapping.Length() == 1) { MatchCtrl = Overlapping[0]; } } if (MatchCtrl) { // woohoo we are cool for (auto s: c->GetStr()->Items) { MatchCtrl->GetStr()->Set(s->GetStr(), s->GetLang()); } } } // Delete the duplicate OnObjSelect(0); DeleteObj(Dialog); } else { // Insert the dialog InsertObject(TYPE_DIALOG, Dialog, false); DlLList.Insert(Dialog); } Dialog = 0; Dlg = 0; Status = true; Mode = IMP_MODE_SEARCH; } break; } case IMP_MODE_STRINGS: { if (stricmp(T[0], "BEGIN") == 0) { } else if (stricmp(T[0], "END") == 0) { Status = true; Mode = IMP_MODE_SEARCH; } else { if (T.Length() > 1) { ResString *Str = String->FindName(T[0]); if (!Str) { Str = String->CreateStr(); if (Str) { ImportDefine *Def = Defines.GetDefine(T[0]); if (Def) { Str->SetId(atoi(Def->Value)); } Str->SetDefine(T[0]); Str->UnDuplicate(); } } if (Str) { // get the language LLanguage *Lang = LFindLang(Language); LLanguageId SLang = (Lang) ? Lang->Id : (char*)"en"; StrLang *s = 0; // look for language present in string object for (auto ss: Str->Items) { if (*ss == SLang) { s = ss; break; } } // if not present then add it if (!s) { s = new StrLang; if (s) { Str->Items.Insert(s); s->SetLang(SLang); } } // set the value if (s) { s->SetStr(T[1]); } } } } break; } case IMP_MODE_MENU: { if (T.Length() >= 1) { if (stricmp(T[0], "BEGIN") == 0) { MenuLevel++; } else if (stricmp(T[0], "END") == 0) { MenuLevel--; if (MenuLevel == 0) { Status = true; Mode = IMP_MODE_SEARCH; Menu->SetLanguages(); if (!MenuNewLang) { InsertObject(TYPE_MENU, Menu, false); } Menu = 0; } } else { ResMenuItem *i = 0; char *Text = T[1]; if (Text) { if (stricmp(T[0], "POPUP") == 0) { if (MenuNewLang) { LTreeItem *Ri = 0; if (MenuItem[MenuLevel]) { Ri = MenuItem[MenuLevel]->GetNext(); } else { if (MenuLevel == 1) { Ri = Menu->ItemAt(0); } else if (MenuItem[MenuLevel-1]) { Ri = MenuItem[MenuLevel-1]->GetChild(); } } if (Ri) { // Seek up to the next submenu while (!Ri->GetChild()) { Ri = Ri->GetNext(); } i = dynamic_cast(Ri); // char *si = i->Str.Get("en"); if (i) { MenuItem[MenuLevel] = i; } } } else { MenuItem[MenuLevel] = i = new ResMenuItem(Menu); } if (i) { LLanguage *Lang = LFindLang(Language); i->GetStr()->Set(Text, (Lang) ? Lang->Id : (char*)"en"); } MenuNextItem = 0; } else if (stricmp(T[0], "MENUITEM") == 0) { if (MenuNewLang) { if (MenuItem[MenuLevel-1] && T.Length() > 2) { ImportDefine *id = Defines.GetDefine(T[2]); if (id) { int Id = atoi(id->Value); int n = 0; for (LTreeItem *o = MenuItem[MenuLevel-1]->GetChild(); o; o = o->GetNext(), n++) { ResMenuItem *Res = dynamic_cast(o); if (Res && Res->GetStr()->GetId() == Id) { i = Res; break; } } } } MenuNextItem++; } else { i = new ResMenuItem(Menu); } if (i) { if (stricmp(Text, "SEPARATOR") == 0) { // Set separator i->Separator(true); } else { if (!MenuNewLang) { // Set Id i->GetStr()->SetDefine(T[2]); if (i->GetStr()->GetDefine()) { ImportDefine *id = Defines.GetDefine(i->GetStr()->GetDefine()); if (id) { i->GetStr()->SetId(atoi(id->Value)); i->GetStr()->UnDuplicate(); } } } // Set Text LLanguage *Lang = LFindLang(Language); i->GetStr()->Set(Text, (Lang) ? Lang->Id : (char*)"en"); } } } } if (i && !MenuNewLang) { if (MenuLevel == 1) { Menu->Insert(i); } else if (MenuItem[MenuLevel-1]) { MenuItem[MenuLevel-1]->Insert(i); } } } } break; } } } T.DeleteArrays(); } DeleteObj(Dialog); if (String->Length() > 0) { String->SetLanguages(); } } Invalidate(); }; if (FileName) Load(FileName); else { auto Select = new LFileSelect(this); Select->Type("Win32 Resource Script", "*.rc"); Select->Open([&](auto dlg, auto status) { if (status) Load(dlg->Name()); delete dlg; }); } } bool AppWnd::SaveWin32() { return false; } ///////////////////////////////////////////////////////////////////////// ResFrame::ResFrame(Resource *child) { Child = child; Name("ResFrame"); } ResFrame::~ResFrame() { if (Child) { Child->App()->OnObjSelect(NULL); Child->Wnd()->Detach(); } } void ResFrame::OnFocus(bool b) { Child->Wnd()->Invalidate(); } bool ResFrame::Attach(LViewI *p) { bool Status = LLayout::Attach(p); if (Status && Child) { Child->Attach(this); Child->Wnd()->Visible(true); } return Status; } bool ResFrame::Pour(LRegion &r) { LRect *Best = FindLargest(r); if (Best) { SetPos(*Best); return true; } return false; } bool ResFrame::OnKey(LKey &k) { bool Status = false; if (k.Down() && Child) { switch (k.c16) { case LK_DELETE: { if (k.Shift()) { Child->Copy(true); } else { Child->Delete(); } Status = true; break; } case 'x': case 'X': { if (k.Ctrl()) { Child->Copy(true); Status = true; } break; } case 'c': case 'C': { if (k.Ctrl()) { Child->Copy(); Status = true; } break; } case LK_INSERT: { if (k.Ctrl()) { Child->Copy(); } else if (k.Shift()) { Child->Paste(); } Status = true; break; } case 'v': case 'V': { if (k.Ctrl()) { Child->Paste(); Status = true; } break; } } } return Child->Wnd()->OnKey(k) || Status; } void ResFrame::OnPaint(LSurface *pDC) { // Draw nice frame LRect r(0, 0, X()-1, Y()-1); LThinBorder(pDC, r, DefaultRaisedEdge); pDC->Colour(L_MED); LFlatBorder(pDC, r, 4); LWideBorder(pDC, r, DefaultSunkenEdge); // Set the child to the client area Child->Wnd()->SetPos(r); // Draw the dialog & controls LView::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// LgiFunc char *_LgiGenLangLookup(); #include "lgi/common/AutoPtr.h" #include "lgi/common/Variant.h" #include "lgi/common/Css.h" #include "lgi/common/TableLayout.h" class Foo : public LLayoutCell { public: Foo() { TextAlign(AlignLeft); } bool Add(LView *v) { return false; } bool Remove(LView *v) { return false; } }; ////////////////////////////////////////////////////////////////////// ShortCutView::ShortCutView(AppWnd *app) { App = app; LRect r(0, 0, 300, 600); SetPos(r); MoveSameScreen(App); Name("Dialog Shortcuts"); if (Attach(0)) { Lst = new LList(100, 0, 0, 100, 100); Lst->Attach(this); Lst->SetPourLargest(true); Lst->AddColumn("Key", 50); Lst->AddColumn("Ref", 80); Lst->AddColumn("Control", 150); Visible(true); } } ShortCutView::~ShortCutView() { App->OnCloseView(this); } void FindShortCuts(LList *Out, LViewI *In) { for (LViewI *c: In->IterateViews()) { ResDialogCtrl *rdc = dynamic_cast(c); if (!rdc || !rdc->GetStr()) continue; char *n = rdc->GetStr()->Get(); if (n) { char *a = strchr(n, '&'); if (a && a[1] != '&') { LListItem *li = new LListItem; LString s(++a, 1); LString id; id.Printf("%i", rdc->GetStr()->GetRef()); li->SetText(s.Upper(), 0); li->SetText(id, 1); li->SetText(rdc->GetClass(), 2); li->_UserPtr = rdc; Out->Insert(li); } } FindShortCuts(Out, c); } } int ShortCutView::OnNotify(LViewI *Ctrl, LNotification n) { if (Ctrl->GetId() == Lst->GetId()) { switch (n.Type) { case LNotifyItemClick: { LListItem *li = Lst->GetSelected(); if (li) { LString s = li->GetText(1); ResDialogCtrl *c = (ResDialogCtrl*) li->_UserPtr; if (c) App->GotoObject(c->GetStr(), NULL, c->GetDlg(), NULL, c); } break; } + default: + break; } } return LWindow::OnNotify(Ctrl, n); } void ShortCutView::OnDialogChange(ResDialog *Dlg) { Lst->Empty(); if (!Dlg) return; FindShortCuts(Lst, Dlg); Lst->Sort(NULL); } ShortCutView *AppWnd::GetShortCutView() { return ShortCuts; } void AppWnd::OnCloseView(ShortCutView *v) { if (v == ShortCuts) ShortCuts = NULL; } ////////////////////////////////////////////////////////////////////// void TestFunc() { /* Foo c; uint32 *p = (uint32*)&c; p++; p = (uint32*)(*p); void *method = (void*)p[2]; for (int i=0; i<16; i++) { printf("[%i]=%p\n", i, p[i]); } c.OnChange(LCss::PropBackground); */ } int LgiMain(OsAppArguments &AppArgs) { LApp a(AppArgs, "LgiRes"); if (a.IsOk()) { if ((a.AppWnd = new AppWnd)) { TestFunc(); a.AppWnd->Visible(true); a.Run(); } } return 0; } diff --git a/include/lgi/common/Dialog.h b/include/lgi/common/Dialog.h --- a/include/lgi/common/Dialog.h +++ b/include/lgi/common/Dialog.h @@ -1,233 +1,233 @@ #pragma once #include #include "lgi/common/Window.h" #include "lgi/common/LgiRes.h" /// \brief A top level dialog window. /// /// LDialog's can either be modal or modeless. A modal dialog blocks the user from accessing /// the parent LWindow until the dialog has been dismissed. A modeless dialog behaves like /// any other top level window in that it doesn't block the user accessing any other top level /// window while it's open. /// /// LDialog's can be created in two different ways. Firstly you can create code to instantiate /// all the various controls and add them manually to the LView::Children list. Or you can load /// all the controls from a resource file. The resource files are in XML format and there is a /// graphical tool LgiRes. /// /// Manual example: /// \code /// #define IDC_STRING 100 /// /// class Example : public LDialog /// { /// public: /// char *Str; /// /// Example(LView *p) /// { /// Str = 0; /// SetParent(p); /// LRect r(0, 0, 400, 300); /// SetPos(p); /// MoveToCenter(); /// /// Children.Insert(new LEdit(IDC_STRING, 10, 10, 200, 20, "")); /// Children.Insert(new LButton(IDOK, 10, 30, 80, 20, "Ok")); /// Children.Insert(new LButton(IDCANCEL, 100, 30, 80, 20, "Cancel")); /// } /// /// ~Example() /// { /// DeleteArray(Str); /// } /// /// int OnNotify(LViewI *c, int Flags) /// { /// switch (c->GetId()) /// { /// case IDOK: /// { /// Str = NewStr(GetCtrlName(IDC_STRING)); /// // fall thru /// } /// case IDCANCEL: /// { /// EndModal(c->GetId()); /// break; /// } /// } /// /// return 0; /// } /// }; /// \endcode /// /// This example shows how to insert child widgets into the window and then process clicks on the buttons /// and finally return data to the caller via a public member variable. It is desirable to pass data using /// this method because a dialog could be running in it's own thread on some systems and therefor should not /// be accessing data structures outside of itself directly without locking them. If your main application' /// doesn't have thread locking in place for it's main data structures then accessing them from the dialog /// wouldn't be thread safe. This is mainly the case with the BeOS port of Lgi, but is good practise for /// the Windows and Linux ports for cross platform compatibility. /// /// The resource file method of creating dialogs is arguably the easier route to take once your've got it /// set up. Firstly create a resource file using LgiRes with the same name as your programs executable, but /// a different extension ("lr8"). When you call the LResourceLoad::LoadFromResource function Lgi will automatically /// look for a file named after the running executable with the right extension and load it. Then it will /// find the dialog resource and instantiate all the controls specified in the resource. All the built in /// Lgi controls are supported directly as tags in the XML file and you can create your own custom controls /// that the resource loader can instantiate as well using the LViewFactory system. /// /// Resource file example: /// \code /// #include "Resdefs.h" // For the dialog defines /// /// class Example : public LDialog /// { /// public: /// char *Str; /// /// Example(LView *p) /// { /// Str = 0; /// SetParent(p); /// if (LoadFromResource(IDD_EXAMPLE)) /// { /// MoveToCenter(); /// } /// } /// /// ~Example() /// { /// DeleteArray(Str); /// } /// /// int OnNotify(LViewI *c, int Flags) /// { /// switch (c->GetId()) /// { /// case IDOK: /// { /// Str = NewStr(GetCtrlName(1)); /// // fall thru /// } /// case IDCANCEL: /// { /// EndModal(c->GetId()); /// break; /// } /// } /// /// return 0; /// } /// }; /// \endcode /// /// This assumes you have created an lr8 file with the resource named 'IDD_EXAMPLE' in it. /// /// Now to actually call either of these dialogs you would use code like this: /// \code /// Example Dlg(MyWindow); /// if (Dlg.DoModal() == IDOK) /// { /// // Do something with Dlg.Str /// } /// \endcode /// /// The built in controls that you can use are: ///
    ///
  • LButton (Push button) ///
  • LEdit (Edit box for text entry) ///
  • GText (Static label) ///
  • LCheckBox (Independent boolean) ///
  • LCombo (Select one from many) ///
  • LSlider (Select a position) ///
  • LBitmap (Display an image) ///
  • LProgressView (Show a progress) ///
  • GList (List containing LListItem) ///
  • LTree (Hierarchy of LTreeItem) ///
  • LRadioGroup (One of many selection using LRadioButton) ///
  • LTabView (Containing LTabPage) ///
class LgiClass LDialog : public LWindow, public LResourceLoad, public ResObject { friend class LControl; private: struct LDialogPriv *d; public: typedef std::function OnClose; /// Constructor LDialog(LViewI *Parent = NULL); /// Destructor ~LDialog(); const char *GetClass() override { return "LDialog"; } /// Load the dialog from a resource bool LoadFromResource ( /// The resource ID int Resource, /// [Optional] tag list to exclude/include various controls via tag char *TagList = 0 ); /// \brief Run the dialog in modal mode. /// /// The user has to dismiss the dialog to continue. virtual void DoModal ( /// Call back to handle post-dialog activity OnClose Callback, /// Optional override parent window handle OsView ParentHnd = NULL ); /// \brief Run the dialog in modeless mode /// /// It will behave like any other top level window. The user doesn't /// have to dismiss the window to continue, it can just move to the back. virtual int DoModeless(); /// Gets the model status virtual bool IsModal(); /// End a modal window. Typically calling in the OnNotify event of the LDialog. virtual void EndModal(int Code = 0); /// End a modeless window. Typically calling in the OnNotify event of the LDialog. virtual void EndModeless(int Code = 0); - LMessage::Result OnEvent(LMessage *Msg); - bool OnRequestClose(bool OsClose); - void OnPosChange(); - void PourAll() {} - void Quit(bool DontDelete = false); + LMessage::Result OnEvent(LMessage *Msg) override; + bool OnRequestClose(bool OsClose) override; + void OnPosChange() override; + void PourAll() override {} + void Quit(bool DontDelete = false) override; /// By default the dialog will finish when a button is pressed. To override this /// behavior you'll have to subclass LDialog and handle the OnNotify yourself. - int OnNotify(LViewI *Ctrl, LNotification n); + int OnNotify(LViewI *Ctrl, LNotification n) override; /// This returns the ID of the button pressed to close the dialog. int GetButtonId(); #if defined(__GTK_H__) friend Gtk::gboolean GtkDialogDestroy(Gtk::GtkWidget *widget, LDialog *This); bool IsResizeable(); void IsResizeable(bool r); bool SetupDialog(bool Modal); #elif defined(LGI_CARBON) void OnPaint(LSurface *pDC); #endif }; diff --git a/include/lgi/common/FileSelect.h b/include/lgi/common/FileSelect.h --- a/include/lgi/common/FileSelect.h +++ b/include/lgi/common/FileSelect.h @@ -1,130 +1,127 @@ #pragma once #include LgiFunc bool LgiGetUsersLinks(LArray &Links); /////////////////////////////////////////////////////////////////////////////////////////////////// // File select dialog class LgiClass LFileType : public LBase { char *Ext; int _Data; public: LFileType() { Ext = 0; _Data = 0; } ~LFileType() { DeleteArray(Ext); } char *Extension() { return Ext; } bool Extension(const char *e) { return (Ext = NewStr(e)) != 0; } const char *Description() { return Name(); } bool Description(const char *d) { return Name(d); } int Data() { return _Data; } void Data(int i) { _Data = i; } char *DefaultExtension(); }; /// \brief File selector dialog /// /// Handles the UI for selecting files for opening and saving and selecting directories. /// Uses the Win32 system dialogs on Windows and has it's own native window on Linux. /// /// A simple example of this class in action: /// \code /// LFileSelect s; /// s.Parent(MyWindow); /// s.Type("PNG Files", "*.png"); /// if (s.Open()) /// { /// LgiMsg(MyWindow, "The file selected is '%s'", "Example", MB_OK, s.Name()); /// } /// \endcode class LgiClass LFileSelect : public LBase { class LFileSelectPrivate *d; public: typedef std::function SelectCb; LFileSelect(LViewI *Window = NULL); ~LFileSelect(); // Properties /// Returns the first file name selected. const char *Name() override; /// Sets the file name bool Name(const char *n) override; /// Returns the n'th file name selected const char *operator [](size_t i); /// Returns the number of file names selected size_t Length(); /// Returns the parent window LViewI *Parent(); /// Sets the parent window void Parent(LViewI *Window); /// Returns whether the use can select multiple files bool MultiSelect(); /// Sets whether the use can select multiple files void MultiSelect(bool Multi); /// Returns whether read only was selected bool ReadOnly(); /// Sets whether the user sees a read only option void ShowReadOnly(bool ro); /// Gets the initial directory to open in const char *InitialDir(); /// Sets the initial directory to open in void InitialDir(const char *InitDir); /// Gets the title of the dialog box const char *Title(); /// Sets the title of the dialog box void Title(const char *Title); /// Gets the default extension to append to files selected without an extension const char *DefaultExtension(); /// Sets the default extension to append to files selected without an extension void DefaultExtension(const char *DefExt); // File types /// Returns the number of types in the type list size_t Types(); /// Returns the 0 based index of the type selected in the type list ssize_t SelectedType(); /// Returns the type into at a given index LFileType *TypeAt(ssize_t n); /// Adds a file type to the type filters list. bool Type ( /// This full description of the file type const char *Description, /// The extension(s) of the file type: e.g: '*.png;*.gif;*.jpg' const char *Extension, /// Application defined 32-bit value. int Data = 0 ); /// Empties the type list void ClearTypes(); // Methods /// Shows the open file dialog - /// \returns true if the user selected a file, otherwise false void Open(SelectCb Cb); /// Shows the save file dialog - /// \returns true if the user selected a file, otherwise false void Save(SelectCb Cb); /// Shows the open folder dialog - /// \returns true if the user selected a folder, otherwise false void OpenFolder(SelectCb Cb); }; diff --git a/include/lgi/common/Window.h b/include/lgi/common/Window.h --- a/include/lgi/common/Window.h +++ b/include/lgi/common/Window.h @@ -1,315 +1,315 @@ #ifndef _LWINDOW_H_ #define _LWINDOW_H_ #include "lgi/common/View.h" /// The available states for a top level window enum LWindowZoom { /// Minimized LZoomMin, /// Restored/Normal LZoomNormal, /// Maximized LZoomMax }; enum LWindowHookType { LNoEvents = 0, /// \sa LWindow::RegisterHook() LMouseEvents = 1, /// \sa LWindow::RegisterHook() LKeyEvents = 2, /// \sa LWindow::RegisterHook() LKeyAndMouseEvents = LMouseEvents | LKeyEvents, }; /// A top level window. class LgiClass LWindow : public LView, // This needs to be second otherwise is causes v-table problems. #ifndef LGI_SDL virtual #endif public LDragDropTarget { friend class BViewRedir; friend class LApp; friend class LView; friend class LButton; friend class LDialog; friend class LWindowPrivate; friend struct LDialogPriv; bool _QuitOnClose; protected: class LWindowPrivate *d; #if WINNATIVE LRect OldPos; LWindow *_Dialog; #elif defined(HAIKU) LWindowZoom _PrevZoom = LZoomNormal; #else OsWindow Wnd; void SetDeleteOnClose(bool i); #endif #if defined __GTK_H__ friend class LMenu; friend void lgi_widget_size_allocate(Gtk::GtkWidget *widget, Gtk::GtkAllocation *allocation); Gtk::GtkWidget *_Root, *_VBox, *_MenuBar; void OnGtkDelete(); Gtk::gboolean OnGtkEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event); #elif defined(LGI_CARBON) friend pascal OSStatus LgiWindowProc(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); void _Delete(); bool _RequestClose(bool os); #elif defined(__OBJC__) public: // This returns the root level content NSView NSView *Handle(); protected: #endif /// The default button LViewI *_Default; /// The menu on the window LMenu *Menu; void SetChildDialog(LDialog *Dlg); void SetDragHandlers(bool On); /// Haiku: This shuts down the window's thread cleanly. int WaitThread(); public: #ifdef _DEBUG LMemDC DebugDC; #endif #ifdef __GTK_H__ LWindow(Gtk::GtkWidget *w = NULL); #elif LGI_CARBON LWindow(WindowRef wr = NULL); #elif LGI_COCOA LWindow(OsWindow wnd = NULL); #else LWindow(); #endif ~LWindow(); const char *GetClass() override { return "LWindow"; } /// Lays out the child views into the client area. virtual void PourAll(); /// Returns the current menu object LMenu *GetMenu() { return Menu; } /// Set the menu object. void SetMenu(LMenu *m) { Menu = m; } /// Set the window's icon bool SetIcon(const char *FileName); /// Gets the "quit on close" setting. bool GetQuitOnClose() { return _QuitOnClose; } /// \brief Sets the "quit on close" setting. /// /// When this is switched on the application will quit the main message /// loop when this LWindow is closed. This is really useful for your /// main application window. Otherwise the UI will disappear but the /// application is still running. void SetQuitOnClose(bool i) { _QuitOnClose = i; } bool GetSnapToEdge(); void SetSnapToEdge(bool b); bool GetAlwaysOnTop(); void SetAlwaysOnTop(bool b); /// Gets the current zoom setting LWindowZoom GetZoom(); /// Sets the current zoom void SetZoom(LWindowZoom i); /// Raises the window to the top of the stack. void Raise(); /// Moves a top level window on screen. void MoveOnScreen(); /// Moves a top level to the center of the screen void MoveToCenter(); /// Moves a top level window to where the mouse is void MoveToMouse(); /// Moves the window to somewhere on the same screen as 'wnd' bool MoveSameScreen(LViewI *wnd); // Focus setting LViewI *GetFocus(); enum FocusType { GainFocus, LoseFocus, ViewDelete }; void SetFocus(LViewI *ctrl, FocusType type); /// Registers a watcher to receive OnView... messages before they /// are passed through to the intended recipient. bool RegisterHook ( /// The target view. LView *Target, /// Combination of: /// #LMouseEvents - Where Target->OnViewMouse(...) is called for each click. /// and /// #LKeyEvents - Where Target->OnViewKey(...) is called for each key. /// OR'd together. LWindowHookType EventType, /// Not implemented int Priority = 0 ); /// Unregisters a hook target bool UnregisterHook(LView *Target); /// Gets the default view LViewI *GetDefault(); /// Sets the default view void SetDefault(LViewI *v); /// Saves/loads the window's state, e.g. position, minimized/maximized etc bool SerializeState ( /// The data store for reading/writing LDom *Store, /// The field name to use for storing settings under const char *FieldName, /// TRUE if loading the settings into the window, FALSE if saving to the store. bool Load ); /// Builds a map of keyboard short cuts. typedef LHashTbl,LViewI*> ShortcutMap; void BuildShortcuts(ShortcutMap &Map, LViewI *v = NULL); ////////////////////// Events /////////////////////////////// /// Called when the window zoom state changes. virtual void OnZoom(LWindowZoom Action) {} /// Called when the tray icon is clicked. (if present) virtual void OnTrayClick(LMouse &m); /// Called when the tray icon menu is about to be displayed. virtual void OnTrayMenu(LSubMenu &m) {} /// Called when the tray icon menu item has been selected. virtual void OnTrayMenuResult(int MenuId) {} /// Called when files are dropped on the window. virtual void OnReceiveFiles(LArray &Files) {} /// Called when a URL is sent to the window virtual void OnUrl(const char *Url) {}; ///////////////// Implementation //////////////////////////// void OnPosChange() override; LMessage::Result OnEvent(LMessage *Msg) override; void OnPaint(LSurface *pDC) override; bool HandleViewMouse(LView *v, LMouse &m); bool HandleViewKey(LView *v, LKey &k); /// Return true to accept application quit bool OnRequestClose(bool OsShuttingDown) override; bool Obscured(); bool Visible() override; void Visible(bool i) override; bool IsActive(); bool SetActive(); LRect &GetPos() override; void SetDecor(bool Visible); LPoint GetDpi(); LPointF GetDpiScale(); void ScaleSizeToDpi(); // D'n'd int WillAccept(LDragFormats &Formats, LPoint Pt, int KeyState) override; int OnDrop(LArray &Data, LPoint Pt, int KeyState) override; #if !WINNATIVE bool Attach(LViewI *p) override; // Props #if defined(HAIKU) OsWindow WindowHandle() override; #else OsWindow WindowHandle() override { return Wnd; } #endif bool Name(const char *n) override; const char *Name() override; bool SetPos(LRect &p, bool Repaint = false) override; LRect &GetClient(bool InClientSpace = true) override; // Events void OnChildrenChanged(LViewI *Wnd, bool Attaching) override; void OnCreate() override; virtual void OnFrontSwitch(bool b); #else OsWindow WindowHandle() override { return _View; } #endif #if defined(LGI_SDL) virtual bool PushWindow(LWindow *v); virtual LWindow *PopWindow(); #elif defined __GTK_H__ void OnGtkRealize(); bool IsAttached(); void Quit(bool DontDelete = false); LRect *GetDecorSize(); bool TranslateMouse(LMouse &m); LViewI *WindowFromPoint(int x, int y, bool Debug = false); void _SetDynamic(bool b); void _OnViewDelete(); void SetParent(LViewI *p) override; #elif defined(MAC) - bool PostEvent(int Cmd, LMessage::Param a = 0, LMessage::Param b = 0) override; + bool PostEvent(int Cmd, LMessage::Param a = 0, LMessage::Param b = 0, int64_t TimeoutMs = -1) override; void Quit(bool DontDelete = false) override; int OnCommand(int Cmd, int Event, OsView Wnd) override; LViewI *WindowFromPoint(int x, int y, int DebugDebug = 0) override; #if defined(LGI_CARBON) OSErr HandlerCallback(DragTrackingMessage *tracking, DragRef theDrag); #endif #endif }; #endif diff --git a/private/mac/AppPriv.h b/private/mac/AppPriv.h --- a/private/mac/AppPriv.h +++ b/private/mac/AppPriv.h @@ -1,58 +1,58 @@ #pragma once #include "lgi/common/Json.h" #include "SymLookup.h" #include "lgi/common/FontCache.h" typedef LArray AppArray; class LAppPrivate { public: LApp *Owner; OsApp NsApp; int RunDepth; // Common LAutoPtr Config; LFileSystem *FileSystem; GdcDevice *GdcSystem; OsAppArguments Args; LLibrary *SkinLib; - LHashTbl,AppArray*> MimeToApp; + LHashTbl,AppArray*> MimeToApp; OsThread GuiThread; OsThreadId GuiThreadId; LSymLookup SymLookup; LAutoString Mime; LAutoString Name; LAutoString UrlArg; /// Any fonts needed for styling the elements LAutoPtr FontCache; LAppPrivate(LApp *owner) : Owner(owner) { NsApp = NULL; RunDepth = 0; FileSystem = 0; GdcSystem = 0; SkinLib = 0; GuiThread = LGetCurrentThread(); GuiThreadId = GetCurrentThreadId(); } ~LAppPrivate() { DeleteObj(SkinLib); for (auto p : MimeToApp) { p.value->DeleteObjects(); DeleteObj(p.value); } } LJson *GetConfig(); bool SaveConfig(); }; diff --git a/src/common/Gdc2/Font/FontType.cpp b/src/common/Gdc2/Font/FontType.cpp --- a/src/common/Gdc2/Font/FontType.cpp +++ b/src/common/Gdc2/Font/FontType.cpp @@ -1,793 +1,791 @@ #include "lgi/common/Lgi.h" #include "lgi/common/FontSelect.h" LFontType::LFontType(const char *face, int pointsize) { #if defined WINNATIVE ZeroObj(Info); if (face) { swprintf_s(Info.lfFaceName, CountOf(Info.lfFaceName), L"%S", face); } if (pointsize) { Info.lfHeight = WinHeightToPoint(pointsize); } #else if (face) Info.Face(face); if (pointsize) Info.PointSize(pointsize); #endif } LFontType::~LFontType() { } const char *LFontType::GetFace() { #ifdef WINNATIVE Buf = Info.lfFaceName; return Buf; #else return Info.Face(); #endif } void LFontType::SetFace(const char *Face) { #ifdef WINNATIVE if (Face) { swprintf_s(Info.lfFaceName, CountOf(Info.lfFaceName), L"%S", Face); } else { Info.lfFaceName[0] = 0; } #else Info.Face(Face); #endif } int LFontType::GetPointSize() { #ifdef WINNATIVE return WinHeightToPoint(Info.lfHeight); #else return Info.PointSize(); #endif } void LFontType::SetPointSize(int PointSize) { #ifdef WINNATIVE Info.lfHeight = WinPointToHeight(PointSize); #else Info.PointSize(PointSize); #endif } void LFontType::DoUI(LView *Parent, std::function Callback) { - bool Status = false; - #if WINNATIVE int bytes = sizeof(Info); #else char str[256]; int bytes = sprintf_s(str, sizeof(str), "%s,%i", Info.Face(), Info.PointSize()); #endif LFontSelect *Dlg = new LFontSelect(Parent, &Info, bytes); Dlg->DoModal([Dlg, this, Callback](auto dlg, auto id) { if (id == IDOK) { #if WINNATIVE Dlg->Serialize(&this->Info, sizeof(this->Info), true); #else if (Dlg->Face) Info.Face(Dlg->Face); Info.PointSize(Dlg->Size); #endif if (Callback) Callback(this); } delete Dlg; }); } bool LFontType::GetDescription(char *Str, int SLen) { if (Str && GetFace()) { sprintf_s(Str, SLen, "%s, %i pt", GetFace(), GetPointSize()); return true; } return false; } bool LFontType::Serialize(LDom *Options, const char *OptName, bool Write) { bool Status = false; if (Options && OptName) { LVariant v; #if defined WINNATIVE if (Write) { v.SetBinary(sizeof(Info), &Info); Status = Options->SetValue(OptName, v); } else { if (Options->GetValue(OptName, v)) { if (v.Type == GV_BINARY && v.Value.Binary.Length == sizeof(Info)) { memcpy(&Info, v.Value.Binary.Data, sizeof(Info)); Status = ValidStrW(Info.lfFaceName); } } } #else if (Write) { char Temp[128]; sprintf_s(Temp, sizeof(Temp), "%s,%i pt", Info.Face(), Info.PointSize()); Status = Options->SetValue(OptName, v = Temp); } else { if (Options->GetValue(OptName, v) && ValidStr(v.Str())) { char *Comma = strchr(v.Str(), ','); if (Comma) { *Comma++ = 0; int PtSize = atoi(Comma); if (stricmp(v.Str(), "(null)")) { Info.Face(v.Str()); Info.PointSize(PtSize); // printf("FontTypeSer getting '%s' = '%s' pt %i\n", OptName, v.Str(), PtSize); Status = true; } } } } #endif } return Status; } bool LFontType::GetConfigFont(const char *Tag) { // read from config file auto Font = LAppInst->GetConfig(Tag); if (!Font) return false; if (Font == "-") return false; // default string added for discoverability auto p = Font.Split(":"); if (p.Length() != 2) { LgiTrace("%s:%i - Font specification '%s' should have the format: :\n", _FL, Font.Get()); return false; } SetFace(p[0]); SetPointSize((int)p[1].Int()); return true; } class LFontTypeCache { char DefFace[64]; int DefSize; char Face[64]; int Size; public: LFontTypeCache(char *defface, int defsize) { ZeroObj(Face); Size = -1; strcpy_s(DefFace, sizeof(DefFace), defface); DefSize = defsize; } char *GetFace(char *Type = 0) { #ifdef LINUX if (!IsInit()) { char f[256]; int s; if (_GetSystemFont(Type, f, sizeof(f), s)) { strcpy_s(Face, sizeof(Face), f); Size = s; } else { Face[0] = 0; Size = 0; } } #endif return ValidStr(Face) ? Face : DefFace; } int GetSize() { return Size > 0 ? Size : DefSize; } bool IsInit() { return Size >= 0; } }; #if defined USE_CORETEXT bool MacGetSystemFont(LTypeFace &Info, CTFontUIFontType Which) { CTFontRef ref = CTFontCreateUIFontForLanguage(Which, 0.0, NULL); if (!ref) return false; bool Status = false; CFStringRef name = CTFontCopyFamilyName(ref); if (name) { CGFloat sz = CTFontGetSize(ref); LString face(name); Info.Face(face); Info.PointSize((int)sz); CFRelease(name); Status = true; } CFRelease(ref); return Status; } #endif bool LFontType::GetSystemFont(const char *Which) { bool Status = false; if (!Which) { LAssert(!"No param supplied."); return false; } #if LGI_SDL #elif defined WINNATIVE // Get the system settings NONCLIENTMETRICS info; info.cbSize = sizeof(info); #if (WINVER >= 0x0600) LArray Ver; if (LGetOs(&Ver) == LGI_OS_WIN32 && Ver[0] <= 5) info.cbSize -= 4; #endif BOOL InfoOk = SystemParametersInfo( SPI_GETNONCLIENTMETRICS, info.cbSize, &info, 0); if (!InfoOk) { LgiTrace("%s:%i - SystemParametersInfo failed with 0x%x (info.cbSize=%i, os=%i, %i)\n", _FL, GetLastError(), info.cbSize, LGetOs(), LGI_OS_WIN9X); } // Convert windows font height into points int Height = WinHeightToPoint(info.lfMessageFont.lfHeight); #elif defined __GTK_H__ // Define some defaults.. in case the system settings aren't there static char DefFont[64] = #ifdef __CYGWIN__ "Luxi Sans"; #else "Sans"; #endif int DefSize = 10; // int Offset = 0; static bool First = true; if (First) { bool ConfigFontUsed = false; char p[MAX_PATH_LEN]; LGetSystemPath(LSP_HOME, p, sizeof(p)); LMakePath(p, sizeof(p), p, ".lgi.conf"); if (LFileExists(p)) { LAutoString a(LReadTextFile(p)); if (a) { LString s; s = a.Get(); LString::Array Lines = s.Split("\n"); for (int i=0; i 1) { strcpy_s(DefFont, sizeof(DefFont), d[0]); int PtSize = d[1].Int(); if (PtSize > 0) { DefSize = PtSize; ConfigFontUsed = true; printf("Config font %s : %i\n", DefFont, DefSize); } } } } } } else printf("Can't read '%s'\n", p); } if (!ConfigFontUsed) { Gtk::GtkStyle *s = Gtk::gtk_style_new(); if (s) { const char *fam = Gtk::pango_font_description_get_family(s->font_desc); if (fam) { strcpy_s(DefFont, sizeof(DefFont), fam); } else printf("%s:%i - pango_font_description_get_family failed.\n", _FL); if (Gtk::pango_font_description_get_size_is_absolute(s->font_desc)) { float Px = Gtk::pango_font_description_get_size(s->font_desc) / PANGO_SCALE; float Dpi = (float)LScreenDpi().x; DefSize = (Px * 72.0) / Dpi; printf("pango px=%f, Dpi=%f\n", Px, Dpi); } else { DefSize = Gtk::pango_font_description_get_size(s->font_desc) / PANGO_SCALE; } g_object_unref(s); } else printf("%s:%i - gtk_style_new failed.\n", _FL); } First = false; } #endif int PtSizeOffset = 0; auto Offset = LAppInst->GetConfig(LApp::CfgFontsPointSizeOffset); if (Offset) { auto i = Offset.Int(); if (i != 0 && std::abs(i) < 20) PtSizeOffset = (int)i; } if (!_stricmp(Which, "System")) { Status = GetConfigFont(LApp::CfgFontsSystemFont); if (!Status) { // read from system #if LGI_SDL #if defined(WIN32) Info.Face("Tahoma"); Info.PointSize(11); Status = true; #elif defined(MAC) Info.Face("LucidaGrande"); Info.PointSize(11); Status = true; #elif defined(LINUX) Info.Face("Sans"); Info.PointSize(11); Status = true; #else #error fix me #endif #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfMessageFont, sizeof(Info)); Status = true; } else LgiTrace("%s:%i - Info not ok.\n", _FL); #elif defined(HAIKU) font_family family = {0}; font_style style = {0}; be_plain_font->GetFamilyAndStyle(&family, &style); Info.PointSize(be_plain_font->Size()); Info.Face(family); Status = true; #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize); Status = true; #elif defined MAC #ifdef USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontControlContent); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeSmallSystemFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; // printf("System=%s,%i\n", Info.Face(), Size); } #endif #endif } } else if (!stricmp(Which, "Menu")) { Status = GetConfigFont(LApp::CfgFontsMenuFont); if (!Status) { #if LGI_SDL LAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfMenuFont, sizeof(Info)); Status = true; } #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize); Status = true; #elif defined MAC && !LGI_COCOA #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontMenuItem); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeMenuItemFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; } #endif #endif } } else if (!stricmp(Which, "Caption")) { Status = GetConfigFont(LApp::CfgFontsCaptionFont); if (!Status) { #if LGI_SDL LAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfCaptionFont, sizeof(Info)); Status = true; } #elif defined LINUX #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize-1); Status = true; #elif defined MAC && !LGI_COCOA #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontToolbar); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeToolbarFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; } #endif #endif } } else if (!stricmp(Which, "Status")) { Status = GetConfigFont("Font-Status"); if (!Status) { #if LGI_SDL LAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfStatusFont, sizeof(Info)); Status = true; } #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize); Status = true; #elif defined MAC && !LGI_COCOA #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontSystemDetail); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeToolbarFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size); Status = true; } #endif #endif } } else if (!stricmp(Which, "Small")) { Status = GetConfigFont(LApp::CfgFontsSmallFont); if (!Status) { #if LGI_SDL LAssert(!"Impl me."); #elif defined WINNATIVE if (InfoOk) { // Copy the font metrics memcpy(&Info, &info.lfSmCaptionFont, sizeof(Info)); if (LGetOs() == LGI_OS_WIN9X && _wcsicmp(Info.lfFaceName, L"ms sans serif") == 0) { SetFace("Arial"); } // Make it a bit smaller than the system font Info.lfHeight = WinPointToHeight(WinHeightToPoint(info.lfMessageFont.lfHeight)-1); Info.lfWeight = FW_NORMAL; Status = true; } #elif defined __GTK_H__ Info.Face(DefFont); Info.PointSize(DefSize-1); Status = true; #elif defined MAC #if USE_CORETEXT Status = MacGetSystemFont(Info, kCTFontUIFontSmallSystem); #else Str255 Name; SInt16 Size; Style St; OSStatus e = GetThemeFont( kThemeSmallSystemFont, smSystemScript, Name, &Size, &St); if (e) printf("%s:%i - GetThemeFont failed with %i\n", __FILE__, __LINE__, (int)e); else { Info.Face(p2c(Name)); Info.PointSize(Size - 2); Status = true; } #endif #endif } } else if (!stricmp(Which, "Fixed")) { Status = GetConfigFont(LApp::CfgFontsMonoFont); if (!Status) { #if LGI_SDL LAssert(!"Impl me."); #elif defined WINNATIVE // SetFace("Courier New"); SetFace("Consolas"); Info.lfHeight = WinPointToHeight(10); Status = true; #elif defined(HAIKU) font_family family = {0}; font_style style = {0}; be_fixed_font->GetFamilyAndStyle(&family, &style); Info.PointSize(be_fixed_font->Size()); Info.Face(family); Status = true; #elif defined __GTK_H__ Info.Face("Courier New"); Info.PointSize(DefSize); Status = true; #elif defined MAC Status = MacGetSystemFont(Info, kCTFontUIFontUserFixedPitch); #else #warning "Impl me" #endif } } else { LAssert(!"Invalid param supplied."); } if (Status && PtSizeOffset) SetPointSize(GetPointSize() + PtSizeOffset); // printf("GetSystemFont(%s)=%i %s,%i\n", Which, Status, Info.Face(), Info.PointSize()); return Status; } bool LFontType::GetFromRef(OsFont Handle) { #if defined WIN32 return GetObject(Handle, sizeof(Info), &Info) == sizeof(Info); #else // FIXME return false; #endif } LFont *LFontType::Create(LSurface *pSurface) { LFont *New = new LFont; if (New) { if (!New->Create(this, pSurface)) { DeleteObj(New); } if (New) LAssert(New->GetHeight() > 0); } return New; } diff --git a/src/common/Lgi/FileSelect.cpp b/src/common/Lgi/FileSelect.cpp --- a/src/common/Lgi/FileSelect.cpp +++ b/src/common/Lgi/FileSelect.cpp @@ -1,2256 +1,2256 @@ /*hdr ** FILE: LFileSelect.cpp ** AUTHOR: Matthew Allen ** DATE: 20/5/2002 ** DESCRIPTION: Common file/directory selection dialog ** ** Copyright (C) 1998-2002, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Popup.h" #include "lgi/common/List.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Edit.h" #include "lgi/common/Button.h" #include "lgi/common/CheckBox.h" #include "lgi/common/Combo.h" #include "lgi/common/Tree.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Box.h" #include "lgi/common/FileSelect.h" #include "lgi/common/Menu.h" #define FSI_FILE 0 #define FSI_DIRECTORY 1 #define FSI_BACK 2 #define FSI_UPDIR 3 #define FSI_NEWDIR 4 #define FSI_DESKTOP 5 #define FSI_HARDDISK 6 #define FSI_CDROM 7 #define FSI_FLOPPY 8 #define FSI_NETWORK 9 enum DlgType { TypeNone, TypeOpenFile, TypeOpenFolder, TypeSaveFile }; class LFileSelectDlg; char ModuleName[] = "File Select"; uint32_t IconBits[] = { 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xC980FA8A, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x738E738E, 0xF81F738E, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8430F81F, 0x84308430, 0x84308430, 0xF81F8430, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE09CE0, 0x9CE09CE0, 0x00009CE0, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCCE0F81F, 0x9800FCF9, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x738E738E, 0xCE6C9E73, 0x738EC638, 0xF81F738E, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9FFF738E, 0x9FFF9FFF, 0x9FFF9FFF, 0x00009FFF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE0F81F, 0xFFF9F7BE, 0xFFF3FFF9, 0x9CE0FFF3, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE09CE0, 0x9CE09CE0, 0x00009CE0, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0x04F904F9, 0x04F904F9, 0xAD720313, 0xAD72AD72, 0xAD72AD72, 0xFE60CCE0, 0x00009B00, 0x0000AD72, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x738EF81F, 0x667334F3, 0xCE6C9E73, 0xC638B5B6, 0x3186C638, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF738E, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xF81FF81F, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0x9CE09CE0, 0x9CE09CE0, 0x9CE09CE0, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE0F81F, 0xFFF9F7BE, 0xFFF3FFF9, 0x9CE0FFF3, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81F0000, 0x667F04F9, 0x031304F9, 0xFFFFCE73, 0xFFF9FFFF, 0xCCE0FFFF, 0x9B00FFF3, 0x04F90000, 0x00000313, 0xF81FF81F, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F738E, 0xF81FF81F, 0xF81FF81F, 0x6673738E, 0x34F36673, 0xCE736673, 0xB5B6C638, 0xB5B6DEFB, 0xF81F3186, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF738E, 0xFFFFFFFF, 0xCFFFCFFF, 0x0000CFFF, 0x738EF81F, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x0000FFFF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xCE6CFFF3, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0x9CE09CE0, 0x9CE09CE0, 0x9CE09CE0, 0xF81FF81F, 0xF81FF81F, 0x9CE09CE0, 0x9CE09CE0, 0xF81F0000, 0x0000F81F, 0x0000F81F, 0x0000F81F, 0xF81FF81F, 0x04F904F9, 0xCE730313, 0xFFFFFFFF, 0xFFF9FFF9, 0xFE60CCE0, 0x00009B00, 0x667F3313, 0x00000313, 0x738EF81F, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0x0000738E, 0xF81FF81F, 0xF81FF81F, 0x34F98430, 0x667364F9, 0x84306679, 0xD6BAB5B6, 0xB5B6C638, 0xF81F3186, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCFFF738E, 0xCFFFCFFF, 0xCFFFCFFF, 0x0000CFFF, 0xFFFF738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9CF3FFFF, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x000007FF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xCE6CFFF3, 0xF81F0000, 0x9CE0F81F, 0xFFF9F7BE, 0xFFF3FFF3, 0x00009CE0, 0xF81FF81F, 0xF81F0000, 0xF81F0000, 0xF81FF81F, 0x031304F9, 0xFFFFCE73, 0x94B294B2, 0xCCE094B2, 0x9B00FFF3, 0xAD720000, 0x3313FFF9, 0x00000313, 0xFFFF738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9CF3FFFF, 0x0000738E, 0xF81FF81F, 0x738EF81F, 0xA53494B2, 0x667964F3, 0x00008430, 0xA5348430, 0xCE79B5B6, 0x3186CE79, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE79738E, 0xCE79C638, 0xC638B5B6, 0x0000B5B6, 0xD6BA738E, 0xC638C638, 0xC638C638, 0xC638C638, 0xB5B6C638, 0x04200660, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xD699D699, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0xF81FF81F, 0x07FF0000, 0x000007FF, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xCE6CFE73, 0xF81F0000, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0x9CE0CE6C, 0x9CE09CE0, 0xF81F9CE0, 0x0000F81F, 0x0000F81F, 0xCE730313, 0xFFFFFFFF, 0xFFFF94B2, 0xFE60CCE0, 0x00009B00, 0xFFF9AD72, 0xCE73CE73, 0x00003313, 0xD6BA738E, 0xC638C638, 0xC638C638, 0xC638C638, 0xB5B6C638, 0x04200660, 0x94B2B5B6, 0x0000738E, 0xF81FF81F, 0x738EF81F, 0xB5B6B5B6, 0x8430CE79, 0xF81F0000, 0x84300000, 0xCE79CE79, 0x3186CE79, 0xF81FF81F, 0x84308430, 0x84308430, 0x84308430, 0xC638738E, 0x84308430, 0x84308430, 0x0000C638, 0xDEFB738E, 0xC638B5B6, 0xC638C638, 0xC638C638, 0xB5B6B5B6, 0xB5B6B5B6, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0x0000F81F, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x00000000, 0xFFF30000, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0xFFF99CE0, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF3FFF9, 0x0000CE6C, 0xF81F0000, 0xF81FF81F, 0xFFFFAD72, 0xFFF9FFFF, 0xFFFF94B2, 0x9B009CEC, 0x00000000, 0xCE73FFF9, 0xAD72FFF9, 0x000094B2, 0xDEFB738E, 0xC638B5B6, 0xC638C638, 0xC638C638, 0xB5B6B5B6, 0xB5B6B5B6, 0x94B2B5B6, 0x0000738E, 0xF81FF81F, 0x738EF81F, 0xF7BEE73C, 0xB5B6E73C, 0x00008430, 0xB5B68430, 0xF7BEEF7D, 0x3186CE79, 0x8430F81F, 0xC638C638, 0xC638C638, 0xC638C638, 0xCE79738E, 0x0000738E, 0xFFFF738E, 0x0000B5B6, 0xDEFB738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0x0000F81F, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0x0000FFF3, 0x00000000, 0x00000000, 0xFFF3FFF3, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0x0000CE6C, 0x0000F81F, 0xF81FF81F, 0xFFFFAD72, 0xFFF9FFF9, 0xFFFF94B2, 0xFFFF0000, 0x0000FFF9, 0xFFF9CE73, 0xCE73AD72, 0x000094B2, 0xDEFB738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x94B2B5B6, 0x0000738E, 0x8430F81F, 0x84308430, 0xA5348430, 0xA534B5B6, 0x8430A534, 0xDEFB34F3, 0xFFFFE73C, 0xF81F3186, 0xFFFF8430, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, 0x00000000, 0xF81F0000, 0xD6BA738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0x07FF0000, 0x000007FF, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0x0000CE6C, 0xF81FF81F, 0xF81F0000, 0xFFF9AD72, 0xFFF9FFF9, 0xFFFF94B2, 0xFFFFFFFF, 0x0000FFF9, 0xCE73FFF9, 0xAD72CE73, 0x000094B2, 0xD6BA738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x94B2B5B6, 0x0000738E, 0xFFFF8430, 0xFFFFFFFF, 0xA534738E, 0xB5B6A534, 0xCE73D6BA, 0x34F96673, 0xB5B6CFFF, 0xF81F3186, 0xC6388430, 0xC638C638, 0xC638C638, 0xC638C638, 0xC638C638, 0xC638F800, 0x738E8430, 0xF81F0000, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F0000, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x000007FF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0xFFF9AD72, 0xFFF9FFF9, 0xFFFF94B2, 0xFFFFFFFF, 0x0000FFF9, 0xCE73CE73, 0xCE73AD72, 0x000094B2, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F0000, 0xFFFF8430, 0xC638C638, 0x738EC638, 0xB5B6A534, 0xCE73CE79, 0x04F99E73, 0x000004F9, 0xF81FF81F, 0xC6388430, 0xC638C638, 0x84308430, 0x84308430, 0xC638C638, 0xC638C638, 0x738E8430, 0xF81F0000, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFE73FE73, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0x00000000, 0x00000000, 0xFE730000, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0xFFF904F9, 0xFFF9FFF9, 0xFFF994B2, 0xFFF9FFF9, 0x0000FFF9, 0xAD72CE73, 0xAD72CE73, 0x00003313, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xFFFF8430, 0x31863186, 0x31863186, 0x84308430, 0xCE73CE79, 0x31869E73, 0x00003186, 0xF81FF81F, 0xC6388430, 0x84308430, 0x00000000, 0x00000000, 0x84308430, 0xC6388430, 0x738E8430, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB5B6F81F, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFE73FE73, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FE73, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0x04F904F9, 0xFFF9FFF9, 0x00000000, 0x00000000, 0x00000000, 0xCE73AD72, 0x3313AD72, 0x00003313, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF8430, 0x042007E0, 0xFFFFFFFF, 0xC638C638, 0x31863186, 0xC6383186, 0x0000738E, 0xF81FF81F, 0xC6388430, 0xC638C638, 0xFFFFFFFF, 0xFFFFFFFF, 0xC638C638, 0xC638C638, 0x738E8430, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB5B6F81F, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF0000, 0xD699D699, 0xD699D699, 0xD699D699, 0xD699D699, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xF81F0000, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0x667F04F9, 0xFFF904F9, 0xCE73FFF9, 0xCE73FFF9, 0xAD72CE73, 0xAD72CE73, 0x667F3313, 0x00003313, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8430738E, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x00008430, 0xF81FF81F, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x00008430, 0xF81FF81F, 0xB5B6F81F, 0xB5B6F81F, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xF81FB5B6, 0xF81FB5B6, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0x03130313, 0x04F90313, 0x94B294B2, 0x94B294B2, 0x94B294B2, 0x331394B2, 0x33133313, 0x00003313, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F}; LInlineBmp FileSelectIcons = { 160, 16, 16, IconBits }; enum Icons2Idx { IcoApps, IcoHome, IcoDesktop, IcoDocuments, IcoDownloads, IcoMovies, IcoMusic, IcoPhotos, IcoFolder, IcoComputer, IcoCDROM, IcoDrive, }; uint32_t Icons2[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF7FFFFFF, 0xFFF56264, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x0E000000, 0xFFFFFF93, 0xFFFFFFFF, 0xFFFFFFFF, 0xC3FFFFFF, 0x04042368, 0xFFB86421, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x5487BCEF, 0xFFFF1821, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xBFFFFFFF, 0x06062568, 0xFFBF6825, 0xFFFFFFFF, 0xFFFFFFFF, 0x43B0FFFF, 0x31313131, 0x31313131, 0xB0413131, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x62A4F8FF, 0xA8664D45, 0xFFFFFFF8, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x180089FF, 0xFFFFFFF5, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x31DEFFFF, 0xDD2F0000, 0xFF0000FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9800FFFF, 0x02319498, 0xFFFF8029, 0xFFFFFFFF, 0xFFFFFFFF, 0x0445DFFF, 0x1616120C, 0x3D040C14, 0xFFFFFFD5, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x23568CCF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0060FFFF, 0x88000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x4141D9FF, 0xB2B2A483, 0x414180A4, 0xFFFFFFD7, 0xFFFFFFFF, 0xBA35F6FF, 0xD9D9D9D9, 0xD9D9D9D9, 0x35BAD9D9, 0xFFFFFFF3, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x5E1F0E92, 0x2C4D4D76, 0xFFFF900F, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000D4FF, 0xFFFFFF7C, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0010B5FF, 0x0E000000, 0xFF0000B4, 0xFFFFFFFF, 0x1DC3FFFF, 0x00000000, 0x00000000, 0x00000000, 0xC11A0000, 0xFFFFFFFF, 0xFEFEFFFF, 0x9800FFFF, 0x00969898, 0xFF7E1AE3, 0xFFFFFFFF, 0xFFFFFFFF, 0x160C10C9, 0x16161616, 0x0C161616, 0xFFFFBC09, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000004, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE500FFFF, 0x00CBE5E5, 0xFFFFFF98, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC9900CC5, 0xB4B4B4C5, 0x78B4B4B4, 0xFFFFC30B, 0xFFFFFFFF, 0xD937D3FF, 0x1212127C, 0xD5701212, 0x37D9BAB2, 0xFFFFFFD3, 0x087EFFFF, 0x00000000, 0x00000000, 0x00000000, 0x7E080000, 0xFFFFFFFF, 0x3FF3FFFF, 0x94C15A16, 0x73C17070, 0xF442285B, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x006899A9, 0xFFFFE006, 0xFFFFFFFF, 0xFFFFFFFF, 0xFDFFFFFF, 0x0E000080, 0x000EABAB, 0xFF000000, 0xFFFFFFFF, 0xC321FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x1FC1FFFF, 0xFFFFFFFF, 0x000000FF, 0x47000000, 0x00989898, 0x7E18D6FF, 0xFFFFFFFF, 0xDFFFFFFF, 0x16160E12, 0x00000008, 0x16160800, 0xFFD7090E, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0x0200F3FF, 0x0014BCFF, 0x14000000, 0xFFFFFFBE, 0xFFFFFFFF, 0xE500FFFF, 0xBAE5E5E5, 0x00000060, 0x00000000, 0x64000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD9FFFFFF, 0xE7FFAC0E, 0xB4B4B6C3, 0xB4B4B4B4, 0xFFD70D81, 0xFFFFFFFF, 0xD943C3FF, 0xD9D9D9D9, 0xA2CBD9D9, 0x43D9B2C7, 0xFFFFFFBF, 0xCD0AFFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x0ACDCDCD, 0xFFFFFFFF, 0x2339FCFF, 0x6A92AA68, 0x6BBA6B6B, 0x3C249298, 0xFFFFFFFE, 0xFFFFFFFF, 0xDCFFFFFF, 0x0E9D0004, 0xFFFF5800, 0xFFFFFFFF, 0xFFFFFFFF, 0x49ECFFFF, 0xD22D0000, 0x2BD1F2F2, 0xEB000000, 0xFFFFFFFF, 0x0800FFFF, 0x08080808, 0x08080808, 0x06060606, 0x00060606, 0xFFFFFFFF, 0xE3E300FF, 0x04025CD9, 0x0098983D, 0x25DDFFFF, 0xFFFFFF8C, 0x45FFFFFF, 0x1616160A, 0xFFFFFF00, 0x161600FF, 0xFF340B16, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x62270000, 0xFFFF007E, 0xFFFFFFFF, 0xFFFFFFFF, 0x66431FCF, 0x43666666, 0xFFFFD723, 0xFFFFFFFF, 0xE500FFFF, 0xE5E5E5E5, 0xE5E5E5E5, 0xE5E5E5E5, 0x00E5E5E5, 0xFFFFFFFF, 0x020274FF, 0x02020202, 0x02020202, 0x02020202, 0xFFFFFF74, 0x39FFFFFF, 0xFFFFFF8A, 0x76769CD5, 0xB2B4B494, 0xFF3877B2, 0xFFFFFFFF, 0xD954A8FF, 0xAAC3DBD9, 0xC7989E9E, 0x54D9AAF3, 0xFFFFFFAA, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0x641691FF, 0x6666B47A, 0x9CB66C66, 0x176365A6, 0xFFFFFF94, 0xFFFFFFFF, 0xA5FFFFFF, 0x8DEE1F7E, 0xFFC80000, 0xFFFFFFFF, 0xFFFFFFFF, 0x001FCCFF, 0x91EC5C00, 0xEB854343, 0x1D00005A, 0xFFFFFFCB, 0x9800FFFF, 0x97979798, 0x97979797, 0x96969696, 0x00969696, 0xFFFFFFFF, 0xE3E300FF, 0x8700DDE3, 0x00982502, 0x02000000, 0xFFFFFF0C, 0x02CDFFFF, 0x16161616, 0xFFFFFF00, 0x161600FF, 0xB8031616, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x763B0400, 0xFFFFEBB0, 0xFFFF00FF, 0xFFFFFFFF, 0x00000035, 0x2D583D12, 0x582D1212, 0x00000E39, 0xFFFF3D00, 0x1000FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00100000, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xCDD1D1D1, 0xFFFFFF04, 0x41BCFFFF, 0xF6FCDBC9, 0x8A8A6074, 0xB2AE5A5E, 0xBA40B2B2, 0xFFFFFFFF, 0xD96698FF, 0xDBB2AAD5, 0xF3E5EDED, 0x66D9A6F3, 0xFFFFFF94, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0x604F10FB, 0x606068B4, 0x96FCD160, 0x92A66E60, 0xFFFFF611, 0xFFFFFFFF, 0x0AF7FFFF, 0xFDFFDE1F, 0xFF6A002F, 0xFFFFFFFF, 0xFFFFFFFF, 0x0400069B, 0x43F2F291, 0xF133DEDE, 0x00028EF1, 0xFFFF9C04, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xFE00E3E3, 0x98250AC5, 0x2D949898, 0xFFFFFF00, 0x0C74FFFF, 0x16161616, 0xFFFFFF00, 0x161600FF, 0x620C1616, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE700, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x66666600, 0x8C143966, 0x148CCFCD, 0x122F5239, 0xFFFF005E, 0x9C00FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x009CB2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0x8366FFFF, 0x70C9BABA, 0xE7E7E794, 0xB25990E7, 0x6581B2B2, 0xFFFFFFFF, 0xD9767EFF, 0xCDF3BFB6, 0xF3CD8585, 0x76D99AF1, 0xFFFFFF7C, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xC9B125AC, 0x8C6060E5, 0x60DBC5B8, 0x5868A4AC, 0xFFFFA61B, 0xFFFFFFFF, 0x0082FFFF, 0xFFFFFF31, 0xE30000E1, 0xFFFFFFFF, 0xFFFFFFFF, 0xC20E0068, 0x43F2F2F2, 0xF133FFFF, 0x0AC1F1F1, 0xFFFF6600, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xFE00E3E3, 0x2B0EC9FE, 0x92969898, 0xFFFFFF00, 0x1231FFFF, 0x16161616, 0xFFFFFF00, 0x161600FF, 0x1F141616, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x66666600, 0x62DB1F49, 0xD7620A0A, 0x12314721, 0xFFFF005E, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xA823FFFF, 0x5E94B4B4, 0x5E5EBFE7, 0x925FE7BF, 0x22A6B2B2, 0xFFFFFFFF, 0xD98C68FF, 0x6CF3EDA0, 0xF370D1D5, 0x8CD9A0ED, 0xFFFFFF68, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xFF622B66, 0x7EA2B6FF, 0xBCC35254, 0x5252526C, 0xFFFF642B, 0x0818FFFF, 0x0000E231, 0x0A0A23D8, 0x35006A7E, 0x166A78B0, 0xFFFFFFFF, 0xF2007AFE, 0x43F2F2F2, 0xF1334747, 0x00F1F1F1, 0xFFFFFE78, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0x0000E3E3, 0x00060000, 0x96969898, 0xFFFFFF00, 0x1414FFFF, 0x00081216, 0xFFFFFF00, 0x080000FF, 0x04161612, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x66666600, 0x394BAE1F, 0x4B396060, 0x5E521DB0, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xB206FFFF, 0x8A76B4B4, 0xF6F65EE7, 0x7588E75E, 0x05B0B2B2, 0xFFFFFFFF, 0xD99E52FF, 0xAAF3EDA0, 0xF3A86E6E, 0x9CD9A0ED, 0xFFFFFF50, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xB8492F4D, 0x49494994, 0xA8FF7E49, 0x49494949, 0xFFFF4531, 0x0000FFFF, 0x640043AB, 0x00000064, 0x0002CD00, 0x0008A8C1, 0xFFFFFFFF, 0xF200FFFF, 0xE2F2F2F2, 0xF1DFD2D2, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x005ADDE3, 0x96969898, 0xFFFFFF00, 0x141AFFFF, 0xFF980816, 0xFFFFFFFF, 0x98FFFFFF, 0x04161608, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x64666600, 0x660ADF00, 0x0A666666, 0x666402DF, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xAC0CFFFF, 0x8A76B4B4, 0xF6F65EE7, 0x7588E55E, 0x05B2B2B2, 0xFFFFFFFF, 0xD9AC39FF, 0xF3F6C3B6, 0xF6F3EDED, 0xAED9B6C3, 0xFFFFFF39, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xA8413F49, 0x41414141, 0x83C98841, 0x41414141, 0xFFFF452B, 0x4308FFFF, 0xD20200C6, 0x0A0A0A0A, 0x8AB25A00, 0x1808C383, 0xFFFFFFFF, 0xF200FFFF, 0xF2F2F2F2, 0xF1F1F2F2, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00D9E3E3, 0x96969898, 0xFFFFFF00, 0x1235FFFF, 0xBC001216, 0xFFFFFFFF, 0x02BCFFFF, 0x21141612, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x64666600, 0x660CDD04, 0x0C666666, 0x666204DD, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xA227FFFF, 0x6094B4B4, 0x605EBFE7, 0x925FE5BF, 0x21A8B2B2, 0xFFFFFFFF, 0xD9C123FF, 0xE5BAAAD5, 0xBAE3F8FA, 0xC1D9D5AA, 0xFFFFFF23, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xA8435A5E, 0x3B3B3B3B, 0xB43FA83B, 0x3B3B3B3D, 0xFFFF702B, 0xF9FFFFFF, 0xFF9F0012, 0xFFFFFFFF, 0x47FFFFFF, 0xFFFF7E00, 0xFFFFFFFF, 0xF200FFFF, 0x00F2F2F2, 0xF1000000, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00DDE3E3, 0x9696989A, 0xFFFFFF00, 0x0C6CFFFF, 0x0C0E1616, 0xFFFFFFDB, 0x0E0EDBFF, 0x640C1616, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00F5, 0xFFFFFFFF, 0x66666600, 0x392DC716, 0x2D396666, 0x666614C9, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0x885EFFFF, 0x5AB4B4B4, 0xE7E7E790, 0xC76F92E5, 0x5C85B8B8, 0xFFFFFFFF, 0xD9D30AFF, 0xAAC3DBD9, 0xC3AA9E9E, 0xD5D9D9DB, 0xFFFFFF08, 0xCD0AFFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x0ACDCDCD, 0xFFFFFFFF, 0xE1A814AC, 0x33333388, 0x56337C54, 0x70416EA2, 0xFFFFA62B, 0x51FFFFFF, 0xFFFB1200, 0xFFFFFFFF, 0xCBFFFFFF, 0xFFDBB03F, 0xFFFFFFFF, 0xF200FFFF, 0x00F2F2F2, 0xF1000000, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x4B4B9798, 0x4B4B4B4B, 0x49494949, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00DDE3E3, 0x9696989A, 0xFFFFFF00, 0x02C7FFFF, 0x0C161616, 0xFFFFF323, 0x160C23F3, 0xB9041616, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFAFFFFFF, 0xFFFFFF00, 0x8EDDFFFF, 0xFFFF0035, 0xFFFFFFFF, 0x66666600, 0x2FD93741, 0xD92F0E0E, 0x66663F39, 0xFFFF0066, 0xB002FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B0B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0x43BAFFFF, 0xAEB4B4B4, 0x888A5E5A, 0xFAF4735F, 0xB841C7D9, 0xFFFFFFFF, 0xD18C2FFF, 0xD9D9D9D9, 0xD9D9D9D9, 0x8ED1D9D9, 0xFFFFFF2F, 0x087EFFFF, 0x00000000, 0x00000000, 0x00000000, 0x7C080000, 0xFFFFFFFF, 0xFF720CFC, 0x2B2B49ED, 0x2B2B458E, 0x62EDFF9A, 0xFFFFF80C, 0x76F0FFFF, 0xFFFF9702, 0xFFFFFFFF, 0xFFFFFFFF, 0xE50C0094, 0xFFFFFFFF, 0xF200FFFF, 0x00F2F2F2, 0xF1006A00, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0xFF4B9798, 0xFFFF4BFF, 0x49FFFF4B, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00E3E3E3, 0x00000000, 0xFFFFFF00, 0x45FFFFFF, 0x1616160A, 0xFFFF410A, 0x16160A41, 0xFF350C16, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0x398CDBFF, 0xFFFFFF00, 0x000085FF, 0xFFFF0000, 0xFFFFFFFF, 0x66666600, 0xBC353966, 0x35BCDBDB, 0x66666639, 0xFFFF0066, 0xB302FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0xFFFF0202, 0x39FFFFFF, 0xB4B4B478, 0x757594B4, 0xFFFFD39A, 0xFF378CFF, 0xFFFFFFFF, 0x165000FF, 0x16161616, 0x16161616, 0x50161616, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x787878FF, 0xFFFFFF78, 0xFFFFFFFF, 0xFFFFFFFF, 0xA40A90FF, 0xAA947892, 0x9E98C9FF, 0x21BFFAAC, 0xFFFFFF96, 0x376EFFFF, 0xFFFFFEB7, 0xFFFFFFFF, 0xFFFFFFFF, 0x7C000083, 0xFFFFFFFF, 0xF300FFFF, 0x00F2F2F2, 0xF1005800, 0x00F1F1F1, 0xFFFFFFFF, 0x7421FFFF, 0xFF4B9798, 0xFFFF4BFF, 0x49FFFF4B, 0x1F749696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00E3E3E3, 0xFEFEFFFE, 0xFFFFFFFF, 0xDFFFFFFF, 0x16160E12, 0x6A680816, 0x16161608, 0xFFD9080E, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0x0000008A, 0xFFFFFF00, 0x00000CFF, 0xFFFF0C00, 0xFFFFFFFF, 0x66666600, 0x18436666, 0x43180000, 0x66666666, 0xFFFF0066, 0x9902FFFF, 0x98989899, 0x98989898, 0x98989898, 0x00989898, 0xFFFFFFFF, 0x02020280, 0x02020202, 0x02020202, 0x02020202, 0xFFFF7F02, 0xD9FFFFFF, 0xB4B4830E, 0xB2B2B2B4, 0xFFE5C1B4, 0xFFD90CAA, 0xFFFFFFFF, 0x1D9800FF, 0x98989898, 0x984B984B, 0x9898984B, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x9E9E9ED3, 0xFFFFD39E, 0xFFFFFFFF, 0xFFFFFFFF, 0x0A3BFFFF, 0x2F1D1DA4, 0x1D1D50D1, 0x3F081D1D, 0xFFFFFFFF, 0x2129FFFF, 0xFFFFFFE2, 0xFFFFFFFF, 0xFFFFFFFF, 0x450039F1, 0xFFFFFFFF, 0xF300FFFF, 0x00F2F2F2, 0xF1000000, 0x00F1F1F1, 0xFFFFFFFF, 0x10C3FFFF, 0x00000000, 0x00000000, 0x00000000, 0xBE100000, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00E3E3E3, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x160C0EC9, 0x0C0C1616, 0x0C161616, 0xFFFFBE0A, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0x0000000C, 0xFFFFFF04, 0x00001AFF, 0xFFFF4D00, 0xFFFFFFFF, 0x00000045, 0x00000000, 0x00000000, 0x00000000, 0xFFFF4300, 0x0235FFFF, 0x00020202, 0x00000000, 0x00000000, 0x31000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xB4780CC5, 0xB2B2B2B2, 0x8CC7C3B2, 0xFFFFC30A, 0xFFFFFFFF, 0x1D9800FF, 0x98989898, 0x984B984B, 0x9800984B, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x64646464, 0xFFFF6464, 0xFFFFFFFF, 0xFFFFFFFF, 0x39F6FFFF, 0x1616391D, 0x16161694, 0xF63D0610, 0xFFFFFFFF, 0xE82BFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x665CFEFF, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x043DDDFF, 0x1616120C, 0x35040C14, 0xFFFFFFD5, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0x0000001F, 0xFFFFFF43, 0x181DB8FF, 0xFFFFED5C, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x4239D9FF, 0xACB0A481, 0x374180A0, 0xFFFFFFD7, 0xFFFFFFFF, 0x1D7810FF, 0x98989898, 0x984B984B, 0x7898984B, 0xFFFFFF10, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x080C1088, 0x08080C62, 0xFFFE880A, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xBEFFFFFF, 0x020A2766, 0xFFB6621F, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x6E2521B8, 0xFFFFFFF1, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xB8FFFFFF, 0x08022164, 0xFFB86427, 0xFFFFFFFF, 0xFFFFFFFF, 0x00028CFF, 0x00000000, 0x00000000, 0x02000000, 0xFFFFFF8C, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x62A8FAFF, 0xAC624349, 0xFFFFFFFA, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, }; LInlineBmp TreeIconsImg = { 308, 22, 8, Icons2 }; ////////////////////////////////////////////////////////////////////////// char *LFileType::DefaultExtension() { char *Status = 0; auto T = LString(Extension()).SplitDelimit(";"); if (T.Length()) { char s[256]; strcpy(s, T[0]); char *Dir = strchr(s, '.'); if (Dir) { Status = NewStr(Dir+1); if (Status) strlwr(Status); } } return Status; } ////////////////////////////////////////////////////////////////////////// class LFolderItem : public LListItem { LFileSelectDlg *Dlg; LString Path; public: char *File; bool IsDir; LFolderItem(LFileSelectDlg *dlg, char *FullPath, LDirectory *Dir); ~LFolderItem(); void OnActivate(); const char *GetText(int i); int GetImage(int Flags); void OnSelect(); void OnDelete(bool Ask = true); void OnRename(); void OnMouseClick(LMouse &m); }; ////////////////////////////////////////////////////////////////////////// // This is just a private data container to make it easier to change the // implementation of this class without effecting headers and applications. class LFileSelectPrivate { friend class LFileSelect; friend class LFileSelectDlg; friend class LFolderList; LView *Parent = NULL; LFileSelect *Select = NULL; DlgType Type = TypeNone; LString Title; LString DefExt; bool MultiSelect = false; List Files; int CurrentType = -1; List Types; List History; bool ShowReadOnly = false; bool ReadOnly = false; bool EatClose = false; public: static LImageList *BtnIcons, *TreeIcons; static LString InitPath; static bool InitShowHiddenFiles; static LRect InitSize; LFileSelectPrivate(LFileSelect *select) { Select = select; if (!BtnIcons) BtnIcons = new LImageList(16, 16, FileSelectIcons.Create(0xF81F)); if (!TreeIcons) { LAutoPtr a(TreeIconsImg.Create(0xF81F)); LAutoPtr m(new LMemDC(a->X(), a->Y(), System32BitColourSpace)); if (a && m) { LColour fore(128, 128, 128); for (int y=0; yY(); y++) { uint8_t *i = (uint8_t*)(*a)[y]; auto *e = i + a->X(); System32BitPixel *o = (System32BitPixel*)(*m)[y]; while (i < e) { o->r = (int)fore.r() * *i / 255; o->g = (int)fore.g() * *i / 255; o->b = (int)fore.b() * *i / 255; o->a = *i; i++; o++; } } TreeIcons = new LImageList(22, 22, m.Release()); } } } virtual ~LFileSelectPrivate() { Types.DeleteObjects(); Files.DeleteArrays(); History.DeleteArrays(); } }; LImageList *LFileSelectPrivate::BtnIcons = NULL; LImageList *LFileSelectPrivate::TreeIcons = NULL; LString LFileSelectPrivate::InitPath; bool LFileSelectPrivate::InitShowHiddenFiles = false; LRect LFileSelectPrivate::InitSize(0, 0, -1, -1); ////////////////////////////////////////////////////////////////////////// // This class implements the UI for the selector. class LFileSelectDlg; class LFolderView { protected: LFileSelectDlg *Dlg; public: LFolderView(LFileSelectDlg *dlg) { Dlg = dlg; } virtual void OnFolder() {} }; class LFolderDrop : public LDropDown, public LFolderView { public: LFolderDrop(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy); void OnFolder(); bool OnLayout(LViewLayoutInfo &Inf) { Inf.Width.Min = Inf.Width.Max = 18; return true; } }; class LIconButton : public LLayout { LImageList *Icons; int Icon; bool Down; public: LIconButton(int Id, int x, int y, int cx, int cy, LImageList *icons, int icon) { Icons = icons; Icon = icon; SetId(Id); LRect r(x, y, x+cx, y+cy); SetPos(r); Down = false; SetTabStop(true); } void OnPaint(LSurface *pDC) { LRect c = GetClient(); LColour Background(L_MED); c.Offset(-c.x1, -c.y1); LWideBorder(pDC, c, Down ? DefaultSunkenEdge : DefaultRaisedEdge); pDC->Colour(Background); pDC->Rectangle(&c); int x = (c.X()-Icons->TileX()) / 2; int y = (c.Y()-Icons->TileY()) / 2; if (Focus()) { #if WINNATIVE RECT r = c; DrawFocusRect(pDC->Handle(), &r); #endif } Icons->Draw(pDC, c.x1+x+Down, c.y1+y+Down, Icon, Background, !Enabled()); } void OnFocus(bool f) { Invalidate(); } void OnMouseClick(LMouse &m) { if (Enabled()) { bool Trigger = Down && !m.Down(); Capture(Down = m.Down()); if (Down) Focus(true); Invalidate(); if (Trigger) { LViewI *n=GetNotify()?GetNotify():GetParent(); if (n) n->OnNotify(this, LNotification(m)); } } } void OnMouseEnter(LMouse &m) { if (IsCapturing()) { Down = true; Invalidate(); } } void OnMouseExit(LMouse &m) { if (IsCapturing()) { Down = false; Invalidate(); } } bool OnKey(LKey &k) { if (k.c16 == ' ' || k.c16 == LK_RETURN) { if (Enabled() && Down ^ k.Down()) { Down = k.Down(); Invalidate(); if (!Down) { LViewI *n=GetNotify()?GetNotify():GetParent(); if (n) n->OnNotify(this, LNotifyActivate); } } return true; } return false; } bool OnLayout(LViewLayoutInfo &Inf) { Inf.Width.Min = Inf.Width.Max = Icons->TileX() + 4; Inf.Width.Max += 4; Inf.Height.Min = Inf.Height.Max = Icons->TileY() + 4; Inf.Height.Max += 4; return true; } }; class LFolderList : public LList, public LFolderView { LString FilterKey; public: LFolderList(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy); void OnFolder(); bool OnKey(LKey &k); void SetFilterKey(LString s) { FilterKey = s; OnFolder(); } }; enum Ctrls { IDC_STATIC = -1, IDD_FILE_SELECT = 1000, IDC_PATH, IDC_DROP, IDC_BACK, IDC_UP, IDC_NEW, IDC_VIEW, IDC_FILE, IDC_TYPE, IDC_SHOWHIDDEN, IDC_SUB_TBL, IDC_BOOKMARKS, IDC_FILTER, IDC_FILTER_CLEAR, }; #if 1 #define USE_FOLDER_CTRL 1 enum FolderCtrlMessages { M_DELETE_EDIT = M_USER + 100, M_NOTIFY_VALUE_CHANGED, M_LOST_FOCUS, }; class FolderCtrlEdit : public LEdit { public: FolderCtrlEdit(int id, LRect c) : LEdit(id, c.x1, c.y1, c.X()-1, c.Y()-1) { } void OnFocus(bool f) { printf("OnFocus(%i)\n", f); if (!f && GetParent()) GetParent()->PostEvent(M_LOST_FOCUS); } }; class FolderCtrl : public LView { struct Part { LAutoPtr ds; LRect Arrow; LRect Text; }; LEdit *e; LArray p; Part *Over; ssize_t Cursor; Part *HitPart(int x, int y, int *Sub = NULL) { for (unsigned i=0; iGetHeight() + 4; Inf.Height.Max = Inf.Height.Min; } return true; } LString NameAt(ssize_t Level) { LString n; #ifndef WINDOWS n += "/"; #endif for (unsigned i=0; i<=Level && iTransparent(false); LDisplayString Arrow(f, ">"); for (unsigned i=0; iArrow.ZOff(Arrow.X()+1, c.Y()-1); n->Arrow.Offset(c.x1, c.y1); f->Colour(LColour(192,192,192), Bk); Arrow.DrawCenter(pDC, &n->Arrow); c.x1 = n->Arrow.x2 + 1; if (n->ds) { // Layout and draw text n->Text.ZOff(n->ds->X() + 4, c.Y()-1); n->Text.Offset(c.x1, c.y1); f->Colour(Fore, Bk); n->ds->DrawCenter(pDC, &n->Text); c.x1 = n->Text.x2 + 1; } } if (p.Length() == 0) { // Layout and draw arrow for the "root folder" f->Colour(LColour(192,192,192), L_WORKSPACE); LRect a; a.ZOff(Arrow.X()+1, c.Y()-1); a.Offset(c.x1, c.y1); Arrow.DrawCenter(pDC, &a); c.x1 = a.x2 + 1; } pDC->Colour(L_WORKSPACE); pDC->Rectangle(&c); } void ExitEditMode() { if (e) { Name(e->Name()); DeleteObj(e); PostEvent(M_DELETE_EDIT); PostEvent(M_NOTIFY_VALUE_CHANGED); Invalidate(); } } void OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { } else if (m.Left()) { if (m.Down()) { Over = HitPart(m.x, m.y); if (p.PtrCheck(Over)) { // Over a path node... Cursor = Over - p.AddressOf(0); Part &o = p[Cursor]; Invalidate(); SendNotify(LNotifyValueChanged); if (o.Arrow.Overlap(m.x, m.y)) { // Show sub-menu at this level ShowMenu(Cursor); } } else if (!e) { // In empty space LRect c = GetClient(); e = new FolderCtrlEdit(GetId()+1, c); if (e) { e->Attach(this); LString s = Name(); e->Name(s); e->SetCaret(s.Length()); e->Focus(true); } } } } } void OnMouseMove(LMouse &m) { Part *o = Over; Over = HitPart(m.x, m.y); if (o != Over) Invalidate(); } void OnMouseExit(LMouse &m) { if (Over) { Over = NULL; Invalidate(); } } int OnNotify(LViewI *c, LNotification n) { if (e != NULL && c->GetId() == e->GetId()) { if (n.Type == LNotifyReturnKey) { ExitEditMode(); } } return 0; } LMessage::Result OnEvent(LMessage *m) { switch (m->Msg()) { case M_LOST_FOCUS: { ExitEditMode(); break; } case M_DELETE_EDIT: { DeleteObj(e); break; } case M_NOTIFY_VALUE_CHANGED: { SendNotify(LNotifyValueChanged); break; } } return LView::OnEvent(m); } virtual bool ShowMenu(ssize_t Level) { if (Level <= 0) return false; LString dir = NameAt(Level-1); LSubMenu s; LDirectory d; LString::Array Opts; for (int b = d.First(dir); b; b = d.Next()) { if (d.IsDir()) { Opts.New() = d.GetName(); s.AppendItem(d.GetName(), (int)Opts.Length()); } } Part &i = p[Level]; LPoint pt(i.Arrow.x1, i.Arrow.y2+1); PointToScreen(pt); int Cmd = s.Float(this, pt.x, pt.y, true); if (Cmd) { LString np; np = dir + DIR_STR + Opts[Cmd-1]; Name(np); PostEvent(M_NOTIFY_VALUE_CHANGED); } else return false; return true; } }; #else #define USE_FOLDER_CTRL 0 #endif class LFileSelectDlg : public LDialog { LRect OldPos; LRect MinSize; LArray Links; LArray Hidden; public: LFileSelectPrivate *d = NULL; LTableLayout *Tbl = NULL; LBox *Sub = NULL; LTree *Bookmarks = NULL; LTextLabel *Ctrl1 = NULL; #if USE_FOLDER_CTRL FolderCtrl *Ctrl2 = NULL; #else LEdit *Ctrl2 = NULL; #endif LFolderDrop *Ctrl3 = NULL; LIconButton *BackBtn = NULL; LIconButton *UpBtn = NULL; LIconButton *NewDirBtn = NULL; LFolderList *FileLst = NULL; LTextLabel *Ctrl8 = NULL; LTextLabel *Ctrl9 = NULL; LEdit *FileNameEdit = NULL; LCombo *FileTypeCbo = NULL; LButton *SaveBtn = NULL; LButton *CancelBtn = NULL; LCheckBox *ShowHidden = NULL; LEdit *FilterEdit = NULL; LFileSelectDlg(LFileSelectPrivate *Select); ~LFileSelectDlg(); const char *GetClass() override { return "LFileSelectDlg"; } - int OnNotify(LViewI *Ctrl, LNotification n); + int OnNotify(LViewI *Ctrl, LNotification n) override; void OnUpFolder(); void SetFolder(char *f); void OnFolder(); void OnFile(char *f); void OnFilter(const char *Key); - bool OnViewKey(LView *v, LKey &k) + bool OnViewKey(LView *v, LKey &k) override { if (k.vkey == LK_UP && k.Alt()) { if (k.Down()) { UpBtn->SendNotify(); } return true; } return false; } void Add(LTreeItem *i, LVolume *v) { if (!i || !v) return; auto Path = v->Path(); i->SetText(v->Name()); i->SetText(Path, 1); for (unsigned n=0; nFirst(); cv; cv = cv->Next()) { LTreeItem *ci = new LTreeItem; if (ci) { i->Insert(ci); Add(ci, cv); } } i->Expanded(true); } }; LFileSelectDlg::LFileSelectDlg(LFileSelectPrivate *select) { d = select; SetParent(d->Parent); MinSize.ZOff(450, 300); if (!d->InitSize.Valid()) { auto Dpi = LScreenDpi(); auto Scale = (float)Dpi.x / 96.0; d->InitSize.Set(0, 0, (int)(650*Scale), (int)(450*Scale) + LAppInst->GetMetric(LGI_MET_DECOR_Y) ); } SetPos(d->InitSize); int x = 0, y = 0; AddView(Tbl = new LTableLayout); // Top Row auto *c = Tbl->GetCell(x++, y); c->Add(Ctrl1 = new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Look in:")); c->VerticalAlign(LCss::Len(LCss::VerticalMiddle)); c = Tbl->GetCell(x++, y); #if USE_FOLDER_CTRL c->Add(Ctrl2 = new FolderCtrl(IDC_PATH)); #else c->Add(Ctrl2 = new LEdit(IDC_PATH, 0, 0, 245, 21, "")); #endif c = Tbl->GetCell(x++, y); c->Add(Ctrl3 = new LFolderDrop(this, IDC_DROP, 336, 7, 16, 21)); c = Tbl->GetCell(x++, y); c->Add(BackBtn = new LIconButton(IDC_BACK, 378, 7, 27, 21, d->BtnIcons, FSI_BACK)); c = Tbl->GetCell(x++, y); c->Add(UpBtn = new LIconButton(IDC_UP, 406, 7, 27, 21, d->BtnIcons, FSI_UPDIR)); c = Tbl->GetCell(x++, y); c->Add(NewDirBtn = new LIconButton(IDC_NEW, 434, 7, 27, 21, d->BtnIcons, FSI_NEWDIR)); // Folders/items row x = 0; y++; c = Tbl->GetCell(x, y, true, 6, 1); c->Add(Sub = new LBox(IDC_SUB_TBL)); Sub->AddView(Bookmarks = new LTree(IDC_BOOKMARKS, 0, 0, -1, -1)); Bookmarks->GetCss(true)->Width(LCss::Len(LCss::LenPx, 150.0f)); Bookmarks->SetImageList(d->TreeIcons, false); LTableLayout *t; Sub->AddView(t = new LTableLayout(11)); // Filter / search row c = t->GetCell(0, 0); c->Add(new LCheckBox(IDC_FILTER_CLEAR, 0, 0, -1, -1, "Filter items:")); c->VerticalAlign(LCss::Len(LCss::VerticalMiddle)); c = t->GetCell(1, 0); c->Add(FilterEdit = new LEdit(IDC_FILTER, 0, 0, 60, 20)); c = t->GetCell(0, 1, true, 2); c->Add(FileLst = new LFolderList(this, IDC_VIEW, 14, 35, 448, 226)); // File name row x = 0; y++; c = Tbl->GetCell(x++, y); c->Add(Ctrl8 = new LTextLabel(IDC_STATIC, 14, 275, -1, -1, "File name:")); c = Tbl->GetCell(x, y, true, 2); x += 2; c->Add(FileNameEdit = new LEdit(IDC_FILE, 100, 268, 266, 21, "")); c = Tbl->GetCell(x, y, true, 3); c->Add(SaveBtn = new LButton(IDOK, 392, 268, 70, 21, "Ok")); // 4th row x = 0; y++; c = Tbl->GetCell(x++, y); c->Add(Ctrl9 = new LTextLabel(IDC_STATIC, 14, 303, -1, -1, "Files of type:")); c = Tbl->GetCell(x, y, true, 2); x += 2; c->Add(FileTypeCbo = new LCombo(IDC_TYPE, 100, 296, 266, 21, "")); c = Tbl->GetCell(x++, y, true, 3); c->Add(CancelBtn = new LButton(IDCANCEL, 392, 296, 70, 21, "Cancel")); // 5th row x = 0; y++; c = Tbl->GetCell(x++, y, true, 6); c->Add(ShowHidden = new LCheckBox(IDC_SHOWHIDDEN, 14, 326, -1, -1, "Show hidden files.")); // Init if (BackBtn) BackBtn->Enabled(false); if (SaveBtn) SaveBtn->Enabled(false); if (FileLst) FileLst->MultiSelect(d->MultiSelect); if (ShowHidden) ShowHidden->Value(d->InitShowHiddenFiles); // Load types if (!d->Types.Length()) { LFileType *t = new LFileType; if (t) { t->Description("All Files"); t->Extension(LGI_ALL_FILES); d->Types.Insert(t); } } for (auto t: d->Types) { char s[256]; sprintf(s, "%s (%s)", t->Description(), t->Extension()); if (FileTypeCbo) FileTypeCbo->Insert(s); } d->CurrentType = 0; // File + Path char *File = d->Files[0]; if (File) { char *Dir = strrchr(File, DIR_CHAR); if (Dir) { OnFile(Dir + 1); } else { OnFile(File); } } if (d->InitPath) { SetFolder(d->InitPath); } else { SetFolder(LGetExePath()); } OnFolder(); // Size/layout SetPos(d->InitSize); MoveToCenter(); RegisterHook(this, LKeyEvents); FileLst->Focus(true); LgiGetUsersLinks(Links); auto v = FileDev->GetRootVolume(); if (v) { for (auto vol = v; vol; vol = vol->Next()) { if (auto *ti = new LTreeItem) { Bookmarks->Insert(ti); Add(ti, vol); ti->Expanded(true); } } } if (Links.Length()) { if (auto *ti = new LTreeItem) { ti->SetText("Bookmarks"); Bookmarks->Insert(ti); for (unsigned n=0; nSetText(leaf?leaf+1:p, 0); ci->SetText(p, 1); ti->Insert(ci); } } ti->Expanded(true); } } } LFileSelectDlg::~LFileSelectDlg() { UnregisterHook(this); d->InitShowHiddenFiles = ShowHidden ? ShowHidden->Value() : false; d->InitSize = GetPos(); auto CurPath = GetCtrlName(IDC_PATH); if (ValidStr(CurPath)) d->InitPath = CurPath; } void LFileSelectDlg::OnFile(char *f) { if (d->Type != TypeOpenFolder) { FileNameEdit->Name(f ? f : (char*)""); SaveBtn->Enabled(ValidStr(f)); } } void LFileSelectDlg::SetFolder(char *f) { auto CurPath = GetCtrlName(IDC_PATH); if (CurPath) { d->History.Insert(NewStr(CurPath)); SetCtrlEnabled(IDC_BACK, true); } SetCtrlName(IDC_PATH, f); } void LFileSelectDlg::OnFolder() { if (Ctrl3) Ctrl3->OnFolder(); if (FileLst) FileLst->OnFolder(); auto CurPath = GetCtrlName(IDC_PATH); if (CurPath && UpBtn) UpBtn->Enabled(strlen(CurPath)>3); } void LFileSelectDlg::OnUpFolder() { auto Cur = GetCtrlName(IDC_PATH); if (Cur) { char Dir[MAX_PATH_LEN]; strcpy(Dir, Cur); if (strlen(Dir) > 3) { LTrimDir(Dir); if (!strchr(Dir, DIR_CHAR)) strcat(Dir, DIR_STR); SetFolder(Dir); OnFolder(); } } } void LFileSelectDlg::OnFilter(const char *Key) { if (FileLst) FileLst->SetFilterKey(Key); } int LFileSelectDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_BOOKMARKS: { if (n.Type == LNotifyItemSelect && Bookmarks) { LTreeItem *s = Bookmarks->Selection(); if (s) { const char *p = s->GetText(1); if (LDirExists(p)) { SetCtrlName(IDC_PATH, p); OnFolder(); } } } break; } case IDC_PATH: { if (n.Type == LNotifyValueChanged) OnFolder(); break; } case IDC_VIEW: { if (FileLst) { /* These functions are handled by the list control's OnKey implementation if (Flags == GLIST_NOTIFY_RETURN) { List s; if (FileLst->GetSelection(s)) { LFolderItem *i = dynamic_cast(s.First()); if (i) { i->OnActivate(); } } } else if (Flags == GLIST_NOTIFY_BACKSPACE) { OnUpFolder(); } */ } break; } case IDC_FILE: { auto f = Ctrl->Name(); if (!f) break; if (n.Type == LNotifyReturnKey) { // allow user to insert new type by typing the pattern into the file name edit box and // hitting enter if (strchr(f, '?') || strchr(f, '*')) { // it's a mask, push the new type on the the type stack and // refilter the content int TypeIndex = -1; int n = 0; for (auto t: d->Types) { if (t->Extension() && stricmp(t->Extension(), f) == 0) { TypeIndex = n; break; } n++; } // insert the new type if not already there if (TypeIndex < 0) { LFileType *n = new LFileType; if (n) { n->Description(f); n->Extension(f); TypeIndex = (int)d->Types.Length(); d->Types.Insert(n); FileTypeCbo->Insert(f); } } // select the new type if (TypeIndex >= 0) { FileTypeCbo->Value(d->CurrentType = TypeIndex); } // clear the edit box Ctrl->Name(""); // Update and don't do normal save btn processing. OnFolder(); // Skip the IDOK message generated by the default button d->EatClose = true; printf("%s:%i - eat close true\n", _FL); break; } if (LDirExists(f)) { // Switch to the folder... SetCtrlName(IDC_PATH, f); OnFolder(); Ctrl->Name(NULL); d->EatClose = true; } else if (LFileExists(f)) { // Select the file... d->Files.Insert(NewStr(f)); EndModal(IDOK); break; } } bool HasFile = ValidStr(f); bool BtnEnabled = SaveBtn->Enabled(); if (HasFile ^ BtnEnabled) { SaveBtn->Enabled(HasFile); } break; } case IDC_BACK: { auto It = d->History.rbegin(); char *Dir = *It; if (Dir) { d->History.Delete(Dir); SetCtrlName(IDC_PATH, Dir); OnFolder(); DeleteArray(Dir); if (!d->History[0]) { SetCtrlEnabled(IDC_BACK, false); } } break; } case IDC_SHOWHIDDEN: { FileLst->OnFolder(); break; } case IDC_TYPE: { d->CurrentType = (int)FileTypeCbo->Value(); FileLst->OnFolder(); if (d->Type == TypeSaveFile) { // change extension of current file LFileType *Type = d->Types.ItemAt(d->CurrentType); auto File = FileNameEdit->Name(); if (Type && File) { char *Ext = strchr(File, '.'); if (Ext) { char *DefExt = Type->DefaultExtension(); if (DefExt) { Ext++; char s[256]; ZeroObj(s); memcpy(s, File, Ext-File); strcat(s, DefExt); OnFile(s); DeleteArray(DefExt); } } } } break; } case IDC_UP: { OnUpFolder(); break; } case IDC_FILTER: { const char *n = Ctrl->Name(); SetCtrlValue(IDC_FILTER_CLEAR, ValidStr(n)); OnFilter(n); break; } case IDC_FILTER_CLEAR: { if (!Ctrl->Value()) { SetCtrlName(IDC_FILTER, NULL); OnFilter(NULL); } break; } case IDC_NEW: { LInput Dlg(this, "", "Create new folder:", "New Folder"); Dlg.DoModal([&](auto d, auto code) { char New[MAX_PATH_LEN]; strcpy(New, GetCtrlName(IDC_PATH)); if (New[strlen(New)-1] != DIR_CHAR) strcat(New, DIR_STR); strcat(New, Dlg.GetStr()); FileDev->CreateFolder(New); OnFolder(); }); break; } case IDOK: { if (d->EatClose) { printf("%s:%i - SKIPPING eat close false\n", _FL); d->EatClose = false; break; } auto Path = GetCtrlName(IDC_PATH); auto File = GetCtrlName(IDC_FILE); if (Path) { char f[MAX_PATH_LEN]; d->Files.DeleteArrays(); if (d->Type == TypeOpenFolder) { d->Files.Insert(NewStr(Path)); } else { List Sel; if (d->Type != TypeSaveFile && FileLst && FileLst->GetSelection(Sel) && Sel.Length() > 1) { for (auto i: Sel) { LMakePath(f, sizeof(f), Path, i->GetText(0)); d->Files.Insert(NewStr(f)); } } else if (ValidStr(File)) { if (strchr(File, DIR_CHAR)) strcpy_s(f, sizeof(f), File); else LMakePath(f, sizeof(f), Path, File); d->Files.Insert(NewStr(f)); } } } // fall thru } case IDCANCEL: { EndModal(Ctrl->GetId()); break; } } return 0; } ////////////////////////////////////////////////////////////////////////// class LFileSystemItem : public LTreeItem { class LFileSystemPopup *Popup; LString Path; public: LFileSystemItem(LFileSystemPopup *popup, LVolume *vol, char *path = 0); char *GetPath() { return Path; } void OnPath(const char *p); void OnMouseClick(LMouse &m); bool OnKey(LKey &k); }; #define IDC_TREE 100 class LFileSystemPopup : public LPopup { friend class LFileSystemItem; LFileSelectDlg *Dlg; LTree *Tree; LFileSystemItem *Root; public: LFileSystemPopup(LView *owner, LFileSelectDlg *dlg, int x) : LPopup(owner) { Dlg = dlg; LRect r(0, 0, x, 150); SetPos(r); Children.Insert(Tree = new LTree(IDC_TREE, 1, 1, X()-3, Y()-3)); if (Tree) { Tree->Sunken(false); LVolume *v = FileDev->GetRootVolume(); if (v) { Tree->SetImageList(Dlg->d->BtnIcons, false); Tree->Insert(Root = new LFileSystemItem(this, v)); for (auto next = v->Next(); next; next = next->Next()) { Tree->SetImageList(Dlg->d->BtnIcons, false); Tree->Insert(Root = new LFileSystemItem(this, next)); } } } } ~LFileSystemPopup() { } void Visible(bool i) { if (i && Root) { Root->OnPath(Dlg->GetCtrlName(IDC_PATH)); } LPopup::Visible(i); } void OnPaint(LSurface *pDC) { // Draw border LRect c = GetClient(); c.Offset(-c.x1, -c.y1); pDC->Colour(L_BLACK); pDC->Box(&c); c.Inset(1, 1); } void OnActivate(LFileSystemItem *i) { if (i) { Dlg->SetFolder(i->GetPath()); Dlg->OnFolder(); Visible(false); } } }; LFileSystemItem::LFileSystemItem(LFileSystemPopup *popup, LVolume *Vol, char *path) { Popup = popup; Expanded(true); if (Vol) { Path = Vol->Path(); SetText(Vol->Name()); switch (Vol->Type()) { case VT_FLOPPY: SetImage(FSI_FLOPPY); break; case VT_HARDDISK: case VT_RAMDISK: SetImage(FSI_HARDDISK); break; case VT_CDROM: SetImage(FSI_CDROM); break; case VT_NETWORK_SHARE: SetImage(FSI_NETWORK); break; case VT_DESKTOP: SetImage(FSI_DESKTOP); break; default: SetImage(FSI_DIRECTORY); break; } for (LVolume *v=Vol->First(); v; v=v->Next()) { Insert(new LFileSystemItem(Popup, v)); } } else { Path = NewStr(path); SetText(strrchr(Path, DIR_CHAR)+1); SetImage(FSI_DIRECTORY); } } void LFileSystemItem::OnPath(const char *p) { switch (GetImage()) { case FSI_DESKTOP: { if (p && Path && stricmp(Path, p) == 0) { Select(true); p = 0; } break; } case FSI_DIRECTORY: { return; } default: { LTreeItem *Old = Items[0]; if (Old) { Old->Remove(); DeleteObj(Old); } break; } } if (p) { auto PathLen = strlen(Path); if (Path && strnicmp(Path, p, PathLen) == 0 && (p[PathLen] == DIR_CHAR || p[PathLen] == 0) #ifdef LINUX && strcmp(Path, "/") != 0 #endif ) { LTreeItem *Item = this; if (GetImage() != FSI_DESKTOP && strlen(p) > 3) { auto Start = p + strlen(Path); if (Start) { char s[256]; strcpy(s, Path); auto T = LString(Start).SplitDelimit(DIR_STR); for (int i=0; iInsert(New); Item = New; } } } } if (Item) { Item->Select(true); } } } for (auto item: Items) { LFileSystemItem *i = dynamic_cast(item); if (i) i->OnPath(p); } } void LFileSystemItem::OnMouseClick(LMouse &m) { if (m.Left() && m.Down()) { Popup->OnActivate(this); } } bool LFileSystemItem::OnKey(LKey &k) { if ((k.c16 == ' ' || k.c16 == LK_RETURN)) { if (k.Down() && k.IsChar) { Popup->OnActivate(this); } return true; } return false; } LFolderDrop::LFolderDrop(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy) : LDropDown(Id, x, y, cx, cy, 0), LFolderView(dlg) { SetPopup(new LFileSystemPopup(this, dlg, cx + (dlg->Ctrl2 ? dlg->Ctrl2->X() : 0) )); } void LFolderDrop::OnFolder() { } ////////////////////////////////////////////////////////////////////////// #define IDM_OPEN 1000 #define IDM_CUT 1001 #define IDM_COPY 1002 #define IDM_RENAME 1003 #define IDM_PROPERTIES 1004 #define IDM_CREATE_SHORTCUT 1005 #define IDM_DELETE 1006 LFolderItem::LFolderItem(LFileSelectDlg *dlg, char *FullPath, LDirectory *Dir) { Dlg = dlg; Path = FullPath; File = strrchr(Path, DIR_CHAR); if (File) File++; IsDir = Dir->IsDir(); } LFolderItem::~LFolderItem() { } const char *LFolderItem::GetText(int i) { return File; } int LFolderItem::GetImage(int Flags) { return IsDir ? 1 : 0; } void LFolderItem::OnSelect() { if (!IsDir && File) { Dlg->OnFile(Select() ? File : 0); } } void LFolderItem::OnDelete(bool Ask) { if (!Ask || LgiMsg(Parent, "Do you want to delete '%s'?", ModuleName, MB_YESNO, Path.Get()) == IDYES) { bool Status = false; if (IsDir) { Status = FileDev->RemoveFolder(Path, true); } else { Status = FileDev->Delete(Path); } if (Status) { Parent->Remove(this); delete this; } } } void LFolderItem::OnRename() { LInput *Inp = new LInput(Dlg, File, "New name:", Dlg->Name()); Inp->DoModal([&](auto d, auto code) { if (!code) return; char Old[MAX_PATH_LEN]; strcpy_s(Old, sizeof(Old), Path); char New[MAX_PATH_LEN]; File[0] = 0; LMakePath(New, sizeof(New), Path, Inp->GetStr()); if (FileDev->Move(Old, New)) { DeleteArray(Path); Path = NewStr(New); File = strrchr(Path, DIR_CHAR); if (File) File++; Update(); } else { LgiMsg(Dlg, "Renaming '%s' failed.", Dlg->Name(), MB_OK); } delete Inp; }); } void LFolderItem::OnActivate() { if (File) { if (IsDir) { char Dir[256]; strcpy(Dir, Dlg->GetCtrlName(IDC_PATH)); if (Dir[strlen(Dir)-1] != DIR_CHAR) strcat(Dir, DIR_STR); strcat(Dir, File); Dlg->SetFolder(Dir); Dlg->OnFolder(); } else // Is file { Dlg->OnNotify(Dlg->SaveBtn, LNotifyActivate); } } } void LFolderItem::OnMouseClick(LMouse &m) { if (m.Down()) { if (m.Left()) { if (m.Double()) { OnActivate(); } } else if (m.Right()) { LSubMenu *RClick = new LSubMenu; if (RClick) { RClick->AppendItem("Select", IDM_OPEN, true); RClick->AppendSeparator(); RClick->AppendItem("Cut", IDM_CUT, false); RClick->AppendItem("Copy", IDM_COPY, false); RClick->AppendSeparator(); RClick->AppendItem("Create Shortcut", IDM_CREATE_SHORTCUT, false); RClick->AppendItem("Delete", IDM_DELETE, true); RClick->AppendItem("Rename", IDM_RENAME, true); RClick->AppendSeparator(); RClick->AppendItem("Properties", IDM_PROPERTIES, false); if (Parent->GetMouse(m, true)) { switch (RClick->Float(Parent, m.x, m.y)) { case IDM_OPEN: { break; } case IDM_DELETE: { OnDelete(); break; } case IDM_RENAME: { OnRename(); break; } } } DeleteObj(RClick); } } } } int LFolderItemCompare(LListItem *A, LListItem *B, NativeInt Data) { LFolderItem *a = dynamic_cast(A); LFolderItem *b = dynamic_cast(B); if (a && b) { if (a->IsDir ^ b->IsDir) { if (a->IsDir) return -1; else return 1; } else if (a->File && b->File) { return stricmp(a->File, b->File); } } return 0; } LFolderList::LFolderList(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy) : LList(Id, x, y, cx, cy), LFolderView(dlg) { SetImageList(Dlg->d->BtnIcons, false); ShowColumnHeader(false); AddColumn("Name", cx-20); SetMode(LListColumns); } bool LFolderList::OnKey(LKey &k) { bool Status = LList::OnKey(k); switch (k.vkey) { case LK_BACKSPACE: { if (k.Down() && GetWindow()) { // Go up a directory LViewI *v = GetWindow()->FindControl(IDC_UP); if (v) { GetWindow()->OnNotify(v, LNotifyBackspaceKey); } } Status = true; break; } case LK_RETURN: #ifdef LK_KP_ENTER case LK_KP_ENTER: #endif { if (k.Down() && GetWindow()) { LFolderItem *Sel = dynamic_cast(GetSelected()); if (Sel) { if (Sel->IsDir) { auto Cur = GetWindow()->GetCtrlName(IDC_PATH); if (Cur) { char Path[256]; LMakePath(Path, sizeof(Path), Cur, Sel->GetText(0)); if (LDirExists(Path)) { GetWindow()->SetCtrlName(IDC_PATH, Path); Dlg->OnFolder(); } } } else { LViewI *Ok = GetWindow()->FindControl(IDOK); if (Ok) { GetWindow()->SetCtrlName(IDC_FILE, Sel->GetText(0)); GetWindow()->OnNotify(Ok, LNotification(k)); } } } } Status = true; break; } case LK_DELETE: { if (k.Down() && !k.IsChar && GetWindow()) { List Sel; if (GetSelection(Sel)) { LStringPipe Msg; Msg.Push("Do you want to delete:\n\n"); List Delete; for (auto i: Sel) { LFolderItem *s = dynamic_cast(i); if (s) { Delete.Insert(s); Msg.Push("\t"); Msg.Push(s->GetText(0)); Msg.Push("\n"); } } char *Mem = Msg.NewStr(); if (Mem) { if (LgiMsg(this, Mem, ModuleName, MB_YESNO) == IDYES) { for (auto d: Delete) { d->OnDelete(false); } } DeleteArray(Mem); } } } Status = true; break; } } // LgiTrace("%s:%i LFolderList::OnKey, key=%i down=%i status=%i\n", _FL, k.vkey, k.Down(), Status); return Status; } void LFolderList::OnFolder() { Empty(); LDirectory Dir; List New; // Get current type LFileType *Type = Dlg->d->Types.ItemAt(Dlg->d->CurrentType); List Ext; if (Type) { auto T = LString(Type->Extension()).SplitDelimit(";"); for (size_t i=0; iCtrl2) return; bool ShowHiddenFiles = Dlg->ShowHidden ? Dlg->ShowHidden->Value() : false; for (auto Found = Dir.First(Dlg->Ctrl2->Name()); Found; Found = Dir.Next()) { char Name[LDirectory::MaxPathLen]; Dir.Path(Name, sizeof(Name)); bool Match = true; if (!ShowHiddenFiles && Dir.IsHidden()) { Match = false; } else if (!Dir.IsDir() && Ext.Length() > 0) { Match = false; for (auto e: Ext) { bool m = MatchStr(e, Name); if (m) { Match = true; break; } } } if (FilterKey && Match) Match = stristr(Dir.GetName(), FilterKey) != NULL; if (Match) New.Insert(new LFolderItem(Dlg, Name, &Dir)); } // Sort items... New.Sort(LFolderItemCompare); // Display items... Insert(New); } ////////////////////////////////////////////////////////////////////////// LFileSelect::LFileSelect(LViewI *Window) { d = new LFileSelectPrivate(this); if (Window) Parent(Window); } LFileSelect::~LFileSelect() { DeleteObj(d); } void LFileSelect::ShowReadOnly(bool b) { d->ShowReadOnly = b;; } bool LFileSelect::ReadOnly() { return d->ReadOnly; } const char *LFileSelect::Name() { return d->Files[0]; } bool LFileSelect::Name(const char *n) { d->Files.DeleteArrays(); if (n) { d->Files.Insert(NewStr(n)); } return true; } const char *LFileSelect::operator [](size_t i) { return d->Files.ItemAt(i); } size_t LFileSelect::Length() { return d->Files.Length(); } size_t LFileSelect::Types() { return d->Types.Length(); } void LFileSelect::ClearTypes() { d->Types.DeleteObjects(); } LFileType *LFileSelect::TypeAt(ssize_t n) { return d->Types.ItemAt(n); } bool LFileSelect::Type(const char *Description, const char *Extension, int Data) { LFileType *Type = new LFileType; if (Type) { Type->Description(Description); Type->Extension(Extension); d->Types.Insert(Type); } return Type != 0; } ssize_t LFileSelect::SelectedType() { return d->CurrentType; } LViewI *LFileSelect::Parent() { return d->Parent; } void LFileSelect::Parent(LViewI *Window) { d->Parent = dynamic_cast(Window); } bool LFileSelect::MultiSelect() { return d->MultiSelect; } void LFileSelect::MultiSelect(bool Multi) { d->MultiSelect = Multi; } #define CharPropImpl(Func, Var) \ const char *LFileSelect::Func() \ { \ return Var; \ } \ void LFileSelect::Func(const char *i) \ { \ Var = i; \ } CharPropImpl(InitialDir, d->InitPath); CharPropImpl(Title, d->Title); CharPropImpl(DefaultExtension, d->DefExt); void LFileSelect::Open(SelectCb Cb) { LFileSelectDlg *Dlg = new LFileSelectDlg(d); d->Type = TypeOpenFile; Dlg->Name("Open"); if (Dlg->SaveBtn) Dlg->SaveBtn->Name("Open"); // printf("LFileSelect domodal.. thread=%p\n", LGetCurrentThread()); Dlg->DoModal([select=this, cb=Cb](auto dlg, auto code) { // printf("LFileSelect cb.. thread=%u lock=%u\n", GetCurrentThreadId(), dlg->WindowHandle()->LockingThread()); if (cb) cb(select, code == IDOK); // printf("LFileSelect deleting.. lock=%u\n", dlg->WindowHandle()->LockingThread()); delete dlg; // printf("LFileSelect deleted..\n"); }); } void LFileSelect::OpenFolder(SelectCb Cb) { auto Dlg = new LFileSelectDlg(d); d->Type = TypeOpenFolder; Dlg->SaveBtn->Enabled(true); Dlg->FileNameEdit->Enabled(false); Dlg->Name("Open Folder"); Dlg->SaveBtn->Name("Open"); printf("LFileSelect::OpenFolder domodal...\n"); Dlg->DoModal([this,Cb](auto d, auto code) { printf("LFileSelect::OpenFolder cb, code=%i\n", code); if (Cb) Cb(this, code != IDOK); delete d; }); } void LFileSelect::Save(SelectCb Cb) { auto *Dlg = new LFileSelectDlg(d); d->Type = TypeSaveFile; Dlg->Name("Save As"); Dlg->SaveBtn->Name("Save As"); // printf("LFileSelect domodal.. thread=%u\n", GetCurrentThreadId()); Dlg->DoModal([FileSelect=this,Cb](auto dlg, auto code) { // printf("LFileSelect cb.. thread=%u lock=%u\n", GetCurrentThreadId(), dlg->WindowHandle()->LockingThread()); if (Cb) Cb(FileSelect, code != IDOK); // printf("LFileSelect deleting.. lock=%u\n", dlg->WindowHandle()->LockingThread()); delete dlg; // printf("LFileSelect deleted..\n"); }); } /////////////////////////////////////////////////////////////////////////////////// #if defined(LINUX) #include "lgi/common/Net.h" #endif bool LgiGetUsersLinks(LArray &Links) { LString Folder = LGetSystemPath(LSP_USER_LINKS); if (!Folder) return false; #if defined(WINDOWS) LDirectory d; for (int b = d.First(Folder); b; b = d.Next()) { char *s = d.GetName(); if (s && stristr(s, ".lnk")) { char lnk[MAX_PATH_LEN]; if (d.Path(lnk, sizeof(lnk)) && LResolveShortcut(lnk, lnk, sizeof(lnk))) { Links.New() = lnk; } } } #elif defined(LINUX) char p[MAX_PATH_LEN]; if (!LMakePath(p, sizeof(p), Folder, "bookmarks")) { LgiTrace("%s:%i - Failed to make path '%s'\n", _FL, Folder.Get()); return false; } if (!LFileExists(p)) return false; LAutoString Txt(LReadTextFile(p)); if (!Txt) { LgiTrace("%s:%i - failed to read '%s'\n", _FL, p); return false; } LString s = Txt.Get(); LString::Array a = s.Split("\n"); for (unsigned i=0; i #ifdef LINUX #include #endif #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Button.h" #include "lgi/common/Css.h" #include "lgi/common/LgiRes.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/Popup.h" #include "lgi/common/CssTools.h" #include "ViewPriv.h" #if 0 #define DEBUG_CAPTURE(...) printf(__VA_ARGS__) #else #define DEBUG_CAPTURE(...) #endif ////////////////////////////////////////////////////////////////////////////////////// // Helper LPoint lgi_view_offset(LViewI *v, bool Debug = false) { LPoint Offset; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) break; LRect pos = p->GetPos(); LRect cli = p->GetClient(false); if (Debug) { const char *cls = p->GetClass(); LgiTrace(" Off[%s] += %i,%i = %i,%i (%s)\n", cls, pos.x1, pos.y1, Offset.x + pos.x1, Offset.y + pos.y1, cli.GetStr()); } Offset.x += pos.x1 + cli.x1; Offset.y += pos.y1 + cli.y1; } return Offset; } LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing, bool Debug) { static LMouse Temp; Temp = Info; if (Wnd) { if (Debug #if 0 || Capturing #endif ) LgiTrace("AdjustClick Target=%s -> Wnd=%s, Info=%i,%i\n", Info.Target?Info.Target->GetClass():"", Wnd?Wnd->GetClass():"", Info.x, Info.y); if (Temp.Target != Wnd) { if (Temp.Target) { auto *TargetWnd = Temp.Target->GetWindow(); auto *WndWnd = Wnd->GetWindow(); if (TargetWnd == WndWnd) { LPoint TargetOff = lgi_view_offset(Temp.Target, Debug); if (Debug) LgiTrace(" WndOffset:\n"); LPoint WndOffset = lgi_view_offset(Wnd, Debug); Temp.x += TargetOff.x - WndOffset.x; Temp.y += TargetOff.y - WndOffset.y; #if 0 LRect c = Wnd->GetClient(false); Temp.x -= c.x1; Temp.y -= c.y1; if (Debug) LgiTrace(" CliOff -= %i,%i\n", c.x1, c.y1); #endif Temp.Target = Wnd; } } else { LPoint Offset; Temp.Target = Wnd; if (Wnd->WindowVirtualOffset(&Offset)) { LRect c = Wnd->GetClient(false); Temp.x -= Offset.x + c.x1; Temp.y -= Offset.y + c.y1; } } } } LAssert(Temp.Target != NULL); return Temp; } ////////////////////////////////////////////////////////////////////////////////////// // LView class methods LViewI *LView::_Capturing = 0; LViewI *LView::_Over = 0; #if LGI_VIEW_HASH struct ViewTbl : public LMutex { typedef LHashTbl, int> T; private: T Map; public: ViewTbl() : Map(2000), LMutex("ViewTbl") { } T *Lock() { if (!LMutex::Lock(_FL)) return NULL; return ⤅ } } ViewTblInst; bool LView::LockHandler(LViewI *v, LView::LockOp Op) { ViewTbl::T *m = ViewTblInst.Lock(); if (!m) return false; int Ref = m->Find(v); bool Status = false; switch (Op) { case OpCreate: { if (Ref == 0) Status = m->Add(v, 1); else LAssert(!"Already exists?"); break; } case OpDelete: { if (Ref == 1) Status = m->Delete(v); else LAssert(!"Either locked or missing."); break; } case OpExists: { Status = Ref > 0; break; } } ViewTblInst.Unlock(); return Status; } #endif LView::LView(OsView view) { #ifdef _DEBUG _Debug = false; #endif d = new LViewPrivate(this); #ifdef LGI_SDL _View = this; #elif LGI_VIEW_HANDLE && !defined(HAIKU) _View = view; #endif Pos.ZOff(-1, -1); WndFlags = GWF_VISIBLE; #ifndef LGI_VIEW_HASH #error "LGI_VIEW_HASH needs to be defined" #elif LGI_VIEW_HASH LockHandler(this, OpCreate); // printf("Adding %p to hash\n", (LViewI*)this); #endif } LView::~LView() { if (d->SinkHnd >= 0) { LEventSinkMap::Dispatch.RemoveSink(this); d->SinkHnd = -1; } #if LGI_VIEW_HASH LockHandler(this, OpDelete); #endif for (unsigned i=0; iOwner == this) pu->Owner = NULL; } _Delete(); // printf("%p::~LView delete %p th=%u\n", this, d, GetCurrentThreadId()); DeleteObj(d); // printf("%p::~LView\n", this); } int LView::AddDispatch() { if (d->SinkHnd < 0) d->SinkHnd = LEventSinkMap::Dispatch.AddSink(this); return d->SinkHnd; } LString LView::CssStyles(const char *Set) { if (Set) { d->Styles = Set; d->CssDirty = true; } return d->Styles; } LString::Array *LView::CssClasses() { return &d->Classes; } LArray LView::IterateViews() { LArray a; for (auto c: Children) a.Add(c); return a; } bool LView::AddView(LViewI *v, int Where) { LAssert(!Children.HasItem(v)); bool Add = Children.Insert(v, Where); if (Add) { LView *gv = v->GetGView(); if (gv && gv->_Window != _Window) { LAssert(!_InLock); gv->_Window = _Window; } v->SetParent(this); v->OnAttach(); OnChildrenChanged(v, true); } return Add; } bool LView::DelView(LViewI *v) { bool Has = Children.HasItem(v); bool b = Children.Delete(v); if (Has) OnChildrenChanged(v, false); Has = Children.HasItem(v); LAssert(!Has); return b; } bool LView::HasView(LViewI *v) { return Children.HasItem(v); } OsWindow LView::WindowHandle() { auto w = GetWindow(); - auto h = w ? w->WindowHandle() : NULL; + OsWindow h; + if (w) + h = w->WindowHandle(); return h; } LWindow *LView::GetWindow() { if (!_Window) { // Walk up parent list and find someone who has a window auto *w = d->GetParent(); for (; w; w = w->d ? w->d->GetParent() : NULL) { if (w->_Window) { LAssert(!_InLock); _Window = w->_Window; break; } } } return dynamic_cast(_Window); } bool LView::Lock(const char *file, int line, int TimeOut) { #ifdef HAIKU bool Debug = !Stricmp("LList", GetClass()); if (!d || !d->Hnd) { if (Debug) printf("%s:%i - no handle %p %p\n", _FL, d, d ? d->Hnd : NULL); return false; } if (d->Hnd->Parent() == NULL) { if (Debug) printf("%s:%p - Lock() no parent.\n", GetClass(), this); return true; } if (TimeOut >= 0) { auto r = d->Hnd->LockLooperWithTimeout(TimeOut * 1000); if (r == B_OK) { _InLock++; if (Debug) printf("%s:%p - Lock() cnt=%i par=%p.\n", GetClass(), this, _InLock, d->Hnd->Parent()); return true; } printf("%s:%i - Lock(%i) failed with %x.\n", _FL, TimeOut, r); return false; } auto r = d->Hnd->LockLooper(); if (r) { _InLock++; if (Debug) { auto w = WindowHandle(); printf("%s:%p - Lock() cnt=%i myThread=%i wndThread=%i.\n", GetClass(), this, _InLock, GetCurrentThreadId(), w ? w->Thread() : -1); } return true; } if (Debug) printf("%s:%i - Lock(%s:%i) failed.\n", _FL, file, line); return false; #else if (!_Window) GetWindow(); _InLock++; // LgiTrace("%s::%p Lock._InLock=%i %s:%i\n", GetClass(), this, _InLock, file, line); if (_Window && _Window->_Lock) { if (TimeOut < 0) { return _Window->_Lock->Lock(file, line); } else { return _Window->_Lock->LockWithTimeout(TimeOut, file, line); } } return true; #endif } void LView::Unlock() { #ifdef HAIKU if (!d || !d->Hnd) { printf("%s:%i - Unlock() error, no hnd.\n", _FL); return; } if (!d->Hnd->Parent()) { // printf("%s:%p - Unlock() no parent.\n", GetClass(), this); return; } if (_InLock > 0) { // printf("%s:%p - Calling UnlockLooper: %i.\n", GetClass(), this, _InLock); d->Hnd->UnlockLooper(); _InLock--; // printf("%s:%p - UnlockLooper done: %i.\n", GetClass(), this, _InLock); } else { printf("%s:%i - Unlock() without lock.\n", _FL); } #else if (_Window && _Window->_Lock) { _Window->_Lock->Unlock(); } _InLock--; // LgiTrace("%s::%p Unlock._InLock=%i\n", GetClass(), this, _InLock); #endif } void LView::OnMouseClick(LMouse &m) { } void LView::OnMouseEnter(LMouse &m) { } void LView::OnMouseExit(LMouse &m) { } void LView::OnMouseMove(LMouse &m) { } bool LView::OnMouseWheel(double Lines) { return false; } bool LView::OnKey(LKey &k) { return false; } void LView::OnAttach() { List::I it = Children.begin(); for (LViewI *v = *it; v; v = *++it) { if (!v->GetParent()) v->SetParent(this); } #if 0 // defined __GTK_H__ if (_View && !DropTarget()) { // If one of our parents is drop capable we need to set a dest here LViewI *p; for (p = GetParent(); p; p = p->GetParent()) { if (p->DropTarget()) { break; } } if (p) { Gtk::gtk_drag_dest_set( _View, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); // printf("%s:%i - Drop dest for '%s'\n", _FL, GetClass()); } else { Gtk::gtk_drag_dest_unset(_View); // printf("%s:%i - Not setting drop dest '%s'\n", _FL, GetClass()); } } #endif } void LView::OnCreate() { } void LView::OnDestroy() { } void LView::OnFocus(bool f) { // printf("%s::OnFocus(%i)\n", GetClass(), f); } void LView::OnPulse() { } void LView::OnPosChange() { } bool LView::OnRequestClose(bool OsShuttingDown) { return true; } int LView::OnHitTest(int x, int y) { return -1; } void LView::OnChildrenChanged(LViewI *Wnd, bool Attaching) { } void LView::OnPaint(LSurface *pDC) { auto c = GetClient(); LCssTools Tools(this); Tools.PaintContent(pDC, c); } int LView::OnNotify(LViewI *Ctrl, LNotification Data) { if (!Ctrl) return 0; if (Ctrl == (LViewI*)this && Data.Type == LNotifyActivate) { // Default activation is to focus the current control. Focus(true); } else if (d && d->Parent) { // default behaviour is just to pass the // notification up to the parent // FIXME: eventually we need to call the 'LNotification' parent fn... return d->Parent->OnNotify(Ctrl, Data); } return 0; } int LView::OnCommand(int Cmd, int Event, OsView Wnd) { return 0; } void LView::OnNcPaint(LSurface *pDC, LRect &r) { int Border = Sunken() || Raised() ? _BorderSize : 0; if (Border == 2) { LEdge e; if (Sunken()) e = Focus() ? EdgeWin7FocusSunken : DefaultSunkenEdge; else e = DefaultRaisedEdge; #if WINNATIVE if (d->IsThemed) DrawThemeBorder(pDC, r); else #endif LWideBorder(pDC, r, e); } else if (Border == 1) { LThinBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge); } } #if LGI_COCOA || defined(__GTK_H__) /* uint64 nPaint = 0; uint64 PaintTime = 0; */ void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { /* uint64 StartTs = Update ? LCurrentTime() : 0; d->InPaint = true; */ // Create temp DC if needed... LAutoPtr Local; if (!pDC) { if (!Local.Reset(new LScreenDC(this))) return; pDC = Local; } #if 0 // This is useful for coverage checking pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif // Non-Client drawing LRect r; if (Offset) { r = Pos; r.Offset(Offset); } else { r = GetClient().ZeroTranslate(); } pDC->SetClient(&r); LRect zr1 = r.ZeroTranslate(), zr2 = zr1; OnNcPaint(pDC, zr1); pDC->SetClient(NULL); if (zr2 != zr1) { r.x1 -= zr2.x1 - zr1.x1; r.y1 -= zr2.y1 - zr1.y1; r.x2 -= zr2.x2 - zr1.x2; r.y2 -= zr2.y2 - zr1.y2; } LPoint o(r.x1, r.y1); // Origin of client // Paint this view's contents... pDC->SetClient(&r); #if 0 if (_Debug) { #if defined(__GTK_H__) Gtk::cairo_matrix_t matrix; cairo_get_matrix(pDC->Handle(), &matrix); double ex[4]; cairo_clip_extents(pDC->Handle(), ex+0, ex+1, ex+2, ex+3); ex[0] += matrix.x0; ex[1] += matrix.y0; ex[2] += matrix.x0; ex[3] += matrix.y0; LgiTrace("%s::_Paint, r=%s, clip=%g,%g,%g,%g - %g,%g\n", GetClass(), r.GetStr(), ex[0], ex[1], ex[2], ex[3], matrix.x0, matrix.y0); #elif LGI_COCOA auto Ctx = pDC->Handle(); CGAffineTransform t = CGContextGetCTM(Ctx); LRect cr = CGContextGetClipBoundingBox(Ctx); printf("%s::_Paint() pos=%s transform=%g,%g,%g,%g-%g,%g clip=%s r=%s\n", GetClass(), GetPos().GetStr(), t.a, t.b, t.c, t.d, t.tx, t.ty, cr.GetStr(), r.GetStr()); #endif } #endif OnPaint(pDC); pDC->SetClient(NULL); // Paint all the children... for (auto i : Children) { LView *w = i->GetGView(); if (w && w->Visible()) { if (!w->Pos.Valid()) continue; #if 0 if (w->_Debug) LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), o.x, o.y); #endif w->_Paint(pDC, &o); } } } #else void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { // Create temp DC if needed... LAutoPtr Local; if (!pDC) { Local.Reset(new LScreenDC(this)); pDC = Local; } if (!pDC) { printf("%s:%i - No context to draw in.\n", _FL); return; } #if 0 // This is useful for coverage checking pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif bool HasClient = false; LRect r(0, 0, Pos.X()-1, Pos.Y()-1), Client; LPoint o; if (Offset) o = *Offset; #if WINNATIVE if (!_View) #endif { // Non-Client drawing Client = r; OnNcPaint(pDC, Client); HasClient = GetParent() && (Client != r); if (HasClient) { Client.Offset(o.x, o.y); pDC->SetClient(&Client); } } r.Offset(o.x, o.y); // Paint this view's contents if (Update) { LRect OldClip = pDC->ClipRgn(); pDC->ClipRgn(Update); OnPaint(pDC); pDC->ClipRgn(OldClip.Valid() ? &OldClip : NULL); } else { OnPaint(pDC); } #if PAINT_VIRTUAL_CHILDREN // Paint any virtual children for (auto i : Children) { LView *w = i->GetGView(); if (w && w->Visible()) { #if LGI_VIEW_HANDLE if (!w->Handle()) #endif { LRect p = w->GetPos(); p.Offset(o.x, o.y); if (HasClient) p.Offset(Client.x1 - r.x1, Client.y1 - r.y1); LPoint co(p.x1, p.y1); // LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), p.x1, p.y1); pDC->SetClient(&p); w->_Paint(pDC, &co); pDC->SetClient(NULL); } } } #endif if (HasClient) pDC->SetClient(0); } #endif LViewI *LView::GetParent() { ThreadCheck(); return d ? d->Parent : NULL; } void LView::SetParent(LViewI *p) { ThreadCheck(); d->Parent = p ? p->GetGView() : NULL; d->ParentI = p; } void LView::SendNotify(LNotification note) { LViewI *n = d->Notify ? d->Notify : d->Parent; if (n) { if ( #if LGI_VIEW_HANDLE && !defined(HAIKU) !_View || #endif InThread()) { n->OnNotify(this, note); } else { // We are not in the same thread as the target window. So we post a message // across to the view. if (GetId() <= 0) { // We are going to generate a control Id to help the receiver of the // M_CHANGE message find out view later on. The reason for doing this // instead of sending a pointer to the object, is that the object // _could_ be deleted between the message being sent and being received. // Which would result in an invalid memory access on that object. LViewI *p = GetWindow(); if (!p) { // No window? Find the top most parent we can... p = this; while (p->GetParent()) p = p->GetParent(); } if (p) { // Give the control a valid ID int i; for (i=10; i<1000; i++) { if (!p->FindControl(i)) { printf("Giving the ctrl '%s' the id '%i' for SendNotify\n", GetClass(), i); SetId(i); break; } } } else { // Ok this is really bad... go random (better than nothing) SetId(5000 + LRand(2000)); } } LAssert(GetId() > 0); // We must have a valid ctrl ID at this point, otherwise // the receiver will never be able to find our object. // printf("Post M_CHANGE %i %i\n", GetId(), Data); n->PostEvent(M_CHANGE, (LMessage::Param) GetId(), (LMessage::Param) new LNotification(note)); } } } LViewI *LView::GetNotify() { ThreadCheck(); return d->Notify; } void LView::SetNotify(LViewI *p) { ThreadCheck(); d->Notify = p; } #define ADJ_LEFT 1 #define ADJ_RIGHT 2 #define ADJ_UP 3 #define ADJ_DOWN 4 int IsAdjacent(LRect &a, LRect &b) { if ( (a.x1 == b.x2 + 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_LEFT; } if ( (a.x2 == b.x1 - 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_RIGHT; } if ( (a.y1 == b.y2 + 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_UP; } if ( (a.y2 == b.y1 - 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_DOWN; } return 0; } LRect JoinAdjacent(LRect &a, LRect &b, int Adj) { LRect t; switch (Adj) { case ADJ_LEFT: case ADJ_RIGHT: { t.y1 = MAX(a.y1, b.y1); t.y2 = MIN(a.y2, b.y2); t.x1 = MIN(a.x1, b.x1); t.x2 = MAX(a.x2, b.x2); break; } case ADJ_UP: case ADJ_DOWN: { t.y1 = MIN(a.y1, b.y1); t.y2 = MAX(a.y2, b.y2); t.x1 = MAX(a.x1, b.x1); t.x2 = MIN(a.x2, b.x2); break; } } return t; } LRect *LView::FindLargest(LRegion &r) { ThreadCheck(); int Pixels = 0; LRect *Best = 0; static LRect Final; // do initial run through the list to find largest single region for (LRect *i = r.First(); i; i = r.Next()) { int Pix = i->X() * i->Y(); if (Pix > Pixels) { Pixels = Pix; Best = i; } } if (Best) { Final = *Best; Pixels = Final.X() * Final.Y(); int LastPixels = Pixels; LRect LastRgn = Final; int ThisPixels = Pixels; LRect ThisRgn = Final; LRegion TempList; for (LRect *i = r.First(); i; i = r.Next()) { TempList.Union(i); } TempList.Subtract(Best); do { LastPixels = ThisPixels; LastRgn = ThisRgn; // search for adjoining rectangles that maybe we can add for (LRect *i = TempList.First(); i; i = TempList.Next()) { int Adj = IsAdjacent(ThisRgn, *i); if (Adj) { LRect t = JoinAdjacent(ThisRgn, *i, Adj); int Pix = t.X() * t.Y(); if (Pix > ThisPixels) { ThisPixels = Pix; ThisRgn = t; TempList.Subtract(i); } } } } while (LastPixels < ThisPixels); Final = ThisRgn; } else return 0; return &Final; } LRect *LView::FindSmallestFit(LRegion &r, int Sx, int Sy) { ThreadCheck(); int X = 1000000; int Y = 1000000; LRect *Best = 0; for (LRect *i = r.First(); i; i = r.Next()) { if ((i->X() >= Sx && i->Y() >= Sy) && (i->X() < X || i->Y() < Y)) { X = i->X(); Y = i->Y(); Best = i; } } return Best; } LRect *LView::FindLargestEdge(LRegion &r, int Edge) { LRect *Best = 0; ThreadCheck(); for (LRect *i = r.First(); i; i = r.Next()) { if (!Best) { Best = i; } if ( ((Edge & GV_EDGE_TOP) && (i->y1 < Best->y1)) || ((Edge & GV_EDGE_RIGHT) && (i->x2 > Best->x2)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 > Best->y2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 < Best->x1)) ) { Best = i; } if ( ( ((Edge & GV_EDGE_TOP) && (i->y1 == Best->y1)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 == Best->y2)) ) && ( i->X() > Best->X() ) ) { Best = i; } if ( ( ((Edge & GV_EDGE_RIGHT) && (i->x2 == Best->x2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 == Best->x1)) ) && ( i->Y() > Best->Y() ) ) { Best = i; } } return Best; } LViewI *LView::FindReal(LPoint *Offset) { ThreadCheck(); if (Offset) { Offset->x = 0; Offset->y = 0; } #if !LGI_VIEW_HANDLE LViewI *w = GetWindow(); #endif LViewI *p = d->Parent; while (p && #if !LGI_VIEW_HANDLE p != w #else !p->Handle() #endif ) { if (Offset) { Offset->x += Pos.x1; Offset->y += Pos.y1; } p = p->GetParent(); } if (p && #if !LGI_VIEW_HANDLE p == w #else p->Handle() #endif ) { return p; } return NULL; } bool LView::HandleCapture(LView *Wnd, bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::HandleCapture(%i)=%i\n", GetClass(), c, (int)(_Capturing == Wnd)); if (c) { if (_Capturing == Wnd) { DEBUG_CAPTURE(" %s already has capture\n", _Capturing?_Capturing->GetClass():0); } else { DEBUG_CAPTURE(" _Capturing=%s -> %s\n", _Capturing?_Capturing->GetClass():0, Wnd?Wnd->GetClass():0); _Capturing = Wnd; #if defined(__GTK_H__) if (d->CaptureThread) d->CaptureThread->Cancel(); d->CaptureThread = new LCaptureThread(this); #elif WINNATIVE LPoint Offset; LViewI *v = _Capturing->Handle() ? _Capturing : FindReal(&Offset); HWND h = v ? v->Handle() : NULL; if (h) SetCapture(h); else LAssert(0); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_TRUE); #else LAppInst->CaptureMouse(true); #endif #endif } } else if (_Capturing) { DEBUG_CAPTURE(" _Capturing=%s -> NULL\n", _Capturing?_Capturing->GetClass():0); _Capturing = NULL; #if defined(__GTK_H__) if (d->CaptureThread) { d->CaptureThread->Cancel(); d->CaptureThread = NULL; // It'll delete itself... } #elif WINNATIVE ReleaseCapture(); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_FALSE); #else LAppInst->CaptureMouse(false); #endif #endif } return true; } bool LView::IsCapturing() { // DEBUG_CAPTURE("%s::IsCapturing()=%p==%p==%i\n", GetClass(), _Capturing, this, (int)(_Capturing == this)); return _Capturing == this; } bool LView::Capture(bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::Capture(%i)\n", GetClass(), c); return HandleCapture(this, c); } bool LView::Enabled() { ThreadCheck(); #if WINNATIVE if (_View) return IsWindowEnabled(_View) != 0; #endif return !TestFlag(GViewFlags, GWF_DISABLED); } void LView::Enabled(bool i) { ThreadCheck(); if (!i) SetFlag(GViewFlags, GWF_DISABLED); else ClearFlag(GViewFlags, GWF_DISABLED); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) { #if WINNATIVE EnableWindow(_View, i); #elif defined LGI_CARBON if (i) { OSStatus e = EnableControl(_View); if (e) printf("%s:%i - Error enabling control (%i)\n", _FL, (int)e); } else { OSStatus e = DisableControl(_View); if (e) printf("%s:%i - Error disabling control (%i)\n", _FL, (int)e); } #endif } #endif Invalidate(); } bool LView::Visible() { // This is a read only operation... which is kinda thread-safe... // ThreadCheck(); #if WINNATIVE if (_View) /* This takes into account all the parent windows as well... Which is kinda not what I want. I want this to reflect just this window. return IsWindowVisible(_View); */ return (GetWindowLong(_View, GWL_STYLE) & WS_VISIBLE) != 0; #endif return TestFlag(GViewFlags, GWF_VISIBLE); } void LView::Visible(bool v) { ThreadCheck(); if (v) SetFlag(GViewFlags, GWF_VISIBLE); else ClearFlag(GViewFlags, GWF_VISIBLE); #if defined(HAIKU) LLocker lck(d->Hnd, _FL); if (!IsAttached() || lck.Lock()) { const int attempts = 3; // printf("%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden()); if (v) { bool parentHidden = false; for (auto p = d->Hnd->Parent(); p; p = p->Parent()) { if (p->IsHidden()) { parentHidden = true; break; } } if (!parentHidden) // Don't try and show if one of the parent's is hidden. { for (int i=0; iHnd->IsHidden(); i++) { // printf("\t%p Show\n", this); d->Hnd->Show(); } if (d->Hnd->IsHidden()) { printf("%s:%i - Failed to show %s.\n", _FL, GetClass()); for (auto p = d->Hnd->Parent(); p; p = p->Parent()) printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden()); } } } else { for (int i=0; iHnd->IsHidden(); i++) { // printf("\t%p Hide\n", this); d->Hnd->Hide(); } if (!d->Hnd->IsHidden()) { printf("%s:%i - Failed to hide %s.\n", _FL, GetClass()); for (auto p = d->Hnd->Parent(); p; p = p->Parent()) printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden()); } } // printf("\t%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden()); } else LgiTrace("%s:%i - Can't lock.\n", _FL); #elif LGI_VIEW_HANDLE if (_View) { #if WINNATIVE ShowWindow(_View, (v) ? SW_SHOWNORMAL : SW_HIDE); #elif LGI_COCOA LAutoPool Pool; [_View.p setHidden:!v]; #elif LGI_CARBON Boolean is = HIViewIsVisible(_View); if (v != is) { OSErr e = HIViewSetVisible(_View, v); if (e) printf("%s:%i - HIViewSetVisible(%p,%i) failed with %i (class=%s)\n", _FL, _View, v, e, GetClass()); } #endif } else #endif { Invalidate(); } } bool LView::Focus() { ThreadCheck(); bool Has = false; #if defined(__GTK_H__) LWindow *w = GetWindow(); if (w) { bool Active = w->IsActive(); if (Active) Has = w->GetFocus() == static_cast(this); } #elif defined(HAIKU) LLocker lck(d->Hnd, _FL); if (lck.Lock()) { Has = d->Hnd->IsFocus(); lck.Unlock(); } #elif defined(WINNATIVE) if (_View) { HWND hFocus = GetFocus(); Has = hFocus == _View; } #elif LGI_COCOA Has = TestFlag(WndFlags, GWF_FOCUS); #elif LGI_CARBON LWindow *w = GetWindow(); if (w) { ControlRef Cur; OSErr e = GetKeyboardFocus(w->WindowHandle(), &Cur); if (e) LgiTrace("%s:%i - GetKeyboardFocus failed with %i\n", _FL, e); else Has = (Cur == _View); } #endif #if !LGI_CARBON if (Has) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); #endif return Has; } void LView::Focus(bool i) { ThreadCheck(); if (i) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); auto *Wnd = GetWindow(); if (Wnd) { Wnd->SetFocus(this, i ? LWindow::GainFocus : LWindow::LoseFocus); } #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) #endif { #if defined(HAIKU) _Focus(i); #elif defined(LGI_SDL) || defined(__GTK_H__) // Nop: Focus is all handled by Lgi's LWindow class. #elif WINNATIVE if (i) { HWND hCur = GetFocus(); if (hCur != _View) { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus %p (%-30s)\n", _FL, Handle(), Name()); } SetFocus(_View); } } else { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus(%p)\n", _FL, GetDesktopWindow()); } SetFocus(GetDesktopWindow()); } #elif defined LGI_CARBON LViewI *Wnd = GetWindow(); if (Wnd && i) { OSErr e = SetKeyboardFocus(Wnd->WindowHandle(), _View, 1); if (e) { // e = SetKeyboardFocus(Wnd->WindowHandle(), _View, kControlFocusNextPart); // if (e) { HIViewRef p = HIViewGetSuperview(_View); // errCouldntSetFocus printf("%s:%i - SetKeyboardFocus failed: %i (%s, %p)\n", _FL, e, GetClass(), p); } } // else printf("%s:%i - SetFocus v=%p(%s)\n", _FL, _View, GetClass()); } else printf("%s:%i - no window?\n", _FL); #endif } } LDragDropSource *LView::DropSource(LDragDropSource *Set) { if (Set) d->DropSource = Set; return d->DropSource; } LDragDropTarget *LView::DropTarget(LDragDropTarget *Set) { if (Set) d->DropTarget = Set; return d->DropTarget; } #if defined LGI_CARBON extern pascal OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); #endif #if defined __GTK_H__ // Recursively add drag dest to all view and all children bool GtkAddDragDest(LViewI *v, bool IsTarget) { if (!v) return false; LWindow *w = v->GetWindow(); if (!w) return false; auto wid = GtkCast(w->WindowHandle(), gtk_widget, GtkWidget); if (IsTarget) { Gtk::gtk_drag_dest_set( wid, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); } else { Gtk::gtk_drag_dest_unset(wid); } for (LViewI *c: v->IterateViews()) GtkAddDragDest(c, IsTarget); return true; } #endif bool LView::DropTarget(bool t) { ThreadCheck(); bool Status = false; if (t) SetFlag(GViewFlags, GWF_DROP_TARGET); else ClearFlag(GViewFlags, GWF_DROP_TARGET); #if WINNATIVE if (_View) { if (t) { if (!d->DropTarget) DragAcceptFiles(_View, t); else Status = RegisterDragDrop(_View, (IDropTarget*) d->DropTarget) == S_OK; } else { if (_View && d->DropTarget) Status = RevokeDragDrop(_View) == S_OK; } } #elif defined MAC && !defined(LGI_SDL) LWindow *Wnd = dynamic_cast(GetWindow()); if (Wnd) { Wnd->SetDragHandlers(t); if (!d->DropTarget) d->DropTarget = t ? Wnd : 0; } #if LGI_COCOA LWindow *w = GetWindow(); if (w) { OsWindow h = w->WindowHandle(); if (h) { NSMutableArray *a = [[NSMutableArray alloc] init]; if (a) { [a addObject:(NSString*)kUTTypeItem]; for (id item in NSFilePromiseReceiver.readableDraggedTypes) [a addObject:item]; [h.p.contentView registerForDraggedTypes:a]; [a release]; } } } #elif LGI_CARBON if (t) { static EventTypeSpec DragEvents[] = { { kEventClassControl, kEventControlDragEnter }, { kEventClassControl, kEventControlDragWithin }, { kEventClassControl, kEventControlDragLeave }, { kEventClassControl, kEventControlDragReceive }, }; if (!d->DndHandler) { OSStatus e = ::InstallControlEventHandler( _View, NewEventHandlerUPP(LgiViewDndHandler), GetEventTypeCount(DragEvents), DragEvents, (void*)this, &d->DndHandler); if (e) LgiTrace("%s:%i - InstallEventHandler failed (%i)\n", _FL, e); } SetControlDragTrackingEnabled(_View, true); } else { SetControlDragTrackingEnabled(_View, false); } #endif #elif defined __GTK_H__ Status = GtkAddDragDest(this, t); if (Status && !d->DropTarget) d->DropTarget = t ? GetWindow() : 0; #endif return Status; } bool LView::Sunken() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE); #else return TestFlag(GViewFlags, GWF_SUNKEN); #endif } void LView::Sunken(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_CLIENTEDGE); else ClearFlag(d->WndExStyle, WS_EX_CLIENTEDGE); if (_View) SetWindowLong(_View, GWL_EXSTYLE, d->WndExStyle); #else if (i) SetFlag(GViewFlags, GWF_SUNKEN); else ClearFlag(GViewFlags, GWF_SUNKEN); #endif if (i) { if (!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } bool LView::Flat() { // ThreadCheck(); #if WINNATIVE return !TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE) && !TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return !TestFlag(GViewFlags, GWF_SUNKEN) && !TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Flat(bool i) { ThreadCheck(); #if WINNATIVE ClearFlag(d->WndExStyle, (WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE)); #else ClearFlag(GViewFlags, (GWF_RAISED|GWF_SUNKEN)); #endif } bool LView::Raised() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Raised(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_WINDOWEDGE); else ClearFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else if (i) SetFlag(GViewFlags, GWF_RAISED); else ClearFlag(GViewFlags, GWF_RAISED); #endif if (i) { if (!!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } int LView::GetId() { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. return d->CtrlId; } void LView::SetId(int i) { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. d->CtrlId = i; #if WINNATIVE if (_View) SetWindowLong(_View, GWL_ID, d->CtrlId); #elif defined __GTK_H__ #elif defined MAC #endif } bool LView::GetTabStop() { ThreadCheck(); #if WINNATIVE return TestFlag(d->WndStyle, WS_TABSTOP); #else return d->TabStop; #endif } void LView::SetTabStop(bool b) { ThreadCheck(); #if WINNATIVE if (b) SetFlag(d->WndStyle, WS_TABSTOP); else ClearFlag(d->WndStyle, WS_TABSTOP); #else d->TabStop = b; #endif } int64 LView::GetCtrlValue(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Value() : 0; } void LView::SetCtrlValue(int Id, int64 i) { ThreadCheck(); LViewI *w = FindControl(Id); if (w) w->Value(i); } const char *LView::GetCtrlName(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Name() : 0; } void LView::SetCtrlName(int Id, const char *s) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Name(s); } else { PostEvent( M_SET_CTRL_NAME, (LMessage::Param)Id, (LMessage::Param)new LString(s)); } } bool LView::GetCtrlEnabled(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Enabled() : 0; } void LView::SetCtrlEnabled(int Id, bool Enabled) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Enabled(Enabled); } else { PostEvent( M_SET_CTRL_ENABLE, (LMessage::Param)Id, (LMessage::Param)Enabled); } } bool LView::GetCtrlVisible(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); if (!w) LgiTrace("%s:%i - Ctrl %i not found.\n", _FL, Id); return (w) ? w->Visible() : 0; } void LView::SetCtrlVisible(int Id, bool v) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Visible(v); } else { PostEvent( M_SET_CTRL_VISIBLE, (LMessage::Param)Id, (LMessage::Param)v); } } bool LView::AttachChildren() { for (auto c: Children) { bool a = c->IsAttached(); if (!a) { if (!c->Attach(this)) { LgiTrace("%s:%i - failed to attach %s\n", _FL, c->GetClass()); return false; } } } return true; } LFont *LView::GetFont() { if (!d->Font && d->Css && LResources::GetLoadStyles()) { LFontCache *fc = LAppInst->GetFontCache(); if (fc) { LFont *f = fc->GetFont(d->Css); if (f) { if (d->FontOwnType == GV_FontOwned) DeleteObj(d->Font); d->Font = f; d->FontOwnType = GV_FontCached; } } } return d->Font ? d->Font : LSysFont; } void LView::SetFont(LFont *Font, bool OwnIt) { bool Change = d->Font != Font; if (Change) { if (d->FontOwnType == GV_FontOwned) { LAssert(d->Font != LSysFont); DeleteObj(d->Font); } d->FontOwnType = OwnIt ? GV_FontOwned : GV_FontPtr; d->Font = Font; #if WINNATIVE if (_View) SendMessage(_View, WM_SETFONT, (WPARAM) (Font ? Font->Handle() : 0), 0); #endif for (LViewI *p = GetParent(); p; p = p->GetParent()) { LTableLayout *Tl = dynamic_cast(p); if (Tl) { Tl->InvalidateLayout(); break; } } Invalidate(); } } bool LView::IsOver(LMouse &m) { return (m.x >= 0) && (m.y >= 0) && (m.x < Pos.X()) && (m.y < Pos.Y()); } bool LView::WindowVirtualOffset(LPoint *Offset) { bool Status = false; if (Offset) { Offset->x = 0; Offset->y = 0; for (LViewI *Wnd = this; Wnd; Wnd = Wnd->GetParent()) { #if !LGI_VIEW_HANDLE auto IsWnd = dynamic_cast(Wnd); if (!IsWnd) #else if (!Wnd->Handle()) #endif { LRect r = Wnd->GetPos(); LViewI *Par = Wnd->GetParent(); if (Par) { LRect c = Par->GetClient(false); Offset->x += r.x1 + c.x1; Offset->y += r.y1 + c.y1; } else { Offset->x += r.x1; Offset->y += r.y1; } Status = true; } else break; } } return Status; } LString _ViewDesc(LViewI *v) { LString s; s.Printf("%s/%s/%i", v->GetClass(), v->Name(), v->GetId()); return s; } LViewI *LView::WindowFromPoint(int x, int y, int DebugDepth) { char Tabs[64]; if (DebugDepth) { memset(Tabs, 9, DebugDepth); Tabs[DebugDepth] = 0; LgiTrace("%s%s %i\n", Tabs, _ViewDesc(this).Get(), Children.Length()); } // We iterate over the child in reverse order because if they overlap the // end of the list is on "top". So they should get the click or whatever // before the the lower windows. auto it = Children.rbegin(); int n = (int)Children.Length() - 1; for (LViewI *c = *it; c; c = *--it) { LRect CPos = c->GetPos(); if (CPos.Overlap(x, y) && c->Visible()) { LRect CClient; CClient = c->GetClient(false); int Ox = CPos.x1 + CClient.x1; int Oy = CPos.y1 + CClient.y1; if (DebugDepth) { LgiTrace("%s[%i] %s Pos=%s Client=%s m(%i,%i)->(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), CClient.GetStr(), x, y, x - Ox, y - Oy); } LViewI *Child = c->WindowFromPoint(x - Ox, y - Oy, DebugDepth ? DebugDepth + 1 : 0); if (Child) return Child; } else if (DebugDepth) { LgiTrace("%s[%i] MISSED %s Pos=%s m(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), x, y); } } if (x >= 0 && y >= 0 && x < Pos.X() && y < Pos.Y()) { return this; } return NULL; } LColour LView::StyleColour(int CssPropType, LColour Default, int Depth) { LColour c = Default; if ((CssPropType >> 8) == LCss::TypeColor) { LViewI *v = this; for (int i=0; v && iGetParent()) { auto Style = v->GetCss(); if (Style) { auto Colour = (LCss::ColorDef*) Style->PropAddress((LCss::PropType)CssPropType); if (Colour) { if (Colour->Type == LCss::ColorRgb) { c.Set(Colour->Rgb32, 32); break; } else if (Colour->Type == LCss::ColorTransparent) { c.Empty(); break; } } } if (dynamic_cast(v) || dynamic_cast(v)) break; } } return c; } bool LView::InThread() { #if WINNATIVE HWND Hnd = _View; for (LViewI *p = GetParent(); p && !Hnd; p = p->GetParent()) { Hnd = p->Handle(); } auto CurThreadId = GetCurrentThreadId(); auto GuiThreadId = LAppInst->GetGuiThreadId(); DWORD ViewThread = Hnd ? GetWindowThreadProcessId(Hnd, NULL) : GuiThreadId; return CurThreadId == ViewThread; #elif defined(HAIKU) return true; #else OsThreadId Me = GetCurrentThreadId(); OsThreadId Gui = LAppInst ? LAppInst->GetGuiThreadId() : 0; #if 0 if (Gui != Me) LgiTrace("%s:%i - Out of thread:" #ifdef LGI_COCOA "%llx, %llx" #else "%x, %x" #endif "\n", _FL, Gui, Me); #endif return Gui == Me; #endif } bool LView::PostEvent(int Cmd, LMessage::Param a, LMessage::Param b, int64_t timeoutMs) { #ifdef LGI_SDL return LPostEvent(this, Cmd, a, b); #elif defined(HAIKU) if (!d || !d->Hnd) { // printf("%s:%i - Bad pointers %p %p\n", _FL, d, d ? d->Hnd : NULL); return false; } BWindow *bWnd = NULL; LWindow *wnd = dynamic_cast(this); if (wnd) { bWnd = wnd->WindowHandle(); } else { // Look for an attached view to lock... for (LViewI *v = this; v; v = v->GetParent()) { auto vhnd = v->Handle(); if (vhnd && ::IsAttached(vhnd)) { bWnd = vhnd->Window(); break; } } } BMessage m(Cmd); auto r = m.AddInt64(LMessage::PropA, a); if (r != B_OK) printf("%s:%i - AddUInt64 failed.\n", _FL); r = m.AddInt64(LMessage::PropB, b); if (r != B_OK) printf("%s:%i - AddUInt64 failed.\n", _FL); r = m.AddPointer(LMessage::PropView, this); if (r != B_OK) printf("%s:%i - AddPointer failed.\n", _FL); if (bWnd) { r = bWnd->PostMessage(&m); if (r != B_OK) printf("%s:%i - PostMessage failed.\n", _FL); return r == B_OK; } // Not attached yet... d->MsgQue.Add(new BMessage(m)); // printf("%s:%i - PostEvent.MsgQue=%i\n", _FL, (int)d->MsgQue.Length()); return true; #elif WINNATIVE if (!_View) return false; BOOL Res = ::PostMessage(_View, Cmd, a, b); if (!Res) { auto Err = GetLastError(); int asd=0; } return Res != 0; #elif !LGI_VIEW_HANDLE return LAppInst->PostEvent(this, Cmd, a, b); #else if (_View) return LPostEvent(_View, Cmd, a, b); LAssert(0); return false; #endif } bool LView::Invalidate(LRegion *r, bool Repaint, bool NonClient) { if (r) { for (int i=0; iLength(); i++) { bool Last = i == r->Length()-1; Invalidate((*r)[i], Last ? Repaint : false, NonClient); } return true; } return false; } LButton *FindDefault(LViewI *w) { LButton *But = 0; for (auto c: w->IterateViews()) { But = dynamic_cast(c); if (But && But->Default()) { break; } But = FindDefault(c); if (But) { break; } } return But; } bool LView::Name(const char *n) { LBase::Name(n); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) { #if WINNATIVE auto Temp = LBase::NameW(); SetWindowTextW(_View, Temp ? Temp : L""); #endif } #endif Invalidate(); return true; } const char *LView::Name() { #if WINNATIVE if (_View) { LView::NameW(); } #endif return LBase::Name(); } bool LView::NameW(const char16 *n) { LBase::NameW(n); #if WINNATIVE if (_View && n) { auto Txt = LBase::NameW(); SetWindowTextW(_View, Txt); } #endif Invalidate(); return true; } const char16 *LView::NameW() { #if WINNATIVE if (_View) { int Length = GetWindowTextLengthW(_View); if (Length > 0) { char16 *Buf = new char16[Length+1]; if (Buf) { Buf[0] = 0; int Chars = GetWindowTextW(_View, Buf, Length+1); Buf[Chars] = 0; LBase::NameW(Buf); } DeleteArray(Buf); } else { LBase::NameW(0); } } #endif return LBase::NameW(); } LViewI *LView::FindControl(int Id) { LAssert(Id != -1); if (GetId() == Id) { return this; } for (auto c : Children) { LViewI *Ctrl = c->FindControl(Id); if (Ctrl) { return Ctrl; } } return 0; } LPoint LView::GetMinimumSize() { return d->MinimumSize; } void LView::SetMinimumSize(LPoint Size) { d->MinimumSize = Size; bool Change = false; LRect p = Pos; if (X() < d->MinimumSize.x) { p.x2 = p.x1 + d->MinimumSize.x - 1; Change = true; } if (Y() < d->MinimumSize.y) { p.y2 = p.y1 + d->MinimumSize.y - 1; Change = true; } if (Change) { SetPos(p); } } bool LView::SetColour(LColour &c, bool Fore) { LCss *css = GetCss(true); if (!css) return false; if (Fore) { if (c.IsValid()) css->Color(LCss::ColorDef(LCss::ColorRgb, c.c32())); else css->DeleteProp(LCss::PropColor); } else { if (c.IsValid()) css->BackgroundColor(LCss::ColorDef(LCss::ColorRgb, c.c32())); else css->DeleteProp(LCss::PropBackgroundColor); } return true; } /* bool LView::SetCssStyle(const char *CssStyle) { if (!d->Css && !d->Css.Reset(new LCss)) return false; const char *Defs = CssStyle; bool b = d->Css->Parse(Defs, LCss::ParseRelaxed); if (b && d->FontOwnType == GV_FontCached) { d->Font = NULL; d->FontOwnType = GV_FontPtr; } return b; } */ void LView::SetCss(LCss *css) { d->Css.Reset(css); } LCss *LView::GetCss(bool Create) { if (Create && !d->Css) d->Css.Reset(new LCss); if (d->CssDirty && d->Css) { const char *Defs = d->Styles; if (d->Css->Parse(Defs, LCss::ParseRelaxed)) d->CssDirty = false; } return d->Css; } LPoint &LView::GetWindowBorderSize() { static LPoint s; ZeroObj(s); #if WINNATIVE if (_View) { RECT Wnd, Client; GetWindowRect(Handle(), &Wnd); GetClientRect(Handle(), &Client); s.x = (Wnd.right-Wnd.left) - (Client.right-Client.left); s.y = (Wnd.bottom-Wnd.top) - (Client.bottom-Client.top); } #elif defined __GTK_H__ #elif defined MAC s.x = 0; s.y = 22; #endif return s; } #ifdef _DEBUG #if defined(LGI_CARBON) void DumpHiview(HIViewRef v, int Depth = 0) { char Sp[256]; memset(Sp, ' ', Depth << 2); Sp[Depth<<2] = 0; printf("%sHIView=%p", Sp, v); if (v) { Boolean vis = HIViewIsVisible(v); Boolean en = HIViewIsEnabled(v, NULL); HIRect pos; HIViewGetFrame(v, &pos); char cls[128]; ZeroObj(cls); GetControlProperty(v, 'meme', 'clas', sizeof(cls), NULL, cls); printf(" vis=%i en=%i pos=%g,%g-%g,%g cls=%s", vis, en, pos.origin.x, pos.origin.y, pos.size.width, pos.size.height, cls); } printf("\n"); for (HIViewRef c = HIViewGetFirstSubview(v); c; c = HIViewGetNextView(c)) { DumpHiview(c, Depth + 1); } } #elif defined(__GTK_H__) void DumpGtk(Gtk::GtkWidget *w, Gtk::gpointer Depth = NULL) { using namespace Gtk; if (!w) return; char Sp[65] = {0}; if (Depth) memset(Sp, ' ', *((int*)Depth)*2); auto *Obj = G_OBJECT(w); LViewI *View = (LViewI*) g_object_get_data(Obj, "LViewI"); GtkAllocation a; gtk_widget_get_allocation(w, &a); LgiTrace("%s%p(%s) = %i,%i-%i,%i\n", Sp, w, View?View->GetClass():G_OBJECT_TYPE_NAME(Obj), a.x, a.y, a.width, a.height); if (GTK_IS_CONTAINER(w)) { auto *c = GTK_CONTAINER(w); if (c) { int Next = Depth ? *((int*)Depth)+1 : 1; gtk_container_foreach(c, DumpGtk, &Next); } } } #elif defined(HAIKU) || defined(WINDOWS) template void _Dump(int Depth, T v) { LString Sp, Name; Sp.Length(Depth<<1); memset(Sp.Get(), ' ', Depth<<1); Sp.Get()[Depth<<1] = 0; #if defined(HAIKU) LRect Frame = v->Frame(); Name = v->Name(); bool IsHidden = v->IsHidden(); #else RECT rc; GetWindowRect(v, &rc); LRect Frame = rc; wchar_t txt[256]; GetWindowTextW(v, txt, CountOf(txt)); Name = txt; bool IsHidden = !(GetWindowLong(v, GWL_STYLE) & WS_VISIBLE); #endif LgiTrace("%s%p::%s frame=%s vis=%i\n", Sp.Get(), v, Name.Get(), Frame.GetStr(), !IsHidden); #if defined(HAIKU) for (int32 i=0; iCountChildren(); i++) _Dump(Depth + 1, v->ChildAt(i)); #else for (auto h = GetWindow(v, GW_CHILD); h; h = GetWindow(h, GW_HWNDNEXT)) _Dump(Depth + 1, h); #endif } #endif void LView::_Dump(int Depth) { char Sp[65] = {0}; memset(Sp, ' ', Depth*2); #if 0 char s[256]; sprintf_s(s, sizeof(s), "%s%p::%s %s (_View=%p)\n", Sp, this, GetClass(), GetPos().GetStr(), _View); LgiTrace(s); List::I i = Children.Start(); for (LViewI *c = *i; c; c = *++i) { LView *v = c->GetGView(); if (v) v->_Dump(Depth+1); } #elif defined(LGI_CARBON) DumpHiview(_View); #elif defined(__GTK_H__) // DumpGtk(_View); - #else + #elif !defined(MAC) #if defined(HAIKU) LLocker lck(WindowHandle(), _FL); if (lck.Lock()) #endif ::_Dump(0, WindowHandle()); #endif } #endif //////////////////////////////////////////////////////////////////////////////////////////////////// static LArray *AllFactories = NULL; #if defined(WIN32) static HANDLE FactoryEvent; #else static pthread_once_t FactoryOnce = PTHREAD_ONCE_INIT; static void GFactoryInitFactories() { AllFactories = new LArray; } #endif LViewFactory::LViewFactory() { #if defined(WIN32) char16 Name[64]; swprintf_s(Name, CountOf(Name), L"LgiFactoryEvent.%i", GetCurrentProcessId()); HANDLE h = CreateEventW(NULL, false, false, Name); DWORD err = GetLastError(); if (err != ERROR_ALREADY_EXISTS) { FactoryEvent = h; AllFactories = new LArray; } else { LAssert(AllFactories != NULL); } #else pthread_once(&FactoryOnce, GFactoryInitFactories); #endif if (AllFactories) AllFactories->Add(this); } LViewFactory::~LViewFactory() { if (AllFactories) { AllFactories->Delete(this); if (AllFactories->Length() == 0) { DeleteObj(AllFactories); #if defined(WIN32) CloseHandle(FactoryEvent); #endif } } } LView *LViewFactory::Create(const char *Class, LRect *Pos, const char *Text) { if (ValidStr(Class) && AllFactories) { for (int i=0; iLength(); i++) { LView *v = (*AllFactories)[i]->NewView(Class, Pos, Text); if (v) { return v; } } } return 0; } #ifdef _DEBUG #if defined(__GTK_H__) using namespace Gtk; #include "LgiWidget.h" #endif void LView::Debug() { _Debug = true; #if defined LGI_COCOA d->ClassName = GetClass(); #endif } #endif diff --git a/src/mac/cocoa/App.mm b/src/mac/cocoa/App.mm --- a/src/mac/cocoa/App.mm +++ b/src/mac/cocoa/App.mm @@ -1,946 +1,946 @@ #include #include #include #include #include #include #import #include "lgi/common/Lgi.h" #include "lgi/common/Process.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/Array.h" #include "lgi/common/Thread.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/Menu.h" #include #include #include #include #include #import "LCocoaView.h" #include "AppPriv.h" extern int hndstate(int hnd); struct OsAppArgumentsPriv { LAutoString Str; LArray Ptr; }; OsAppArguments::OsAppArguments(int args, const char **arg) { d = new OsAppArgumentsPriv; Args = args; Arg = arg; } OsAppArguments::~OsAppArguments() { DeleteObj(d); } void OsAppArguments::Set(const char *CmdLine) { d->Str.Reset(); d->Ptr.Length(0); LArray Raw; LArray Offsets; auto Exe = LGetExeFile(); Offsets.Add(0); if (Exe) { Raw.Length(Exe.Length() + 1); strcpy(Raw.AddressOf(), Exe); } else { Raw.Add(0); } if (CmdLine) { for (auto s = CmdLine; *s; ) { while (*s && strchr(WhiteSpace, *s)) s++; const char *e; if (*s == '\'' || *s == '\"') { auto delim = *s++; Offsets.Add(Raw.Length()); e = s; while (*e && *e != delim) { Raw.Add(*e++); } Raw.Add(0); } else { Offsets.Add(Raw.Length()); for (e = s; *e && !strchr(WhiteSpace, *e); e++) { Raw.Add(*e); } Raw.Add(0); } s = *e ? e + 1 : e; } } d->Str.Reset(Raw.Release()); for (int n=0; nPtr[n] = d->Str + Offsets[n]; } Args = (int)d->Ptr.Length(); Arg = (const char**) &d->Ptr[0]; } OsAppArguments &OsAppArguments::operator =(OsAppArguments &a) { LArray Raw; LArray Offsets; for (int i=0; iStr.Reset(new char[Raw.Length()]); memcpy(d->Str, &Raw[0], Raw.Length()); for (int n=0; nPtr[n] = d->Str + Offsets[n]; } Args = (int)d->Ptr.Length(); Arg = (const char**) &d->Ptr[0]; return *this; } //////////////////////////////////////////////////////////////// #if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 #define SDK_10_12(newSym, oldSym) newSym #else #define SDK_10_12(newSym, oldSym) oldSym #endif void LMouse::SetFromEvent(NSEvent *ev, NSView *view) { auto r = view.frame; auto pt = ev.locationInWindow; x = (int)pt.x; y = (int)(r.size.height - pt.y); SetModifer((uint32)ev.modifierFlags); Event = ev; switch (ev.type) { case SDK_10_12(NSEventTypeLeftMouseDown, NX_LMOUSEDOWN): Down(true); Left(true); break; case SDK_10_12(NSEventTypeLeftMouseUp, NX_LMOUSEUP): Down(false); Left(true); break; case SDK_10_12(NSEventTypeRightMouseDown, NX_RMOUSEDOWN): Down(true); Right(true); break; case SDK_10_12(NSEventTypeRightMouseUp, NX_RMOUSEUP): Down(false); Right(true); break; case SDK_10_12(NSEventTypeOtherMouseDown, NX_OMOUSEDOWN): Down(true); Middle(true); break; case SDK_10_12(NSEventTypeOtherMouseUp, NX_OMOUSEUP): Down(false); Middle(true); break; case SDK_10_12(NSEventTypeMouseMoved, NX_MOUSEMOVED): IsMove(true); break; case SDK_10_12(NSEventTypeLeftMouseDragged, NX_LMOUSEDRAGGED): Down(true); IsMove(true); Left(true); break; case SDK_10_12(NSEventTypeRightMouseDragged, NX_RMOUSEDRAGGED): Down(true); IsMove(true); Right(true); break; default: LAssert(!"Unknown event."); break; } Double(ev.clickCount == 2 && Down()); } void LUiEvent::SetModifer(uint32_t modifierKeys) { System(modifierKeys & SDK_10_12(NSEventModifierFlagCommand, NSCommandKeyMask)); Shift (modifierKeys & SDK_10_12(NSEventModifierFlagShift, NSShiftKeyMask)); Alt (modifierKeys & SDK_10_12(NSEventModifierFlagOption, NSAlternateKeyMask)); Ctrl (modifierKeys & SDK_10_12(NSEventModifierFlagControl, NSControlKeyMask)); } void LMessage::Set(int msg, Param A, Param B) { m = msg; a = A; b = B; } //////////////////////////////////////////////////////////////// void OnSigPipe(int i) { } void OnCrash(int i) { printf("%s:%i - on crash.\n", __FILE__, __LINE__); signal(SIGBUS, 0); signal(SIGSEGV, 0); struct Pipe { int Read; int Write; Pipe() { Read = -1; Write = -1; } }; Pipe Read; Pipe Write; Pipe Error; int Pid; pipe((int*)&Read); pipe((int*)&Error); auto Exe = LGetExeFile(); // Has stdin pipe pipe((int*)&Write); if (!(Pid = fork())) { // stdin -> Write close(0); // close stdin dup(Write.Read); close(Write.Write); // stdout -> Read close(1); // close stdout dup(Read.Write); close(Read.Read); // stderr -> Error close(2); // close stderr dup(Error.Write); close(Error.Read); // setup read & write handles char sPid[32]; sprintf(sPid, "--pid=%i", getpid()); char *Args[] = {sPid, Exe, 0}; // printf("Calling: execv('gdb', '%s', '%s');\n", Exe, sPid); execv("/usr/bin/gdb", Args); // We should never get here printf("%s:%i - execv(gdb) failed.\n", __FILE__, __LINE__); exit(-1); } ssize_t r, Used = 0; char Buf[1025]; bool Capture = false; while ((r = read(Read.Read, Buf + Used, sizeof(Buf) - Used - 1)) > 0) { printf("Got %i bytes\n", (int)r); Used += r; Buf[Used] = 0; printf("Capt=%i Buf='%s'\n", Capture, Buf); if (!Capture) { if (stristr(Buf, "(gdb)")) { char c[] = "info pid\n"; ssize_t w = write(Write.Write, c, strlen(c)); printf("Writing cmd %i bytes\n", (int)w); Capture = true; Used = 0; Buf[0] = 0; } } char *Eol; while ((Eol = strstr(Buf, "\n"))) { *Eol = 0; if (Capture) { printf("Capture '%s'\n", Buf); } Eol += 1; ptrdiff_t Len = Eol - Buf; memmove(Buf, Eol, Used + 1 - Len); Used -= Len; } } exit(-1); } //////////////////////////////////////////////////////////////////////////// @implementation LNsApplication - (id)init { if ((self = [super init]) != nil) { self.d = NULL; } return self; } - (void)setPriv:(nonnull LAppPrivate*)priv { self.d = priv; } - (void)terminate:(nullable id)sender { [super terminate:sender]; } - (void)dealloc { [super dealloc]; } - (void)assert:(LCocoaAssert*)ca { NSAlert *a = [[NSAlert alloc] init]; a.messageText = ca.msg.NsStr(); a.alertStyle = NSAlertStyleCritical; [a addButtonWithTitle:@"Debug"]; [a addButtonWithTitle:@"Ignore"]; [a addButtonWithTitle:@"Abort"]; ca.result = [a runModal]; [a release]; } - (void)onUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply { LString s = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; if (self.d && self.d->Owner) self.d->Owner->OnUrl(s); } @end ///////////////////////////////////////////////////////////////////////////// LSkinEngine *LApp::SkinEngine = 0; LApp *TheApp = 0; LMouseHook *LApp::MouseHook = 0; LApp::LApp(OsAppArguments &AppArgs, const char *AppName, LAppArguments *ObjArgs) : OsApplication(AppArgs.Args, AppArgs.Arg) { TheApp = this; d = new LAppPrivate(this); d->Name.Reset(NewStr(AppName)); AppWnd = 0; Name(AppName); if (LIsRelativePath(AppArgs.Arg[0])) { char wd[MAX_PATH_LEN]; char exe[MAX_PATH_LEN]; if (LMakePath(exe, sizeof(exe), getcwd(wd, sizeof(wd)), AppArgs.Arg[0])) LgiArgsAppPath = exe; else printf("%s:%i - LMakePath for Exe failed.\n", _FL); } else LgiArgsAppPath = AppArgs.Arg[0]; // printf("%s:%i - LgiArgsAppPath='%s'\n", _FL, LgiArgsAppPath.Get()); // Catch and ignore SIGPIPE signal(SIGPIPE, OnSigPipe); #if 0 // Crash handler... signal(SIGBUS, OnCrash); signal(SIGSEGV, OnCrash); #endif // We want our printf's NOW! setvbuf(stdout,(char *)NULL,_IONBF,0); // print mesgs immediately. // Connect to the server d->NsApp = [LNsApplication sharedApplication]; [d->NsApp setPriv:d]; // Register to get apple events NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager]; [em setEventHandler:d->NsApp andSelector:@selector(onUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; // Setup the file and graphics sub-systems d->FileSystem = new LFileSystem; d->GdcSystem = new GdcDevice; srand((unsigned)LCurrentTime()); LColour::OnChange(); SetAppArgs(AppArgs); MouseHook = new LMouseHook; // System font setup SystemNormal = 0; LFontType SysFontType; if (SysFontType.GetSystemFont("System")) { SystemNormal = SysFontType.Create(); if (SystemNormal) { SystemNormal->Transparent(true); } else { printf("%s:%i - Error creating system font.\n", __FILE__, __LINE__); } SystemBold = SysFontType.Create(); if (SystemBold) { SystemBold->Bold(true); SystemBold->Transparent(true); SystemBold->Create(); } else { printf("%s:%i - Error creating bold version of the system font.\n", __FILE__, __LINE__); } } else { printf("%s:%i - Couldn't get system font setting.\n", __FILE__, __LINE__); } if (!SystemNormal) { LgiMsg(0, "Error: Couldn't create system font.", "Lgi Error: LApp::LApp", MB_OK); LExitApp(); } if (!GetOption("noskin")) { extern LSkinEngine *CreateSkinEngine(LApp *App); SkinEngine = CreateSkinEngine(this); } Default.Reset(new LMenu); } LApp::~LApp() { DeleteObj(AppWnd); DeleteObj(SystemNormal); DeleteObj(SystemBold); DeleteObj(MouseHook); DeleteObj(d->FileSystem); DeleteObj(d->GdcSystem); DeleteObj(LFontSystem::Me); DeleteObj(d); TheApp = 0; } OsApp &LApp::Handle() { return d->NsApp; } bool LApp::PostEvent(LViewI *View, int Msg, LMessage::Param A, LMessage::Param B) { if (!View) { printf("%s:%i - No view.\n", _FL); return false; } bool Exists = LView::LockHandler(View, LView::LockOp::OpExists); if (!Exists) { printf("%s:%i - View deleted.\n", _FL); return false; } LWindow *w = View->GetWindow(); if (!w) { // printf("%s:%i - No window.\n", _FL); return false; } auto v = w->Handle(); if (!v) { // printf("%s:%i - No handle.\n", _FL); return false; } auto m = [[LCocoaMsg alloc] init:View msg:Msg a:A b:B]; [v performSelectorOnMainThread:@selector(userEvent:) withObject:m waitUntilDone:false]; return true; } LApp *LApp::ObjInstance() { return TheApp; } bool LApp::IsOk() { bool Status = #if !defined(__clang__) (this != 0) && #endif (d != 0) /* #ifdef XWIN && (XDisplay() != 0) #endif */ ; LAssert(Status); return Status; } LMouseHook *LApp::GetMouseHook() { return MouseHook; } int LApp::GetMetric(LSystemMetric Metric) { switch (Metric) { default: break; case LGI_MET_DECOR_X: { return 0; } case LGI_MET_DECOR_Y: case LGI_MET_DECOR_CAPTION: { if (AppWnd) { #if 0 Rect r; OSStatus e = GetWindowBounds(AppWnd->WindowHandle(), kWindowTitleBarRgn, &r); if (e) printf("%s:%i - GetWindowBounds failed with %i\n", _FL, (int)e); else { int y = r.bottom - r.top; return y; } #endif } return 22; } } return 0; } LViewI *LApp::GetFocus() { auto kw = d->NsApp.p.keyWindow; if (!kw) return NULL; LNsWindow *w = objc_dynamic_cast(LNsWindow, kw); LWindow *gw = w ? [w getWindow] : nil; if (!gw) return NULL; return gw->GetFocus(); } OsThread LApp::_GetGuiThread() { return d->GuiThread; } OsThreadId LApp::GetGuiThreadId() { return d->GuiThreadId; } bool LApp::InThread() { return GetCurrentThreadId() == d->GuiThreadId; } OsProcessId LApp::GetProcessId() { return getpid(); } OsAppArguments *LApp::GetAppArgs() { return IsOk() ? &d->Args : 0; } void LApp::SetAppArgs(OsAppArguments &AppArgs) { if (IsOk()) { d->Args = AppArgs; } } struct IdleGluePtrs { LApp::OnIdleProc Callback; void *Param; }; #define CUSTOM_LOOP 0 #if 0 void IdleGlue(EventLoopTimerRef inTimer, void *inUserData) { IdleGluePtrs *p = (IdleGluePtrs*)inUserData; p->Callback(p->Param); } #endif bool LApp::Run(OnIdleProc IdleCallback, void *IdleParam) { if (!d->NsApp) { LAssert(!"No d->NsApp"); return false; } #if CUSTOM_LOOP // This impl allows for us to exit gracefully. int Depth = ++d->RunDepth; do { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSEvent *event = [ d->NsApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; [d->NsApp sendEvent:event]; [d->NsApp updateWindows]; [pool release]; } while (d->RunDepth >= Depth); #else OnCommandLine(); NSApplicationMain(GetArgs(), GetArg()); #endif return true; } bool LApp::Yield() { printf("%s:%i - Yield not supported.\n", _FL); return false; } void LApp::Exit(int Code) { #if CUSTOM_LOOP if (!Code) { if (d->RunDepth > 0) d->RunDepth--; } #else if (!Code) { if (AppWnd) AppWnd->Quit(); [d->NsApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; } else #endif { // hard exit ::exit(Code); } } void LApp::OnUrl(const char *Url) { if (AppWnd) AppWnd->OnUrl(Url); else d->UrlArg.Reset(NewStr(Url)); } void LApp::OnReceiveFiles(LArray &Files) { if (AppWnd) AppWnd->OnReceiveFiles(Files); } const char *LApp::GetArgumentAt(int n) { return n >= 0 && n < d->Args.Args ? d->Args.Arg[n] : 0; } bool LApp::GetOption(const char *Option, char *Dest, int DestLen) { LString Buf; if (GetOption(Option, Buf)) { if (Dest) strcpy_s(Dest, DestLen, Buf); return true; } return false; } bool LApp::GetOption(const char *Option, LString &Buf) { if (IsOk() && Option) { size_t OptLen = strlen(Option); for (int i=1; iArgs.Args; i++) { const char *a = d->Args.Arg[i]; if (strchr("-/\\", a[0])) { if (strncmp(a+1, Option, OptLen) == 0) { const char *Arg = 0; if (strlen(a+1+OptLen) > 0) { Arg = a + 1 + OptLen; } else if (i < d->Args.Args - 1) { Arg = d->Args.Arg[i + 1]; } if (Arg) { if (strchr("\'\"", *Arg)) { char Delim = *Arg++; char *End = strchr(Arg, Delim); if (End) { size_t Len = End-Arg; if (Len > 0) { Buf.Set(Arg, Len); } else return false; } else return false; } else { Buf = Arg; } } return true; } } } } return false; } void LApp::OnCommandLine() { LArray Files; for (int i=1; iArgs; i++) { const char *a = GetAppArgs()->Arg[i]; if (LFileExists(a)) { Files.Add(NewStr(a)); } } // call app if (Files.Length() > 0) { OnReceiveFiles(Files); } // clear up Files.DeleteArrays(); } LString MimeFromData(const char *File) { LString Ret; LFile f; if (!f.Open(File, O_READ)) return Ret; LArray b; b.Length(1024); auto r = f.Read(b.AddressOf(), b.Length()); if (r <= 0) return Ret; if (b.Length() >= 8) { if (memcmp(b.AddressOf(), "GIF89a\x01", 7) == 0) Ret = "image/gif"; } return Ret; } LString LApp::GetFileMimeType(const char *File) { LString Ret; if (!LFileExists(File)) { // Look in the path auto p = LString(getenv("PATH")).SplitDelimit(LGI_PATH_SEPARATOR); for (int i=0; i &Apps) +bool LApp::GetAppsForMimeType(const char *Mime, LArray &Apps) { // Use LSCopyApplicationForMIMEType? // Find alternative version of the MIME type (e.g. x-type and type). char AltMime[256]; strcpy(AltMime, Mime); char *s = strchr(AltMime, '/'); if (s) { s++; size_t Len = strlen(s) + 1; if (strnicmp(s, "x-", 2) == 0) { memmove(s, s+2, Len - 2); } else { memmove(s+2, s, Len); s[0] = 'x'; s[1] = '-'; } } if (!d->MimeToApp.Length()) { // printf("%s:%i - Building MimeToApp.\n", __FILE__, __LINE__); } AppArray *p = (AppArray*)d->MimeToApp.Find(Mime); if (p) { for (int i=0; iLength(); i++) { - Apps[i] = (*p)[i]; + Apps[i] = *(*p)[i]; } return true; } return false; } LSymLookup *LApp::GetSymLookup() { return &d->SymLookup; } bool LApp::IsElevated() { return geteuid() == 0; } int LApp::GetCpuCount() { return 1; } LFontCache *LApp::GetFontCache() { if (!d->FontCache) d->FontCache.Reset(new LFontCache(SystemNormal)); return d->FontCache; } diff --git a/src/mac/cocoa/General.mm b/src/mac/cocoa/General.mm --- a/src/mac/cocoa/General.mm +++ b/src/mac/cocoa/General.mm @@ -1,581 +1,602 @@ // Mac Implementation of General LGI functions #include #include #include #include #include // #define _POSIX_TIMERS #include #include "lgi/common/Lgi.h" #include "lgi/common/Process.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/Net.h" #include #include #include //////////////////////////////////////////////////////////////// // Local helper functions +CFStringRef Utf8ToCFString(const char *s, ssize_t len = -1) +{ + if (s && len < 0) + len = strlen(s); + return CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)s, len, kCFStringEncodingUTF8, false); +} + +char *CFStringToUtf8(CFStringRef r) +{ + if (r == NULL) + return 0; + + char *Buffer = 0; + CFRange g = { 0, CFStringGetLength(r) }; + CFIndex Used; + + if (CFStringGetBytes(r, + g, + kCFStringEncodingUTF8, + 0, + false, + 0, + 0, + &Used)) + { + if ((Buffer = new char[Used+1])) + { + CFStringGetBytes(r, + g, + kCFStringEncodingUTF8, + 0, + false, + (UInt8*)Buffer, + Used, + &Used); + + Buffer[Used] = 0; + } + } + + return Buffer; +} + bool _lgi_check_file(char *Path) { if (Path) { if (LFileExists(Path)) { // file is there return true; } else { // shortcut? char *e = Path + strlen(Path); strcpy(e, ".lnk"); if (LFileExists(Path)) { // resolve shortcut char Link[256]; if (LResolveShortcut(Path, Link, sizeof(Link))) { // check destination of link if (LFileExists(Link)) { strcpy(Path, Link); return true; } } } *e = 0; } } return false; } void LSleep(uint32 i) { struct timespec request, remain; ZeroObj(request); ZeroObj(remain); request.tv_sec = i / 1000; request.tv_nsec = (i % 1000) * 1000000; while (nanosleep(&request, &remain) == -1) { request = remain; } } char *p2c(unsigned char *s) { if (s) { s[1+s[0]] = 0; return (char*)s + 1; } return 0; } void c2p255(Str255 &d, char *s) { if (s) { size_t Len = strlen(s); if (Len > 255) Len = 255; d[0] = Len; for (int i=0; iHandle(); [hnd.p performSelectorOnMainThread:@selector(assert:) withObject:ca waitUntilDone:true]; switch (ca.result) { case NSAlertFirstButtonReturn: // Debug/Break Result = 2; break; case NSAlertSecondButtonReturn: // Ingore/Continue Result = 3; break; case NSAlertThirdButtonReturn: // Exit/Abort Result = 1; break; } [ca release]; } #else GAlert a(0, "Assert Failed", Assert.Msg, "Abort", "Debug", "Ignore"); Result = a.DoModal(); #endif switch (Result) { default: { exit(-1); break; } case 2: { // Crash here to bring up the debugger... int *p = 0; *p = 0; break; } case 3: { break; } } #endif Asserting = false; } } //////////////////////////////////////////////////////////////////////// // Implementations LMessage CreateMsg(int m, LMessage::Param a, LMessage::Param b) { static class LMessage Msg(0); Msg.Set(m, a, b); return Msg; } OsView DefaultOsView(LView *v) { return NULL; } LString LGetFileMimeType(const char *File) { return LAppInst ? LAppInst->GetFileMimeType(File) : NULL; } bool _GetIniField(char *Grp, char *Field, char *In, char *Out, int OutSize) { if (ValidStr(In)) { bool InGroup = false; auto t = LString(In).SplitDelimit("\r\n"); for (int i=0; i Ver; LgiGetOs(Ver); if (Ver.Length() > 1) { if (Ver[0] < 10 || Ver[1] < 6) { IsAppBundle = false; } } } */ } struct stat s; int st = stat(File, &s); if (IsAppBundle) { char cmd[512]; if (ValidStr((char*)Args)) snprintf(cmd, sizeof(cmd), "open -a \"%s\" %s", File, Args); else snprintf(cmd, sizeof(cmd), "open -a \"%s\"", File); system(cmd); } else if (st == 0 && S_ISREG(s.st_mode) && (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { // This is an executable file if (!fork()) { if (Dir) chdir(Dir); LArray a; a.Add(File); char *p; while ((p = LTokStr(Args))) { a.Add(p); } a.Add(0); char *env[] = {0}; execve(File, (char*const*)&a[0], env); } return true; } else { // Document #if LGI_CARBON e = FinderLaunch(1, &r); if (e) printf("%s:%i - FinderLaunch faied with %i\n", _FL, (int)e); else Status = true; #elif LGI_COCOA LString file = File; auto url = [[NSURL alloc] initFileURLWithPath:file.NsStr()]; Status = [[NSWorkspace sharedWorkspace] openURL:url]; #endif } } } } return Status; } -CFStringRef Utf8ToCFString(char *s, ssize_t len) -{ - if (s && len < 0) - len = strlen(s); - return CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)s, len, kCFStringEncodingUTF8, false); -} - -char *CFStringToUtf8(CFStringRef r) -{ - if (r == NULL) - return 0; - - char *Buffer = 0; - CFRange g = { 0, CFStringGetLength(r) }; - CFIndex Used; - - if (CFStringGetBytes(r, - g, - kCFStringEncodingUTF8, - 0, - false, - 0, - 0, - &Used)) - { - if ((Buffer = new char[Used+1])) - { - CFStringGetBytes(r, - g, - kCFStringEncodingUTF8, - 0, - false, - (UInt8*)Buffer, - Used, - &Used); - - Buffer[Used] = 0; - } - } - - return Buffer; -} - bool LGetMimeTypeExtensions(const char *Mime, LArray &Ext) { size_t Start = Ext.Length(); #define HardCodeExtention(Mime, Ext1, Ext2) \ else if (!stricmp(Mime, Mime)) \ { if (Ext1) Ext.Add(Ext1); \ if (Ext2) Ext.Add(Ext2); } if (!Mime); HardCodeExtention("text/calendar", "ics", (const char*)NULL) HardCodeExtention("text/x-vcard", "vcf", (const char*)NULL) HardCodeExtention("text/mbox", "mbx", "mbox"); return Ext.Length() > Start; } LString LCurrentUserName() { struct passwd *pw = getpwuid(geteuid()); if (pw) return pw->pw_name; return ""; } diff --git a/src/mac/cocoa/Menu.mm b/src/mac/cocoa/Menu.mm --- a/src/mac/cocoa/Menu.mm +++ b/src/mac/cocoa/Menu.mm @@ -1,1451 +1,1442 @@ /*hdr ** FILE: GuiMenu.cpp ** AUTHOR: Matthew Allen ** DATE: 18/7/98 ** DESCRIPTION: Gui menu system ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Menu.h" #include "lgi/common/ToolBar.h" #define DEBUG_INFO 0 @interface LNSMenuItem : NSMenuItem { } @property LMenuItem* item; - (id)init:(LMenuItem*)it; - (void)activate; - (BOOL)worksWhenModal; @end @implementation LNSMenuItem - (id)init:(LMenuItem*)it { if ((self = [super init]) != nil) { self.item = it; [self setTarget:self]; self.action = @selector(activate); } return self; } - (BOOL)worksWhenModal { return YES; } - (void)activate { printf("activate\n"); self.item->OnActivate(self.item); } @end struct LShortcut { NSString *Str; public: NSString *Key; NSEventModifierFlags Mod; LShortcut(const char *s) { Key = @""; Str = nil; Mod = 0; auto Keys = LString(s).SplitDelimit("+-"); if (Keys.Length() <= 0) return; for (auto k: Keys) { if (stricmp(k, "CtrlCmd") == 0 || stricmp(k, "AltCmd") == 0 || stricmp(k, "Cmd") == 0 || stricmp(k, "Command") == 0) { Mod |= NSEventModifierFlagCommand; } else if (stricmp(k, "Ctrl") == 0 || stricmp(k, "Control") == 0) { Mod |= NSEventModifierFlagControl; } else if (stricmp(k, "Alt") == 0 || stricmp(k, "Option") == 0) { Mod |= NSEventModifierFlagOption; } else if (stricmp(k, "Shift") == 0) { Mod |= NSEventModifierFlagShift; } else if (stricmp(k, "Del") == 0 || stricmp(k, "Delete") == 0) { unichar s[] = {NSDeleteCharacter}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (stricmp(k, "Ins") == 0 || stricmp(k, "Insert") == 0) { unichar s[] = {NSInsertFunctionKey}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (stricmp(k, "Home") == 0) { unichar s[] = {NSHomeFunctionKey}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (stricmp(k, "End") == 0) { unichar s[] = {NSEndFunctionKey}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (stricmp(k, "PageUp") == 0) { unichar s[] = {NSPageUpFunctionKey}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (stricmp(k, "PageDown") == 0) { unichar s[] = {NSPageDownFunctionKey}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (stricmp(k, "Backspace") == 0) { unichar s[] = {NSBackspaceCharacter}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (stricmp(k, "Space") == 0) { Key = @" "; } else if (k[0] == 'F' && isdigit(k[1])) { int64 index = k.Strip("F").Int(); unichar s[] = {(unichar)(NSF1FunctionKey + index - 1)}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else if (isalpha(k[0])) { Key = Str = LString(k).Lower().NsStr(); } else if (isdigit(k[0])) { Key = Str = LString(k).NsStr(); } else if (strchr(",.", k(0))) { unichar s[] = {(unichar)k(0)}; Key = Str = [[NSString alloc] initWithCharacters:s length:1]; } else { printf("%s:%i - Unhandled shortcut token '%s'\n", _FL, k.Get()); } } } ~LShortcut() { if (Str) [Str release]; } }; /////////////////////////////////////////////////////////////////////////////////////////////// LSubMenu::LSubMenu(const char *name, bool Popup) { Menu = 0; Parent = 0; Info = NULL; LBase::Name(name); Info.p = [[NSMenu alloc] init]; [Info.p setAutoenablesItems:NO]; } LSubMenu::~LSubMenu() { while (Items.Length()) { LMenuItem *i = Items[0]; if (i->Parent != this) { i->Parent = NULL; Items.Delete(i); } delete i; } if (Info) { [Info.p release]; Info = NULL; } } void LSubMenu::OnAttach(bool Attach) { for (auto i: Items) { i->OnAttach(Attach); } if (Attach && this != Menu && Parent && Parent->Parent) { } } size_t LSubMenu::Length() { return Items.Length(); } LMenuItem *LSubMenu::ItemAt(int Id) { return Items.ItemAt(Id); } LMenuItem *LSubMenu::AppendItem(const char *Str, int Id, bool Enabled, int Where, const char *Shortcut) { LMenuItem *i = new LMenuItem(Menu, this, Str, Id, Where, Shortcut); if (!i || !Info) return NULL; Items.Insert(i, Where); auto Index = Items.IndexOf(i); // auto Max = Info.p.numberOfItems; LString s(i->LBase::Name()); auto name = s.NsStr(); if (!name) { delete i; return NULL; } LShortcut sc(Shortcut); i->Info.p = [[LNSMenuItem alloc] init:i]; if (!i->Info) { Items.Delete(i); delete i; return NULL; } [i->Info.p setTitle:name]; i->Info.p.keyEquivalent = sc.Key; i->Info.p.keyEquivalentModifierMask = sc.Mod; i->Id(Id); i->Enabled(Enabled); [Info.p insertItem:i->Info atIndex:Index]; return i; } LMenuItem *LSubMenu::AppendSeparator(int Where) { LMenuItem *i = new LMenuItem; if (i) { i->Parent = this; i->Menu = Menu; i->Id(-2); Items.Insert(i, Where); if (Info) { auto Index = Items.IndexOf(i); // auto Max = Info.p.numberOfItems; // printf("Adding ----- @ %i, %i\n", (int)Index, (int)Max); i->Info = [NSMenuItem separatorItem]; [Info.p insertItem:i->Info atIndex:Index]; } else { printf("%s:%i - No menu to attach item to.\n", _FL); } return i; } return 0; } LSubMenu *LSubMenu::AppendSub(const char *Str, int Where) { LMenuItem *i = new LMenuItem; if (i && Str) { i->Parent = this; i->Menu = Menu; i->Id(-1); Items.Insert(i, Where); if (Info) { i->Child = new LSubMenu(Str); if (i->Child) { i->Child->Parent = i; i->Child->Menu = Menu; i->Child->Window = Window; i->Info.p = [[NSMenuItem alloc] init]; LAssert(i->Info); i->Name(Str); LString s(i->LBase::Name()); [i->Child->Info.p setTitle:s.NsStr()]; [i->Info.p setSubmenu:i->Child->Info.p]; auto Index = Items.IndexOf(i); auto IsMenu = dynamic_cast(this); auto Offset = IsMenu && Where >= 0 ? 1 : 0; // Adjust for 'Root' element "app menu" [Info.p insertItem:i->Info atIndex:Index + Offset]; } } else { printf("%s:%i - No menu to attach item to.\n", __FILE__, __LINE__); } return i->Child; } return 0; } void LSubMenu::Empty() { while (Items[0]) { if (!RemoveItem(Items[0])) break; // Otherwise we'll get an infinite loop. } } bool LSubMenu::RemoveItem(int i) { LMenuItem *Item = Items[i]; if (Item) { return Item->Remove(); } return false; } bool LSubMenu::RemoveItem(LMenuItem *Item) { if (Item && Items.HasItem(Item)) { return Item->Remove(); } return false; } bool LSubMenu::OnKey(LKey &k) { return false; } void LSubMenu::OnActivate(LMenuItem *item) { if (!item) return; if (FloatResult) *FloatResult = item->Id(); else if (Parent) Parent->OnActivate(item); else LAssert(!"Should have a float result OR a parent.."); } int LSubMenu::Float(LView *From, int x, int y, int Btns) { LPoint p(x, y); OsView v = nil; if (From) From->Capture(false); auto w = From ? From->GetWindow() : NULL; if (w) { v = w->Handle(); w->PointToView(p); p = w->Flip(p); } FloatResult.Reset(new int(0)); // auto item = Items[0]; // auto menuitem = item->Info.p; // auto en = menuitem.enabled; NSPoint loc = {(double)p.x, (double)p.y}; [Info.p popUpMenuPositioningItem:nil atLocation:loc inView:v]; return FloatResult ? *FloatResult : 0; } LSubMenu *LSubMenu::FindSubMenu(int Id) { for (auto i: Items) { LSubMenu *Sub = i->Sub(); if (i->Id() == Id) { return Sub; } else if (Sub) { LSubMenu *m = Sub->FindSubMenu(Id); if (m) { return m; } } } return 0; } LMenuItem *LSubMenu::FindItem(int Id) { for (auto i: Items) { LSubMenu *Sub = i->Sub(); if (i->Id() == Id) { return i; } else if (Sub) { i = Sub->FindItem(Id); if (i) { return i; } } } return 0; } /////////////////////////////////////////////////////////////////////////////////////////////// class LMenuItemPrivate { public: LString Shortcut; }; LMenuItem::LMenuItem() { d = new LMenuItemPrivate(); Menu = NULL; Info = NULL; Child = NULL; Parent = NULL; _Icon = -1; _Id = 0; _Flags = 0; } LMenuItem::LMenuItem(LMenu *m, LSubMenu *p, const char *Str, int Id, int Pos, const char *Shortcut) { d = new LMenuItemPrivate(); LBase::Name(Str); Menu = m; Parent = p; Info = NULL; Child = NULL; _Icon = -1; _Id = Id; _Flags = 0; d->Shortcut = Shortcut; Name(Str); ScanForAccel(); } LMenuItem::~LMenuItem() { if (Parent) { Parent->Items.Delete(this); Parent = NULL; } DeleteObj(Child); DeleteObj(d); } void LMenuItem::OnActivate(LMenuItem *item) { if (Parent) Parent->OnActivate(item); else LAssert(!"Should have a parent."); } void LMenuItem::OnAttach(bool Attach) { if (Attach) { if (_Icon >= 0) { Icon(_Icon); } if (Sub()) { Sub()->OnAttach(Attach); } } } // the following 3 functions paint the menus according the to // windows standard. but also allow for correct drawing of menuitem // icons. some implementations of windows force the program back // to the 8-bit palette when specifying the icon graphic, thus removing // control over the colours displayed. these functions remove that // limitation and also provide the application the ability to override // the default painting behaviour if desired. void LMenuItem::_Measure(LPoint &Size) { auto Font = Menu && Menu->GetFont() ? Menu->GetFont() : LSysFont; bool BaseMenu = Parent == Menu; // true if attached to a windows menu // else is a submenu int Ht = Font->GetHeight(); // int IconX = BaseMenu ? ((24-Ht)/2)-Ht : 20; int IconX = BaseMenu ? 2 : 16; if (Separator()) { Size.x = 8; Size.y = 8; } else { // remove '&' chars for string measurement char Str[256]; const char *n = Name(), *i = n; char *o = Str; while (i && *i) { if (*i == '&') { if (i[1] == '&') { *o++ = *i++; } } else { *o++ = *i; } i++; } *o++ = 0; // check for accelerators char *Tab = strchr(Str, '\t'); if (Tab) { // string with accel int Mx, Tx; LDisplayString ds(Font, Str, Tab-Str); Mx = ds.X(); LDisplayString ds2(Font, Tab + 1); Tx = ds2.X(); Size.x = IconX + 32 + Mx + Tx; } else { // normal string LDisplayString ds(Font, Str); Size.x = IconX + ds.X() + 4; } if (!BaseMenu) { // leave room for child pointer Size.x += Child ? 8 : 0; } Size.y = MAX(IconX, Ht+2); } } #define Time(a, b) ((double)(b - a) / 1000) void LMenuItem::_PaintText(LSurface *pDC, int x, int y, int Width) { auto n = Name(); if (n) { auto Font = Menu && Menu->GetFont() ? Menu->GetFont() : LSysFont; bool Underline = false; const char *e = 0; for (auto s=n; s && *s; s = *e ? e : 0) { switch (*s) { case '&': { if (s[1] == '&') { e = s + 2; LDisplayString d(Font, "&"); d.Draw(pDC, x, y, 0); x += d.X(); } else { Underline = true; e = s + 1; } break; } case '\t': { LDisplayString ds(Font, e + 1); x = Width - ds.X() - 8; e = s + 1; break; } default: { if (Underline) { LgiNextUtf8(e); } else { for (e = s; *e; e++) { if (*e == '\t') break; if (*e == '&') break; } } ptrdiff_t Len = e - s; if (Len > 0) { // paint text till that point LDisplayString d(Font, s, Len); d.Draw(pDC, x, y, 0); if (Underline) { LDisplayString ds(Font, s, 1); int UnderX = ds.X(); int Ascent = (int)ceil(Font->Ascent()); pDC->Colour(Font->Fore()); pDC->Line(x, y+Ascent+1, x+MAX(UnderX-2, 1), y+Ascent+1); Underline = false; } x += d.X(); } break; } } } } } void LMenuItem::_Paint(LSurface *pDC, int Flags) { bool BaseMenu = Parent == Menu; int IconX = BaseMenu ? 5 : 20; bool Selected = TestFlag(Flags, ODS_SELECTED); bool Disabled = TestFlag(Flags, ODS_DISABLED); bool Checked = TestFlag(Flags, ODS_CHECKED); #if defined(WIN32) || defined(MAC) LRect r(0, 0, pDC->X()-1, pDC->Y()-1); #else LRect r = Info->GetClient(); #endif if (Separator()) { // Paint a separator int Cy = r.Y() / 2; pDC->Colour(L_MED); pDC->Rectangle(); pDC->Colour(L_LOW); pDC->Line(0, Cy-1, pDC->X()-1, Cy-1); pDC->Colour(L_LIGHT); pDC->Line(0, Cy, pDC->X()-1, Cy); } else { // Paint a text menu item LColour Fore(L_TEXT); LColour Back(Selected ? L_HIGH : L_MED); int x = IconX; int y = 1; // For a submenu pDC->Colour(Back); pDC->Rectangle(); // Draw the text on top LFont *Font = Menu && Menu->GetFont() ? Menu->GetFont() : LSysFont; Font->Transparent(true); if (Disabled) { // Disabled text if (!Selected) { Font->Colour(L_LIGHT); _PaintText(pDC, x+1, y+1, r.X()); } // Else selected... don't draw the hilight // "greyed" text... Font->Colour(L_LOW); _PaintText(pDC, x, y, r.X()); } else { // Normal coloured text Font->Fore(Fore); _PaintText(pDC, x, y, r.X()); } auto ImgLst = (Menu && Menu->GetImageList()) ? Menu->GetImageList() : Parent ? Parent->GetImageList() : 0; // Draw icon/check mark if (Checked && IconX > 0) { // it's a check! int x = 4; int y = 6; pDC->Colour(Fore); pDC->Line(x, y, x+2, y+2); pDC->Line(x+2, y+2, x+6, y-2); y++; pDC->Line(x, y, x+2, y+2); pDC->Line(x+2, y+2, x+6, y-2); y++; pDC->Line(x, y, x+2, y+2); pDC->Line(x+2, y+2, x+6, y-2); } else if (ImgLst && _Icon >= 0) { // it's an icon! LColour Bk(L_MED); ImgLst->Draw(pDC, 0, 0, _Icon, Bk); } // Sub menu arrow if (Child && !dynamic_cast(Parent)) { pDC->Colour(L_TEXT); int x = r.x2 - 4; int y = r.y1 + (r.Y()/2); for (int i=0; i<4; i++) { pDC->Line(x, y-i, x, y+i); x--; } } } } bool LMenuItem::ScanForAccel() { if (!d->Shortcut) return false; // printf("d->Shortcut=%s\n", d->Shortcut.Get()); auto Keys = d->Shortcut.SplitDelimit("+-"); if (Keys.Length() > 0) { int Flags = 0; int Vkey = 0; int Chr = 0; for (int i=0; iShortcut.Get()); } if (Vkey || Chr) { if ( ( (Flags & LGI_EF_ALT) != 0 && (Flags & LGI_EF_SYSTEM) == 0 ) || Vkey == LK_BACKSPACE ) { auto Ident = Id(); LAssert(Ident > 0); Menu->Accel.Insert( new LAccelerator(Flags, Vkey, Chr, Ident) ); } } else { printf("%s:%i - Accel scan failed, str='%s'\n", _FL, d->Shortcut.Get()); return false; } } return true; } LSubMenu *LMenuItem::GetParent() { return Parent; } bool LMenuItem::Remove() { if (!Parent) return false; if (Parent->Info && Info) { [Parent->Info.p removeItem:Info]; Parent->Items.Delete(this); Info = NULL; } else { Parent->Items.Delete(this); } return true; } void LMenuItem::Id(int i) { _Id = i; if (Parent && Parent->Info && Info) { #if LGI_COCOA #else SetMenuItemCommandID(Parent->Info, Info, _Id); #endif } } void LMenuItem::Separator(bool s) { if (s) { _Id = -2; } if (Parent) { #if LGI_COCOA #else if (s) ChangeMenuItemAttributes(Parent->Info, Info, kMenuItemAttrSeparator, 0); else ChangeMenuItemAttributes(Parent->Info, Info, 0, kMenuItemAttrSeparator); #endif } } void LMenuItem::Checked(bool c) { if (c) SetFlag(_Flags, ODS_CHECKED); else ClearFlag(_Flags, ODS_CHECKED); if (Info) [Info.p setState: c ? NSControlStateValueOn : NSControlStateValueOff]; } bool LMenuItem::Name(const char *n) { char *Tmp = NewStr(n); if (Tmp) { char *in = Tmp, *out = Tmp; while (*in) { if (*in != '&') *out++ = *in; in++; } *out++ = 0; } bool Status = LBase::Name(Tmp); if (Status && Info) { LString s(Tmp); [Info.p setTitle:s.NsStr()]; } DeleteArray(Tmp); return Status; } void LMenuItem::Enabled(bool e) { #if 1 if (Info && Info.p.enabled ^ e) Info.p.enabled = e; #endif } void LMenuItem::Focus(bool f) { } void LMenuItem::Sub(LSubMenu *s) { Child = s; } void LMenuItem::Icon(int i) { _Icon = i; auto Lst = Menu ? Menu->GetImageList() : Parent->GetImageList(); if (!Lst || !Info) return; if (_Icon < 0 || _Icon >= Lst->GetItems()) return; auto r = Lst->GetIconRect(_Icon); [Info.p setImage: Lst->NsImage(&r)]; } void LMenuItem::Visible(bool i) { } int LMenuItem::Id() { return _Id; } const char *LMenuItem::Name() { return LBase::Name(); } bool LMenuItem::Separator() { return _Id == -2; } bool LMenuItem::Checked() { return TestFlag(_Flags, ODS_CHECKED); } bool LMenuItem::Enabled() { if (Parent) { #if LGI_COCOA #else return IsMenuItemEnabled(Parent->Info, Info); #endif } return true; } bool LMenuItem::Visible() { return true; } bool LMenuItem::Focus() { return 0; } LSubMenu *LMenuItem::Sub() { return Child; } int LMenuItem::Icon() { return _Icon; } /////////////////////////////////////////////////////////////////////////////////////////////// class LMenuPrivate { public: int PrefId, AboutId; LMenuItem *PrefItem = NULL; LMenuItem *AboutItem = NULL; LMenuPrivate() { PrefId = AboutId = 0; } }; LMenu::LMenu(const char *AppName) : LSubMenu("", false) { Menu = this; d = new LMenuPrivate; auto s = AppendSub("Root"); if (s) { d->AboutItem = s->AppendItem("About", M_ABOUT); s->AppendSeparator(); d->PrefItem = s->AppendItem("Preferences", M_PERFERENCES, true, -1, "Cmd+,"); s->AppendItem("Hide", M_HIDE, true, -1, "Cmd+H"); s->AppendSeparator(); s->AppendItem("Quit", M_QUIT, true, -1, "Cmd+Q"); } } LMenu::~LMenu() { Accel.DeleteObjects(); DeleteObj(d); } void LMenu::OnActivate(LMenuItem *item) { if (!item) { LAssert(0); return; } switch (item->Id()) { case M_ABOUT: if (Window && d->AboutId) Window->PostEvent(M_COMMAND, d->AboutId); break; case M_PERFERENCES: if (Window && d->PrefId) Window->PostEvent(M_COMMAND, d->PrefId); break; case M_HIDE: [[NSApplication sharedApplication] hide:Info]; break; case M_QUIT: LCloseApp(); break; default: if (Window) { printf("%s:%i - post M_COMMAND\n", _FL); Window->PostEvent(M_COMMAND, item->Id()); } break; } } bool LMenu::SetPrefAndAboutItems(int PrefId, int AboutId) { d->PrefId = PrefId; if (d->PrefItem) d->PrefItem->Enabled(d->PrefId > 0); d->AboutId = AboutId; if (d->AboutItem) d->AboutItem->Enabled(d->AboutId > 0); return true; } struct LMenuFont { LFont *f; LMenuFont() { f = NULL; } ~LMenuFont() { DeleteObj(f); } } MenuFont; LFont *LMenu::GetFont() { if (!MenuFont.f) { LFontType Type; if (Type.GetSystemFont("Menu")) { MenuFont.f = Type.Create(); if (MenuFont.f) { #ifndef MAC _Font->CodePage(SysFont->CodePage()); #endif } else { printf("LMenu::GetFont Couldn't create menu font.\n"); } } else { printf("LMenu::GetFont Couldn't get menu typeface.\n"); } if (!MenuFont.f) { MenuFont.f = new LFont; if (MenuFont.f) *MenuFont.f = *LSysFont; } } return MenuFont.f ? MenuFont.f : LSysFont; } bool LMenu::Attach(LViewI *p) { bool Status = false; auto w = dynamic_cast(p); if (w) { Window = p; [NSApplication sharedApplication].mainMenu = Info; if (Info) { OnAttach(true); Status = true; } else { printf("%s:%i - No menu\n", _FL); } } return Status; } bool LMenu::Detach() { bool Status = false; return Status; } bool LMenu::OnKey(LView *v, LKey &k) { if (k.Down()) { k.Trace("MenuKey"); printf("Accel.len=%i\n", (int)Accel.Length()); for (auto a: Accel) { if (a->Match(k)) { Window->OnCommand(a->GetId(), 0, NULL); return true; } } if (k.Alt() && !dynamic_cast(v) && !dynamic_cast(v)) { bool Hide = false; for (auto s: Items) { if (!s->Separator()) { if (Hide) { // s->Info->HideSub(); } else { auto n = s->Name(); if (ValidStr(n)) { char *Amp = strchr(n, '&'); while (Amp && Amp[1] == '&') { Amp = strchr(Amp + 2, '&'); } if (Amp) { char Accel = tolower(Amp[1]); char Press = tolower(k.c16); if (Accel == Press) { Hide = true; } } } if (Hide) { // s->Info->ShowSub(); } else { // s->Info->HideSub(); } } } } if (Hide) { return true; } } } return false; } //////////////////////////////////////////////////////////////////////////// LAccelerator::LAccelerator(int flags, int vkey, int chr, int id) { Flags = flags; Vkey = vkey; Chr = chr; Id = id; } bool LAccelerator::Match(LKey &k) { int Press = (uint) k.c16; auto Up = toupper(Press); bool Match = false; #if 1 printf("LAccelerator::Match %i(%c)%s%s%s = %i(%c)%s%s%s\n", Up, Up>=' '?Up:'.', k.Ctrl()?" ctrl":"", k.Alt()?" alt":"", k.Shift()?" shift":"", Chr, Chr>=' '?Chr:'.', TestFlag(Flags, LGI_EF_CTRL)?" ctrl":"", TestFlag(Flags, LGI_EF_ALT)?" alt":"", TestFlag(Flags, LGI_EF_SHIFT)?" shift":"" ); #endif if (k.Alt() && !k.System() && !k.Ctrl()) { switch (k.vkey) { #define _(k) case LK_##k: \ Match = Vkey == #k[0]; break; _(A) _(B) _(C) _(D) _(E) _(F) _(G) _(H) _(I) _(J) _(K) _(L) _(M) _(N) _(O) _(P) _(Q) _(R) _(S) _(T) _(U) _(V) _(W) _(X) _(Y) _(Z) default: printf("%s:%i - No case for '%i'\n", _FL, k.vkey); break; } } else if (Vkey) { Match = k.vkey == Vkey; } else if (Chr) { Match = Up == (uint)Chr; } if (Match) { if ( ((TestFlag(Flags, LGI_EF_CTRL) ^ k.Ctrl()) == 0) && ((TestFlag(Flags, LGI_EF_ALT) ^ k.Alt()) == 0) && ((TestFlag(Flags, LGI_EF_SHIFT) ^ k.Shift()) == 0) ) { return true; } } return false; } //////////////////////////////////////////////////////////////////////////// LCommand::LCommand() { - Flags = GWF_VISIBLE; - Id = 0; - ToolButton = 0; - MenuItem = 0; - Accelerator = 0; - TipHelp = 0; - PrevValue = false; } LCommand::~LCommand() { - DeleteArray(Accelerator); - DeleteArray(TipHelp); } bool LCommand::Enabled() { if (ToolButton) return ToolButton->Enabled(); if (MenuItem) return MenuItem->Enabled(); return false; } void LCommand::Enabled(bool e) { if (ToolButton) { ToolButton->Enabled(e); } if (MenuItem) { MenuItem->Enabled(e); } } bool LCommand::Value() { bool HasChanged = false; if (ToolButton) { HasChanged |= (ToolButton->Value() != 0) ^ PrevValue; } if (MenuItem) { HasChanged |= (MenuItem->Checked() != 0) ^ PrevValue; } if (HasChanged) { Value(!PrevValue); } return PrevValue; } void LCommand::Value(bool v) { if (ToolButton) { ToolButton->Value(v); } if (MenuItem) { MenuItem->Checked(v); } PrevValue = v; } diff --git a/src/mac/cocoa/Printer.mm b/src/mac/cocoa/Printer.mm --- a/src/mac/cocoa/Printer.mm +++ b/src/mac/cocoa/Printer.mm @@ -1,175 +1,187 @@ #include "lgi/common/Lgi.h" #include "lgi/common/List.h" #include "lgi/common/Button.h" #include "lgi/common/Printer.h" //////////////////////////////////////////////////////////////////// class LPrinterPrivate { public: LString Printer; LString Err; LPrinterPrivate() { } ~LPrinterPrivate() { } }; //////////////////////////////////////////////////////////////////// LPrinter::LPrinter() { d = new LPrinterPrivate; } LPrinter::~LPrinter() { DeleteObj(d); } bool LPrinter::Browse(LView *Parent) { return false; } bool LPrinter::Serialize(LString &Str, bool Write) { if (Write) { Str = d->Printer; } else { d->Printer = Str; } return true; } LString LPrinter::GetErrorMsg() { return d->Err; } #define ErrCheck(fn) \ if (e != noErr) \ { \ d->Err.Printf("%s:%i - %s failed with %i\n", _FL, fn, e); \ LgiTrace(d->Err); \ goto OnError; \ } +void LPrinter::Print(LPrintEvents *Events, + std::function callback, + const char *PrintJobName, + int MaxPages, + LView *Parent) +{ + int Status = LPrintEvents::OnBeginPrintError; -int LPrinter::Print(LPrintEvents *Events, const char *PrintJobName, int MaxPages, LView *Parent) -{ if (!Events) { LAssert(0); - return false; + if (callback) + callback(Status); + return; } - - - bool Status = false; + + #if LGI_COCOA + + #warning "No Cocoa Printing Impl." + + #elif LGI_CARBON // Carbon printing code? + + PMPrintSession ps = NULL; + PMPageFormat PageFmt = NULL; + PMPrintSettings PrintSettings = NULL; + auto Wnd = Parent ? Parent->GetWindow() : NULL; + Boolean Accepted = false; + Boolean Changed = false; + LAutoPtr dc; + int Pages; + LPrintDcParams Params; + double paperWidth, paperHeight; + PMPaper Paper = NULL; + UInt32 ResCount; + PMPrinter CurrentPrinter = NULL; + + OSStatus e = PMCreateSession(&ps); + ErrCheck("PMCreateSession"); + + e = PMCreatePageFormat(&PageFmt); + ErrCheck("PMCreatePageFormat"); + + e = PMSessionDefaultPageFormat(ps, PageFmt); + ErrCheck("PMSessionDefaultPageFormat"); + + e = PMCreatePrintSettings(&PrintSettings); + ErrCheck("PMCreatePrintSettings"); + #if 0 - PMPrintSession ps = NULL; - PMPageFormat PageFmt = NULL; - PMPrintSettings PrintSettings = NULL; - GWindow *Wnd = Parent ? Parent->GetWindow() : NULL; - Boolean Accepted = false; - Boolean Changed = false; - GAutoPtr dc; - int Pages; - GPrintDcParams Params; - double paperWidth, paperHeight; - PMPaper Paper = NULL; - UInt32 ResCount; - PMPrinter CurrentPrinter = NULL; - - OSStatus e = PMCreateSession(&ps); - ErrCheck("PMCreateSession"); - - e = PMCreatePageFormat(&PageFmt); - ErrCheck("PMCreatePageFormat"); - - e = PMSessionDefaultPageFormat(ps, PageFmt); - ErrCheck("PMSessionDefaultPageFormat"); - - e = PMCreatePrintSettings(&PrintSettings); - ErrCheck("PMCreatePrintSettings"); - -#if 0 - e = PMSessionUseSheets(ps, Wnd ? Wnd->WindowHandle() : NULL, NULL /*PMSheetDoneUPP sheetDoneProc*/); - ErrCheck("PMSessionUseSheets"); -#endif - - e = PMSessionPrintDialog(ps, PrintSettings, PageFmt, &Accepted); - ErrCheck("PMSessionPrintDialog"); - - e = PMSessionValidatePrintSettings(ps, PrintSettings, &Changed); - e = PMSessionValidatePageFormat(ps, PageFmt, &Changed); - - e = PMSessionBeginCGDocumentNoDialog(ps, PrintSettings, PageFmt); - ErrCheck("PMSessionBeginCGDocumentNoDialog"); - - e = PMSessionBeginPageNoDialog(ps, PageFmt, NULL); - ErrCheck("PMSessionBeginPageNoDialog"); - - e = PMGetAdjustedPaperRect(PageFmt, &Params.Page); //PMGetUnadjustedPageRect - ErrCheck("PMGetAdjustedPaperRect"); - - e = PMSessionGetCGGraphicsContext(ps, &Params.Ctx); - ErrCheck("PMSessionGetCGGraphicsContext"); - - e = PMSessionGetCurrentPrinter(ps, &CurrentPrinter); - ErrCheck("PMSessionGetCurrentPrinter"); - - e = PMGetPageFormatPaper(PageFmt, &Paper); - e = PMPaperGetWidth(Paper, &paperWidth); - e = PMPaperGetHeight(Paper, &paperHeight); - - e = PMPrinterGetPrinterResolutionCount(CurrentPrinter, &ResCount); - ErrCheck("PMPrinterGetPrinterResolutionCount"); - - for (unsigned i=0; iOnBeginPrint(dc); - for (int Page = 0; Page < Pages; Page++) - { - if (Page > 0) + e = PMSessionUseSheets(ps, Wnd ? Wnd->WindowHandle() : NULL, NULL /*PMSheetDoneUPP sheetDoneProc*/); + ErrCheck("PMSessionUseSheets"); + #endif + + e = PMSessionPrintDialog(ps, PrintSettings, PageFmt, &Accepted); + ErrCheck("PMSessionPrintDialog"); + + e = PMSessionValidatePrintSettings(ps, PrintSettings, &Changed); + e = PMSessionValidatePageFormat(ps, PageFmt, &Changed); + + e = PMSessionBeginCGDocumentNoDialog(ps, PrintSettings, PageFmt); + ErrCheck("PMSessionBeginCGDocumentNoDialog"); + + e = PMSessionBeginPageNoDialog(ps, PageFmt, NULL); + ErrCheck("PMSessionBeginPageNoDialog"); + + e = PMGetAdjustedPaperRect(PageFmt, &Params.Page); //PMGetUnadjustedPageRect + ErrCheck("PMGetAdjustedPaperRect"); + + e = PMSessionGetCGGraphicsContext(ps, &Params.Ctx); + ErrCheck("PMSessionGetCGGraphicsContext"); + + e = PMSessionGetCurrentPrinter(ps, &CurrentPrinter); + ErrCheck("PMSessionGetCurrentPrinter"); + + e = PMGetPageFormatPaper(PageFmt, &Paper); + e = PMPaperGetWidth(Paper, &paperWidth); + e = PMPaperGetHeight(Paper, &paperHeight); + + e = PMPrinterGetPrinterResolutionCount(CurrentPrinter, &ResCount); + ErrCheck("PMPrinterGetPrinterResolutionCount"); + + for (unsigned i=0; iOnPrintPage(dc, Page); - PMSessionEndPage(ps); - } - - e = PMSessionEndDocumentNoDialog(ps); - ErrCheck("PMSessionEndDocumentNoDialog"); - - return Status; - -OnError: - PMRelease(PrintSettings); - PMRelease(ps); + e = PMPrinterSetOutputResolution(CurrentPrinter, PrintSettings, &Params.Dpi); + ErrCheck("PMPrinterSetOutputResolution"); + + dc.Reset(new GPrintDC(&Params, PrintJobName)); + Pages = Events->OnBeginPrint(dc); + for (int Page = 0; Page < Pages; Page++) + { + if (Page > 0) + { + e = PMSessionBeginPage(ps, PageFmt, NULL); + ErrCheck("PMSessionBeginPage"); + + e = PMSessionGetCGGraphicsContext(ps, &Params.Ctx); + ErrCheck("PMSessionGetCGGraphicsContext"); + + dc.Reset(new GPrintDC(&Params, PrintJobName)); + } + + Status |= Events->OnPrintPage(dc, Page); + PMSessionEndPage(ps); + } + + e = PMSessionEndDocumentNoDialog(ps); + ErrCheck("PMSessionEndDocumentNoDialog"); + + return Status; + + OnError: + PMRelease(PrintSettings); + PMRelease(ps); + #endif - return Status; + if (callback) + callback(Status); } diff --git a/src/mac/cocoa/Widgets.mm b/src/mac/cocoa/Widgets.mm --- a/src/mac/cocoa/Widgets.mm +++ b/src/mac/cocoa/Widgets.mm @@ -1,249 +1,259 @@ /*hdr ** FILE: GWidgets.cpp ** AUTHOR: Matthew Allen ** DATE: 30/12/2006 ** DESCRIPTION: Mac dialog components ** ** Copyright (C) 2006 Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Slider.h" #include "lgi/common/Bitmap.h" #include "lgi/common/TableLayout.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Button.h" /////////////////////////////////////////////////////////////////////////////////////////// #define GreyBackground() struct LDialogPriv { bool IsModal; int ModalStatus; int BtnId; LDialogPriv() { IsModal = false; ModalStatus = -1; BtnId = -1; } }; /////////////////////////////////////////////////////////////////////////////////////////// -LDialog::LDialog() +LDialog::LDialog(LViewI *Parent) : ResObject(Res_Dialog) { d = new LDialogPriv; Name("Dialog"); SetDeleteOnClose(false); + + if (Parent) + SetParent(Parent); } LDialog::~LDialog() { DeleteObj(d); } int LDialog::GetButtonId() { return d->BtnId; } int LDialog::OnNotify(LViewI *Ctrl, LNotification n) { auto b = dynamic_cast(Ctrl); if (b) { d->BtnId = b->GetId(); if (d->IsModal) EndModal(d->BtnId); else EndModeless(d->BtnId); } return 0; } bool LDialog::IsModal() { return d->IsModal; } void LDialog::Quit(bool DontDelete) { if (d->IsModal) EndModal(0); else EndModeless(0); } void LDialog::OnPosChange() { if (Children.Length() == 1) { auto it = Children.begin(); auto t = dynamic_cast((LViewI*)it); if (t) { auto r = GetClient(); r.Inset(LTableLayout::CellSpacing, LTableLayout::CellSpacing); t->SetPos(r); } } } bool LDialog::LoadFromResource(int Resource, char *TagList) { LAutoString n; LRect p; bool Status = LResourceLoad::LoadFromResource(Resource, this, &p, &n, TagList); if (Status) { Name(n); SetPos(p); } return Status; } bool LDialog::OnRequestClose(bool OsClose) { if (d->IsModal) { EndModal(0); return false; } return true; } -int LDialog::DoModal(OsView OverideParent) +void LDialog::DoModal(OnClose Callback, OsView OverideParent) { d->ModalStatus = 0; if (Wnd && Attach(0)) { // LAutoPool Pool; LWindow *Owner = GetParent() ? GetParent()->GetWindow() : 0; if (Owner) { auto Pr = Owner->GetPos(); auto Mr = GetPos(); Mr.Offset( Pr.x1 + (Pr.X() - Mr.X()) / 2 - Mr.x1, Pr.y1 + (Pr.Y() - Mr.Y()) / 2 - Mr.y1); SetPos(Mr); Owner->SetChildDialog(this); } d->IsModal = true; AttachChildren(); Visible(true); auto app = LAppInst->Handle(); auto wnd = WindowHandle(); [app runModalForWindow:wnd]; if (Owner) Owner->SetChildDialog(NULL); LWindow::Visible(false); } - return d->ModalStatus; + if (Callback) + { + Callback(this, d->ModalStatus); + } + else + { + delete this; + } } void LDialog::EndModal(int Code) { if (d->IsModal) { // LAutoPool Pool; d->IsModal = false; d->ModalStatus = Code; NSApplication *app = LAppInst->Handle(); [app stopModal]; } else { LAssert(0); } } int LDialog::DoModeless() { d->IsModal = false; if (Attach(0)) { AttachChildren(); Visible(true); } return 0; } void LDialog::EndModeless(int Code) { LWindow::Quit(Code); } extern LButton *FindDefault(LView *w); LMessage::Result LDialog::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_CLOSE: { printf("M_CLOSE received... Fixme!\n"); break; } } return LView::OnEvent(Msg); } /////////////////////////////////////////////////////////////////////////////////////////// LControl::LControl(OsView view) : LView(view) { Pos.ZOff(10, 10); } LControl::~LControl() { } LMessage::Result LControl::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { } return 0; } LPoint LControl::SizeOfStr(const char *Str) { auto Fnt = GetFont(); int y = Fnt->GetHeight(); LPoint Pt(0, 0); if (Str) { const char *e = 0; for (const char *s = Str; s && *s; s = e?e+1:0) { e = strchr(s, '\n'); size_t Len = e ? e-s : strlen(s); LDisplayString ds(Fnt, s, Len); Pt.x = MAX(Pt.x, ds.X()); Pt.y += y; } } return Pt; } diff --git a/src/mac/cocoa/Window.mm b/src/mac/cocoa/Window.mm --- a/src/mac/cocoa/Window.mm +++ b/src/mac/cocoa/Window.mm @@ -1,1424 +1,1424 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Popup.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Menu.h" #include "LCocoaView.h" extern void NextTabStop(LViewI *v, int dir); extern void SetDefaultFocus(LViewI *v); extern void BuildTabStops(LArray &Stops, LViewI *v); #define DEBUG_KEYS 0 #define DEBUG_SETFOCUS 0 #define DEBUG_LOGGING 0 #if DEBUG_LOGGING #define LOG(...) printf(__VA_ARGS__) #else #define LOG(...) #endif /* Deleting a LWindow senarios: Users clicks close: NSWindowDelegate::windowWillClose GWindowPrivate::OnClose(CloseUser) LNsWindow::onDelete Something deletes the LWindow programmatically: LWindow::~LWindow GWindowPriv::OnClose(CloseDestructor) LNsWindow::onDelete self.close windowWillClose -> block Something calls LWindow::Quit() LNsWindow::onQuit (async) self.close NSWindowDelegate::windowWillClose GWindowPrivate::OnClose(CloseUser) LNsWindow::onDelete */ #if DEBUG_SETFOCUS || DEBUG_KEYS static GString DescribeView(GViewI *v) { if (!v) return GString(); char s[512]; int ch = 0; GArray p; for (GViewI *i = v; i; i = i->GetParent()) { p.Add(i); } for (int n=MIN(3, (int)p.Length()-1); n>=0; n--) { char Buf[256] = ""; if (!stricmp(v->GetClass(), "GMdiChild")) sprintf(Buf, "'%s'", v->Name()); v = p[n]; ch += sprintf_s(s + ch, sizeof(s) - ch, "%s>%s", Buf, v->GetClass()); } return s; } #endif LRect LScreenFlip(LRect r) { LRect screen(0, 0, -1, -1); for (NSScreen *s in [NSScreen screens]) { LRect pos = s.frame; if (r.Overlap(&pos)) { screen = pos; break; } } if (screen.Valid()) { LRect rc = r; rc.Offset(0, (screen.Y() - r.y1 - r.Y()) - r.y1); // printf("%s:%i - Flip %s -> %s (%s)\n", _FL, r.GetStr(), rc.GetStr(), screen.GetStr()); return rc; } else { // printf("%s:%i - No Screen?\n", _FL); r.ZOff(-1, -1); } return r; } /////////////////////////////////////////////////////////////////////// class HookInfo { public: int Flags; LView *Target; }; @interface LWindowDelegate : NSObject { } - (id)init; - (void)dealloc; - (void)windowDidResize:(NSNotification*)aNotification; - (void)windowDidMove:(NSNotification*)aNotification; - (void)windowWillClose:(NSNotification*)aNotification; - (BOOL)windowShouldClose:(id)sender; - (void)windowDidBecomeMain:(NSNotification*)notification; - (void)windowDidResignMain:(NSNotification*)notification; @end LWindowDelegate *Delegate = nil; class LWindowPrivate { public: LWindow *Wnd; LDialog *ChildDlg; LMenu *EmptyMenu; LViewI *Focus; NSView *ContentCache; int Sx, Sy; LKey LastKey; LArray Hooks; uint64 LastMinimize; uint64 LastDragDrop; bool DeleteOnClose; bool SnapToEdge; bool InitVisible; LWindowPrivate(LWindow *wnd) { ContentCache = NULL; Focus = NULL; InitVisible = false; LastMinimize = 0; Wnd = wnd; LastDragDrop = 0; DeleteOnClose = true; ChildDlg = 0; Sx = Sy = -1; SnapToEdge = false; EmptyMenu = 0; } ~LWindowPrivate() { DeleteObj(EmptyMenu); } void OnClose(LCloseContext Ctx) { LOG("GWindowPrivate::OnClose %p/%s\n", Wnd, Wnd?Wnd->GetClass():NULL); auto &osw = Wnd->Wnd; if (!osw) return; LCocoaView *cv = objc_dynamic_cast(LCocoaView, osw.p.contentView); if (cv) cv.w = NULL; LNsWindow *w = objc_dynamic_cast(LNsWindow, osw.p); if (w) [w onDelete:Ctx]; osw.p.delegate = nil; [osw.p autorelease]; osw = nil; if (DeleteOnClose) delete Wnd; } ssize_t GetHookIndex(LView *Target, bool Create = false) { for (int i=0; iTarget = Target; n->Flags = 0; return Hooks.Length() - 1; } } return -1; } void OnResize() { NSWindow *wnd = Wnd->WindowHandle().p; Wnd->Pos = wnd.frame; Wnd->OnPosChange(); wnd.contentView.needsLayout = YES; } }; @implementation LNsWindow - (id)init:(LWindowPrivate*)priv Frame:(NSRect)rc { NSUInteger windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; if ((self = [super initWithContentRect:rc styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:NO ]) != nil) { self.d = priv; self->ReqClose = CSNone; self.contentView = [[LCocoaView alloc] init:priv->Wnd]; [self makeFirstResponder:self.contentView]; self.acceptsMouseMovedEvents = true; self.ignoresMouseEvents = false; // printf("LNsWindow.init\n"); } return self; } - (void)dealloc { if (self.d) self.d->Wnd->OnDealloc(); LCocoaView *cv = objc_dynamic_cast(LCocoaView, self.contentView); cv.w = NULL; [cv release]; self.contentView = NULL; [super dealloc]; // printf("LNsWindow.dealloc.\n"); } - (LWindow*)getWindow { return self.d ? self.d->Wnd : nil; } - (BOOL)canBecomeKeyWindow { return YES; } - (void)onQuit { #if DEBUG_LOGGING LWindow *wnd = self.d ? self.d->Wnd : NULL; auto cls = wnd ? wnd->GetClass() : NULL; #endif LOG("LNsWindow::onQuit %p/%s %i\n", wnd, cls, self->ReqClose); if (self->ReqClose == CSNone) { self->ReqClose = CSInRequest; if (!self.d) LOG("%s:%i - No priv pointer?\n", _FL); if (!self.d || !self.d->Wnd || !self.d->Wnd->OnRequestClose(false)) { LOG(" ::onQuit %p/%s no 'd' or OnReqClose failed\n", wnd, cls); self->ReqClose = CSNone; return; } } else return; LOG(" ::onQuit %p/%s self.close\n", wnd, cls); self->ReqClose = CSClosed; self.d->Wnd->SetPulse(); [self close]; } - (void)onDelete:(LCloseContext)ctx { LOG("LNsWindow::onDelete %p/%s\n", self.d->Wnd, self.d->Wnd->GetClass()); if (ctx == CloseDestructor && self->ReqClose != CSClosed) { // This is called during the ~LWindow destructor to make sure we // closed the window self->ReqClose = CSClosed; LOG(" ::onDelete %p self.close\n", self.d->Wnd); [self close]; } self.d = NULL; } @end @implementation LWindowDelegate - (id)init { if ((self = [super init]) != nil) { } return self; } - (void)dealloc { [super dealloc]; } - (void)windowDidResize:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->OnResize(); } - (void)windowDidMove:(NSNotification*)event { // LNsWindow *w = event.object; // GRect r = LScreenFlip(w.frame); // printf("windowDidMove: %s\n", r.GetStr()); } - (BOOL)windowShouldClose:(NSWindow*)sender { LNsWindow *w = objc_dynamic_cast(LNsWindow, sender); if (w && w.d && w.d->Wnd) return w.d->Wnd->OnRequestClose(false); return YES; } - (void)windowWillClose:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->OnClose(CloseUser); } - (void)windowDidBecomeMain:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->Wnd->OnFrontSwitch(true); } - (void)windowDidResignMain:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->Wnd->OnFrontSwitch(false); } @end /////////////////////////////////////////////////////////////////////// #define GWND_CREATE 0x0010000 #if __has_feature(objc_arc) #error "NO ARC!" #endif LWindow::LWindow(OsWindow wnd) : LView(NULL) { d = new LWindowPrivate(this); _QuitOnClose = false; Wnd = NULL; Menu = 0; _Default = 0; _Window = this; WndFlags |= GWND_CREATE; LView::Visible(false); _Lock = new LMutex("LWindow"); LRect pos(200, 200, 200, 200); NSRect frame = pos; if (wnd) Wnd = wnd; else Wnd.p = [[LNsWindow alloc] init:d Frame:frame]; if (Wnd) { [Wnd.p retain]; if (!Delegate) Delegate = [[LWindowDelegate alloc] init]; //[Wnd.p makeKeyAndOrderFront:NSApp]; Wnd.p.delegate = Delegate; d->ContentCache = Wnd.p.contentView; } } LWindow::~LWindow() { LOG("LWindow::~LWindow %p\n", this); if (LAppInst->AppWnd == this) LAppInst->AppWnd = 0; _Delete(); d->DeleteOnClose = false; // We're already in the destructor, don't redelete. d->OnClose(CloseDestructor); DeleteObj(Menu); DeleteObj(d); DeleteObj(_Lock); } NSView *LWindow::Handle() { if (!InThread()) return d->ContentCache; if (Wnd.p != nil) return Wnd.p.contentView; return NULL; } bool LWindow::SetIcon(const char *FileName) { return false; } LViewI *LWindow::GetFocus() { return d->Focus; } void LWindow::SetFocus(LViewI *ctrl, FocusType type) { const char *TypeName = NULL; switch (type) { case GainFocus: TypeName = "Gain"; break; case LoseFocus: TypeName = "Lose"; break; case ViewDelete: TypeName = "Delete"; break; } switch (type) { case GainFocus: { // Check if the control already has focus if (d->Focus == ctrl) return; if (d->Focus) { LView *v = d->Focus->GetGView(); if (v) v->WndFlags &= ~GWF_FOCUS; d->Focus->OnFocus(false); d->Focus->Invalidate(); #if DEBUG_SETFOCUS auto _foc = DescribeView(d->Focus); LgiTrace(".....defocus: %s\n", _foc.Get()); #endif } d->Focus = ctrl; if (d->Focus) { LView *v = d->Focus->GetGView(); if (v) v->WndFlags |= GWF_FOCUS; d->Focus->OnFocus(true); d->Focus->Invalidate(); #if DEBUG_SETFOCUS auto _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) focusing\n", _set.Get(), TypeName); #endif } break; } case LoseFocus: { if (ctrl == d->Focus) { LView *v = d->Focus->GetGView(); if (v) { if (v->WndFlags & GWF_FOCUS) { // View thinks it has focus v->WndFlags &= ~GWF_FOCUS; d->Focus->OnFocus(false); // keep d->Focus pointer, as we want to be able to re-focus the child // view when we get focus again #if DEBUG_SETFOCUS auto _ctrl = DescribeView(ctrl); auto _foc = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) keep_focus: %s\n", _ctrl.Get(), TypeName, _foc.Get()); #endif } // else view doesn't think it has focus anyway... } else { // Non GView handler d->Focus->OnFocus(false); d->Focus->Invalidate(); d->Focus = NULL; } } else { /* LgiTrace("LWindow::SetFocus(%p.%s, %s) error on losefocus: %p(%s)\n", ctrl, ctrl ? ctrl->GetClass() : NULL, TypeName, d->Focus, d->Focus ? d->Focus->GetClass() : NULL); */ } break; } case ViewDelete: { if (ctrl == d->Focus) { #if DEBUG_SETFOCUS LgiTrace("LWindow::SetFocus(%p.%s, %s) delete_focus: %p(%s)\n", ctrl, ctrl ? ctrl->GetClass() : NULL, TypeName, d->Focus, d->Focus ? d->Focus->GetClass() : NULL); #endif d->Focus = NULL; } break; } } } void LWindow::SetDragHandlers(bool On) { #if 0 if (Wnd && _View) SetAutomaticControlDragTrackingEnabledForWindow(Wnd, On); #endif } void LWindow::Quit(bool DontDelete) { // LAutoPool Pool; if (_QuitOnClose) { _QuitOnClose = false; LCloseApp(); } if (Wnd) SetDragHandlers(false); if (d && DontDelete) { // If DontDelete is true, we should be already in the destructor of the LWindow. // Which means we DON'T call onQuit, as it's too late to ask the user if they don't // want to close the window. The window IS closed come what may, and the object is // going away. Futhermore we can't access the window's memory after it's deleted and // that may happen if the onQuit is processed after ~LWindow. d->DeleteOnClose = false; if (Wnd) [Wnd.p close]; } else if (Wnd) { [Wnd.p performSelectorOnMainThread:@selector(onQuit) withObject:nil waitUntilDone:false]; } } void LWindow::SetChildDialog(LDialog *Dlg) { d->ChildDlg = Dlg; } bool LWindow::GetSnapToEdge() { return d->SnapToEdge; } void LWindow::SetSnapToEdge(bool s) { d->SnapToEdge = s; } void LWindow::OnFrontSwitch(bool b) { if (b && Menu) { [NSApplication sharedApplication].mainMenu = Menu->Handle().p; } else { auto m = LAppInst->Default.Get(); [NSApplication sharedApplication].mainMenu = m ? m->Handle() : nil; } // printf("%s:%i - menu for %s is %p\n", _FL, Name(), [NSApplication sharedApplication].mainMenu); } bool LWindow::Visible() { // LAutoPool Pool; if (!Wnd) return false; return [Wnd.p isVisible]; } void LWindow::Visible(bool i) { // LAutoPool Pool; if (!Wnd) return; if (i) { d->InitVisible = true; PourAll(); [Wnd.p makeKeyAndOrderFront:NULL]; [NSApp activateIgnoringOtherApps:YES]; SetDefaultFocus(this); OnPosChange(); } else { [Wnd.p orderOut:Wnd.p]; } } bool LWindow::IsActive() { return Wnd ? [Wnd.p isKeyWindow] : false; } bool LWindow::SetActive() { [[NSApplication sharedApplication] activateIgnoringOtherApps : YES]; return false; } void LWindow::SetDeleteOnClose(bool i) { d->DeleteOnClose = i; } void LWindow::SetAlwaysOnTop(bool b) { } -bool LWindow::PostEvent(int Event, LMessage::Param a, LMessage::Param b) +bool LWindow::PostEvent(int Event, LMessage::Param a, LMessage::Param b, int64_t TimeoutMs) { return LAppInst->PostEvent(this, Event, a, b); } bool LWindow::Attach(LViewI *p) { bool Status = false; if (Wnd) { if (LBase::Name()) Name(LBase::Name()); Status = true; // Setup default button... if (!_Default) { _Default = FindControl(IDOK); if (_Default) _Default->Invalidate(); } OnCreate(); OnAttach(); OnPosChange(); // Set the first control as the focus... NextTabStop(this, 0); } return Status; } bool LWindow::OnRequestClose(bool OsShuttingDown) { if (GetQuitOnClose()) { LCloseApp(); } return LView::OnRequestClose(OsShuttingDown); } bool LWindow::HandleViewMouse(LView *v, LMouse &m) { if (m.Down()) { bool ParentPopup = false; LViewI *p = m.Target; while (p && p->GetParent()) { if (dynamic_cast(p)) { ParentPopup = true; break; } p = p->GetParent(); } if (!ParentPopup) { for (int i=0; iVisible()) { // printf("Hiding popup %s\n", pu->GetClass()); pu->Visible(false); } } } if (!m.IsMove() && LAppInst) { auto mh = LAppInst->GetMouseHook(); if (mh) mh->TrackClick(v); } } for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LMouseEvents) { if (!d->Hooks[i].Target->OnViewMouse(v, m)) { return false; } } } return true; } bool LWindow::HandleViewKey(LView *v, LKey &k) { bool Status = false; LViewI *Ctrl = NULL; if (!v && d->Focus) v = d->Focus->GetGView(); if (!v) { #if DEBUG_KEYS k.Trace("No focus view to handle key."); #endif return false; } // Give key to popups if (LAppInst && LAppInst->GetMouseHook() && LAppInst->GetMouseHook()->OnViewKey(v, k)) { goto AllDone; } // Allow any hooks to see the key... for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LKeyEvents) { if (d->Hooks[i].Target->OnViewKey(v, k)) { Status = true; #if DEBUG_KEYS printf("Hook ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } } // Give the key to the window... if (v->OnKey(k)) { #if DEBUG_KEYS GString vv = DescribeView(v); printf("%s ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", vv.Get(), k.c16, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif Status = true; goto AllDone; } // Window didn't want the key... switch (k.vkey) { case LK_RETURN: case LK_KEYPADENTER: { Ctrl = _Default; break; } case LK_ESCAPE: { Ctrl = FindControl(IDCANCEL); break; } case LK_TAB: { // Go to the next control? if (k.Down()) { LArray Stops; BuildTabStops(Stops, v->GetWindow()); ssize_t Idx = Stops.IndexOf(v); if (Idx >= 0) { if (k.Shift()) { Idx--; if (Idx < 0) Idx = Stops.Length() - 1; } else { Idx++; if (Idx >= Stops.Length()) Idx = 0; } Stops[Idx]->Focus(true); } } return true; } } if (Ctrl && Ctrl->Enabled()) { if (Ctrl->OnKey(k)) { Status = true; #if DEBUG_KEYS printf("Default Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } if (Menu) { Status = Menu->OnKey(v, k); if (Status) { #if DEBUG_KEYS printf("Menu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif } } // Command+W closes the window... if it doesn't get nabbed earlier. if (k.Down() && k.System() && tolower(k.c16) == 'w') { // Close Quit(); return true; } AllDone: if (d) d->LastKey = k; else LAssert(!"Window was deleted and we are accessing unallocated mem."); return Status; } void LWindow::Raise() { if (Wnd) { // BringToFront(Wnd); } } LWindowZoom LWindow::GetZoom() { if (Wnd) { #if 0 bool c = IsWindowCollapsed(Wnd); // printf("IsWindowCollapsed=%i\n", c); if (c) return LZoomMin; c = IsWindowInStandardState(Wnd, NULL, NULL); // printf("IsWindowInStandardState=%i\n", c); if (!c) return LZoomMax; #endif } return LZoomNormal; } void LWindow::SetZoom(LWindowZoom i) { #if 0 OSStatus e = 0; switch (i) { case LZoomMin: { e = CollapseWindow(Wnd, true); if (e) printf("%s:%i - CollapseWindow failed with %i\n", _FL, (int)e); // else printf("LZoomMin ok.\n"); break; } default: case LZoomNormal: { e = CollapseWindow(Wnd, false); if (e) printf("%s:%i - [Un]CollapseWindow failed with %i\n", _FL, (int)e); // else printf("LZoomNormal ok.\n"); break; } } #endif } LViewI *LWindow::GetDefault() { return _Default; } void LWindow::SetDefault(LViewI *v) { if (v && v->GetWindow() == (LViewI*)this) { if (_Default != v) { auto Old = _Default; _Default = v; if (Old) Old->Invalidate(); if (_Default) _Default->Invalidate(); } } else { _Default = 0; } } bool LWindow::Name(const char *n) { // LAutoPool Pool; bool Status = LBase::Name(n); if (Wnd) { NSString *ns = [NSString stringWithCString:n encoding:NSUTF8StringEncoding]; Wnd.p.title = ns; //[ns release]; } return Status; } const char *LWindow::Name() { return LBase::Name(); } LRect &LWindow::GetClient(bool ClientSpace) { // LAutoPool Pool; static LRect r; if (Wnd) { r = Wnd.p.contentView.frame; if (ClientSpace) r.Offset(-r.x1, -r.y1); } else { r.ZOff(-1, -1); } return r; } bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load) { if (!Store || !FieldName) return false; if (Load) { LVariant v; if (Store->GetValue(FieldName, v) && v.Str()) { LRect Position(0, 0, -1, -1); LWindowZoom State = LZoomNormal; auto t = LString(v.Str()).SplitDelimit(";"); for (auto s: t) { auto v = s.SplitDelimit("=", 1); if (v.Length() == 2) { if (v[0].Equals("State")) State = (LWindowZoom)v[1].Int(); else if (v[0].Equals("Pos")) Position.SetStr(v[1]); } else return false; } if (Position.Valid()) { if (Position.X() < 64) Position.x2 = Position.x1 + 63; if (Position.Y() < 64) Position.y2 = Position.y1 + 63; SetPos(Position); } SetZoom(State); } else return false; } else { char s[256]; LWindowZoom State = GetZoom(); sprintf(s, "State=%i;Pos=%s", State, GetPos().GetStr()); LVariant v = s; if (!Store->SetValue(FieldName, v)) return false; } return true; } LPoint LWindow::GetDpi() { return LScreenDpi(); } LPointF LWindow::GetDpiScale() { auto Dpi = GetDpi(); return LPointF(Dpi.x / 100.0, Dpi.y / 100.0); } LRect &LWindow::GetPos() { // LAutoPool Pool; if (Wnd) { Pos = LScreenFlip(Wnd.p.frame); // printf("%s::GetPos %s\n", GetClass(), Pos.GetStr()); } return Pos; } bool LWindow::SetPos(LRect &p, bool Repaint) { // LAutoPool Pool; Pos = p; if (Wnd) { LRect r = LScreenFlip(p); [Wnd.p setFrame:r display:YES]; // printf("%s::SetPos %s\n", GetClass(), Pos.GetStr()); } return true; } void LWindow::OnChildrenChanged(LViewI *Wnd, bool Attaching) { if (dynamic_cast(Wnd)) { printf("%s:%i - Ignoring GPopup in OnChildrenChanged handler.\n", _FL); return; } PourAll(); } void LWindow::OnCreate() { } void LWindow::OnPaint(LSurface *pDC) { pDC->Colour(L_MED); pDC->Rectangle(); } void LWindow::OnPosChange() { LView::OnPosChange(); if (d->Sx != X() || d->Sy != Y()) { PourAll(); d->Sx = X(); d->Sy = Y(); } } #define IsTool(v) \ ( \ dynamic_cast(v) \ && \ dynamic_cast(v)->_IsToolBar \ ) void LWindow::PourAll() { LRect r = GetClient(); // printf("::Pour r=%s\n", r.GetStr()); LRegion Client(r); LRegion Update(Client); bool HasTools = false; LViewI *v; List::I Lst = Children.begin(); { LRegion Tools; for (v = *Lst; v; v = *++Lst) { if (IsTool(v)) { LRect OldPos = v->GetPos(); Update.Union(&OldPos); if (HasTools) { // 2nd and later toolbars if (v->Pour(Tools)) { if (!v->Visible()) { v->Visible(true); } if (OldPos != v->GetPos()) { // position has changed update... v->Invalidate(); } Tools.Subtract(&v->GetPos()); Update.Subtract(&v->GetPos()); } } else { // First toolbar if (v->Pour(Client)) { HasTools = true; if (!v->Visible()) { v->Visible(true); } if (OldPos != v->GetPos()) { v->Invalidate(); } LRect Bar(v->GetPos()); Bar.x2 = GetClient().x2; Tools = Bar; Tools.Subtract(&v->GetPos()); Client.Subtract(&Bar); Update.Subtract(&Bar); } } } } } Lst = Children.begin(); for (LViewI *v = *Lst; v; v = *++Lst) { if (!IsTool(v)) { LRect OldPos = v->GetPos(); Update.Union(&OldPos); if (v->Pour(Client)) { if (!v->Visible()) { v->Visible(true); } v->Invalidate(); Client.Subtract(&v->GetPos()); Update.Subtract(&v->GetPos()); } else { // non-pourable } } } for (int i=0; iMsg()) { case M_CLOSE: { if (Wnd) [Wnd.p performSelectorOnMainThread:@selector(onQuit) withObject:nil waitUntilDone:false]; else LAssert(!"No window?"); break; } case M_DESTROY: { delete this; return true; } } return LView::OnEvent(m); } bool LWindow::RegisterHook(LView *Target, LWindowHookType EventType, int Priority) { bool Status = false; if (Target && EventType) { ssize_t i = d->GetHookIndex(Target, true); if (i >= 0) { d->Hooks[i].Flags = EventType; Status = true; } } return Status; } bool LWindow::UnregisterHook(LView *Target) { ssize_t i = d->GetHookIndex(Target); if (i >= 0) { d->Hooks.DeleteAt(i); return true; } return false; } LViewI *LWindow::WindowFromPoint(int x, int y, int DebugDepth) { for (int i=0; iVisible()) { auto r = p->GetPos(); if (r.Overlap(x, y)) { // printf("WindowFromPoint got %s click (%i,%i)\n", p->GetClass(), x, y); return p->WindowFromPoint(x - r.x1, y - r.y1, DebugDepth ? DebugDepth + 1 : 0); } } } return LView::WindowFromPoint(x, y, DebugDepth ? DebugDepth + 1 : 0); } int LWindow::OnCommand(int Cmd, int Event, OsView SrcCtrl) { #if 0 OsView v; switch (Cmd) { case kHICommandCut: { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_CUT); break; } case kHICommandCopy: { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_COPY); break; } case kHICommandPaste: { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_PASTE); break; } case 'dele': { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_DELETE); break; } } #endif return 0; } void LWindow::OnTrayClick(LMouse &m) { if (m.Down() || m.IsContextMenu()) { LSubMenu RClick; OnTrayMenu(RClick); if (GetMouse(m, true)) { #if WINNATIVE SetForegroundWindow(Handle()); #endif int Result = RClick.Float(this, m.x, m.y); #if WINNATIVE PostMessage(Handle(), WM_NULL, 0, 0); #endif OnTrayMenuResult(Result); } } } bool LWindow::Obscured() { // LAutoPool Pool; if (!Wnd) return false; auto s = [Wnd.p occlusionState]; return !(s & NSWindowOcclusionStateVisible); }