diff --git a/ResourceEditor/Code/LgiResApp.cpp b/ResourceEditor/Code/LgiResApp.cpp --- a/ResourceEditor/Code/LgiResApp.cpp +++ b/ResourceEditor/Code/LgiResApp.cpp @@ -1,4565 +1,4565 @@ /* ** 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 "GAbout.h" #include "GTextLabel.h" #include "GEdit.h" #include "GCheckBox.h" #include "GProgressDlg.h" #include "GTextView3.h" #include "resdefs.h" #include "GToken.h" #include "GDataDlg.h" #include "GButton.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 const char *TypeNames[] = { "", "Css", "Dialog", "String", "Menu", 0}; ////////////////////////////////////////////////////////////////////////////// ResFileFormat GetFormat(char *File) { ResFileFormat Format = Lr8File; char *Ext = LgiGetExtension(File); if (Ext) { if (stricmp(Ext, "lr") == 0) Format = CodepageFile; else if (stricmp(Ext, "xml") == 0) Format = XmlFile; } return Format; } char *EncodeXml(char *Str, int Len) { char *Ret = 0; if (Str) { GStringPipe p; char *s = Str; for (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(char *Str, int Len) { if (Str) { GStringPipe p; char *s = Str; for (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 { LgiAssert(0); } while (*e && *e != ';') e++; } else if (isalpha(*e)) { // named entity char *Name = e; while (*e && *e != ';') e++; int 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 LgiAssert(0); } } else { LgiAssert(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; LgiAssert(AppWindow); } Resource::~Resource() { if (Item) { Item->Obj = 0; DeleteObj(Item); } } bool Resource::IsSelected() { return Item?Item->Select():false; } bool Resource::Attach(GViewI *Parent) { GView *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); } } char *ObjTreeItem::GetText(int i) { if (Obj) { int Type = Obj->Type(); if (Type > 0) { return Obj->Wnd()->Name(); } else { return (char*)TypeNames[-Type]; } } return (char*)"#NO_OBJ"; } void ObjTreeItem::OnSelect() { if (Obj) { Obj->App()->OnResourceSelect(Obj); } } void ObjTreeItem::OnMouseClick(GMouse &m) { if (!Obj) return; if (m.IsContextMenu()) { Tree->Select(this); GSubMenu 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(); GSubMenu *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: { GInput Dlg(Tree, GetText(), "Enter the name for the object", "Object Name"); if (Dlg.DoModal()) { Obj->Wnd()->Name(Dlg.Str); Update(); Obj->App()->SetDirty(true); } break; } case IDM_IMPORT: { GFileSelect Select; Select.Parent(Obj->App()); Select.Type("Text", "*.txt"); if (Select.Open()) { GFile F; if (F.Open(Select.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."); } } 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); #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()) { GViewI *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: { LgiAssert(0); break; } } } else LgiAssert(0); } */ Ignore = false; } class TextViewEdit : public GTextView3 { public: bool Multiline; TextViewEdit( int Id, int x, int y, int cx, int cy, GFontType *FontInfo = 0) : GTextView3(Id, x, y, cx, cy, FontInfo) { Multiline = false; #ifdef WIN32 SetDlgCode(DLGC_WANTARROWS | DLGC_WANTCHARS); #endif } bool OnKey(GKey &k) { if (!Multiline && (k.c16 == '\t' || k.c16 == VK_RETURN)) { return false; } return GTextView3::OnKey(k); } }; class Hr : public GView { public: Hr(int x1, int y, int x2) { GRect r(x1, y, x2, y+1); SetPos(r); } void OnPaint(GSurface *pDC) { GRect c = GetClient(); LgiThinBorder(pDC, c, DefaultSunkenEdge); } bool OnLayout(GViewLayoutInfo &Inf) { if (Inf.Width.Min) Inf.Height.Min = Inf.Height.Max = 2; else Inf.Width.Min = Inf.Width.Max = -1; return true; } }; void FieldView::OnSelect(FieldSource *s) { Ignore = true; if (Source) { // Clear fields Source->_FieldView = 0; Fields.Empty(); // remove all children for (GViewI *c = Children.First(); c; c = Children.First()) { c->Detach(); DeleteObj(c); } Source = 0; } if (s) { // Add new fields Source = s; Source->_FieldView = Handle(); if (Source->GetFields(Fields)) { GFontType Sys; Sys.GetSystemFont("System"); GTableLayout *t = new GTableLayout(IDC_TABLE); int Row = 0; GLayoutCell *Cell; GArray 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->Add(new GTextLabel(-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(GCss::Len(GCss::LenPx, c->Multiline ? SysFont->GetHeight() * 8 : SysFont->GetHeight() + 8)); Tv->SetWrapType(TEXTED_WRAP_NONE); Tv->Sunken(true); } if (c->Type == DATA_FILENAME) { Cell = t->GetCell(2, Row); Cell->Add(new GButton(-c->Id, 0, 0, 21, 21, "...")); } break; } case DATA_BOOL: { Cell = t->GetCell(1, Row, true, 2); Cell->Add(new GCheckBox(c->Id, 0, 0, -1, -1, c->Label)); break; } default: LgiAssert(!"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(); } Serialize(false); Ignore = false; } } void FieldView::OnPosChange() { GRect c = GetClient(); c.Size(6, 6); GViewI *v; if (GetViewById(IDC_TABLE, v)) v->SetPos(c); } GMessage::Result FieldView::OnEvent(GMessage *m) { switch (MsgCode(m)) { case M_OBJECT_CHANGED: { FieldSource *Src = (FieldSource*)MsgA(m); if (Src == Source) { Fields.SetMode(FieldTree::ObjToUi); Fields.SetView(this); Serialize(false); } else LgiAssert(0); break; } } return GLayout::OnEvent(m); } int FieldView::OnNotify(GViewI *Ctrl, int Flags) { if (!Ignore) { GTextView3 *Tv = dynamic_cast(Ctrl); if (Tv && Flags == GNotifyCursorChanged) { return 0; } GArray 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()) { GFileSelect s; s.Parent(this); if (s.Open()) { char *File = App->GetCurFile(); if (File) { - GFile::Path p = File; - p--; + GFile::Path p = File; + p--; GAutoString Rel = LgiMakeRelativePath(p, s.Name()); if (Rel) SetCtrlName(c->Id, Rel); else SetCtrlName(c->Id, s.Name()); } else SetCtrlName(c->Id, s.Name()); Fields.SetMode(FieldTree::UiToObj); Fields.SetView(this); Source->Serialize(Fields); return 0; } } } } } return 0; } void FieldView::OnPaint(GSurface *pDC) { pDC->Colour(LC_MED, 24); pDC->Rectangle(); } ////////////////////////////////////////////////////////////////////////////// ObjContainer::ObjContainer(AppWnd *w) : GTree(100, 0, 0, 100, 100, "LgiResObjTree") { Window = w; Sunken(false); 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"; GAutoString f(LgiFindFile(IconFile)); if (f) { Images = LgiLoadImageList(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) { GTreeItem *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 const char *Icon = MAKEINTRESOURCE(IDI_ICON1); #else const char *Icon = "icon64.png"; #endif AppWnd::AppWnd() : GDocApp(AppName, Icon) { LastRes = 0; Fields = 0; ViewMenu = 0; ShortCuts = 0; CurLang = -1; ShowLanguages.Add("en", true); if (_Create()) { GVariant Langs; if (GetOptions()->GetValue(OPT_ShowLanguages, Langs)) { ShowLanguages.Empty(); GToken L(Langs.Str(), ","); for (int i=0; iEmpty(); _Destroy(); } void AppWnd::OnCreate() { SetupUi(); } void AppWnd::OnLanguagesChange(GLanguageId 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(GFindLang(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(GLanguageId Lang) { return ShowLanguages.Find(Lang) != 0; } void AppWnd::ShowLang(GLanguageId Lang, bool Show) { // Apply change if (Show) { OnLanguagesChange(Lang, true); ShowLanguages.Add(Lang, true); } else { ShowLanguages.Delete(Lang); } // Store the setting for next time GStringPipe p; const char *L; for (bool i = ShowLanguages.First(&L); i; i = ShowLanguages.Next(&L)) { if (p.GetSize()) p.Push(","); p.Push(L); } char *Langs = p.NewStr(); if (Langs) { GVariant v; GetOptions()->SetValue(OPT_ShowLanguages, v = Langs); DeleteArray(Langs); } // Update everything List res; if (ListObjects(res)) { for (Resource *r = res.First(); r; r = res.Next()) { r->OnShowLanguages(); } } } GLanguage *AppWnd::GetCurLang() { if (CurLang >= 0 && CurLang < Languages.Length()) return Languages[CurLang]; return GFindLang("en"); } void AppWnd::SetCurLang(GLanguage *L) { for (int i=0; iId == L->Id) { // Set new current CurLang = i; // Update everything List res; if (ListObjects(res)) { for (Resource *r = res.First(); r; r = res.Next()) { r->OnShowLanguages(); } } break; } } } GArray *AppWnd::GetLanguages() { return &Languages; } class Test : public GView { COLOUR c; public: Test(COLOUR col, int x1, int y1, int x2, int y2) { c = col; GRect r(x1, y1, x2, y2); SetPos(r); _BorderSize = 1; Sunken(true); } void OnPaint(GSurface *pDC) { pDC->Colour(c, 24); pDC->Rectangle(); } }; void AppWnd::SetupUi() { 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); LgiAssert(ViewMenu); } else printf("%s:%i - _LoadMenu failed.\n", _FL); Status = 0; StatusInfo[0] = StatusInfo[1] = 0; MainSplit = new GSplitter; if (MainSplit) { MainSplit->Attach(this); MainSplit->Value(240); SubSplit = new GSplitter; if (SubSplit) { SubSplit->Raised(false); MainSplit->SetViewA(SubSplit, false); SubSplit->Border(false); SubSplit->IsVertical(false); SubSplit->Value(200); SubSplit->SetViewA(Objs = new ObjContainer(this)); if (Objs) { Objs->AskImage(true); Objs->AskText(true); } SubSplit->SetViewB(Fields = new FieldView(this)); } } char Opt[256]; if (LgiApp->GetOption("o", Opt)) { LoadLgi(Opt); } DropTarget(true); } GMessage::Result AppWnd::OnEvent(GMessage *m) { GMru::OnEvent(m); switch (MsgCode(m)) { case M_CHANGE: { return OnNotify((GViewI*) MsgA(m), MsgB(m)); } case M_DESCRIBE: { char *Text = (char*) MsgA(m); if (Text) { SetStatusText(Text, STATUS_NORMAL); } break; } } return GWindow::OnEvent(m); } #include "GToken.h" void _CountGroup(ResStringGroup *Grp, int &Words, int &Multi) { for (ResString *s = Grp->GetStrs()->First(); s; s=Grp->GetStrs()->Next()) { if (s->Items.Length() > 1) { Multi++; char *e = s->Get("en"); if (e) { GToken t(e, " "); Words += t.Length(); } } } } int AppWnd::OnCommand(int Cmd, int Event, OsView Handle) { SerialiseContext Ctx; switch (Cmd) { case IDM_SHOW_LANG: { ShowLanguagesDlg Dlg(this); 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 (Resource *r = l.First(); r; r = l.Next()) { 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: { LgiCloseApp(); break; } case IDM_FIND: { Search s(this); if (s.DoModal()) { new Results(this, &s); } break; } case IDM_NEXT: { LgiMsg(this, "Not implemented :(", AppName); break; } case IDM_CUT: { Resource *r = Objs->CurrentResource(); if (r) r->Copy(true); break; } case IDM_COPY: { Resource *r = Objs->CurrentResource(); if (r) r->Copy(false); break; } case IDM_PASTE: { Resource *r = Objs->CurrentResource(); if (r) r->Paste(); break; } case IDM_TABLELAYOUT_TEST: { OpenTableLayoutTest(this); break; } case IDM_HELP: { char ExeName[256]; LgiGetExePath(ExeName, sizeof(ExeName)); while (strchr(ExeName, DIR_CHAR) && strlen(ExeName) > 3) { char p[256]; LgiMakePath(p, sizeof(p), ExeName, "index.html"); if (!FileExists(p)) { LgiMakePath(p, sizeof(p), ExeName, "help"); LgiMakePath(p, sizeof(p), p, "index.html"); } if (FileExists(p)) { LgiExecute(HelpFile, NULL, ExeName); break; } LgiTrimDir(ExeName); } break; } case IDM_SHOW_SHORTCUTS: { if (!ShortCuts) ShortCuts = new ShortCutView(this); break; } case IDM_ABOUT: { GAbout Dlg( this, AppName, APP_VER, "\nLgi Resource Editor.", "icon64.png", "http://www.memecode.com/lgires.php", "fret@memecode.com"); break; } default: { int Idx = Cmd - IDM_LANG_BASE; if (Idx >= 0 && Idx < Languages.Length()) { // Deselect the old lang GMenuItem *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 (Resource *r = res.First(); r; r = res.Next()) { r->OnShowLanguages(); } } } break; } } return GDocApp::OnCommand(Cmd, Event, Handle); } int AppWnd::OnNotify(GViewI *Ctrl, int Flags) { 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 (Resource *r = l.First(); r; r = l.Next()) { StringList *s = r->GetStrs(); if (s) { for (ResString *Str = s->First(); Str; Str = s->Next()) { 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)) { GHashTbl t; for (Resource *r = l.First(); r; r = l.Next()) { StringList *sl = r->GetStrs(); if (sl) { for (ResString *s = sl->First(); s; s = sl->Next()) { if (s->GetId() > 0 && !t.Find(s->GetId())) { t.Add(s->GetId(), s->GetId()); } - Max = max(s->GetId(), Max); + 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; GHashTbl Map; GArray Dupes; for (Resource *r = l.First(); r; r = l.Next()) { ResStringGroup *Grp = r->GetStringGroup(); if (Grp) { List::I it = Grp->GetStrs()->Start(); 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 { int Idx = Grp->GetStrs()->IndexOf(s); LgiAssert(!"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 (Resource *r = l.First(); r && !Str; r = l.Next()) { 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 (Resource *r = l.First(); r; r = l.Next()) { ResStringGroup *Grp = dynamic_cast(r); if (Grp) { char *ObjName = Grp->Wnd()->Name(); if (ObjName && stricmp(ObjName, StrDialogSymbols) == 0) { return Grp; } } } } } return NULL; } void AppWnd::OnReceiveFiles(GArray &Files) { char *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, GXmlTag *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) { 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(GTreeItem *ti, Resource *r) { for (GTreeItem *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(GTree *ti, Resource *r) { for (GTreeItem *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(); LgiYield(); s->Select(true); } else if (d) { LgiYield(); d->SelectCtrl(c); } else if (m) { for (GTreeItem *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::OnResourceSelect(Resource *r) { if (LastRes) { OnObjSelect(NULL); MainSplit->SetViewB(0); LastRes = 0; } if (r) { GView *Wnd = r->CreateUI(); if (Wnd) { if (SubSplit) { MainSplit->SetViewB(Wnd, false); } LgiYield(); PourAll(); Wnd->Invalidate(); LastRes = r; } } } char *TagName(GXmlTag *t) { static char Buf[1024]; GArray Tags; for (; t; t = t->Parent) { Tags.AddAt(0, t); } Buf[0] = 0; for (int i=0; iGetTag()); } return Buf; } class ResCompare : public GWindow, public GLgiRes { LList *Lst; public: ResCompare(char *File1, char *File2) { Lst = 0; GRect p; GAutoString n; if (LoadFromResource(IDD_COMPARE, this, &p, &n)) { SetPos(p); Name(n); MoveToCenter(); GetViewById(IDC_DIFFS, Lst); if (Attach(0)) { Visible(true); AttachChildren(); GXmlTag *t1 = new GXmlTag; GXmlTag *t2 = new GXmlTag; if (t1 && File1) { GFile f; if (f.Open(File1, O_READ)) { GXmlTree x(GXT_NO_ENTITIES); if (!x.Read(t1, &f, 0)) { DeleteObj(t1); } } else { DeleteObj(t1); } } if (t2 && File2) { GFile f; if (f.Open(File2, O_READ)) { GXmlTree 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(GXmlTag *t1, GXmlTag *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); } } GHashTbl a(0, false); for (int i=0; iAttr.Length(); i++) { GXmlAttr *a1 = &t1->Attr[i]; a.Add(a1->GetName(), a1); } for (int n=0; nAttr.Length(); n++) { GXmlAttr *a2 = &t2->Attr[n]; GXmlAttr *a1 = (GXmlAttr*) 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)) { GXmlAttr *a1 = (GXmlAttr *)v; 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")) { GXmlTag *t; GArray r1, r2; for (t = t1->Children.First(); t; t = t1->Children.Next()) { char *Ref; if ((Ref = t->GetAttr("ref"))) { int r = atoi(Ref); if (r) { r1[r] = t; } } } for (t = t2->Children.First(); t; t = t2->Children.Next()) { char *Ref; if ((Ref = t->GetAttr("ref"))) { int r = atoi(Ref); if (r) { r2[r] = t; } } } - int Max = max(r1.Length(), r2.Length()); + int 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 { GXmlTag *c1 = t1->Children.First(); GXmlTag *c2 = t2->Children.First(); while (c1 && c2) { Compare(c1, c2); c1 = t1->Children.Next(); c2 = t2->Children.Next(); } } LgiYield(); } void OnPosChange() { GRect c = GetClient(); if (Lst) { c.Size(7, 7); Lst->SetPos(c); } } }; void AppWnd::Compare() { GFileSelect s; s.Parent(this); s.Type("Lgi Resource", "*.lr8"); if (s.Open()) { new ResCompare(GetCurFile(), s.Name()); } } void AppWnd::ImportLang() { // open dialog GFileSelect Select; Select.Parent(this); Select.Type("Lgi Resources", "*.lr8;*.xml"); if (Select.Open()) { GFile F; if (F.Open(Select.Name(), O_READ)) { SerialiseContext Ctx; Ctx.Format = GetFormat(Select.Name()); // convert file to Xml objects GXmlTag *Root = new GXmlTag; if (Root) { GXmlTree Tree(GXT_NO_ENTITIES); if (Tree.Read(Root, &F, 0)) { List Menus; List Groups; GXmlTag *t; for (t = Root->Children.First(); t; t = Root->Children.Next()) { 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 (ResStringGroup *g=Groups.First(); g; g=Groups.Next()) { g->SetLanguages(); if (g->GetStrs()->Length() > 0 && g->GetLanguages() > 0) { HasData = true; } } if (HasData) { List Langs; for (ResStringGroup *g=Groups.First(); g; g=Groups.Next()) { for (int i=0; iGetLanguages(); i++) { GLanguage *Lang = g->GetLanguage(i); if (Lang) { bool Has = false; for (GLanguage *l=Langs.First(); l; l=Langs.Next()) { if (stricmp((char*)l, (char*)Lang) == 0) { Has = true; break; } } if (!Has) { Langs.Insert(Lang); } } } } LangDlg Dlg(this, Langs); if (Dlg.DoModal() == IDOK && Dlg.Lang) { GStringPipe Errors; int Matches = 0; int NotFound = 0; int Imported = 0; int Different = 0; for (ResStringGroup *g=Groups.First(); g; g=Groups.Next()) { List::I Strings = g->GetStrs()->Start(); 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 (ResMenu *m=Menus.First(); m; m=Menus.Next()) { // find matching menu in our list ResMenu *Match = 0; for (Resource *r=Lst.First(); r; r=Lst.Next()) { 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 (ResString *s=Src->First(); s; s = Src->Next()) { bool FoundRef = false; for (ResString *d=Dst->First(); d; d=Dst->Next()) { 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 (Resource *r=Lst.First(); r; r=Lst.Next()) { 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*)""); } } 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); } } } } void AppWnd::Empty() { // Delete any existing objects List l; if (ListObjects(l)) { Resource *r; for (r = l.First(); r; ) { if (r->SystemObject()) { l.Delete(r); r = l.Current(); } else { r=l.Next(); } } for (r = l.First(); r; r=l.Next()) { DelObject(r); } } } bool AppWnd::OpenFile(char *FileName, bool Ro) { if (stristr(FileName, ".lr8") || stristr(FileName, ".xml")) { return LoadLgi(FileName); } else if (stristr(FileName, ".rc")) { return LoadWin32(FileName); } return false; } bool AppWnd::SaveFile(char *FileName) { if (stristr(FileName, ".lr8") || stristr(FileName, ".xml")) { return SaveLgi(FileName); } else if (stristr(FileName, ".rc")) { } return false; } void AppWnd::GetFileTypes(GFileSelect *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 (Resource *r = l.First(); r; r = l.Next()) { Status &= r->Test(&Errors); } if (Errors.StrErr.Length() > 0) { GStringPipe Sample; for (int i=0; iGetRef(), s->GetDefine(), Errors.StrErr[i].Msg.Get()); } char *Sam = Sample.NewStr(); LgiMsg(this, "%i strings have errors.\n\n%s", AppName, MB_OK, Errors.StrErr.Length(), Sam); DeleteArray(Sam); } else if (!Quite) { LgiMsg(this, "Object are all ok.", AppName); } } return Status; } bool AppWnd::LoadLgi(char *FileName) { bool Status = false; Empty(); if (FileName) { ResFileFormat Format = GetFormat(FileName); GFile f; if (f.Open(FileName, O_READ)) { GProgressDlg Progress(this); Progress.SetDescription("Initializing..."); Progress.SetType("Tags"); GXmlTag *Root = new GXmlTag; if (Root) { // convert file to Xml objects GXmlTree Xml(0); Progress.SetDescription("Lexing..."); if (Xml.Read(Root, &f, 0)) { Progress.SetLimits(0, Root->Children.Length()-1); // convert Xml list into objects int i=0; DoEvery Timer(500); SerialiseContext Ctx; for (GXmlTag *t = Root->Children.First(); t; t = Root->Children.Next(), i++) { if (Timer.DoNow()) { Progress.Value(Root->Children.IndexOf(t)); LgiYield(); } 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 { LgiAssert(!"Unexpected tag"); } if (RType > 0) { NewObject(Ctx, t, RType, false); } } Ctx.PostLoad(this); SortDialogs(); TestLgi(); // Scan for languages and update the view lang menu Languages.Length(0); GHashTbl Langs; if (ViewMenu) { // Remove existing language menu items while (ViewMenu->RemoveItem(1)); ViewMenu->AppendSeparator(); // Enumerate all languages List res; if (ListObjects(res)) { for (Resource *r = res.First(); r; r = res.Next()) { ResStringGroup *Sg = r->IsStringGroup(); if (Sg) { for (int i=0; iGetLanguages(); i++) { GLanguage *Lang = Sg->GetLanguage(i); if (Lang) { Langs.Add(Lang->Id, Lang); } } } } } // Update languages array int n = 0; for (GLanguage *i = Langs.First(); i; i = Langs.Next(), n++) { Languages.Add(i); GMenuItem *Item = ViewMenu->AppendItem(i->Name, IDM_LANG_BASE + n, true); if (Item && i->IsEnglish()) { Item->Checked(true); CurLang = n; } } if (Languages.Length() == 0) { ViewMenu->AppendItem("(none)", -1, false); } } Status = true; } else { LgiMsg(this, "Xml read failed: %s", AppName, MB_OK, Xml.GetErrorMsg()); } DeleteObj(Root); } } } return Status; } void SerialiseContext::PostLoad(AppWnd *App) { for (int i=0; iGetUniqueCtrlId(); s->SetId(Id); Log.Print("Repaired CtrlId of string ref %i to %i\n", s->GetRef(), Id); } GAutoString a(Log.NewStr()); if (ValidStr(a)) { LgiMsg(App, "%s", "Load Warnings", MB_OK, a.Get()); } } int DialogNameCompare(ResDialog *a, ResDialog *b, NativeInt Data) { char *A = (a)?a->Name():0; 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 (Resource *r = Lst.First(); r; r = Lst.Next()) { ResDialog *Dlg = dynamic_cast(r); if (Dlg) { Dlgs.Insert(Dlg); Dlg->Item->Remove(); } } Dlgs.Sort(DialogNameCompare, 0); for (ResDialog *d = Dlgs.First(); d; d = Dlgs.Next()) { 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(GFile &Defs) { bool Status = false; ResTree Tree; // Empty file Defs.Seek(0, SEEK_SET); Defs.SetSize(0); Defs.Write(HeaderStr, strlen(HeaderStr)); // make a unique list of #define's List Lst; if (ListObjects(Lst)) { GHashTbl Def; GHashTbl Ident; for (Resource *r = Lst.First(); r; r = Lst.Next()) { List *StrList = r->GetStrs(); if (StrList) { Status = true; List::I sl = StrList->Start(); 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 (ResString *Ns = n.First(); Ns; Ns = n.Next()) { 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... LgiAssert(s->GetId()); if (s->GetId()) Def.Add(s->GetDefine(), s->GetId()); } } } } } } // write the list out GArray Pairs; char *s = 0; for (int i = Def.First(&s); i; i = Def.Next(&s)) { if (ValidStr(s) && stricmp(s, "IDOK") != 0 && stricmp(s, "IDCANCEL") != 0 && stricmp(s, "IDC_STATIC") != 0 && stricmp(s, "-1") != 0) { DefinePair &p = Pairs.New(); p.Name = s; p.Value = i; } } Pairs.Sort(PairCmp); for (int n=0; n=' ' && (uint8)(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(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 (FileExists(FileName)) { char Bak[MAX_PATH]; strcpy_s(Bak, sizeof(Bak), FileName); char *e = LgiGetExtension(Bak); if (e) { strcpy(e, "bak"); if (FileExists(Bak)) FileDev->Delete(Bak, false); FileDev->Move(FileName, Bak); } } // Save the file to xml if (FileName) { GFile f; GFile Defs; ResFileFormat Format = GetFormat(FileName); char DefsName[256]; strcpy(DefsName, FileName); LgiTrimDir(DefsName); strcat(DefsName, DIR_STR); strcat(DefsName, "resdefs.h"); if (f.Open(FileName, O_WRITE) && Defs.Open(DefsName, 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 Resource *r; for (r = l.First(); r; r = l.Next()) { ResDialog *Dlg = dynamic_cast(r); if (Dlg) { Dlg->CleanSymbols(); } } // write defines WriteDefines(Defs); GXmlTag 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 (r = l.First(); r; r = l.Next()) { if (r->Type() == TYPE_STRING) { GXmlTag *c = new GXmlTag; if (c && r->Write(c, Ctx)) { Root.InsertTag(c); } else { LgiAssert(0); DeleteObj(c); } } } // now write the rest of the objects out for (r = l.First(); r; r = l.Next()) { if (r->Type() != TYPE_STRING) { GXmlTag *c = new GXmlTag; if (c && r->Write(c, Ctx)) { Root.InsertTag(c); } else { r->Write(c, Ctx); LgiAssert(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 shouldv'e been. That was fixed, but // to differentiate between the 2 systems, we store a tag at the // root element. Root.SetAttr("Offset", 1); GXmlTree Tree(GXT_NO_ENTITIES); Status = Tree.Write(&Root, &f); } } else { 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); } } 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 "GToken.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 = LgiSkipDelim(Line); char *Start = Line; const char *WhiteSpace = " \r\n\t"; while (*Line && !strchr(WhiteSpace, *Line)) { Line++; } Name = NewStr(Start, Line-Start); Line = LgiSkipDelim(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 (ImportDefine *i = First(); i; i = Next()) { DeleteObj(i); } for (char *c = IncludeDirs.First(); c; c = IncludeDirs.Next()) { 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 (ImportDefine *i = First(); i; i = Next()) { if (i->Name && stricmp(i->Name, Name) == 0) { return i; } } } return NULL; } void ProcessLine(char *Line) { if (NestLevel > 16) { return; } if (Line && *Line == '#') { Line++; GToken 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++; GFile F; if (T.Length() > 1) { for (char *IncPath = IncludeDirs.First(); IncPath; IncPath = IncludeDirs.Next()) { 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 = LgiSkipDelim(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(GArray &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 = LgiTokStr(s); if (t) T.Add(t); else break; } } } bool AppWnd::LoadWin32(char *FileName) { bool Status = false; GFileSelect Select; GHashTbl 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(); if (!FileName) { Select.Parent(this); Select.Type("Win32 Resource Script", "*.rc"); if (Select.Open()) { FileName = Select.Name(); } } if (FileName) { GProgressDlg Progress(this); Progress.SetDescription("Initializing..."); Progress.SetType("K"); Progress.SetScale(1.0/1024.0); char *FileTxt = ReadTextFile(Select.Name()); if (FileTxt) { GToken Lines(FileTxt, "\r\n"); DeleteArray(FileTxt); DefineList Defines; ResStringGroup *String = new ResStringGroup(this); int Mode = IMP_MODE_SEARCH; // Language char *Language = 0; GLanguageId 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, Select.Name()); LgiTrimDir(IncPath); Defines.IncludeDirs.Insert(NewStr(IncPath)); Defines.DefineSymbol("_WIN32"); Defines.DefineSymbol("IDC_STATIC", "-1"); DoEvery Ticker(200); Progress.SetDescription("Reading resources..."); Progress.SetLimits(0, Lines.Length()-1); if (String) { InsertObject(TYPE_STRING, String, false); } for (int CurLine = 0; CurLine < Lines.Length(); CurLine++) { if (Ticker.DoNow()) { Progress.Value(CurLine); LgiYield(); } // Skip white space char *Line = Lines[CurLine]; char *p = LgiSkipDelim(Line); Defines.ProcessLine(Line); // Tokenize GArray 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]); GAutoPtr It(Dialog->IterateViews()); Dlg = dynamic_cast(It->First()); if (Dlg) { int Pos[4] = {0, 0, 0, 0}; int i = 0; for (; iResDialogCtrl::SetPos(r); Dlg->Str->SetDefine(T[0]); ImportDefine *Def = Defines.GetDefine(T[0]); if (Def) { Dlg->Str->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 (ResMenu *m = Menus.First(); m; m = Menus.Next()) { 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) { GLanguage *Info = GFindLang(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->Str->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 = LgiSkipDelim(Next); char *NextTok = LgiTokStr((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->Str->Set(T[1], LanguageId); Ctrl->Str->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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->Str->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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->Str->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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->Str->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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->Str->Set(T[1], LanguageId); Ctrl->Str->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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->Str->Set(T[1], LanguageId); Ctrl->Str->SetDefine(T[2]); ImportDefine *Def = Defines.GetDefine(T[2]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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->Str->Set(Caption, LanguageId); if (Id) Ctrl->Str->SetDefine(Id); ImportDefine *Def = Defines.GetDefine(Id); if (Def) { Ctrl->Str->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->Str->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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->Str->SetDefine(T[1]); ImportDefine *Def = Defines.GetDefine(T[1]); if (Def) { Ctrl->Str->SetId(atoi(Def->Value)); } GRect 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 (ResDialog *d = DlLList.First(); d; d = DlLList.Next()) { GAutoPtr It(d->IterateViews()); GViewI *Wnd = It->First(); if (Wnd) { CtrlDlg *Obj = dynamic_cast(Wnd); if (Obj) { if (Obj->Str->GetId() == Dlg->Str->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 (StrLang *s = Dlg->Str->Items.First(); s; s = Dlg->Str->Items.Next()) { if (!MatchObj->Str->Get(s->GetLang())) { MatchObj->Str->Set(s->GetStr(), s->GetLang()); } } for (ResDialogCtrl *c = New.First(); c; c = New.Next()) { ResDialogCtrl *MatchCtrl = 0; // try matching by Id { for (ResDialogCtrl *Mc = Old.First(); Mc; Mc = Old.Next()) { if (Mc->Str->GetId() == c->Str->GetId() && Mc->Str->GetId() > 0) { MatchCtrl = Mc; break; } } } // ok no Id match, match by location and type if (!MatchCtrl) { List Overlapping; for (ResDialogCtrl *Mc = Old.First(); Mc; Mc = Old.Next()) { GRect a = Mc->View()->GetPos(); GRect b = c->View()->GetPos(); GRect 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.First(); } } if (MatchCtrl) { // woohoo we are cool for (StrLang *s = c->Str->Items.First(); s; s = c->Str->Items.Next()) { MatchCtrl->Str->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 GLanguage *Lang = GFindLang(Language); GLanguageId SLang = (Lang) ? Lang->Id : (char*)"en"; StrLang *s = 0; // look for language present in string object for (s = Str->Items.First(); s && *s != SLang; s = Str->Items.Next()); // 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) { GTreeItem *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) { GLanguage *Lang = GFindLang(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 (GTreeItem *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 GLanguage *Lang = GFindLang(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(); } return Status; } 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(GViewI *p) { bool Status = GLayout::Attach(p); if (Status && Child) { Child->Attach(this); Child->Wnd()->Visible(true); } return Status; } bool ResFrame::Pour(GRegion &r) { GRect *Best = FindLargest(r); if (Best) { SetPos(*Best); return true; } return false; } bool ResFrame::OnKey(GKey &k) { bool Status = false; if (k.Down() && Child) { switch (k.c16) { case VK_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 VK_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(GSurface *pDC) { // Draw nice frame GRect r(0, 0, X()-1, Y()-1); LgiThinBorder(pDC, r, DefaultRaisedEdge); pDC->Colour(LC_MED, 24); LgiFlatBorder(pDC, r, 4); LgiWideBorder(pDC, r, DefaultSunkenEdge); // Set the child to the client area Child->Wnd()->SetPos(r); // Draw the dialog & controls GView::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// LgiFunc char *_LgiGenLangLookup(); #include "GAutoPtr.h" #include "GVariant.h" #include "GCss.h" #include "GTableLayout.h" class Foo : public GLayoutCell { public: Foo() { TextAlign(AlignLeft); } bool Add(GView *v) { return false; } bool Remove(GView *v) { return false; } }; ////////////////////////////////////////////////////////////////////// ShortCutView::ShortCutView(AppWnd *app) { App = app; GRect 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, GViewI *In) { GAutoPtr it(In->IterateViews()); for (GViewI *c = it->First(); c; c = it->Next()) { ResDialogCtrl *rdc = dynamic_cast(c); if (!rdc || !rdc->Str) continue; char *n = rdc->Str->Get(); if (n) { char *a = strchr(n, '&'); if (a && a[1] != '&') { LListItem *li = new LListItem; GString s(++a, 1); GString id; id.Printf("%i", rdc->Str->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(GViewI *Ctrl, int Flags) { if (Ctrl->GetId() == Lst->GetId()) { switch (Flags) { case GNotifyItem_Click: { LListItem *li = Lst->GetSelected(); if (li) { GString s = li->GetText(1); ResDialogCtrl *c = (ResDialogCtrl*) li->_UserPtr; if (c) App->GotoObject(c->Str, NULL, c->GetDlg(), NULL, c); } break; } } } return GWindow::OnNotify(Ctrl, Flags); } void ShortCutView::OnDialogChange(ResDialog *Dlg) { Lst->Empty(); if (!Dlg) return; FindShortCuts(Lst, Dlg); Lst->Sort(NULL, 0); } 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(GCss::PropBackground); */ } int LgiMain(OsAppArguments &AppArgs) { GApp 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,4311 +1,4311 @@ /* ** 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 "GButton.h" #include "GVariant.h" #include "GToken.h" #include "GDisplayString.h" #include "GClipBoard.h" #include "resdefs.h" //////////////////////////////////////////////////////////////////// #define IDC_UP 101 #define IDC_DOWN 102 // 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, "GDialog", Res_Dialog, false}, {UI_TABLE, "GTableLayout", Res_Table, true}, {UI_TEXT, "GText", Res_StaticText, true}, {UI_EDITBOX, "GEdit", Res_EditBox, true}, {UI_CHECKBOX, "GCheckBox", Res_CheckBox, true}, {UI_BUTTON, "GButton", Res_Button, true}, {UI_GROUP, "GRadioGroup", Res_Group, true}, {UI_RADIO, "GRadioButton", Res_RadioBox, true}, {UI_TABS, "GTabView", Res_TabView, true}, {UI_TAB, "GTabPage", Res_Tab, false}, {UI_LIST, "LList", Res_ListView, true}, {UI_COLUMN, "LListColumn", Res_Column, false}, {UI_COMBO, "GCombo", Res_ComboBox, true}, {UI_TREE, "GTree", Res_TreeView, true}, {UI_BITMAP, "GBitmap", Res_Bitmap, true}, {UI_PROGRESS, "GProgress", Res_Progress, true}, {UI_SCROLL_BAR, "GScrollBar", Res_ScrollBar, true}, {UI_CUSTOM, "GCustom", Res_Custom, true}, {UI_CONTROL_TREE, "GControlTree", 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; } char *GetText(int Col) { switch (Col) { case 0: { if (Ctrl && Ctrl->Str && Ctrl->Str->GetDefine()) { return Ctrl->Str->GetDefine(); } break; } case 1: { if (Ctrl && Ctrl->Str && Ctrl->Str->Get()) { return Ctrl->Str->Get(); } break; } } return (char*)""; } }; class TabOrder : public GDialog { ResDialogCtrl *Top; LList *Lst; GButton *Ok; GButton *Cancel; GButton *Up; GButton *Down; public: TabOrder(GView *Parent, ResDialogCtrl *top) { Top = top; SetParent(Parent); Children.Insert(Lst = new LList(IDC_LIST, 10, 10, 350, 300)); Children.Insert(Ok = new GButton(IDOK, Lst->GetPos().x2 + 10, 10, 60, 20, "Ok")); Children.Insert(Cancel = new GButton(IDCANCEL, Lst->GetPos().x2 + 10, Ok->GetPos().y2 + 5, 60, 20, "Cancel")); Children.Insert(Up = new GButton(IDC_UP, Lst->GetPos().x2 + 10, Cancel->GetPos().y2 + 15, 60, 20, "Up")); Children.Insert(Down = new GButton(IDC_DOWN, Lst->GetPos().x2 + 10, Up->GetPos().y2 + 5, 60, 20, "Down")); GRect 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) { GAutoPtr It(Top->View()->IterateViews()); for (ResDialogCtrl *Ctrl=dynamic_cast(It->First()); Ctrl; Ctrl=dynamic_cast(It->Next())) { if (Ctrl->GetType() != UI_TEXT) { Lst->Insert(new CtrlItem(Ctrl)); } } char s[256]; sprintf(s, "Set Tab Order: %s", Top->Str->GetDefine()); Name(s); DoModal(); } } int OnNotify(GViewI *Ctrl, int Flags) { 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 (CtrlItem *n=All.First(); n; n=All.Next()) { Top->View()->DelView(n->Ctrl->View()); Top->View()->AddView(n->Ctrl->View(), i++); } } // fall through } case IDCANCEL: { EndModal(0); break; } } return 0; } }; //////////////////////////////////////////////////////////////////// void DrawGoobers(GSurface *pDC, GRect &r, GRect *Goobers, COLOUR c) { int Mx = (r.x2 + r.x1) / 2 - (GOOBER_SIZE / 2); int My = (r.y2 + r.y1) / 2 - (GOOBER_SIZE / 2); pDC->Colour(c, 24); 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++) { pDC->Box(Goobers+i); } } //////////////////////////////////////////////////////////////////// int ResDialogCtrl::TabDepth = 0; ResDialogCtrl::ResDialogCtrl(ResDialog *dlg, char *CtrlTypeName, GXmlTag *load) : ResObject(CtrlTypeName) { Dlg = dlg; DragCtrl = -1; AcceptChildren = false; Movable = true; MoveCtrl = false; Vis = true; Client.ZOff(-1, -1); SelectMode = SelNone; SelectStart.ZOff(-1, -1); if (load) { // Base a string off the xml int r = load->GetAsInt("ref"); if (Dlg) { Str = Dlg->Symbols->FindRef(r); LgiAssert(Str); if (!Str) // oh well we should have one anyway... fix things up so to speak. Dlg->Symbols->CreateStr(); } LgiAssert(Str); } else if (Dlg->CreateSymbols) { // We create a symbol for this resource Str = (Dlg && Dlg->Symbols) ? Dlg->Symbols->CreateStr() : 0; if (Str) { char Def[256]; sprintf(Def, "IDC_%i", Str->GetRef()); Str->SetDefine(Def); } } else { // Someone else is going to create the symbol Str = 0; } LgiAssert(Str); } ResDialogCtrl::~ResDialogCtrl() { if (ResDialog::Symbols) { ResDialog::Symbols->DeleteStr(Str); } if (Dlg) { Dlg->OnDeselect(this); } } char *ResDialogCtrl::GetRefText() { static char Buf[64]; if (Str) { sprintf(Buf, "Ref=%i", Str->GetRef()); } else { Buf[0] = 0; } return Buf; } void ResDialogCtrl::ListChildren(List &l, bool Deep) { GAutoPtr it(View()->IterateViews()); for (GViewI *w = it->First(); w; w = it->Next()) { ResDialogCtrl *c = dynamic_cast(w); LgiAssert(c); if (c) { l.Insert(c); if (Deep) { c->ListChildren(l); } } } } GRect ResDialogCtrl::GetMinSize() { GRect m(0, 0, GRID_X-1, GRID_Y-1); if (IsContainer()) { GRect cli = View()->GetClient(false); GAutoPtr it(View()->IterateViews()); for (GViewI *c=it->First(); c; c=it->Next()) { GRect cpos = c->GetPos(); cpos.Offset(cli.x1, cli.y1); m.Union(&cpos); } } return m; } bool ResDialogCtrl::SetPos(GRect &p, bool Repaint) { GRect 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(); GRect r(0, 0, p.X()-1, p.Y()-1); r.Size(-GOOBER_BORDER, -GOOBER_BORDER); View()->Invalidate(&r, false, true); // check our parents are big enough to show us... ResDialogCtrl *Par = ParentCtrl(); if (Par) { GRect t = Par->View()->GetPos(); Par->ResDialogCtrl::SetPos(t, true); } return Status; } return true; } void ResDialogCtrl::TabString(char *Str) { if (Str) { for (int i=0; iGetPos(); r.Offset(-r.x1, -r.y1); for (; w && w != Dlg; w = w->GetParent()) { GRect pos = w->GetPos(); if (w->GetParent()) { // GView *Ctrl = w->GetParent()->GetGView(); GRect 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 Str = Dlg->App()->GetStrFromRef(Ref); if (!Str) { LgiTrace("%s:%i - String with ref '%i' missing.\n", _FL, Ref); LgiAssert(0); if ((Str = Dlg->App()->GetDialogSymbols()->CreateStr())) { Str->SetRef(Ref); } } // 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 // LgiAssert(Str->UpdateWnd == 0); // set the string's control to us Str->UpdateWnd = View(); // make the strings refid unique Str->UnDuplicate(); View()->Name(Str->Get()); } bool ResDialogCtrl::GetFields(FieldTree &Fields) { if (Str) { Str->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) && Str) { Str->Serialize(Fields); } GRect 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.Size(-GOOBER_BORDER, -GOOBER_BORDER); if (View()->GetParent()) { View()->GetParent()->Invalidate(&r); } } return true; } void ResDialogCtrl::CopyText() { if (Str) { Str->CopyText(); } } void ResDialogCtrl::PasteText() { if (Str) { Str->PasteText(); View()->Invalidate(); } } bool ResDialogCtrl::AttachCtrl(ResDialogCtrl *Ctrl, GRect *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(GSurface *pDC) { if (DragCtrl >= 0) { GRect r = DragRgn; r.Normal(); pDC->Colour(LC_FOCUS_SEL_BACK, 24); pDC->Box(&r); } } void ResDialogCtrl::OnMouseClick(GMouse &m) { if (m.Down()) { if (m.Left()) { if (Dlg) { bool Processed = false; GRect c = View()->GetClient(); bool ClickedThis = c.Overlap(m.x, m.y); GRect Cli = View()->GetClient(false); GMouse Ms = m; GdcPt2 Off; View()->WindowVirtualOffset(&Off); Ms.x += Off.x + Cli.x1; Ms.y += Off.y + Cli.y1; Dlg->OnMouseClick(Ms); if (ClickedThis && !Dlg->IsDraging()) { DragCtrl = Dlg->CurrentTool(); if ((DragCtrl > 0 && AcceptChildren) || ((DragCtrl == 0) && !Movable)) { GdcPt2 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()) { GSubMenu RClick; bool PasteData = false; bool PasteTranslations = false; { GClipBoard 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 (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.First(); if (Ctrl) { Ctrl->CopyText(); } break; } case IDM_PASTE_TEXT: { PasteText(); 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) { GRect r = View()->GetPos(); if (SelectStart == r) { Dlg->OnSelect(this, SelectMode != SelAdd); } SelectMode = SelNone; } } } void ResDialogCtrl::OnMouseMove(GMouse &m) { // Drag a rubber band... if (DragCtrl >= 0) { GRect 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.Size(-1, -1); View()->Invalidate(&Old); } // 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) { if (!Dlg->IsSelected(this)) { Dlg->OnSelect(this); } GRect Old = View()->GetPos(); GRect New = Old; New.Offset( m.x - DragRgn.x1, m.y - DragRgn.y1); GdcPt2 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) { GRect 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, GXmlTag *load) : ResDialogCtrl(dlg, Res_Dialog, load) { Movable = false; AcceptChildren = true; Str->UpdateWnd = View(); View()->Name("CtrlDlg"); } IMPL_DIALOG_CTRL(CtrlDlg) void CtrlDlg::OnPaint(GSurface *pDC) { // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } GRect &CtrlDlg::GetClient(bool InClientSpace) { static GRect r; Client.Set(0, 0, View()->X()-1, View()->Y()-1); Client.Size(3, 3); Client.y1 += LgiApp->GetMetric(LGI_MET_DECOR_CAPTION); if (Client.y1 > Client.y2) Client.y1 = Client.y2; r = Client; if (InClientSpace) r.Offset(-r.x1, -r.y1); return r; } void CtrlDlg::_Paint(GSurface *pDC, GdcPt2 *Offset, GRegion *Update) { Client = GetClient(false); // Draw the border GRect r(0, 0, View()->X()-1, View()->Y()-1); LgiWideBorder(pDC, r, DefaultRaisedEdge); pDC->Colour(LC_MED, 24); LgiFlatBorder(pDC, r, 1); // Draw the title bar Title = r; Title.y2 = Client.y1 - 1; pDC->Colour(LC_ACTIVE_TITLE, 24); pDC->Rectangle(&Title); if (Str) { GDisplayString ds(SysFont, Str->Get()); SysFont->Fore(LC_ACTIVE_TITLE_TEXT); SysFont->Transparent(true); ds.Draw(pDC, Title.x1 + 10, Title.y1 + ((Title.Y()-ds.Y())/2)); } // Draw the client area GRect c = Client; GdcPt2 o; if (Offset) o = *Offset; c.Offset(o.x, o.y); pDC->SetClient(&c); // Draw the grid pDC->Colour(LC_MED, 24); pDC->Rectangle(0, 0, Client.X()-1, Client.Y()-1); pDC->Colour(Rgb24(0x80, 0x80, 0x80), 24); for (int y=0; ySet(x, y); } } // Paint children GdcPt2 co(c.x1, c.y1); GView::_Paint(pDC, &co); pDC->SetOrigin(o.x, o.y); } ///////////////////////////////////////////////////////////////////// // Text box CtrlText::CtrlText(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_StaticText, load) { if (Str && !load) { Str->SetDefine("IDC_STATIC"); } } IMPL_DIALOG_CTRL(CtrlText) void CtrlText::OnPaint(GSurface *pDC) { Client.ZOff(X()-1, Y()-1); char *Text = Str->Get(); SysFont->Fore(0); SysFont->Transparent(true); if (Text) { GRect Client = GetClient(); int y = 0; char *Start = Text; for (char *s = Text; 1; s++) { if ((*s == '\\' && *(s+1) == 'n') || (*s == 0)) { GDisplayString ds(SysFont, 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, GXmlTag *load) : ResDialogCtrl(dlg, Res_EditBox, load) { Password = false; MultiLine = false; } IMPL_DIALOG_CTRL(CtrlEditbox) void CtrlEditbox::OnPaint(GSurface *pDC) { GRect r(0, 0, X()-1, Y()-1); Client = r; // Draw the ctrl LgiWideBorder(pDC, r, DefaultSunkenEdge); pDC->Colour(Enabled() ? LC_WORKSPACE : LC_MED, 24); pDC->Rectangle(&r); char *Text = Str->Get(); SysFont->Fore(Enabled() ? 0 : LC_LOW); SysFont->Transparent(true); if (Text) { if (Password) { char *t = NewStr(Text); if (t) { for (char *p = t; *p; p++) *p = '*'; GDisplayString ds(SysFont, t); ds.Draw(pDC, 4, 4); DeleteArray(t); } } else { GDisplayString ds(SysFont, 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, GXmlTag *load) : ResDialogCtrl(dlg, Res_CheckBox, load) { } IMPL_DIALOG_CTRL(CtrlCheckbox) void CtrlCheckbox::OnPaint(GSurface *pDC) { Client.ZOff(X()-1, Y()-1); GRect r(0, 0, 12, 12); // Draw the ctrl LgiWideBorder(pDC, r, DefaultSunkenEdge); pDC->Colour(LC_WORKSPACE, 24); pDC->Rectangle(&r); GdcPt2 Pt[6] = { GdcPt2(3, 4), GdcPt2(3, 7), GdcPt2(5, 10), GdcPt2(10, 5), GdcPt2(10, 2), GdcPt2(5, 7)}; pDC->Colour(0); pDC->Polygon(6, Pt); pDC->Set(3, 5); char *Text = Str->Get(); if (Text) { SysFont->Fore(LC_TEXT); SysFont->Transparent(true); GDisplayString ds(SysFont, Text); ds.Draw(pDC, r.x2 + 10, r.y1-2); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Button CtrlButton::CtrlButton(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_Button, load) { } 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"); return Status; } bool CtrlButton::Serialize(FieldTree &Fields) { bool Status = ResDialogCtrl::Serialize(Fields); if (Status) Fields.Serialize(this, VAL_Image, Image); return Status; } void CtrlButton::OnPaint(GSurface *pDC) { Client.ZOff(X()-1, Y()-1); GRect r = Client; char *Text = Str->Get(); // Draw the ctrl LgiWideBorder(pDC, r, DefaultRaisedEdge); SysFont->Fore(LC_TEXT); if (ValidStr(Text)) { SysFont->Back(LC_MED); SysFont->Transparent(false); GDisplayString ds(SysFont, Text); ds.Draw(pDC, r.x1 + ((r.X()-ds.X())/2), r.y1 + ((r.Y()-ds.Y())/2), &r); } else { pDC->Colour(LC_MED, 24); pDC->Rectangle(&r); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Group CtrlGroup::CtrlGroup(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_Group, load) { AcceptChildren = true; if (Str && !load) { Str->SetDefine("IDC_STATIC"); } } IMPL_DIALOG_CTRL(CtrlGroup) void CtrlGroup::OnPaint(GSurface *pDC) { Client.ZOff(X()-1, Y()-1); GRect r = Client; // Draw the ctrl r.y1 += 5; LgiWideBorder(pDC, r, EdgeXpChisel); r.y1 -= 5; SysFont->Fore(LC_TEXT); SysFont->Back(LC_MED); SysFont->Transparent(false); char *Text = Str->Get(); GDisplayString ds(SysFont, Text); ds.Draw(pDC, r.x1 + 8, r.y1 - 2); // Draw children //GWindow::OnPaint(pDC); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //Radio button uint32 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, GXmlTag *load) : ResDialogCtrl(dlg, Res_RadioBox, load) { Bmp = new GMemDC; 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(GSurface *pDC) { Client.ZOff(X()-1, Y()-1); GRect r(0, 0, 12, 12); // Draw the ctrl if (RadioBmp) { pDC->Blt(r.x1, r.y1, Bmp); } char *Text = Str->Get(); if (Text) { SysFont->Fore(LC_TEXT); SysFont->Transparent(true); GDisplayString ds(SysFont, Text); ds.Draw(pDC, r.x2 + 10, r.y1-2); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Tab CtrlTab::CtrlTab(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_Tab, load) { Str->UpdateWnd = this; } IMPL_DIALOG_CTRL(CtrlTab) void CtrlTab::OnPaint(GSurface *pDC) { } void CtrlTab::ListChildren(List &l, bool Deep) { ResDialogCtrl *Ctrl = dynamic_cast(GetParent()); CtrlTabs *Par = dynamic_cast(Ctrl); LgiAssert(Par); int MyIndex = Par->Tabs.IndexOf(this); LgiAssert(MyIndex >= 0); List *CList = (Par->Current == MyIndex) ? &Par->Children : &Children; for (GViewI *w = CList->First(); w; w = CList->Next()) { ResDialogCtrl *c = dynamic_cast(w); if (c) { l.Insert(c); if (Deep) { c->ListChildren(l, Deep); } } } } // Tab control CtrlTabs::CtrlTabs(ResDialog *dlg, GXmlTag *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->Str) { t->Str->Set(Text); t->SetParent(this); Tabs.Insert(t); } else { DeleteObj(t); } } } } } CtrlTabs::~CtrlTabs() { Empty(); } void CtrlTabs::OnMouseMove(GMouse &m) { ResDialogCtrl::OnMouseMove(m); } void CtrlTabs::ShowMe(ResDialogCtrl *Child) { CtrlTab *t = dynamic_cast(Child); if (t) { int Idx = Tabs.IndexOf(t); if (Idx >= 0) { ToTab(); Current = Idx; FromTab(); } } } void CtrlTabs::EnumCtrls(List &Ctrls) { List::I it = Tabs.Start(); for (CtrlTab *t = *it; t; t = *++it) { t->EnumCtrls(Ctrls); } ResDialogCtrl::EnumCtrls(Ctrls); } GRect CtrlTabs::GetMinSize() { List l; ListChildren(l, false); GRect 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 GRect cli = GetClient(false); for (ResDialogCtrl *c=l.First(); c; c=l.Next()) { GRect 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 (CtrlTab *t = Tabs.First(); t; t = Tabs.Next(), n++) { l.Add(t); GAutoPtr It((Current == n ? (GViewI*)this : (GViewI*)t)->IterateViews()); for (GViewI *w = It->First(); w; w = It->Next()) { ResDialogCtrl *c = dynamic_cast(w); if (c) { l.Insert(c); c->ListChildren(l, Deep); } } } } void CtrlTabs::Empty() { ToTab(); Tabs.DeleteObjects(); } void CtrlTabs::OnPaint(GSurface *pDC) { // Draw the ctrl Title.ZOff(X()-1, 17); Client.ZOff(X()-1, Y()-1); Client.y1 = Title.y2; GRect r = Client; LgiWideBorder(pDC, r, DefaultRaisedEdge); // Draw the tabs int i = 0; int x = 2; for (CtrlTab *Tab = Tabs.First(); Tab; Tab = Tabs.Next(), i++) { char *Str = Tab->Str ? Tab->Str->Get() : 0; GDisplayString ds(SysFont, Str); int Width = 12 + ds.X(); GRect t(x, Title.y1 + 2, x + Width - 1, Title.y2 - 1); if (Current == i) { t.Size(-2, -2); GAutoPtr It(Tab->IterateViews()); if (It->Length() > 0) { FromTab(); } } Tab->View()->SetPos(t); pDC->Colour(LC_LIGHT, 24); 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(LC_MED, 24); 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(LC_LOW, 24); 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.Size(2, 2); t.y2 += 2; SysFont->Fore(LC_TEXT); SysFont->Back(LC_MED); SysFont->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); } // Draw children //GWindow::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 GAutoPtr CurIt(Cur->IterateViews()); GAutoPtr ThisIt(IterateViews()); GViewI *v; while ((v = CurIt->First())) { Cur->DelView(v); } while ((v = ThisIt->First())) { DelView(v); Cur->AddView(v); } } } void CtrlTabs::FromTab() { CtrlTab *Cur = Tabs.ItemAt(Current); if (Cur) { // load all our children from the new tab GAutoPtr CurIt(Cur->IterateViews()); GAutoPtr ThisIt(IterateViews()); GViewI *v; while ((v = ThisIt->First())) { DelView(v); } while ((v = CurIt->First())) { Cur->DelView(v); AddView(v); } } } void CtrlTabs::OnMouseClick(GMouse &m) { if (m.Down()) { if (Title.Overlap(m.x, m.y)) { // select current tab int i = 0; for (CtrlTab *Tab = Tabs.First(); Tab; Tab = Tabs.Next(), i++) { if (Tab->View()->GetPos().Overlap(m.x, m.y) /* && i != Current*/) { ToTab(); Current = i; FromTab(); Dlg->OnSelect(Tab); Invalidate(); break; } } } if (m.IsContextMenu() && Title.Overlap(m.x, m.y)) { GSubMenu *RClick = new GSubMenu; 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 %i", Tabs.Length()+1); t->Str->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->Str) t->Str = Dlg->CreateSymbol(); GInput Input(this, t->Str->Get(), "Enter tab name:", "Rename"); Input.SetParent(Dlg); if (Input.DoModal() && Input.Str) { t->Str->Set(Input.Str); } } 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; } } else { int asd=0; } ResDialogCtrl::OnMouseClick(m); } // List column ListCol::ListCol(ResDialog *dlg, GXmlTag *load, char *s, int Width) : ResDialogCtrl(dlg, Res_Column, load) { if (s && Str) { Str->Set(s); } GRect r(0, 0, Width-1, 18); ResDialogCtrl::SetPos(r); } IMPL_DIALOG_CTRL(ListCol) void ListCol::OnPaint(GSurface *pDC) { } // List control CtrlList::CtrlList(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_ListView, load) { DragCol = -1; } CtrlList::~CtrlList() { Empty(); } void CtrlList::ListChildren(List &l, bool Deep) { if (Deep) { for (GView *w = Cols.First(); w; w = Cols.Next()) { ResDialogCtrl *c = dynamic_cast(w); if (c) { l.Insert(c); c->ListChildren(l); } } } } void CtrlList::Empty() { for (ListCol *c = Cols.First(); c; c = Cols.Next()) { DeleteObj(c); } Cols.Empty(); } void CtrlList::OnMouseClick(GMouse &m) { if (m.Down()) { if (Title.Overlap(m.x, m.y)) { int x=0; ListCol *c = 0; int DragOver = -1; for (ListCol *Col = Cols.First(); Col; Col = Cols.Next()) { 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()) { GSubMenu *RClick = new GSubMenu; 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->Str) { c->Str->CopyText(); } break; } case IDM_PASTE_TEXT: { if (c && c->Str) { c->Str->PasteText(); } break; } case IDM_NEW: { ListCol *c = dynamic_cast(Dlg->CreateCtrl(UI_COLUMN,0)); if (c) { char Text[256]; sprintf(Text, "Col %i", Cols.Length()+1); c->Str->Set(Text); Cols.Insert(c); } break; } case IDM_DELETE: { Cols.Delete(c); DeleteObj(c); break; } case IDM_RENAME: { if (c) { GInput Input(this, c->Str->Get(), "Enter column name:", "Rename"); Input.SetParent(Dlg); if (Input.DoModal()) { c->Str->Set(Input.Str); } } break; } case IDM_MOVE_LEFT: { int Current = Cols.IndexOf(c); if (c && Current > 0) { Cols.Delete(c); Cols.Insert(c, --Current); } break; } case IDM_MOVE_RIGHT: { int 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(GMouse &m) { if (DragCol >= 0) { int i=0, x=0;; for (ListCol *Col = Cols.First(); Col; Col = Cols.Next(), i++) { if (i == DragCol) { int Dx = (m.x - x - Title.x1); GRect r = Col->GetPos(); r.x2 = r.x1 + Dx; Col->ResDialogCtrl::SetPos(r); break; } x += Col->r().X(); } Invalidate(); } ResDialogCtrl::OnMouseMove(m); } void CtrlList::OnPaint(GSurface *pDC) { GRect r(0, 0, X()-1, Y()-1); // Draw the ctrl LgiWideBorder(pDC, r, DefaultSunkenEdge); Title = r; Title.y2 = Title.y1 + 15; Client = r; Client.y1 = Title.y2 + 1; pDC->Colour(LC_WORKSPACE, 24); pDC->Rectangle(&Client); int x = Title.x1; for (ListCol *c = Cols.First(); c; c = Cols.Next()) { int Width = c->r().X(); c->r().Set(x, Title.y1, x + Width - 1, Title.y2); GRect r = c->r(); - r.x2 = min(r.x2, Title.x2); + r.x2 = MIN(r.x2, Title.x2); x = r.x2 + 1; if (r.Valid()) { LgiWideBorder(pDC, r, DefaultRaisedEdge); SysFont->Fore(LC_TEXT); SysFont->Back(LC_MED); SysFont->Transparent(false); const char *Str = c->Str->Get(); if (!Str) Str = ""; GDisplayString ds(SysFont, Str); ds.Draw(pDC, r.x1 + 2, r.y1 + ((r.Y()-ds.Y())/2) - 1, &r); } } GRect Client(x, Title.y1, Title.x2, Title.y2); if (Client.Valid()) { LgiWideBorder(pDC, Client, DefaultRaisedEdge); pDC->Colour(LC_MED, 24);; pDC->Rectangle(&Client); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } // Combo box CtrlComboBox::CtrlComboBox(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_ComboBox, load) { } IMPL_DIALOG_CTRL(CtrlComboBox) void CtrlComboBox::OnPaint(GSurface *pDC) { GRect r(0, 0, X()-1, Y()-1); Client = r; // Draw the ctrl LgiWideBorder(pDC, r, DefaultSunkenEdge); // Allocate space GRect e = r; e.x2 -= 15; GRect d = r; d.x1 = e.x2 + 1; // Draw edit pDC->Colour(LC_WORKSPACE, 24); pDC->Rectangle(&e); // Draw drap down LgiWideBorder(pDC, d, DefaultRaisedEdge); pDC->Colour(LC_MED, 24); 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 = Str->Get(); SysFont->Fore(0); SysFont->Transparent(true); if (Text) { GDisplayString ds(SysFont, Text); ds.Draw(pDC, 4, 4); } // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// CtrlScrollBar::CtrlScrollBar(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_ScrollBar, load) { } IMPL_DIALOG_CTRL(CtrlScrollBar) void CtrlScrollBar::OnPaint(GSurface *pDC) { GRect 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(); GRect 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 LgiWideBorder(pDC, a, DefaultRaisedEdge); LgiWideBorder(pDC, c, DefaultRaisedEdge); pDC->Colour(LC_MED, 24); 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, GXmlTag *load) : ResDialogCtrl(dlg, Res_TreeView, load) { } IMPL_DIALOG_CTRL(CtrlTree) void CtrlTree::OnPaint(GSurface *pDC) { GRect r(0, 0, X()-1, Y()-1); Client = r; LgiWideBorder(pDC, r, DefaultSunkenEdge); pDC->Colour(Rgb24(255, 255, 255), 24); pDC->Rectangle(&r); SysFont->Colour(0, 0xffffff); SysFont->Transparent(true); GDisplayString ds(SysFont, "Tree"); ds.Draw(pDC, r.x1 + 3, r.y1 + 3, &r); // Draw any rubber band ResDialogCtrl::OnPaint(pDC); } //////////////////////////////////////////////////////////////////// CtrlBitmap::CtrlBitmap(ResDialog *dlg, GXmlTag *load) : ResDialogCtrl(dlg, Res_Bitmap, load) { } IMPL_DIALOG_CTRL(CtrlBitmap) void CtrlBitmap::OnPaint(GSurface *pDC) { GRect r(0, 0, X()-1, Y()-1); Client = r; LgiWideBorder(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, GXmlTag *load) : ResDialogCtrl(dlg, Res_Progress, load) { } IMPL_DIALOG_CTRL(CtrlProgress) void CtrlProgress::OnPaint(GSurface *pDC) { GRect r(0, 0, X()-1, Y()-1); Client = r; LgiWideBorder(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, GXmlTag *load) : ResDialogCtrl(dlg, Res_Custom, load) { Control = 0; } IMPL_DIALOG_CTRL(CtrlCustom) CtrlCustom::~CtrlCustom() { DeleteArray(Control); } void CtrlCustom::OnPaint(GSurface *pDC) { GRect r(0, 0, X()-1, Y()-1); Client = r; LgiWideBorder(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); } SysFont->Colour(LC_TEXT, LC_WORKSPACE); GDisplayString ds(SysFont, 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); ChildIterator It(View()->IterateViews()); for (GViewI *c = It->First(); c; c = It->Next()) { ResDialogCtrl *dc = dynamic_cast(c); LgiAssert(dc); dc->EnumCtrls(Ctrls); } } void ResDialog::EnumCtrls(List &Ctrls) { for (ResDialogCtrl *c = dynamic_cast(Children.First()); c; c = dynamic_cast(Children.Next())) { 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.First()); Invalidate(); OnLanguageChange(); } char *ResDialog::Name() { GViewI *v = Children.First(); ResDialogCtrl *Ctrl = dynamic_cast(v); if (Ctrl && Ctrl->Str && Ctrl->Str->GetDefine()) { return Ctrl->Str->GetDefine(); } return (char*)""; } bool ResDialog::Name(const char *n) { ResDialogCtrl *Ctrl = dynamic_cast(Children.First()); if (Ctrl && Ctrl->Str) { Ctrl->Str->SetDefine((n)?n:""); return Ctrl->Str->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, GDom *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; } GDom *ResDialog::Res_GetDom(ResObject *Obj) { return dynamic_cast(Obj); } bool ResDialog::Res_SetProperties(ResObject *Obj, GDom *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(GXmlTag *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) { GRect 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); } } } GRect ResDialog::Res_GetPos(ResObject *Obj) { if (Obj) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); LgiAssert(Ctrl); if (Ctrl) { return Ctrl->View()->GetPos(); } } return GRect(0, 0, 0, 0); } int ResDialog::Res_GetStrRef(ResObject *Obj) { if (Obj) { ResDialogCtrl *Ctrl = dynamic_cast((ResDialogCtrl*)Obj); if (Ctrl) { return Ctrl->Str->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); LgiAssert(Ctrl && Ctrl->Str); return Ctrl->Str != 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 (ResDialogCtrl *o = Child.First(); o; o = Child.Next()) { 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 ( CtrlTab *Tab = Tabs->Tabs.First(); Tab; Tab = Tabs->Tabs.Next()) { l->Insert(Tab); } return true; } CtrlList *Lst = dynamic_cast(Obj); if (Lst) { for ( ListCol *Col = Lst->Cols.First(); Col; Col = Lst->Cols.Next()) { l->Insert(Col); } return true; } } return false; } void ResDialog::Create(GXmlTag *load, SerialiseContext *Ctx) { CtrlDlg *Dlg = new CtrlDlg(this, load); if (Dlg) { GRect r = DlgPos; r.Offset(GOOBER_BORDER, GOOBER_BORDER); Children.Insert(Dlg); Dlg->SetParent(this); if (load) { if (Ctx) Read(load, *Ctx); else LgiAssert(0); } else { Dlg->ResDialogCtrl::SetPos(r); if (Dlg->Str) Dlg->Str->Set("Dialog"); } } } void ResDialog::Delete() { // Deselect the dialog ctrl OnDeselect(dynamic_cast(Children.First())); // Delete selected controls for (ResDialogCtrl *c = Selection.First(); c; c = Selection.First()) { c->View()->Detach(); DeleteObj(c); } // Repaint GView::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 (ResDialogCtrl *c = Selection.First(); c; c = Selection.Next()) { // is c a child of an item already in Top? bool Ignore = false; for (ResDialogCtrl *p = Top.First(); p; p = Top.Next()) { 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.First())); // 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; ResDialogCtrl *c; GXmlTag *Root = new GXmlTag("Resources"); if (Root) { if (Delete) { // remove selection from UI AppWindow->OnObjSelect(0); } // write the string resources first for (c = Top.First(); c; c = Top.Next()) { 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 (c = All.First(); c; c = All.Next()) { // Write the string out GXmlTag *t = new GXmlTag; if (t && c->Str->Write(t, Ctx)) { Root->InsertTag(t); } else { DeleteObj(t); } } // write the objects themselves for (c = Top.First(); c; c = Top.Next()) { GXmlTag *t = new GXmlTag; if (t && Res_Write(c, t)) { Root->InsertTag(t); } else { DeleteObj(t); } } // Read the file in and copy to the clipboard GStringPipe Xml; GXmlTree Tree; if (Tree.Write(Root, &Xml)) { char *s = Xml.NewStr(); { GClipBoard 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 for (c = Selection.First(); c; c = Selection.First()) { c->View()->Detach(); Selection.Delete(c); DeleteObj(c); } } // Repaint GView::Invalidate(); } } class StringId { public: ResString *Str; int OldRef; int NewRef; }; void RemapAllRefs(GXmlTag *t, List &Strs) { char *RefStr; if ((RefStr = t->GetAttr("Ref"))) { int r = atoi(RefStr); for (StringId *i=Strs.First(); i; i=Strs.Next()) { 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 LgiAssert(0); } } for (GXmlTag *c = t->Children.First(); c; c = t->Children.Next()) { RemapAllRefs(c, Strs); } } void ResDialog::Paste() { // Get the clipboard data char *Mem = 0; char *Data = 0; { GClipBoard 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.First(); if (c && c->IsContainer()) { Container = c; } if (!Container) { // Otherwise just use the dialog as the container Container = dynamic_cast(Children.First()); } if (Container) { // remap list List Strings; int NextRef = 0; // Deselect everything OnSelect(NULL); // Parse the data List NewStrs; GXmlTree Tree; GStringPipe p; p.Push(Data); // Create the new controls, strings first // that way we can setup the remapping properly to avoid // string ref duplicates GXmlTag Root; if (Tree.Read(&Root, &p, 0)) { GXmlTag *t; for (t = Root.Children.First(); t; t = Root.Children.Next()) { if (t->IsTag("string")) { // string tag LgiAssert(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; LgiAssert(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 (t = Root.Children.First(); t; t = Root.Children.Next()) { 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 ResDialogCtrl *c = NewCtrls.First(); if (c) { GRect All = c->View()->GetPos(); while ((c = NewCtrls.Next())) { All.Union(&c->View()->GetPos()); } // now paste in the controls for (c = NewCtrls.First(); c; c = NewCtrls.Next()) { GRect *Preference = Container->GetPasteArea(); GRect 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 GRect cp = Container->View()->GetPos(); Container->SetPos(cp, true); } // Deduplicate all these new strings for (ResString *s = NewStrs.First(); s; s = NewStrs.Next()) { s->UnDuplicate(); } } // Repaint GView::Invalidate(); } } DeleteArray(Mem); } void ResDialog::SnapPoint(GdcPt2 *p, ResDialogCtrl *From) { ResDialogCtrl *Ctrl = dynamic_cast(Children.First()); if (p && Ctrl) { int Ox = 0; // -Ctrl->Client.x1; int Oy = 0; // -Ctrl->Client.y1; GView *Parent = dynamic_cast(Ctrl); if (From) { for (GViewI *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(GRect *r, ResDialogCtrl *From) { ResDialogCtrl *Ctrl = dynamic_cast(Children.First()); if (r && Ctrl) { int Ox = 0; // -Ctrl->Client.x1; int Oy = 0; // -Ctrl->Client.y1; GView *Parent = dynamic_cast(Ctrl); for (GViewI *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) { ResDialogCtrl *w = Selection.First(); if (w) { ResDialogCtrl *Parent = w->ParentCtrl(); if (Parent) { // find dimensions of group GRect All = w->View()->GetPos(); for (; w; w = Selection.Next()) { All.Union(&w->View()->GetPos()); } // limit the move to the top-left corner of the parent's client GRect 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 GRegion Update; for (w = Selection.First(); w; w = Selection.Next()) { GRect Old = w->View()->GetPos(); GRect New = Old; New.Offset(Dx, Dy); // optionally limit the move to the containers bounds GRect *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); } } GRect Up = w->AbsPos(); Up.Size(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); w->SetPos(New, false); Up = w->AbsPos(); Up.Size(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); } Invalidate(&Update); } } } void ResDialog::SelectCtrl(ResDialogCtrl *c) { Selection.Empty(); if (c) { bool IsMine = false; for (GViewI *p = c->View(); p; p = p->GetParent()) { if ((GViewI*)this == p) { IsMine = true; break; } } if (IsMine) { Selection.Insert(c); // int TabIdx = -1; ResDialogCtrl *Prev = 0; for (GViewI *p = c->View()->GetParent(); p; p = p->GetParent()) { ResDialogCtrl *c = dynamic_cast(p); if (c) { c->ShowMe(Prev); Prev = c; } else break; } } else { printf("%s:%i - Ctrl doesn't belong to me.\n", __FILE__, __LINE__); } } else { printf("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, GRect *r, bool ClearPrev) { if (ClearPrev) { Selection.Empty(); } if (Parent && r) { GAutoPtrIt(Parent->View()->IterateViews()); for (GViewI *c = It->First(); c; c = It->Next()) { 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.First() : 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.First() : 0); } } } void ResDialog::OnDeselect(ResDialogCtrl *Wnd) { if (Selection.HasItem(Wnd)) { Selection.Delete(Wnd); AppWindow->OnObjSelect(0); } } ResDialogCtrl *ResDialog::CreateCtrl(GXmlTag *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, GXmlTag *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->Str && !load) { Ctrl->Str->Set("Some text"); } break; } case UI_EDITBOX: { Ctrl = new CtrlEditbox(this, load); break; } case UI_CHECKBOX: { Ctrl = new CtrlCheckbox(this, load); if (Ctrl && Ctrl->Str && !load) { Ctrl->Str->Set("Checkbox"); } break; } case UI_BUTTON: { Ctrl = new CtrlButton(this, load); if (Ctrl && Ctrl->Str && !load) { static int i = 1; char Text[256]; sprintf(Text, "Button %i", i++); Ctrl->Str->Set(Text); } break; } case UI_GROUP: { Ctrl = new CtrlGroup(this, load); if (Ctrl && Ctrl->Str && !load) { Ctrl->Str->Set("Text"); } break; } case UI_RADIO: { Ctrl = new CtrlRadio(this, load); if (Ctrl && Ctrl->Str && !load) { Ctrl->Str->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: { LgiAssert(!"No control factory handler."); break; } } if (Ctrl && Ctrl->Str) { Ctrl->Str->UpdateWnd = Ctrl->View(); } return Ctrl; } GView *ResDialog::CreateUI() { return Ui = new ResDialogUi(this); } void ResDialog::DrawSelection(GSurface *pDC) { if (Selection.Length() == 0) return; // Draw selection for (ResDialogCtrl *Ctrl = Selection.First(); Ctrl; Ctrl = Selection.Next()) { GRect r = Ctrl->AbsPos(); COLOUR s = Rgb24(255, 0, 0); COLOUR c = GetParent()->Focus() ? s : GdcMixColour(s, LC_MED, 0.4); DrawGoobers(pDC, r, Ctrl->Goobers, c); } } void ResDialog::_Paint(GSurface *pDC, GdcPt2 *Offset, GRegion *Update) { #ifndef MAC GScreenDC DC(this); pDC = &DC; #endif ResDialogCtrl *Ctrl = dynamic_cast(Children.First()); if (Ctrl) { GRect c = Ctrl->View()->GetPos(); c.Size(-GOOBER_BORDER, -GOOBER_BORDER); #ifdef MAC GSurface *pMemDC = pDC; if (pMemDC) #else GAutoPtr pMemDC(new GMemDC); if (pMemDC && pMemDC->Create(c.X(), c.Y(), GdcD->GetColourSpace())) #endif { #ifdef MAC GScreenDC *Scr = dynamic_cast(pDC); if (Scr) Scr->PushState(); #endif // Draw client pMemDC->Colour(LC_WORKSPACE, 24); // pMemDC->Colour(Rgb24(0, 128, 0), 24); pMemDC->Rectangle(); // Paint children GRect Pos = Ctrl->View()->GetPos(); pMemDC->SetClient(&Pos); GView::_Paint(pMemDC); pMemDC->SetClient(0); if (GetParent()) { #ifdef MAC if (Scr) Scr->PopState(); #endif // Draw selection DrawSelection(pMemDC); } #ifndef MAC // Put on screen pMemDC->SetOrigin(0, 0); pDC->Blt(0, 0, pMemDC); // Draw other non Mem-DC regions pDC->Colour(LC_WORKSPACE, 24); if (X() > c.X()) { pDC->Rectangle(c.x2 + 1, 0, X()-1, c.y2); } if (Y() > c.Y()) { pDC->Rectangle(0, c.y2 + 1, X()-1, Y()-1); } #endif } else { // error SysFont->Colour(LC_TEXT, LC_WORKSPACE); SysFont->Transparent(false); GDisplayString Ds(SysFont, "Can't create memory bitmap."); Ds.Draw(pDC, 2, 0, &GetClient()); } } else { pDC->Colour(LC_WORKSPACE, 24); pDC->Rectangle(); } } void ResDialog::OnLanguageChange() { if (Ui && Ui->StatusInfo) { GLanguage *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(GKey &k) { if (k.Ctrl()) { switch (k.c16) { case VK_UP: { if (k.Down()) { int Idx = Symbols->GetLangIdx(App()->GetCurLang()->Id); if (Idx > 0) { GLanguage *l = Symbols->GetLanguage(Idx - 1); if (l) { App()->SetCurLang(l); } } Invalidate(); OnLanguageChange(); } return true; break; } case VK_DOWN: { if (k.Down()) { int Idx = Symbols->GetLangIdx(App()->GetCurLang()->Id); if (Idx < Symbols->GetLanguages() - 1) { GLanguage *l = Symbols->GetLanguage(Idx + 1); if (l) { App()->SetCurLang(l); } } Invalidate(); OnLanguageChange(); } return true; break; } } } return false; } void ResDialog::OnMouseClick(GMouse &m) { if (m.Down()) { if (GetParent()) GetParent()->Focus(true); if (m.Left()) { DragGoober = -1; for (ResDialogCtrl *c = Selection.First(); c; c = Selection.Next()) { for (int i=0; i<8; i++) { if (c->Goobers[i].Overlap(m.x, m.y)) { DragGoober = i; DragCtrl = c; break; } } } 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(GMouse &m) { if (DragGoober >= 0) { if (DragX) { *DragX = m.x - DragOx; } if (DragY) { *DragY = m.y - DragOy; } // DragRgn in real coords GRect Old = DragCtrl->View()->GetPos(); GRect New = DragRgn; GAutoPtr It(IterateViews()); if (DragCtrl->View() != It->First()) { SnapRect(&New, DragCtrl->ParentCtrl()); } // New now in snapped coords // If that means the dragging control changes then if (New != Old) { GRegion Update; // change everyone else by the same amount for (ResDialogCtrl *c = Selection.First(); c; c = Selection.Next()) { GRect OldPos = c->View()->GetPos(); GRect 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; GRect Up = c->AbsPos(); Up.Size(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); c->SetPos(NewPos); Up = c->AbsPos(); Up.Size(-GOOBER_BORDER, -GOOBER_BORDER); Update.Union(&Up); } Invalidate(&Update); } } } bool ResDialog::Test(ErrorCollection *e) { return true; } bool ResDialog::Read(GXmlTag *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.First()); 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.First()); if (Ctrl) { // list all the entries List l; Ctrl->ListChildren(l); l.Insert(Ctrl); // insert the dialog too // remove duplicate string entries for (ResDialogCtrl *c = l.First(); c; c = l.Next()) { LgiAssert(c->Str); c->Str->UnDuplicate(); } } // sort list (cause I need to read the file myself) if (Symbols) { Symbols->Sort(); } } bool ResDialog::Write(GXmlTag *t, SerialiseContext &Ctx) { bool Status = false; ResDialogCtrl *Ctrl = dynamic_cast(Children.First()); 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(GSubMenu *RClick) { if (RClick) { if (Enabled()) { RClick->AppendSeparator(); if (Type() > 0) { RClick->AppendItem("Dump to C++", IDM_DUMP, true); GSubMenu *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 "GWindow"; } 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->Str->Get(); sprintf(Buf, ", \"%s\"", s?s:""); return Buf; } // Not processed case UI_COLUMN: case UI_TAB: { LgiAssert(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(GStringPipe &Def, GStringPipe &Var, GStringPipe &Inst, ResDialogCtrl *Ctrl, ResDialogCtrl *Parent, int &Index) { char Str[256]; const char *Type = "GView"; for (LgiObjectName *on=NameMap; on->Type; on++) { if (Ctrl->GetType() == on->Type) { Type = on->ObjectName; break; } } if (stricmp(Type, "GDialog")) { if (ValidStr(Ctrl->Str->GetDefine()) && stricmp(Ctrl->Str->GetDefine(), "IDOK") && stricmp(Ctrl->Str->GetDefine(), "IDCANCEL") && stricmp(Ctrl->Str->GetDefine(), "IDC_STATIC")) { char Tab[8]; int Tabs = (32 - strlen(Ctrl->Str->GetDefine()) - 1) / 4; memset(Tab, '\t', Tabs); Tab[Tabs] = 0; sprintf(Str, "#define %s%s%i\n", Ctrl->Str->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->Str->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 (ListCol *c=List->Cols.First(); c; c=List->Cols.Next()) { sprintf(Str, "\tCtrl%i->AddColumn(\"%s\", %i);\n", Index, c->Str->Get(), c->X()); Inst.Push(Str); } } Index++; } GAutoPtr It(Ctrl->View()->IterateViews()); for ( ResDialogCtrl *c=dynamic_cast(It->First()); c; c=dynamic_cast(It->Next())) { OutputCtrl(Def, Var, Inst, c, Ctrl, Index); } } void ResDialog::OnCommand(int Cmd) { switch (Cmd) { case IDM_DUMP: { GStringPipe Def, Var, Inst; GStringPipe Buf; char Str[256]; ResDialogCtrl *Dlg = dynamic_cast(Children.First()); 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 GDialog\n" "{\n"); // Variables char *Vars = Var.NewStr(); if (Vars) { Buf.Push(Vars); Var.Empty(); } // Member functions Buf.Push( "\n" "public:\n" "\tDlg(GView *Parent);\n" "\t~Dlg();\n" "\n" "\tint OnNotify(GViewI *Ctrl, int Flags);\n" "};\n" "\n"); // Class impl Buf.Push( "Dlg::Dlg(GView *Parent)\n" "{\n" "\tSetParent(Parent);\n"); sprintf(Str, "\tName(\"%s\");\n" "\tGRegion r(0, 0, %i, %i);\n" "\tSetPos(r);\n", Dlg->Str->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(GViewI *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) { GClipBoard Clip(Ui); Clip.Text(Text); DeleteArray(Text); } } break; } case IDM_EXPORT: { GFileSelect Select; Select.Parent(AppWindow); Select.Type("Text", "*.txt"); if (Select.Save()) { GFile F; if (F.Open(Select.Name(), O_WRITE)) { F.SetSize(0); // Serialize(F, true); } else { LgiMsg(AppWindow, "Couldn't open file for writing."); } } 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.First()->IsContainer()) { Top = Selection.First(); } if (!Top) { Top = dynamic_cast(Children.First()); } if (Top) { TabOrder Dlg(this, Top); } 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.First()); 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(GSurface *pDC) { GRegion Client(0, 0, X()-1, Y()-1); for (GViewI *w = Children.First(); w; w = Children.Next()) { GRect r = w->GetPos(); Client.Subtract(&r); } pDC->Colour(LC_MED, 24); for (GRect *r = Client.First(); r; r = Client.Next()) { pDC->Rectangle(r); } } void ResDialogUi::PourAll() { GRegion Client(GetClient()); GRegion Update; for (GViewI *v = Children.First(); v; v = Children.Next()) { GRect 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); } DeleteArray(FileName); } Status = new GStatusBar; 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) { int i=0; GAutoPtr It(Tools->IterateViews()); for (GViewI *w = It->First(); w; w = It->Next(), i++) { GToolButton *But = dynamic_cast(w); if (But) { if (But->Value()) { return But->GetId(); } } } } return -1; } void ResDialogUi::SelectTool(int i) { if (Tools) { GAutoPtr It(Tools->IterateViews()); GViewI *w = (*It)[i]; if (w) { GToolButton *But = dynamic_cast(w); if (But) { But->Value(true); } } } } GMessage::Result ResDialogUi::OnEvent(GMessage *Msg) { switch (MsgCode(Msg)) { case M_COMMAND: { Dialog->OnCommand(MsgA(Msg)&0xffff, MsgA(Msg)>>16, (OsView) MsgB(Msg)); break; } case M_DESCRIBE: { char *Text = (char*) MsgB(Msg); if (Text) { StatusInfo->Name(Text); } break; } } return GView::OnEvent(Msg); } diff --git a/include/common/LDateTime.h b/include/common/LDateTime.h --- a/include/common/LDateTime.h +++ b/include/common/LDateTime.h @@ -1,363 +1,369 @@ /// \file /// \author Matthew Allen /** * \defgroup Time Time and date handling * \ingroup Lgi */ #ifndef __DATE_TIME_H #define __DATE_TIME_H #include #include "GStringClass.h" #define GDTF_DEFAULT 0 /// Format the date as DD/MM/YYYY /// \ingroup Time #define GDTF_DAY_MONTH_YEAR 0x001 /// Format the date as MM/DD/YYYY /// \ingroup Time #define GDTF_MONTH_DAY_YEAR 0x002 /// Format the date as YYYY/MM/DD /// \ingroup Time #define GDTF_YEAR_MONTH_DAY 0x004 /// The bit mask for the date /// \ingroup Time #define GDTF_DATE_MASK 0x00f /// Format the time as HH:MM and an am/pm marker /// \ingroup Time #define GDTF_12HOUR 0x010 /// Format the time as 24 hour time /// \ingroup Time #define GDTF_24HOUR 0x020 /// The bit mask for the time /// \ingroup Time #define GDTF_TIME_MASK 0x0f0 /// Format the date with a leading zero /// \ingroup Time #define GDTF_DAY_LEADINGZ 0x100 /// Format the month with a leading zero /// \ingroup Time #define GDTF_MONTH_LEADINGZ 0x200 /// A date/time class /// /// This class interacts with system times represented as 64bit ints. The various OS support different /// formats for that 64bit int values. On windows the system times are in 100-nanosecond intervals since /// January 1, 1601 (UTC), as per the FILETIME structure, on Posix systems (Linux/Mac) the 64bit values /// are in milliseconds since January 1, 1970 UTC. This is just unix time * 1000. If you are serializing /// these 64bit values you should take that into account, they are NOT cross platform. The GDirectory class /// uses the same 64bit values as accepted here for the file's last modified timestamp etc. To convert the /// 64bit values to seconds, divide by LDateTime::Second64Bit, useful for calculating the time in seconds /// between 2 LDateTime objects. /// /// \ingroup Time class LgiClass LDateTime // This class can't have a virtual table, because it's used in // GArray's which initialize with all zero bytes. { /// 1 - DaysInMonth int16 _Day; /// #### int16 _Year; /// Milliseconds: 0-999 int16 _Thousands; /// 1-12 int16 _Month; /// 0-59 int16 _Seconds; /// 0-59 int16 _Minutes; /// 0-23 (24hr) int16 _Hours; /// The current timezone of this object, defaults to the system timezone int16 _Tz; // in minutes (+10 == 600 etc) /// Combination of (#GDTF_DAY_MONTH_YEAR or #GDTF_MONTH_DAY_YEAR or #GDTF_YEAR_MONTH_DAY) and (#GDTF_12HOUR or #GDTF_24HOUR) uint16 _Format; /// The default formatting of datetimes static uint16 DefaultFormat; /// The default date separator character static char DefaultSeparator; public: LDateTime(const char *Init = NULL); ~LDateTime(); enum { /// Resolution of a second when using 64 bit int's /// \sa LDateTime::Get(int64), LDateTime::Set(int64) #ifdef WIN32 Second64Bit = 10000000, #else Second64Bit = 1000, #endif }; /// Returns true if all the components are in a valid range bool IsValid(); /// Sets the date to an NULL state void Empty(); /// Returns the day int Day() { return _Day; } /// Sets the day void Day(int d) { _Day = d; } /// Returns the month int Month() { return _Month; } /// Sets the month void Month(int m) { _Month = m; } /// Sets the month by it's name void Month(char *m); /// Returns the year int Year() { return _Year; } /// Sets the year void Year(int y) { _Year = y; } /// Returns the millisecond part of the time int Thousands() { return _Thousands; } /// Sets the millisecond part of the time void Thousands(int t) { _Thousands = t; } /// Returns the seconds part of the time int Seconds() { return _Seconds; } /// Sets the seconds part of the time void Seconds(int s) { _Seconds = s; } /// Returns the minutes part of the time int Minutes() { return _Minutes; } /// Sets the minutes part of the time void Minutes(int m) { _Minutes = m; } /// Returns the hours part of the time int Hours() { return _Hours; } /// Sets the hours part of the time void Hours(int h) { _Hours = h; } /// Returns the timezone of this current date time object in minutes (+10 = 600) int GetTimeZone() { return _Tz; } /// Returns the timezone in hours double GetTimeZoneHours() { return (double)_Tz / 60.0; } /// Sets the timezone of this current object.in minutes (+10 = 600) void SetTimeZone ( /// The new timezone int Tz, /// True if you want to convert the date and time to the new zone, /// False if you want to leave the date/time as it is. bool ConvertTime ); /// Set this object to UTC timezone, changing the other members as /// needed void ToUtc(bool AssumeLocal = false) { if (AssumeLocal) _Tz = SystemTimeZone(); SetTimeZone(0, true); } /// Changes the timezone to the local zone, changing other members /// as needed. void ToLocal(bool AssumeUtc = false) { if (AssumeUtc) _Tz = 0; SetTimeZone(SystemTimeZone(), true); } /// Gets the current formatting of the date, the format only effects /// the representation of the date when converted to/from a string. /// \returns a bit mask of (#GDTF_DAY_MONTH_YEAR or #GDTF_MONTH_DAY_YEAR or #GDTF_YEAR_MONTH_DAY) and (#GDTF_12HOUR or #GDTF_24HOUR) uint16 GetFormat() { return _Format; } /// Sets the current formatting of the date, the format only effects /// the representation of the date when converted to/from a string void SetFormat ( /// a bit mask of (#GDTF_DAY_MONTH_YEAR or #GDTF_MONTH_DAY_YEAR or #GDTF_YEAR_MONTH_DAY) and (#GDTF_12HOUR or #GDTF_24HOUR) uint16 f ) { _Format = f; } + + /// \returns zero based index of weekday, or -1 if not found. + static int IsWeekDay(const char *s); + /// \returns zero based index of month, or -1 if not found. + static int IsMonth(const char *s); /// The default format for the date when formatted as a string static uint16 GetDefaultFormat(); /// Sets the default format for the date when formatted as a string static void SetDefaultFormat(uint16 f) { DefaultFormat = f; } - /// Returns the day of the week as an index, 0=sun, 1=mon, 2=teus etc - int DayOfWeek(); - /// Gets the data and time as a GString GString Get(); /// Gets the date and time as a string /// \sa LDateTime::GetFormat() void Get(char *Str, size_t SLen); /// Gets the data and time as a 64 bit int (os specific) bool Get(uint64 &s); /// Gets just the date as a string /// \sa LDateTime::GetFormat() /// \returns The number of characters written to 'Str' int GetDate(char *Str, size_t SLen); /// Gets just the date as a GString /// \sa LDateTime::GetFormat() GString GetDate(); /// Gets just the time as a string /// \sa LDateTime::GetFormat() /// \returns The number of characters written to 'Str' int GetTime(char *Str, size_t SLen); /// Gets just the time as a GString /// \sa LDateTime::GetFormat() GString GetTime(); /// Returns the 64bit timestamp. uint64 Ts(); /// Sets the date and time to the system clock void SetNow(); /// Parses a date time from a string /// \sa LDateTime::GetFormat() bool Set(const char *Str); /// Sets the date and time from a 64 bit int (os specific) bool Set(uint64 s); /// Sets the time from a time_t bool Set(time_t tt); /// Parses the date from a string /// \sa LDateTime::GetFormat() bool SetDate(const char *Str); /// Parses the time from a string /// \sa LDateTime::GetFormat() bool SetTime(const char *Str); /// Parses the date time from a free form string bool Parse(GString s); + /// \returns true if 'd' is on the same day as this object + bool IsSameDay(LDateTime &d); + /// \returns true if 'd' is on the same month as this object + bool IsSameMonth(LDateTime &d); + /// \returns true if 'd' is on the same year as this object + bool IsSameYear(LDateTime &d); + /// \returns whether a year is a leap year or not bool IsLeapYear ( /// Pass a specific year here, or ignore to return if the current Date/Time is in a leap year. int Year = -1 ); - /// \returns true if 'd' is on the same day as this object - bool IsSameDay(LDateTime &d); - /// \returns zero based index of weekday, or -1 if not found. - static int IsWeekDay(const char *s); - /// \returns zero based index of month, or -1 if not found. - static int IsMonth(const char *s); + + /// Returns the day of the week as an index, 0=sun, 1=mon, 2=teus etc + int DayOfWeek(); /// \returns the number of days in the current month int DaysInMonth(); /// Adds a number of seconds to the current date/time void AddSeconds(int64 Seconds); /// Adds a number of minutes to the current date/time void AddMinutes(int64 Minutes); /// Adds a number of hours to the current date/time void AddHours(int64 Hours); /// Adds a number of days to the current date/time bool AddDays(int64 Days); /// Adds a number of months to the current date/time void AddMonths(int64 Months); /// The system timezone including daylight savings offset in minutes, +10 would return 600 static int SystemTimeZone(bool ForceUpdate = false); /// Any daylight savings offset applied to TimeZone(), in minutes. e.g. to retreive the /// timezone uneffected by DST use TimeZone() - TimeZoneOffset(). static int SystemTimeZoneOffset(); /// Daylight savings info record struct LgiClass GDstInfo { /// Timestamp where the DST timezone changes to 'Offset' uint64 UtcTimeStamp; /// The new offset in minutes (e.g. 600 = +10 hours) int Offset; LDateTime GetLocal(); }; /// Retreives daylight savings start and end events for a given period. One event will be emitted /// for the current DST/TZ setting at the datetime specified by 'Start', followed by any changes that occur /// between that and the 'End' datetime. To retreive just the DST info for start, use NULL for end. static bool GetDaylightSavingsInfo ( /// [Out] The array to receive DST info. At minimum one record will be returned /// matching the TZ in place for the start datetime. GArray &Out, /// [In] The start date that you want DST info for. LDateTime &Start, /// [Optional In] The end of the period you want DST info for. LDateTime *End = 0 ); /// Decodes an email date into the current instance bool Decode(const char *In); /// Returns a month index from a month name static int MonthFromName(const char *Name); // File int Sizeof(); bool Serialize(class GFile &f, bool Write); bool Serialize(class GDom *Props, char *Name, bool Write); // operators bool operator <(LDateTime &dt); bool operator <=(LDateTime &dt); bool operator >(LDateTime &dt); bool operator >=(LDateTime &dt); bool operator ==(LDateTime &dt); bool operator !=(LDateTime &dt); int Compare(const LDateTime *d); LDateTime operator -(LDateTime &dt); LDateTime operator +(LDateTime &dt); - LDateTime DiffMonths(LDateTime &dt); + int DiffMonths(LDateTime &dt); operator uint64() { uint64 ts = 0; Get(ts); return ts; } LDateTime &operator =(uint64 ts) { Set(ts); return *this; } LDateTime &operator =(struct tm *t); - + /// GDom interface. /// /// Even though we don't inherit from a GDom class this class supports the same /// interface for ease of use. Currently there are cases where LDateTime is used /// in GArray's which don't implement calling a constructor (they init with all /// zeros). bool GetVariant(const char *Name, class GVariant &Value, char *Array = NULL); bool SetVariant(const char *Name, class GVariant &Value, char *Array = NULL); bool CallMethod(const char *Name, class GVariant *ReturnValue, GArray &Args); }; /// Time zone information struct GTimeZone { public: /// The offset from UTC float Offset; /// The name of the zone const char *Text; }; /// A list of all known timezones. extern GTimeZone GTimeZones[]; #ifdef _DEBUG LgiFunc bool LDateTime_Test(); #endif #endif diff --git a/src/common/General/LDateTime.cpp b/src/common/General/LDateTime.cpp --- a/src/common/General/LDateTime.cpp +++ b/src/common/General/LDateTime.cpp @@ -1,2053 +1,2008 @@ /* ** FILE: LDateTime.cpp ** AUTHOR: Matthew Allen ** DATE: 11/11/98 ** DESCRIPTION: Scribe Date Time Object ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #define _INTEGRAL_MAX_BITS 64 #include #include #include #include #include #ifdef MAC #include #endif #ifdef WINDOWS #include #endif #include "Lgi.h" #include "LDateTime.h" #include "GToken.h" #include "GDocView.h" ////////////////////////////////////////////////////////////////////////////// uint16 LDateTime::DefaultFormat = GDTF_DEFAULT; char LDateTime::DefaultSeparator = '/'; uint16 LDateTime::GetDefaultFormat() { if (DefaultFormat == GDTF_DEFAULT) { #ifdef WIN32 TCHAR s[80] = _T("1"); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, s, CountOf(s)); switch (_tstoi(s)) { case 0: DefaultFormat = GDTF_MONTH_DAY_YEAR; break; default: case 1: DefaultFormat = GDTF_DAY_MONTH_YEAR; break; case 2: DefaultFormat = GDTF_YEAR_MONTH_DAY; break; } GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, s, sizeof(s)); if (_tstoi(s) == 1) { DefaultFormat |= GDTF_24HOUR; } else { DefaultFormat |= GDTF_12HOUR; } if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, s, sizeof(s))) DefaultSeparator = s[0]; if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s, sizeof(s))) { char Sep[] = { DefaultSeparator, '/', '\\', '-', '.', 0 }; GString Str = s; GToken t(Str, Sep); for (int i=0; i= low && (v) <= high) bool LDateTime::IsValid() { return InRange(_Day, 1, 31) && InRange(_Year, 1600, 2100) && InRange(_Thousands, 0, 999) && InRange(_Month, 1, 12) && InRange(_Seconds, 0, 59) && InRange(_Minutes, 0, 59) && InRange(_Hours, 0, 23) && InRange(_Tz, -720, 720); } void LDateTime::SetTimeZone(int NewTz, bool ConvertTime) { if (ConvertTime && NewTz != _Tz) { // printf("SetTimeZone: %i\n", NewTz - _Tz); AddMinutes(NewTz - _Tz); } _Tz = NewTz; } int LDateTime::SystemTimeZone(bool ForceUpdate) { if (ForceUpdate || CurTz == NO_ZONE) { CurTz = 0; CurTzOff = 0; #ifdef MAC #ifdef COCOA LgiAssert(!"Fixme"); #else CFTimeZoneRef tz = CFTimeZoneCopySystem(); CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); Boolean dst = CFTimeZoneIsDaylightSavingTime(tz, now); if (dst) { CFAbsoluteTime next = CFTimeZoneGetNextDaylightSavingTimeTransition(tz, now); CurTz = CFTimeZoneGetSecondsFromGMT(tz, next + 100) / 60; } else { CurTz = CFTimeZoneGetSecondsFromGMT(tz, now) / 60; } CurTzOff = CFTimeZoneGetDaylightSavingTimeOffset(tz, now) / 60; CFRelease(tz); #endif #elif defined(WIN32) timeb tbTime; ftime(&tbTime); CurTz = -tbTime.timezone; TIME_ZONE_INFORMATION Tzi; if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_DAYLIGHT) CurTzOff = -Tzi.DaylightBias; #elif defined(LINUX) int six_months = (365 * 24 * 60 * 60) / 2; time_t now = 0, then = 0; time (&now); then = now - six_months; tm now_tz, then_tz; tm *t = localtime_r(&now, &now_tz); if (t) { localtime_r(&then, &then_tz); CurTz = now_tz.tm_gmtoff / 60; if (now_tz.tm_isdst) { CurTzOff = (now_tz.tm_gmtoff - then_tz.tm_gmtoff) / 60; CurTz = then_tz.tm_gmtoff / 60; } else CurTzOff = (then_tz.tm_gmtoff - now_tz.tm_gmtoff) / 60; } else return NO_ZONE; #else #error "Impl me." #endif } return CurTz + CurTzOff; } int LDateTime::SystemTimeZoneOffset() { if (CurTz == NO_ZONE) SystemTimeZone(); return CurTzOff; } #if defined WIN32 LDateTime ConvertSysTime(SYSTEMTIME &st, int year) { LDateTime n; if (st.wYear) { n.Year(st.wYear); n.Month(st.wMonth); n.Day(st.wDay); } else { n.Year(year); n.Month(st.wMonth); // Find the 'nth' matching weekday, starting from the first day in the month n.Day(1); LDateTime c = n; for (int i=0; iCompare(b); } #elif defined POSIX static bool ParseValue(char *s, GAutoString &var, GAutoString &val) { if (!s) return false; char *e = strchr(s, '='); if (!e) return false; *e++ = 0; var.Reset(NewStr(s)); val.Reset(NewStr(e)); *e = '='; return var != 0 && val != 0; } #endif /* Testing code... LDateTime Start, End; GArray Info; Start.Set("1/1/2010"); End.Set("31/12/2014"); LDateTime::GetDaylightSavingsInfo(Info, Start, &End); GStringPipe p; for (int i=0; i { MonthHash() : GHashTbl(0, false) { Add("Jan", 1); Add("Feb", 2); Add("Mar", 3); Add("Apr", 4); Add("May", 5); Add("Jun", 6); Add("Jul", 7); Add("Aug", 8); Add("Sep", 9); Add("Oct", 10); Add("Nov", 11); Add("Dec", 12); Add("January", 1); Add("February", 2); Add("March", 3); Add("April", 4); Add("May", 5); Add("June", 6); Add("July", 7); Add("August", 8); Add("September", 9); Add("October", 10); Add("November", 11); Add("December", 12); } }; GString::Array Zdump; bool LDateTime::GetDaylightSavingsInfo(GArray &Info, LDateTime &Start, LDateTime *End) { bool Status = false; #if defined(WIN32) TIME_ZONE_INFORMATION Tzi; if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_DAYLIGHT) { Info.Length(0); // Find the DST->Normal date in the same year as Start LDateTime n = ConvertSysTime(Tzi.StandardDate, Start.Year()); // Find the Normal->DST date in the same year as Start LDateTime d = ConvertSysTime(Tzi.DaylightDate, Start.Year()); // Create initial Info entry Info[0].UtcTimeStamp = Start; bool IsDst = (n < d) ^ !(Start < n || Start > d); if (IsDst) // Start is DST Info[0].Offset = -(Tzi.Bias + Tzi.DaylightBias); else // Start is normal Info[0].Offset = -(Tzi.Bias + Tzi.StandardBias); if (End) { // Build list of DST change dates GArray c; c.Add(n); c.Add(d); for (int y = Start.Year() + 1; y <= End->Year(); y++) { // Calculate the dates for the following years if required c.Add(ConvertSysTime(Tzi.StandardDate, y)); c.Add(ConvertSysTime(Tzi.DaylightDate, y)); } c.Sort(GDateCmp); // Itererate over the list to generate further Info entries for (int i=0; i Start && dt < *End) { IsDst = !IsDst; GDstInfo &inf = Info.New(); if (IsDst) inf.Offset = -(Tzi.Bias + Tzi.DaylightBias); else inf.Offset = -(Tzi.Bias + Tzi.StandardBias); dt.SetTimeZone(inf.Offset, false); dt.SetTimeZone(0, true); inf.UtcTimeStamp = dt; } } } Status = true; } #elif defined(MAC) || defined(LINUX) if (!Zdump.Length()) { FILE *f = popen("zdump -v /etc/localtime", "r"); if (f) { char s[256]; size_t r; GStringPipe p(1024); while ((r = fread(s, 1, sizeof(s), f)) > 0) { p.Write(s, (int)r); } fclose(f); GString ps = p.NewGStr(); Zdump = ps.Split("\n"); } } MonthHash Lut; LDateTime Prev; int PrevOff = 0; for (int i=0; i= 16 && !stricmp(l[0], "/etc/localtime")) { // /etc/localtime Sat Oct 3 15:59:59 2037 UTC = Sun Oct 4 01:59:59 2037 EST isdst=0 gmtoff=36000 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LDateTime Utc; Utc.Year(atoi(l[5])); GToken Tm(l[4], ":"); if (Tm.Length() == 3) { Utc.Hours(atoi(Tm[0])); Utc.Minutes(atoi(Tm[1])); Utc.Seconds(atoi(Tm[2])); if (Utc.Minutes() == 0) { int m = Lut.Find(l[2]); if (m) { Utc.Day(atoi(l[3])); Utc.Month(m); GAutoString Var, Val; if (ParseValue(l[14], Var, Val) && !stricmp(Var, "isdst")) { // int IsDst = atoi(Val); if (ParseValue(l[15], Var, Val) && !stricmp(Var, "gmtoff")) { int Off = atoi(Val) / 60; if (Prev.Year() && Prev < Start && Start < Utc) { /* char Tmp[64]; Utc.Get(Tmp, sizeof(Tmp)); printf("[%i] Utc=%s\n", Info.Length(), Tmp); Prev.Get(Tmp, sizeof(Tmp)); printf("[%i] Prev=%s\n", Info.Length(), Tmp); Start.Get(Tmp, sizeof(Tmp)); printf("[%i] Start=%s\n", Info.Length(), Tmp); */ // Emit initial entry for 'start' Info[0].UtcTimeStamp = Start; Info[0].Offset = PrevOff; Status = true; } if (Utc > Start && End && Utc < *End) { // Emit furthur entries for DST events between start and end. GDstInfo &inf = Info.New(); inf.UtcTimeStamp = Utc; inf.Offset = Off; } Prev = Utc; PrevOff = Off; } else printf("%s:%i - Unknown value for isdst\n", _FL); } else printf("%s:%i - Unknown value for isdst\n", _FL); } else printf("%s:%i - Unknown month '%s'\n", _FL, l[2]); } // else printf("%s:%i - UTC min wrong %s.\n", _FL, l[4]); } else printf("%s:%i - Tm '%s' has wrong parts: %s\n", _FL, l[4], Line); } } #elif defined BEOS #else LgiAssert(!"Not implemented."); #endif return Status; } int LDateTime::DayOfWeek() { int Index = 0; int Day = IsLeapYear() ? 29 : 28; switch (_Year / 100) { case 19: { Index = 3; break; } case 20: { Index = 2; break; } } // get year right int y = _Year % 100; int r = y % 12; Index = (Index + (y / 12) + r + (r / 4)) % 7; // get month right if (_Month % 2 == 0) { // even month if (_Month > 2) Day = _Month; } else { // odd month switch (_Month) { case 1: { Day = 31; if (IsLeapYear()) { Index = Index > 0 ? Index - 1 : Index + 6; } break; } case 11: case 3: { Day = 7; break; } case 5: { Day = 9; break; } case 7: { Day = 11; break; } case 9: { Day = 5; break; } } } // get day right int Diff = Index - (Day - _Day); while (Diff < 0) Diff += 7; return Diff % 7; } void LDateTime::SetNow() { #ifdef WIN32 SYSTEMTIME stNow; FILETIME ftNow; GetSystemTime(&stNow); SystemTimeToFileTime(&stNow, &ftNow); uint64 i64 = ((uint64)ftNow.dwHighDateTime << 32) | ftNow.dwLowDateTime; Set(i64); #else time_t now; time(&now); struct tm *time = localtime(&now); if (time) *this = time; #ifndef LGI_STATIC else { LgiTrace("%s:%i - Error: localtime failed, now=%u\n", _FL, now); } #endif #endif } #define Convert24HrTo12Hr(h) ( (h) == 0 ? 12 : (h) > 12 ? (h) % 12 : (h) ) #define Convert24HrToAmPm(h) ( (h) >= 12 ? "p" : "a" ) GString LDateTime::GetDate() { char s[32]; int Ch = GetDate(s, sizeof(s)); return GString(s, Ch); } int LDateTime::GetDate(char *Str, size_t SLen) { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_DATE_MASK) { case GDTF_MONTH_DAY_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%2.2i" :"%i" , _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; default: case GDTF_DAY_MONTH_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%2.2i" :"%i" , _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; case GDTF_YEAR_MONTH_DAY: Ch += sprintf_s(Str+Ch, SLen-Ch, "%i", _Year); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); break; } } return Ch; } GString LDateTime::GetTime() { char s[32]; int Ch = GetTime(s, sizeof(s)); return GString(s, Ch); } int LDateTime::GetTime(char *Str, size_t SLen) { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_TIME_MASK) { case GDTF_12HOUR: default: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i%s", Convert24HrTo12Hr(_Hours), _Minutes, _Seconds, Convert24HrToAmPm(_Hours)); break; } case GDTF_24HOUR: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i", _Hours, _Minutes, _Seconds); break; } } } return Ch; } uint64 LDateTime::Ts() { uint64 ts; Get(ts); return ts; } bool LDateTime::Set(uint64 s) { #if defined WIN32 FILETIME Utc; SYSTEMTIME System; // Adjust to the desired timezone uint64 u = s + ((int64)_Tz * 60 * Second64Bit); Utc.dwHighDateTime = u >> 32; Utc.dwLowDateTime = u & 0xffffffff; if (FileTimeToSystemTime(&Utc, &System)) { _Year = System.wYear; _Month = System.wMonth; _Day = System.wDay; _Hours = System.wHour; _Minutes = System.wMinute; _Seconds = System.wSecond; _Thousands = System.wMilliseconds; return true; } return false; #else Set((time_t)(s / Second64Bit)); _Thousands = s % Second64Bit; return true; #endif } bool LDateTime::Set(time_t tt) { struct tm *t; #if !defined(_MSC_VER) || _MSC_VER < _MSC_VER_VS2005 t = localtime(&tt); if (t) #else struct tm tmp; if (_localtime64_s(t = &tmp, &tt) == 0) #endif { _Year = t->tm_year + 1900; _Month = t->tm_mon + 1; _Day = t->tm_mday; _Hours = t->tm_hour; _Minutes = t->tm_min; _Seconds = t->tm_sec; _Thousands = 0; _Tz = SystemTimeZone(); return true; } return false; } bool LDateTime::Get(uint64 &s) { #ifdef WIN32 FILETIME Utc; SYSTEMTIME System; System.wYear = _Year; System.wMonth = limit(_Month, 1, 12); System.wDay = limit(_Day, 1, 31); System.wHour = limit(_Hours, 0, 23); System.wMinute = limit(_Minutes, 0, 59); System.wSecond = limit(_Seconds, 0, 59); System.wMilliseconds = limit(_Thousands, 0, 999); System.wDayOfWeek = DayOfWeek(); BOOL b1, b2; if (b1 = SystemTimeToFileTime(&System, &Utc)) { // Convert to 64bit s = ((uint64)Utc.dwHighDateTime << 32) | Utc.dwLowDateTime; // Adjust for timezone s -= (int64)_Tz * 60 * Second64Bit; return true; } DWORD Err = GetLastError(); s = 0; LgiAssert(!"SystemTimeToFileTime failed."); return false; #else struct tm t; ZeroObj(t); t.tm_year = _Year - 1900; t.tm_mon = _Month - 1; t.tm_mday = _Day; t.tm_hour = _Hours; t.tm_min = _Minutes; t.tm_sec = _Seconds; t.tm_isdst = -1; /* mktime assumes input is in localtime. This is fine if CurTz == _Tz but if it's different we need to adjust the output to give the correct value. */ time_t sec = mktime(&t); if (sec == -1) return false; /* int CurTz = SystemTimeZone(); if (CurTz != _Tz) { // Adjust the output to the correct time zone.. int Diff = _Tz - CurTz; sec += Diff * 60; printf("Adjusting += %i (%i -> %i)\n", Diff * 60, CurTz, _Tz); } else printf("No Adjusting\n"); */ s = (uint64)sec * Second64Bit + _Thousands; return true; #endif } GString LDateTime::Get() { char buf[32]; int Ch = GetDate(buf, sizeof(buf)); buf[Ch++] = ' '; Ch += GetTime(buf+Ch, sizeof(buf)-Ch); return GString(buf, Ch); } void LDateTime::Get(char *Str, size_t SLen) { if (Str) { GetDate(Str, SLen); size_t len = strlen(Str); if (len < SLen - 1) { Str[len++] = ' '; GetTime(Str+len, SLen-len); } } } bool LDateTime::Set(const char *Str) { bool Status = false; if (Str) { char Local[256]; strcpy_s(Local, sizeof(Local), Str); char *Sep = strchr(Local, ' '); if (Sep) { *Sep++ = 0; Status |= SetTime(Sep); } Status |= SetDate(Local); } return Status; } void LDateTime::Month(char *m) { int i = IsMonth(m); if (i >= 0) _Month = i + 1; } bool LDateTime::SetDate(const char *Str) { bool Status = false; if (Str) { GToken T(Str, "/-.,_\\"); if (T.Length() == 3) { switch (_Format & GDTF_DATE_MASK) { case GDTF_MONTH_DAY_YEAR: { _Month = atoi(T[0]); _Day = atoi(T[1]); _Year = atoi(T[2]); break; } case GDTF_DAY_MONTH_YEAR: { _Day = atoi(T[0]); _Month = atoi(T[1]); _Year = atoi(T[2]); break; } case GDTF_YEAR_MONTH_DAY: { _Year = atoi(T[0]); _Month = atoi(T[1]); _Day = atoi(T[2]); break; } default: { int n[3] = { atoi(T[0]), atoi(T[1]), atoi(T[2]) }; if (n[0] > 1000) { // yyyy/m/d _Year = n[0]; _Month = n[1]; _Day = n[2]; } else if (n[2] > 1000) { _Year = n[2]; if (n[0] > 12) { // d/m/yyyy _Day = n[0]; _Month = n[1]; } else if (n[1] > 12) { // m/d/yyyy _Day = n[1]; _Month = n[0]; } else if ((DefaultFormat & GDTF_DATE_MASK) == GDTF_MONTH_DAY_YEAR) { // Assume m/d/yyyy _Day = n[1]; _Month = n[0]; } else { // Who knows??? // Assume d/m/yyyy _Day = n[0]; _Month = n[1]; } } break; } } if (_Year < 100) { if (_Year >= 80) { _Year += 1900; } else { _Year += 2000; } } Status = true; } else { // Fall back to fuzzy matching GToken T(Str, " ,"); MonthHash Lut; int FMonth = 0; int FDay = 0; int FYear = 0; for (unsigned i=0; i 0) { if (i >= 1000) { FYear = i; } else if (i < 32) { FDay = i; } } } else { int i = Lut.Find(p); if (i) FMonth = i; } } if (FMonth && FDay) { Day(FDay); Month(FMonth); } if (FYear) { Year(FYear); } else { LDateTime Now; Now.SetNow(); Year(Now.Year()); } } } return Status; } bool LDateTime::SetTime(const char *Str) { bool Status = false; if (Str) { GToken T(Str, ":."); if (T.Length() >= 2 && T.Length() <= 4) { _Hours = atoi(T[0]); _Minutes = atoi(T[1]); char *s = T[2]; if (s) _Seconds = atoi(s); else _Seconds = 0; s = T[T.Length()-1]; if (s) { if (strchr(s, 'p') || strchr(s, 'P')) { if (_Hours != 12) { _Hours += 12; } } else if (strchr(s, 'a') || strchr(s, 'A')) { if (_Hours == 12) { _Hours -= 12; } } } _Thousands = (T.Length() > 3) ? atoi(T[3]) : 0; Status = true; } } return Status; } int LDateTime::IsWeekDay(const char *s) { static const char *Short[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static const char *Long[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; for (unsigned n=0; n= 0) continue; int Mnth = IsMonth(c); if (Mnth >= 0) Month(Mnth + 1); } else if (*c == '-' || *c == '+') { c++; if (strlen(c) == 4) { // Timezone.. int64 Tz = a[i].Int(); int Hrs = (int) (Tz / 100); int Min = (int) (Tz % 100); SetTimeZone(Hrs * 60 + Min, false); } } } return IsValid(); } int LDateTime::Sizeof() { return sizeof(int) * 7; } bool LDateTime::Serialize(GFile &f, bool Write) { int32 i; if (Write) { #define wf(fld) i = fld; f << i; wf(_Day); wf(_Month); wf(_Year); wf(_Thousands); wf(_Seconds); wf(_Minutes); wf(_Hours); } else { #define rf(fld) f >> i; fld = i; rf(_Day); rf(_Month); rf(_Year); rf(_Thousands); rf(_Seconds); rf(_Minutes); rf(_Hours); } return true; } /* bool LDateTime::Serialize(ObjProperties *Props, char *Name, bool Write) { #ifndef LGI_STATIC if (Props && Name) { struct _Date { uint8 Day; uint8 Month; int16 Year; uint8 Hour; uint8 Minute; uint16 ThouSec; }; LgiAssert(sizeof(_Date) == 8); if (Write) { _Date d; d.Day = _Day; d.Month = _Month; d.Year = _Year; d.Hour = _Hours; d.Minute = _Minutes; d.ThouSec = (_Seconds * 1000) + _Thousands; return Props->Set(Name, &d, sizeof(d)); } else // Read { void *Ptr; int Len; if (Props->Get(Name, Ptr, Len) && sizeof(_Date) == Len) { _Date *d = (_Date*) Ptr; _Day = d->Day; _Month = d->Month; _Year = d->Year; _Hours = d->Hour; _Minutes = d->Minute; _Seconds = d->ThouSec / 1000; _Thousands = d->ThouSec % 1000; return true; } } } #endif return false; } */ int LDateTime::Compare(const LDateTime *d) { int c = 0; if (d) { c = _Year - d->_Year; if (!c) { c = _Month - d->_Month; if (!c) { c = _Day - d->_Day; if (!c) { c = _Hours - d->_Hours; if (!c) { c = _Minutes - d->_Minutes; if (!c) { c = _Seconds - d->_Seconds; if (!c) { c = _Thousands - d->_Thousands; } } } } } } } return c; } bool LDateTime::operator <(LDateTime &dt) { if (_Year < dt._Year) return true; else if (_Year > dt._Year) return false; if (_Month < dt._Month) return true; else if (_Month > dt._Month) return false; if (_Day < dt._Day) return true; else if (_Day > dt._Day) return false; if (_Hours < dt._Hours) return true; else if (_Hours > dt._Hours) return false; if (_Minutes < dt._Minutes) return true; else if (_Minutes > dt._Minutes) return false; if (_Seconds < dt._Seconds) return true; else if (_Seconds > dt._Seconds) return false; if (_Thousands < dt._Thousands) return true; else if (_Thousands > dt._Thousands) return false; return false; } bool LDateTime::operator <=(LDateTime &dt) { return !(*this > dt); } bool LDateTime::operator >(LDateTime &dt) { if (_Year > dt._Year) return true; else if (_Year < dt._Year) return false; if (_Month > dt._Month) return true; else if (_Month < dt._Month) return false; if (_Day > dt._Day) return true; else if (_Day < dt._Day) return false; if (_Hours > dt._Hours) return true; else if (_Hours < dt._Hours) return false; if (_Minutes > dt._Minutes) return true; else if (_Minutes < dt._Minutes) return false; if (_Seconds > dt._Seconds) return true; else if (_Seconds < dt._Seconds) return false; if (_Thousands > dt._Thousands) return true; else if (_Thousands < dt._Thousands) return false; return false; } bool LDateTime::operator >=(LDateTime &dt) { return !(*this < dt); } bool LDateTime::operator ==(LDateTime &dt) { return _Year == dt._Year && _Month == dt._Month && _Day == dt._Day && _Hours == dt._Hours && _Minutes == dt._Minutes && _Seconds == dt._Seconds && _Thousands == dt._Thousands; } bool LDateTime::operator !=(LDateTime &dt) { return _Year != dt._Year || _Month != dt._Month || _Day != dt._Day || _Hours != dt._Hours || _Minutes != dt._Minutes || _Seconds != dt._Seconds || _Thousands != dt._Thousands; } -LDateTime LDateTime::DiffMonths(LDateTime &dt) +int LDateTime::DiffMonths(LDateTime &dt) { - LDateTime s; - - int Months = 0; - s._Year = _Year; - s._Month = _Month; - s._Day = _Day; - - while (s._Year != dt._Year) - { - if (s._Year > dt._Year) - { - s._Year--; - Months += 12; - } - else - { - Months -= 12; - s._Year++; - } - } - - while (s._Month != dt._Month) - { - if - ( - s._Month > dt._Month + 1 - || - ( - s._Month > dt._Month - && - s._Day >= dt._Day - ) - ) - { - s._Month--; - Months++; - } - else if - ( - s._Month < dt._Month - 1 - || - ( - s._Month < dt._Month - && - s._Day <= dt._Day - ) - ) - { - Months--; - s._Month++; - } - else break; - } - - s._Month = Months; - s._Day = 0; - s._Year = 0; - - return s; + int a = (Year() * 12) + Month(); + int b = (dt.Year() * 12) + dt.Month(); + return b - a; } LDateTime LDateTime::operator -(LDateTime &dt) { uint64 a, b; Get(a); dt.Get(b); /// Resolution of a second when using 64 bit timestamps int64 Sec = Second64Bit; int64 Min = 60 * Sec; int64 Hr = 60 * Min; int64 Day = 24 * Hr; int64 d = (int64)a - (int64)b; LDateTime r; r._Day = d / Day; d -= r._Day * Day; r._Hours = d / Hr; d -= r._Hours * Hr; r._Minutes = d / Min; d -= r._Minutes * Min; r._Seconds = d / Sec; #ifdef WIN32 d -= r._Seconds * Sec; r._Thousands = d / 10000; #else r._Thousands = 0; #endif return r; } LDateTime LDateTime::operator +(LDateTime &dt) { LDateTime s = *this; s.AddMonths(dt.Month()); s.AddDays(dt.Day()); s.AddHours(dt.Hours()); s.AddMinutes(dt.Minutes()); // s.AddSeconds(dt.Seconds()); return s; } LDateTime &LDateTime::operator =(struct tm *time) { if (time) { _Seconds = time->tm_sec; _Minutes = time->tm_min; _Hours = time->tm_hour; _Day = time->tm_mday; _Month = time->tm_mon + 1; _Year = time->tm_year + 1900; } else Empty(); return *this; } bool LDateTime::IsSameDay(LDateTime &d) { return Day() == d.Day() && Month() == d.Month() && Year() == d.Year(); } +bool LDateTime::IsSameMonth(LDateTime &d) +{ + return Day() == d.Day() && + Month() == d.Month(); +} + +bool LDateTime::IsSameYear(LDateTime &d) +{ + return Year() == d.Year(); +} + bool LDateTime::IsLeapYear(int Year) { if (Year < 0) Year = _Year; if (Year % 4 != 0) { return false; } if (Year % 400 == 0) { return true; } if (Year % 100 == 0) { return false; } return true; } int LDateTime::DaysInMonth() { if (_Month == 2 && IsLeapYear()) { return 29; } short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return _Month >= 1 && _Month <= 12 ? DaysInMonth[_Month-1] : 0; } #define MinutesInDay (60*24) void LDateTime::AddSeconds(int64 Seconds) { uint64 i; if (Get(i)) { i += Seconds * Second64Bit; Set(i); } } void LDateTime::AddMinutes(int64 Minutes) { uint64 i; if (Get(i)) { int64 delta = Minutes * 60 * Second64Bit; uint64 n = i + delta; // printf("AddMin " LGI_PrintfInt64 " + " LGI_PrintfInt64 " = " LGI_PrintfInt64 "\n", i, delta, n); Set(n); } } void LDateTime::AddHours(int64 Hours) { uint64 i; if (Get(i)) { i += Hours * 3600 * Second64Bit; Set(i); } } bool LDateTime::AddDays(int64 Days) { if (!Days) return true; uint64 Ts; if (!Get(Ts)) return false; uint64 DayTicks = (uint64)LDateTime::Second64Bit * 60 * 60 * 24; Ts += Days * DayTicks; bool b = Set(Ts); return b; } void LDateTime::AddMonths(int64 Months) { int m = _Month + Months; do { if (m < 1) { _Year--; m += 12; } else if (m > 12) { _Year++; m -= 12; } else { break; } } while (1); _Month = m; if (_Day > DaysInMonth()) _Day = DaysInMonth(); } int LDateTime::MonthFromName(const char *Name) { if (Name) { const char *MonthName[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; for (int m=0; m<12; m++) { if (strnicmp(Name, MonthName[m], strlen(MonthName[m])) == 0) { return m + 1; break; } } } return -1; } bool LDateTime::Decode(const char *In) { // Test data: // // Tue, 6 Dec 2005 1:25:32 -0800 Empty(); if (!In) { LgiAssert(0); return false; } bool Status = false; // Tokenize delimited by whitespace GString::Array T = GString(In).SplitDelimit(); if (T.Length() >= 2) { bool GotDate = false; for (unsigned i=0; i 31) { // Y/M/D? Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else if (Date[2].Int() > 31) { // D/M/Y? Day((int)Date[0].Int()); Year((int)Date[2].Int()); } else { // Ambiguous year... bool YrFirst = true; if (Date[0].Length() == 1) YrFirst = false; // else we really can't tell.. just go with year first if (YrFirst) { Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else { Day((int)Date[0].Int()); Year((int)Date[2].Int()); } LDateTime Now; Now.SetNow(); if (Year() + 2000 <= Now.Year()) Year(2000 + Year()); else Year(1900 + Year()); } if (Date[1].IsNumeric()) Month((int)Date[1].Int()); else { int m = MonthFromName(Date[1]); if (m > 0) Month(m); } GotDate = true; Status = true; } else if (s.Find(":") >= 0) { // whole time GString::Array Time = s.Split(":"); if (Time.Length() == 2 || Time.Length() == 3) { // Hour Hours((int)Time[0].Int()); if (s.Lower().Find("p") >= 0) { if (Hours() < 12) Hours(Hours() + 12); } // Minute Minutes((int)Time[1].Int()); if (Time.Length() == 3) // Second Seconds((int)Time[2].Int()); Status = true; } } else if (IsAlpha(s(0))) { // text int m = MonthFromName(s); if (m > 0) Month(m); } else if (s.IsNumeric()) { int Count = 0; for (char *c = s; *c; c++) { if (!IsDigit(*c)) break; Count++; } if (Count <= 2) { if (Day()) { // We already have a day... so this might be // a 2 digit year... LDateTime Now; Now.SetNow(); int Yr = atoi(s); if (2000 + Yr <= Now.Year()) Year(2000 + Yr); else Year(1900 + Yr); } else { // A day number (hopefully)? Day((int)s.Int()); } } else if (Count == 4) { if (!Year()) { // A year! Year((int)s.Int()); Status = true; } else { goto DoTimeZone; } // My one and only Y2K fix // d.Year((Yr < 100) ? (Yr > 50) ? 1900+Yr : 2000+Yr : Yr); } } else if (strchr("+-", *s)) { // timezone DoTimeZone: LDateTime Now; double OurTmz = (double)Now.SystemTimeZone() / 60; if (s && strchr("-+", *s) && strlen(s) == 5) { #if 1 int i = atoi(s); int hr = i / 100; int min = i % 100; SetTimeZone(hr * 60 + min, false); #else // adjust for timezone char Buf[32]; memcpy(Buf, s, 3); Buf[3] = 0; double TheirTmz = atof(Buf); memcpy(Buf+1, s + 3, 2); TheirTmz += (atof(Buf) / 60); if (Tz) { *Tz = TheirTmz; } double AdjustHours = OurTmz - TheirTmz; AddMinutes((int) (AdjustHours * 60)); #endif } else { // assume GMT AddMinutes((int) (OurTmz * 60)); } } } } return Status; } bool LDateTime::GetVariant(const char *Name, GVariant &Dst, char *Array) { GDomProperty p = LgiStringToDomProp(Name); switch (p) { case DateYear: // Type: Int32 Dst = Year(); break; case DateMonth: // Type: Int32 Dst = Month(); break; case DateDay: // Type: Int32 Dst = Day(); break; case DateHour: // Type: Int32 Dst = Hours(); break; case DateMinute: // Type: Int32 Dst = Minutes(); break; case DateSecond: // Type: Int32 Dst = Seconds(); break; case DateDate: // Type: String { char s[32]; GetDate(s, sizeof(s)); Dst = s; break; } case DateTime: // Type: String { char s[32]; GetTime(s, sizeof(s)); Dst = s; break; } case DateDateAndTime: // Type: String { char s[32]; Get(s, sizeof(s)); Dst = s; break; } case DateTimestamp: // Type: Int64 { uint64 i = 0; Get(i); Dst = (int64)i; break; } default: { return false; } } return true; } bool LDateTime::SetVariant(const char *Name, GVariant &Value, char *Array) { GDomProperty p = LgiStringToDomProp(Name); switch (p) { case DateYear: Year(Value.CastInt32()); break; case DateMonth: Month(Value.CastInt32()); break; case DateDay: Day(Value.CastInt32()); break; case DateHour: Hours(Value.CastInt32()); break; case DateMinute: Minutes(Value.CastInt32()); break; case DateSecond: Seconds(Value.CastInt32()); break; case DateDate: SetDate(Value.Str()); break; case DateTime: SetTime(Value.Str()); break; case DateDateAndTime: Set(Value.Str()); break; case DateTimestamp: Set((uint64)Value.CastInt64()); break; default: return false; } return true; } bool LDateTime::CallMethod(const char *Name, GVariant *ReturnValue, GArray &Args) { switch (LgiStringToDomProp(Name)) { case DateSetNow: SetNow(); if (ReturnValue) *ReturnValue = true; break; case DateSetStr: if (Args.Length() < 1) return false; bool Status; if (Args[0]->Type == GV_INT64) Status = Set((uint64) Args[0]->Value.Int64); else Status = Set(Args[0]->Str()); if (ReturnValue) *ReturnValue = Status; break; case DateGetStr: { char s[256] = ""; Get(s, sizeof(s)); if (ReturnValue) *ReturnValue = s; break; } default: return false; } return true; } #ifdef _DEBUG #define DATE_ASSERT(i) \ if (!(i)) \ { \ LgiAssert(!"LDateTime unit test failed."); \ return false; \ } bool LDateTime_Test() { // Check 64bit get/set LDateTime t("1/1/2017 0:0:0"); uint64 i; DATE_ASSERT(t.Get(i)); LgiTrace("Get='%s'\n", t.Get().Get()); uint64 i2 = i + (24ULL * 60 * 60 * LDateTime::Second64Bit); LDateTime t2; t2.Set(i2); GString s = t2.Get(); LgiTrace("Set='%s'\n", s.Get()); DATE_ASSERT(!stricmp(s, "2/1/2017 12:00:00a") || !stricmp(s, "2/01/2017 12:00:00a")); t.SetNow(); LgiTrace("Now.Local=%s Tz=%.2f\n", t.Get().Get(), t.GetTimeZoneHours()); t2 = t; t2.ToUtc(); LgiTrace("Now.Utc=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); t2.ToLocal(); LgiTrace("Now.Local=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); DATE_ASSERT(t == t2); return true; } #endif