diff --git a/Ide/Code/DocEditStyling.cpp b/Ide/Code/DocEditStyling.cpp --- a/Ide/Code/DocEditStyling.cpp +++ b/Ide/Code/DocEditStyling.cpp @@ -1,1248 +1,1248 @@ #include "lgi/common/Lgi.h" #include "LgiIde.h" #include "DocEdit.h" #define COMP_STYLE 1 #define LOG_STYLE 0 #define PROFILE_STYLE 0 #if PROFILE_STYLE #define PROF(str) Prof.Add(str) #else #define PROF(str) #endif #define ColourComment LColour(0, 140, 0) #define ColourHashDef LColour(0, 0, 222) #define ColourLiteral LColour(192, 0, 0) #define ColourKeyword LColour::Black #define ColourType LColour(0, 0, 222) #define ColourCode LColour(140, 140, 180) #define ColourHtml LColour(80, 80, 255) #define ColourPre LColour(150, 110, 110) #define ColourStyle LColour(110, 110, 150) #define IsSymbolChar(ch) (IsAlpha(ch) || (ch) == '_') #define DetectKeyword() \ Node *n = &Root; \ char16 *e = s; \ do \ { \ int idx = n->Map(*e); \ if (idx < 0) \ break; \ n = n->Next[idx]; \ e++; \ } \ while (n); struct LanguageParams { const char **Keywords; const char **Types; const char **Edges; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CPP const char *DefaultKeywords[] = {"if", "elseif", "endif", "else", "ifeq", "ifdef", "ifndef", "ifneq", "include", NULL}; const char *CppKeywords[] = {"extern", "class", "struct", "static", "default", "case", "break", "switch", "new", "delete", "sizeof", "return", "enum", "else", "if", "for", "while", "do", "continue", "public", "private", "virtual", "protected", "friend", "union", "template", "typedef", "dynamic_cast", "inline", NULL}; const char *CppTypes[] = { "int", "char", "short", "long", "signed", "unsigned", "double", "float", "bool", "const", "void", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "char16", "wchar_t", "LArray", "GHashTbl", "List", "LString", "LAutoString", "LAutoWString", "LAutoPtr", "LHashTbl", NULL}; const char *CppEdges[] = { "/*", "*/", "\"", NULL }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Python const char *PythonKeywords[] = {"def", "try", "except", "import", "if", "for", "elif", "else", "class", NULL}; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // XML /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HTML const char *HtmlEdges[] = { "", "/*", "*/", "", "", "\"", "\'", NULL }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// LanguageParams LangParam[] = { // Unknown {NULL, NULL, NULL}, // Plain text {DefaultKeywords, NULL, NULL}, // C/C++ {CppKeywords, CppTypes, CppEdges}, // Python {PythonKeywords, NULL, NULL}, // Xml {NULL, NULL, NULL}, // Html/Php {NULL, NULL, HtmlEdges} }; DocEditStyling::DocEditStyling(DocEdit *view) : LThread("DocEditStyling.Thread"), LMutex("DocEditStyling.Lock"), View(view), Event("DocEditStyling.Event"), ParentState(KWaiting), WorkerState(KWaiting), Params(view) { } void DocEdit::OnApplyStyles() { #if PROFILE_STYLE LProfile Prof("OnApplyStyles"); #endif PROF("Lock"); LTextView3::LStyle Vis(STYLE_NONE); GetVisible(Vis); if (DocEditStyling::Lock(_FL)) { PROF("Insert"); if (Params.Styles.Length()) { Style.Swap(Params.Styles); #if LOG_STYLE LgiTrace("Swapped in %i styles.\n", (int)Style.Length()); #endif PROF("Inval"); if (Params.Dirty.Start >= 0) { #if LOG_STYLE LgiTrace("Visible rgn: %i + %i = %i\n", Vis.Start, Vis.Len, Vis.End()); LgiTrace("Dirty rgn: %i + %i = %i\n", Params.Dirty.Start, Params.Dirty.Len, Params.Dirty.End()); #endif ssize_t CurLine = -1, DirtyStartLine = -1, DirtyEndLine = -1; GetTextLine(Cursor, &CurLine); LTextLine *Start = GetTextLine(Params.Dirty.Start, &DirtyStartLine); LTextLine *End = GetTextLine(MIN(Size, Params.Dirty.End()), &DirtyEndLine); if (CurLine >= 0 && DirtyStartLine >= 0 && DirtyEndLine >= 0) { #if LOG_STYLE LgiTrace("Dirty lines %i, %i, %i\n", CurLine, DirtyStartLine, DirtyEndLine); #endif if (DirtyStartLine != CurLine || DirtyEndLine != CurLine) { LRect c = GetClient(); LRect r(c.x1, Start->r.Valid() ? DocToScreen(Start->r).y1 : c.y1, c.x2, Params.Dirty.End() >= Vis.End() ? c.y2 : DocToScreen(End->r).y2); #if LOG_STYLE LgiTrace("Cli: %s, Start rgn: %s, End rgn: %s, Update: %s\n", c.GetStr(), Start->r.GetStr(), End->r.GetStr(), r.GetStr()); #endif Invalidate(&r); } } else { #if LOG_STYLE LgiTrace("No Change: %i, %i, %i\n", CurLine, DirtyStartLine, DirtyEndLine); #endif } } else { #if LOG_STYLE LgiTrace("Invalidate everything\n"); #endif Invalidate(); } } PROF("Unlock"); DocEditStyling::Unlock(); } } int DocEditStyling::Main() { LThreadEvent::WaitStatus s; while (ParentState != KExiting && (s = Event.Wait()) == LThreadEvent::WaitSignaled) { StylingParams p(View); if (Lock(_FL)) { Params.Dirty.Empty(); Params.Styles.Empty(); p = Params; Unlock(); } WorkerState = KStyling; #if LOG_STYLE LgiTrace("DocEdit.Worker starting style...\n"); #endif switch (FileType) { case SrcCpp: StyleCpp(p); break; case SrcPython: StylePython(p); break; case SrcXml: StyleXml(p); break; case SrcHtml: StyleHtml(p); break; default: StyleDefault(p); break; } if (LMutex::Lock(_FL)) { Params.Dirty = p.Dirty; Params.Styles.Swap(p.Styles); LMutex::Unlock(); } WorkerState = KWaiting; if (ParentState != KCancel && ParentState != KExiting) { #if LOG_STYLE LgiTrace("DocEdit.Worker finished style... Items=%i ParentState=%i\n", (int)p.Styles.Length(), ParentState); #endif View->PostEvent(M_STYLING_DONE); } } return 0; } void DocEditStyling::StyleCpp(StylingParams &p) { #if PROFILE_STYLE LProfile Prof("DocEdit::StyleCpp"); #endif if (!p.Text.Length()) return; char16 *Text = p.Text.AddressOf(); char16 *s = Text; char16 *e = s + p.Text.Length(); PROF("Scan"); LUnrolledList Out; for (; ParentState != KCancel && s < e; s++) { switch (*s) { case '\"': case '\'': p.StyleString(s, e, ColourLiteral, &Out); break; case '#': { // Check that the '#' is the first non-whitespace on the line bool IsWhite = true; for (char16 *w = s - 1; w >= Text && *w != '\n'; w--) { if (!IsWhiteSpace(*w)) { IsWhite = false; break; } } if (IsWhite) { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); char LastNonWhite = 0; while (s < e) { if ( // Break at end of line (*s == '\n' && LastNonWhite != '\\') || // Or the start of a comment (*s == '/' && s[1] == '/') || (*s == '/' && s[1] == '*') ) break; if (!IsWhiteSpace(*s)) LastNonWhite = *s; s++; } st.Len = (s - Text) - st.Start; st.Fore = ColourHashDef; s--; } break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } case '/': { if (s[1] == '/') { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); while (s < e && *s != '\n') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; } else if (s[1] == '*') { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); s += 2; while (s < e && !(s[-2] == '*' && s[-1] == '/')) s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; } break; } default: { wchar_t Ch = ToLower(*s); if (Ch >= 'a' && Ch <= 'z') { DetectKeyword(); if (n && n->Type) { auto &st = Out.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; #ifdef MAC #warning "Weird mac bold issues fixme.") st.Font = View->GetFont(); #else st.Font = n->Type == KType ? View->GetFont() : View->GetBold(); #endif st.Len = e - s; st.Fore = n->Type == KType ? ColourType : ColourKeyword; } else { while ( IsAlpha(*e) || IsDigit(*e) || *e == '_') e++; } s = e - 1; } } } } #if COMP_STYLE PROF("Compare"); auto &Vis = p.Visible; if (Vis.Valid() && ParentState != KCancel && PrevStyle.Length()) { LArray Old, Cur; for (auto s : PrevStyle) { if (s.Overlap(Vis)) Old.Add(&s); else if (Old.Length()) break; } for (auto s : Out) { if (s.Overlap(Vis)) Cur.Add(&s); else if (Cur.Length()) break; } for (int o=0; oGetFont(); while (s < e && *s != '\n') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } default: { wchar_t Ch = ToLower(*s); if (Ch >= 'a' && Ch <= 'z') { DetectKeyword(); if (n && n->Type) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = n->Type == KType ? View->GetFont() : View->GetBold(); st.Len = e - s; st.Fore = n->Type == KType ? ColourType : ColourKeyword; } s = e - 1; } break; } } } } void DocEditStyling::StyleDefault(StylingParams &p) { char16 *Text = p.Text.AddressOf(); char16 *e = Text + p.Text.Length(); auto &Style = p.Styles; for (char16 *s = Text; s < e && ParentState < KCancel; s++) { switch (*s) { case '\"': case '\'': case '`': { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); bool Quoted = s > Text && s[-1] == '\\'; st.Start = s - Text - Quoted; st.Font = View->GetFont(); char16 Delim = *s++; while ( s < e && *s != Delim && !(Delim == '`' && *s == '\'') ) { if (*s == '\\') { if (!Quoted || s[1] != Delim) s++; } else if (*s == '\n') break; s++; } st.Len = (s - Text) - st.Start + 1; st.Fore = ColourLiteral; break; } case '#': { // Single line comment auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); while (s < e && *s != '\n') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text - ((s > Text && strchr("-+", s[-1])) ? 1 : 0); st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } if (*s == '%') s++; st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } default: { if (*s >= 'a' && *s <= 'z') { /* if (HasKeyword[*s - 'a']) { static char16 buf[64], *o = buf; char16 *e = s; while (IsSymbolChar(*e)) *o++ = *e++; *o = 0; KeyworkType type = Keywords.Find(buf); if (type != KNone) { LAutoPtr st(new LTextView3::LStyle(STYLE_IDE)); if (st) { st->View = this; st->Start = s - Text; st->Font = Bold; st->Len = e - s; st->Fore = ColourKeyword; InsertStyle(st); s = e - 1; } } } */ } break; } } } } void DocEditStyling::StyleXml(StylingParams &p) { char16 *Text = p.Text.AddressOf(); char16 *e = Text + p.Text.Length(); auto &Style = p.Styles; for (char16 *s = Text; s < e; s++) { switch (*s) { case '\"': case '\'': p.StyleString(s, e, ColourLiteral); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if (s == Text || !IsSymbolChar(s[-1])) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); bool IsHex = false; if (s[0] == '0' && ToLower(s[1]) == 'x') { s += 2; IsHex = true; } while (s < e) { if ( IsDigit(*s) || *s == '.' || ( IsHex && ( (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F') ) ) ) s++; else break; } st.Len = (s - Text) - st.Start; st.Fore = ColourLiteral; s--; } while (s < e - 1 && IsDigit(s[1])) s++; break; } case '<': { if (s[1] == '!' && s[2] == '-' && s[3] == '-') { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); s += 4; while ( s < e && ! ( s[-3] == '-' && s[-2] == '-' && s[-1] == '>' ) ) s++; st.Len = (s - Text) - st.Start; st.Fore = ColourComment; s--; } break; } default: { if (*s >= 'a' && *s <= 'z') { /* if (HasKeyword[*s - 'a']) { static char16 buf[64], *o = buf; char16 *e = s; while (IsSymbolChar(*e)) *o++ = *e++; *o = 0; KeyworkType type = Keywords.Find(buf); if (type != KNone) { LAutoPtr st(new LTextView3::LStyle(STYLE_IDE)); if (st) { st->View = this; st->Start = s - Text; st->Font = Bold; st->Len = e - s; st->Fore = ColourKeyword; InsertStyle(st); s = e - 1; } } } */ } break; } } } } LColour DocEditStyling::ColourFromType(DocType t) { switch (t) { default: return LColour::Black; case CodePhp: case CodeJavascript: return ColourCode; case CodeCss: return ColourStyle; case CodePre: return ColourPre; case CodeComment: return ColourComment; } } void DocEditStyling::StyleHtml(StylingParams &p) { char16 *Text = p.Text.AddressOf(); char16 *e = Text + p.Text.Length(); auto &Style = p.Styles; LString Ext = LGetExtension(p.FileName); DocType Type = CodeHtml; LTextView3::LStyle *Cur = NULL; #define START_CODE() \ if (Type != CodeHtml) \ { \ Cur = &Style.New().Construct(View, LTextView3::STYLE_IDE); \ Cur->Start = s - Text; \ Cur->Font = View->GetFont(); \ Cur->Fore = ColourFromType(Type); \ } #define END_CODE() \ if (Cur) \ { \ Cur->Len = (s - Text) - Cur->Start; \ if (Cur->Len <= 0) \ Style.Delete(Style.rbegin()); \ Cur = NULL; \ } char16 *s = Text; bool IsJavascript = Ext.Equals("js"); bool IsPHP = Ext.Equals("php"); bool IsCSS = Ext.Equals("css"); if (IsJavascript) { Type = CodeJavascript; START_CODE(); } else if (IsPHP) { Type = CodePhp; START_CODE(); } else if (IsCSS) { Type = CodeCss; START_CODE(); } for (s = Text; s < e; s++) { switch (*s) { case '`': { if (Type == CodeJavascript) { END_CODE(); p.StyleString(s, e, ColourLiteral); s++; START_CODE(); s--; } break; } case '\'': { if (s > Text && IsAlpha(s[-1])) break; // else fall through } case '\"': { if (Type != CodeComment) { END_CODE(); p.StyleString(s, e, ColourLiteral); s++; START_CODE(); s--; } break; } case '/': { if (Type == CodeJavascript || Type == CodePhp || Type == CodeCss) { if (s[1] == '/') { END_CODE(); char16 *nl = Strchr(s, '\n'); if (!nl) nl = s + Strlen(s); auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourComment; st.Len = nl - s; s = nl; START_CODE(); } else if (s[1] == '*') { END_CODE(); char16 *end_comment = Stristr(s, L"*/"); if (!end_comment) end_comment = s + Strlen(s); else end_comment += 2; auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourComment; st.Len = end_comment - s; s = end_comment; START_CODE(); } } break; } case '<': { #define IS_TAG(name) \ ((tmp = strlen(#name)) && \ len == tmp && \ !Strnicmp(tag, L ## #name, tmp)) #define SCAN_TAG() \ char16 *tag = s + 1; \ while (tag < e && strchr(WhiteSpace, *tag)) tag++; \ char16 *c = tag; \ while (c < e && (IsAlpha(*c) || strchr("!_/0123456789", *c))) c++; \ size_t len = c - tag, tmp; if (Type == CodeHtml) { if (s[1] == '?' && s[2] == 'p' && s[3] == 'h' && s[4] == 'p') { // Start PHP block Type = CodePhp; START_CODE(); s += 4; } else if ( s[1] == '!' && s[2] == '-' && s[3] == '-') { // Start comment Type = CodeComment; START_CODE(); s += 3; } else { // Html element SCAN_TAG(); bool start = false; if (IS_TAG(pre)) { Type = CodePre; while (*c && *c != '>') c++; if (*c) c++; start = true; } else if (IS_TAG(style)) { Type = CodeCss; while (*c && *c != '>') c++; if (*c) c++; start = true; } else if (IS_TAG(script)) { Type = CodeJavascript; while (c < e && *c != '>') c++; if (c < e) c++; start = true; } auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourHtml; st.Len = c - s; s = c - 1; if (start) { s++; START_CODE(); s--; } } } else if (Type == CodePre) { SCAN_TAG(); if (IS_TAG(/pre)) { END_CODE(); Type = CodeHtml; s--; } } else if (Type == CodeCss) { SCAN_TAG(); if (IS_TAG(/style)) { END_CODE(); Type = CodeHtml; s--; } } break; } case '>': { if (Type == CodeHtml) { auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourHtml; st.Len = 1; } else if (Type == CodeComment) { if (s - 2 >= Text && s[-1] == '-' && s[-2] == '-') { s++; END_CODE(); Type = CodeHtml; } } else if (Type == CodeJavascript) { // Check for if (!Strnicmp(s - 8, L"", 9)) { s -= 8; END_CODE(); auto &st = Style.New().Construct(View, LTextView3::STYLE_IDE); st.Start = s - Text; st.Font = View->GetFont(); st.Fore = ColourHtml; st.Len = 9; s += 9; Type = CodeHtml; } } break; } case '?': { if (Type == CodePhp && s[1] == '>') { Type = CodeHtml; s += 2; END_CODE(); s--; } break; } } } END_CODE(); } void DocEditStyling::AddKeywords(const char **keys, bool IsType) { for (const char **k = keys; *k; k++) { Node *n = &Root; for (const char *c = *k; *c && n; c++) { int idx = n->Map(*c); LAssert(idx >= 0); if (!n->Next[idx]) n->Next[idx] = new Node; n = n->Next[idx]; } if (n) n->Type = IsType ? KType : KLang; } } void DocEdit::PourStyle(size_t Start, ssize_t EditSize) { if (FileType == SrcUnknown) { char *Ext = LGetExtension(Doc->GetFileName()); if (!Ext) FileType = SrcPlainText; else if (!stricmp(Ext, "c") || !stricmp(Ext, "cpp") || !stricmp(Ext, "mm") || !stricmp(Ext, "cc") || !stricmp(Ext, "h") || !stricmp(Ext, "hpp") || !stricmp(Ext, "java") ) FileType = SrcCpp; else if (!stricmp(Ext, "py")) FileType = SrcPython; else if (!stricmp(Ext, "xml")) FileType = SrcXml; else if (!stricmp(Ext, "html") || !stricmp(Ext, "htm") || !stricmp(Ext, "php") || !stricmp(Ext, "js")) FileType = SrcHtml; else FileType = SrcPlainText; if (LangParam[FileType].Keywords) AddKeywords(LangParam[FileType].Keywords, false); if (LangParam[FileType].Types) AddKeywords(LangParam[FileType].Types, true); if (LangParam[FileType].Edges) { RefreshEdges = LangParam[FileType].Edges; RefreshSize = 0; for (const char **e = RefreshEdges; *e; e++) RefreshSize = MAX((int)strlen(*e), RefreshSize); } } // Get into the waiting mode (so the worker is not using 'StyleIn' data) if (WorkerState == KStyling) { #ifdef _DEBUG - uint64 Start = LgiMicroTime(); + auto Start = LMicroTime(); #endif ParentState = KCancel; while (WorkerState != KWaiting) LSleep(1); #ifdef _DEBUG - uint64 Tm = LgiMicroTime() - Start; + auto Tm = LMicroTime() - 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; } diff --git a/Ide/Makefile.linux b/Ide/Makefile.linux --- a/Ide/Makefile.linux +++ b/Ide/Makefile.linux @@ -1,421 +1,422 @@ #!/usr/bin/make # # This makefile generated by LgiIde # http://www.memecode.com/lgi.php # .SILENT : CC = gcc CPP = g++ Target = ./lgiide ifndef Build Build = Debug endif BuildDir = $(Build) Flags = -fPIC -w -fno-inline -fpermissive ifeq ($(Build),Debug) Flags += -g -std=c++14 Tag = d Defs = -D_DEBUG -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ -L../Debug \ -lmagic \ -lpthread \ `pkg-config --libs gtk+-3.0` \ -static-libgcc \ -llgi-gtk3$(Tag) \ -L../$(BuildDir) Inc = \ `pkg-config --cflags gtk+-3.0` \ -I./Resources \ -I./Code \ -I../include/lgi/linux/Gtk \ -I../include/lgi/linux \ -I../include \ -I../../../../codelib/openssl/include else Flags += -s -Os -std=c++14 Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ -L../Release \ -lmagic \ -lpthread \ `pkg-config --libs gtk+-3.0` \ -static-libgcc \ -llgi-gtk3$(Tag) \ -L../$(BuildDir) Inc = \ `pkg-config --cflags gtk+-3.0` \ -I./Resources \ -I./Code \ -I../include/lgi/linux/Gtk \ -I../include/lgi/linux \ -I../include \ -I../../../../codelib/openssl/include endif # Dependencies Source = Code/WebFldDlg.cpp \ Code/SysCharSupport.cpp \ Code/SpaceTabConv.cpp \ Code/SimpleCppParser.cpp \ Code/PythonParser.cpp \ Code/ProjectNode.cpp \ Code/NewProjectFromTemplate.cpp \ Code/MissingFiles.cpp \ Code/MemDumpViewer.cpp \ Code/LgiUtils.cpp \ Code/LgiIde.cpp \ Code/levenshtein.c \ Code/JavascriptParser.cpp \ Code/IdeProjectSettings.cpp \ Code/IdeProject.cpp \ Code/IdeDoc.cpp \ Code/IdeCommon.cpp \ Code/History.cpp \ Code/FtpThread.cpp \ Code/FindSymbol.cpp \ Code/FindInFiles.cpp \ Code/DocEditStyling.cpp \ Code/DocEdit.cpp \ Code/Debugger.cpp \ Code/DebugContext.cpp \ Code/AddFtpFile.cpp \ ../src/common/Text/TextConvert.cpp \ ../src/common/Text/HtmlParser.cpp \ ../src/common/Text/HtmlCommon.cpp \ ../src/common/Text/Html.cpp \ ../src/common/Text/Homoglyphs/HomoglyphsTable.cpp \ ../src/common/Text/Homoglyphs/Homoglyphs.cpp \ ../src/common/Text/DocView.cpp \ ../src/common/Net/OpenSSLSocket.cpp \ ../src/common/Net/Http.cpp \ ../src/common/Net/Ftp.cpp \ ../src/common/Lgi/Mdi.cpp \ ../src/common/Lgi/LgiMain.cpp \ ../src/common/Lgi/About.cpp \ ../src/common/Gdc2/Filters/Png.cpp \ ../src/common/Coding/ParseCpp.cpp \ ../src/common/Coding/LexCpp.cpp SourceLst := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(Source))) Objects := $(addprefix $(BuildDir)/,$(SourceLst)) Deps := $(patsubst %.o,%.d,$(Objects)) # Target # Executable target $(Target) : ../$(BuildDir)/liblgi-gtk3$(Tag).so $(Objects) mkdir -p $(BuildDir) @echo Linking $(Target) [$(Build)]... $(CPP) -Wl,-export-dynamic,-R. -o \ $(Target) $(Objects) $(Libs) @echo Done. ../$(BuildDir)/liblgi-gtk3$(Tag).so : ../include/lgi/common/App.h \ ../include/lgi/common/Array.h \ ../include/lgi/common/AutoPtr.h \ ../include/lgi/common/Base64.h \ ../include/lgi/common/Bitmap.h \ ../include/lgi/common/Box.h \ ../include/lgi/common/Button.h \ ../include/lgi/common/CairoSurface.h \ ../include/lgi/common/Cancel.h \ ../include/lgi/common/Capabilities.h \ ../include/lgi/common/Charset.h \ ../include/lgi/common/CheckBox.h \ ../include/lgi/common/ClipBoard.h \ ../include/lgi/common/Colour.h \ ../include/lgi/common/ColourSpace.h \ ../include/lgi/common/Com.h \ ../include/lgi/common/Combo.h \ ../include/lgi/common/Containers.h \ ../include/lgi/common/Core.h \ ../include/lgi/common/Css.h \ ../include/lgi/common/CssTools.h \ ../include/lgi/common/CurrentTime.h \ ../include/lgi/common/DataDlg.h \ ../include/lgi/common/DateTime.h \ ../include/lgi/common/Dialog.h \ ../include/lgi/common/DisplayString.h \ ../include/lgi/common/DocView.h \ ../include/lgi/common/Dom.h \ ../include/lgi/common/DomFields.h \ ../include/lgi/common/DragAndDrop.h \ ../include/lgi/common/DropFiles.h \ ../include/lgi/common/Edit.h \ ../include/lgi/common/Error.h \ ../include/lgi/common/EventTargetThread.h \ ../include/lgi/common/File.h \ ../include/lgi/common/FileSelect.h \ ../include/lgi/common/Filter.h \ ../include/lgi/common/FindReplaceDlg.h \ ../include/lgi/common/Font.h \ ../include/lgi/common/FontCache.h \ ../include/lgi/common/FontSelect.h \ ../include/lgi/common/Gdc2.h \ ../include/lgi/common/GdcTools.h \ ../include/lgi/common/GdiLeak.h \ ../include/lgi/common/HashTable.h \ ../include/lgi/common/ImageList.h \ ../include/lgi/common/Input.h \ ../include/lgi/common/ItemContainer.h \ ../include/lgi/common/Json.h \ ../include/lgi/common/Layout.h \ ../include/lgi/common/Lgi.h \ ../include/lgi/common/LgiClasses.h \ ../include/lgi/common/LgiCommon.h \ ../include/lgi/common/LgiDefs.h \ ../include/lgi/common/LgiInc.h \ ../include/lgi/common/LgiInterfaces.h \ ../include/lgi/common/LgiMsgs.h \ ../include/lgi/common/LgiNetInc.h \ ../include/lgi/common/LgiRes.h \ ../include/lgi/common/LgiString.h \ ../include/lgi/common/LgiUiBase.h \ ../include/lgi/common/Library.h \ ../include/lgi/common/LibraryUtils.h \ ../include/lgi/common/List.h \ ../include/lgi/common/ListItemCheckBox.h \ ../include/lgi/common/ListItemRadioBtn.h \ ../include/lgi/common/LMallocArray.h \ ../include/lgi/common/Mail.h \ ../include/lgi/common/Matrix.h \ ../include/lgi/common/Mem.h \ ../include/lgi/common/Menu.h \ ../include/lgi/common/Message.h \ ../include/lgi/common/Mime.h \ ../include/lgi/common/Mru.h \ ../include/lgi/common/Mutex.h \ ../include/lgi/common/Net.h \ ../include/lgi/common/NetTools.h \ ../include/lgi/common/Notifications.h \ ../include/lgi/common/OAuth2.h \ ../include/lgi/common/OptionsFile.h \ ../include/lgi/common/Palette.h \ ../include/lgi/common/Panel.h \ ../include/lgi/common/Password.h \ ../include/lgi/common/Path.h \ ../include/lgi/common/PixelRops.h \ ../include/lgi/common/Point.h \ ../include/lgi/common/Popup.h \ ../include/lgi/common/PopupList.h \ + ../include/lgi/common/PopupNotification.h \ ../include/lgi/common/Printer.h \ ../include/lgi/common/Profile.h \ ../include/lgi/common/Progress.h \ ../include/lgi/common/ProgressDlg.h \ ../include/lgi/common/ProgressView.h \ ../include/lgi/common/Properties.h \ ../include/lgi/common/RadioGroup.h \ ../include/lgi/common/Range.h \ ../include/lgi/common/Rect.h \ ../include/lgi/common/RectF.h \ ../include/lgi/common/RefCount.h \ ../include/lgi/common/RegKey.h \ ../include/lgi/common/Res.h \ ../include/lgi/common/Rops.h \ ../include/lgi/common/ScrollBar.h \ ../include/lgi/common/SkinEngine.h \ ../include/lgi/common/Slider.h \ ../include/lgi/common/Splitter.h \ ../include/lgi/common/StatusBar.h \ ../include/lgi/common/Store3Defs.h \ ../include/lgi/common/Stream.h \ ../include/lgi/common/StringClass.h \ ../include/lgi/common/StringLayout.h \ ../include/lgi/common/StructuredIo.h \ ../include/lgi/common/StructuredLog.h \ ../include/lgi/common/SubProcess.h \ ../include/lgi/common/TableLayout.h \ ../include/lgi/common/TabView.h \ ../include/lgi/common/TextFile.h \ ../include/lgi/common/TextLabel.h \ ../include/lgi/common/TextLog.h \ ../include/lgi/common/TextView3.h \ ../include/lgi/common/Thread.h \ ../include/lgi/common/ThreadEvent.h \ ../include/lgi/common/Token.h \ ../include/lgi/common/ToolBar.h \ ../include/lgi/common/ToolTip.h \ ../include/lgi/common/TrayIcon.h \ ../include/lgi/common/Tree.h \ ../include/lgi/common/Undo.h \ ../include/lgi/common/Unicode.h \ ../include/lgi/common/UnicodeString.h \ ../include/lgi/common/UnrolledList.h \ ../include/lgi/common/Variant.h \ ../include/lgi/common/View.h \ ../include/lgi/common/Widgets.h \ ../include/lgi/common/Window.h \ ../include/lgi/common/XmlTree.h \ ../include/lgi/linux/Gtk/LgiOsClasses.h \ ../include/lgi/linux/Gtk/LgiOsDefs.h \ ../include/lgi/linux/Gtk/LgiWidget.h \ ../include/lgi/linux/Gtk/LgiWinManGlue.h \ ../include/lgi/linux/SymLookup.h \ ../include/lgi/mac/cocoa/LCocoaView.h \ ../include/lgi/mac/cocoa/LgiMac.h \ ../include/lgi/mac/cocoa/LgiOs.h \ ../include/lgi/mac/cocoa/LgiOsClasses.h \ ../include/lgi/mac/cocoa/LgiOsDefs.h \ ../include/lgi/mac/cocoa/ObjCWrapper.h \ ../include/lgi/mac/cocoa/SymLookup.h \ ../private/common/FontPriv.h \ ../private/common/ViewPriv.h \ ../private/linux/AppPriv.h \ ../src/common/Gdc2/15Bit.cpp \ ../src/common/Gdc2/16Bit.cpp \ ../src/common/Gdc2/24Bit.cpp \ ../src/common/Gdc2/32Bit.cpp \ ../src/common/Gdc2/8Bit.cpp \ ../src/common/Gdc2/Alpha.cpp \ ../src/common/Gdc2/Colour.cpp \ ../src/common/Gdc2/Filters/Filter.cpp \ ../src/common/Gdc2/Font/Charset.cpp \ ../src/common/Gdc2/Font/DisplayString.cpp \ ../src/common/Gdc2/Font/Font.cpp \ ../src/common/Gdc2/Font/FontSystem.cpp \ ../src/common/Gdc2/Font/FontType.cpp \ ../src/common/Gdc2/Font/StringLayout.cpp \ ../src/common/Gdc2/Font/TypeFace.cpp \ ../src/common/Gdc2/GdcCommon.cpp \ ../src/common/Gdc2/Path/Path.cpp \ ../src/common/Gdc2/Rect.cpp \ ../src/common/Gdc2/RopsCases.cpp \ ../src/common/Gdc2/Surface.cpp \ ../src/common/Gdc2/Tools/ColourReduce.cpp \ ../src/common/Gdc2/Tools/GdcTools.cpp \ ../src/common/General/Containers.cpp \ ../src/common/General/DateTime.cpp \ ../src/common/General/ExeCheck.cpp \ ../src/common/General/FileCommon.cpp \ ../src/common/General/Password.cpp \ ../src/common/General/Properties.cpp \ ../src/common/Hash/md5/md5.c \ ../src/common/Hash/md5/md5.h \ ../src/common/Hash/sha1/sha1.c \ ../src/common/Hash/sha1/sha1.h \ ../src/common/Lgi/Alert.cpp \ ../src/common/Lgi/AppCommon.cpp \ ../src/common/Lgi/Css.cpp \ ../src/common/Lgi/CssTools.cpp \ ../src/common/Lgi/DataDlg.cpp \ ../src/common/Lgi/DragAndDropCommon.cpp \ ../src/common/Lgi/FileSelect.cpp \ ../src/common/Lgi/FindReplace.cpp \ ../src/common/Lgi/FontSelect.cpp \ ../src/common/Lgi/GuiUtils.cpp \ ../src/common/Lgi/Input.cpp \ ../src/common/Lgi/LgiCommon.cpp \ ../src/common/Lgi/Library.cpp \ ../src/common/Lgi/LMsg.cpp \ ../src/common/Lgi/MemStream.cpp \ ../src/common/Lgi/MenuCommon.cpp \ ../src/common/Lgi/Mru.cpp \ ../src/common/Lgi/Mutex.cpp \ ../src/common/Lgi/Object.cpp \ ../src/common/Lgi/OptionsFile.cpp \ ../src/common/Lgi/Rand.cpp \ ../src/common/Lgi/Stream.cpp \ ../src/common/Lgi/SubProcess.cpp \ ../src/common/Lgi/ThreadCommon.cpp \ ../src/common/Lgi/ThreadEvent.cpp \ ../src/common/Lgi/ToolTip.cpp \ ../src/common/Lgi/TrayIcon.cpp \ ../src/common/Lgi/Variant.cpp \ ../src/common/Lgi/ViewCommon.cpp \ ../src/common/Lgi/WindowCommon.cpp \ ../src/common/Net/Base64.cpp \ ../src/common/Net/MDStringToDigest.cpp \ ../src/common/Net/Net.cpp \ ../src/common/Net/NetTools.cpp \ ../src/common/Net/Uri.cpp \ ../src/common/Resource/LgiRes.cpp \ ../src/common/Resource/Res.cpp \ ../src/common/Skins/Gel/Gel.cpp \ ../src/common/Text/DocView.cpp \ ../src/common/Text/String.cpp \ ../src/common/Text/TextView3.cpp \ ../src/common/Text/Token.cpp \ ../src/common/Text/Unicode.cpp \ ../src/common/Text/Utf8.cpp \ ../src/common/Text/XmlTree.cpp \ ../src/common/Widgets/Bitmap.cpp \ ../src/common/Widgets/Box.cpp \ ../src/common/Widgets/Button.cpp \ ../src/common/Widgets/CheckBox.cpp \ ../src/common/Widgets/Combo.cpp \ ../src/common/Widgets/Edit.cpp \ ../src/common/Widgets/ItemContainer.cpp \ ../src/common/Widgets/List.cpp \ ../src/common/Widgets/Panel.cpp \ ../src/common/Widgets/Popup.cpp \ ../src/common/Widgets/Progress.cpp \ ../src/common/Widgets/ProgressDlg.cpp \ ../src/common/Widgets/RadioGroup.cpp \ ../src/common/Widgets/ScrollBar.cpp \ ../src/common/Widgets/Slider.cpp \ ../src/common/Widgets/Splitter.cpp \ ../src/common/Widgets/StatusBar.cpp \ ../src/common/Widgets/TableLayout.cpp \ ../src/common/Widgets/TabView.cpp \ ../src/common/Widgets/TextLabel.cpp \ ../src/common/Widgets/ToolBar.cpp \ ../src/common/Widgets/Tree.cpp \ ../src/linux/General/File.cpp \ ../src/linux/General/Mem.cpp \ ../src/linux/General/ShowFileProp_Linux.cpp \ ../src/linux/Gtk/Gdc2.cpp \ ../src/linux/Gtk/LgiWidget.cpp \ ../src/linux/Gtk/MemDC.cpp \ ../src/linux/Gtk/PrintDC.cpp \ ../src/linux/Gtk/ScreenDC.cpp \ ../src/linux/Lgi/App.cpp \ ../src/linux/Lgi/ClipBoard.cpp \ ../src/linux/Lgi/DragAndDrop.cpp \ ../src/linux/Lgi/General.cpp \ ../src/linux/Lgi/Layout.cpp \ ../src/linux/Lgi/Menu.cpp \ ../src/linux/Lgi/Printer.cpp \ ../src/linux/Lgi/Thread.cpp \ ../src/linux/Lgi/View.cpp \ ../src/linux/Lgi/Widgets.cpp \ ../src/linux/Lgi/Window.cpp export Build=$(Build); \ $(MAKE) -C .. -f Makefile.linux .SECONDEXPANSION: $(Objects): $(BuildDir)/%.o: $$(wildcard %.c*) mkdir -p $(@D) @echo $( #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Token.h" #include "lgi/common/Popup.h" #include "lgi/common/Panel.h" #include "lgi/common/Notifications.h" #include "lgi/common/Menu.h" #include "ViewPriv.h" using namespace Gtk; #undef Status #include "LgiWidget.h" #define DEBUG_SETFOCUS 0 #define DEBUG_HANDLEVIEWKEY 0 extern Gtk::GdkDragAction EffectToDragAction(int Effect); /////////////////////////////////////////////////////////////////////// class HookInfo { public: LWindowHookType Flags; LView *Target; }; enum LAttachState { LUnattached, LAttaching, LAttached, LDetaching, }; class LWindowPrivate { public: int Sx, Sy; bool Dynamic; LKey LastKey; ::LArray Hooks; bool SnapToEdge; ::LString Icon; LRect Decor; gulong DestroySig; LAutoPtr IconImg; LAttachState AttachState; // State GdkWindowState State; bool HadCreateEvent; // Focus stuff OsView FirstFocus; LViewI *Focus; bool Active; LWindowPrivate() { AttachState = LUnattached; DestroySig = 0; Decor.ZOff(-1, -1); FirstFocus = NULL; Focus = NULL; Active = false; State = (Gtk::GdkWindowState)0; HadCreateEvent = false; Sx = Sy = 0; Dynamic = true; SnapToEdge = false; LastKey.vkey = 0; LastKey.c16 = 0; LastKey.Data = 0; LastKey.IsChar = 0; } int GetHookIndex(LView *Target, bool Create = false) { for (int i=0; iTarget = Target; n->Flags = LNoEvents; return Hooks.Length() - 1; } } return -1; } }; /////////////////////////////////////////////////////////////////////// #define GWND_CREATE 0x0010000 LWindow::LWindow(GtkWidget *w) : LView(0) { d = new LWindowPrivate; _QuitOnClose = false; Menu = NULL; Wnd = GTK_WINDOW(w); if (Wnd) g_object_set_data(G_OBJECT(Wnd), "LViewI", (LViewI*)this); _Root = NULL; _MenuBar = NULL; _VBox = NULL; _Default = 0; _Window = this; WndFlags |= GWND_CREATE; ClearFlag(WndFlags, GWF_VISIBLE); _Lock = new ::LMutex("LWindow"); } LWindow::~LWindow() { d->AttachState = LDetaching; if (Wnd && d->DestroySig > 0) { // As we are already in the destructor, we don't want // GtkWindowDestroy to try and delete the object again. g_signal_handler_disconnect(Wnd, d->DestroySig); } if (LAppInst->AppWnd == this) LAppInst->AppWnd = NULL; if (_Root) { lgi_widget_detach(_Root); _Root = NULL; } if (Wnd) { gtk_widget_destroy(GTK_WIDGET(Wnd)); Wnd = NULL; } d->AttachState = LUnattached; DeleteObj(Menu); DeleteObj(d); DeleteObj(_Lock); } int LWindow::WaitThread() { return 0; // Nop for linux } bool LWindow::SetIcon(const char *FileName) { LString a; if (Wnd) { if (!LFileExists(FileName)) { if (a = LFindFile(FileName)) FileName = a; } if (!LFileExists(FileName)) { LgiTrace("%s:%i - SetIcon failed to find '%s'\n", _FL, FileName); return false; } else { #if defined(LINUX) LAppInst->SetApplicationIcon(FileName); #endif #if _MSC_VER GError *error = NULL; if (gtk_window_set_icon_from_file(Wnd, FileName, &error)) return true; #else // On windows this is giving a red for blue channel swap error... if (d->IconImg.Reset(GdcD->Load(a))) gtk_window_set_icon(Wnd, d->IconImg->CreatePixBuf()); #endif } } if (FileName != d->Icon.Get()) d->Icon = FileName; return d->Icon != NULL; } bool LWindow::GetSnapToEdge() { return d->SnapToEdge; } void LWindow::SetSnapToEdge(bool s) { d->SnapToEdge = s; } bool LWindow::IsActive() { return d->Active; } bool LWindow::SetActive() { if (!Wnd) return false; gtk_window_present(Wnd); return true; } bool LWindow::Visible() { return LView::Visible(); } void LWindow::Visible(bool i) { ThreadCheck(); auto w = GTK_WIDGET(Wnd); if (i) gtk_widget_show(w); else gtk_widget_hide(w); } bool LWindow::Obscured() { return d->State == GDK_WINDOW_STATE_WITHDRAWN || d->State == GDK_WINDOW_STATE_ICONIFIED; } void LWindow::_SetDynamic(bool i) { d->Dynamic = i; } void LWindow::_OnViewDelete() { if (d->Dynamic) { delete this; } } void LWindow::OnGtkRealize() { d->AttachState = LAttached; LView::OnGtkRealize(); } void LWindow::OnGtkDelete() { // Delete everything we own... // DeleteObj(Menu); #if 0 while (Children.Length()) { LViewI *c = Children.First(); c->Detach(); } #else for (unsigned i=0; iGetGView(); if (v) v->OnGtkDelete(); } #endif // These will be destroyed by GTK after returning from LWindowCallback Wnd = NULL; #ifndef __GTK_H__ _View = NULL; #endif } LRect *LWindow::GetDecorSize() { return d->Decor.x2 >= 0 ? &d->Decor : NULL; } void LWindow::SetDecor(bool Visible) { if (Wnd) gtk_window_set_decorated (Wnd, Visible); else LgiTrace("%s:%i - No window to set decor.\n", _FL); } LViewI *LWindow::WindowFromPoint(int x, int y, bool Debug) { if (!_Root) return NULL; auto rpos = GtkGetPos(_Root).ZeroTranslate(); if (!rpos.Overlap(x, y)) return NULL; return LView::WindowFromPoint(x - rpos.x1, y - rpos.y1, Debug); } bool LWindow::TranslateMouse(LMouse &m) { m.Target = WindowFromPoint(m.x, m.y, false); if (!m.Target) return false; LViewI *w = this; for (auto p = m.Target; p; p = p->GetParent()) { if (p == w) { auto ppos = GtkGetPos(GTK_WIDGET(WindowHandle())); m.x -= ppos.x1; m.y -= ppos.y1; break; } else { auto pos = p->GetPos(); m.x -= pos.x1; m.y -= pos.y1; } } return true; } gboolean LWindow::OnGtkEvent(GtkWidget *widget, GdkEvent *event) { if (!event) { printf("%s:%i - No event.\n", _FL); return FALSE; } #if 0 if (event->type != 28) LgiTrace("%s::OnGtkEvent(%i) name=%s\n", GetClass(), event->type, Name()); #endif switch (event->type) { case GDK_DELETE: { bool Close = OnRequestClose(false); if (Close) OnGtkDelete(); return !Close; } case GDK_DESTROY: { delete this; return true; } case GDK_KEY_PRESS: case GDK_KEY_RELEASE: { auto ModFlags = LAppInst->GetKeyModFlags(); auto e = &event->key; #define KEY(name) GDK_KEY_##name LKey k; k.Down(e->type == GDK_KEY_PRESS); k.c16 = k.vkey = e->keyval; k.Shift((e->state & ModFlags->Shift) != 0); k.Ctrl((e->state & ModFlags->Ctrl) != 0); k.Alt((e->state & ModFlags->Alt) != 0); k.System((e->state & ModFlags->System) != 0); #if 0//def _DEBUG if (k.vkey == GDK_KEY_Meta_L || k.vkey == GDK_KEY_Meta_R) break; #endif k.IsChar = !k.Ctrl() && !k.Alt() && !k.System() && (k.c16 >= ' ') && (k.c16 >> 8 != 0xff); if (e->keyval > 0xff && e->string != NULL) { // Convert string to unicode char auto *i = e->string; ptrdiff_t len = strlen(i); k.c16 = LgiUtf8To32((uint8_t *&) i, len); } switch (k.vkey) { case GDK_KEY_ISO_Left_Tab: case KEY(Tab): k.IsChar = true; k.c16 = k.vkey = LK_TAB; break; case KEY(Return): case KEY(KP_Enter): k.IsChar = true; k.c16 = k.vkey = LK_RETURN; break; case GDK_KEY_BackSpace: k.c16 = k.vkey = LK_BACKSPACE; k.IsChar = !k.Ctrl() && !k.Alt() && !k.System(); break; case KEY(Left): k.vkey = k.c16 = LK_LEFT; break; case KEY(Right): k.vkey = k.c16 = LK_RIGHT; break; case KEY(Up): k.vkey = k.c16 = LK_UP; break; case KEY(Down): k.vkey = k.c16 = LK_DOWN; break; case KEY(Page_Up): k.vkey = k.c16 = LK_PAGEUP; break; case KEY(Page_Down): k.vkey = k.c16 = LK_PAGEDOWN; break; case KEY(Home): k.vkey = k.c16 = LK_HOME; break; case KEY(End): k.vkey = k.c16 = LK_END; break; case KEY(Delete): k.vkey = k.c16 = LK_DELETE; break; #define KeyPadMap(gdksym, ch, is) \ case gdksym: k.c16 = ch; k.IsChar = is; break; KeyPadMap(KEY(KP_0), '0', true) KeyPadMap(KEY(KP_1), '1', true) KeyPadMap(KEY(KP_2), '2', true) KeyPadMap(KEY(KP_3), '3', true) KeyPadMap(KEY(KP_4), '4', true) KeyPadMap(KEY(KP_5), '5', true) KeyPadMap(KEY(KP_6), '6', true) KeyPadMap(KEY(KP_7), '7', true) KeyPadMap(KEY(KP_8), '8', true) KeyPadMap(KEY(KP_9), '9', true) KeyPadMap(KEY(KP_Space), ' ', true) KeyPadMap(KEY(KP_Tab), '\t', true) KeyPadMap(KEY(KP_F1), LK_F1, false) KeyPadMap(KEY(KP_F2), LK_F2, false) KeyPadMap(KEY(KP_F3), LK_F3, false) KeyPadMap(KEY(KP_F4), LK_F4, false) KeyPadMap(KEY(KP_Home), LK_HOME, false) KeyPadMap(KEY(KP_Left), LK_LEFT, false) KeyPadMap(KEY(KP_Up), LK_UP, false) KeyPadMap(KEY(KP_Right), LK_RIGHT, false) KeyPadMap(KEY(KP_Down), LK_DOWN, false) KeyPadMap(KEY(KP_Page_Up), LK_PAGEUP, false) KeyPadMap(KEY(KP_Page_Down), LK_PAGEDOWN, false) KeyPadMap(KEY(KP_End), LK_END, false) KeyPadMap(KEY(KP_Begin), LK_HOME, false) KeyPadMap(KEY(KP_Insert), LK_INSERT, false) KeyPadMap(KEY(KP_Delete), LK_DELETE, false) KeyPadMap(KEY(KP_Equal), '=', true) KeyPadMap(KEY(KP_Multiply), '*', true) KeyPadMap(KEY(KP_Add), '+', true) KeyPadMap(KEY(KP_Separator), '|', true) // is this right? KeyPadMap(KEY(KP_Subtract), '-', true) KeyPadMap(KEY(KP_Decimal), '.', true) KeyPadMap(KEY(KP_Divide), '/', true) } if (ModFlags->Debug) { :: LString Msg; Msg.Printf("e->state=%x %s", e->state, ModFlags->FlagsToString(e->state).Get()); k.Trace(Msg); } auto v = d->Focus ? d->Focus : this; if (!HandleViewKey(v->GetGView(), k)) { if (!k.Down()) return false; if (k.vkey == LK_TAB || k.vkey == KEY(ISO_Left_Tab)) { // Do tab between controls ::LArray a; BuildTabStops(this, a); int idx = a.IndexOf((LViewI*)v); if (idx >= 0) { idx += k.Shift() ? -1 : 1; int next_idx = idx == 0 ? a.Length() -1 : idx % a.Length(); LViewI *next = a[next_idx]; if (next) { // LgiTrace("Setting focus to %i of %i: %s, %s, %i\n", next_idx, a.Length(), next->GetClass(), next->GetPos().GetStr(), next->GetId()); next->Focus(true); } } } else if (k.System()) { if (ToLower(k.c16) == 'q') { auto AppWnd = LAppInst->AppWnd; auto Wnd = AppWnd ? AppWnd : this; if (Wnd->OnRequestClose(false)) { Wnd->Quit(); return true; } } } else return false; } break; } case GDK_CONFIGURE: { GdkEventConfigure *c = &event->configure; Pos.Set(c->x, c->y, c->x+c->width-1, c->y+c->height-1); // printf("%s::GDK_CONFIGURE %s\n", GetClass(), Pos.GetStr()); OnPosChange(); return FALSE; break; } case GDK_FOCUS_CHANGE: { d->Active = event->focus_change.in; #if 0 printf("%s/%s::GDK_FOCUS_CHANGE(%i)\n", GetClass(), Name(), event->focus_change.in); #endif break; } case GDK_WINDOW_STATE: { d->State = event->window_state.new_window_state; break; } case GDK_PROPERTY_NOTIFY: { gchar *Name = gdk_atom_name (event->property.atom); if (!Name) break; if (!_stricmp(Name, "_NET_FRAME_EXTENTS")) { // printf("PropChange: %i - %s\n", event->property.atom, Name); unsigned long *extents = NULL; if (gdk_property_get(event->property.window, gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE), gdk_atom_intern ("CARDINAL", FALSE), 0, sizeof (unsigned long) * 4, FALSE, NULL, NULL, NULL, (guchar **)&extents)) { d->Decor.Set(extents[0], extents[2], extents[1], extents[3]); g_free(extents); } else printf("%s:%i - Error: gdk_property_get failed.\n", _FL); } g_free(Name); break; } case GDK_UNMAP: { // LgiTrace("%s:%i - Unmap %s\n", _FL, GetClass()); break; } case GDK_VISIBILITY_NOTIFY: { // LgiTrace("%s:%i - Visible %s\n", _FL, GetClass()); break; } case GDK_DRAG_ENTER: { LgiTrace("%s:%i - GDK_DRAG_ENTER\n", _FL); break; } case GDK_DRAG_LEAVE: { LgiTrace("%s:%i - GDK_DRAG_LEAVE\n", _FL); break; } case GDK_DRAG_MOTION: { LgiTrace("%s:%i - GDK_DRAG_MOTION\n", _FL); break; } case GDK_DRAG_STATUS: { LgiTrace("%s:%i - GDK_DRAG_STATUS\n", _FL); break; } case GDK_DROP_START: { LgiTrace("%s:%i - GDK_DROP_START\n", _FL); break; } case GDK_DROP_FINISHED: { LgiTrace("%s:%i - GDK_DROP_FINISHED\n", _FL); break; } default: { printf("%s:%i - Unknown event %i\n", _FL, event->type); return false; } } return true; } static gboolean GtkWindowDestroy(GtkWidget *widget, LWindow *This) { delete This; return true; } static void GtkWindowRealize(GtkWidget *widget, LWindow *This) { #if 0 LgiTrace("GtkWindowRealize, This=%p(%s\"%s\")\n", This, (NativeInt)This > 0x1000 ? This->GetClass() : 0, (NativeInt)This > 0x1000 ? This->Name() : 0); #endif This->OnGtkRealize(); } static void GtkRootResize(GtkWidget *widget, GdkRectangle *alloc, LView *This) { LWindow *w = This->GetWindow(); if (w) w->PourAll(); } void LWindowUnrealize(GtkWidget *widget, LWindow *wnd) { // printf("%s:%i - LWindowUnrealize %s\n", _FL, wnd->GetClass()); } bool DndPointMap(LViewI *&v, LPoint &p, LDragDropTarget *&t, LWindow *Wnd, int x, int y) { LRect cli = Wnd->GetClient(); t = NULL; v = Wnd->WindowFromPoint(x - cli.x1, y - cli.y1, false); if (!v) { LgiTrace("%s:%i - @ %i,%i\n", _FL, x, y); return false; } v->WindowVirtualOffset(&p); p.x = x - p.x; p.y = y - p.y; for (LViewI *view = v; !t && view; view = view->GetParent()) t = view->DropTarget(); if (t) return true; LgiTrace("%s:%i - No target for %s\n", _FL, v->GetClass()); return false; } void LWindowDragBegin(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataDelete(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataGet(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *data, guint info, guint time, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataReceived(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, LWindow *Wnd) { LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return; for (auto &d: t->Data) { auto type = gdk_atom_name(gtk_selection_data_get_data_type(data)); if (d.Format.Equals(type)) { gint length = 0; auto ptr = gtk_selection_data_get_data_with_length(data, &length); if (ptr) { d.Data[0].SetBinary(length, (void*)ptr, false); } break; } } } int GetAcceptFmts(::LString::Array &Formats, GdkDragContext *context, LDragDropTarget *t, LPoint &p) { int KeyState = 0; LDragFormats Fmts(true); int Flags = DROPEFFECT_NONE; GList *targets = gdk_drag_context_list_targets(context); Gtk::GList *i = targets; while (i) { auto a = gdk_atom_name((GdkAtom)i->data); if (a) Fmts.Supports(a); i = i->next; } Fmts.SetSource(false); Flags = t->WillAccept(Fmts, p, KeyState); auto Sup = Fmts.GetSupported(); for (auto &s: Sup) Formats.New() = s; return Flags; } gboolean LWindowDragDataDrop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, LWindow *Wnd) { // Map the point to a view... LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return false; t->Data.Length(0); // Request the data... ::LArray Data; ::LString::Array Formats; int KeyState = 0; int Flags = GetAcceptFmts(Formats, context, t, p); for (auto f: Formats) { t->Data.New().Format = f; gtk_drag_get_data(widget, context, gdk_atom_intern(f, true), time); } // Wait for the data to arrive... uint64_t Start = LCurrentTime(); while (LCurrentTime()-Start < 2000) { int HasData = 0; for (auto d: t->Data) if (d.Data.Length() > 0) HasData++; if (HasData >= Formats.Length()) break; LYield(); } auto Result = t->OnDrop(t->Data, p, KeyState); if (Flags != DROPEFFECT_NONE) gdk_drag_status(context, EffectToDragAction(Flags), time); return Result != DROPEFFECT_NONE; } void LWindowDragEnd(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } gboolean LWindowDragFailed(GtkWidget *widget, GdkDragContext *context, GtkDragResult result, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); return false; } void LWindowDragLeave(GtkWidget *widget, GdkDragContext *context, guint time, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } gboolean LWindowDragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, LWindow *Wnd) { LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return false; ::LString::Array Formats; int Flags = GetAcceptFmts(Formats, context, t, p); if (Flags != DROPEFFECT_NONE) gdk_drag_status(context, EffectToDragAction(Flags), time); return Flags != DROPEFFECT_NONE; } bool LWindow::Attach(LViewI *p) { bool Status = false; ThreadCheck(); // Setup default button... if (!_Default) _Default = FindControl(IDOK); // Create a window if needed.. if (!Wnd) Wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); if (Wnd) { auto Widget = GTK_WIDGET(Wnd); LView *i = this; if (Pos.X() > 0 && Pos.Y() > 0) gtk_window_resize(Wnd, Pos.X(), Pos.Y()); gtk_window_move(Wnd, Pos.x1, Pos.y1); auto Obj = G_OBJECT(Wnd); g_object_set_data(Obj, "LViewI", (LViewI*)this); d->DestroySig = g_signal_connect(Obj, "destroy", G_CALLBACK(GtkWindowDestroy), this); g_signal_connect(Obj, "delete_event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "key-press-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "key-release-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "focus-in-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "focus-out-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "window-state-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "property-notify-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "configure-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "unmap-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "visibility-notify-event",G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "realize", G_CALLBACK(GtkWindowRealize), i); g_signal_connect(Obj, "unrealize", G_CALLBACK(LWindowUnrealize), i); g_signal_connect(Obj, "drag-begin", G_CALLBACK(LWindowDragBegin), i); g_signal_connect(Obj, "drag-data-delete", G_CALLBACK(LWindowDragDataDelete), i); g_signal_connect(Obj, "drag-data-get", G_CALLBACK(LWindowDragDataGet), i); g_signal_connect(Obj, "drag-data-received", G_CALLBACK(LWindowDragDataReceived), i); g_signal_connect(Obj, "drag-drop", G_CALLBACK(LWindowDragDataDrop), i); g_signal_connect(Obj, "drag-end", G_CALLBACK(LWindowDragEnd), i); g_signal_connect(Obj, "drag-failed", G_CALLBACK(LWindowDragFailed), i); g_signal_connect(Obj, "drag-leave", G_CALLBACK(LWindowDragLeave), i); g_signal_connect(Obj, "drag-motion", G_CALLBACK(LWindowDragMotion), i); #if 0 g_signal_connect(Obj, "button-press-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "button-release-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "motion-notify-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "scroll-event", G_CALLBACK(GtkViewCallback), i); #endif gtk_widget_add_events( Widget, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_VISIBILITY_NOTIFY_MASK); gtk_window_set_title(Wnd, LBase::Name()); d->AttachState = LAttaching; // g_action_map_add_action_entries (G_ACTION_MAP(Wnd), app_entries, G_N_ELEMENTS (app_entries), Wnd); if ((_Root = lgi_widget_new(this, true))) { g_signal_connect(_Root, "size-allocate", G_CALLBACK(GtkRootResize), i); GtkContainer *AttachPoint = NULL; if (GTK_IS_DIALOG(Wnd)) { auto content = gtk_dialog_get_content_area(GTK_DIALOG(Wnd)); if (!content) { LAssert(!"No content area"); return false; } AttachPoint = GTK_CONTAINER(content); } else { AttachPoint = GTK_CONTAINER(Wnd); } LAssert(AttachPoint != NULL); gtk_container_add(AttachPoint, _Root); // Check it actually worked... (would a return value kill you GTK? no it would not) auto p = gtk_widget_get_parent(_Root); if (!p) { LAssert(!"Add failed"); return false; } gtk_widget_show(_Root); } // This call sets up the GdkWindow handle gtk_widget_realize(Widget); // Do a rough layout of child windows PourAll(); // Add icon if (d->Icon) { SetIcon(d->Icon); d->Icon.Empty(); } auto p = GetParent(); if (p) { auto pHnd = p->WindowHandle(); if (!pHnd) LgiTrace("%s:%i - SetParent() - no pHnd from %s.\n", _FL, p->GetClass()); else gtk_window_set_transient_for(GTK_WINDOW(Wnd), pHnd); } Status = true; } return Status; } bool LWindow::OnRequestClose(bool OsShuttingDown) { if (GetQuitOnClose()) { LCloseApp(); } return LView::OnRequestClose(OsShuttingDown); } bool LWindow::HandleViewMouse(LView *v, LMouse &m) { if (m.Down() && !m.IsMove()) { bool InPopup = false; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) { InPopup = true; break; } } if (!InPopup && LPopup::CurrentPopups.Length()) { for (int i=0; iVisible()) p->Visible(false); } } } for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LMouseEvents) { if (!d->Hooks[i].Target->OnViewMouse(v, m)) { return false; } } } return true; } bool LWindow::HandleViewKey(LView *v, LKey &k) { bool Status = false; LViewI *Ctrl = 0; #if DEBUG_HANDLEVIEWKEY bool Debug = 1; // k.vkey == LK_RETURN; char SafePrint = k.c16 < ' ' ? ' ' : k.c16; // if (Debug) { LgiTrace("%s/%p::HandleViewKey=%i ischar=%i %s%s%s%s (d->Focus=%s/%p)\n", v->GetClass(), v, k.c16, k.IsChar, (char*)(k.Down()?" Down":" Up"), (char*)(k.Shift()?" Shift":""), (char*)(k.Alt()?" Alt":""), (char*)(k.Ctrl()?" Ctrl":""), d->Focus?d->Focus->GetClass():0, d->Focus); } #endif // Any window in a popup always gets the key... LViewI *p; for (p = v->GetParent(); p; p = p->GetParent()) { if (dynamic_cast(p)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tSending key to popup\n"); #endif return v->OnKey(k); } } // Give key to popups if (LAppInst && LAppInst->GetMouseHook() && LAppInst->GetMouseHook()->OnViewKey(v, k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMouseHook got key\n"); #endif goto AllDone; } // Allow any hooks to see the key... for (int i=0; iHooks.Length(); i++) { #if DEBUG_HANDLEVIEWKEY // if (Debug) LgiTrace("\tHook[%i]\n", i); #endif if (d->Hooks[i].Flags & LKeyEvents) { LView *Target = d->Hooks[i].Target; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i].Target=%p %s\n", i, Target, Target->GetClass()); #endif if (Target->OnViewKey(v, k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i] ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", i, SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } } // Give the key to the window... if (v->OnKey(k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tView ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif Status = true; goto AllDone; } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\t%s didn't eat '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", v->GetClass(), SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif // Window didn't want the key... switch (k.vkey) { case LK_RETURN: #ifdef LK_KEYPADENTER case LK_KEYPADENTER: #endif { Ctrl = _Default; break; } case LK_ESCAPE: { Ctrl = FindControl(IDCANCEL); break; } } // printf("Ctrl=%p\n", Ctrl); if (Ctrl) { if (Ctrl->Enabled()) { if (Ctrl->OnKey(k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tDefault Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } // else printf("OnKey()=false\n"); } // else printf("Ctrl=disabled\n"); } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\tNo default ctrl to handle key.\n"); #endif if (Menu) { Status = Menu->OnKey(v, k); if (Status) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMenu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif } } // Tab through controls if (k.vkey == LK_TAB && k.Down() && !k.IsChar) { LViewI *Wnd = GetNextTabStop(v, k.Shift()); #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tTab moving focus shift=%i Wnd=%p\n", k.Shift(), Wnd); #endif if (Wnd) Wnd->Focus(true); } // Control shortcut? if (k.Down() && k.Alt() && k.c16 > ' ') { ShortcutMap Map; BuildShortcuts(Map); LViewI *c = Map.Find(ToUpper(k.c16)); if (c) { c->OnNotify(c, LNotifyActivate); return true; } } AllDone: if (d) d->LastKey = k; return Status; } void LWindow::Raise() { if (Wnd) gtk_window_present(Wnd); } LWindowZoom LWindow::GetZoom() { switch (d->State) { case GDK_WINDOW_STATE_ICONIFIED: return LZoomMin; case GDK_WINDOW_STATE_MAXIMIZED: return LZoomMax; default: break; } return LZoomNormal; } void LWindow::SetZoom(LWindowZoom i) { if (!Wnd) { // LgiTrace("%s:%i - No window.\n", _FL); return; } ThreadCheck(); switch (i) { case LZoomMin: { gtk_window_iconify(Wnd); break; } case LZoomNormal: { gtk_window_deiconify(Wnd); gtk_window_unmaximize(Wnd); break; } case LZoomMax: { gtk_window_maximize(Wnd); break; } default: { LgiTrace("%s:%i - Error: unsupported zoom.\n", _FL); break; } } } LViewI *LWindow::GetDefault() { return _Default; } void LWindow::SetDefault(LViewI *v) { if (v && v->GetWindow() == this) { if (_Default != v) { LViewI *Old = _Default; _Default = v; if (Old) Old->Invalidate(); if (_Default) _Default->Invalidate(); } } else { _Default = 0; } } +bool LWindow::SetTitleBar(bool ShowTitleBar) +{ + return false; +} + bool LWindow::Name(const char *n) { if (Wnd) { ThreadCheck(); gtk_window_set_title(Wnd, n); } return LBase::Name(n); } const char *LWindow::Name() { return LBase::Name(); } struct CallbackParams { LRect Menu; int Depth; CallbackParams() { Menu.ZOff(-1, -1); Depth = 0; } }; void ClientCallback(GtkWidget *w, CallbackParams *p) { const char *Name = gtk_widget_get_name(w); if (Name && !_stricmp(Name, "GtkMenuBar")) { GtkAllocation alloc = {0}; gtk_widget_get_allocation(w, &alloc); p->Menu.ZOff(alloc.width-1, alloc.height-1); // LgiTrace("GtkMenuBar = %s\n", p->Menu.GetStr()); } if (!p->Menu.Valid()) { p->Depth++; if (GTK_IS_CONTAINER(w)) gtk_container_forall(GTK_CONTAINER(w), (GtkCallback)ClientCallback, p); p->Depth--; } } LPoint LWindow::GetDpi() { return LScreenDpi(); } LPointF LWindow::GetDpiScale() { auto Dpi = GetDpi(); return LPointF((double)Dpi.x/96.0, (double)Dpi.y/96.0); } LRect &LWindow::GetClient(bool ClientSpace) { static LRect r; r = LView::GetClient(ClientSpace); if (Wnd) { CallbackParams p; gtk_container_forall(GTK_CONTAINER(Wnd), (GtkCallback)ClientCallback, &p); if (p.Menu.Valid()) { if (ClientSpace) r.y2 -= p.Menu.Y(); else r.y1 += p.Menu.Y(); } } return r; } bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load) { if (!Store || !FieldName) return false; if (Load) { ::LVariant v; if (Store->GetValue(FieldName, v) && v.Str()) { LRect Position(0, 0, -1, -1); LWindowZoom State = LZoomNormal; // printf("SerializeState load %s\n", v.Str()); LToken t(v.Str(), ";"); for (int i=0; iName()); v = p[n]; ch += sprintf_s(s + ch, sizeof(s) - ch, "%s>%s", Buf, v->GetClass()); } return LAutoString(NewStr(s)); } #endif void LWindow::SetFocus(LViewI *ctrl, FocusType type) { #if DEBUG_SETFOCUS const char *TypeName = NULL; switch (type) { case GainFocus: TypeName = "Gain"; break; case LoseFocus: TypeName = "Lose"; break; case ViewDelete: TypeName = "Delete"; break; } #endif switch (type) { case GainFocus: { if (d->Focus == ctrl) { #if DEBUG_SETFOCUS LAutoString _ctrl = DescribeView(ctrl); LgiTrace("SetFocus(%s, %s) already has focus.\n", _ctrl.Get(), TypeName); #endif return; } if (d->Focus) { LView *gv = d->Focus->GetGView(); if (gv) { #if DEBUG_SETFOCUS LAutoString _foc = DescribeView(d->Focus); LgiTrace(".....defocus LView: %s\n", _foc.Get()); #endif gv->_Focus(false); } else if (IsActive()) { #if DEBUG_SETFOCUS LAutoString _foc = DescribeView(d->Focus); LgiTrace(".....defocus view: %s (active=%i)\n", _foc.Get(), IsActive()); #endif d->Focus->OnFocus(false); d->Focus->Invalidate(); } } d->Focus = ctrl; if (d->Focus) { #if DEBUG_SETFOCUS static int Count = 0; #endif LView *gv = d->Focus->GetGView(); if (gv) { #if DEBUG_SETFOCUS LAutoString _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) %i focusing LView\n", _set.Get(), TypeName, Count++); #endif gv->_Focus(true); } else if (IsActive()) { #if DEBUG_SETFOCUS LAutoString _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) %i focusing nonGView (active=%i)\n", _set.Get(), TypeName, Count++, IsActive()); #endif d->Focus->OnFocus(true); d->Focus->Invalidate(); } } break; } case LoseFocus: { #if DEBUG_SETFOCUS LAutoString _Ctrl = DescribeView(d->Focus); LAutoString _Focus = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) d->Focus=%s\n", _Ctrl.Get(), TypeName, _Focus.Get()); #endif if (ctrl == d->Focus) { d->Focus = NULL; } break; } case ViewDelete: { if (ctrl == d->Focus) { #if DEBUG_SETFOCUS LAutoString _Ctrl = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) on delete\n", _Ctrl.Get(), TypeName); #endif d->Focus = NULL; } break; } } } +void LWindow::SetWillFocus(bool f) +{ +} + void LWindow::SetDragHandlers(bool On) { } void LWindow::SetParent(LViewI *p) { LView::SetParent(p); if (p && Wnd) { auto pHnd = p->WindowHandle(); if (!pHnd) LgiTrace("%s:%i - SetParent() - no pHnd from %s.\n", _FL, p->GetClass()); else if (!GTK_IS_WINDOW(Wnd)) LgiTrace("%s:%i - SetParent() - GTK_IS_WINDOW failed.\n", _FL); else gtk_window_set_transient_for(GTK_WINDOW(Wnd), pHnd); } } bool LWindow::IsAttached() { return d->AttachState == LAttaching || d->AttachState == LAttached; } void LWindow::OnTrayClick(LMouse &m) { if (m.Down() || m.IsContextMenu()) { LSubMenu RClick; OnTrayMenu(RClick); if (GetMouse(m, true)) { #if WINNATIVE SetForegroundWindow(Handle()); #endif int Result = RClick.Float(this, m.x, m.y); #if WINNATIVE PostMessage(Handle(), WM_NULL, 0, 0); #endif OnTrayMenuResult(Result); } } } void LWindow::Quit(bool DontDelete) { ThreadCheck(); if (Wnd) { d->AttachState = LDetaching; auto wnd = Wnd; Wnd = NULL; gtk_widget_destroy(GTK_WIDGET(wnd)); } } void LWindow::SetAlwaysOnTop(bool b) { }