diff --git a/ResourceEditor/Code/LgiResApp.cpp b/ResourceEditor/Code/LgiResApp.cpp --- a/ResourceEditor/Code/LgiResApp.cpp +++ b/ResourceEditor/Code/LgiResApp.cpp @@ -1,4653 +1,4660 @@ /* ** FILE: LgiRes.cpp ** AUTHOR: Matthew Allen ** DATE: 3/8/99 ** DESCRIPTION: Lgi Resource Editor ** ** Copyright (C) 1999, Matthew Allen ** fret@memecode.com */ #include + #include "LgiResEdit.h" #include "LgiRes_Dialog.h" #include "LgiRes_Menu.h" + #include "lgi/common/About.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Edit.h" #include "lgi/common/CheckBox.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/TextView3.h" -#include "resdefs.h" #include "lgi/common/Token.h" #include "lgi/common/DataDlg.h" #include "lgi/common/Button.h" #include "lgi/common/Menu.h" #include "lgi/common/StatusBar.h" +#include "resdefs.h" + char AppName[] = "Lgi Resource Editor"; char HelpFile[] = "Help.html"; char OptionsFileName[] = "Options.r"; char TranslationStrMagic[] = "LgiRes.String"; #define VIEW_PULSE_RATE 100 #ifndef DIALOG_X #define DIALOG_X 1.56 #define DIALOG_Y 1.85 #define CTRL_X 1.50 #define CTRL_Y 1.64 #endif enum Ctrls { IDC_HBOX = 100, IDC_VBOX, }; const char *TypeNames[] = { "", "Css", "Dialog", "String", "Menu", 0}; ////////////////////////////////////////////////////////////////////////////// ResFileFormat GetFormat(const char *File) { ResFileFormat Format = Lr8File; char *Ext = LGetExtension(File); if (Ext) { if (stricmp(Ext, "lr") == 0) Format = CodepageFile; else if (stricmp(Ext, "xml") == 0) Format = XmlFile; } return Format; } char *EncodeXml(const char *Str, int Len) { char *Ret = 0; if (Str) { LStringPipe p; const char *s = Str; for (const char *e = Str; e && *e && (Len < 0 || ((e-Str) < Len)); ) { switch (*e) { case '<': { p.Push(s, e-s); p.Push("<"); s = ++e; break; } case '>': { p.Push(s, e-s); p.Push(">"); s = ++e; break; } case '&': { p.Push(s, e-s); p.Push("&"); s = ++e; break; } case '\\': { if (e[1] == 'n') { // Newline p.Push(s, e-s); p.Push("\n"); s = (e += 2); break; } // fall thru } case '\'': case '\"': case '/': { // Convert to entity p.Push(s, e-s); char b[32]; sprintf(b, "&#%i;", *e); p.Push(b); s = ++e; break; } default: { // Regular character e++; break; } } } p.Push(s); Ret = p.NewStr(); } return Ret; } char *DecodeXml(const char *Str, int Len) { if (Str) { LStringPipe p; const char *s = Str; for (const char *e = Str; e && *e && (Len < 0 || ((e-Str) < Len)); ) { switch (*e) { case '&': { // Store string up to here p.Push(s, e-s); e++; if (*e == '#') { // Numerical e++; if (*e == 'x' || *e == 'X') { // Hex e++; char16 c = htoi(e); char *c8 = WideToUtf8(&c, 1); if (c8) { p.Push(c8); DeleteArray(c8); } } else if (isdigit(*e)) { // Decimal char16 c = atoi(e); char *c8 = WideToUtf8(&c, 1); if (c8) { p.Push(c8); DeleteArray(c8); } } else { LAssert(0); } while (*e && *e != ';') e++; } else if (isalpha(*e)) { // named entity const char *Name = e; while (*e && *e != ';') e++; auto Len = e - Name; if (Len == 3 && strnicmp(Name, "amp", Len) == 0) { p.Push("&"); } else if (Len == 2 && strnicmp(Name, "gt", Len) == 0) { p.Push(">"); } else if (Len == 2 && strnicmp(Name, "lt", Len) == 0) { p.Push("<"); } else { // Unsupported entity LAssert(0); } } else { LAssert(0); while (*e && *e != ';') e++; } s = ++e; break; } case '\n': { p.Push(s, e-s); p.Push("\\n"); s = ++e; break; } default: { e++; break; } } } p.Push(s); return p.NewStr(); } return 0; } ////////////////////////////////////////////////////////////////////////////// Resource::Resource(AppWnd *w, int t, bool enabled) { AppWindow = w; ResType = t; Item = 0; SysObject = false; LAssert(AppWindow); } Resource::~Resource() { AppWindow->OnResourceDelete(this); if (Item) { Item->Obj = 0; DeleteObj(Item); } } bool Resource::IsSelected() { return Item?Item->Select():false; } bool Resource::Attach(LViewI *Parent) { LView *w = Wnd(); if (w) { return w->Attach(Parent); } return false; } ////////////////////////////////////////////////////////////////////////////// ResFolder::ResFolder(AppWnd *w, int t, bool enabled) : Resource(w, t, enabled) { Wnd()->Name(""); Wnd()->Enabled(enabled); } ////////////////////////////////////////////////////////////////////////////// ObjTreeItem::ObjTreeItem(Resource *Object) { if ((Obj = Object)) { Obj->Item = this; if (dynamic_cast(Object)) SetImage(ICON_FOLDER); else { int t = Object->Type(); switch (t) { case TYPE_CSS: SetImage(ICON_CSS); break; case TYPE_DIALOG: SetImage(ICON_DIALOG); break; case TYPE_STRING: SetImage(ICON_STRING); break; case TYPE_MENU: SetImage(ICON_MENU); break; } } } } ObjTreeItem::~ObjTreeItem() { if (Obj) { Obj->Item = 0; DeleteObj(Obj); } } const char *ObjTreeItem::GetText(int i) { if (Obj) { int Type = Obj->Type(); if (Type > 0) return Obj->Wnd()->Name(); else return TypeNames[-Type]; } return "#NO_OBJ"; } void ObjTreeItem::OnSelect() { if (Obj) { Obj->App()->OnResourceSelect(Obj); } } void ObjTreeItem::OnMouseClick(LMouse &m) { if (!Obj) return; if (m.IsContextMenu()) { Tree->Select(this); LSubMenu RClick; if (Obj->Wnd()->Enabled()) { if (Obj->Type() > 0) { // Resource RClick.AppendItem("Delete", IDM_DELETE, !Obj->SystemObject()); RClick.AppendItem("Rename", IDM_RENAME, !Obj->SystemObject()); } else { // Folder RClick.AppendItem("New", IDM_NEW, true); RClick.AppendSeparator(); auto Insert = RClick.AppendSub("Import from..."); if (Insert) { Insert->AppendItem("Lgi File", IDM_IMPORT, true); Insert->AppendItem("Win32 Resource Script", IDM_IMPORT_WIN32, false); } } // Custom entries if (!Obj->SystemObject()) { Obj->OnRightClick(&RClick); } } else { RClick.AppendItem("Not implemented", 0, false); } if (Tree->GetMouse(m, true)) { int Cmd = 0; switch (Cmd = RClick.Float(Tree, m.x, m.y)) { case IDM_NEW: { SerialiseContext Ctx; Obj->App()->NewObject(Ctx, 0, -Obj->Type()); break; } case IDM_DELETE: { Obj->App()->SetDirty(true); Obj->App()->DelObject(Obj); break; } case IDM_RENAME: { auto Dlg = new LInput(Tree, GetText(), "Enter the name for the object", "Object Name"); Dlg->DoModal([&](auto dlg, auto id) { if (id) { Obj->Wnd()->Name(Dlg->GetStr()); Update(); Obj->App()->SetDirty(true); } delete dlg; }); break; } case IDM_IMPORT: { auto Select = new LFileSelect(Obj->App()); Select->Type("Text", "*.txt"); Select->Open([&](auto dlg, auto status) { if (status) { LFile F; if (F.Open(dlg->Name(), O_READ)) { SerialiseContext Ctx; Resource *Res = Obj->App()->NewObject(Ctx, 0, -Obj->Type()); if (Res) { // TODO // Res->Read(); } } else { LgiMsg(Obj->App(), "Couldn't open file for reading."); } } delete dlg; }); break; } case IDM_IMPORT_WIN32: { /* List l; if (ImportWin32Dialogs(l, MainWnd)) { for (ResDialog *r = l.First(); r; r = l.Next()) { Obj->App()->InsertObject(TYPE_DIALOG, r); } } */ break; } default: { Obj->OnCommand(Cmd); break; } } } } } ////////////////////////////////////////////////////////////////////////////// FieldView::FieldView(AppWnd *app) : Fields(NextId, true) { NextId = 100; App = app; Source = 0; Ignore = true; SetTabStop(true); Sunken(true); #ifdef WIN32 SetExStyle(GetExStyle() | WS_EX_CONTROLPARENT); #endif } FieldView::~FieldView() { } void FieldView::Serialize(bool Write) { if (!Source) return; Ignore = !Write; Fields.SetMode(Write ? FieldTree::UiToObj : FieldTree::ObjToUi); Fields.SetView(this); Source->Serialize(Fields); /* for (DataDlgField *f=Fields.First(); f; f=Fields.Next()) { LViewI *v; if (GetViewById(f->GetCtrl(), v)) { switch (f->GetType()) { case DATA_STR: { if (Write) // Ctrl -> Options { char *s = v->Name(); Options->Set(f->GetOption(), s); } else // Options -> Ctrl { char *s = 0; Options->Get(f->GetOption(), s); v->Name(s?s:(char*)""); } break; } case DATA_BOOL: case DATA_INT: { if (Write) // Ctrl -> Options { char *s = v->Name(); if (s && (s = strchr(s, '\''))) { s++; char *e = strchr(s, '\''); int i = 0; if (e - s == 4) { memcpy(&i, s, 4); i = LgiSwap32(i); Options->Set(f->GetOption(), i); } } else { int i = v->Value(); Options->Set(f->GetOption(), i); } } else // Options -> Ctrl { int i = 0; Options->Get(f->GetOption(), i); if (i != -1 && (i & 0xff000000) != 0) { char m[8]; i = LgiSwap32(i); sprintf(m, "'%04.4s'", &i); v->Name(m); } else { v->Value(i); } } break; } case DATA_FLOAT: case DATA_PASSWORD: case DATA_STR_SYSTEM: default: { LAssert(0); break; } } } else LAssert(0); } */ Ignore = false; } class TextViewEdit : public LTextView3 { public: bool Multiline; TextViewEdit( int Id, int x, int y, int cx, int cy, LFontType *FontInfo = 0) : LTextView3(Id, x, y, cx, cy, FontInfo) { Multiline = false; #ifdef WIN32 SetDlgCode(DLGC_WANTARROWS | DLGC_WANTCHARS); #endif } bool OnKey(LKey &k) { if (!Multiline && (k.c16 == '\t' || k.c16 == LK_RETURN)) { return false; } return LTextView3::OnKey(k); } }; class Hr : public LView { public: Hr(int x1, int y, int x2) { LRect r(x1, y, x2, y+1); SetPos(r); } void OnPaint(LSurface *pDC) { LRect c = GetClient(); LThinBorder(pDC, c, DefaultSunkenEdge); } bool OnLayout(LViewLayoutInfo &Inf) { if (Inf.Width.Min) Inf.Height.Min = Inf.Height.Max = 2; else Inf.Width.Min = Inf.Width.Max = -1; return true; } }; void FieldView::OnDelete(FieldSource *s) { if (Source != NULL && Source == s) { // Clear fields Source->_FieldView = 0; Fields.Empty(); // remove all children LViewI *c; while ((c = Children[0])) { c->Detach(); DeleteObj(c); } Source = NULL; } } void FieldView::OnSelect(FieldSource *s) { Ignore = true; OnDelete(Source); if (Source) { // Clear fields Source->_FieldView = 0; Fields.Empty(); // remove all children LViewI *c; while ((c = Children[0])) { c->Detach(); DeleteObj(c); } Source = 0; } if (s) { // Add new fields Source = s; Source->_FieldView = AddDispatch(); if (Source->GetFields(Fields)) { LFontType Sys; Sys.GetSystemFont("System"); LTableLayout *t = new LTableLayout(IDC_TABLE); int Row = 0; LLayoutCell *Cell; LArray a; Fields.GetAll(a); for (int i=0; iLength(); n++, Row++) { FieldTree::Field *c = (*b)[n]; switch (c->Type) { case DATA_STR: case DATA_FLOAT: case DATA_INT: case DATA_FILENAME: { Cell = t->GetCell(0, Row); Cell->VerticalAlign(LCss::VerticalMiddle); Cell->Add(new LTextLabel(-1, 0, 0, -1, -1, c->Label)); TextViewEdit *Tv; Cell = t->GetCell(1, Row, true, c->Type == DATA_FILENAME ? 1 : 2); Cell->Add(Tv = new TextViewEdit(c->Id, 0, 0, 100, 20, &Sys)); if (Tv) { Tv->Multiline = c->Multiline; Tv->GetCss(true)->Height(LCss::Len(LCss::LenPx, c->Multiline ? LSysFont->GetHeight() * 8 : LSysFont->GetHeight() + 8)); Tv->SetWrapType(TEXTED_WRAP_NONE); Tv->Sunken(true); } if (c->Type == DATA_FILENAME) { Cell = t->GetCell(2, Row); Cell->Add(new LButton(-c->Id, 0, 0, 21, 21, "...")); } break; } case DATA_BOOL: { Cell = t->GetCell(1, Row, true, 2); Cell->Add(new LCheckBox(c->Id, 0, 0, -1, -1, c->Label)); break; } default: LAssert(!"Impl me."); break; } } if (i < a.Length() - 1) { Cell = t->GetCell(0, Row++, true, 3); Cell->Add(new Hr(0, 0, X()-1)); } } AddView(t); OnPosChange(); AttachChildren(); Invalidate(); } Serialize(false); Ignore = false; } } void FieldView::OnPosChange() { LRect c = GetClient(); c.Inset(6, 6); LViewI *v; if (GetViewById(IDC_TABLE, v)) v->SetPos(c); } LMessage::Result FieldView::OnEvent(LMessage *m) { switch (m->Msg()) { case M_OBJECT_CHANGED: { FieldSource *Src = (FieldSource*)m->A(); if (Src == Source) { Fields.SetMode(FieldTree::ObjToUi); Fields.SetView(this); Serialize(false); } else LAssert(0); break; } } return LLayout::OnEvent(m); } int FieldView::OnNotify(LViewI *Ctrl, LNotification n) { if (!Ignore) { LTextView3 *Tv = dynamic_cast(Ctrl); if (Tv && n.Type == LNotifyCursorChanged) { return 0; } LArray a; Fields.GetAll(a); for (int i=0; iLength(); n++) { FieldTree::Field *c = (*b)[n]; if (c->Id == Ctrl->GetId()) { // Write the value back to the objects Fields.SetMode(FieldTree::UiToObj); Fields.SetView(this); Source->Serialize(Fields); return 0; } else if (c->Id == -Ctrl->GetId()) { auto s = new LFileSelect(this); s->Open([&](auto dlg, auto status) { if (status) { auto File = App->GetCurFile(); if (File) { LFile::Path p = File; p--; auto Rel = LMakeRelativePath(p, dlg->Name()); if (Rel) SetCtrlName(c->Id, Rel); else SetCtrlName(c->Id, dlg->Name()); } else SetCtrlName(c->Id, dlg->Name()); Fields.SetMode(FieldTree::UiToObj); Fields.SetView(this); Source->Serialize(Fields); } delete dlg; }); } } } } return 0; } void FieldView::OnPaint(LSurface *pDC) { pDC->Colour(L_MED); pDC->Rectangle(); } ////////////////////////////////////////////////////////////////////////////// ObjContainer::ObjContainer(AppWnd *w) : LTree(100, 0, 0, 100, 100, "LgiResObjTree") { Window = w; Sunken(true); Insert(Style = new ObjTreeItem( new ResFolder(Window, -TYPE_CSS))); Insert(Dialogs = new ObjTreeItem( new ResFolder(Window, -TYPE_DIALOG))); Insert(Strings = new ObjTreeItem( new ResFolder(Window, -TYPE_STRING))); Insert(Menus = new ObjTreeItem( new ResFolder(Window, -TYPE_MENU))); const char *IconFile = "_icons.gif"; auto f = LFindFile(IconFile); if (f) { Images = LLoadImageList(f, 16, 16); if (Images) SetImageList(Images, false); else LgiTrace("%s:%i - failed to load '%s'\n", _FL, IconFile); } } ObjContainer::~ObjContainer() { DeleteObj(Images); } bool ObjContainer::AppendChildren(ObjTreeItem *Res, List &Lst) { bool Status = true; if (Res) { LTreeItem *Item = Res->GetChild(); while (Item) { ObjTreeItem *i = dynamic_cast(Item); if (i) Lst.Insert(i->GetObj()); else Status = false; Item = Item->GetNext(); } } return Status; } Resource *ObjContainer::CurrentResource() { ObjTreeItem *Item = dynamic_cast(Selection()); if (!Item) return NULL; return Item->GetObj(); } bool ObjContainer::ListObjects(List &Lst) { bool Status = AppendChildren(Style, Lst); Status &= AppendChildren(Dialogs, Lst); Status &= AppendChildren(Strings, Lst); Status &= AppendChildren(Menus, Lst); return Status; } ////////////////////////////////////////////////////////////////////////////// #ifdef WIN32 int Icon = IDI_ICON1; #else const char *Icon = "icon64.png"; #endif AppWnd::AppWnd() : LDocApp(AppName, Icon) { LastRes = 0; Fields = 0; ViewMenu = 0; ContentView = NULL; VBox = NULL; HBox = NULL; ShortCuts = 0; CurLang = -1; ShowLanguages.Add("en", true); if (_Create()) { LVariant Langs; if (GetOptions()->GetValue(OPT_ShowLanguages, Langs)) { ShowLanguages.Empty(); LToken L(Langs.Str(), ","); for (int i=0; iEmpty(); _Destroy(); } void AppWnd::OnCreate() { if (_LoadMenu("IDM_MENU")) { if (_FileMenu) { int n = 6; _FileMenu->AppendSeparator(n++); _FileMenu->AppendItem("Import Win32 Script", IDM_IMPORT_WIN32, true, n++); _FileMenu->AppendItem("Import LgiRes Language", IDM_IMPORT_LANG, true, n++); _FileMenu->AppendItem("Compare To File...", IDM_COMPARE, true, n++); _FileMenu->AppendSeparator(n++); _FileMenu->AppendItem("Properties", IDM_PROPERTIES, true, n++); } ViewMenu = Menu->FindSubMenu(IDM_VIEW); LAssert(ViewMenu); } else LgiTrace("%s:%i - _LoadMenu failed.\n", _FL); Status = 0; StatusInfo[0] = StatusInfo[1] = 0; HBox = new LBox(IDC_HBOX); if (HBox) { HBox->GetCss(true)->Padding("5px"); VBox = new LBox(IDC_VBOX, true); if (VBox) { HBox->AddView(VBox); VBox->AddView(Objs = new ObjContainer(this)); if (Objs) { Objs->AskImage(true); Objs->AskText(true); } VBox->AddView(Fields = new FieldView(this)); VBox->Value(200); } HBox->Value(240); HBox->Attach(this); } DropTarget(true); LString Open; if (LAppInst->GetOption("o", Open)) LoadLgi(Open); } void AppWnd::OnLanguagesChange(LLanguageId Lang, bool Add, bool Update) { bool Change = false; if (Lang) { // Update the list.... bool Has = false; for (int i=0; iId, Lang) == 0) { Has = true; if (!Add) { Languages.DeleteAt(i); Change = true; } break; } } if (Add && !Has) { Change = true; Languages.Add(LFindLang(Lang)); } } // Update the menu... if (ViewMenu && (Change || Update)) { // Remove existing language menu items while (ViewMenu->RemoveItem(2)); // Add new ones int n = 0; for (int i=0; iAppendItem(Lang->Name, IDM_LANG_BASE + n, true); if (Item) { if (CurLang == i) { Item->Checked(true); } } } } } } bool AppWnd::ShowLang(LLanguageId Lang) { return ShowLanguages.Find(Lang) != 0; } void AppWnd::ShowLang(LLanguageId Lang, bool Show) { // Apply change if (Show) { OnLanguagesChange(Lang, true); ShowLanguages.Add(Lang, true); } else { ShowLanguages.Delete(Lang); } // Store the setting for next time LStringPipe p; // const char *L; // for (bool i = ShowLanguages.First(&L); i; i = ShowLanguages.Next(&L)) for (auto i : ShowLanguages) { if (p.GetSize()) p.Push(","); p.Push(i.key); } char *Langs = p.NewStr(); if (Langs) { LVariant v; GetOptions()->SetValue(OPT_ShowLanguages, v = Langs); DeleteArray(Langs); } // Update everything List res; if (ListObjects(res)) { for (auto r: res) { r->OnShowLanguages(); } } } LLanguage *AppWnd::GetCurLang() { if (CurLang >= 0 && CurLang < Languages.Length()) return Languages[CurLang]; return LFindLang("en"); } void AppWnd::SetCurLang(LLanguage *L) { for (int i=0; iId == L->Id) { // Set new current CurLang = i; // Update everything List res; if (ListObjects(res)) { for (auto r: res) { r->OnShowLanguages(); } } break; } } } LArray *AppWnd::GetLanguages() { return &Languages; } class Test : public LView { COLOUR c; public: Test(COLOUR col, int x1, int y1, int x2, int y2) { c = col; LRect r(x1, y1, x2, y2); SetPos(r); _BorderSize = 1; Sunken(true); } void OnPaint(LSurface *pDC) { pDC->Colour(c, 24); pDC->Rectangle(); } }; LMessage::Result AppWnd::OnEvent(LMessage *m) { LMru::OnEvent(m); switch (m->Msg()) { case M_CHANGE: { LAutoPtr note((LNotification*)m->B()); return OnNotify((LViewI*) m->A(), *note); } case M_DESCRIBE: { char *Text = (char*) m->A(); if (Text) { SetStatusText(Text, STATUS_NORMAL); } break; } } return LWindow::OnEvent(m); } void _CountGroup(ResStringGroup *Grp, int &Words, int &Multi) { for (auto s: *Grp->GetStrs()) { if (s->Items.Length() > 1) { Multi++; char *e = s->Get("en"); if (e) { LToken t(e, " "); Words += t.Length(); } } } } int AppWnd::OnCommand(int Cmd, int Event, OsView Handle) { SerialiseContext Ctx; switch (Cmd) { case IDM_SHOW_LANG: { auto Dlg = new ShowLanguagesDlg(this); Dlg->DoModal([](auto dlg, auto ctrlId) { delete dlg; }); break; } case IDM_NEW_CSS: { NewObject(Ctx, 0, TYPE_CSS); break; } case IDM_NEW_DIALOG: { NewObject(Ctx, 0, TYPE_DIALOG); break; } case IDM_NEW_STRING_GRP: { NewObject(Ctx, 0, TYPE_STRING); break; } case IDM_NEW_MENU: { NewObject(Ctx, 0, TYPE_MENU); break; } case IDM_CLOSE: { Empty(); break; } case IDM_IMPORT_WIN32: { LoadWin32(); break; } case IDM_IMPORT_LANG: { ImportLang(); break; } case IDM_COMPARE: { Compare(); break; } case IDM_PROPERTIES: { List l; if (Objs->ListObjects(l)) { int Dialogs = 0; int Strings = 0; int Menus = 0; int Words = 0; int MultiLingual = 0; for (auto r: l) { switch (r->Type()) { case TYPE_DIALOG: { Dialogs++; break; } case TYPE_STRING: { ResStringGroup *Grp = dynamic_cast(r); if (Grp) { Strings += Grp->GetStrs()->Length(); _CountGroup(Grp, Words, MultiLingual); } break; } case TYPE_MENU: { Menus++; ResMenu *Menu = dynamic_cast(r); if (Menu) { if (Menu->Group) { Strings += Menu->Group->GetStrs()->Length(); _CountGroup(Menu->Group, Words, MultiLingual); } } break; } } } LgiMsg( this, "This file contains:\n" "\n" " Dialogs: %i\n" " Menus: %i\n" " Strings: %i\n" " Multi-lingual: %i\n" " Words: %i", AppName, MB_OK, Dialogs, Menus, Strings, MultiLingual, Words); } break; } case IDM_EXIT: { LCloseApp(); break; } case IDM_FIND: { auto s = new Search(this); s->DoModal([&](auto dlg, auto id) { if (id) new Results(this, s); delete dlg; }); break; } case IDM_NEXT: { LgiMsg(this, "Not implemented :(", AppName); break; } case IDM_CUT: { auto Focus = LAppInst->GetFocus(); if (Focus) { Focus->PostEvent(M_CUT); } else { Resource *r = Objs->CurrentResource(); if (r) r->Copy(true); } break; } case IDM_COPY: { auto Focus = LAppInst->GetFocus(); if (Focus) { Focus->PostEvent(M_COPY); } else { Resource *r = Objs->CurrentResource(); if (r) r->Copy(false); } break; } case IDM_PASTE: { auto Focus = LAppInst->GetFocus(); if (Focus) { Focus->PostEvent(M_PASTE); } else { Resource *r = Objs->CurrentResource(); if (r) r->Paste(); } break; } case IDM_TABLELAYOUT_TEST: { OpenTableLayoutTest(this); break; } case IDM_HELP: { char ExeName[MAX_PATH_LEN]; sprintf_s(ExeName, sizeof(ExeName), "%s", LGetExePath().Get()); while (strchr(ExeName, DIR_CHAR) && strlen(ExeName) > 3) { char p[256]; LMakePath(p, sizeof(p), ExeName, "index.html"); if (!LFileExists(p)) { LMakePath(p, sizeof(p), ExeName, "help"); LMakePath(p, sizeof(p), p, "index.html"); } if (LFileExists(p)) { LExecute(HelpFile, NULL, ExeName); break; } LTrimDir(ExeName); } break; } case IDM_SHOW_SHORTCUTS: { if (!ShortCuts) ShortCuts = new ShortCutView(this); break; } case IDM_ABOUT: { LAbout Dlg( this, AppName, APP_VER, "\nLgi Resource Editor (lr8 files).", "icon64.png", "http://www.memecode.com/lgi/res", "fret@memecode.com"); break; } default: { int Idx = Cmd - IDM_LANG_BASE; if (Idx >= 0 && Idx < Languages.Length()) { // Deselect the old lang auto Item = ViewMenu ? ViewMenu->ItemAt(CurLang + 2) : 0; if (Item) { Item->Checked(false); } // Set the current CurLang = Idx; // Set the new lang's menu item Item = ViewMenu ? ViewMenu->ItemAt(CurLang + 2) : 0; if (Item) { Item->Checked(true); } // Update everything List res; if (ListObjects(res)) { for (auto r: res) { r->OnShowLanguages(); } } } break; } } return LDocApp::OnCommand(Cmd, Event, Handle); } int AppWnd::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { default: { break; } } return 0; } void AppWnd::FindStrings(List &Strs, char *Define, int *CtrlId) { if (Objs) { List l; if (Objs->ListObjects(l)) { for (auto r: l) { StringList *s = r->GetStrs(); if (s) { for (auto Str: *s) { if (Define && ValidStr(Str->GetDefine())) { if (strcmp(Define, Str->GetDefine()) == 0) { Strs.Insert(Str); continue; } } if (CtrlId) { if (*CtrlId == Str->GetId()) { Strs.Insert(Str); continue; } } } } } } } } int AppWnd::GetUniqueCtrlId() { int Max = 0; if (Objs) { List l; if (Objs->ListObjects(l)) { LHashTbl, int> t; for (auto r: l) { StringList *sl = r->GetStrs(); if (sl) { for (auto s: *sl) { if (s->GetId() > 0 && !t.Find(s->GetId())) { t.Add(s->GetId(), s->GetId()); } Max = MAX(s->GetId(), Max); } } } int i = 500; while (true) { if (t.Find(i)) { i++; } else { return i; } } } } return Max + 1; } int AppWnd::GetUniqueStrRef(int Start) { if (!Objs) return -1; List l; if (!Objs->ListObjects(l)) return -1; LHashTbl, ResString*> Map; LArray Dupes; for (auto r: l) { ResStringGroup *Grp = r->GetStringGroup(); if (Grp) { List::I it = Grp->GetStrs()->begin(); for (ResString *s = *it; s; s = *++it) { if (s->GetRef()) { ResString *Existing = Map.Find(s->GetRef()); if (Existing) { // These get their ref's reset to a unique value as a side // effect of this function... Dupes.Add(s); } else { Map.Add(s->GetRef(), s); } } else { // auto Idx = Grp->GetStrs()->IndexOf(s); LAssert(!"No string ref?"); } } } } for (int i=Start; true; i++) { if (!Map.Find(i)) { if (Dupes.Length()) { ResString *s = Dupes[0]; Dupes.DeleteAt(0); s->SetRef(i); SetDirty(true); } else { return i; } } } return -1; } ResString *AppWnd::GetStrFromRef(int Ref) { ResString *Str = 0; if (Objs) { List l; if (Objs->ListObjects(l)) { for (auto r: l) { ResStringGroup *Grp = dynamic_cast(r); if (Grp) { if ((Str = Grp->FindRef(Ref))) break; } } } } return Str; } ResStringGroup *AppWnd::GetDialogSymbols() { if (Objs) { List l; if (Objs->ListObjects(l)) { for (auto r: l) { ResStringGroup *Grp = dynamic_cast(r); if (Grp) { auto ObjName = Grp->Wnd()->Name(); if (ObjName && stricmp(ObjName, StrDialogSymbols) == 0) { return Grp; } } } } } return NULL; } void AppWnd::OnReceiveFiles(LArray &Files) { auto f = Files.Length() ? Files[0] : 0; if (f) { _OpenFile(f, false); } } void AppWnd::SetStatusText(char *Text, int Pane) { if (Pane >= 0 && Pane < STATUS_MAX && StatusInfo[Pane]) { StatusInfo[Pane]->Name(Text); } } Resource *AppWnd::NewObject(SerialiseContext ctx, LXmlTag *load, int Type, bool Select) { Resource *r = 0; ObjTreeItem *Dir = 0; switch (Type) { case TYPE_CSS: { r = new ResCss(this); Dir = Objs->Style; break; } case TYPE_DIALOG: { r = new ResDialog(this); Dir = Objs->Dialogs; break; } case TYPE_STRING: { r = new ResStringGroup(this); Dir = Objs->Strings; break; } case TYPE_MENU: { r = new ResMenu(this); Dir = Objs->Menus; break; } } if (r) { ObjTreeItem *Item = new ObjTreeItem(r); if (Item) { Dir->Insert(Item); Dir->Update(); Dir->Expanded(true); if (Select) { Objs->Select(Item); } } r->Create(load, &ctx); if (Item) { Item->Update(); } SetDirty(true); } return r; } bool AppWnd::InsertObject(int Type, Resource *r, bool Select) { bool Status = false; if (r) { ObjTreeItem *Dir = 0; switch (Type) { case TYPE_CSS: { Dir = Objs->Style; break; } case TYPE_DIALOG: { Dir = Objs->Dialogs; break; } case TYPE_STRING: { Dir = Objs->Strings; break; } case TYPE_MENU: { Dir = Objs->Menus; break; } } if (Dir) { ObjTreeItem *Item = new ObjTreeItem(r); if (Item) { const char *Name = Item->GetText(); r->Item = Item; Dir->Insert(Item, (Name && Name[0] == '_') ? 0 : -1); Dir->Update(); Dir->Expanded(true); if (Select) { Objs->Select(Item); } Status = true; } } } return Status; } void AppWnd::DelObject(Resource *r) { OnResourceSelect(0); DeleteObj(r); } ObjTreeItem *GetTreeItem(LTreeItem *ti, Resource *r) { for (LTreeItem *i=ti->GetChild(); i; i=i->GetNext()) { ObjTreeItem *o = dynamic_cast(i); if (o) { if (o->GetObj() == r) return o; } o = GetTreeItem(i, r); if (o) return o; } return 0; } ObjTreeItem *GetTreeItem(LTree *ti, Resource *r) { for (LTreeItem *i=ti->GetChild(); i; i=i->GetNext()) { ObjTreeItem *o = dynamic_cast(i); if (o) { if (o->GetObj() == r) return o; } o = GetTreeItem(i, r); if (o) return o; } return 0; } void AppWnd::GotoObject(ResString *s, ResStringGroup *g, ResDialog *d, ResMenuItem *m, ResDialogCtrl *c) { if (s) { Resource *Res = 0; if (g) { Res = g; } else if (d) { Res = d; } else if (m) { Res = m->GetMenu(); } if (Res) { ObjTreeItem *ti = GetTreeItem(Objs, Res); if (ti) { ti->Select(true); if (g) { s->GetList()->Select(0); s->ScrollTo(); LYield(); s->Select(true); } else if (d) { LYield(); d->SelectCtrl(c); } else if (m) { for (LTreeItem *i=m; i; i=i->GetParent()) { i->Expanded(true); } m->Select(true); m->ScrollTo(); } } else { printf("%s:%i - couldn't find resources tree item\n", _FL); } } } } bool AppWnd::ListObjects(List &Lst) { if (Objs) { return Objs->ListObjects(Lst); } return false; } void AppWnd::OnObjChange(FieldSource *r) { if (Fields) { Fields->Serialize(false); SetDirty(true); } } void AppWnd::OnObjSelect(FieldSource *r) { if (Fields) Fields->OnSelect(r); } void AppWnd::OnObjDelete(FieldSource *r) { if (Fields) { Fields->OnDelete(r); } } void AppWnd::OnResourceDelete(Resource *r) { } void AppWnd::OnResourceSelect(Resource *r) { if (LastRes) { OnObjSelect(NULL); if (ContentView) { ContentView->Detach(); DeleteObj(ContentView); } LastRes = NULL; } if (r) { ContentView = r->CreateUI(); if (ContentView) { if (HBox) ContentView->Attach(HBox); LastRes = r; } } } char *TagName(LXmlTag *t) { static char Buf[1024]; LArray Tags; for (; t; t = t->Parent) { Tags.AddAt(0, t); } Buf[0] = 0; for (int i=0; iGetTag()); } return Buf; } class ResCompare : public LWindow, public LResourceLoad { LList *Lst; public: ResCompare(const char *File1, const char *File2) { Lst = 0; LRect p; LAutoString n; if (LoadFromResource(IDD_COMPARE, this, &p, &n)) { SetPos(p); Name(n); MoveToCenter(); GetViewById(IDC_DIFFS, Lst); if (Attach(0)) { Visible(true); AttachChildren(); LXmlTag *t1 = new LXmlTag; LXmlTag *t2 = new LXmlTag; if (t1 && File1) { LFile f; if (f.Open(File1, O_READ)) { LXmlTree x(GXT_NO_ENTITIES); if (!x.Read(t1, &f, 0)) { DeleteObj(t1); } } else { DeleteObj(t1); } } if (t2 && File2) { LFile f; if (f.Open(File2, O_READ)) { LXmlTree x(GXT_NO_ENTITIES); if (!x.Read(t2, &f, 0)) { DeleteObj(t2); } } else { DeleteObj(t2); } } if (Lst && t1 && t2) { Lst->Enabled(false); Compare(t1, t2); Lst->Enabled(true); } DeleteObj(t1); DeleteObj(t2); } } } void Compare(LXmlTag *t1, LXmlTag *t2) { char s[1024]; if (stricmp(t1->GetTag(), t2->GetTag()) != 0) { sprintf(s, "Different Tag: '%s' <-> '%s'", t1->GetTag(), t2->GetTag()); LListItem *i = new LListItem; if (i) { i->SetText(s); i->SetText(TagName(t1), 1); Lst->Insert(i); } } LHashTbl,LXmlAttr*> a; for (int i=0; iAttr.Length(); i++) { LXmlAttr *a1 = &t1->Attr[i]; a.Add(a1->GetName(), a1); } for (int n=0; nAttr.Length(); n++) { LXmlAttr *a2 = &t2->Attr[n]; LXmlAttr *a1 = (LXmlAttr*) a.Find(a2->GetName()); if (a1) { if (strcmp(a1->GetValue(), a2->GetValue()) != 0) { sprintf(s, "Different Attr Value: '%s' <-> '%s'", a1->GetValue(), a2->GetValue()); LListItem *i = new LListItem; if (i) { i->SetText(s); sprintf(s, "%s.%s", TagName(t1), a1->GetName()); i->SetText(s, 1); Lst->Insert(i); } } a.Delete(a2->GetName()); } else { sprintf(s, "[Right] Missing Attr: '%s' = '%s'", a2->GetName(), a2->GetValue()); LListItem *i = new LListItem; if (i) { i->SetText(s); i->SetText(TagName(t1), 1); Lst->Insert(i); } } } // char *Key; // for (void *v = a.First(&Key); v; v = a.Next(&Key)) for (auto v : a) { LXmlAttr *a1 = v.value; sprintf(s, "[Left] Missing Attr: '%s' = '%s'", a1->GetName(), a1->GetValue()); LListItem *i = new LListItem; if (i) { i->SetText(s); i->SetText(TagName(t1), 1); Lst->Insert(i); } } if (t1->IsTag("string-group")) { LArray r1, r2; for (auto t: t1->Children) { char *Ref; if ((Ref = t->GetAttr("ref"))) { int r = atoi(Ref); if (r) { r1[r] = t; } } } for (auto t: t2->Children) { char *Ref; if ((Ref = t->GetAttr("ref"))) { int r = atoi(Ref); if (r) { r2[r] = t; } } } auto Max = MAX(r1.Length(), r2.Length()); for (int i = 0; iGetAttr("ref"), r1[i]->GetAttr("Define")); LListItem *n = new LListItem; if (n) { n->SetText(s); n->SetText(TagName(r1[i]), 1); Lst->Insert(n); } } else if (r2[i]) { sprintf(s, "[Left] Missing String: Ref=%s, Def=%s", r2[i]->GetAttr("ref"), r2[i]->GetAttr("Define")); LListItem *n = new LListItem; if (n) { n->SetText(s); n->SetText(TagName(r2[i]), 1); Lst->Insert(n); } } } } else { LXmlTag *c1 = t1->Children[0]; LXmlTag *c2 = t2->Children[0]; while (c1 && c2) { Compare(c1, c2); c1 = t1->Children[0]; c2 = t2->Children[0]; } } LYield(); } void OnPosChange() { LRect c = GetClient(); if (Lst) { c.Inset(7, 7); Lst->SetPos(c); } } }; void AppWnd::Compare() { auto s = new LFileSelect(this); s->Type("Lgi Resource", "*.lr8"); s->Open([&](auto dlg, auto status) { if (status) new ResCompare(GetCurFile(), dlg->Name()); delete dlg; }); } void AppWnd::ImportLang() { // open dialog auto Select = new LFileSelect(this); Select->Type("Lgi Resources", "*.lr8;*.xml"); Select->Open([&](auto dlg, auto status) { if (status) { LFile F; if (F.Open(dlg->Name(), O_READ)) { SerialiseContext Ctx; Ctx.Format = GetFormat(dlg->Name()); // convert file to Xml objects LXmlTag *Root = new LXmlTag; if (Root) { LXmlTree Tree(GXT_NO_ENTITIES); if (Tree.Read(Root, &F, 0)) { List Menus; List Groups; for (auto t: Root->Children) { if (t->IsTag("menu")) { ResMenu *Menu = new ResMenu(this); if (Menu && Menu->Read(t, Ctx)) { Menus.Insert(Menu); } else break; } else if (t->IsTag("string-group")) { ResStringGroup *g = new ResStringGroup(this); if (g && g->Read(t, Ctx)) { Groups.Insert(g); } else break; } } Ctx.PostLoad(this); bool HasData = false; for (auto g: Groups) { g->SetLanguages(); if (g->GetStrs()->Length() > 0 && g->GetLanguages() > 0) { HasData = true; } } if (HasData) { List Langs; for (auto g: Groups) { for (int i=0; iGetLanguages(); i++) { LLanguage *Lang = g->GetLanguage(i); if (Lang) { bool Has = false; for (auto l: Langs) { if (stricmp((char*)l, (char*)Lang) == 0) { Has = true; break; } } if (!Has) { Langs.Insert(Lang); } } } } auto Dlg = new LangDlg(this, Langs); Dlg->DoModal([&](auto dlg, auto id) { if (id == IDOK && Dlg->Lang) { LStringPipe Errors; int Matches = 0; int NotFound = 0; int Imported = 0; int Different = 0; for (auto g: Groups) { List::I Strings = g->GetStrs()->begin(); for (ResString *s=*Strings; s; s=*++Strings) { ResString *d = GetStrFromRef(s->GetRef()); if (d) { Matches++; char *Str = s->Get(Dlg->Lang->Id); char *Dst = d->Get(Dlg->Lang->Id); if ( ( Str && Dst && strcmp(Dst, Str) != 0 ) || ( (Str != 0) ^ (Dst != 0) ) ) { Different++; d->Set(Str, Dlg->Lang->Id); Imported++; } } else { NotFound++; char e[256]; sprintf(e, "String ref=%i (%s)\n", s->GetRef(), s->GetDefine()); Errors.Push(e); } } } List Lst; if (ListObjects(Lst)) { for (auto m: Menus) { // find matching menu in our list ResMenu *Match = 0; for (auto r: Lst) { ResMenu *n = dynamic_cast(r); if (n && stricmp(n->Name(), m->Name()) == 0) { Match = n; break; } } if (Match) { // match strings List *Src = m->GetStrs(); List *Dst = Match->GetStrs(); for (auto s: *Src) { bool FoundRef = false; for (auto d: *Dst) { if (s->GetRef() == d->GetRef()) { FoundRef = true; char *Str = s->Get(Dlg->Lang->Id); if (Str) { char *Dst = d->Get(Dlg->Lang->Id); if (!Dst || strcmp(Dst, Str)) { Different++; } d->Set(Str, Dlg->Lang->Id); Imported++; } break; } } if (!FoundRef) { NotFound++; char e[256]; sprintf(e, "MenuString ref=%i (%s)\n", s->GetRef(), s->GetDefine()); Errors.Push(e); } } Match->SetLanguages(); } } for (auto r: Lst) { ResStringGroup *StrRes = dynamic_cast(r); if (StrRes) { StrRes->SetLanguages(); } } } char *ErrorStr = Errors.NewStr(); LgiMsg( this, "Imported: %i\n" "Matched: %i\n" "Not matched: %i\n" "Different: %i\n" "Total: %i\n" "\n" "Import complete.\n" "\n%s", AppName, MB_OK, Imported, Matches, NotFound, Different, Matches + NotFound, ErrorStr?ErrorStr:(char*)""); } delete dlg; }); } else { LgiMsg(this, "No language information to import", AppName, MB_OK); } // Groups.DeleteObjects(); // Menus.DeleteObjects(); } else { LgiMsg(this, "Failed to parse XML from file.\nError: %s", AppName, MB_OK, Tree.GetErrorMsg()); } DeleteObj(Root); } } } delete dlg; }); } bool AppWnd::Empty() { // Delete any existing objects List l; if (ListObjects(l)) { for (auto It = l.begin(); It != l.end(); ) { auto r = *It; if (r->SystemObject()) l.Delete(It); else It++; } for (auto r: l) { DelObject(r); } } return true; } bool AppWnd::OpenFile(const char *FileName, bool Ro) { if (stristr(FileName, ".lr8") || stristr(FileName, ".xml")) { return LoadLgi(FileName); } else if (stristr(FileName, ".rc")) { LoadWin32(FileName); return true; } return false; } bool AppWnd::SaveFile(const char *FileName) { if (stristr(FileName, ".lr8") || stristr(FileName, ".xml")) { return SaveLgi(FileName); } else if (stristr(FileName, ".rc")) { } return false; } void AppWnd::GetFileTypes(LFileSelect *Dlg, bool Write) { Dlg->Type("Lgi Resources", "*.lr8;*.xml"); if (!Write) { Dlg->Type("All Files", LGI_ALL_FILES); } } // Lgi load/save bool AppWnd::TestLgi(bool Quite) { bool Status = true; List l; if (ListObjects(l)) { ErrorCollection Errors; for (auto r: l) { Status &= r->Test(&Errors); } if (Errors.StrErr.Length() > 0) { LStringPipe Sample; for (int i=0; iGetRef(), s->GetDefine(), Errors.StrErr[i].Msg.Get()); } char *Sam = Sample.NewStr(); LgiMsg(this, "%i strings have errors.\n\n%s", AppName, MB_OK, Errors.StrErr.Length(), Sam); DeleteArray(Sam); } else if (!Quite) { LgiMsg(this, "Object are all ok.", AppName); } } return Status; } #define PROFILE_LOAD 0 #if PROFILE_LOAD #define PROF(s) prof.Add(s) #else #define PROF(s) #endif bool AppWnd::LoadLgi(const char *FileName) { #if PROFILE_LOAD LProfile prof("LoadLgi"); #endif Empty(); if (!FileName) return false; PROF("fOpen"); LFile f; if (!f.Open(FileName, O_READ)) return false; PROF("prog"); LAutoPtr Progress(new LProgressDlg(this)); Progress->SetDescription("Initializing..."); Progress->SetType("Tags"); LAutoPtr Root(new LXmlTag); if (!Root) return false; // convert file to Xml objects LXmlTree Xml(0); Progress->SetDescription("Lexing..."); PROF("xml.read"); if (!Xml.Read(Root, &f, 0)) { LgiMsg(this, "Xml read failed: %s", AppName, MB_OK, Xml.GetErrorMsg()); return false; } Progress->SetRange(Root->Children.Length()); PROF("xml to objs"); // convert Xml list into objects SerialiseContext Ctx; for (auto t: Root->Children) { Progress->Value(Root->Children.IndexOf(t)); int RType = 0; if (t->IsTag("dialog")) RType = TYPE_DIALOG; else if (t->IsTag("string-group")) RType = TYPE_STRING; else if (t->IsTag("menu")) RType = TYPE_MENU; else if (t->IsTag("style")) RType = TYPE_CSS; else LAssert(!"Unexpected tag"); if (RType > 0) NewObject(Ctx, t, RType, false); } PROF("postload"); Ctx.PostLoad(this); PROF("sort"); SortDialogs(); PROF("test"); TestLgi(); PROF("scan langs"); // Scan for languages and update the view lang menu Languages.Length(0); LHashTbl, LLanguage*> Langs; if (!ViewMenu) return false; PROF("remove menu items"); // Remove existing language menu items while (ViewMenu->RemoveItem(1)); ViewMenu->AppendSeparator(); PROF("enum objs"); // Enumerate all languages List res; if (ListObjects(res)) { for (auto r: res) { ResStringGroup *Sg = r->IsStringGroup(); if (Sg) { for (int i=0; iGetLanguages(); i++) { LLanguage *Lang = Sg->GetLanguage(i); if (Lang) { Langs.Add(Lang->Id, Lang); } } } } } PROF("update langs"); // Update languages array int n = 0; for (auto i : Langs) { Languages.Add(i.value); auto Item = ViewMenu->AppendItem(i.value->Name, IDM_LANG_BASE + n, true); if (Item && i.value->IsEnglish()) { Item->Checked(true); CurLang = n; } n++; } PROF("none menu"); if (Languages.Length() == 0) ViewMenu->AppendItem("(none)", -1, false); return true; } void SerialiseContext::PostLoad(AppWnd *App) { for (int i=0; iGetUniqueCtrlId(); s->SetId(Id); Log.Print("Repaired CtrlId of string ref %i to %i\n", s->GetRef(), Id); } LAutoString a(Log.NewStr()); if (ValidStr(a)) { LgiMsg(App, "%s", "Load Warnings", MB_OK, a.Get()); } } int DialogNameCompare(ResDialog *a, ResDialog *b, NativeInt Data) { const char *A = (a)?a->Name():0; const char *B = (b)?b->Name():0; if (A && B) return stricmp(A, B); return -1; } void AppWnd::SortDialogs() { List Lst; if (ListObjects(Lst)) { List Dlgs; for (auto r: Lst) { ResDialog *Dlg = dynamic_cast(r); if (Dlg) { Dlgs.Insert(Dlg); Dlg->Item->Remove(); } } Dlgs.Sort(DialogNameCompare); for (auto d: Dlgs) { Objs->Dialogs->Insert(d->Item); } } } class ResTreeNode { public: char *Str; ResTreeNode *a, *b; ResTreeNode(char *s) { a = b = 0; Str = s; } ~ResTreeNode() { DeleteArray(Str); DeleteObj(a); DeleteObj(b); } void Enum(List &l) { if (a) { a->Enum(l); } if (Str) { l.Insert(Str); } if (b) { b->Enum(l); } } bool Add(char *s) { int Comp = (Str && s) ? stricmp(Str, s) : -1; if (Comp == 0) { return false; } if (Comp < 0) { if (a) { return a->Add(s); } else { a = new ResTreeNode(s); } } if (Comp > 0) { if (b) { return b->Add(s); } else { b = new ResTreeNode(s); } } return true; } }; class ResTree { ResTreeNode *Root; public: ResTree() { Root = 0; } ~ResTree() { DeleteObj(Root); } bool Add(char *s) { if (s) { if (!Root) { Root = new ResTreeNode(NewStr(s)); return true; } else { return Root->Add(NewStr(s)); } } return false; } void Enum(List &l) { if (Root) { Root->Enum(l); } } }; const char *HeaderStr = "// This file generated by LgiRes\r\n\r\n"; struct DefinePair { char *Name; int Value; }; int PairCmp(DefinePair *a, DefinePair *b) { return a->Value - b->Value; } bool AppWnd::WriteDefines(LStream &Defs) { bool Status = false; ResTree Tree; // Empty file Defs.Write(HeaderStr, strlen(HeaderStr)); // make a unique list of #define's List Lst; if (ListObjects(Lst)) { LHashTbl,int> Def; LHashTbl,char*> Ident; for (auto r: Lst) { List *StrList = r->GetStrs(); if (StrList) { Status = true; List::I sl = StrList->begin(); for (ResString *s = *sl; s; s = *++sl) { if (ValidStr(s->GetDefine())) { if (stricmp(s->GetDefine(), "IDOK") == 0) { s->SetId(IDOK); } else if (stricmp(s->GetDefine(), "IDCANCEL") == 0) { s->SetId(IDCANCEL); } else if (stricmp(s->GetDefine(), "IDC_STATIC") == 0) { s->SetId(-1); } else if (stricmp(s->GetDefine(), "-1") == 0) { s->SetDefine(0); } else { // Remove dupe ID's char IdStr[32]; sprintf(IdStr, "%i", s->GetId()); char *Define; if ((Define = Ident.Find(IdStr))) { if (strcmp(Define, s->GetDefine())) { List n; FindStrings(n, s->GetDefine()); int NewId = GetUniqueCtrlId(); for (auto Ns: n) { Ns->SetId(NewId); } } } else { Ident.Add(IdStr, s->GetDefine()); } // Make all define's the same int CtrlId; if ((CtrlId = Def.Find(s->GetDefine()))) { // Already there... s->SetId(CtrlId); } else { // Add... LAssert(s->GetId()); if (s->GetId()) Def.Add(s->GetDefine(), s->GetId()); } } } } } } // write the list out LArray Pairs; // char *s = 0; // for (int i = Def.First(&s); i; i = Def.Next(&s)) for (auto i : Def) { if (ValidStr(i.key) && stricmp(i.key, "IDOK") != 0 && stricmp(i.key, "IDCANCEL") != 0 && stricmp(i.key, "IDC_STATIC") != 0 && stricmp(i.key, "-1") != 0) { DefinePair &p = Pairs.New(); p.Name = i.key; p.Value = i.value; } } Pairs.Sort(PairCmp); for (int n=0; n=' ' && (uint8_t)(c) <= 127) if (IsPrintable(s[0]) && IsPrintable(s[1]) && IsPrintable(s[2]) && IsPrintable(s[3])) { #ifndef __BIG_ENDIAN__ int32 i = LgiSwap32(p.Value); memcpy(s, &i, 4); #endif Defs.Print("#define %s%s'%04.4s'\r\n", p.Name, Tab, s); } else Defs.Print("#define %s%s%i\r\n", p.Name, Tab, p.Value); } } return Status; } bool AppWnd::SaveLgi(const char *FileName) { bool Status = false; if (!TestLgi()) { if (LgiMsg(this, "Do you want to save the file with errors?", AppName, MB_YESNO) == IDNO) return false; } // Rename the existing file to 'xxxxxx.bak' if (LFileExists(FileName)) { char Bak[MAX_PATH_LEN]; strcpy_s(Bak, sizeof(Bak), FileName); char *e = LGetExtension(Bak); if (e) { strcpy(e, "bak"); if (LFileExists(Bak)) FileDev->Delete(Bak, false); FileDev->Move(FileName, Bak); } } // Save the file to xml if (FileName) { LFile f; LFile::Path DefsName = FileName; DefsName += "../resdefs.h"; LStringPipe Defs; if (f.Open(FileName, O_WRITE)) { SerialiseContext Ctx; f.SetSize(0); Defs.SetSize(0); Defs.Print("// Generated by LgiRes\r\n\r\n"); List l; if (ListObjects(l)) { // Remove all duplicate symbol Id's from the dialogs for (auto r: l) { ResDialog *Dlg = dynamic_cast(r); if (Dlg) Dlg->CleanSymbols(); } // write defines WriteDefines(Defs); LXmlTag Root("resources"); // Write all string lists out first so that when we load objects // back in again the strings will already be loaded and can // be referenced for (auto r: l) { if (r->Type() == TYPE_STRING) { LXmlTag *c = new LXmlTag; if (c && r->Write(c, Ctx)) { Root.InsertTag(c); } else { LAssert(0); DeleteObj(c); } } } // now write the rest of the objects out for (auto r: l) { if (r->Type() != TYPE_STRING) { LXmlTag *c = new LXmlTag; if (c && r->Write(c, Ctx)) { Root.InsertTag(c); } else { r->Write(c, Ctx); LAssert(0); DeleteObj(c); } } } // Set the offset type. // // Older versions of LgiRes stored the dialog's controls at a fixed // offset (3,17) from where they should've been. That was fixed, but // to differentiate between the 2 systems, we store a tag at the // root element. Root.SetAttr("Offset", 1); LXmlTree Tree(GXT_NO_ENTITIES); Status = Tree.Write(&Root, &f); if (Status) { // Also write the header... but only if it's changed... auto DefsContent = Defs.NewGStr(); LAutoString OldDefsContent(LReadTextFile(DefsName)); if (Strcmp(DefsContent.Get(), OldDefsContent.Get())) { LFile DefsFile; if (!DefsFile.Open(DefsName, O_WRITE)) goto FileErrorMsg; DefsFile.SetSize(0); DefsFile.Write(DefsContent); } } } } else { FileErrorMsg: LgiMsg(this, "Couldn't open these files for output:\n" "\t%s\n" "\t%s\n" "\n" "Maybe they are read only or locked by another application.", AppName, MB_OK, FileName, DefsName.GetFull().Get()); } } return Status; } // Win32 load/save #define ADJUST_CTRLS_X 2 #define ADJUST_CTRLS_Y 12 #define IMP_MODE_SEARCH 0 #define IMP_MODE_DIALOG 1 #define IMP_MODE_DLG_CTRLS 2 #define IMP_MODE_STRINGS 3 #define IMP_MODE_MENU 4 #include "lgi/common/Token.h" class ImportDefine { public: char *Name; char *Value; ImportDefine() { Name = Value = 0; } ImportDefine(char *Line) { Name = Value = 0; if (Line && *Line == '#') { Line++; if (strnicmp(Line, "define", 6) == 0) { Line += 6; Line = LSkipDelim(Line); char *Start = Line; const char *WhiteSpace = " \r\n\t"; while (*Line && !strchr(WhiteSpace, *Line)) { Line++; } Name = NewStr(Start, Line-Start); Line = LSkipDelim(Line); Start = Line; while (*Line && !strchr(WhiteSpace, *Line)) { Line++; } if (Start != Line) { Value = NewStr(Start, Line-Start); } } } } ~ImportDefine() { DeleteArray(Name); DeleteArray(Value); } }; class DefineList : public List { int NestLevel; public: bool Defined; List IncludeDirs; DefineList() { Defined = true; NestLevel = 0; } ~DefineList() { for (auto i: *this) { DeleteObj(i); } for (auto c: IncludeDirs) { DeleteArray(c); } } void DefineSymbol(const char *Name, const char *Value = 0) { ImportDefine *Def = new ImportDefine; if (Def) { Def->Name = NewStr(Name); if (Value) Def->Value = NewStr(Value); Insert(Def); } } ImportDefine *GetDefine(char *Name) { if (Name) { for (auto i: *this) { if (i->Name && stricmp(i->Name, Name) == 0) { return i; } } } return NULL; } void ProcessLine(char *Line) { if (NestLevel > 16) { return; } if (Line && *Line == '#') { Line++; LToken T(Line); if (T.Length() > 0) { if (stricmp(T[0], "define") == 0) // #define { ImportDefine *Def = new ImportDefine(Line-1); if (Def) { if (Def->Name) { Insert(Def); } else { DeleteObj(Def); } } } else if (stricmp(T[0], "include") == 0) // #include { NestLevel++; LFile F; if (T.Length() > 1) { for (auto IncPath: IncludeDirs) { char FullPath[256]; strcpy(FullPath, IncPath); if (FullPath[strlen(FullPath)-1] != DIR_CHAR) { strcat(FullPath, DIR_STR); } strcat(FullPath, T[1]); if (F.Open(FullPath, O_READ)) { char Line[1024]; while (!F.Eof()) { F.ReadStr(Line, sizeof(Line)); char *p = LSkipDelim(Line); if (*p == '#') { ProcessLine(p); } } break; } } } NestLevel--; } else if (stricmp(T[0], "if") == 0) // #if { } else if (stricmp(T[0], "ifdef") == 0) // #if { if (T.Length() > 1) { Defined = GetDefine(T[1]) != 0; } } else if (stricmp(T[0], "endif") == 0) // #endif { Defined = true; } else if (stricmp(T[0], "pragma") == 0) { ImportDefine *Def = new ImportDefine; if (Def) { char *Str = Line + 7; char *First = strchr(Str, '('); char *Second = (First) ? strchr(First+1, ')') : 0; if (First && Second) { Insert(Def); Def->Name = NewStr(Str, First-Str); First++; Def->Value = NewStr(First, Second-First); } else { DeleteObj(Def); } } } else if (stricmp(T[0], "undef") == 0) { } } } // else it's not for us anyway } }; void TokLine(LArray &T, char *Line) { if (Line) { // Exclude comments for (int k=0; Line[k]; k++) { if (Line[k] == '/' && Line[k+1] == '/') { Line[k] = 0; break; } } // Break into tokens for (const char *s = Line; s && *s; ) { while (*s && strchr(" \t", *s)) s++; char *t = LTokStr(s); if (t) T.Add(t); else break; } } } void AppWnd::LoadWin32(const char *FileName) { bool Status = false; LHashTbl,bool> CtrlNames; CtrlNames.Add("LTEXT", true); CtrlNames.Add("EDITTEXT", true); CtrlNames.Add("COMBOBOX", true); CtrlNames.Add("SCROLLBAR", true); CtrlNames.Add("GROUPBOX", true); CtrlNames.Add("PUSHBUTTON", true); CtrlNames.Add("DEFPUSHBUTTON", true); CtrlNames.Add("CONTROL", true); CtrlNames.Add("ICON", true); CtrlNames.Add("LISTBOX", true); Empty(); auto Load = [&](const char *FileName) { if (!FileName) return; LProgressDlg Progress(this); Progress.SetDescription("Initializing..."); Progress.SetType("K"); Progress.SetScale(1.0/1024.0); char *FileTxt = LReadTextFile(FileName); if (FileTxt) { LToken Lines(FileTxt, "\r\n"); DeleteArray(FileTxt); DefineList Defines; ResStringGroup *String = new ResStringGroup(this); int Mode = IMP_MODE_SEARCH; // Language char *Language = 0; LLanguageId LanguageId = 0; // Dialogs List DlLList; CtrlDlg *Dlg = 0; // Menus ResDialog *Dialog = 0; ResMenu *Menu = 0; List Menus; ResMenuItem *MenuItem[32]; int MenuLevel = 0; bool MenuNewLang = false; int MenuNextItem = 0; // Include defines char IncPath[256]; strcpy(IncPath, FileName); LTrimDir(IncPath); Defines.IncludeDirs.Insert(NewStr(IncPath)); Defines.DefineSymbol("_WIN32"); Defines.DefineSymbol("IDC_STATIC", "-1"); DoEvery Ticker(200); Progress.SetDescription("Reading resources..."); Progress.SetRange(Lines.Length()); if (String) { InsertObject(TYPE_STRING, String, false); } for (int CurLine = 0; CurLine < Lines.Length(); CurLine++) { if (Ticker.DoNow()) { Progress.Value(CurLine); LYield(); } // Skip white space char *Line = Lines[CurLine]; char *p = LSkipDelim(Line); Defines.ProcessLine(Line); // Tokenize LArray T; TokLine(T, Line); // Process line if (Defines.Defined) { switch (Mode) { case IMP_MODE_SEARCH: { DeleteObj(Dialog); Dlg = 0; if (*p != '#') { if (T.Length() > 1 && (stricmp(T[1], "DIALOG") == 0 || stricmp(T[1], "DIALOGEX") == 0)) { Mode = IMP_MODE_DIALOG; Dialog = new ResDialog(this); if (Dialog) { Dialog->Create(NULL, NULL); Dialog->Name(T[0]); auto It = Dialog->IterateViews(); Dlg = dynamic_cast(It[0]); if (Dlg) { int Pos[4] = {0, 0, 0, 0}; int i = 0; for (; iResDialogCtrl::SetPos(r); Dlg->GetStr()->SetDefine(T[0]); ImportDefine *Def = Defines.GetDefine(T[0]); if (Def) { Dlg->GetStr()->SetId(atoi(Def->Value)); } } } break; } if (T.Length() > 1 && stricmp(T[1], "MENU") == 0) { ZeroObj(MenuItem); Mode = IMP_MODE_MENU; Menu = 0; // Check for preexisting menu in another language MenuNewLang = false; for (auto m: Menus) { if (stricmp(m->Name(), T[0]) == 0) { MenuNewLang = true; Menu = m; break; } } // If it doesn't preexist then create it if (!Menu) { Menu = new ResMenu(this); if (Menu) { Menus.Insert(Menu); Menu->Create(NULL, NULL); Menu->Name(T[0]); } } break; } if (T.Length() > 0 && stricmp(T[0], "STRINGTABLE") == 0) { Mode = IMP_MODE_STRINGS; if (String) { String->Name("_Win32 Imports_"); } break; } if (T.Length() > 2 && stricmp(T[0], "LANGUAGE") == 0) { LanguageId = 0; DeleteArray(Language); char *Language = NewStr(T[1]); if (Language) { LLanguage *Info = LFindLang(0, Language); if (Info) { LanguageId = Info->Id; ResDialog::AddLanguage(Info->Id); } } break; } } break; } case IMP_MODE_DIALOG: { if (T.Length() > 0 && Dlg) { if (stricmp(T[0], "CAPTION") == 0) { char *Caption = T[1]; if (Caption) { Dlg->GetStr()->Set(Caption, LanguageId); } } else if (stricmp(T[0], "BEGIN") == 0) { Mode = IMP_MODE_DLG_CTRLS; } } break; } case IMP_MODE_DLG_CTRLS: { char *Type = T[0]; if (!Type) break; if (stricmp(Type, "end") != 0) { // Add wrapped content to the token array. char *Next = Lines[CurLine+1]; if (Next) { Next = LSkipDelim(Next); char *NextTok = LTokStr((const char*&)Next); if (NextTok) { if (stricmp(NextTok, "END") != 0 && !CtrlNames.Find(NextTok)) { TokLine(T, Lines[++CurLine]); } DeleteArray(NextTok); } } } // Process controls if (stricmp(Type, "LTEXT") == 0) { if (T.Length() >= 7) { CtrlText *Ctrl = new CtrlText(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->Set(T[1], LanguageId); Ctrl->GetStr()->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "EDITTEXT") == 0) { if (T.Length() >= 7) { CtrlEditbox *Ctrl = new CtrlEditbox(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "COMBOBOX") == 0) { if (T.Length() >= 6) { CtrlComboBox *Ctrl = new CtrlComboBox(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "SCROLLBAR") == 0) { if (T.Length() == 6) { CtrlScrollBar *Ctrl = new CtrlScrollBar(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "GROUPBOX") == 0) { if (T.Length() >= 7) { CtrlGroup *Ctrl = new CtrlGroup(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->Set(T[1], LanguageId); Ctrl->GetStr()->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "PUSHBUTTON") == 0 || stricmp(Type, "DEFPUSHBUTTON") == 0) { if (T.Length() >= 7) { CtrlButton *Ctrl = new CtrlButton(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->Set(T[1], LanguageId); Ctrl->GetStr()->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "CONTROL") == 0) { if (T.Length() >= 7) { char *Caption = T[1]; char *Id = T[2]; char *Type = T[3]; bool Checkbox = false; bool Radio = false; bool Done = false; // loop through styles int i; for (i=4; !Done && iSetPos(r); if (Caption) Ctrl->GetStr()->Set(Caption, LanguageId); if (Id) Ctrl->GetStr()->SetDefine(Id); ImportDefine *Def = Defines.GetDefine(Id); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } Dlg->AddView(Ctrl->View()); } } } } else if (stricmp(Type, "ICON") == 0) { if (T.Length() >= 7) { CtrlBitmap *Ctrl = new CtrlBitmap(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[5])*CTRL_X, atoi(T[6])*CTRL_Y); r.Offset(atoi(T[3])*CTRL_X+ADJUST_CTRLS_X, atoi(T[4])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl->View()); } } } else if (stricmp(Type, "LISTBOX") == 0) { if (T.Length() >= 7) { CtrlList *Ctrl = new CtrlList(Dialog, 0); if (Ctrl) { Ctrl->GetStr()->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->GetStr()->SetId(atoi(Def->Value)); } LRect r; r.ZOff(atoi(T[4])*CTRL_X, atoi(T[5])*CTRL_Y); r.Offset(atoi(T[2])*CTRL_X+ADJUST_CTRLS_X, atoi(T[3])*CTRL_Y+ADJUST_CTRLS_Y); Ctrl->ResDialogCtrl::SetPos(r); Dlg->AddView(Ctrl); } } } else if (stricmp(Type, "END") == 0) { // search for an existing dialog resource in // another language ResDialog *Match = 0; CtrlDlg *MatchObj = 0; for (auto d: DlLList) { auto It = d->IterateViews(); LViewI *Wnd = It[0]; if (Wnd) { CtrlDlg *Obj = dynamic_cast(Wnd); if (Obj) { if (Obj->GetStr()->GetId() == Dlg->GetStr()->GetId()) { MatchObj = Obj; Match = d; break; } } } } if (Match) { // Merge the controls from "Dlg" to "MatchObj" List Old; List New; Dlg->ListChildren(New); MatchObj->ListChildren(Old); // add the language strings for the caption // without clobbering the languages already // present for (auto s: Dlg->GetStr()->Items) { if (!MatchObj->GetStr()->Get(s->GetLang())) { MatchObj->GetStr()->Set(s->GetStr(), s->GetLang()); } } for (auto c: New) { ResDialogCtrl *MatchCtrl = 0; // try matching by Id { for (auto Mc: Old) { if (Mc->GetStr()->GetId() == c->GetStr()->GetId() && Mc->GetStr()->GetId() > 0) { MatchCtrl = Mc; break; } } } // ok no Id match, match by location and type if (!MatchCtrl) { List Overlapping; for (auto Mc: Old) { LRect a = Mc->View()->GetPos(); LRect b = c->View()->GetPos(); LRect c = a; c.Bound(&b); if (c.Valid()) { int Sa = a.X() * a.Y(); int Sb = b.X() * b.Y(); int Sc = c.X() * c.Y(); int Total = Sa + Sb - Sc; double Amount = (double) Sc / (double) Total; if (Amount > 0.5) { // mostly similar in size Overlapping.Insert(Mc); } } } if (Overlapping.Length() == 1) { MatchCtrl = Overlapping[0]; } } if (MatchCtrl) { // woohoo we are cool for (auto s: c->GetStr()->Items) { MatchCtrl->GetStr()->Set(s->GetStr(), s->GetLang()); } } } // Delete the duplicate OnObjSelect(0); DeleteObj(Dialog); } else { // Insert the dialog InsertObject(TYPE_DIALOG, Dialog, false); DlLList.Insert(Dialog); } Dialog = 0; Dlg = 0; Status = true; Mode = IMP_MODE_SEARCH; } break; } case IMP_MODE_STRINGS: { if (stricmp(T[0], "BEGIN") == 0) { } else if (stricmp(T[0], "END") == 0) { Status = true; Mode = IMP_MODE_SEARCH; } else { if (T.Length() > 1) { ResString *Str = String->FindName(T[0]); if (!Str) { Str = String->CreateStr(); if (Str) { ImportDefine *Def = Defines.GetDefine(T[0]); if (Def) { Str->SetId(atoi(Def->Value)); } Str->SetDefine(T[0]); Str->UnDuplicate(); } } if (Str) { // get the language LLanguage *Lang = LFindLang(Language); LLanguageId SLang = (Lang) ? Lang->Id : (char*)"en"; StrLang *s = 0; // look for language present in string object for (auto ss: Str->Items) { if (*ss == SLang) { s = ss; break; } } // if not present then add it if (!s) { s = new StrLang; if (s) { Str->Items.Insert(s); s->SetLang(SLang); } } // set the value if (s) { s->SetStr(T[1]); } } } } break; } case IMP_MODE_MENU: { if (T.Length() >= 1) { if (stricmp(T[0], "BEGIN") == 0) { MenuLevel++; } else if (stricmp(T[0], "END") == 0) { MenuLevel--; if (MenuLevel == 0) { Status = true; Mode = IMP_MODE_SEARCH; Menu->SetLanguages(); if (!MenuNewLang) { InsertObject(TYPE_MENU, Menu, false); } Menu = 0; } } else { ResMenuItem *i = 0; char *Text = T[1]; if (Text) { if (stricmp(T[0], "POPUP") == 0) { if (MenuNewLang) { LTreeItem *Ri = 0; if (MenuItem[MenuLevel]) { Ri = MenuItem[MenuLevel]->GetNext(); } else { if (MenuLevel == 1) { Ri = Menu->ItemAt(0); } else if (MenuItem[MenuLevel-1]) { Ri = MenuItem[MenuLevel-1]->GetChild(); } } if (Ri) { // Seek up to the next submenu while (!Ri->GetChild()) { Ri = Ri->GetNext(); } i = dynamic_cast(Ri); // char *si = i->Str.Get("en"); if (i) { MenuItem[MenuLevel] = i; } } } else { MenuItem[MenuLevel] = i = new ResMenuItem(Menu); } if (i) { LLanguage *Lang = LFindLang(Language); i->GetStr()->Set(Text, (Lang) ? Lang->Id : (char*)"en"); } MenuNextItem = 0; } else if (stricmp(T[0], "MENUITEM") == 0) { if (MenuNewLang) { if (MenuItem[MenuLevel-1] && T.Length() > 2) { ImportDefine *id = Defines.GetDefine(T[2]); if (id) { int Id = atoi(id->Value); int n = 0; for (LTreeItem *o = MenuItem[MenuLevel-1]->GetChild(); o; o = o->GetNext(), n++) { ResMenuItem *Res = dynamic_cast(o); if (Res && Res->GetStr()->GetId() == Id) { i = Res; break; } } } } MenuNextItem++; } else { i = new ResMenuItem(Menu); } if (i) { if (stricmp(Text, "SEPARATOR") == 0) { // Set separator i->Separator(true); } else { if (!MenuNewLang) { // Set Id i->GetStr()->SetDefine(T[2]); if (i->GetStr()->GetDefine()) { ImportDefine *id = Defines.GetDefine(i->GetStr()->GetDefine()); if (id) { i->GetStr()->SetId(atoi(id->Value)); i->GetStr()->UnDuplicate(); } } } // Set Text LLanguage *Lang = LFindLang(Language); i->GetStr()->Set(Text, (Lang) ? Lang->Id : (char*)"en"); } } } } if (i && !MenuNewLang) { if (MenuLevel == 1) { Menu->Insert(i); } else if (MenuItem[MenuLevel-1]) { MenuItem[MenuLevel-1]->Insert(i); } } } } break; } } } T.DeleteArrays(); } DeleteObj(Dialog); if (String->Length() > 0) { String->SetLanguages(); } } Invalidate(); }; if (FileName) Load(FileName); else { auto Select = new LFileSelect(this); Select->Type("Win32 Resource Script", "*.rc"); Select->Open([&](auto dlg, auto status) { if (status) Load(dlg->Name()); delete dlg; }); } } bool AppWnd::SaveWin32() { return false; } ///////////////////////////////////////////////////////////////////////// ResFrame::ResFrame(Resource *child) { Child = child; Name("ResFrame"); } ResFrame::~ResFrame() { if (Child) { Child->App()->OnObjSelect(NULL); Child->Wnd()->Detach(); } } void ResFrame::OnFocus(bool b) { Child->Wnd()->Invalidate(); } bool ResFrame::Attach(LViewI *p) { bool Status = LLayout::Attach(p); if (Status && Child) { Child->Attach(this); Child->Wnd()->Visible(true); } return Status; } bool ResFrame::Pour(LRegion &r) { LRect *Best = FindLargest(r); if (Best) { SetPos(*Best); return true; } return false; } bool ResFrame::OnKey(LKey &k) { bool Status = false; if (k.Down() && Child) { switch (k.c16) { case LK_DELETE: { if (k.Shift()) { Child->Copy(true); } else { Child->Delete(); } Status = true; break; } case 'x': case 'X': { if (k.Ctrl()) { Child->Copy(true); Status = true; } break; } case 'c': case 'C': { if (k.Ctrl()) { Child->Copy(); Status = true; } break; } case LK_INSERT: { if (k.Ctrl()) { Child->Copy(); } else if (k.Shift()) { Child->Paste(); } Status = true; break; } case 'v': case 'V': { if (k.Ctrl()) { Child->Paste(); Status = true; } break; } } } return Child->Wnd()->OnKey(k) || Status; } void ResFrame::OnPaint(LSurface *pDC) { // Draw nice frame LRect r(0, 0, X()-1, Y()-1); LThinBorder(pDC, r, DefaultRaisedEdge); pDC->Colour(L_MED); LFlatBorder(pDC, r, 4); LWideBorder(pDC, r, DefaultSunkenEdge); // Set the child to the client area Child->Wnd()->SetPos(r); // Draw the dialog & controls LView::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// LgiFunc char *_LgiGenLangLookup(); #include "lgi/common/AutoPtr.h" #include "lgi/common/Variant.h" #include "lgi/common/Css.h" #include "lgi/common/TableLayout.h" class Foo : public LLayoutCell { public: Foo() { TextAlign(AlignLeft); } bool Add(LView *v) { return false; } bool Remove(LView *v) { return false; } }; ////////////////////////////////////////////////////////////////////// ShortCutView::ShortCutView(AppWnd *app) { App = app; LRect r(0, 0, 300, 600); SetPos(r); MoveSameScreen(App); Name("Dialog Shortcuts"); if (Attach(0)) { Lst = new LList(100, 0, 0, 100, 100); Lst->Attach(this); Lst->SetPourLargest(true); Lst->AddColumn("Key", 50); Lst->AddColumn("Ref", 80); Lst->AddColumn("Control", 150); Visible(true); } } ShortCutView::~ShortCutView() { App->OnCloseView(this); } void FindShortCuts(LList *Out, LViewI *In) { for (LViewI *c: In->IterateViews()) { ResDialogCtrl *rdc = dynamic_cast(c); if (!rdc || !rdc->GetStr()) continue; char *n = rdc->GetStr()->Get(); if (n) { char *a = strchr(n, '&'); if (a && a[1] != '&') { LListItem *li = new LListItem; LString s(++a, 1); LString id; id.Printf("%i", rdc->GetStr()->GetRef()); li->SetText(s.Upper(), 0); li->SetText(id, 1); li->SetText(rdc->GetClass(), 2); li->_UserPtr = rdc; Out->Insert(li); } } FindShortCuts(Out, c); } } int ShortCutView::OnNotify(LViewI *Ctrl, LNotification n) { if (Ctrl->GetId() == Lst->GetId()) { switch (n.Type) { case LNotifyItemClick: { LListItem *li = Lst->GetSelected(); if (li) { LString s = li->GetText(1); ResDialogCtrl *c = (ResDialogCtrl*) li->_UserPtr; if (c) App->GotoObject(c->GetStr(), NULL, c->GetDlg(), NULL, c); } break; } default: break; } } return LWindow::OnNotify(Ctrl, n); } void ShortCutView::OnDialogChange(ResDialog *Dlg) { Lst->Empty(); if (!Dlg) return; FindShortCuts(Lst, Dlg); Lst->Sort(NULL); } ShortCutView *AppWnd::GetShortCutView() { return ShortCuts; } void AppWnd::OnCloseView(ShortCutView *v) { if (v == ShortCuts) ShortCuts = NULL; } ////////////////////////////////////////////////////////////////////// void TestFunc() { /* Foo c; uint32 *p = (uint32*)&c; p++; p = (uint32*)(*p); void *method = (void*)p[2]; for (int i=0; i<16; i++) { printf("[%i]=%p\n", i, p[i]); } c.OnChange(LCss::PropBackground); */ } int LgiMain(OsAppArguments &AppArgs) { LApp a(AppArgs, "LgiRes"); if (a.IsOk()) { if ((a.AppWnd = new AppWnd)) { TestFunc(); a.AppWnd->Visible(true); a.Run(); } } return 0; } diff --git a/ResourceEditor/Code/LgiRes_Dialog.cpp b/ResourceEditor/Code/LgiRes_Dialog.cpp --- a/ResourceEditor/Code/LgiRes_Dialog.cpp +++ b/ResourceEditor/Code/LgiRes_Dialog.cpp @@ -1,4371 +1,4374 @@ /* ** FILE: LgiRes_Dialog.cpp ** AUTHOR: Matthew Allen ** DATE: 5/8/1999 ** DESCRIPTION: Dialog Resource Editor ** ** ** Copyright (C) 1999, Matthew Allen ** fret@memecode.com */ // Old control offset 3,17 //////////////////////////////////////////////////////////////////// #include #include "LgiResEdit.h" #include "LgiRes_Dialog.h" #include "lgi/common/Button.h" #include "lgi/common/Variant.h" #include "lgi/common/Token.h" #include "lgi/common/DisplayString.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/Menu.h" #include "lgi/common/StatusBar.h" #include "lgi/common/ToolBar.h" #include "resdefs.h" //////////////////////////////////////////////////////////////////// #define IDC_UP 101 #define IDC_DOWN 102 #define DEBUG_OVERLAY 0 +int GOOBER_SIZE = 6; +int GOOBER_BORDER = 8; + // Name mapping table class LgiObjectName { public: int Type; const char *ObjectName; char *ResourceName; bool ToolbarBtn; } NameMap[] = { // ID Lgi's name Resource editor name {UI_DIALOG, "LDialog", Res_Dialog, false}, {UI_TABLE, "LTableLayout", Res_Table, true}, {UI_TEXT, "LText", Res_StaticText, true}, {UI_EDITBOX, "LEdit", Res_EditBox, true}, {UI_CHECKBOX, "LCheckBox", Res_CheckBox, true}, {UI_BUTTON, "LButton", Res_Button, true}, {UI_GROUP, "LRadioGroup", Res_Group, true}, {UI_RADIO, "LRadioButton", Res_RadioBox, true}, {UI_TABS, "LTabView", Res_TabView, true}, {UI_TAB, "LTabPage", Res_Tab, false}, {UI_LIST, "LList", Res_ListView, true}, {UI_COLUMN, "LListColumn", Res_Column, false}, {UI_COMBO, "LCombo", Res_ComboBox, true}, {UI_TREE, "LTree", Res_TreeView, true}, {UI_BITMAP, "LBitmap", Res_Bitmap, true}, {UI_PROGRESS, "LProgressView", Res_Progress, true}, {UI_SCROLL_BAR, "LScrollBar", Res_ScrollBar, true}, {UI_CUSTOM, "LCustom", Res_Custom, true}, {UI_CONTROL_TREE, "LControlTree", Res_ControlTree, true}, // If you add a new control here update ResDialog::CreateCtrl(int Tool) as well {0, 0, 0, 0} }; class CtrlItem : public LListItem { friend class TabOrder; ResDialogCtrl *Ctrl; public: CtrlItem(ResDialogCtrl *ctrl) { Ctrl = ctrl; } const char *GetText(int Col) { switch (Col) { case 0: { if (Ctrl && Ctrl->GetStr()) return Ctrl->GetStr()->GetDefine(); break; } case 1: { if (Ctrl && Ctrl->GetStr()) return Ctrl->GetStr()->Get(); break; } } return NULL; } }; class TabOrder : public LDialog { ResDialogCtrl *Top; LList *Lst; LButton *Ok; LButton *Cancel; LButton *Up; LButton *Down; public: TabOrder(LView *Parent, ResDialogCtrl *top) { Top = top; SetParent(Parent); Children.Insert(Lst = new LList(IDC_LIST, 10, 10, 350, 300)); Children.Insert(Ok = new LButton(IDOK, Lst->GetPos().x2 + 10, 10, 60, 20, "Ok")); Children.Insert(Cancel = new LButton(IDCANCEL, Lst->GetPos().x2 + 10, Ok->GetPos().y2 + 5, 60, 20, "Cancel")); Children.Insert(Up = new LButton(IDC_UP, Lst->GetPos().x2 + 10, Cancel->GetPos().y2 + 15, 60, 20, "Up")); Children.Insert(Down = new LButton(IDC_DOWN, Lst->GetPos().x2 + 10, Up->GetPos().y2 + 5, 60, 20, "Down")); LRect r(0, 0, Ok->GetPos().x2 + 17, Lst->GetPos().y2 + 40); SetPos(r); MoveToCenter(); Lst->AddColumn("#define", 150); Lst->AddColumn("Text", 150); Lst->MultiSelect(false); if (Top) { for (auto c: Top->View()->IterateViews()) { ResDialogCtrl *Ctrl = dynamic_cast(c); if (Ctrl->GetType() != UI_TEXT) { Lst->Insert(new CtrlItem(Ctrl)); } } char s[256]; sprintf(s, "Set Tab Order: %s", Top->GetStr()->GetDefine()); Name(s); } } int OnNotify(LViewI *Ctrl, LNotification n) { int MoveDir = 1; switch (Ctrl->GetId()) { case IDC_UP: { MoveDir = -1; // fall through } case IDC_DOWN: { if (Lst) { CtrlItem *Sel = dynamic_cast(Lst->GetSelected()); if (Sel) { int Index = Lst->IndexOf(Sel); CtrlItem *Up = dynamic_cast(Lst->ItemAt(Index+MoveDir)); if (Up) { Lst->Remove(Sel); Lst->Insert(Sel, Index+MoveDir); Sel->Select(true); Sel->ScrollTo(); } } } break; } case IDOK: { if (Lst) { int i=0; List All; Lst->GetAll(All); for (auto n: All) { Top->View()->DelView(n->Ctrl->View()); Top->View()->AddView(n->Ctrl->View(), i++); } } // fall through } case IDCANCEL: { EndModal(0); break; } } return 0; } }; //////////////////////////////////////////////////////////////////// void DrawGoobers(LSurface *pDC, LRect &r, LRect *Goobers, LColour c, int OverIdx) { int Mx = (r.x2 + r.x1) / 2 - (GOOBER_SIZE / 2); int My = (r.y2 + r.y1) / 2 - (GOOBER_SIZE / 2); pDC->Colour(c); Goobers[0].x1 = r.x1 - GOOBER_BORDER; Goobers[0].y1 = r.y1 - GOOBER_BORDER; Goobers[0].x2 = r.x1 - (GOOBER_BORDER - GOOBER_SIZE); Goobers[0].y2 = r.y1 - (GOOBER_BORDER - GOOBER_SIZE); Goobers[1].x1 = Mx; Goobers[1].y1 = r.y1 - GOOBER_BORDER; Goobers[1].x2 = Mx + GOOBER_SIZE; Goobers[1].y2 = r.y1 - (GOOBER_BORDER - GOOBER_SIZE); Goobers[2].x1 = r.x2 + (GOOBER_BORDER - GOOBER_SIZE); Goobers[2].y1 = r.y1 - GOOBER_BORDER; Goobers[2].x2 = r.x2 + GOOBER_BORDER; Goobers[2].y2 = r.y1 - (GOOBER_BORDER - GOOBER_SIZE); Goobers[3].x1 = r.x2 + (GOOBER_BORDER - GOOBER_SIZE); Goobers[3].y1 = My; Goobers[3].x2 = r.x2 + GOOBER_BORDER; Goobers[3].y2 = My + GOOBER_SIZE; Goobers[4].x1 = r.x2 + (GOOBER_BORDER - GOOBER_SIZE); Goobers[4].y1 = r.y2 + (GOOBER_BORDER - GOOBER_SIZE); Goobers[4].x2 = r.x2 + GOOBER_BORDER; Goobers[4].y2 = r.y2 + GOOBER_BORDER; Goobers[5].x1 = Mx; Goobers[5].y1 = r.y2 + (GOOBER_BORDER - GOOBER_SIZE); Goobers[5].x2 = Mx + GOOBER_SIZE; Goobers[5].y2 = r.y2 + GOOBER_BORDER; Goobers[6].x1 = r.x1 - GOOBER_BORDER; Goobers[6].y1 = r.y2 + (GOOBER_BORDER - GOOBER_SIZE); Goobers[6].x2 = r.x1 - (GOOBER_BORDER - GOOBER_SIZE); Goobers[6].y2 = r.y2 + GOOBER_BORDER; Goobers[7].x1 = r.x1 - GOOBER_BORDER; Goobers[7].y1 = My; Goobers[7].x2 = r.x1 - (GOOBER_BORDER - GOOBER_SIZE); Goobers[7].y2 = My + GOOBER_SIZE; for (int i=0; i<8; i++) { if (OverIdx == i) pDC->Rectangle(Goobers+i); else pDC->Box(Goobers+i); } } //////////////////////////////////////////////////////////////////// int ResDialogCtrl::TabDepth = 0; ResDialogCtrl::ResDialogCtrl(ResDialog *dlg, char *CtrlTypeName, LXmlTag *load) : ResObject(CtrlTypeName) { Dlg = dlg; Client.ZOff(-1, -1); SelectStart.ZOff(-1, -1); if (load) { // Base a string off the xml int r = load->GetAsInt("ref"); if (Dlg) { SetStr(Dlg->Symbols->FindRef(r)); LAssert(GetStr()); if (!GetStr()) // oh well we should have one anyway... fix things up so to speak. SetStr(Dlg->Symbols->CreateStr()); } LAssert(GetStr()); } else if (Dlg->CreateSymbols) { // We create a symbol for this resource SetStr((Dlg && Dlg->Symbols) ? Dlg->Symbols->CreateStr() : NULL); if (GetStr()) { char Def[256]; sprintf(Def, "IDC_%i", GetStr()->GetRef()); GetStr()->SetDefine(Def); } } else { // Someone else is going to create the symbol SetStr(NULL); } } ResDialogCtrl::~ResDialogCtrl() { if (ResDialog::Symbols) { auto s = GetStr(); SetStr(NULL); ResDialog::Symbols->DeleteStr(s); } if (Dlg) { Dlg->App()->OnObjDelete(this); Dlg->OnDeselect(this); } } char *ResDialogCtrl::GetRefText() { static char Buf[64]; if (GetStr()) { sprintf(Buf, "Ref=%i", GetStr()->GetRef()); } else { Buf[0] = 0; } return Buf; } void ResDialogCtrl::ListChildren(List &l, bool Deep) { for (LViewI *w: View()->IterateViews()) { ResDialogCtrl *c = dynamic_cast(w); LAssert(c); if (c) { l.Insert(c); if (Deep) { c->ListChildren(l); } } } } LRect ResDialogCtrl::GetMinSize() { LRect m(0, 0, GRID_X-1, GRID_Y-1); if (IsContainer()) { LRect cli = View()->GetClient(false); for (LViewI *c: View()->IterateViews()) { LRect cpos = c->GetPos(); cpos.Offset(cli.x1, cli.y1); m.Union(&cpos); } } return m; } bool ResDialogCtrl::SetPos(LRect &p, bool Repaint) { LRect m = GetMinSize(); if (m.X() > p.X()) p.x2 = p.x1 + m.X() - 1; if (m.Y() > p.Y()) p.y2 = p.y1 + m.Y() - 1; if (p != View()->GetPos()) { /* if (ParentCtrl) { if (p.x1 < ParentCtrl->Client.x1) { p.Offset(ParentCtrl->Client.x1 - p.x1, 0); } if (p.y1 < ParentCtrl->Client.y1) { p.Offset(0, ParentCtrl->Client.y1 - p.y1); } } */ // set our size bool Status = View()->SetPos(p, Repaint); // tell everyone else about the change OnFieldChange(); LRect r(0, 0, p.X()-1, p.Y()-1); r.Inset(-GOOBER_BORDER, -GOOBER_BORDER); View()->Invalidate(&r, false, true); // check our parents are big enough to show us... ResDialogCtrl *Par = ParentCtrl(); if (Par) { LRect t = Par->View()->GetPos(); Par->ResDialogCtrl::SetPos(t, true); } return Status; } return true; } bool ResDialogCtrl::SetStr(ResString *s) { if (_Str == s) { if (_Str) LAssert(_Str->Refs.HasItem(this)); return true; } if (_Str) { if (!_Str->Refs.HasItem(this)) { LAssert(!"Refs incorrect."); return false; } _Str->Refs.Delete(this); } _Str = s; if (_Str) { if (_Str->Refs.HasItem(this)) { LAssert(!"Refs already has us."); return false; } _Str->Refs.Add(this); } else { // LgiTrace("%s:%i - %p::SetStr(NULL)\n", _FL, this); } return true; } void ResDialogCtrl::TabString(char *Str) { if (!Str) return; memset(Str, '\t', TabDepth); Str[TabDepth] = 0; } LRect ResDialogCtrl::AbsPos() { LViewI *w = View(); LRect r = w->GetPos(); r.Offset(-r.x1, -r.y1); for (; w && w != Dlg; w = w->GetParent()) { LRect pos = w->GetPos(); if (w->GetParent()) { // LView *Ctrl = w->GetParent()->GetGView(); LRect client = w->GetParent()->GetClient(false); r.Offset(pos.x1 + client.x1, pos.y1 + client.y1); } else { r.Offset(pos.x1, pos.y1); } } return r; } void ResDialogCtrl::StrFromRef(int Ref) { // get the string object SetStr(Dlg->App()->GetStrFromRef(Ref)); if (!GetStr()) { LgiTrace("%s:%i - String with ref '%i' missing.\n", _FL, Ref); LAssert(0); if (SetStr(Dlg->App()->GetDialogSymbols()->CreateStr())) { GetStr()->SetRef(Ref); } else return; } // if this assert fails then the Ref doesn't exist // and the string can't be found // if this assert fails then the Str is already // associated with a control, and thus we would // duplicate the pointer to the string if we let // it go by // LAssert(Str->UpdateWnd == 0); // set the string's control to us GetStr()->UpdateWnd = View(); // make the strings refid unique GetStr()->UnDuplicate(); View()->Name(GetStr()->Get()); } bool ResDialogCtrl::GetFields(FieldTree &Fields) { if (GetStr()) { GetStr()->GetFields(Fields); } int Id = 101; Fields.Insert(this, DATA_STR, Id++, VAL_Pos, "Pos"); Fields.Insert(this, DATA_BOOL, Id++, VAL_Visible, "Visible"); Fields.Insert(this, DATA_BOOL, Id++, VAL_Enabled, "Enabled"); Fields.Insert(this, DATA_STR, Id++, VAL_Class, "Class", -1); Fields.Insert(this, DATA_STR, Id++, VAL_Style, "Style", -1, true); return true; } bool ResDialogCtrl::Serialize(FieldTree &Fields) { if ((Fields.GetMode() == FieldTree::ObjToUi || Fields.GetMode() == FieldTree::UiToObj) && GetStr()) { GetStr()->Serialize(Fields); } LRect r = View()->GetPos(), Old = View()->GetPos(); bool e = true; if (Fields.GetMode() == FieldTree::ObjToUi || Fields.GetMode() == FieldTree::ObjToStore) { r = View()->GetPos(); e = View()->Enabled(); } Fields.Serialize(this, VAL_Pos, r); Fields.Serialize(this, VAL_Visible, Vis, true); Fields.Serialize(this, VAL_Enabled, e, true); Fields.Serialize(this, VAL_Class, CssClass); Fields.Serialize(this, VAL_Style, CssStyle); if (Fields.GetMode() == FieldTree::UiToObj || Fields.GetMode() == FieldTree::StoreToObj) { View()->Enabled(e); SetPos(r); r.Union(&Old); r.Inset(-GOOBER_BORDER, -GOOBER_BORDER); if (View()->GetParent()) { View()->GetParent()->Invalidate(&r); } } if (Dlg && Dlg->Item) Dlg->Item->Update(); return true; } void ResDialogCtrl::CopyText() { if (GetStr()) GetStr()->CopyText(); } void ResDialogCtrl::PasteText() { if (GetStr()) { GetStr()->PasteText(); View()->Invalidate(); } } bool ResDialogCtrl::AttachCtrl(ResDialogCtrl *Ctrl, LRect *r) { bool Status = false; if (Ctrl) { Ctrl->View()->Visible(true); View()->AddView(Ctrl->View()); Ctrl->View()->SetParent(View()); if (r) { if (!dynamic_cast(Ctrl->View()->GetParent())) { Dlg->SnapRect(r, this); } Ctrl->SetPos(*r); } if (Dlg->Resource::IsSelected()) { Dlg->OnSelect(Ctrl); } Status = true; } return Status; } void ResDialogCtrl::OnPaint(LSurface *pDC) { if (DragCtrl >= 0) { LRect r = DragRgn; r.Normal(); pDC->Colour(L_FOCUS_SEL_BACK); pDC->Box(&r); } } LMouse ResDialogCtrl::MapToDialog(LMouse m) { // Convert co-ords from out own local space to be relative to 'Dlg' // the parent dialog. LMouse Ms = m; LViewI *Parent; for (LViewI *i = View(); i && i != (LViewI*)Dlg; i = Parent) { Parent = i->GetParent(); LRect Pos = i->GetPos(), Cli = i->GetClient(false); #if DEBUG_OVERLAY LgiTrace("%s %i,%i + %i,%i + %i,%i = %i,%i\n", i->GetClass(), Ms.x, Ms.y, Pos.x1, Pos.y1, Cli.x1, Cli.y1, Ms.x + Pos.x1 + Cli.x1, Ms.y + Pos.y1 + Cli.y1); #endif Ms.x += Pos.x1 + Cli.x1; Ms.y += Pos.y1 + Cli.y1; } return Ms; } void ResDialogCtrl::OnMouseClick(LMouse &m) { if (m.Down()) { if (m.Left()) { // LgiTrace("Click down=%i %i,%i\n", m.Down(), m.x, m.y); if (Dlg) { #if DEBUG_OVERLAY LPoint Prev(0, 0); auto &DebugOverlay = Dlg->DebugOverlay; if (!DebugOverlay) { DebugOverlay.Reset(new LMemDC(Dlg->X(), Dlg->Y(), System32BitColourSpace)); DebugOverlay->Colour(0, 32); DebugOverlay->Rectangle(); DebugOverlay->Colour(LColour(64, 192, 64)); } #endif bool Processed = false; LRect c = View()->GetClient(); bool ClickedThis = c.Overlap(m.x, m.y); // Convert co-ords from out own local space to be relative to 'Dlg' // the parent dialog. LMouse Ms = MapToDialog(m); #if DEBUG_OVERLAY if (DebugOverlay) { DebugOverlay->Line(Prev.x, Prev.y, Ms.x, Ms.y); DebugOverlay->Circle(Ms.x, Ms.y, 5); Prev.x = Ms.x; Prev.y = Ms.y; } #endif Dlg->OnMouseClick(Ms); if (ClickedThis && !Dlg->IsDraging()) { DragCtrl = Dlg->CurrentTool(); if ((DragCtrl > 0 && AcceptChildren) || ((DragCtrl == 0) && !Movable)) { LPoint p(m.x, m.y); Dlg->SnapPoint(&p, ParentCtrl()); DragStart.x = DragRgn.x1 = DragRgn.x2 = p.x; DragStart.y = DragRgn.y1 = DragRgn.y2 = p.y; View()->Capture(true); Processed = true; } else { DragCtrl = -1; if (Movable) { DragRgn.x1 = m.x; DragRgn.y1 = m.y; MoveCtrl = true; Processed = true; View()->Capture(true); } } SelectMode = (m.Shift()) ? SelAdd : SelSet; SelectStart = View()->GetPos(); } } } else if (m.IsContextMenu()) { LSubMenu RClick; bool PasteData = false; bool PasteTranslations = false; CtrlButton *Btn = dynamic_cast(this); { LClipBoard c(Dlg); char *Clip = c.Text(); if (Clip) { PasteTranslations = strstr(Clip, TranslationStrMagic); char *p = Clip; while (*p && strchr(" \r\n\t", *p)) p++; PasteData = *p == '<'; } } RClick.AppendItem("Cu&t", IDM_CUT, Dlg->Selection.Length()>0); RClick.AppendItem("&Copy Control", IDM_COPY, Dlg->Selection.Length()>0); RClick.AppendItem("&Paste", IDM_PASTE, PasteData); RClick.AppendSeparator(); RClick.AppendItem("Copy Text", IDM_COPY_TEXT, Dlg->Selection.Length()==1); RClick.AppendItem("Paste Text", IDM_PASTE_TEXT, PasteTranslations); RClick.AppendSeparator(); RClick.AppendItem("&Delete", IDM_DELETE, Dlg->Selection.Length()>0); if (Btn) { RClick.AppendSeparator(); RClick.AppendItem("Set 'Ok'", IDM_SET_OK); RClick.AppendItem("Set 'Cancel'", IDM_SET_CANCEL); } if (Dlg->GetMouse(m, true)) { int Cmd = 0; switch (Cmd = RClick.Float(Dlg, m.x, m.y)) { case IDM_DELETE: { Dlg->Delete(); break; } case IDM_CUT: { Dlg->Copy(true); break; } case IDM_COPY: { Dlg->Copy(); break; } case IDM_PASTE: { Dlg->Paste(); break; } case IDM_COPY_TEXT: { ResDialogCtrl *Ctrl = Dlg->Selection[0]; if (Ctrl) Ctrl->CopyText(); break; } case IDM_PASTE_TEXT: { PasteText(); break; } case IDM_SET_OK: { ResString *s = Btn->GetStr(); if (s) { s->Set("Ok"); s->SetDefine("IDOK"); } break; } case IDM_SET_CANCEL: { ResString *s = Btn->GetStr(); if (s) { s->Set("Cancel"); s->SetDefine("IDCANCEL"); } break; } } } } } else { if (DragCtrl >= 0) { bool Exit = false; if (Dlg && (DragRgn.X() > 1 || DragRgn.Y() > 1)) { if (DragCtrl == 0) { Dlg->SelectRect(this, &DragRgn, SelectMode != SelAdd); } else { ResDialogCtrl *Ctrl = Dlg->CreateCtrl(DragCtrl, 0); if (Ctrl) { AttachCtrl(Ctrl, &DragRgn); } } Exit = true; } DragCtrl = -1; View()->Invalidate(); View()->Capture(false); if (Exit) { return; } } if (MoveCtrl) { View()->Capture(false); MoveCtrl = false; } if (SelectMode > SelNone) { LRect r = View()->GetPos(); if (SelectStart == r) { Dlg->OnSelect(this, SelectMode != SelAdd); } SelectMode = SelNone; } } } void ResDialogCtrl::OnMouseMove(LMouse &m) { // Drag a rubber band... if (DragCtrl >= 0) { LRect Old = DragRgn; DragRgn.x1 = DragStart.x; DragRgn.y1 = DragStart.y; DragRgn.x2 = m.x; DragRgn.y2 = m.y; DragRgn.Normal(); Dlg->SnapRect(&DragRgn, this); Old.Union(&DragRgn); Old.Inset(-1, -1); View()->Invalidate(&Old); } if (Dlg) { LMouse Ms = MapToDialog(m); Dlg->OnMouseMove(Ms); } // Move some ctrl(s) if (MoveCtrl && !m.Shift()) { int Dx = m.x - DragRgn.x1; int Dy = m.y - DragRgn.y1; if (Dx != 0 || Dy != 0) { // LgiTrace("Move %i,%i + %i,%i\n", m.x, m.y, Dx, Dy); if (!Dlg->IsSelected(this)) { Dlg->OnSelect(this); } LRect Old = View()->GetPos(); LRect New = Old; New.Offset( m.x - DragRgn.x1, m.y - DragRgn.y1); LPoint p(New.x1, New.y1); Dlg->SnapPoint(&p, ParentCtrl()); New.Set(p.x, p.y, p.x + New.X() - 1, p.y + New.Y() - 1); if (New != Old) { Dlg->MoveSelection(New.x1 - Old.x1, New.y1 - Old.y1); } } } } char *ReadInt(char *s, int &Value) { char *c = strchr(s, ','); if (c) { *c = 0; Value = atoi(s); return c+1; } Value = atoi(s); return 0; } void ResDialogCtrl::ReadPos(char *Str) { if (Str) { char *s = NewStr(Str); if (s) { LRect r = View()->GetPos(); char *p = ReadInt(s, r.x1); if (p) p = ReadInt(p, r.y1); if (p) p = ReadInt(p, r.x2); if (p) p = ReadInt(p, r.y2); DeleteArray(s); } } } //////////////////////////////////////////////////////////////////// CtrlDlg::CtrlDlg(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Dialog, load) { Movable = false; AcceptChildren = true; GetStr()->UpdateWnd = View(); View()->Name("CtrlDlg"); } IMPL_DIALOG_CTRL(CtrlDlg) LRect &CtrlDlg::GetClient(bool InClientSpace) { static LRect r; Client.Set(0, 0, View()->X()-1, View()->Y()-1); Client.Inset(2, 2); Client.y1 += LAppInst->GetMetric(LGI_MET_DECOR_CAPTION); if (Client.y1 > Client.y2) Client.y1 = Client.y2; if (InClientSpace) r = Client.ZeroTranslate(); else r = Client; return r; } void CtrlDlg::OnNcPaint(LSurface *pDC, LRect &r) { // Draw the border LWideBorder(pDC, r, DefaultRaisedEdge); // Draw the title bar int TitleY = LAppInst->GetMetric(LGI_MET_DECOR_CAPTION); LRect t = r; t.y2 = t.y1 + TitleY - 1; pDC->Colour(L_ACTIVE_TITLE); pDC->Rectangle(&t); if (GetStr()) { LDisplayString ds(LSysFont, GetStr()->Get()); LSysFont->Fore(L_ACTIVE_TITLE_TEXT); LSysFont->Transparent(true); ds.Draw(pDC, t.x1 + 10, t.y1 + ((t.Y()-ds.Y())/2)); } r.y1 = t.y2 + 1; } void CtrlDlg::OnPaint(LSurface *pDC) { Client = GetClient(); // Draw the grid pDC->Colour(L_MED); pDC->Rectangle(&Client); pDC->Colour(Rgb24(0x80, 0x80, 0x80), 24); for (int y=Client.y1; ySet(x, y); } ResDialogCtrl::OnPaint(pDC); } ///////////////////////////////////////////////////////////////////// // Text box CtrlText::CtrlText(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_StaticText, load) { if (GetStr() && !load) { GetStr()->SetDefine("IDC_STATIC"); } } IMPL_DIALOG_CTRL(CtrlText) void CtrlText::OnPaint(LSurface *pDC) { Client.ZOff(X()-1, Y()-1); char *Text = GetStr()->Get(); LSysFont->Fore(L_TEXT); LSysFont->Transparent(true); if (Text) { LRect Client = GetClient(); int y = 0; char *Start = Text; for (char *s = Text; 1; s++) { if ((*s == '\\' && *(s+1) == 'n') || (*s == 0)) { LDisplayString ds(LSysFont, Start, s - Start); ds.Draw(pDC, 0, y, &Client); y += 15; Start = s + 2; if (*s == '\\') s++; } if (*s == 0) break; } } } // Editbox CtrlEditbox::CtrlEditbox(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_EditBox, load) { Password = false; MultiLine = false; } IMPL_DIALOG_CTRL(CtrlEditbox) void CtrlEditbox::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); Client = r; // Draw the ctrl LWideBorder(pDC, r, DefaultSunkenEdge); pDC->Colour(Enabled() ? L_WORKSPACE : L_MED); pDC->Rectangle(&r); char *Text = GetStr()->Get(); LSysFont->Fore(Enabled() ? L_TEXT : L_LOW); LSysFont->Transparent(true); if (Text) { if (Password) { char *t = NewStr(Text); if (t) { for (char *p = t; *p; p++) *p = '*'; LDisplayString ds(LSysFont, t); ds.Draw(pDC, 4, 4); DeleteArray(t); } } else { LDisplayString ds(LSysFont, Text); ds.Draw(pDC, 4, 4); } } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } #define VAL_Password "Pw" #define VAL_MultiLine "MultiLine" bool CtrlEditbox::GetFields(FieldTree &Fields) { bool Status = ResDialogCtrl::GetFields(Fields); if (Status) { Fields.Insert(this, DATA_BOOL, 300, VAL_Password, "Password"); Fields.Insert(this, DATA_BOOL, 301, VAL_MultiLine, "MultiLine"); } return Status; } bool CtrlEditbox::Serialize(FieldTree &Fields) { bool Status = ResDialogCtrl::Serialize(Fields); if (Status) { Fields.Serialize(this, VAL_Password, Password, false); Fields.Serialize(this, VAL_MultiLine, MultiLine, false); } return Status; } // Check box CtrlCheckbox::CtrlCheckbox(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_CheckBox, load) { } IMPL_DIALOG_CTRL(CtrlCheckbox) void CtrlCheckbox::OnPaint(LSurface *pDC) { Client.ZOff(X()-1, Y()-1); LRect r(0, 0, 12, 12); // Draw the ctrl LWideBorder(pDC, r, DefaultSunkenEdge); pDC->Colour(L_WORKSPACE); pDC->Rectangle(&r); LPoint Pt[6] = { LPoint(3, 4), LPoint(3, 7), LPoint(5, 10), LPoint(10, 5), LPoint(10, 2), LPoint(5, 7)}; pDC->Colour(0); pDC->Polygon(6, Pt); pDC->Set(3, 5); char *Text = GetStr()->Get(); if (Text) { LSysFont->Fore(L_TEXT); LSysFont->Transparent(true); LDisplayString ds(LSysFont, Text); ds.Draw(pDC, r.x2 + 10, r.y1-2); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Button CtrlButton::CtrlButton(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Button, load) { IsToggle = false; } IMPL_DIALOG_CTRL(CtrlButton) bool CtrlButton::GetFields(FieldTree &Fields) { bool Status = ResDialogCtrl::GetFields(Fields); int Id = 160; Fields.Insert(this, DATA_FILENAME, Id++, VAL_Image, "Image"); Fields.Insert(this, DATA_BOOL, Id++, VAL_Toggle, "Toggle"); return Status; } bool CtrlButton::Serialize(FieldTree &Fields) { bool Status = ResDialogCtrl::Serialize(Fields); if (Status) { Fields.Serialize(this, VAL_Image, Image); Fields.Serialize(this, VAL_Toggle, IsToggle); } return Status; } void CtrlButton::OnPaint(LSurface *pDC) { Client.ZOff(X()-1, Y()-1); LRect r = Client; char *Text = GetStr()->Get(); // Draw the ctrl LWideBorder(pDC, r, DefaultRaisedEdge); LSysFont->Fore(L_TEXT); if (ValidStr(Text)) { LSysFont->Back(L_MED); LSysFont->Transparent(false); LDisplayString ds(LSysFont, Text); ds.Draw(pDC, r.x1 + ((r.X()-ds.X())/2), r.y1 + ((r.Y()-ds.Y())/2), &r); } else { pDC->Colour(L_MED); pDC->Rectangle(&r); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Group CtrlGroup::CtrlGroup(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Group, load) { AcceptChildren = true; if (GetStr() && !load) { GetStr()->SetDefine("IDC_STATIC"); } } IMPL_DIALOG_CTRL(CtrlGroup) void CtrlGroup::OnPaint(LSurface *pDC) { Client.ZOff(X()-1, Y()-1); LRect r = Client; // Draw the ctrl r.y1 += 5; LWideBorder(pDC, r, EdgeXpChisel); r.y1 -= 5; LSysFont->Fore(L_TEXT); LSysFont->Back(L_MED); LSysFont->Transparent(false); char *Text = GetStr()->Get(); LDisplayString ds(LSysFont, Text); ds.Draw(pDC, r.x1 + 8, r.y1 - 2); // Draw children //LWindow::OnPaint(pDC); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //Radio button uint32_t RadioBmp[] = { 0xC0C0C0C0, 0xC0C0C0C0, 0xC0C0C0C0, 0x80808080, 0x80808080, 0x80808080, 0xC0C0C0C0, 0xC0C0C0C0, 0xC0C0C0C0, 0xC0C0C0C0, 0x8080C0C0, 0x80808080, 0x00000000, 0x00000000, 0x00000000, 0x80808080, 0xC0C08080, 0xC0C0C0C0, 0x80C0C0C0, 0x00008080, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFF0000, 0xC0C0C0FF, 0x80C0C0C0, 0x00008080, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xDFFFFFFF, 0xFFFFDFDF, 0xC0C0C0FF, 0x00808080, 0xFFFF0000, 0xFFFFFFFF, 0x00FFFFFF, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0xDFDFFFFF, 0xFFFFFFDF, 0x00808080, 0xFFFF0000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xDFDFFFFF, 0xFFFFFFDF, 0x00808080, 0xFFFF0000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xDFDFFFFF, 0xFFFFFFDF, 0x00808080, 0xFFFF0000, 0xFFFFFFFF, 0x00FFFFFF, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0xDFDFFFFF, 0xFFFFFFDF, 0x80C0C0C0, 0x00008080, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xDFFFFFFF, 0xFFFFDFDF, 0xC0C0C0FF, 0x80C0C0C0, 0xDFDF8080, 0xDFDFDFDF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xDFDFDFDF, 0xFFFFDFDF, 0xC0C0C0FF, 0xC0C0C0C0, 0xFFFFC0C0, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, 0xFFFFFFFF, 0xC0C0FFFF, 0xC0C0C0C0, 0xC0C0C0C0, 0xC0C0C0C0, 0xC0C0C0C0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0C0C0C0, 0xC0C0C0C0, 0xC0C0C0C0}; CtrlRadio::CtrlRadio(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_RadioBox, load) { Bmp = new LMemDC; if (Bmp && Bmp->Create(12, 12, GdcD->GetColourSpace())) { int Len = ((Bmp->X()*24)+31)/32*4; for (int y=0; yY(); y++) { for (int x=0; xX(); x++) { uchar *s = ((uchar*) RadioBmp) + (y * Len) + (x * 3); Bmp->Colour(Rgb24(s[0], s[1], s[2]), 24); Bmp->Set(x, y); } } } } CtrlRadio::~CtrlRadio() { DeleteObj(Bmp); } IMPL_DIALOG_CTRL(CtrlRadio) void CtrlRadio::OnPaint(LSurface *pDC) { Client.ZOff(X()-1, Y()-1); LRect r(0, 0, 12, 12); // Draw the ctrl if (Bmp) pDC->Blt(r.x1, r.y1, Bmp); char *Text = GetStr()->Get(); if (Text) { LSysFont->Fore(L_TEXT); LSysFont->Transparent(true); LDisplayString ds(LSysFont, Text); ds.Draw(pDC, r.x2 + 10, r.y1-2); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Tab CtrlTab::CtrlTab(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Tab, load) { GetStr()->UpdateWnd = this; } IMPL_DIALOG_CTRL(CtrlTab) void CtrlTab::OnPaint(LSurface *pDC) { } void CtrlTab::ListChildren(List &l, bool Deep) { ResDialogCtrl *Ctrl = dynamic_cast(GetParent()); CtrlTabs *Par = dynamic_cast(Ctrl); LAssert(Par); auto MyIndex = Par->Tabs.IndexOf(this); LAssert(MyIndex >= 0); List *CList = (Par->Current == MyIndex) ? &Par->Children : &Children; for (auto w: *CList) { ResDialogCtrl *c = dynamic_cast(w); if (c) { l.Insert(c); if (Deep) { c->ListChildren(l, Deep); } } } } // Tab control CtrlTabs::CtrlTabs(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_TabView, load) { AcceptChildren = true; Current = 0; if (!load) { for (int i=0; i<3; i++) { CtrlTab *t = new CtrlTab(dlg, load); if (t) { char Text[256]; sprintf(Text, "Tab %i", i+1); if (t->GetStr()) { t->GetStr()->Set(Text); t->SetParent(this); Tabs.Insert(t); } else { DeleteObj(t); } } } } } CtrlTabs::~CtrlTabs() { Empty(); } void CtrlTabs::OnMouseMove(LMouse &m) { ResDialogCtrl::OnMouseMove(m); } void CtrlTabs::ShowMe(ResDialogCtrl *Child) { CtrlTab *t = dynamic_cast(Child); if (t) { auto Idx = Tabs.IndexOf(t); if (Idx >= 0) { ToTab(); Current = Idx; FromTab(); } } } void CtrlTabs::EnumCtrls(List &Ctrls) { List::I it = Tabs.begin(); for (CtrlTab *t = *it; t; t = *++it) { t->EnumCtrls(Ctrls); } ResDialogCtrl::EnumCtrls(Ctrls); } LRect CtrlTabs::GetMinSize() { List l; ListChildren(l, false); LRect r(0, 0, GRID_X-1, GRID_Y-1); /* // don't resize smaller than the tabs for (CtrlTab *t = Tabs.First(); t; t = Tabs.Next()) { r.x2 = max(r.x2, t->r.x2 + 10); r.y2 = max(r.y2, t->r.y2 + 10); } */ // don't resize smaller than any of the children // on any of the tabs LRect cli = GetClient(false); for (auto c: l) { LRect cpos = c->View()->GetPos(); cpos.Offset(cli.x1, cli.y1); r.Union(&cpos); } return r; } void CtrlTabs::ListChildren(List &l, bool Deep) { int n=0; for (auto t: Tabs) { l.Add(t); auto It = (Current == n ? (LViewI*)this : (LViewI*)t)->IterateViews(); for (LViewI *w: It) { ResDialogCtrl *c = dynamic_cast(w); if (c) { l.Insert(c); c->ListChildren(l, Deep); } } n++; } } void CtrlTabs::Empty() { ToTab(); Tabs.DeleteObjects(); } void CtrlTabs::OnPaint(LSurface *pDC) { // Draw the ctrl Title.ZOff(X()-1, 17); Client.ZOff(X()-1, Y()-1); Client.y1 = Title.y2; LRect r = Client; LWideBorder(pDC, r, DefaultRaisedEdge); // Draw the tabs int i = 0; int x = 2; for (auto Tab: Tabs) { char *Str = Tab->GetStr() ? Tab->GetStr()->Get() : 0; LDisplayString ds(LSysFont, Str); int Width = 12 + ds.X(); LRect t(x, Title.y1 + 2, x + Width - 1, Title.y2 - 1); if (Current == i) { t.Inset(-2, -2); if (Tab->IterateViews().Length() > 0) FromTab(); } if (Tab->View()->GetPos() != t) Tab->View()->SetPos(t); pDC->Colour(L_LIGHT); pDC->Line(t.x1, t.y1+2, t.x1, t.y2); pDC->Set(t.x1+1, t.y1+1); pDC->Line(t.x1+2, t.y1, t.x2-2, t.y1); pDC->Colour(L_MED); pDC->Line(t.x1+1, t.y1+2, t.x1+1, t.y2); pDC->Line(t.x1+2, t.y1+1, t.x2-2, t.y1+1); pDC->Colour(L_LOW); pDC->Line(t.x2-1, t.y1, t.x2-1, t.y2); pDC->Colour(0, 24); pDC->Line(t.x2, t.y1+2, t.x2, t.y2); t.Inset(2, 2); t.y2 += 2; LSysFont->Fore(L_TEXT); LSysFont->Back(L_MED); LSysFont->Transparent(false); ds.Draw(pDC, t.x1 + ((t.X()-ds.X())/2), t.y1 + ((t.Y()-ds.Y())/2), &t); x += Width + ((Current == i) ? 2 : 1); i++; } // Draw children //LWindow::OnPaint(pDC); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } void CtrlTabs::ToTab() { CtrlTab *Cur = Tabs.ItemAt(Current); if (Cur) { // move all our children into the tab losing focus auto CurIt = Cur->IterateViews(); auto ThisIt = IterateViews(); for (auto v: CurIt) { Cur->DelView(v); } for (auto v: ThisIt) { DelView(v); Cur->AddView(v); } } } void CtrlTabs::FromTab() { CtrlTab *Cur = Tabs.ItemAt(Current); if (Cur) { // load all our children from the new tab auto CurIt = Cur->IterateViews(); auto ThisIt = IterateViews(); for (auto v: ThisIt) DelView(v); for (auto v: CurIt) { Cur->DelView(v); AddView(v); } } } void CtrlTabs::OnMouseClick(LMouse &m) { if (m.Down()) { if (Title.Overlap(m.x, m.y)) { // select current tab int i = 0; for (auto Tab: Tabs) { if (Tab->View()->GetPos().Overlap(m.x, m.y) /* && i != Current*/) { ToTab(); Current = i; FromTab(); Dlg->OnSelect(Tab); Invalidate(); break; } i++; } } if (m.IsContextMenu() && Title.Overlap(m.x, m.y)) { auto RClick = new LSubMenu; if (RClick) { bool HasTab = Tabs.ItemAt(Current); RClick->AppendItem("New tab", IDM_NEW, true); RClick->AppendItem("Delete tab", IDM_DELETE, HasTab); RClick->AppendItem("Rename tab", IDM_RENAME, HasTab); RClick->AppendItem("Move tab left", IDM_MOVE_LEFT, HasTab); RClick->AppendItem("Move tab right", IDM_MOVE_RIGHT, HasTab); RClick->AppendSeparator(); RClick->AppendItem("Copy Text", IDM_COPY_TEXT, Dlg->Selection.Length()==1); RClick->AppendItem("Paste Text", IDM_PASTE_TEXT, true); if (GetMouse(m, true)) { switch (RClick->Float(this, m.x, m.y, false)) { case IDM_NEW: { CtrlTab *t = new CtrlTab(Dlg, 0); if (t) { char Text[256]; sprintf(Text, "Tab " LPrintfSizeT, Tabs.Length()+1); t->GetStr()->Set(Text); t->SetParent(this); Tabs.Insert(t); } break; } case IDM_DELETE: { CtrlTab *t = Tabs.ItemAt(Current); if (t) { ToTab(); Tabs.Delete(t); DeleteObj(t); FromTab(); } break; } case IDM_RENAME: { CtrlTab *t = Tabs.ItemAt(Current); if (t) { if (!t->GetStr()) t->SetStr(Dlg->CreateSymbol()); auto Input = new LInput(this, t->GetStr()->Get(), "Enter tab name:", "Rename"); Input->SetParent(Dlg); Input->DoModal([t, Input](auto dlg, auto id) { if (id) t->GetStr()->Set(Input->GetStr()); delete dlg; }); } break; } case IDM_MOVE_LEFT: { CtrlTab *t = Tabs.ItemAt(Current); if (t && Current > 0) { Tabs.Delete(t); Tabs.Insert(t, --Current); } break; } case IDM_MOVE_RIGHT: { CtrlTab *t = Tabs.ItemAt(Current); if (t && Current < Tabs.Length()-1) { Tabs.Delete(t); Tabs.Insert(t, ++Current); } break; } case IDM_COPY_TEXT: { CtrlTab *t = Tabs.ItemAt(Current); if (t) { t->CopyText(); } break; } case IDM_PASTE_TEXT: { CtrlTab *t = Tabs.ItemAt(Current); if (t) { t->PasteText(); } break; } } Invalidate(); } } return; } } ResDialogCtrl::OnMouseClick(m); } // List column ListCol::ListCol(ResDialog *dlg, LXmlTag *load, char *s, int Width) : ResDialogCtrl(dlg, Res_Column, load) { if (s && GetStr()) { GetStr()->Set(s); } LRect r(0, 0, Width-1, 18); ResDialogCtrl::SetPos(r); } IMPL_DIALOG_CTRL(ListCol) void ListCol::OnPaint(LSurface *pDC) { } // List control CtrlList::CtrlList(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_ListView, load) { DragCol = -1; } CtrlList::~CtrlList() { Empty(); } void CtrlList::ListChildren(List &l, bool Deep) { if (Deep) { for (auto w: Cols) { ResDialogCtrl *c = dynamic_cast(w); if (c) { l.Insert(c); c->ListChildren(l); } } } } void CtrlList::Empty() { for (auto c: Cols) { DeleteObj(c); } Cols.Empty(); } void CtrlList::OnMouseClick(LMouse &m) { if (m.Down()) { if (Title.Overlap(m.x, m.y)) { int x=0; ListCol *c = 0; ssize_t DragOver = -1; for (auto Col: Cols) { if (m.x >= Col->r().x1 && m.x <= Col->r().x2) { if (m.x > Col->r().x2 - 6) { DragOver = Cols.IndexOf(Col); } if (m.x < Col->r().x1 + 6) { DragOver = Cols.IndexOf(Col) - 1; } c = Col; break; } x += Col->r().X(); } if (m.Left()) { if (DragOver >= 0) { DragCol = DragOver; Capture(true); } } else if (m.IsContextMenu()) { auto RClick = new LSubMenu; if (RClick) { bool HasCol = c != 0; RClick->AppendItem("New column", IDM_NEW, true); RClick->AppendItem("Delete column", IDM_DELETE, HasCol); RClick->AppendItem("Rename column", IDM_RENAME, HasCol); RClick->AppendItem("Move column left", IDM_MOVE_LEFT, HasCol); RClick->AppendItem("Move column right", IDM_MOVE_RIGHT, HasCol); RClick->AppendSeparator(); RClick->AppendItem("Copy Text", IDM_COPY_TEXT, HasCol); RClick->AppendItem("Paste Text", IDM_PASTE_TEXT, HasCol); if (GetMouse(m, true)) { switch (RClick->Float(this, m.x, m.y, false)) { case IDM_COPY_TEXT: { if (c && c->GetStr()) { c->GetStr()->CopyText(); } break; } case IDM_PASTE_TEXT: { if (c && c->GetStr()) { c->GetStr()->PasteText(); } break; } case IDM_NEW: { ListCol *c = dynamic_cast(Dlg->CreateCtrl(UI_COLUMN,0)); if (c) { char Text[256]; sprintf(Text, "Col " LPrintfSizeT, Cols.Length()+1); c->GetStr()->Set(Text); Cols.Insert(c); } break; } case IDM_DELETE: { Cols.Delete(c); DeleteObj(c); break; } case IDM_RENAME: { if (c) { auto Input = new LInput(this, c->GetStr()->Get(), "Enter column name:", "Rename"); Input->SetParent(Dlg); Input->DoModal([Input, c](auto dlg, auto id) { if (id) c->GetStr()->Set(Input->GetStr()); delete dlg; }); } break; } case IDM_MOVE_LEFT: { auto Current = Cols.IndexOf(c); if (c && Current > 0) { Cols.Delete(c); Cols.Insert(c, --Current); } break; } case IDM_MOVE_RIGHT: { auto Current = Cols.IndexOf(c); if (c && Current < Cols.Length()-1) { Cols.Delete(c); Cols.Insert(c, ++Current); } break; } } Invalidate(); } DeleteObj(RClick); return; } } return; } } else { if (DragCol >= 0) { Capture(false); DragCol = -1; } } ResDialogCtrl::OnMouseClick(m); } void CtrlList::OnMouseMove(LMouse &m) { if (DragCol >= 0) { int i=0, x=0;; for (auto Col: Cols) { if (i == DragCol) { int Dx = (m.x - x - Title.x1); LRect r = Col->GetPos(); r.x2 = r.x1 + Dx; Col->ResDialogCtrl::SetPos(r); break; } x += Col->r().X(); i++; } Invalidate(); } ResDialogCtrl::OnMouseMove(m); } void CtrlList::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); // Draw the ctrl LWideBorder(pDC, r, DefaultSunkenEdge); Title = r; Title.y2 = Title.y1 + 15; Client = r; Client.y1 = Title.y2 + 1; pDC->Colour(L_WORKSPACE); pDC->Rectangle(&Client); int x = Title.x1; for (auto c: Cols) { int Width = c->r().X(); c->r().Set(x, Title.y1, x + Width - 1, Title.y2); LRect r = c->r(); r.x2 = MIN(r.x2, Title.x2); x = r.x2 + 1; if (r.Valid()) { LWideBorder(pDC, r, DefaultRaisedEdge); LSysFont->Fore(L_TEXT); LSysFont->Back(L_MED); LSysFont->Transparent(false); const char *Str = c->GetStr()->Get(); if (!Str) Str = ""; LDisplayString ds(LSysFont, Str); ds.Draw(pDC, r.x1 + 2, r.y1 + ((r.Y()-ds.Y())/2) - 1, &r); } } LRect Client(x, Title.y1, Title.x2, Title.y2); if (Client.Valid()) { LWideBorder(pDC, Client, DefaultRaisedEdge); pDC->Colour(L_MED);; pDC->Rectangle(&Client); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Combo box CtrlComboBox::CtrlComboBox(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_ComboBox, load) { } IMPL_DIALOG_CTRL(CtrlComboBox) void CtrlComboBox::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); Client = r; // Draw the ctrl LWideBorder(pDC, r, DefaultSunkenEdge); // Allocate space LRect e = r; e.x2 -= 15; LRect d = r; d.x1 = e.x2 + 1; // Draw edit pDC->Colour(L_WORKSPACE); pDC->Rectangle(&e); // Draw drap down LWideBorder(pDC, d, DefaultRaisedEdge); pDC->Colour(L_MED); pDC->Rectangle(&d); pDC->Colour(0, 24); int Size = 4; int cx = (d.X()/2) + d.x1 - 4; int cy = (d.Y()/2) + d.y1 - 3; for (int i=1; i<=Size; i++) { pDC->Line(cx+i, cy+i, cx+(Size*2)-i, cy+i); } // Text char *Text = GetStr()->Get(); LSysFont->Fore(L_TEXT); LSysFont->Transparent(true); if (Text) { LDisplayString ds(LSysFont, Text); ds.Draw(pDC, 4, 4); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// CtrlScrollBar::CtrlScrollBar(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_ScrollBar, load) { } IMPL_DIALOG_CTRL(CtrlScrollBar) void CtrlScrollBar::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); Client = r; // Draw the ctrl bool Vertical = r.Y() > r.X(); int ButSize = Vertical ? r.X() : r.Y(); LRect a, b, c; if (Vertical) { a.Set(r.x1, r.y1, r.x2, r.y1 + ButSize); c.Set(r.x1, r.y2 - ButSize, r.x2, r.y2); b.Set(r.x1, a.y2 + 1, r.x2, c.y1 - 1); } else { a.Set(r.x1, r.y1, r.x1 + ButSize, r.y2); c.Set(r.x2 - ButSize, r.y1, r.x2, r.y2); b.Set(a.x2 + 1, r.y1, c.x1 - 1, r.y2); } // Buttons LWideBorder(pDC, a, DefaultRaisedEdge); LWideBorder(pDC, c, DefaultRaisedEdge); pDC->Colour(L_MED); pDC->Rectangle(&a); pDC->Rectangle(&c); // Arrows pDC->Colour(0); int x = a.x1 + (a.X()/2) - 2; int y = a.y1 + (a.Y()/2); int i; for (i=0; i<5; i++) { pDC->Line(x+i, y-i, x+i, y+i); } x = c.x1 + (c.X()/2) - 2; y = c.y1 + (c.Y()/2); for (i=0; i<5; i++) { pDC->Line(x+i, y-(4-i), x+i, y+(4-i)); } // Slider pDC->Colour(0xd0d0d0, 24); pDC->Rectangle(&b); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// CtrlTree::CtrlTree(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_TreeView, load) { } IMPL_DIALOG_CTRL(CtrlTree) void CtrlTree::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); Client = r; LWideBorder(pDC, r, DefaultSunkenEdge); pDC->Colour(Rgb24(255, 255, 255), 24); pDC->Rectangle(&r); LSysFont->Colour(L_TEXT, L_WORKSPACE); LSysFont->Transparent(true); LDisplayString ds(LSysFont, "Tree"); ds.Draw(pDC, r.x1 + 3, r.y1 + 3, &r); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// CtrlBitmap::CtrlBitmap(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Bitmap, load) { } IMPL_DIALOG_CTRL(CtrlBitmap) void CtrlBitmap::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); Client = r; LWideBorder(pDC, r, DefaultSunkenEdge); pDC->Colour(Rgb24(255, 255, 255), 24); pDC->Rectangle(&r); pDC->Colour(0, 24); pDC->Line(r.x1, r.y1, r.x2, r.y2); pDC->Line(r.x2, r.y1, r.x1, r.y2); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// CtrlProgress::CtrlProgress(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Progress, load) { } IMPL_DIALOG_CTRL(CtrlProgress) void CtrlProgress::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); Client = r; LWideBorder(pDC, r, DefaultSunkenEdge); COLOUR Flat = Rgb24(0x7f, 0x7f, 0x7f); COLOUR White = Rgb24(0xff, 0xff, 0xff); int Pos = 60; int x = Pos * r.X() / 100; pDC->Colour(Flat, 24); pDC->Rectangle(r.x1, r.y1, r.x1 + x, r.y2); pDC->Colour(White, 24); pDC->Rectangle(r.x1 + x + 1, r.y1, r.x2, r.y2); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// CtrlCustom::CtrlCustom(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Custom, load) { Control = 0; } IMPL_DIALOG_CTRL(CtrlCustom) CtrlCustom::~CtrlCustom() { DeleteArray(Control); } void CtrlCustom::OnPaint(LSurface *pDC) { LRect r(0, 0, X()-1, Y()-1); Client = r; LWideBorder(pDC, r, DefaultSunkenEdge); COLOUR White = Rgb24(0xff, 0xff, 0xff); pDC->Colour(White, 24); pDC->Rectangle(&r); char s[256] = "Custom: "; if (Control) { strcat(s, Control); } LSysFont->Colour(L_TEXT, L_WORKSPACE); LDisplayString ds(LSysFont, s); ds.Draw(pDC, r.x1+2, r.y1+1, &r); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } #define VAL_Control "Ctrl" bool CtrlCustom::GetFields(FieldTree &Fields) { bool Status = ResDialogCtrl::GetFields(Fields); if (Status) { Fields.Insert(this, DATA_STR, 320, VAL_Control, "Control", 1); } return Status; } bool CtrlCustom::Serialize(FieldTree &Fields) { bool Status = ResDialogCtrl::Serialize(Fields); if (Status) { Fields.Serialize(this, VAL_Control, Control); } return Status; } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// ResStringGroup *ResDialog::Symbols = 0; int ResDialog::SymbolRefs = 0; bool ResDialog::CreateSymbols = true; void ResDialog::AddLanguage(const char *Id) { if (Symbols) { Symbols->AppendLanguage(Id); } } void ResDialogCtrl::EnumCtrls(List &Ctrls) { Ctrls.Insert(this); for (LViewI *c: View()->IterateViews()) { ResDialogCtrl *dc = dynamic_cast(c); LAssert(dc); dc->EnumCtrls(Ctrls); } } void ResDialog::EnumCtrls(List &Ctrls) { for (auto ci: Children) { ResDialogCtrl *c = dynamic_cast(ci); if (c) c->EnumCtrls(Ctrls); } } int GooberCaps[] = { RESIZE_X1 | RESIZE_Y1, RESIZE_Y1, RESIZE_X2 | RESIZE_Y1, RESIZE_X2, RESIZE_X2 | RESIZE_Y2, RESIZE_Y2, RESIZE_X1 | RESIZE_Y2, RESIZE_X1}; ResDialog::ResDialog(AppWnd *w, int type) : Resource(w, type) { // Maintain a string group just for our dialog // defines if (!Symbols && w) { // First check to see of the symbols have been loaded from a file // and we don't have a pointer to it yet Symbols = App()->GetDialogSymbols(); if (!Symbols) { // else we need to create the symbols object Symbols = new ResStringGroup(w); if (Symbols) { Symbols->Name(StrDialogSymbols); w->InsertObject(TYPE_STRING, Symbols); } } if (Symbols) { Symbols->SystemObject(true); } } SymbolRefs++; // Init dialog resource Ui = 0; DlgPos.ZOff(500-1, 400-1); DragGoober = -1; DragX = 0; DragY = 0; DragCtrl = 0; } ResDialog::~ResDialog() { // Decrement and delete the shared string group SymbolRefs--; if (SymbolRefs < 1) { App()->DelObject(Symbols); Symbols = 0; } // Delete our Ui DeleteObj(Ui); } void ResDialog::OnShowLanguages() { // Current language changed. OnSelect(Selection[0]); Invalidate(); OnLanguageChange(); } void ResDialog::OnChildrenChanged(LViewI *Wnd, bool Attaching) { printf("ResDialog::OnChildrenChanged %p, %i\n", Wnd, Attaching); } const char *ResDialog::Name() { LViewI *v = Children[0]; ResDialogCtrl *Ctrl = dynamic_cast(v); if (!Ctrl) { static char msg[256]; sprintf_s(msg, sizeof(msg), "#no_ctrl=%p,children=%i", v, (int)Children.Length()); return msg; } if (!Ctrl->GetStr()) return "#no_str"; if (!Ctrl->GetStr()->GetDefine()) return "#no_defined"; return Ctrl->GetStr()->GetDefine(); } bool ResDialog::Name(const char *n) { ResDialogCtrl *Ctrl = dynamic_cast(Children[0]); if (Ctrl && Ctrl->GetStr()) { Ctrl->GetStr()->SetDefine((n)?n:""); return Ctrl->GetStr()->GetDefine() != 0; } return false; } char *ResDialog::StringFromRef(int Ref) { if (Symbols) { ResString *Str = Symbols->FindRef(Ref); if (Str) { return Str->Get(); } } return 0; } bool ResDialog::Res_GetProperties(ResObject *Obj, LDom *Props) { ResDialogCtrl *Ctrl = dynamic_cast(Obj); if (Ctrl && Props) { int Next = -1000; FieldTree t(Next, false); t.SetStore(Props); t.SetMode(FieldTree::ObjToStore); Ctrl->GetFields(t); Ctrl->Serialize(t); return true; } return false; } LDom *ResDialog::Res_GetDom(ResObject *Obj) { return dynamic_cast(Obj); } bool ResDialog::Res_SetProperties(ResObject *Obj, LDom *Props) { ResDialogCtrl *Ctrl = dynamic_cast(Obj); if (Ctrl && Props) { int Next = -1000; FieldTree t(Next, false); t.SetStore(Props); t.SetMode(FieldTree::StoreToObj); Ctrl->GetFields(t); Ctrl->Serialize(t); return true; } return false; } ResObject *ResDialog::CreateObject(LXmlTag *Tag, ResObject *Parent) { return dynamic_cast(CreateCtrl(Tag)); } void ResDialog::Res_SetPos(ResObject *Obj, int x1, int y1, int x2, int y2) { if (Obj) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); if (Ctrl) { LRect r(x1, y1, x2, y2); Ctrl->SetPos(r); } } } void ResDialog::Res_SetPos(ResObject *Obj, char *s) { if (Obj) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); if (Ctrl) { Ctrl->ReadPos(s); } } } LRect ResDialog::Res_GetPos(ResObject *Obj) { if (Obj) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); LAssert(Ctrl); if (Ctrl) { return Ctrl->View()->GetPos(); } } return LRect(0, 0, 0, 0); } int ResDialog::Res_GetStrRef(ResObject *Obj) { if (Obj) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); if (Ctrl) { return Ctrl->GetStr()->GetRef(); } } return -1; } bool ResDialog::Res_SetStrRef(ResObject *Obj, int Ref, ResReadCtx *Ctx) { ResDialogCtrl *Ctrl = 0; if (!Obj || !Symbols) return false; Ctrl = dynamic_cast((ResDialogCtrl*)Obj); if (!Ctrl) return false; Ctrl->StrFromRef(Ref); LAssert(Ctrl && Ctrl->GetStr()); return Ctrl->GetStr() != 0; } void ResDialog::Res_Attach(ResObject *Obj, ResObject *Parent) { if (Obj && Parent) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); ResDialogCtrl *Par = dynamic_cast((ResDialogCtrl*)Parent); if (Ctrl && Par) { Par->AttachCtrl(Ctrl); } } } bool ResDialog::Res_GetChildren(ResObject *Obj, List *l, bool Deep) { bool Status = false; if (Obj) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); if (Ctrl) { Status = true; List Child; Ctrl->ListChildren(Child, Deep); for (auto o: Child) { l->Insert(o); } } } return Status; } void ResDialog::Res_Append(ResObject *Obj, ResObject *Parent) { if (Obj && Parent) { CtrlTabs *Tabs = dynamic_cast(Obj); CtrlTab *Tab = dynamic_cast(Parent); if (Tabs && Tab) { Tab->SetParent(Tabs); Tabs->Tabs.Insert(Tab); if (Tabs->Tabs.Length() == 1) { Tabs->FromTab(); } return; } CtrlList *Lst = dynamic_cast(Obj); ListCol *Col = dynamic_cast(Parent); if (Lst && Col) { Lst->Cols.Insert(Col); return; } } } bool ResDialog::Res_GetItems(ResObject *Obj, List *l) { if (Obj && l) { CtrlTabs *Tabs = dynamic_cast(Obj); if (Tabs) { for (auto Tab: Tabs->Tabs) { l->Insert(Tab); } return true; } CtrlList *Lst = dynamic_cast(Obj); if (Lst) { for (auto Col: Lst->Cols) { l->Insert(Col); } return true; } } return false; } void ResDialog::Create(LXmlTag *load, SerialiseContext *Ctx) { CtrlDlg *Dlg = new CtrlDlg(this, load); if (Dlg) { LRect r = DlgPos; r.Offset(GOOBER_BORDER, GOOBER_BORDER); Children.Insert(Dlg); Dlg->SetParent(this); if (load) { if (Ctx) Read(load, *Ctx); else LAssert(0); } else { Dlg->ResDialogCtrl::SetPos(r); if (Dlg->GetStr()) Dlg->GetStr()->Set("Dialog"); } } } void ResDialog::Delete() { // Deselect the dialog ctrl OnDeselect(dynamic_cast(Children[0])); // Delete selected controls ResDialogCtrl *c; while ((c = Selection[0])) { c->View()->Detach(); DeleteObj(c); } // Repaint LView::Invalidate(); } bool IsChild(ResDialogCtrl *Parent, ResDialogCtrl *Child) { if (Parent && Child) { for ( ResDialogCtrl *c = Child->ParentCtrl(); c; c = c->ParentCtrl()) { if (c == Parent) { return true; } } } return false; } void GetTopCtrls(List &Top, List &Selection) { // all children will automatically be cut as well for (auto c: Selection) { // is c a child of an item already in Top? bool Ignore = false; for (auto p: Top) { if (IsChild(p, c)) { Ignore = true; break; } } if (!Ignore) { // is not a child Top.Insert(c); } } } void ResDialog::Copy(bool Delete) { bool Status = false; // Deselect the dialog... can't cut that OnDeselect(dynamic_cast(Children[0])); // Get top level list List Top; GetTopCtrls(Top, Selection); // ok we have a top level list of ctrls // write them to the file List All; LXmlTag *Root = new LXmlTag("Resources"); if (Root) { if (Delete) { // remove selection from UI AppWindow->OnObjSelect(0); } // write the string resources first for (auto c: Top) { All.Insert(c); c->ListChildren(All, true); } // write the strings out at the top of the block // so that we can reference them from the objects // below. SerialiseContext Ctx; for (auto c: All) { // Write the string out LXmlTag *t = new LXmlTag; if (t && c->GetStr()->Write(t, Ctx)) { Root->InsertTag(t); } else { DeleteObj(t); } } // write the objects themselves for (auto c: Top) { LXmlTag *t = new LXmlTag; if (t && Res_Write(c, t)) { Root->InsertTag(t); } else { DeleteObj(t); } } // Read the file in and copy to the clipboard LStringPipe Xml; LXmlTree Tree; if (Tree.Write(Root, &Xml)) { char *s = Xml.NewStr(); { LClipBoard Clip(Ui); char16 *w = Utf8ToWide(s); Clip.TextW(w); Status = Clip.Text(s, false); DeleteObj(w); } DeleteArray(s); } DeleteObj(Root); if (Delete && Status) { // delete them ResDialogCtrl *c; while ( Selection.Length() && (c = Selection[0])) { c->View()->Detach(); Selection.Delete(c); DeleteObj(c); } } // Repaint LView::Invalidate(); } } class StringId { public: ResString *Str; int OldRef; int NewRef; }; void RemapAllRefs(LXmlTag *t, List &Strs) { char *RefStr; if ((RefStr = t->GetAttr("Ref"))) { int r = atoi(RefStr); for (auto i: Strs) { if (i->OldRef == r) { // this string ref is to be remapped char Buf[32]; sprintf(Buf, "%i", i->NewRef); t->SetAttr("Ref", Buf); // delete the string ref map // it's not needed anymore Strs.Delete(i); DeleteObj(i); // leave the loop RefStr = 0; break; } } if (RefStr) { // if this assert failes then no map for this // string was found. every incomming string needs // to be remapped LAssert(0); } } for (auto c: t->Children) { RemapAllRefs(c, Strs); } } void ResDialog::Paste() { // Get the clipboard data char *Mem = 0; char *Data = 0; { LClipBoard Clip(Ui); char16 *w = Clip.TextW(); if (w) Data = Mem = WideToUtf8(w); else Data = Clip.Text(); } if (Data) { ResDialogCtrl *Container = 0; // Find a container to plonk the controls in ResDialogCtrl *c = Selection[0]; if (c && c->IsContainer()) { Container = c; } if (!Container) { // Otherwise just use the dialog as the container Container = dynamic_cast(Children[0]); } if (Container) { // remap list List Strings; int NextRef = 0; // Deselect everything OnSelect(NULL); // Parse the data List NewStrs; LXmlTree Tree; LStringPipe p; p.Push(Data); // Create the new controls, strings first // that way we can setup the remapping properly to avoid // string ref duplicates LXmlTag Root; if (Tree.Read(&Root, &p, 0)) { for (auto t: Root.Children) { if (t->IsTag("string")) { // string tag LAssert(Symbols); ResString *Str = Symbols->CreateStr(); SerialiseContext Ctx; if (Str && Str->Read(t, Ctx)) { // setup remap object, so that we can make all the strings // unique StringId *Id = new StringId; LAssert(Id); Id->Str = Str; Id->OldRef = Str->GetRef(); NextRef = Str->SetRef(Id->NewRef = AppWindow->GetUniqueStrRef(NextRef + 1)); Strings.Insert(Id); // insert out new string NewStrs.Insert(Str); } else { break; } } else { // object tag // all strings should have been processed by the time // we get in here. We remap the incomming string refs to // unique values so that we don't get conflicts later. RemapAllRefs(t, Strings); } } // load all the objects now List NewCtrls; for (auto t: Root.Children) { if (!t->IsTag("string")) { // object (not string) CreateSymbols = false; ResDialogCtrl *Ctrl = dynamic_cast(CreateCtrl(t)); CreateSymbols = true; ResReadCtx Ctx; if (Ctrl && Res_Read(Ctrl, t, Ctx)) { NewCtrls.Insert(Ctrl); } else { break; } } } // calculate the new control set's dimensions so we // can paste them in at the top of the container auto It = NewCtrls.begin(); ResDialogCtrl *c = *It; if (c) { LRect All = c->View()->GetPos(); while ((c = *(++It))) { All.Union(&c->View()->GetPos()); } // now paste in the controls for (auto c: NewCtrls) { LRect *Preference = Container->GetPasteArea(); LRect p = c->View()->GetPos(); p.Offset(-All.x1, -All.y1); p.Offset(Preference ? Preference->x1 : Container->Client.x1 + GRID_X, Preference ? Preference->y1 : Container->Client.y1 + GRID_Y); c->SetPos(p); Container->AttachCtrl(c, &c->View()->GetPos()); OnSelect(c, false); } // reset parent size to fit LRect cp = Container->View()->GetPos(); Container->SetPos(cp, true); } // Deduplicate all these new strings for (auto s: NewStrs) { s->UnDuplicate(); } } // Repaint LView::Invalidate(); } } DeleteArray(Mem); } void ResDialog::SnapPoint(LPoint *p, ResDialogCtrl *From) { ResDialogCtrl *Ctrl = dynamic_cast(Children[0]); if (p && Ctrl) { int Ox = 0; // -Ctrl->Client.x1; int Oy = 0; // -Ctrl->Client.y1; LView *Parent = dynamic_cast(Ctrl); if (From) { for (LViewI *w = From->View(); w && w != Parent; w = w->GetParent()) { Ox += w->GetPos().x1; Oy += w->GetPos().y1; } } p->x += Ox; p->y += Oy; p->x = ((p->x + (GRID_X / 2)) / GRID_X * GRID_X); p->y = ((p->y + (GRID_X / 2)) / GRID_X * GRID_X); p->x -= Ox; p->y -= Oy; } } void ResDialog::SnapRect(LRect *r, ResDialogCtrl *From) { ResDialogCtrl *Ctrl = dynamic_cast(Children[0]); if (r && Ctrl) { int Ox = 0; // -Ctrl->Client.x1; int Oy = 0; // -Ctrl->Client.y1; LView *Parent = dynamic_cast(Ctrl); for (LViewI *w = From->View(); w && w != Parent; w = w->GetParent()) { Ox += w->GetPos().x1; Oy += w->GetPos().y1; } r->Normal(); r->Offset(Ox, Oy); r->x1 = ((r->x1 + (GRID_X / 2)) / GRID_X * GRID_X); r->y1 = ((r->y1 + (GRID_X / 2)) / GRID_X * GRID_X); r->x2 = ((r->x2 + (GRID_X / 2)) / GRID_X * GRID_X) - 1; r->y2 = ((r->y2 + (GRID_X / 2)) / GRID_X * GRID_X) - 1; r->Offset(-Ox, -Oy); } } bool ResDialog::IsDraging() { return DragGoober >= 0; } int ResDialog::CurrentTool() { return (Ui) ? Ui->CurrentTool() : 0; } void ResDialog::MoveSelection(int Dx, int Dy) { auto It = Selection.begin(); ResDialogCtrl *w = *It; if (!w) return; ResDialogCtrl *Parent = w->ParentCtrl(); if (!Parent) return; // find dimensions of group LRect All = w->View()->GetPos(); for (; w; w = *(++It)) All.Union(&w->View()->GetPos()); // limit the move to the top-left corner of the parent's client LRect ParentClient = Parent->Client; if (dynamic_cast(Parent)) ParentClient.ZOff(-ParentClient.x1, -ParentClient.y1); if (All.x1 + Dx < ParentClient.x1) Dx = ParentClient.x1 - All.x1; if (All.y1 + Dy < ParentClient.y1) Dy = ParentClient.y1 - All.y1; // move the ctrls LRegion Update; for (auto w: Selection) { LRect Old = w->View()->GetPos(); LRect New = Old; New.Offset(Dx, Dy); // optionally limit the move to the containers bounds LRect *b = w->ParentCtrl()->GetChildArea(w); if (b) { if (New.x1 < b->x1) New.Offset(b->x1 - New.x1, 0); else if (New.x2 > b->x2) New.Offset(b->x2 - New.x2, 0); if (New.y1 < b->y1) New.Offset(0, b->y1 - New.y1); else if (New.y2 > b->y2) New.Offset(0, b->y2 - New.y2); } LRect Up = w->AbsPos(); Up.Inset(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); w->SetPos(New, false); Up = w->AbsPos(); Up.Inset(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); } Invalidate(&Update); } void ResDialog::SelectCtrl(ResDialogCtrl *c) { Selection.Empty(); if (c) { bool IsMine = false; for (LViewI *p = c->View(); p; p = p->GetParent()) { if ((LViewI*)this == p) { IsMine = true; break; } } if (IsMine) { Selection.Insert(c); // int TabIdx = -1; ResDialogCtrl *Prev = 0; for (LViewI *p = c->View()->GetParent(); p; p = p->GetParent()) { ResDialogCtrl *c = dynamic_cast(p); if (c) { c->ShowMe(Prev); Prev = c; } else break; } } else LgiTrace("%s:%i - Ctrl doesn't belong to me.\n", __FILE__, __LINE__); } else LgiTrace("Selecting '0'\n"); Invalidate(); if (AppWindow) AppWindow->OnObjSelect(c); } void ResDialog::SelectNone() { Selection.Empty(); Invalidate(); if (AppWindow) { AppWindow->OnObjSelect(0); } } void ResDialog::SelectRect(ResDialogCtrl *Parent, LRect *r, bool ClearPrev) { if (ClearPrev) { Selection.Empty(); } if (Parent && r) { for (LViewI *c: Parent->View()->IterateViews()) { if (c->GetPos().Overlap(r)) { ResDialogCtrl *Ctrl = dynamic_cast(c); if (Ctrl) { if (Selection.IndexOf(Ctrl) >= 0) { Selection.Delete(Ctrl); } else { Selection.Insert(Ctrl); } } } } } Invalidate(); if (AppWindow) { AppWindow->OnObjSelect((Selection.Length() == 1) ? Selection[0] : 0); } } bool ResDialog::IsSelected(ResDialogCtrl *Ctrl) { return Selection.IndexOf(Ctrl) >= 0; } void ResDialog::OnSelect(ResDialogCtrl *Wnd, bool ClearPrev) { if (ClearPrev) { Selection.Empty(); } if (Wnd) { ResDialogCtrl *Ctrl = dynamic_cast(Wnd); if (Ctrl) { if (!Selection.HasItem(Ctrl)) { Selection.Insert(Ctrl); } if (Ui) { Ui->SelectTool(UI_CURSOR); } Invalidate(); } if (AppWindow) { AppWindow->OnObjSelect((Selection.Length() == 1) ? Selection[0] : 0); } } } void ResDialog::OnDeselect(ResDialogCtrl *Wnd) { if (Selection.HasItem(Wnd)) { Selection.Delete(Wnd); AppWindow->OnObjSelect(0); } } ResDialogCtrl *ResDialog::CreateCtrl(LXmlTag *t) { if (t) { for (LgiObjectName *o = NameMap; o->Type; o++) { if (t->IsTag(o->ResourceName)) { return CreateCtrl(o->Type, t); } } } return 0; } ResDialogCtrl *ResDialog::CreateCtrl(int Tool, LXmlTag *load) { ResDialogCtrl *Ctrl = 0; switch (Tool) { case UI_DIALOG: { Ctrl = new CtrlDlg(this, load); break; } case UI_TABLE: { Ctrl = new CtrlTable(this, load); break; } case UI_TEXT: { Ctrl = new CtrlText(this, load); if (Ctrl && Ctrl->GetStr() && !load) { Ctrl->GetStr()->Set("Some text"); } break; } case UI_EDITBOX: { Ctrl = new CtrlEditbox(this, load); break; } case UI_CHECKBOX: { Ctrl = new CtrlCheckbox(this, load); if (Ctrl && Ctrl->GetStr() && !load) { Ctrl->GetStr()->Set("Checkbox"); } break; } case UI_BUTTON: { Ctrl = new CtrlButton(this, load); if (Ctrl && Ctrl->GetStr() && !load) { static int i = 1; char Text[256]; sprintf(Text, "Button %i", i++); Ctrl->GetStr()->Set(Text); } break; } case UI_GROUP: { Ctrl = new CtrlGroup(this, load); if (Ctrl && Ctrl->GetStr() && !load) { Ctrl->GetStr()->Set("Text"); } break; } case UI_RADIO: { Ctrl = new CtrlRadio(this, load); if (Ctrl && Ctrl->GetStr() && !load) { Ctrl->GetStr()->Set("Radio"); } break; } case UI_TAB: { Ctrl = new CtrlTab(this, load); break; } case UI_TABS: { Ctrl = new CtrlTabs(this, load); break; } case UI_LIST: { Ctrl = new CtrlList(this, load); break; } case UI_COLUMN: { Ctrl = new ListCol(this, load); break; } case UI_COMBO: { Ctrl = new CtrlComboBox(this, load); break; } case UI_TREE: { Ctrl = new CtrlTree(this, load); break; } case UI_BITMAP: { Ctrl = new CtrlBitmap(this, load); break; } case UI_SCROLL_BAR: { Ctrl = new CtrlScrollBar(this, load); break; } case UI_PROGRESS: { Ctrl = new CtrlProgress(this, load); break; } case UI_CUSTOM: { Ctrl = new CtrlCustom(this, load); break; } case UI_CONTROL_TREE: { Ctrl = new CtrlControlTree(this, load); break; } default: { LAssert(!"No control factory handler."); break; } } if (Ctrl && Ctrl->GetStr()) { Ctrl->GetStr()->UpdateWnd = Ctrl->View(); } return Ctrl; } LView *ResDialog::CreateUI() { return Ui = new ResDialogUi(this); } void ResDialog::DrawSelection(LSurface *pDC) { if (Selection.Length() == 0) return; // Draw selection for (auto Ctrl: Selection) { LRect r = Ctrl->AbsPos(); LColour s(255, 0, 0); LColour c = GetParent()->Focus() ? s : s.Mix(LColour(L_MED), 0.4); DrawGoobers(pDC, r, Ctrl->Goobers, c, Ctrl->OverGoober); } } #ifdef WINDOWS #define USE_MEM_DC 1 #else #define USE_MEM_DC 0 #endif void ResDialog::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { // Create temp DC if needed... LAutoPtr Local; if (!pDC) { if (!Local.Reset(new LScreenDC(this))) return; pDC = Local; } #if USE_MEM_DC LDoubleBuffer DblBuf(pDC); #endif LView::_Paint(pDC, Offset, Update); if (GetParent()) { #ifndef WIN32 LRect p = GetPos(); if (Offset) p.Offset(Offset); pDC->SetClient(&p); #endif DrawSelection(pDC); #ifndef WIN32 pDC->SetClient(NULL); #endif if (DebugOverlay) { pDC->Op(GDC_ALPHA); pDC->Blt(0, 0, DebugOverlay); } } } void ResDialog::OnPaint(LSurface *pDC) { pDC->Colour(L_WORKSPACE); pDC->Rectangle(); ResDialogCtrl *Ctrl = dynamic_cast(Children[0]); if (!Ctrl) return; } void ResDialog::OnLanguageChange() { if (Ui && Ui->StatusInfo) { LLanguage *l = Symbols->GetLanguage(App()->GetCurLang()->Id); if (l) { char Str[256]; sprintf(Str, "Current Language: %s (Id: %s)", l->Name, l->Id); Ui->StatusInfo->Name(Str); } } } bool ResDialog::OnKey(LKey &k) { if (k.Ctrl()) { switch (k.c16) { case LK_UP: { if (k.Down()) { int Idx = Symbols->GetLangIdx(App()->GetCurLang()->Id); if (Idx > 0) { LLanguage *l = Symbols->GetLanguage(Idx - 1); if (l) { App()->SetCurLang(l); } } Invalidate(); OnLanguageChange(); } return true; break; } case LK_DOWN: { if (k.Down()) { int Idx = Symbols->GetLangIdx(App()->GetCurLang()->Id); if (Idx < Symbols->GetLanguages() - 1) { LLanguage *l = Symbols->GetLanguage(Idx + 1); if (l) { App()->SetCurLang(l); } } Invalidate(); OnLanguageChange(); } return true; break; } } } return false; } void ResDialog::OnMouseClick(LMouse &m) { if (m.Down()) { if (GetParent()) GetParent()->Focus(true); if (m.Left()) { DragGoober = -1; for (auto c: Selection) { for (int i=0; i<8; i++) { if (c->Goobers[i].Overlap(m.x, m.y)) { DragGoober = i; DragCtrl = c; // LgiTrace("IN goober[%i]=%s %i,%i\n", i, c->Goobers[i].GetStr(), m.x, m.y); break; } else { // LgiTrace("goober[%i]=%s %i,%i\n", i, c->Goobers[i].GetStr(), m.x, m.y); } } } if (DragGoober >= 0) { DragRgn = DragCtrl->View()->GetPos(); DragX = DragY = 0; int Cap = GooberCaps[DragGoober]; // Lock the dialog to the top left corner if (dynamic_cast(DragCtrl)) { Cap &= ~(RESIZE_X1|RESIZE_Y1); } if (!Cap) { // Can't move the object anyway so abort the drag DragGoober = -1; } else { // Allow movement along the appropriate edge if (Cap & RESIZE_X1) { DragX = &DragRgn.x1; } if (Cap & RESIZE_Y1) { DragY = &DragRgn.y1; } if (Cap & RESIZE_X2) { DragX = &DragRgn.x2; } if (Cap & RESIZE_Y2) { DragY = &DragRgn.y2; } // Remember the offset to the mouse from the egdes if (DragX) DragOx = m.x - *DragX; if (DragY) DragOy = m.y - *DragY; Capture(true); } } } } else { if (DragGoober >= 0) { Capture(false); DragGoober = -1; DragX = 0; DragY = 0; } } } void ResDialog::OnMouseMove(LMouse &m) { // This code hilights the goober when the mouse is over it. for (auto c: Selection) { int Old = c->OverGoober; c->OverGoober = -1; for (int i=0; i<8; i++) { if (c->Goobers[i].Overlap(m.x, m.y)) { c->OverGoober = i; break; } } if (c->OverGoober != Old) Invalidate(); } if (DragGoober >= 0) { if (DragX) *DragX = m.x - DragOx; if (DragY) *DragY = m.y - DragOy; // DragRgn in real coords LRect Old = DragCtrl->View()->GetPos(); LRect New = DragRgn; auto It = IterateViews(); if (DragCtrl->View() != It[0]) SnapRect(&New, DragCtrl->ParentCtrl()); // New now in snapped coords // If that means the dragging control changes then if (New != Old) { LRegion Update; // change everyone else by the same amount for (auto c: Selection) { LRect OldPos = c->View()->GetPos(); LRect NewPos = OldPos; NewPos.x1 += New.x1 - Old.x1; NewPos.y1 += New.y1 - Old.y1; NewPos.x2 += New.x2 - Old.x2; NewPos.y2 += New.y2 - Old.y2; LRect Up = c->AbsPos(); Up.Inset(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); c->SetPos(NewPos); Up = c->AbsPos(); Up.Inset(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); } Invalidate(&Update); } } } bool ResDialog::Test(ErrorCollection *e) { return true; } bool ResDialog::Read(LXmlTag *t, SerialiseContext &Ctx) { bool Status = false; // turn symbol creation off so that ctrls find their // symbol via Id matching instead of creating their own CreateSymbols = false; ResDialogCtrl *Dlg = dynamic_cast(Children[0]); if (Dlg) { // Load the resource ResReadCtx Ctx; Status = Res_Read(Dlg, t, Ctx); Item->Update(); } CreateSymbols = true; return Status; } void ResDialog::CleanSymbols() { // removed unreferenced dialog strings if (Symbols) { Symbols->RemoveUnReferenced(); } // re-ID duplicate entries ResDialogCtrl *Ctrl = dynamic_cast(Children[0]); if (Ctrl) { // list all the entries List l; Ctrl->ListChildren(l); l.Insert(Ctrl); // insert the dialog too // remove duplicate string entries for (auto c: l) { LAssert(c->GetStr()); c->GetStr()->UnDuplicate(); } } // sort list (cause I need to read the file myself) if (Symbols) { Symbols->Sort(); } } bool ResDialog::Write(LXmlTag *t, SerialiseContext &Ctx) { bool Status = false; ResDialogCtrl *Ctrl = dynamic_cast(Children[0]); if (Ctrl) { // duplicates symbols should have been removed before the // strings were written out // so we don't have to do it here. // write the resources out if (Ctrl) { Status = Res_Write(Ctrl, t); } } else LgiTrace("%s:%i - Not a ResDialogCtrl.\n", _FL); return Status; } void ResDialog::OnRightClick(LSubMenu *RClick) { if (RClick) { if (Enabled()) { RClick->AppendSeparator(); if (Type() > 0) { RClick->AppendItem("Dump to C++", IDM_DUMP, true); auto Export = RClick->AppendSub("Export to..."); if (Export) { Export->AppendItem("Lgi File", IDM_EXPORT, true); Export->AppendItem("Win32 Resource Script", IDM_EXPORT_WIN32, false); } } } } } const char *TypeOfRes(ResDialogCtrl *Ctrl) { // the default return "LWindow"; } const char *TextOfCtrl(ResDialogCtrl *Ctrl) { static char Buf[256]; switch (Ctrl->GetType()) { // Has text case UI_TEXT: case UI_EDITBOX: case UI_CHECKBOX: case UI_BUTTON: case UI_GROUP: case UI_RADIO: case UI_COMBO: { char *s = Ctrl->GetStr()->Get(); sprintf(Buf, ", \"%s\"", s?s:""); return Buf; } // Not processed case UI_COLUMN: case UI_TAB: { LAssert(0); break; } // No text... case UI_BITMAP: case UI_PROGRESS: case UI_TABS: case UI_LIST: case UI_TREE: case UI_SCROLL_BAR: { break; } } return ""; } void OutputCtrl(LStringPipe &Def, LStringPipe &Var, LStringPipe &Inst, ResDialogCtrl *Ctrl, ResDialogCtrl *Parent, int &Index) { char Str[256]; const char *Type = "LView"; for (LgiObjectName *on=NameMap; on->Type; on++) { if (Ctrl->GetType() == on->Type) { Type = on->ObjectName; break; } } if (stricmp(Type, "LDialog")) { if (ValidStr(Ctrl->GetStr()->GetDefine()) && stricmp(Ctrl->GetStr()->GetDefine(), "IDOK") && stricmp(Ctrl->GetStr()->GetDefine(), "IDCANCEL") && stricmp(Ctrl->GetStr()->GetDefine(), "IDC_STATIC")) { char Tab[8]; auto Tabs = (32 - strlen(Ctrl->GetStr()->GetDefine()) - 1) / 4; memset(Tab, '\t', Tabs); Tab[Tabs] = 0; sprintf(Str, "#define %s%s%i\n", Ctrl->GetStr()->GetDefine(), Tab, 1000 + Index); Def.Push(Str); } sprintf(Str, "\t%s *Ctrl%i;\n", Type, Index); Var.Push(Str); sprintf(Str, "\tChildren.Insert(Ctrl%i = new %s(%s, %i, %i, %i, %i%s));\n", Index, Type, Ctrl->GetStr()->GetDefine(), Ctrl->View()->GetPos().x1 - 3, Ctrl->View()->GetPos().y1 - 17, Ctrl->View()->X(), Ctrl->View()->Y(), TextOfCtrl(Ctrl)); Inst.Push(Str); CtrlList *List = dynamic_cast(Ctrl); if (List) { // output columns for (auto c: List->Cols) { sprintf(Str, "\tCtrl%i->AddColumn(\"%s\", %i);\n", Index, c->GetStr()->Get(), c->X()); Inst.Push(Str); } } Index++; } for (auto c: Ctrl->View()->IterateViews()) OutputCtrl(Def, Var, Inst, dynamic_cast(c), Ctrl, Index); } void ResDialog::OnCommand(int Cmd) { switch (Cmd) { case IDM_DUMP: { LStringPipe Def, Var, Inst; LStringPipe Buf; char Str[256]; ResDialogCtrl *Dlg = dynamic_cast(Children[0]); if (Dlg) { // List controls int i=0; OutputCtrl(Def, Var, Inst, Dlg, 0, i); // #define's char *Defs = Def.NewStr(); if (Defs) { Buf.Push(Defs); Def.Empty(); } // Class def Buf.Push( "\nclass Dlg : public LDialog\n" "{\n"); // Variables char *Vars = Var.NewStr(); if (Vars) { Buf.Push(Vars); Var.Empty(); } // Member functions Buf.Push( "\n" "public:\n" "\tDlg(LView *Parent);\n" "\t~Dlg();\n" "\n" "\tint OnNotify(LViewI *Ctrl, int Flags);\n" "};\n" "\n"); // Class impl Buf.Push( "Dlg::Dlg(LView *Parent)\n" "{\n" "\tSetParent(Parent);\n"); sprintf(Str, "\tName(\"%s\");\n" "\tGRegion r(0, 0, %i, %i);\n" "\tSetPos(r);\n", Dlg->GetStr()->Get(), Dlg->View()->X(), Dlg->View()->Y()); Buf.Push(Str); Buf.Push("\tMoveToCenter();\n\n"); // Ctrl instancing char *NewCtrls = Inst.NewStr(); if (NewCtrls) { Buf.Push(NewCtrls); Inst.Empty(); } Buf.Push( "}\n" "\n"); // Destructor Buf.Push( "Dlg::~Dlg()\n" "{\n" "}\n" "\n"); // ::OnNotify Buf.Push( "int Dlg::OnNotify(LViewI *Ctrl, int Flags)\n" "{\n" "\tswitch (Ctrl->GetId())\n" "\t{\n" "\t\tcase IDOK:\n" "\t\t{\n" "\t\t\t// Do something here\n" "\t\t\t// fall thru\n" "\t\t}\n" "\t\tcase IDCANCEL:\n" "\t\t{\n" "\t\t\tEndModal(Ctrl->GetId());\n" "\t\t\tbreak;\n" "\t\t}\n" "\t}\n" "\n" "\treturn 0;\n" "}\n"); // Output to clipboard char *Text = Buf.NewStr(); if (Text) { LClipBoard Clip(Ui); Clip.Text(Text); DeleteArray(Text); } } break; } case IDM_EXPORT: { auto Select = new LFileSelect(AppWindow); Select->Type("Text", "*.txt"); Select->Save([&](auto dlg, auto status) { if (status) { LFile F; if (F.Open(Select->Name(), O_WRITE)) { F.SetSize(0); // Serialize(F, true); } else { LgiMsg(AppWindow, "Couldn't open file for writing."); } } delete dlg; }); break; } case IDM_EXPORT_WIN32: { break; } } } int ResDialog::OnCommand(int Cmd, int Event, OsView hWnd) { switch (Cmd) { /* case IDM_SET_LANG: { Symbols->SetCurrent(); OnSelect(Selection.First()); Invalidate(); OnLanguageChange(); break; } */ case IDM_TAB_ORDER: { ResDialogCtrl *Top = 0; if (Selection.Length() == 1 && Selection[0]->IsContainer()) { Top = Selection[0]; } if (!Top) { Top = dynamic_cast(Children[0]); } if (Top) { auto Dlg = new TabOrder(this, Top); Dlg->DoModal([](auto dlg, auto id) { delete dlg; }); } break; } } return 0; } ResString *ResDialog::CreateSymbol() { return (Symbols) ? Symbols->CreateStr() : 0; } //////////////////////////////////////////////////////////////////// ResDialogUi::ResDialogUi(ResDialog *Res) { Dialog = Res; Tools = 0; Status = 0; StatusInfo = 0; Name("ResDialogUi"); if (Res) { Res->OnSelect(Res->Selection[0]); ShortCutView *scv = Res->App()->GetShortCutView(); if (scv) scv->OnDialogChange(Res); } } ResDialogUi::~ResDialogUi() { if (Dialog) { ShortCutView *scv = Dialog->App()->GetShortCutView(); if (scv) scv->OnDialogChange(NULL); Dialog->Ui = 0; } } void ResDialogUi::OnPaint(LSurface *pDC) { LRegion Client(0, 0, X()-1, Y()-1); for (auto w: Children) { LRect r = w->GetPos(); Client.Subtract(&r); } pDC->Colour(L_MED); for (LRect *r = Client.First(); r; r = Client.Next()) { pDC->Rectangle(r); } } void ResDialogUi::PourAll() { LRegion Client(GetClient()); LRegion Update; for (auto v: Children) { LRect OldPos = v->GetPos(); Update.Union(&OldPos); if (v->Pour(Client)) { if (!v->Visible()) { v->Visible(true); } if (OldPos != v->GetPos()) { // position has changed update... v->Invalidate(); } Client.Subtract(&v->GetPos()); Update.Subtract(&v->GetPos()); } else { // make the view not visible v->Visible(false); } } for (int i=0; iSetBitmap(FileName, 16, 16)) { Tools->Attach(this); Tools->AppendButton("Cursor", 0, TBT_RADIO); for (LgiObjectName *o=NameMap; o->Type; o++) { if (o->ToolbarBtn) { Tools->AppendButton(o->ObjectName, o->Type, TBT_RADIO); } } Tools->AppendSeparator(); // Tools->AppendButton("Change language", IDM_SET_LANG, TBT_PUSH); Tools->AppendButton("Tab Order", IDM_TAB_ORDER, TBT_PUSH, true, 17); } else { DeleteObj(Tools); } } Status = new LStatusBar; if (Status) { Status->Attach(this); StatusInfo = Status->AppendPane("", 2); } ResFrame *Frame = new ResFrame(Dialog); if (Frame) { Frame->Attach(this); } PourAll(); } int ResDialogUi::CurrentTool() { if (Tools) { auto It = Tools->IterateViews(); for (size_t i=0; i(It[i]); if (But && But->Value()) return But->GetId(); } } return -1; } void ResDialogUi::SelectTool(int i) { if (Tools) { auto It = Tools->IterateViews(); LViewI *w = It[i]; if (w) { LToolButton *But = dynamic_cast(w); if (But) But->Value(true); } } } LMessage::Result ResDialogUi::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_COMMAND: { Dialog->OnCommand(Msg->A()&0xffff, (int)(Msg->A()>>16), (OsView) Msg->B()); break; } case M_DESCRIBE: { char *Text = (char*) Msg->B(); if (Text) { StatusInfo->Name(Text); } break; } } return LView::OnEvent(Msg); } diff --git a/ResourceEditor/Code/LgiRes_Dialog.h b/ResourceEditor/Code/LgiRes_Dialog.h --- a/ResourceEditor/Code/LgiRes_Dialog.h +++ b/ResourceEditor/Code/LgiRes_Dialog.h @@ -1,522 +1,522 @@ /* ** FILE: LgiRes_Dialog.h ** AUTHOR: Matthew Allen ** DATE: 5/8/1999 ** DESCRIPTION: Dialog Resource Editor ** ** ** Copyright (C) 1999, Matthew Allen ** fret@memecode.com */ #ifndef __LGIRES_DIALOG_H #define __LGIRES_DIALOG_H #include "lgi/common/Res.h" #include "LgiRes_String.h" class ResDialog; class ResDialogUi; #define UI_CURSOR 0 #define UI_TABLE 1 #define UI_TEXT 2 #define UI_EDITBOX 3 #define UI_CHECKBOX 4 #define UI_BUTTON 5 #define UI_GROUP 6 #define UI_RADIO 7 #define UI_TABS 8 #define UI_LIST 9 #define UI_COMBO 10 #define UI_TREE 11 #define UI_BITMAP 12 #define UI_PROGRESS 13 #define UI_SCROLL_BAR 14 #define UI_CUSTOM 15 #define UI_TAB 16 #define UI_COLUMN 17 #define UI_CONTROL_TREE 18 #define UI_DIALOG 100 -#define GOOBER_SIZE 5 -#define GOOBER_BORDER 7 +extern int GOOBER_SIZE; +extern int GOOBER_BORDER; #define GRID_X 7 #define GRID_Y 7 //////////////////////////////////////////////////////////////// #define DECL_DIALOG_CTRL(id) \ int GetType() { return id; } \ LView *View() { return this; } \ void OnMouseClick(LMouse &m); \ void OnMouseMove(LMouse &m); \ void OnPaint(LSurface *pDC); #define IMPL_DIALOG_CTRL(cls) \ void cls::OnMouseClick(LMouse &m) { ResDialogCtrl::OnMouseClick(m); } \ void cls::OnMouseMove(LMouse &m) { ResDialogCtrl::OnMouseMove(m); } enum DlgSelectMode { SelNone, SelSet, SelAdd, }; class ResDialogCtrl : public FieldSource, public ResObject { friend class ResDialog; ResString *_Str = NULL; protected: static int TabDepth; void TabString(char *Str); void ReadPos(char *Str); char *GetRefText(); LRect Goobers[8]; int OverGoober = -1; ResDialog *Dlg; LRect Title; LRect Client; int DragCtrl = -1; LRect DragRgn; LPoint DragStart; bool MoveCtrl = false; DlgSelectMode SelectMode = SelNone; LRect SelectStart; bool AcceptChildren = false; bool Movable = true; bool Vis = true; LAutoString CssClass; LAutoString CssStyle; LMouse MapToDialog(LMouse m); public: ResDialogCtrl(ResDialog *dlg, char *CtrlTypeName, LXmlTag *load); ~ResDialogCtrl(); const char *GetClass() { return "ResDialogCtrl"; } virtual LView *View() = 0; ResDialogCtrl *ParentCtrl() { return dynamic_cast(View()->GetParent()); } ResDialog *GetDlg() { return Dlg; } ResString *GetStr() { return _Str; } bool SetStr(ResString *s); bool IsContainer() { return AcceptChildren; } void OnPaint(LSurface *pDC); void OnMouseClick(LMouse &m); void OnMouseMove(LMouse &m); bool SetPos(LRect &p, bool Repaint = false); void StrFromRef(int Id); LRect AbsPos(); bool GetFields(FieldTree &Fields); // Copy/Paste translations only void CopyText(); void PasteText(); virtual int GetType() = 0; virtual LRect GetMinSize(); virtual bool Serialize(FieldTree &Fields); virtual void ListChildren(List &l, bool Deep = true); virtual bool AttachCtrl(ResDialogCtrl *Ctrl, LRect *r = 0); virtual LRect *GetChildArea(ResDialogCtrl *Ctrl) { return 0; } virtual LRect *GetPasteArea() { return 0; } virtual void EnumCtrls(List &Ctrls); virtual void ShowMe(ResDialogCtrl *Child) {} }; #define RESIZE_X1 0x0001 #define RESIZE_Y1 0x0002 #define RESIZE_X2 0x0004 #define RESIZE_Y2 0x0008 class ResDialog : public Resource, public LLayout, public ResFactory { friend class ResDialogCtrl; friend class ResDialogUi; friend class CtrlDlg; friend class CtrlTabs; friend class CtNode; protected: static int SymbolRefs; static ResStringGroup *Symbols; static bool CreateSymbols; ResDialogUi *Ui; List Selection; LRect DlgPos; int DragGoober; int *DragX, *DragY; int DragOx, DragOy; LRect DragRgn; ResDialogCtrl *DragCtrl; void DrawSelection(LSurface *pDC); public: LAutoPtr DebugOverlay; ResDialog(AppWnd *w, int type = TYPE_DIALOG); ~ResDialog(); void Create(LXmlTag *load, SerialiseContext *Ctx) override; const char *GetClass() override { return "ResDialog"; } LView *Wnd() override { return dynamic_cast(this); } static void AddLanguage(LLanguageId Id); ResDialog *IsDialog() override { return this; } void EnumCtrls(List &Ctrls); void OnChildrenChanged(LViewI *Wnd, bool Attaching) override; // GObj overrides const char *Name() override; bool Name(const char *n) override; // Factory char *StringFromRef(int Ref) override; ResObject *CreateObject(LXmlTag *Tag, ResObject *Parent) override; int Res_GetStrRef(ResObject *Obj) override; bool Res_SetStrRef(ResObject *Obj, int Id, ResReadCtx *Ctx) override; void Res_SetPos(ResObject *Obj, int x1, int y1, int x2, int y2) override; void Res_SetPos(ResObject *Obj, char *s) override; LRect Res_GetPos(ResObject *Obj) override; void Res_Attach(ResObject *Obj, ResObject *Parent) override; bool Res_GetChildren(ResObject *Obj, List *l, bool Deep) override; void Res_Append(ResObject *Obj, ResObject *Parent) override; bool Res_GetItems(ResObject *Obj, List *l) override; bool Res_GetProperties(ResObject *Obj, LDom *Props) override; bool Res_SetProperties(ResObject *Obj, LDom *Props) override; LDom *Res_GetDom(ResObject *Obj) override; // Implementation int CurrentTool(); ResDialogCtrl *CreateCtrl(LXmlTag *Tag); ResDialogCtrl *CreateCtrl(int Tool, LXmlTag *load); bool IsSelected(ResDialogCtrl *Ctrl); bool IsDraging(); void SnapPoint(LPoint *p, ResDialogCtrl *From); void SnapRect(LRect *r, ResDialogCtrl *From); void MoveSelection(int Dx, int Dy); void SelectRect(ResDialogCtrl *Parent, LRect *r, bool ClearPrev = true); void SelectNone(); void SelectCtrl(ResDialogCtrl *c); void CleanSymbols(); ResString *CreateSymbol(); void OnShowLanguages() override; // Copy/Paste whole controls void Delete() override; void Copy(bool Delete = false) override; void Paste() override; // Methods LView *CreateUI() override; void OnMouseClick(LMouse &m) override; void OnMouseMove(LMouse &m) override; bool OnKey(LKey &k) override; void OnSelect(ResDialogCtrl *Wnd, bool ClearPrev = true); void OnDeselect(ResDialogCtrl *Wnd); void OnRightClick(LSubMenu *RClick) override; void OnCommand(int Cmd) override; int OnCommand(int Cmd, int Event, OsView hWnd) override; void OnLanguageChange(); void _Paint(LSurface *pDC = NULL, LPoint *Offset = NULL, LRect *Update = NULL) override; void OnPaint(LSurface *pDC) override; bool Test(ErrorCollection *e) override; bool Read(LXmlTag *Tag, SerialiseContext &Ctx) override; bool Write(LXmlTag *Tag, SerialiseContext &Ctx) override; }; class ResDialogUi : public LLayout { friend class ResDialog; LToolBar *Tools; ResDialog *Dialog; LStatusBar *Status; LStatusPane *StatusInfo; public: ResDialogUi(ResDialog *Res); ~ResDialogUi(); const char *GetClass() { return "ResDialogUi"; } void OnPaint(LSurface *pDC); void PourAll(); void OnPosChange(); void OnCreate(); LMessage::Result OnEvent(LMessage *Msg); int CurrentTool(); void SelectTool(int i); }; /////////////////////////////////////////////////////////////////////// class CtrlDlg : public ResDialogCtrl, public LView { public: CtrlDlg(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_DIALOG) const char *GetClass() { return "CtrlDlg"; } LRect &GetClient(bool InClientSpace = true); // void _Paint(LSurface *pDC = NULL, LPoint *Offset = NULL, LRect *Update = NULL); void OnNcPaint(LSurface *pDC, LRect &r); }; class CtrlTable : public ResDialogCtrl, public LDom, public LView { class CtrlTablePrivate *d; public: CtrlTable(ResDialog *dlg, LXmlTag *load); ~CtrlTable(); DECL_DIALOG_CTRL(UI_TABLE) const char *GetClass() { return "CtrlTable"; } bool GetFields(FieldTree &Fields); bool Serialize(FieldTree &Fields); void SetAttachCell(class ResTableCell *c); bool AttachCtrl(ResDialogCtrl *Ctrl, LRect *r = 0); void OnChildrenChanged(LViewI *Wnd, bool Attaching); LRect *GetPasteArea(); LRect *GetChildArea(ResDialogCtrl *Ctrl); void Layout(); void UnMerge(class ResTableCell *Cell); void Fix(); void InsertCol(int x); void InsertRow(int y); void EnumCtrls(List &Ctrls); void OnPosChange(); bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); }; class CtrlText : public ResDialogCtrl, public LView { public: CtrlText(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_TEXT) const char *GetClass() { return "CtrlText"; } }; class CtrlEditbox : public ResDialogCtrl, public LView { bool Password; bool MultiLine; public: CtrlEditbox(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_EDITBOX) const char *GetClass() { return "CtrlEditbox"; } bool GetFields(FieldTree &Fields); bool Serialize(FieldTree &Fields); }; class CtrlCheckbox : public ResDialogCtrl, public LView { public: CtrlCheckbox(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_CHECKBOX) const char *GetClass() { return "CtrlCheckbox"; } }; class CtrlButton : public ResDialogCtrl, public LView { LString Image; bool IsToggle; public: CtrlButton(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_BUTTON) const char *GetClass() { return "CtrlButton"; } bool GetFields(FieldTree &Fields); bool Serialize(FieldTree &Fields); }; class CtrlGroup : public ResDialogCtrl, public LView { public: CtrlGroup(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_GROUP) const char *GetClass() { return "CtrlGroup"; } }; class CtrlRadio : public ResDialogCtrl, public LView { LSurface *Bmp; public: CtrlRadio(ResDialog *dlg, LXmlTag *load); ~CtrlRadio(); DECL_DIALOG_CTRL(UI_RADIO) const char *GetClass() { return "CtrlRadio"; } }; class CtrlTab : public ResDialogCtrl, public LView { public: CtrlTab(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_TAB) const char *GetClass() { return "CtrlTab"; } void ListChildren(List &l, bool Deep); }; class CtrlTabs : public ResDialogCtrl, public LView { friend class CtrlTab; ssize_t Current; public: List Tabs; CtrlTabs(ResDialog *dlg, LXmlTag *load); ~CtrlTabs(); DECL_DIALOG_CTRL(UI_TABS) const char *GetClass() { return "CtrlTabs"; } void ListChildren(List &l, bool Deep); void Empty(); void ToTab(); void FromTab(); LRect GetMinSize(); void EnumCtrls(List &Ctrls); void ShowMe(ResDialogCtrl *Child); }; class ListCol : public ResDialogCtrl, public LView { public: LRect &r() { return GetPos(); } DECL_DIALOG_CTRL(UI_COLUMN) const char *GetClass() { return "ListCol"; } ListCol(ResDialog *dlg, LXmlTag *load, char *Str = 0, int Width = 50); }; class CtrlList : public ResDialogCtrl, public LView { ssize_t DragCol; public: List Cols; CtrlList(ResDialog *dlg, LXmlTag *load); ~CtrlList(); DECL_DIALOG_CTRL(UI_LIST) const char *GetClass() { return "CtrlList"; } void ListChildren(List &l, bool Deep); void Empty(); }; class CtrlComboBox : public ResDialogCtrl, public LView { public: CtrlComboBox(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_COMBO) const char *GetClass() { return "CtrlComboBox"; } }; class CtrlScrollBar : public ResDialogCtrl, public LView { public: CtrlScrollBar(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_SCROLL_BAR) const char *GetClass() { return "CtrlScrollBar"; } }; class CtrlTree : public ResDialogCtrl, public LView { public: CtrlTree(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_TREE) const char *GetClass() { return "CtrlTree"; } }; class CtrlBitmap : public ResDialogCtrl, public LView { public: CtrlBitmap(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_BITMAP) const char *GetClass() { return "CtrlBitmap"; } }; class CtrlProgress : public ResDialogCtrl, public LView { public: CtrlProgress(ResDialog *dlg, LXmlTag *load); DECL_DIALOG_CTRL(UI_PROGRESS) const char *GetClass() { return "CtrlProgress"; } }; class CtrlCustom : public ResDialogCtrl, public LView { char *Control; public: CtrlCustom(ResDialog *dlg, LXmlTag *load); ~CtrlCustom(); DECL_DIALOG_CTRL(UI_CUSTOM) const char *GetClass() { return "CtrlCustom"; } bool GetFields(FieldTree &Fields); bool Serialize(FieldTree &Fields); }; class CtrlControlTree : public ResDialogCtrl, public LTree, public LDom { class CtrlControlTreePriv *d; public: CtrlControlTree(ResDialog *dlg, LXmlTag *load); ~CtrlControlTree(); DECL_DIALOG_CTRL(UI_CONTROL_TREE) const char *GetClass() { return "CtrlControlTree"; } bool GetFields(FieldTree &Fields); bool Serialize(FieldTree &Fields); bool GetVariant(const char *Name, LVariant &Value, const char *Array = 0); bool SetVariant(const char *Name, LVariant &Value, const char *Array = 0); }; //////////////////////////////////////////////////////////////// #endif diff --git a/ResourceEditor/Code/LgiRes_TableLayout.cpp b/ResourceEditor/Code/LgiRes_TableLayout.cpp --- a/ResourceEditor/Code/LgiRes_TableLayout.cpp +++ b/ResourceEditor/Code/LgiRes_TableLayout.cpp @@ -1,1737 +1,1738 @@ #include #include "LgiResEdit.h" #include "LgiRes_Dialog.h" #include "lgi/common/Button.h" #include "lgi/common/Variant.h" #include "lgi/common/Token.h" #include "lgi/common/TableLayout.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Menu.h" #define DRAW_CELL_INDEX 0 #define DRAW_TABLE_SIZE 0 enum Cmds { IDM_ALIGN_X_MIN = 100, IDM_ALIGN_X_CTR, IDM_ALIGN_X_MAX, IDM_ALIGN_Y_MIN, IDM_ALIGN_Y_CTR, IDM_ALIGN_Y_MAX, IDM_UNMERGE, IDM_FIX_TABLE, IDM_INSERT_ROW, IDM_INSERT_COL, }; static LColour Blue(0, 30, 222); ///////////////////////////////////////////////////////////////////// struct Pair { int Pos, Size; }; void CalcCell(LArray &p, LArray &s, int Total) { int CurI = 0; for (int i=0; i T SumElements(LArray &a, ssize_t start, ssize_t end) { T Sum = 0; for (auto i=start; i<=end; i++) { Sum += a[i]; } return Sum; } void MakeSumUnity(LArray &a) { double s = SumElements(a, 0, a.Length()-1); double Diff = s - 1.0; if (Diff < 0) Diff *= -1.0; if (Diff > 0.001) { for (int i=0; i Ctrls; TableAlign AlignX; TableAlign AlignY; LAutoString Class; // CSS class for styling LAutoString Style; // CSS styles ResTableCell(CtrlTable *table, ssize_t cx, ssize_t cy) { AlignX = AlignY = AlignMin; Table = table; Selected = false; Cell.ZOff(0, 0); Cell.Offset((int)cx, (int)cy); Pos.ZOff(-1, -1); } ResTableCell() { Ctrls.DeleteObjects(); } void MoveCtrls() { for (int i=0; iView()->GetPos(); if (r.X() > Pos.X()) { r.x2 = r.x1 + Pos.X() - 1; } if (r.Y() > Pos.Y()) { r.y2 = r.y1 + Pos.Y() - 1; } if (r.x2 > Pos.x2) { r.Offset(Pos.x2 - r.x2, 0); } if (r.y2 > Pos.y2) { r.Offset(0, Pos.y2 - r.y2); } c->SetPos(r); } } } void SetPos(LRect &p) { int Dx = p.x1 - Pos.x1; int Dy = p.y1 - Pos.y1; for (int i=0; iView()->GetPos(); r.Offset(Dx, Dy); c->SetPos(r); } } Pos = p; MoveCtrls(); } bool GetVariant(const char *Name, LVariant &Value, const char *Array) { if (stricmp(Name, VAL_Span) == 0) { Value = Cell.Describe(); } else if (stricmp(Name, VAL_Children) == 0) { List c; for (int i=0; iType == GV_VOID_PTR); ResDialogCtrl *Obj = dynamic_cast((ResObject*) v->Value.Ptr); if (Obj) { Table->SetAttachCell(this); LRect r = Obj->View()->GetPos(); Table->AttachCtrl(Obj, &r); Table->SetAttachCell(0); } } } else if (stricmp(Name, VAL_HorizontalAlign) == 0) { if (Value.Str()) { for (int i=0; iAppendSub("Horizontal Align"); if (s) { s->AppendItem("Left", IDM_ALIGN_X_MIN, AlignX != AlignMin); s->AppendItem("Center", IDM_ALIGN_X_CTR, AlignX != AlignCenter); s->AppendItem("Right", IDM_ALIGN_X_MAX, AlignX != AlignMax); } s = RClick->AppendSub("Vertical Align"); if (s) { s->AppendItem("Top", IDM_ALIGN_Y_MIN, AlignY != AlignMin); s->AppendItem("Center", IDM_ALIGN_Y_CTR, AlignY != AlignCenter); s->AppendItem("Bottom", IDM_ALIGN_Y_MAX, AlignY != AlignMax); } RClick->AppendItem("Unmerge", IDM_UNMERGE, Cell.X() > 1 || Cell.Y() > 1); RClick->AppendItem("Fix Missing Cells", IDM_FIX_TABLE, true); RClick->AppendItem("Insert Row", IDM_INSERT_ROW, true); RClick->AppendItem("Insert Column", IDM_INSERT_COL, true); m.ToScreen(); switch (RClick->Float(Table, m.x, m.y)) { case IDM_ALIGN_X_MIN: AlignX = AlignMin; break; case IDM_ALIGN_X_CTR: AlignX = AlignCenter; break; case IDM_ALIGN_X_MAX: AlignX = AlignMax; break; case IDM_ALIGN_Y_MIN: AlignY = AlignMin; break; case IDM_ALIGN_Y_CTR: AlignY = AlignCenter; break; case IDM_ALIGN_Y_MAX: AlignY = AlignMax; break; case IDM_UNMERGE: Table->UnMerge(this); break; case IDM_FIX_TABLE: Table->Fix(); break; case IDM_INSERT_ROW: Table->InsertRow(Cell.y1); break; case IDM_INSERT_COL: Table->InsertCol(Cell.x1); break; } DeleteObj(RClick); } } } }; enum HandleTypes { LAddCol, LAddRow, LDeleteCol, LDeleteRow, LSizeCol, LSizeRow, LJoinCells, }; struct OpHandle : public LRect { bool Over; HandleTypes Type; int Index; ResTableCell *a, *b; OpHandle &Set(HandleTypes type, int x = -1, int y = -1) { Type = type; Over = false; Index = -1; a = b = NULL; if (x > 0 && y > 0) ZOff(x, y); return *this; } void OnPaint(LSurface *pDC) { #define OFF 1 int cx = X() >> 1; int cy = Y() >> 1; pDC->Colour(Over ? LColour::Red : Blue); pDC->Rectangle(this); pDC->Colour(LColour::White); switch (Type) { case LSizeRow: { pDC->Line(x1 + cx, y1 + OFF, x1 + cx, y2 - OFF); pDC->Line(x1 + cx - 1, y1 + OFF + 1, x1 + cx + 1, y1 + OFF + 1); pDC->Line(x1 + cx - 1, y2 - OFF - 1, x1 + cx + 1, y2 - OFF - 1); break; } case LSizeCol: { pDC->Line(x1 + OFF, y1 + cy, x2 - OFF, y1 + cy); pDC->Line(x1 + OFF + 1, y1 + cy - 1, x1 + OFF + 1, y1 + cy + 1); pDC->Line(x2 - OFF - 1, y1 + cy - 1, x2 - OFF - 1, y1 + cy + 1); break; } case LAddCol: case LAddRow: case LJoinCells: { pDC->Line(x1 + cx, y1 + OFF, x1 + cx, y2 - OFF); pDC->Line(x1 + OFF, y1 + cy, x2 - OFF, y1 + cy); break; } case LDeleteCol: case LDeleteRow: { pDC->Line(x1 + OFF, y1 + cy, x2 - OFF, y1 + cy); break; } default: LAssert(!"Impl me."); } } }; class CtrlTablePrivate { public: bool InLayout, LayoutDirty; // The cell container CtrlTable *Table; ssize_t CellX, CellY; LArray Cells; ResTableCell *AttachTo; // Column + Row sizes LArray ColSize; LArray RowSize; // Goobers for editing the cell layout LArray Handles; int DragRowSize; int DragColSize; // Methods CtrlTablePrivate(CtrlTable *t) { InLayout = false; LayoutDirty = false; Table = t; CellX = CellY = 2; AttachTo = 0; Cells[0] = new ResTableCell(t, 0, 0); Cells[1] = new ResTableCell(t, 1, 0); Cells[2] = new ResTableCell(t, 0, 1); Cells[3] = new ResTableCell(t, 1, 1); ColSize[0] = 0.5; ColSize[1] = 0.5; RowSize[0] = 0.5; RowSize[1] = 0.5; DragRowSize = DragColSize = -1; } ~CtrlTablePrivate() { Cells.DeleteObjects(); } bool GetSelected(LArray &s) { for (int i=0; iSelected) s.Add(Cells[i]); } return s.Length(); } ResTableCell *GetCellAt(int cx, int cy) { for (int i=0; iCell.Overlap(cx, cy)) { return Cells[i]; } } return 0; } void Layout(LRect c) { - #define ADD_BORDER 10 + auto scale = Table->GetWindow()->GetDpiScale(); + int ADD_BORDER = (int) (10.0 * scale.x + 0.5); #define CELL_SPACING 1 if (InLayout) return; InLayout = true; LayoutDirty = false; Handles.Length(0); int x, y; int BtnSize = ADD_BORDER * 2 / 3; auto &AddX = Handles.New().Set(LAddCol, BtnSize, BtnSize); auto &AddY = Handles.New().Set(LAddRow, BtnSize, BtnSize); int BtnX = c.X() - AddX.X() - 2; int BtnY = c.Y() - AddY.Y() - 2; AddX.Offset(BtnX, 0); AddY.Offset(0, BtnY); c.x2 = AddX.x2 - ADD_BORDER; c.y2 = AddY.y2 - ADD_BORDER; if (c.Valid()) { int AvailX = c.X(); int AvailY = c.Y(); LArray XPair, YPair; CalcCell(XPair, ColSize, AvailX); CalcCell(YPair, RowSize, AvailY); XPair[CellX].Pos = AvailX; for (x=0; xCell.x1 == x && Cell->Cell.y1 == y) { LRect Pos = Cell->Pos; // Set cells pixel position, with spanning int Ex = XPair[Cell->Cell.x2 + 1].Pos; int Ey = YPair[Cell->Cell.y2 + 1].Pos; Pos.ZOff(Ex - Px - 1 - CELL_SPACING, Ey - Py - 1 - CELL_SPACING); // Offset the cell into place Pos.Offset(Px, Py); Cell->SetPos(Pos); if (Cell->Selected) { // Add cell joining goobers... ResTableCell *c = GetCellAt(x + Cell->Cell.X(), y); if (c && c->Selected) { auto &j = Handles.New().Set(LJoinCells, BtnSize, BtnSize); j.a = Cell; j.b = c; j.Offset(Cell->Pos.x2 - (j.X()>>1), Cell->Pos.y1 + ((Cell->Pos.Y()-j.Y()) >> 1)); int asd=0; } c = GetCellAt(x, y + Cell->Cell.Y()); // LgiTrace("%s %i,%i+%i = %p\n", Cell->Cell.GetStr(), x, y, Cell->Cell.Y(), c); if (c && c->Selected) { auto &j = Handles.New().Set(LJoinCells, BtnSize, BtnSize); j.a = Cell; j.b = c; j.Offset(Cell->Pos.x1 + ((Cell->Pos.X()-j.X()) >> 1), Cell->Pos.y2 - (j.Y()>>1)); int asd=0; } } } LAssert(Cell->Cell.X()); x += Cell->Cell.X(); Px += Cell->Pos.X() + CELL_SPACING; } else break; } } } InLayout = false; } bool DeleteCol(int x) { bool Status = false; int OldCellX = CellX; // Delete column 'x' for (int y=0; yCell.y2 + 1; if (c->Cell.X() == 1) { Cells.Delete(c); DeleteObj(c); } else { c->Cell.x2--; } Status = true; } else break; } CellX--; double Last = ColSize[x]; ColSize.DeleteAt(x, true); for (int i=0; iCell.x1 == x && c->Cell.y1 == y) { c->Cell.Offset(-1, 0); } } } } Table->Layout(); return Status; } bool DeleteRow(int y) { bool Status = false; int x; int OldCellY = CellY; // Delete row 'y' for (x=0; xCell.x2 + 1; if (c->Cell.Y() == 1) { Cells.Delete(c); DeleteObj(c); } else { c->Cell.y2--; } Status = true; } else break; } CellY--; double Last = RowSize[y]; RowSize.DeleteAt(y, true); for (int i=0; iCell.x1 == x && c->Cell.y1 == y) { c->Cell.Offset(0, -1); } } } } Table->Layout(); return Status; } ResTableCell *GetCell(ResDialogCtrl *Ctrl) { for (int i=0; iCtrls.HasItem(Ctrl)) { return Cells[i]; } } return 0; } }; CtrlTable::CtrlTable(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Table, load) { d = new CtrlTablePrivate(this); AcceptChildren = true; } CtrlTable::~CtrlTable() { DeleteObj(d); } bool CtrlTable::GetFields(FieldTree &Fields) { bool Status = ResDialogCtrl::GetFields(Fields); LArray s; if (d->GetSelected(s) == 1) { int Id = 150; Fields.Insert(this, DATA_STR, Id++, VAL_CellClass, "Cell Class"); Fields.Insert(this, DATA_STR, Id++, VAL_CellStyle, "Cell Style", -1, true); } return Status; } bool CtrlTable::Serialize(FieldTree &Fields) { bool Status = ResDialogCtrl::Serialize(Fields); LArray s; ResTableCell *c; if (d->GetSelected(s) == 1 && ((c = s[0])) != NULL) { Fields.Serialize(this, VAL_CellClass, c->Class); Fields.Serialize(this, VAL_CellStyle, c->Style); } return Status; } void CtrlTable::EnumCtrls(List &Ctrls) { // LgiTrace("Tbl Ref=%i\n", Str->GetRef()); for (int i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; for (int n=0; nCtrls.Length(); n++) { // LgiTrace(" Ref=%i\n", c->Ctrls[n]->Str->GetRef()); c->Ctrls[n]->EnumCtrls(Ctrls); } } } bool CtrlTable::GetVariant(const char *Name, LVariant &Value, const char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case TableLayoutCols: { LStringPipe p; for (int i=0; iColSize.Length(); i++) { if (i) p.Push(","); p.Print("%.3f", d->ColSize[i]); } Value.OwnStr(p.NewStr()); break; } case TableLayoutRows: { LStringPipe p; for (int i=0; iRowSize.Length(); i++) { if (i) p.Push(","); p.Print("%.3f", d->RowSize[i]); } Value.OwnStr(p.NewStr()); break; } case TableLayoutCell: { auto Coords = LString(Array).SplitDelimit(","); if (Coords.Length() != 2) return false; Value = d->GetCellAt(Coords[0].Int(), Coords[1].Int()); break; } default: { LAssert(!"Invalid property."); return false; } } return true; } bool CtrlTable::SetVariant(const char *Name, LVariant &Value, const char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case TableLayoutCols: { d->Cells.DeleteObjects(); LToken t(Value.Str(), ","); d->ColSize.Length(0); for (int i=0; iColSize.Add(atof(t[i])); } MakeSumUnity(d->ColSize); d->CellX = d->ColSize.Length(); break; } case TableLayoutRows: { d->Cells.DeleteObjects(); LToken t(Value.Str(), ","); d->RowSize.Length(0); for (int i=0; iRowSize.Add(atof(t[i])); } MakeSumUnity(d->RowSize); d->CellY = d->RowSize.Length(); break; } case TableLayoutCell: { auto Coords = LString(Array).SplitDelimit(","); if (Coords.Length() != 2) return false; auto Cx = Coords[0].Int(); auto Cy = Coords[1].Int(); ResTableCell *c = new ResTableCell(this, Cx, Cy); if (!c) return false; d->Cells.Add(c); LDom **Ptr = (LDom**)Value.Value.Ptr; if (!Ptr) return false; *Ptr = c; // LgiTrace("Create cell %i,%i = %p\n", (int)Cx, (int)Cy, c); d->LayoutDirty = true; break; } case ObjStyle: { const char *s = Value.Str(); GetCss(true)->Parse(s); break; } default: { LAssert(!"Invalid property."); return false; } } return true; } LRect *CtrlTable::GetPasteArea() { for (int i=0; iCells.Length(); i++) { if (d->Cells[i]->Selected) return &d->Cells[i]->Pos; } return 0; } LRect *CtrlTable::GetChildArea(ResDialogCtrl *Ctrl) { ResTableCell *c = d->GetCell(Ctrl); if (c) return &c->Pos; return NULL; } void CtrlTable::OnChildrenChanged(LViewI *Wnd, bool Attaching) { if (Attaching) return; ResDialogCtrl *Rc = dynamic_cast(Wnd); if (Rc) { ResTableCell *c = d->GetCell(Rc); if (c) c->Ctrls.Delete(Rc); } } void CtrlTable::SetAttachCell(ResTableCell *c) { d->AttachTo = c; } bool CtrlTable::AttachCtrl(ResDialogCtrl *Ctrl, LRect *r) { if (d->AttachTo) { Layout(); if (!d->AttachTo->Ctrls.HasItem(Ctrl)) { d->AttachTo->Ctrls.Add(Ctrl); } LRect b = d->AttachTo->Pos; if (r->X() > b.X()) { r->x1 = b.x1; r->x2 = b.x2; } else if (r->x2 > b.x2) { r->Offset(b.x2 - r->x2, 0); } if (r->Y() > b.Y()) { r->y1 = b.y1; r->y2 = b.y2; } else if (r->y2 > b.y2) { r->Offset(b.y2 - r->y2, 0); } Ctrl->SetPos(*r); } else { for (int i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { if (c->Pos.Overlap(r->x1, r->y1)) { if (!c->Ctrls.HasItem(Ctrl)) { c->Ctrls.Add(Ctrl); } Ctrl->SetPos(*r); c->MoveCtrls(); *r = Ctrl->View()->GetPos(); break; } } } } return ResDialogCtrl::AttachCtrl(Ctrl, r); } void CtrlTable::Layout() { d->Layout(GetClient()); } void CtrlTable::OnPosChange() { Layout(); } void CtrlTable::OnPaint(LSurface *pDC) { int i; Client.Set(0, 0, X()-1, Y()-1); if (d->LayoutDirty || d->Handles.Length() == 0) d->Layout(GetClient()); pDC->Colour(Blue); for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { pDC->Box(&c->Pos); if (c->Selected) { int Op = pDC->Op(GDC_ALPHA); if (pDC->Applicator()) { pDC->Colour(Blue); pDC->Applicator()->SetVar(GAPP_ALPHA_A, 0x20); } else pDC->Colour(LColour(Blue.r(), Blue.g(), Blue.b(), 0x20)); pDC->Rectangle(c->Pos.x1 + 1, c->Pos.y1 + 1, c->Pos.x2 - 1, c->Pos.y2 - 1); pDC->Op(Op); pDC->Colour(Blue); } #if DRAW_CELL_INDEX LSysFont->Colour(Blue, 24); LSysFont->Transparent(true); char s[256]; sprintf(s, "%i,%i-%i,%i", c->Cell.x1, c->Cell.y1, c->Cell.X(), c->Cell.Y()); LSysFont->Text(pDC, c->Pos.x1 + 3, c->Pos.y1 + 1, s); #endif // LgiTrace("Drawing %i,%i = %p @ %s\n", c->Cell.x1, c->Cell.y1, c, c->Pos.GetStr()); } } for (auto &h: d->Handles) h.OnPaint(pDC); #if DRAW_TABLE_SIZE LSysFont->Colour(Blue, 24); LSysFont->Transparent(true); char s[256]; sprintf(s, "Cells: %i,%i", d->CellX, d->CellY); LSysFont->Text(pDC, 3, 24, s); #endif ResDialogCtrl::OnPaint(pDC); } void CtrlTable::OnMouseMove(LMouse &m) { bool Change = false; for (auto &h: d->Handles) { bool Over = h.Overlap(m.x, m.y); if (Over != h.Over) { h.Over = Over; Change = true; } } if (Change) Invalidate(); if (IsCapturing()) { LArray p; int Fudge = 6; if (d->DragRowSize >= 0) { // Adjust RowSize[d->DragRowSize] and RowSize[d->DragRowSize+1] to // center on the mouse y location int AvailY = GetClient().Y(); CalcCell(p, d->RowSize, AvailY); int BothPx = p[d->DragRowSize].Size + p[d->DragRowSize+1].Size; int PxOffset = m.y - p[d->DragRowSize].Pos + Fudge; PxOffset = limit(PxOffset, 2, BothPx - 2); double Frac = (double) PxOffset / BothPx; double Total = d->RowSize[d->DragRowSize] + d->RowSize[d->DragRowSize+1]; d->RowSize[d->DragRowSize] = Frac * Total; d->RowSize[d->DragRowSize+1] = (1.0 - Frac) * Total; // LgiTrace("Int: %i/%i, Frac: %f,%f\n", PxOffset, BothPx, d->RowSize[d->DragRowSize], d->RowSize[d->DragRowSize+1]); Layout(); Invalidate(); return; } else if (d->DragColSize >= 0) { // Adjust ColSize[d->DragColSize] and ColSize[d->DragColSize+1] to // center on the mouse x location int AvailX = GetClient().X(); CalcCell(p, d->ColSize, AvailX); int BothPx = p[d->DragColSize].Size + p[d->DragColSize+1].Size; int PxOffset = m.x - p[d->DragColSize].Pos + Fudge; PxOffset = limit(PxOffset, 2, BothPx - 2); double Frac = (double) PxOffset / BothPx; double Total = d->ColSize[d->DragColSize] + d->ColSize[d->DragColSize+1]; d->ColSize[d->DragColSize] = Frac * Total; d->ColSize[d->DragColSize+1] = (1.0 - Frac) * Total; Layout(); Invalidate(); return; } } ResDialogCtrl::OnMouseMove(m); } void CtrlTable::Fix() { // Fix missing cells for (int y=0; yCellY; y++) { for (int x=0; xCellX; ) { ResTableCell *c = d->GetCellAt(x, y); // LgiTrace("[%i][%i] = %p (%ix%i, %s)\n", x, y, c, c ? c->Cell.X() : -1, c ? c->Cell.Y() : -1, c ? c->Pos.GetStr() : NULL); if (c) { x += c->Cell.X(); } else { c = new ResTableCell(this, x, y); if (c) { d->Cells.Add(c); x++; } else break; } } } // Fix rows/cols size #define absf(i) ((i) < 0.0 ? -(i) : (i)) double Sum = 0.0; int n; for (n=0; nRowSize.Length(); n++) { Sum += d->RowSize[n]; } if (absf(Sum - 1.0) > 0.001) { for (n=0; nRowSize.Length(); n++) { double k = d->RowSize[n]; k /= Sum; d->RowSize[n] = k; } } Sum = 0.0; for (n=0; nRowSize.Length(); n++) { Sum += d->RowSize[n]; } if (absf(Sum - 1.0) > 0.001) { for (n=0; nRowSize.Length(); n++) { double k = d->RowSize[n]; k /= Sum; d->RowSize[n] = k; } } Layout(); Invalidate(); } void CtrlTable::InsertRow(int y) { // Shift existing cells down int i; for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { if (c->Cell.y1 >= y) c->Cell.Offset(0, 1); else if (c->Cell.y2 >= y) // Make spanned cells taller... c->Cell.y2++; } } // Add new row of 1x1 cells for (i=0; iCellX; i++) { d->Cells.Add(new ResTableCell(this, i, y)); } d->CellY++; // Update rows double Last = d->RowSize[d->RowSize.Length()-1]; d->RowSize.Add(Last); for (i=0; iRowSize.Length(); i++) { d->RowSize[i] = d->RowSize[i] / (1.0 + Last); } // Refresh the screen Layout(); Invalidate(); } void CtrlTable::InsertCol(int x) { auto Par = GetParent(); // Shift existing cells down int i; for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { if (c->Cell.x1 >= x) c->Cell.Offset(1, 0); else if (c->Cell.x2 >= x) // Make spanned cells wider... c->Cell.x2++; } } // Add new row of 1x1 cells for (i=0; iCellY; i++) { d->Cells.Add(new ResTableCell(this, x, i)); } d->CellX++; // Update rows double Last = d->ColSize.Last(); d->ColSize.Add(Last); for (i=0; iColSize.Length(); i++) { d->ColSize[i] = d->ColSize[i] / (1.0 + Last); } // Refresh the screen Layout(); Invalidate(); } void CtrlTable::UnMerge(ResTableCell *Cell) { if (Cell && (Cell->Cell.X() > 1 || Cell->Cell.Y() > 1)) { for (int y=Cell->Cell.x1; y<=Cell->Cell.y2; y++) { for (int x=Cell->Cell.x1; x<=Cell->Cell.x2; x++) { if (x > Cell->Cell.x1 || y > Cell->Cell.y1) { ResTableCell *n = new ResTableCell(this, x, y); if (n) { d->Cells.Add(n); } } } } Cell->Cell.x2 = Cell->Cell.x1; Cell->Cell.y2 = Cell->Cell.y1; Layout(); Invalidate(); } } void CtrlTable::OnMouseClick(LMouse &m) { if (m.Down()) { if (m.Left()) { OpHandle *h = NULL; for (auto &i: d->Handles) { if (i.Overlap(m.x, m.y)) { h = &i; break; } } if (h && h->Type == LAddCol) { for (int i=0; iCellY; i++) d->Cells.Add(new ResTableCell(this, d->CellX, i)); d->CellX++; double Last = d->ColSize[d->ColSize.Length()-1]; d->ColSize.Add(Last); for (int i=0; iColSize.Length(); i++) d->ColSize[i] = d->ColSize[i] / (1.0 + Last); Layout(); Invalidate(); return; } else if (h && h->Type == LAddRow) { for (int i=0; iCellX; i++) d->Cells.Add(new ResTableCell(this, i, d->CellY)); d->CellY++; double Total = 0; double Last = d->RowSize[d->RowSize.Length()-1]; d->RowSize.Add(Last); for (int i=0; iRowSize.Length(); i++) { d->RowSize[i] = d->RowSize[i] / (1.0 + Last); Total += d->RowSize[i]; } Layout(); Invalidate(); return; } else { bool Dirty = false; bool EatClick = true; int i; ResTableCell *Over = 0; // Look at cell joins if (h && h->Type == LJoinCells) { // Do a cell merge LRect u = h->a->Cell; u.Union(&h->b->Cell); d->Cells.Delete(h->a); for (int y=u.y1; y<=u.y2; y++) { for (int x=u.x1; x<=u.x2; x++) { ResTableCell *c = d->GetCellAt(x, y); if (c) { d->Cells.Delete(c); DeleteObj(c); } } } h->a->Cell = u; d->Cells.Add(h->a); Dirty = true; } if (!Dirty) { // Select a cell? for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c->Pos.Overlap(m.x, m.y)) { Over = c; if (!c->Selected || m.Ctrl()) { Dirty = true; EatClick = false; c->Selected = !c->Selected; } } else if (!m.Ctrl()) { if (c->Selected) { Dirty = true; EatClick = false; c->Selected = false; } } } } // Delete column goobers if (h && h->Type == LDeleteCol) Dirty = d->DeleteCol(h->Index); // Delete row goobers if (!Dirty && h && h->Type == LDeleteRow) Dirty = d->DeleteRow(h->Index); // Size row goobs if (!Dirty && h && h->Type == LSizeRow) { Dirty = true; d->DragRowSize = h->Index; Capture(true); } // Size col goobs if (!Dirty && h && h->Type == LSizeCol) { Dirty = true; d->DragColSize = h->Index; Capture(true); } if (Dirty) { Layout(); Invalidate(); if (EatClick) return; } } } else if (m.IsContextMenu()) { for (int i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c->Pos.Overlap(m.x, m.y)) { c->OnMouseClick(m); return; } } } } else { Capture(false); d->DragRowSize = -1; d->DragColSize = -1; } ResDialogCtrl::OnMouseClick(m); } //////////////////////////////////////////////////////////////////////////////////////////////// enum { M_FINISHED = M_USER + 1000, }; class TableLayoutTest : public LDialog { LTableLayout *Tbl; LView *Msg; LTree *Tree; class DlgContainer *View; LAutoPtr Worker; LAutoString Base; public: TableLayoutTest(LViewI *par); ~TableLayoutTest(); void OnDialog(LDialogRes *Dlg); int OnNotify(LViewI *Ctrl, LNotification n); LMessage::Param OnEvent(LMessage *m); }; class DlgItem : public LTreeItem { TableLayoutTest *Wnd; LDialogRes *Dlg; public: DlgItem(TableLayoutTest *w, LDialogRes *dlg) { Wnd = w; Dlg = dlg; } const char *GetText(int i) { return Dlg->Str->Str; } void OnSelect() { Wnd->OnDialog(Dlg); } }; static bool HasTableLayout(LXmlTag *t) { if (t->IsTag("TableLayout")) return true; for (auto c: t->Children) { if (HasTableLayout(c)) return true; } return false; } class Lr8Item : public LTreeItem { TableLayoutTest *Wnd; LString File; LAutoPtr Res; public: Lr8Item(TableLayoutTest *w, LAutoPtr res, char *file) { Wnd = w; Res = res; File = file; List::I d = Res->GetDialogs(); for (LDialogRes *dlg = *d; dlg; dlg = *++d) { if (dlg->Str && HasTableLayout(dlg->Dialog)) { Insert(new DlgItem(Wnd, dlg)); } } if (stristr(File, "Scribe-Branches\\v2.00")) { Expanded(true); } } const char *GetText(int i) { return File ? File.Get() : "#error"; } }; class Lr8Search : public LThread { TableLayoutTest *Wnd; char *Base; LTree *Tree; public: Lr8Search(TableLayoutTest *w, char *base, LTree *tree) : LThread("Lr8Search") { Wnd = w; Base = base; Tree = tree; Run(); } ~Lr8Search() { while (!IsExited()) { LSleep(1); } } int Main() { LArray Ext; LArray Files; Ext.Add("*.lr8"); if (LRecursiveFileSearch(Base, &Ext, &Files)) { for (int i=0; i Res; if (Res.Reset(new LResources(Files[i]))) { List::I d = Res->GetDialogs(); bool HasTl = false; for (LDialogRes *dlg = *d; dlg; dlg = *++d) { if (dlg->Str && HasTableLayout(dlg->Dialog)) { HasTl = true; break; } } if (HasTl) { auto r = LMakeRelativePath(Base, Files[i]); Tree->Insert(new Lr8Item(Wnd, Res, r)); } } } } Files.DeleteArrays(); Wnd->PostEvent(M_FINISHED); return 0; } }; class DlgContainer : public LLayout { LDialogRes *Dlg; LRect Size; public: DlgContainer(int id) { Dlg = 0; Sunken(true); SetId(id); Size.Set(0, 0, 100, 100); SetPos(Size); } void OnPaint(LSurface *pDC) { pDC->Colour(L_WORKSPACE); pDC->Rectangle(); } void OnDialog(LDialogRes *d) { while (Children.Length()) delete Children[0]; if ((Dlg = d)) { if (Dlg->GetRes()->LoadDialog(Dlg->Str->Id, this, &Size)) { LRect r = GetPos(); r.SetSize(Size.X(), Size.Y()); SetPos(r); AttachChildren(); SendNotify(LNotifyTableLayoutRefresh); } } } }; TableLayoutTest::TableLayoutTest(LViewI *par) { LRect r(0, 0, 1000, 800); SetPos(r); SetParent(par); AddView(Tbl = new LTableLayout); auto *c = Tbl->GetCell(0, 0, true, 2); c->Add(Msg = new LTextLabel(100, 0, 0, -1, -1, "Searching for files...")); c = Tbl->GetCell(0, 1); c->Add(Tree = new LTree(101, 0, 0, 100, 100)); c = Tbl->GetCell(1, 1); c->Add(View = new DlgContainer(102)); c = Tbl->GetCell(0, 2, true, 2); c->TextAlign(LCss::AlignRight); c->Add(new LButton(IDOK, 0, 0, -1, -1, "Close")); char e[MAX_PATH_LEN]; LGetSystemPath(LSP_APP_INSTALL, e, sizeof(e)); LMakePath(e, sizeof(e), e, "../../.."); Base.Reset(NewStr(e)); Worker.Reset(new Lr8Search(this, Base, Tree)); } TableLayoutTest::~TableLayoutTest() { Worker.Reset(); } void TableLayoutTest::OnDialog(LDialogRes *Dlg) { if (View) View->OnDialog(Dlg); } int TableLayoutTest::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDOK: EndModal(1); break; } return LDialog::OnNotify(Ctrl, n); } LMessage::Param TableLayoutTest::OnEvent(LMessage *m) { if (m->Msg() == M_FINISHED) { Tree->Invalidate(); Msg->Name("Finished."); Worker.Reset(); } return LDialog::OnEvent(m); } void OpenTableLayoutTest(LViewI *p) { auto Dlg = new TableLayoutTest(p); Dlg->DoModal([](auto dlg, auto id) { delete dlg; }); }