diff --git a/Ide/Code/DocEdit.h b/Ide/Code/DocEdit.h --- a/Ide/Code/DocEdit.h +++ b/Ide/Code/DocEdit.h @@ -1,200 +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; + 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 - // Thread only - Node Root; - LUnrolledList PrevStyle; - // End 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); - void AddKeywords(const char **keys, bool IsType); // 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); }; #endif diff --git a/Ide/Code/DocEditStyling.cpp b/Ide/Code/DocEditStyling.cpp --- a/Ide/Code/DocEditStyling.cpp +++ b/Ide/Code/DocEditStyling.cpp @@ -1,1252 +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); } - else - { - #if LOG_STYLE - LgiTrace("DocEdit.Worker canceled style...\n"); - #endif - } - if (LMutex::Lock(_FL)) - { - Params.Dirty = p.Dirty; - Params.Styles.Swap(p.Styles); - LMutex::Unlock(); - } - WorkerState = KWaiting; } 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/ProjectNode.cpp b/Ide/Code/ProjectNode.cpp --- a/Ide/Code/ProjectNode.cpp +++ b/Ide/Code/ProjectNode.cpp @@ -1,1538 +1,1538 @@ #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() { if (sFile && Project) Project->OnNode(sFile, this, false); if (Dep) DeleteObj(Dep); } 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::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) { 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]); } } } delete s; }); break; } case IDM_IMPORT_FOLDER: { LFileSelect *s = new LFileSelect; s->Parent(Tree); LAutoString Dir = Project->GetBasePath(); if (Dir) { s->InitialDir(Dir); } s->OpenFolder([&](auto s, auto ok) { if (ok) { LArray Files; LArray Ext; LToken e(SourcePatterns, ";"); for (int i=0; iName(), &Ext, &Files)) { auto Start = strlen(s->Name()) + 1; for (int i=0; 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(); } } } Files.DeleteArrays(); } } 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([&](auto dlg, auto code) + 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/src/haiku/File.cpp b/src/haiku/File.cpp --- a/src/haiku/File.cpp +++ b/src/haiku/File.cpp @@ -1,1722 +1,1743 @@ /*hdr ** FILE: File.cpp ** AUTHOR: Matthew Allen ** DATE: 8/10/2000 ** DESCRIPTION: The new file subsystem ** ** Copyright (C) 2000, Matthew Allen ** fret@memecode.com */ /****************************** Includes **********************************/ #include #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif #include "lgi/common/LgiDefs.h" #include "lgi/common/File.h" #include "lgi/common/Containers.h" #include "lgi/common/Token.h" #include "lgi/common/Gdc2.h" #include "lgi/common/LgiCommon.h" #include "lgi/common/LgiString.h" #include "lgi/common/DateTime.h" #if defined(WIN32) #include "errno.h" #endif /****************************** Defines ***********************************/ // #define FILEDEBUG #define FLOPPY_360K 0x0001 #define FLOPPY_720K 0x0002 #define FLOPPY_1_2M 0x0004 #define FLOPPY_1_4M 0x0008 #define FLOPPY_5_25 (FLOPPY_360K | FLOPPY_1_2M) #define FLOPPY_3_5 (FLOPPY_720K | FLOPPY_1_4M) /****************************** Globals ***********************************/ LString LFile::Path::Sep(DIR_STR); struct ErrorCodeType { const char *Name; int Code; const char *Desc; } ErrorCodes[] = { #if defined(WIN32) {"EPERM", 1, "Not owner"}, {"ENOENT", 2, "No such file"}, {"ESRCH", 3, "No such process"}, {"EINTR", 4, "Interrupted system"}, {"EIO", 5, "I/O error"}, {"ENXIO", 6, "No such device"}, {"E2BIG", 7, "Argument list too long"}, {"ENOEXEC", 8, "Exec format error"}, {"EBADF", 9, "Bad file number"}, {"ECHILD", 10, "No children"}, {"EAGAIN", 11, "No more processes"}, {"ENOMEM", 12, "Not enough core"}, {"EACCES", 13, "Permission denied"}, {"EFAULT", 14, "Bad address"}, {"ENOTBLK", 15, "Block device required"}, {"EBUSY", 16, "Mount device busy"}, {"EEXIST", 17, "File exists"}, {"EXDEV", 18, "Cross-device link"}, {"ENODEV", 19, "No such device"}, {"ENOTDIR", 20, "Not a directory"}, {"EISDIR", 21, "Is a directory"}, {"EINVAL", 22, "Invalid argument"}, {"ENFILE", 23, "File table overflow"}, {"EMFILE", 24, "Too many open file"}, {"ENOTTY", 25, "Not a typewriter"}, {"ETXTBSY", 26, "Text file busy"}, {"EFBIG", 27, "File too large"}, {"ENOSPC", 28, "No space left on"}, {"ESPIPE", 29, "Illegal seek"}, {"EROFS", 30, "Read-only file system"}, {"EMLINK", 31, "Too many links"}, {"EPIPE", 32, "Broken pipe"}, {"EWOULDBLOCK", 35, "Operation would block"}, {"EINPROGRESS", 36, "Operation now in progress"}, {"EALREADY", 37, "Operation already in progress"}, {"ENOTSOCK", 38, "Socket operation on"}, {"EDESTADDRREQ", 39, "Destination address required"}, {"EMSGSIZE", 40, "Message too long"}, {"EPROTOTYPE", 41, "Protocol wrong type"}, {"ENOPROTOOPT", 42, "Protocol not available"}, {"EPROTONOSUPPORT", 43, "Protocol not supported"}, {"ESOCKTNOSUPPORT", 44, "Socket type not supported"}, {"EOPNOTSUPP", 45, "Operation not supported"}, {"EPFNOSUPPORT", 46, "Protocol family not supported"}, {"EAFNOSUPPORT", 47, "Address family not supported"}, {"EADDRINUSE", 48, "Address already in use"}, {"EADDRNOTAVAIL", 49, "Can't assign requested address"}, {"ENETDOWN", 50, "Network is down"}, {"ENETUNREACH", 51, "Network is unreachable"}, {"ENETRESET", 52, "Network dropped connection"}, {"ECONNABORTED", 53, "Software caused connection"}, {"ECONNRESET", 54, "Connection reset by peer"}, {"ENOBUFS", 55, "No buffer space available"}, {"EISCONN", 56, "Socket is already connected"}, {"ENOTCONN", 57, "Socket is not connected" }, {"ESHUTDOWN", 58, "Can't send after shutdown"}, {"ETOOMANYREFS", 59, "Too many references"}, {"ETIMEDOUT", 60, "Connection timed out"}, {"ECONNREFUSED", 61, "Connection refused"}, {"ELOOP", 62, "Too many levels of nesting"}, {"ENAMETOOLONG", 63, "File name too long" }, {"EHOSTDOWN", 64, "Host is down"}, {"EHOSTUNREACH", 65, "No route to host"}, {"ENOTEMPTY", 66, "Directory not empty"}, {"EPROCLIM", 67, "Too many processes"}, {"EUSERS", 68, "Too many users"}, {"EDQUOT", 69, "Disc quota exceeded"}, {"ESTALE", 70, "Stale NFS file handle"}, {"EREMOTE", 71, "Too many levels of remote in the path"}, {"ENOSTR", 72, "Device is not a stream"}, {"ETIME", 73, "Timer expired"}, {"ENOSR", 74, "Out of streams resources"}, {"ENOMSG", 75, "No message"}, {"EBADMSG", 76, "Trying to read unreadable message"}, {"EIDRM", 77, "Identifier removed"}, {"EDEADLK", 78, "Deadlock condition"}, {"ENOLCK", 79, "No record locks available"}, {"ENONET", 80, "Machine is not on network"}, {"ERREMOTE", 81, "Object is remote"}, {"ENOLINK", 82, "The link has been severed"}, {"EADV", 83, "ADVERTISE error"}, {"ESRMNT", 84, "SRMOUNT error"}, {"ECOMM", 85, "Communication error"}, {"EPROTO", 86, "Protocol error"}, {"EMULTIHOP", 87, "Multihop attempted"}, {"EDOTDOT", 88, "Cross mount point"}, {"EREMCHG", 89, "Remote address change"}, #elif defined(LINUX) || defined(__GTK_H__) {"EPERM", EPERM, "Operation not permitted"}, {"ENOENT", ENOENT, "No such file or directory"}, {"ESRCH", ESRCH, "No such process"}, {"EINTR", EINTR, "Interrupted system call"}, {"EIO", EIO, "I/O error"}, {"ENXIO", ENXIO, "No such device or address"}, {"E2BIG", E2BIG, "Argument list too long"}, {"ENOEXEC", ENOEXEC, "Exec format error"}, {"EBADF", EBADF, "Bad file number"}, {"ECHILD", ECHILD, "No child processes"}, {"EAGAIN", EAGAIN, "Try again"}, {"ENOMEM", ENOMEM, "Out of memory"}, {"EACCES", EACCES, "Permission denied"}, {"EFAULT", EFAULT, "Bad address"}, {"ENOTBLK", ENOTBLK, "Block device required"}, {"EBUSY", EBUSY, "Device or resource busy"}, {"EEXIST", EEXIST, "File exists"}, {"EXDEV", EXDEV, "Cross-device link"}, {"ENODEV", ENODEV, "No such device"}, {"ENOTDIR", ENOTDIR, "Not a directory"}, {"EISDIR", EISDIR, "Is a directory"}, {"EINVAL", EINVAL, "Invalid argument"}, {"ENFILE", ENFILE, "File table overflow"}, {"EMFILE", EMFILE, "Too many open files"}, {"ENOTTY", ENOTTY, "Not a typewriter"}, {"ETXTBSY", ETXTBSY, "Text file busy"}, {"EFBIG", EFBIG, "File too large"}, {"ENOSPC", ENOSPC, "No space left on device"}, {"ESPIPE", ESPIPE, "Illegal seek"}, {"EROFS", EROFS, "Read-only file system"}, {"EMLINK", EMLINK, "Too many links"}, {"EPIPE", EPIPE, "Broken pipe"}, {"EDOM", EDOM, "Math argument out of domain of func"}, {"ERANGE", ERANGE, "Math result not representable"}, {"EDEADLK", EDEADLK, "Resource deadlock would occur"}, {"ENAMETOOLONG", ENAMETOOLONG, "File name too long"}, {"ENOLCK", ENOLCK, "No record locks available"}, {"ENOSYS", ENOSYS, "Function not implemented"}, {"ENOTEMPTY", ENOTEMPTY, "Directory not empty"}, {"ELOOP", ELOOP, "Too many symbolic links encountered"}, {"EWOULDBLOCK", EWOULDBLOCK, "Operation would block"}, {"ENOMSG", ENOMSG, "No message of desired type"}, {"EIDRM", EIDRM, "Identifier removed"}, {"EREMOTE", EREMOTE, "Object is remote"}, {"ENOLINK", ENOLINK, "Link has been severed"}, {"ENOSTR", ENOSTR, "Device not a stream"}, {"ENODATA", ENODATA, "No data available"}, {"ETIME", ETIME, "Timer expired"}, {"ENOSR", ENOSR, "Out of streams resources"}, {"EPROTO", EPROTO, "Protocol error"}, {"EMULTIHOP", EMULTIHOP, "Multihop attempted"}, {"EBADMSG", EBADMSG, "Not a data message"}, {"EOVERFLOW", EOVERFLOW, "Value too large for defined data type"}, {"EILSEQ", EILSEQ, "Illegal byte sequence"}, {"EUSERS", EUSERS, "Too many users"}, {"ENOTSOCK", ENOTSOCK, "Socket operation on non-socket"}, {"EDESTADDRREQ", EDESTADDRREQ, "Destination address required"}, {"EMSGSIZE", EMSGSIZE, "Message too long"}, {"EPROTOTYPE", EPROTOTYPE, "Protocol wrong type for socket"}, {"ENOPROTOOPT", ENOPROTOOPT, "Protocol not available"}, {"EPROTONOSUPPORT", EPROTONOSUPPORT, "Protocol not supported"}, {"ESOCKTNOSUPPORT", ESOCKTNOSUPPORT, "Socket type not supported"}, {"EOPNOTSUPP", EOPNOTSUPP, "Operation not supported on transport endpoint"}, {"EPFNOSUPPORT", EPFNOSUPPORT, "Protocol family not supported"}, {"EAFNOSUPPORT", EAFNOSUPPORT, "Address family not supported by protocol"}, {"EADDRINUSE", EADDRINUSE, "Address already in use"}, {"EADDRNOTAVAIL", EADDRNOTAVAIL, "Cannot assign requested address"}, {"ENETDOWN", ENETDOWN, "Network is down"}, {"ENETUNREACH", ENETUNREACH, "Network is unreachable"}, {"ENETRESET", ENETRESET, "Network dropped connection because of reset"}, {"ECONNABORTED", ECONNABORTED, "Software caused connection abort"}, {"ECONNRESET", ECONNRESET, "Connection reset by peer"}, {"ENOBUFS", ENOBUFS, "No buffer space available"}, {"EISCONN", EISCONN, "Transport endpoint is already connected"}, {"ENOTCONN", ENOTCONN, "Transport endpoint is not connected"}, {"ESHUTDOWN", ESHUTDOWN, "Cannot send after transport endpoint shutdown"}, {"ETOOMANYREFS", ETOOMANYREFS, "Too many references: cannot splice"}, {"ETIMEDOUT", ETIMEDOUT, "Connection timed out"}, {"ECONNREFUSED", ECONNREFUSED, "Connection refused"}, {"EHOSTDOWN", EHOSTDOWN, "Host is down"}, {"EHOSTUNREACH", EHOSTUNREACH, "No route to host"}, {"EALREADY", EALREADY, "Operation already in progress"}, {"EINPROGRESS", EINPROGRESS, "Operation now in progress"}, {"ESTALE", ESTALE, "Stale NFS file handle"}, #ifndef __GTK_H__ {"EDQUOT", EDQUOT, "Quota exceeded"}, {"ENOMEDIUM", ENOMEDIUM, "No medium found"}, {"EMEDIUMTYPE", EMEDIUMTYPE, "Wrong medium type"}, {"EUCLEAN", EUCLEAN, "Structure needs cleaning"}, {"ENOTNAM", ENOTNAM, "Not a XENIX named type file"}, {"ENAVAIL", ENAVAIL, "No XENIX semaphores available"}, {"EISNAM", EISNAM, "Is a named type file"}, {"EREMOTEIO", EREMOTEIO, "Remote I/O error"}, {"ERESTART", ERESTART, "Interrupted system call should be restarted"}, {"ESTRPIPE", ESTRPIPE, "Streams pipe error"}, {"ECOMM", ECOMM, "Communication error on send"}, {"EDOTDOT", EDOTDOT, "RFS specific error"}, {"ENOTUNIQ", ENOTUNIQ, "Name not unique on network"}, {"EBADFD", EBADFD, "File descriptor in bad state"}, {"EREMCHG", EREMCHG, "Remote address changed"}, {"ELIBACC", ELIBACC, "Can not access a needed shared library"}, {"ELIBBAD", ELIBBAD, "Accessing a corrupted shared library"}, {"ELIBSCN", ELIBSCN, ".lib section in a.out corrupted"}, {"ELIBMAX", ELIBMAX, "Attempting to link in too many shared libraries"}, {"ELIBEXEC", ELIBEXEC, "Cannot exec a shared library directly"}, {"ECHRNG", ECHRNG, "Channel number out of range"}, {"EL2NSYNC", EL2NSYNC, "Level 2 not synchronized"}, {"EL3HLT", EL3HLT, "Level 3 halted"}, {"EL3RST", EL3RST, "Level 3 reset"}, {"ELNRNG", ELNRNG, "Link number out of range"}, {"EUNATCH", EUNATCH, "Protocol driver not attached"}, {"ENOCSI", ENOCSI, "No CSI structure available"}, {"EL2HLT", EL2HLT, "Level 2 halted"}, {"EBADE", EBADE, "Invalid exchange"}, {"EBADR", EBADR, "Invalid request descriptor"}, {"EXFULL", EXFULL, "Exchange full"}, {"ENOANO", ENOANO, "No anode"}, {"EBADRQC", EBADRQC, "Invalid request code"}, {"EBADSLT", EBADSLT, "Invalid slot"}, {"EBFONT", EBFONT, "Bad font file format"}, {"EADV", EADV, "Advertise error"}, {"ESRMNT", ESRMNT, "Srmount error"}, {"ENONET", ENONET, "Machine is not on the network"}, {"ENOPKG", ENOPKG, "Package not installed"}, #endif #endif {"NONE", 0, "No error"}, }; const char *GetErrorName(int e) { for (ErrorCodeType *c=ErrorCodes; c->Code; c++) { if (e == c->Code) { return c->Name; } } static char s[32]; sprintf(s, "Unknown(%i)", e); return s; } const char *GetErrorDesc(int e) { for (ErrorCodeType *c=ErrorCodes; c->Code; c++) { if (e == c->Code) return c->Desc; } return 0; } /****************************** Helper Functions **************************/ char *LReadTextFile(const char *File) { char *s = 0; LFile f; if (File && f.Open(File, O_READ)) { int Len = f.GetSize(); s = new char[Len+1]; if (s) { int Read = f.Read(s, Len); s[Read] = 0; } } return s; } int64 LFileSize(const char *FileName) { struct stat s; if (FileName && stat(FileName, &s) == 0) { return s.st_size; } return 0; } bool LDirExists(const char *FileName, char *CorrectCase) { bool Status = false; if (FileName) { struct stat s; // Check for exact match... int r = lstat(FileName, &s); if (r == 0) { Status = S_ISDIR(s.st_mode) || S_ISLNK(s.st_mode); // printf("DirStatus(%s) lstat = %i, %i\n", FileName, Status, s.st_mode); } else { r = stat(FileName, &s); if (r == 0) { Status = S_ISDIR(s.st_mode) || S_ISLNK(s.st_mode); // printf("DirStatus(%s) stat ok = %i, %i\n", FileName, Status, s.st_mode); } else { // printf("DirStatus(%s) lstat and stat failed, r=%i, errno=%i\n", FileName, r, errno); } } } return Status; } bool LFileExists(const char *FileName, char *CorrectCase) { bool Status = false; if (FileName) { struct stat s; // Check for exact match... if (stat(FileName, &s) == 0) { Status = !S_ISDIR(s.st_mode); } else if (CorrectCase) { // Look for altenate case by enumerating the directory char d[256]; strcpy(d, FileName); char *e = strrchr(d, DIR_CHAR); if (e) { *e++ = 0; DIR *Dir = opendir(d); if (Dir) { dirent *De; while ((De = readdir(Dir))) { if (stricmp(De->d_name, e) == 0) { try { // Tell the calling program the actual case of the file... e = (char*) strrchr(FileName, DIR_CHAR); // If this crashes because the argument is read only then we get caught by the try catch strcpy(e+1, De->d_name); // It worked! Status = true; } catch (...) { // It didn't work :( #ifdef _DEBUG printf("%s,%i - LFileExists(%s) found an alternate case version but couldn't return it to the caller.\n", __FILE__, __LINE__, FileName); #endif } break; } } closedir(Dir); } } } } return Status; } bool LResolveShortcut(const char *LinkFile, char *Path, ssize_t Len) { bool Status = false; return Status; } void WriteStr(LFile &f, const char *s) { uint32_t Len = (s) ? strlen(s) : 0; f << Len; if (Len > 0) { f.Write(s, Len); } } char *ReadStr(LFile &f DeclDebugArgs) { char *s = 0; // read the strings length... uint32_t Len; f >> Len; if (Len > 0) { // 16mb sanity check.... anything over this // is _probably_ an error if (Len >= (16 << 20)) { // LAssert(0); return 0; } // allocate the memory buffer #if defined(_DEBUG) && defined MEMORY_DEBUG s = new(_file, _line) char[Len+1]; #else s = new char[Len+1]; #endif if (s) { // read the bytes from disk f.Read(s, Len); s[Len] = 0; } else { // memory allocation error, skip the data // on disk so the caller is where they think // they are in the file. f.Seek(Len, SEEK_CUR); } } return s; } ssize_t SizeofStr(const char *s) { return sizeof(ulong) + ((s) ? strlen(s) : 0); } bool LGetDriveInfo ( char *Path, uint64 *Free, uint64 *Size, uint64 *Available ) { bool Status = false; if (Path) { struct stat s; if (lstat(Path, &s) == 0) { // printf("LGetDriveInfo dev=%i\n", s.st_dev); } } return Status; } ///////////////////////////////////////////////////////////////////////// #include #include struct LVolumePriv { int64 _Size, _Free; int _Type, _Flags; LSystemPath SysPath; LString _Name, _Path; List _Sub; List::I _It; void Init() { SysPath = LSP_ROOT; _Type = VT_NONE; _Flags = 0; _Size = 0; _Free = 0; } LVolumePriv(const char *path) : _It(_Sub.end()) { Init(); _Path = path; _Name = LGetLeaf(path); _Type = VT_FOLDER; } LVolumePriv(LSystemPath sys, const char *name) : _It(_Sub.end()) { Init(); SysPath = sys; if (SysPath == LSP_ROOT) _Path = "/"; else _Path = LGetSystemPath(SysPath); if (_Path) { _Name = name; _Type = sys == LSP_DESKTOP ? VT_DESKTOP : VT_FOLDER; } } ~LVolumePriv() { _Sub.DeleteObjects(); } LVolume *First() { if (SysPath == LSP_DESKTOP && !_Sub.Length()) { // Get various shortcuts to points of interest LVolume *v = new LVolume(LSP_ROOT, "Root"); if (v) _Sub.Insert(v); struct passwd *pw = getpwuid(getuid()); if (pw) { v = new LVolume(LSP_HOME, "Home"); if (v) _Sub.Insert(v); } // Get mount list // this is just a hack at this stage to establish some base // functionality. I would appreciate someone telling me how // to do this properly. Till then... LFile f; auto fstab = "/etc/fstab"; if (LFileExists(fstab) && f.Open(fstab, O_READ)) { auto Buf= f.Read(); f.Close(); auto Lines = Buf.SplitDelimit("\r\n"); for (auto ln : Lines) { auto M = ln.Strip().SplitDelimit(" \t"); if (M[0](0) != '#' && M.Length() > 2) { auto &Device = M[0]; auto &Mount = M[1]; auto &FileSys = M[2]; if ( (Device.Find("/dev/") == 0 || Mount.Find("/mnt/") == 0) && Device.Lower().Find("/by-uuid/") < 0 && Mount.Length() > 1 && !FileSys.Equals("swap") ) { v = new LVolume(0); if (v) { char *MountName = strrchr(Mount, '/'); v->d->_Name = (MountName ? MountName + 1 : Mount.Get()); v->d->_Path = Mount; v->d->_Type = VT_HARDDISK; char *Device = M[0]; // char *FileSys = M[2]; if (stristr(Device, "fd")) { v->d->_Type = VT_FLOPPY; } else if (stristr(Device, "cdrom")) { v->d->_Type = VT_CDROM; } _Sub.Insert(v); } } } } } LSystemPath p[] = {LSP_USER_DOCUMENTS, LSP_USER_MUSIC, LSP_USER_VIDEO, LSP_USER_DOWNLOADS, LSP_USER_PICTURES}; for (int i=0; id->_Path = Path; v->d->_Name = *Parts.rbegin(); v->d->_Type = VT_FOLDER; _Sub.Insert(v); } } } _It = _Sub.begin(); return *_It; } LVolume *Next() { return *(++_It); } }; LVolume::LVolume(const char *Path) { d = new LVolumePriv(Path); } LVolume::LVolume(LSystemPath SysPath, const char *Name) { d = new LVolumePriv(SysPath, Name); } LVolume::~LVolume() { DeleteObj(d); } const char *LVolume::Name() const { return d->_Name; } const char *LVolume::Path() const { return d->_Path; } int LVolume::Type() const { return d->_Type; } int LVolume::Flags() const { return d->_Flags; } uint64 LVolume::Size() const { return d->_Size; } uint64 LVolume::Free() const { return d->_Free; } LSurface *LVolume::Icon() const { return NULL; } bool LVolume::IsMounted() const { return true; } bool LVolume::SetMounted(bool Mount) { return Mount; } LVolume *LVolume::First() { return d->First(); } LVolume *LVolume::Next() { return d->Next(); } void LVolume::Insert(LAutoPtr v) { d->_Sub.Insert(v.Release()); } LDirectory *LVolume::GetContents() { LDirectory *Dir = 0; if (d->_Path) { Dir = new LDirectory; if (Dir && !Dir->First(d->_Path)) DeleteObj(Dir); } return Dir; } /////////////////////////////////////////////////////////////////////////////// LFileSystem *LFileSystem::Instance = 0; LFileSystem::LFileSystem() { Instance = this; Root = 0; } LFileSystem::~LFileSystem() { DeleteObj(Root); } void LFileSystem::OnDeviceChange(char *Reserved) { } LVolume *LFileSystem::GetRootVolume() { if (!Root) Root = new LVolume(LSP_DESKTOP, "Desktop"); return Root; } bool LFileSystem::Copy(const char *From, const char *To, LError *Status, CopyFileCallback Callback, void *Token) { LArray Buf; if (Status) *Status = 0; if (Buf.Length(2 << 20)) { LFile In, Out; if (!In.Open(From, O_READ)) { if (Status) *Status = In.GetError(); return false; } if (!Out.Open(To, O_WRITE)) { if (Status) *Status = Out.GetError(); return false; } int64 Size = In.GetSize(), Done = 0; for (int64 i=0; i 0) { int w = Out.Write(&Buf[0], r); if (w <= 0) break; r -= w; Done += w; if (Callback) Callback(Token, Done, Size); } if (r > 0) break; } return Done == Size; } return false; } bool LFileSystem::Delete(LArray &Files, LArray *Status, bool ToTrash) { bool Error = false; if (ToTrash) { char p[MAX_PATH_LEN]; if (LGetSystemPath(LSP_TRASH, p, sizeof(p))) { for (int i=0; i f; f.Add(FileName); return Delete(f, 0, ToTrash); } return false; } bool LFileSystem::CreateFolder(const char *PathName, bool CreateParentTree, LError *ErrorCode) { int r = mkdir(PathName, S_IRWXU | S_IXGRP | S_IXOTH); if (r) { if (ErrorCode) *ErrorCode = errno; if (CreateParentTree) { LFile::Path p(PathName); LString dir = DIR_STR; for (size_t i=1; i<=p.Length(); i++) { LFile::Path Par(dir + dir.Join(p.Slice(0, i))); if (!Par.Exists()) { const char *ParDir = Par; r = mkdir(ParDir, S_IRWXU | S_IXGRP | S_IXOTH); if (r) { if (ErrorCode) *ErrorCode = errno; break; } LgiTrace("%s:%i - Created '%s'\n", _FL, Par.GetFull().Get()); } } if (p.Exists()) return true; } LgiTrace("%s:%i - mkdir('%s') failed with %i, errno=%i\n", _FL, PathName, r, errno); } return r == 0; } bool LFileSystem::RemoveFolder(const char *PathName, bool Recurse) { if (Recurse) { LDirectory *Dir = new LDirectory; if (Dir && Dir->First(PathName)) { do { char Str[256]; Dir->Path(Str, sizeof(Str)); if (Dir->IsDir()) { RemoveFolder(Str, Recurse); } else { Delete(Str, false); } } while (Dir->Next()); } DeleteObj(Dir); } return rmdir(PathName) == 0; } bool LFileSystem::Move(const char *OldName, const char *NewName, LError *Err) { if (rename(OldName, NewName)) { printf("%s:%i - rename failed, error: %s(%i)\n", _FL, GetErrorName(errno), errno); return false; } return true; } /* bool Match(char *Name, char *Mask) { strupr(Name); strupr(Mask); while (*Name && *Mask) { if (*Mask == '*') { if (*Name == *(Mask+1)) { Mask++; } else { Name++; } } else if (*Mask == '?' || *Mask == *Name) { Mask++; Name++; } else { return false; } } while (*Mask && ((*Mask == '*') || (*Mask == '.'))) Mask++; return (*Name == 0 && *Mask == 0); } */ short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int LeapYear(int year) { if (year & 3) { return 0; } if ((year % 100 == 0) && !(year % 400 == 0)) { return 0; } return 1; } ///////////////////////////////////////////////////////////////////////////////// bool LDirectory::ConvertToTime(char *Str, int SLen, uint64 Time) const { time_t k = Time; struct tm *t = localtime(&k); if (t) { strftime(Str, SLen, "%I:%M:%S", t); return true; } return false; } bool LDirectory::ConvertToDate(char *Str, int SLen, uint64 Time) const { time_t k = Time; struct tm *t = localtime(&k); if (t) { strftime(Str, SLen, "%d/%m/%y", t); return true; } return false; } ///////////////////////////////////////////////////////////////////////////////// //////////////////////////// Directory ////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// struct LDirectoryPriv { char BasePath[MAX_PATH_LEN]; DIR *Dir; struct dirent *De; struct stat Stat; LString Pattern; LDirectoryPriv() { Dir = 0; De = 0; BasePath[0] = 0; } bool Ignore() { return De && ( strcmp(De->d_name, ".") == 0 || strcmp(De->d_name, "..") == 0 || ( Pattern && !MatchStr(Pattern, De->d_name) ) ); } }; LDirectory::LDirectory() { d = new LDirectoryPriv; } LDirectory::~LDirectory() { Close(); DeleteObj(d); } LDirectory *LDirectory::Clone() { return new LDirectory; } int LDirectory::First(const char *Name, const char *Pattern) { Close(); if (!Name) return 0; strcpy(d->BasePath, Name); if (!Pattern || stricmp(Pattern, LGI_ALL_FILES) == 0) { struct stat S; if (lstat(Name, &S) == 0) { if (S_ISREG(S.st_mode)) { char *Dir = strrchr(d->BasePath, DIR_CHAR); if (Dir) { *Dir++ = 0; d->Pattern = Dir; } } } } else { d->Pattern = Pattern; } d->Dir = opendir(d->BasePath); if (d->Dir) { d->De = readdir(d->Dir); if (d->De) { char s[MaxPathLen]; LMakePath(s, sizeof(s), d->BasePath, GetName()); lstat(s, &d->Stat); if (d->Ignore() && !Next()) return false; } } return d->Dir != NULL && d->De != NULL; } int LDirectory::Next() { int Status = false; while (d->Dir && d->De) { if ((d->De = readdir(d->Dir))) { char s[MaxPathLen]; LMakePath(s, sizeof(s), d->BasePath, GetName()); lstat(s, &d->Stat); if (!d->Ignore()) { Status = true; break; } } } return Status; } int LDirectory::Close() { if (d->Dir) { closedir(d->Dir); d->Dir = 0; } d->De = 0; return true; } const char *LDirectory::FullPath() { static char s[MAX_PATH_LEN]; #warning this should really be optimized, and thread safe... Path(s, sizeof(s)); return s; } LString LDirectory::FileName() const { return GetName(); } bool LDirectory::Path(char *s, int BufLen) const { if (!s) { return false; } return LMakePath(s, BufLen, d->BasePath, GetName()); } int LDirectory::GetType() const { return IsDir() ? VT_FOLDER : VT_FILE; } int LDirectory::GetUser(bool Group) const { if (Group) { return d->Stat.st_gid; } else { return d->Stat.st_uid; } } bool LDirectory::IsReadOnly() const { if (getuid() == d->Stat.st_uid) { // Check user perms return !TestFlag(GetAttributes(), S_IWUSR); } else if (getgid() == d->Stat.st_gid) { // Check group perms return !TestFlag(GetAttributes(), S_IWGRP); } // Check global perms return !TestFlag(GetAttributes(), S_IWOTH); } bool LDirectory::IsHidden() const { return GetName() && GetName()[0] == '.'; } bool LDirectory::IsDir() const { int a = GetAttributes(); return !S_ISLNK(a) && S_ISDIR(a); } bool LDirectory::IsSymLink() const { int a = GetAttributes(); return S_ISLNK(a); } long LDirectory::GetAttributes() const { return d->Stat.st_mode; } char *LDirectory::GetName() const { return (d->De) ? d->De->d_name : NULL; } uint64 LDirectory::GetCreationTime() const { return (uint64) d->Stat.st_ctime * LDateTime::Second64Bit; } uint64 LDirectory::GetLastAccessTime() const { return (uint64) d->Stat.st_atime * LDateTime::Second64Bit; } uint64 LDirectory::GetLastWriteTime() const { return (uint64) d->Stat.st_mtime * LDateTime::Second64Bit; } uint64 LDirectory::GetSize() const { return (uint32_t)d->Stat.st_size; } int64 LDirectory::GetSizeOnDisk() { return (uint32_t)d->Stat.st_size; } ///////////////////////////////////////////////////////////////////////////////// //////////////////////////// File /////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// class LFilePrivate { public: int hFile; char *Name; bool Swap; int Status; int Attributes; int ErrorCode; LFilePrivate() { hFile = INVALID_HANDLE; Name = 0; Swap = false; Status = true; Attributes = 0; ErrorCode = 0; } ~LFilePrivate() { DeleteArray(Name); } }; LFile::LFile(const char *Path, int Mode) { d = new LFilePrivate; if (Path) Open(Path, Mode); } LFile::~LFile() { if (d && ValidHandle(d->hFile)) { Close(); } DeleteObj(d); } OsFile LFile::Handle() { return d->hFile; } int LFile::GetError() { return d->ErrorCode; } bool LFile::IsOpen() { return ValidHandle(d->hFile); } #define DEBUG_OPEN_FILES 0 #if DEBUG_OPEN_FILES LMutex Lck; LArray OpenFiles; #endif int LFile::Open(const char *File, int Mode) { int Status = false; if (File) { if (TestFlag(Mode, O_WRITE) || TestFlag(Mode, O_READWRITE)) { Mode |= O_CREAT; } Close(); d->hFile = open(File, Mode #ifdef O_LARGEFILE | O_LARGEFILE #endif , S_IRUSR | S_IWUSR); if (ValidHandle(d->hFile)) { d->Attributes = Mode; d->Name = new char[strlen(File)+1]; if (d->Name) { strcpy(d->Name, File); } Status = true; d->Status = true; #if DEBUG_OPEN_FILES if (Lck.Lock(_FL)) { if (!OpenFiles.HasItem(this)) OpenFiles.Add(this); Lck.Unlock(); } #endif } else { d->ErrorCode = errno; #if DEBUG_OPEN_FILES if (Lck.Lock(_FL)) { for (unsigned i=0; iGetName()); Lck.Unlock(); } #endif printf("LFile::Open failed\n\topen(%s,%08.8x) = %i\n\terrno=%s (%s)\n", File, Mode, d->hFile, GetErrorName(d->ErrorCode), GetErrorDesc(d->ErrorCode)); } } return Status; } int LFile::Close() { if (ValidHandle(d->hFile)) { close(d->hFile); d->hFile = INVALID_HANDLE; DeleteArray(d->Name); #if DEBUG_OPEN_FILES if (Lck.Lock(_FL)) { OpenFiles.Delete(this); Lck.Unlock(); } #endif } return true; } +uint64_t LFile::GetModifiedTime() +{ + struct stat s; + stat(d->Name, &s); + return s.st_mtime; +} + +bool LFile::SetModifiedTime(uint64_t dt) +{ + struct stat s; + stat(d->Name, &s); + + struct timeval times[2] = {}; + times[0].tv_sec = s.st_atime; + times[1].tv_sec = dt; + + int r = utimes(d->Name, times); + + return r == 0; +} + void LFile::ChangeThread() { } #define CHUNK 0xFFF0 ssize_t LFile::Read(void *Buffer, ssize_t Size, int Flags) { int Red = 0; if (Buffer && Size > 0) { Red = read(d->hFile, Buffer, Size); } d->Status = Red == Size; return MAX(Red, 0); } ssize_t LFile::Write(const void *Buffer, ssize_t Size, int Flags) { int Written = 0; if (Buffer && Size > 0) { Written = write(d->hFile, Buffer, Size); } d->Status = Written == Size; return MAX(Written, 0); } #ifdef LINUX #define LINUX64 1 #endif int64 LFile::Seek(int64 To, int Whence) { #if LINUX64 return lseek64(d->hFile, To, Whence); // If this doesn't compile, switch off LINUX64 #else return lseek(d->hFile, To, Whence); #endif } int64 LFile::SetPos(int64 Pos) { #if LINUX64 int64 p = lseek64(d->hFile, Pos, SEEK_SET); if (p < 0) { int e = errno; printf("%s:%i - lseek64(%Lx) failed (error %i: %s).\n", __FILE__, __LINE__, Pos, e, GetErrorName(e)); } #else return lseek(d->hFile, Pos, SEEK_SET); #endif } int64 LFile::GetPos() { #if LINUX64 int64 p = lseek64(d->hFile, 0, SEEK_CUR); if (p < 0) { int e = errno; printf("%s:%i - lseek64 failed (error %i: %s).\n", __FILE__, __LINE__, e, GetErrorName(e)); } return p; #else return lseek(d->hFile, 0, SEEK_CUR); #endif } int64 LFile::GetSize() { int64 Here = GetPos(); #if LINUX64 int64 Ret = lseek64(d->hFile, 0, SEEK_END); #else int64 Ret = lseek(d->hFile, 0, SEEK_END); #endif SetPos(Here); return Ret; } int64 LFile::SetSize(int64 Size) { if (ValidHandle(d->hFile)) { int64 Pos = GetPos(); #if LINUX64 ftruncate64(d->hFile, Size); #else ftruncate(d->hFile, Size); #endif if (d->hFile) { SetPos(Pos); } } return GetSize(); } bool LFile::Eof() { return GetPos() >= GetSize(); } ssize_t LFile::SwapRead(uchar *Buf, ssize_t Size) { ssize_t i = 0; ssize_t r = 0; Buf = &Buf[Size-1]; while (Size--) { r = read(d->hFile, Buf--, 1); i += r; } return i; } ssize_t LFile::SwapWrite(uchar *Buf, ssize_t Size) { ssize_t i = 0; ssize_t w = 0; Buf = &Buf[Size-1]; while (Size--) { w = write(d->hFile, Buf--, 1); i += w; } return i; } ssize_t LFile::ReadStr(char *Buf, ssize_t Size) { ssize_t i = 0; ssize_t r = 0; if (Buf && Size > 0) { char c; Size--; do { r = read(d->hFile, &c, 1); if (Eof()) { break; } *Buf++ = c; i++; } while (i < Size - 1 && c != '\n'); *Buf = 0; } return i; } ssize_t LFile::WriteStr(char *Buf, ssize_t Size) { ssize_t i = 0; ssize_t w; while (i <= Size) { w = write(d->hFile, Buf, 1); Buf++; i++; if (*Buf == '\n') break; } return i; } void LFile::SetStatus(bool s) { d->Status = s; } bool LFile::GetStatus() { return d->Status; } void LFile::SetSwap(bool s) { d->Swap = s; } bool LFile::GetSwap() { return d->Swap; } int LFile::GetOpenMode() { return d->Attributes; } const char *LFile::GetName() { return d->Name; } #define GFileOp(type) LFile &LFile::operator >> (type &i) { d->Status |= ((d->Swap) ? SwapRead((uchar*) &i, sizeof(i)) : Read(&i, sizeof(i))) != sizeof(i); return *this; } GFileOps(); #undef GFileOp #define GFileOp(type) LFile &LFile::operator << (type i) { d->Status |= ((d->Swap) ? SwapWrite((uchar*) &i, sizeof(i)) : Write(&i, sizeof(i))) != sizeof(i); return *this; } GFileOps(); #undef GFileOp ////////////////////////////////////////////////////////////////////////////// bool LFile::Path::FixCase() { LString Prev; bool Changed = false; // printf("FixCase '%s'\n", GetFull().Get()); for (size_t i=0; i %s\n", part.Get(), name); Part = Name; Next = (Prev ? Prev : "") + Sep + Part; Changed = true; } } } Prev = Next; } return Changed; } diff --git a/src/haiku/View.cpp b/src/haiku/View.cpp --- a/src/haiku/View.cpp +++ b/src/haiku/View.cpp @@ -1,1114 +1,1117 @@ /*hdr ** FILE: LView.cpp ** AUTHOR: Matthew Allen ** DATE: 29/11/2021 ** DESCRIPTION: Haiku LView Implementation ** ** Copyright (C) 2021, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Edit.h" #include "lgi/common/Popup.h" #include "lgi/common/Css.h" #include "ViewPriv.h" #include #define DEBUG_MOUSE_EVENTS 0 #if 0 #define DEBUG_INVALIDATE(...) printf(__VA_ARGS__) #else #define DEBUG_INVALIDATE(...) #endif struct CursorInfo { public: LRect Pos; LPoint HotSpot; } CursorMetrics[] = { // up arrow { LRect(0, 0, 8, 15), LPoint(4, 0) }, // cross hair { LRect(20, 0, 38, 18), LPoint(29, 9) }, // hourglass { LRect(40, 0, 51, 15), LPoint(45, 8) }, // I beam { LRect(60, 0, 66, 17), LPoint(63, 8) }, // N-S arrow { LRect(80, 0, 91, 16), LPoint(85, 8) }, // E-W arrow { LRect(100, 0, 116, 11), LPoint(108, 5) }, // NW-SE arrow { LRect(120, 0, 132, 12), LPoint(126, 6) }, // NE-SW arrow { LRect(140, 0, 152, 12), LPoint(146, 6) }, // 4 way arrow { LRect(160, 0, 178, 18), LPoint(169, 9) }, // Blank { LRect(0, 0, 0, 0), LPoint(0, 0) }, // Vertical split { LRect(180, 0, 197, 16), LPoint(188, 8) }, // Horizontal split { LRect(200, 0, 216, 17), LPoint(208, 8) }, // Hand { LRect(220, 0, 233, 13), LPoint(225, 0) }, // No drop { LRect(240, 0, 258, 18), LPoint(249, 9) }, // Copy drop { LRect(260, 0, 279, 19), LPoint(260, 0) }, // Move drop { LRect(280, 0, 299, 19), LPoint(280, 0) }, }; // CursorData is a bitmap in an array of uint32's. This is generated from a graphics file: // ./Code/cursors.png // // The pixel values are turned into C code by a program called i.Mage: // http://www.memecode.com/image.php // // Load the graphic into i.Mage and then go Edit->CopyAsCode // Then paste the text into the CursorData variable at the bottom of this file. // // This saves a lot of time finding and loading an external resouce, and even having to // bundle extra files with your application. Which have a tendancy to get lost along the // way etc. extern uint32_t CursorData[]; LInlineBmp Cursors = { 300, 20, 8, CursorData }; //////////////////////////////////////////////////////////////////////////// void _lgi_yield() { LAppInst->Yield(); } void *IsAttached(BView *v) { auto pview = v->Parent(); auto pwnd = v->Window(); return pwnd ? (void*)pwnd : (void*)pview; } bool LgiIsKeyDown(int Key) { LAssert(0); return false; } LKey::LKey(int Vkey, uint32_t flags) { vkey = Vkey; Flags = flags; IsChar = false; } //////////////////////////////////////////////////////////////////////////////////////////////////// template struct LBView : public Parent { LViewPrivate *d = NULL; static uint32 MouseButtons; LBView(LViewPrivate *priv) : d(priv), Parent ( "", B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS ) { Parent::SetName(d->View->GetClass()); } ~LBView() { if (d) d->Hnd = NULL; } void AttachedToWindow() { if (d) d->View->OnCreate(); } LKey ConvertKey(const char *bytes, int32 numBytes) { LKey k; uint8_t *utf = (uint8_t*)bytes; ssize_t len = numBytes; auto w = LgiUtf8To32(utf, len); key_info KeyInfo; if (get_key_info(&KeyInfo) == B_OK) { k.Ctrl(TestFlag(KeyInfo.modifiers, B_CONTROL_KEY)); k.Alt(TestFlag(KeyInfo.modifiers, B_MENU_KEY)); k.Shift(TestFlag(KeyInfo.modifiers, B_SHIFT_KEY)); k.System(TestFlag(KeyInfo.modifiers, B_COMMAND_KEY)); } #if 0 LString::Array a; for (int i=0; iCurrentMessage(); if (bmsg) { int32 key = 0; if (bmsg->FindInt32("key", &key) == B_OK) { // Translate the function keys into the LGI address space... switch (key) { case B_F1_KEY: w = LK_F1; break; case B_F2_KEY: w = LK_F2; break; case B_F3_KEY: w = LK_F3; break; case B_F4_KEY: w = LK_F4; break; case B_F5_KEY: w = LK_F5; break; case B_F6_KEY: w = LK_F6; break; case B_F7_KEY: w = LK_F7; break; case B_F8_KEY: w = LK_F8; break; case B_F9_KEY: w = LK_F9; break; case B_F10_KEY: w = LK_F10; break; case B_F11_KEY: w = LK_F11; break; case B_F12_KEY: w = LK_F12; break; default: printf("%s:%i - Upsupported key %i.\n", _FL, key); break; } } else printf("%s:%i - No 'key' in BMessage.\n", _FL); } else printf("%s:%i - No BMessage.\n", _FL); } k.c16 = k.vkey = w; } k.IsChar = !( k.System() || k.Alt() ) && ( (k.c16 >= ' ' && k.c16 < LK_DELETE) || k.c16 == LK_BACKSPACE || k.c16 == LK_TAB || k.c16 == LK_RETURN ); return k; } void KeyDown(const char *bytes, int32 numBytes) { if (!d) return; auto k = ConvertKey(bytes, numBytes); k.Down(true); auto wnd = d->View->GetWindow(); if (wnd) wnd->HandleViewKey(d->View, k); else d->View->OnKey(k); } void KeyUp(const char *bytes, int32 numBytes) { if (!d) return; auto k = ConvertKey(bytes, numBytes); auto wnd = d->View->GetWindow(); if (wnd) wnd->HandleViewKey(d->View, k); else d->View->OnKey(k); } void FrameMoved(BPoint newPosition) { if (!d) return; d->View->Pos = Parent::Frame(); d->View->OnPosChange(); } void FrameResized(float newWidth, float newHeight) { if (!d) return; d->View->Pos = Parent::Frame(); d->View->OnPosChange(); } void MessageReceived(BMessage *message) { if (!d) return; void *v = NULL; if (message->FindPointer(LMessage::PropView, &v) == B_OK) { // Proxy'd event for child view... ((LView*)v)->OnEvent((LMessage*)message); return; } else d->View->OnEvent((LMessage*)message); if (message->what == B_MOUSE_WHEEL_CHANGED) { float x = 0.0f, y = 0.0f; message->FindFloat("be:wheel_delta_x", &x); message->FindFloat("be:wheel_delta_y", &y); d->View->OnMouseWheel(y * 3.0f); } else if (message->what == M_SET_SCROLL) { return; } Parent::MessageReceived(message); } void Draw(BRect updateRect) { if (!d) return; LScreenDC dc(d->View); LPoint off(0,0); d->View->_Paint(&dc, &off, NULL); } LMouse ConvertMouse(BPoint where, bool down = false) { LMouse m; BPoint loc; uint32 buttons = 0; m.Target = d->View; m.x = where.x; m.y = where.y; if (down) { m.Down(true); Parent::GetMouse(&loc, &buttons, false); MouseButtons = buttons; // save for up click } else { buttons = MouseButtons; } if (buttons & B_PRIMARY_MOUSE_BUTTON) m.Left(true); if (buttons & B_TERTIARY_MOUSE_BUTTON) m.Middle(true); if (buttons & B_SECONDARY_MOUSE_BUTTON) m.Right(true); uint32 mod = modifiers(); if (mod & B_SHIFT_KEY) m.Shift(true); if (mod & B_OPTION_KEY) m.Alt(true); if (mod & B_CONTROL_KEY) m.Ctrl(true); if (mod & B_COMMAND_KEY) m.System(true); return m; } void MouseDown(BPoint where) { if (!d) return; static uint64_t lastClick = 0; bigtime_t interval = 0; status_t r = get_click_speed(&interval); auto now = LCurrentTime(); bool doubleClick = now-lastClick < (interval/1000); lastClick = now; LMouse m = ConvertMouse(where, true); m.Double(doubleClick); d->View->_Mouse(m, false); } void MouseUp(BPoint where) { if (!d) return; LMouse m = ConvertMouse(where); m.Down(false); d->View->_Mouse(m, false); } void MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage) { if (!d) return; LMouse m = ConvertMouse(where); m.Down( m.Left() || m.Middle() || m.Right()); m.IsMove(true); d->View->_Mouse(m, true); } void MakeFocus(bool focus=true) { if (!d) return; Parent::MakeFocus(focus); d->View->OnFocus(focus); } }; template uint32 LBView::MouseButtons = 0; //////////////////////////////////////////////////////////////////////////////////////////////////// LViewPrivate::LViewPrivate(LView *view) : View(view), Hnd(new LBView(this)) { } LViewPrivate::~LViewPrivate() { View->d = NULL; if (Font && FontOwnType == GV_FontOwned) DeleteObj(Font); if (Hnd) { auto *bv = dynamic_cast*>(Hnd); // printf("%p::~LViewPrivate View=%p bv=%p th=%u\n", this, View, bv, GetCurrentThreadId()); if (bv) bv->d = NULL; auto Wnd = Hnd->Window(); if (Wnd) Wnd->LockLooper(); if (Hnd->Parent()) Hnd->RemoveSelf(); DeleteObj(Hnd); if (Wnd) Wnd->UnlockLooper(); } } void LView::_Focus(bool f) { ThreadCheck(); if (f) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); LLocker lck(d->Hnd, _FL); if (lck.Lock()) { d->Hnd->MakeFocus(f); lck.Unlock(); } // OnFocus will be called by the LBview handler... Invalidate(); } void LView::_Delete() { SetPulse(); // Remove static references to myself if (_Over == this) _Over = 0; if (_Capturing == this) _Capturing = 0; auto *Wnd = GetWindow(); if (Wnd && Wnd->GetFocus() == static_cast(this)) Wnd->SetFocus(this, LWindow::ViewDelete); if (LAppInst && LAppInst->AppWnd == this) { LAppInst->AppWnd = 0; } // Hierarchy LViewI *c; while ((c = Children[0])) { if (c->GetParent() != (LViewI*)this) { printf("%s:%i - ~LView error, %s not attached correctly: %p(%s) Parent: %p(%s)\n", _FL, c->GetClass(), c, c->Name(), c->GetParent(), c->GetParent() ? c->GetParent()->Name() : ""); Children.Delete(c); } DeleteObj(c); } Detach(); // Misc Pos.ZOff(-1, -1); } LView *&LView::PopupChild() { return d->Popup; } BCursorID LgiToHaiku(LCursor c) { switch (c) { #define _(l,h) case l: return h; _(LCUR_Blank, B_CURSOR_ID_NO_CURSOR) _(LCUR_Normal, B_CURSOR_ID_SYSTEM_DEFAULT) _(LCUR_UpArrow, B_CURSOR_ID_RESIZE_NORTH) _(LCUR_DownArrow, B_CURSOR_ID_RESIZE_SOUTH) _(LCUR_LeftArrow, B_CURSOR_ID_RESIZE_WEST) _(LCUR_RightArrow, B_CURSOR_ID_RESIZE_EAST) _(LCUR_Cross, B_CURSOR_ID_CROSS_HAIR) _(LCUR_Wait, B_CURSOR_ID_PROGRESS) _(LCUR_Ibeam, B_CURSOR_ID_I_BEAM) _(LCUR_SizeVer, B_CURSOR_ID_RESIZE_NORTH_SOUTH) _(LCUR_SizeHor, B_CURSOR_ID_RESIZE_EAST_WEST) _(LCUR_SizeBDiag, B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST) _(LCUR_SizeFDiag, B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST) _(LCUR_PointingHand, B_CURSOR_ID_GRAB) _(LCUR_Forbidden, B_CURSOR_ID_NOT_ALLOWED) _(LCUR_DropCopy, B_CURSOR_ID_COPY) _(LCUR_DropMove, B_CURSOR_ID_MOVE) // _(LCUR_SizeAll, // _(LCUR_SplitV, // _(LCUR_SplitH, #undef _ } return B_CURSOR_ID_SYSTEM_DEFAULT; } bool LView::_Mouse(LMouse &m, bool Move) { ThreadCheck(); #if DEBUG_MOUSE_EVENTS if (!Move) LgiTrace("%s:%i - %s\n", _FL, m.ToString().Get()); #endif if ( GetWindow() && !GetWindow()->HandleViewMouse(this, m) ) { #if DEBUG_MOUSE_EVENTS LgiTrace("%s:%i - HandleViewMouse consumed event, cls=%s\n", _FL, GetClass()); #endif return false; } #if 0 //DEBUG_MOUSE_EVENTS if (!Move) LgiTrace("%s:%i - _Capturing=%p/%s\n", _FL, _Capturing, _Capturing ? _Capturing->GetClass() : NULL); #endif if (Move) { auto *o = m.Target; if (_Over != o) { #if DEBUG_MOUSE_EVENTS // if (!o) WindowFromPoint(m.x, m.y, true); LgiTrace("%s:%i - _Over changing from %p/%s to %p/%s\n", _FL, _Over, _Over ? _Over->GetClass() : NULL, o, o ? o->GetClass() : NULL); #endif if (_Over) _Over->OnMouseExit(lgi_adjust_click(m, _Over)); _Over = o; if (_Over) _Over->OnMouseEnter(lgi_adjust_click(m, _Over)); } int cursor = GetCursor(m.x, m.y); if (cursor >= 0) { BCursorID haikuId = LgiToHaiku((LCursor)cursor); static BCursorID curId = B_CURSOR_ID_SYSTEM_DEFAULT; if (curId != haikuId) { curId = haikuId; LLocker lck(Handle(), _FL); if (lck.Lock()) { Handle()->SetViewCursor(new BCursor(curId)); lck.Unlock(); } } } } LView *Target = NULL; if (_Capturing) Target = dynamic_cast(_Capturing); else Target = dynamic_cast(_Over ? _Over : this); if (!Target) return false; LRect Client = Target->LView::GetClient(false); m = lgi_adjust_click(m, Target, !Move); if (!Client.Valid() || Client.Overlap(m.x, m.y) || _Capturing) { if (Move) Target->OnMouseMove(m); else Target->OnMouseClick(m); } else if (!Move) { #if DEBUG_MOUSE_EVENTS LgiTrace("%s:%i - Click outside %s %s %i,%i\n", _FL, Target->GetClass(), Client.GetStr(), m.x, m.y); #endif } return true; } LRect &LView::GetClient(bool ClientSpace) { int Edge = (Sunken() || Raised()) ? _BorderSize : 0; static LRect c; if (ClientSpace) { c.ZOff(Pos.X() - 1 - (Edge<<1), Pos.Y() - 1 - (Edge<<1)); } else { c.ZOff(Pos.X()-1, Pos.Y()-1); c.Inset(Edge, Edge); } return c; } LViewI *LView::FindControl(OsView hCtrl) { if (d->Hnd == hCtrl) { return this; } for (auto i : Children) { LViewI *Ctrl = i->FindControl(hCtrl); if (Ctrl) { return Ctrl; } } return 0; } void LView::Quit(bool DontDelete) { ThreadCheck(); if (DontDelete) { Visible(false); } else { delete this; } } bool LView::SetPos(LRect &p, bool Repaint) { if (Pos != p) { Pos = p; LLocker lck(d->Hnd, _FL); if (lck.Lock()) { d->Hnd->ResizeTo(Pos.X(), Pos.Y()); d->Hnd->MoveTo(Pos.x1, Pos.y1); lck.Unlock(); } OnPosChange(); } return true; } bool LView::Invalidate(LRect *rc, bool Repaint, bool Frame) { auto *ParWnd = GetWindow(); if (!ParWnd) return false; // Nothing we can do till we attach if (!InThread()) { DEBUG_INVALIDATE("%s::Invalidate out of thread\n", GetClass()); return PostEvent(M_INVALIDATE, NULL, (LMessage::Param)this); } LRect r; if (rc) { r = *rc; } else { if (Frame) r = Pos.ZeroTranslate(); else r = GetClient().ZeroTranslate(); } DEBUG_INVALIDATE("%s::Invalidate r=%s frame=%i\n", GetClass(), r.GetStr(), Frame); if (!Frame) r.Offset(_BorderSize, _BorderSize); LPoint Offset; WindowVirtualOffset(&Offset); r.Offset(Offset.x, Offset.y); DEBUG_INVALIDATE(" voffset=%i,%i = %s\n", Offset.x, Offset.y, r.GetStr()); if (!r.Valid()) { DEBUG_INVALIDATE(" error: invalid\n"); return false; } static bool Repainting = false; if (!Repainting) { Repainting = true; if (d->Hnd) { LLocker lck(d->Hnd, _FL); if (lck.Lock()) d->Hnd->Invalidate(); } Repainting = false; } else { DEBUG_INVALIDATE(" error: repainting\n"); } return true; } void LView::SetPulse(int Length) { DeleteObj(d->PulseThread); if (Length > 0) d->PulseThread = new LPulseThread(this, Length); } LMessage::Param LView::OnEvent(LMessage *Msg) { ThreadCheck(); int Id; switch (Id = Msg->Msg()) { case M_HANDLE_IN_THREAD: { LMessage::InThreadCb *Cb = NULL; if (Msg->FindPointer(LMessage::PropCallback, (void**)&Cb) == B_OK) { + // printf("M_HANDLE_IN_THREAD before call..\n"); (*Cb)(); + // printf("M_HANDLE_IN_THREAD after call..\n"); delete Cb; + // printf("M_HANDLE_IN_THREAD after delete..\n"); } else printf("%s:%i - No Callback.\n", _FL); break; } case M_INVALIDATE: { if ((LView*)this == (LView*)Msg->B()) { LAutoPtr r((LRect*)Msg->A()); Invalidate(r); } break; } case M_PULSE: { OnPulse(); break; } case M_CHANGE: { LViewI *Ctrl = NULL; if (GetViewById(Msg->A(), Ctrl)) { LNotification n((LNotifyType)Msg->B()); return OnNotify(Ctrl, n); } break; } case M_COMMAND: { // printf("M_COMMAND %i\n", (int)Msg->A()); return OnCommand(Msg->A(), 0, 0); } case M_THREAD_COMPLETED: { auto Th = (LThread*)Msg->A(); if (!Th) break; Th->OnComplete(); if (Th->GetDeleteOnExit()) delete Th; return true; } } return 0; } OsView LView::Handle() const { if (!d) { printf("%s:%i - No priv?\n", _FL); return NULL; } return d->Hnd; } bool LView::PointToScreen(LPoint &p) { if (!Handle()) { LgiTrace("%s:%i - No handle.\n", _FL); return false; } LPoint Offset; WindowVirtualOffset(&Offset); // printf("p=%i,%i offset=%i,%i\n", p.x, p.y, Offset.x, Offset.y); p += Offset; // printf("p=%i,%i\n", p.x, p.y); LLocker lck(Handle(), _FL); if (!lck.Lock()) { LgiTrace("%s:%i - Can't lock.\n", _FL); return false; } BPoint pt = Handle()->ConvertToScreen(BPoint(p.x, p.y)); // printf("pt=%g,%g\n", pt.x, pt.y); p.x = pt.x; p.y = pt.y; // printf("p=%i,%i\n\n", p.x, p.y); return true; } bool LView::PointToView(LPoint &p) { if (!Handle()) { LgiTrace("%s:%i - No handle.\n", _FL); return false; } LPoint Offset; WindowVirtualOffset(&Offset); p -= Offset; LLocker lck(Handle(), _FL); if (!lck.Lock()) { LgiTrace("%s:%i - Can't lock.\n", _FL); return false; } BPoint pt = Handle()->ConvertFromScreen(BPoint(Offset.x, Offset.y)); Offset.x = pt.x; Offset.y = pt.y; return true; } bool LView::GetMouse(LMouse &m, bool ScreenCoords) { LLocker Locker(d->Hnd, _FL); if (!Locker.Lock()) return false; // get mouse state BPoint Cursor; uint32 Buttons; d->Hnd->GetMouse(&Cursor, &Buttons); if (ScreenCoords) d->Hnd->ConvertToScreen(&Cursor); // position m.x = Cursor.x; m.y = Cursor.y; // buttons m.Left(TestFlag(Buttons, B_PRIMARY_MOUSE_BUTTON)); m.Middle(TestFlag(Buttons, B_TERTIARY_MOUSE_BUTTON)); m.Right(TestFlag(Buttons, B_SECONDARY_MOUSE_BUTTON)); // key states key_info KeyInfo; if (get_key_info(&KeyInfo) == B_OK) { m.Ctrl(TestFlag(KeyInfo.modifiers, B_CONTROL_KEY)); m.Alt(TestFlag(KeyInfo.modifiers, B_MENU_KEY)); m.Shift(TestFlag(KeyInfo.modifiers, B_SHIFT_KEY)); } return true; } bool LView::IsAttached() { bool attached = false; LLocker lck(d->Hnd, _FL); if (lck.Lock()) { auto pview = d->Hnd->Parent(); auto pwnd = d->Hnd->Window(); attached = pview != NULL || pwnd != NULL; } return attached; } const char *LView::GetClass() { return "LView"; } bool LView::Attach(LViewI *parent) { bool Status = false; LView *Parent = d->GetParent(); LAssert(Parent == NULL || Parent == parent); SetParent(parent); Parent = d->GetParent(); auto WndNull = _Window == NULL; _Window = Parent ? Parent->_Window : this; if (!parent) { LgiTrace("%s:%i - No parent window.\n", _FL); } else { auto w = GetWindow(); if (w && TestFlag(WndFlags, GWF_FOCUS)) w->SetFocus(this, LWindow::GainFocus); auto bview = parent->Handle(); if (bview) { LLocker lck(bview, _FL); if (lck.Lock()) { #if 0 LgiTrace("%s:%i - Attaching %s to view %s\n", _FL, GetClass(), parent->GetClass()); #endif d->Hnd->SetName(GetClass()); if (::IsAttached(d->Hnd)) d->Hnd->RemoveSelf(); bview->AddChild(d->Hnd); d->Hnd->ResizeTo(Pos.X(), Pos.Y()); d->Hnd->MoveTo(Pos.x1, Pos.y1); if (TestFlag(GViewFlags, GWF_VISIBLE) && d->Hnd->IsHidden()) d->Hnd->Show(); Status = true; #if 0 LgiTrace("%s:%i - Attached %s/%p to view %s/%p, success\n", _FL, GetClass(), d->Hnd, parent->GetClass(), bview); #endif } else { #if 0 LgiTrace("%s:%i - Error attaching %s to view %s, can't lock.\n", _FL, GetClass(), parent->GetClass()); #endif } } if (!Parent->HasView(this)) { OnAttach(); if (!d->Parent->HasView(this)) d->Parent->AddView(this); d->Parent->OnChildrenChanged(this, true); } } return Status; } bool LView::Detach() { ThreadCheck(); // Detach view if (_Window) { auto *Wnd = dynamic_cast(_Window); if (Wnd) Wnd->SetFocus(this, LWindow::ViewDelete); _Window = NULL; } LViewI *Par = GetParent(); if (Par) { // Events Par->DelView(this); Par->OnChildrenChanged(this, false); Par->Invalidate(&Pos); } d->Parent = 0; d->ParentI = 0; #if 0 // Windows is not doing a deep detach... so we shouldn't either? { int Count = Children.Length(); if (Count) { int Detached = 0; LViewI *c, *prev = NULL; while ((c = Children[0])) { LAssert(!prev || c != prev); if (c->GetParent()) c->Detach(); else Children.Delete(c); Detached++; prev = c; } LAssert(Count == Detached); } } #endif return true; } LCursor LView::GetCursor(int x, int y) { return LCUR_Normal; } /////////////////////////////////////////////////////////////////// bool LgiIsMounted(char *Name) { return false; } bool LgiMountVolume(char *Name) { return false; } //////////////////////////////// uint32_t CursorData[] = { 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x02010101, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x01020202, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x02020202, 0x02020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x01000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x01020202, 0x02020201, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010102, 0x00000000, 0x02020101, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010001, 0x00010001, 0x01000001, 0x02020202, 0x02020202, 0x00010102, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01000102, 0x00000100, 0x02010000, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020100, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00010101, 0x01000000, 0x02020202, 0x00000001, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00000102, 0x01010100, 0x01010101, 0x01000000, 0x02020202, 0x02020201, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x01010102, 0x01010001, 0x02020101, 0x02020202, 0x02020202, 0x01020201, 0x02010000, 0x02020102, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x01000001, 0x02020101, 0x02020202, 0x02020202, 0x00010202, 0x01010000, 0x01020202, 0x00000001, 0x02020201, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01000001, 0x01010101, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x02020101, 0x00000102, 0x00000100, 0x02020201, 0x02020202, 0x01000001, 0x01000000, 0x01020202, 0x02020201, 0x02020202, 0x02020202, 0x02020201, 0x02010001, 0x02010202, 0x02020202, 0x01020202, 0x01020201, 0x02010000, 0x02010102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x01010000, 0x02020101, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x00000102, 0x02020100, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x02010001, 0x00000001, 0x00010201, 0x02020201, 0x02020202, 0x02010001, 0x00000001, 0x00010201, 0x02020201, 0x02020202, 0x01020202, 0x02020201, 0x02010001, 0x01010202, 0x02020202, 0x00010202, 0x01020201, 0x02010000, 0x01000102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02010102, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x00000102, 0x00000001, 0x02020201, 0x00010202, 0x02020100, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01010100, 0x01010101, 0x01000000, 0x02020202, 0x01000001, 0x01000000, 0x01020202, 0x02020201, 0x02020202, 0x02020101, 0x00000102, 0x00000100, 0x02020201, 0x02020202, 0x00010202, 0x02020201, 0x02010001, 0x00010202, 0x02020201, 0x00000102, 0x01010101, 0x01010000, 0x00000101, 0x02020201, 0x01010101, 0x01010101, 0x01010100, 0x01010101, 0x02020201, 0x01000001, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x00000001, 0x00000101, 0x02020100, 0x00010202, 0x02010000, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01010102, 0x01010101, 0x01010001, 0x01010101, 0x02020101, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020100, 0x01020202, 0x02010000, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020201, 0x02020202, 0x02020201, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x01010101, 0x01010001, 0x00010101, 0x02020100, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020100, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x00000001, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x00010202, 0x02010000, 0x01020202, 0x02010000, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x01020202, 0x02020100, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02010000, 0x00000102, 0x01010101, 0x01010000, 0x00000101, 0x02020201, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x00000102, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x01020202, 0x01000000, 0x01020202, 0x02010000, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x01010102, 0x01010101, 0x01010001, 0x01010101, 0x02020101, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x01020202, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x01010101, 0x01010001, 0x00010101, 0x02020100, 0x00010202, 0x01020201, 0x02010000, 0x01000102, 0x02020202, 0x01010101, 0x01010101, 0x01010100, 0x01010101, 0x02020201, 0x00010202, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x00000001, 0x01020201, 0x02010000, 0x00000001, 0x01000000, 0x02010101, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02010101, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x01000001, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01000001, 0x01010101, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x01020202, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00010202, 0x02020201, 0x02010001, 0x00010202, 0x02020201, 0x01020202, 0x01020201, 0x02010000, 0x02010102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x00000001, 0x02020201, 0x00000102, 0x00010100, 0x02010000, 0x00000001, 0x01000001, 0x01020202, 0x01010101, 0x01010101, 0x00000001, 0x01000001, 0x01020202, 0x01010101, 0x01010101, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01000102, 0x01000001, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x01020202, 0x02020201, 0x02010001, 0x01010202, 0x02020202, 0x02020202, 0x01020201, 0x02010000, 0x02020102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x00000102, 0x02020201, 0x00010202, 0x00010000, 0x02020100, 0x01000001, 0x01000001, 0x01020202, 0x00000000, 0x01000000, 0x01000001, 0x01000001, 0x01020202, 0x00000000, 0x01000000, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010001, 0x00000000, 0x01000100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02010001, 0x02010202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x02020100, 0x01020202, 0x00000000, 0x02020100, 0x02010001, 0x00000102, 0x01020201, 0x01010000, 0x01000001, 0x02010001, 0x00000102, 0x01020201, 0x01010100, 0x01000001, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01010102, 0x01010001, 0x02020101, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01010202, 0x01010101, 0x02020202, 0x02020202, 0x00010202, 0x01010000, 0x01020202, 0x00000001, 0x02020201, 0x02020101, 0x00000102, 0x01020201, 0x00000100, 0x01000100, 0x02020101, 0x00000102, 0x01020201, 0x01000100, 0x01000100, 0x01020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x00010102, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00010101, 0x01000000, 0x02020202, 0x02020202, 0x00010202, 0x01020100, 0x00000100, 0x01000000, 0x02020202, 0x00010202, 0x01020100, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00010202, 0x01020100, 0x00000100, 0x01000100, 0x02020202, 0x00010202, 0x01020100, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010101, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010102, 0x00000000, 0x02020101, 0x02020202, 0x02020202, 0x01020202, 0x01020201, 0x01010000, 0x01000001, 0x02020202, 0x01020202, 0x01020201, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x01010101, }; diff --git a/src/haiku/Widgets.cpp b/src/haiku/Widgets.cpp --- a/src/haiku/Widgets.cpp +++ b/src/haiku/Widgets.cpp @@ -1,270 +1,269 @@ /*hdr ** FILE: GuiDlg.cpp ** AUTHOR: Matthew Allen ** DATE: 8/9/1998 ** DESCRIPTION: Dialog components ** ** Copyright (C) 1998 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 { int ModalStatus; int BtnId; bool IsModal, IsModeless; bool Resizable; LDialog::OnClose ModalCb; thread_id CallingThread = NULL; LDialogPriv() { IsModal = false; IsModeless = false; Resizable = true; ModalStatus = 0; BtnId = -1; } }; /////////////////////////////////////////////////////////////////////////////////////////// LDialog::LDialog(LViewI *parent) : #ifdef __GTK_H__ // , LWindow(gtk_dialog_new()) LWindow(gtk_window_new(GTK_WINDOW_TOPLEVEL)), #endif ResObject(Res_Dialog) { d = new LDialogPriv(); Name("Dialog"); if (parent) SetParent(parent); } LDialog::~LDialog() { DeleteObj(d); } bool LDialog::IsModal() { return d->IsModal; } int LDialog::GetButtonId() { return d->BtnId; } int LDialog::OnNotify(LViewI *Ctrl, LNotification n) { LButton *b = dynamic_cast(Ctrl); if (b) { d->BtnId = b->GetId(); if (d->IsModal) EndModal(); else if (d->IsModeless) EndModeless(); } return 0; } void LDialog::Quit(bool DontDelete) { if (d->IsModal) EndModal(0); else LView::Quit(DontDelete); } void LDialog::OnPosChange() { LWindow::OnPosChange(); if (Children.Length() == 1) { List::I it = Children.begin(); LTableLayout *t = dynamic_cast((LViewI*)it); if (t) { LRect r = GetClient(); r.Inset(LTableLayout::CellSpacing, LTableLayout::CellSpacing); t->SetPos(r); // _Dump(); } } } bool LDialog::LoadFromResource(int Resource, char *TagList) { LAutoString n; LRect p; LProfile Prof("LDialog::LoadFromResource"); bool Status = LResourceLoad::LoadFromResource(Resource, this, &p, &n, TagList); if (Status) { Prof.Add("Name."); Name(n); SetPos(p); } return Status; } bool LDialog::OnRequestClose(bool OsClose) { if (d->IsModal) { EndModal(0); return false; } return true; } void LDialog::DoModal(OnClose Cb, OsView OverrideParent) { d->ModalStatus = -1; auto Parent = GetParent(); if (Parent) MoveSameScreen(Parent); d->IsModal = true; d->IsModeless = false; d->ModalCb = Cb; d->CallingThread = find_thread(NULL); BLooper *looper = BLooper::LooperForThread(d->CallingThread); if (!looper) printf("%s:%i - no looper for domodal thread.\n",_FL); if (Attach(0)) { AttachChildren(); Visible(true); } else printf("%s:%i - attach failed..\n", _FL); } void LDialog::EndModal(int Code) { if (!d->IsModal) { LgiTrace("%s:%i - EndModal error: LDialog is not model.\n", _FL); return; } d->IsModal = false; if (!d->ModalCb) { // If no callback is supplied, the default option is to just delete the // dialog, closing it. delete this; return; } BLooper *looper = BLooper::LooperForThread(d->CallingThread); if (!looper) { LgiTrace("%s:%i - Failed to get looper for %p\n", _FL, d->CallingThread); delete this; return; } BMessage *m = new BMessage(M_HANDLE_IN_THREAD); m->AddPointer ( LMessage::PropCallback, new LMessage::InThreadCb ( [dlg=this,cb=d->ModalCb,code=Code]() { - // printf("%s:%i - Calling LDialog callback.. in original thread?\n", _FL); + // printf("%s:%i - Calling LDialog callback.. in original thread\n", _FL); cb(dlg, code); // printf("%s:%i - Calling LDialog callback.. done\n", _FL); } ) ); - // printf("%s:%i - Posting M_HANDLE_IN_THREAD.\n", _FL); looper->PostMessage(m); } int LDialog::DoModeless() { d->IsModal = false; d->IsModeless = true; Visible(true); return 0; } void LDialog::EndModeless(int Code) { Quit(Code); } extern LButton *FindDefault(LView *w); LMessage::Param LDialog::OnEvent(LMessage *Msg) { return LView::OnEvent(Msg); } /////////////////////////////////////////////////////////////////////////////////////////// LControl::LControl(OsView view) : LView(view) { Pos.ZOff(10, 10); } LControl::~LControl() { } LMessage::Param LControl::OnEvent(LMessage *Msg) { return 0; } LPoint LControl::SizeOfStr(const char *Str) { int y = LSysFont->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'); auto Len = e ? e - s : strlen(s); LDisplayString ds(LSysFont, s, Len); Pt.x = MAX(Pt.x, ds.X()); Pt.y += y; } } return Pt; }