#include "ET++.ph" #ifdef __GNUG__ #pragma implementation #endif #include "Fields.h" #include "Class.h" #include "Look.h" #include "GapText.h" #include "Manager.h" #include "RegularExp.h" #include "Clipper.h" #include "TextView.h" #include "Alert_e.h" #include "Window.h" #include "Math.h" #include "ET_stdio.h" #include "String.h" //---- TextViewOverlay --------------------------------------------------------- static TextViewOverlay *gTVO; ONEXIT(Fields) { SafeDelete(gTVO); } TextViewOverlay::TextViewOverlay() { currentfield= 0; ct= 0; cl= 0; tv= 0; noupdate= FALSE; } TextViewOverlay::~TextViewOverlay() { currentfield= 0; if (ct) { Object *op; Iter next(ct->GetObserverIter()); while (op= next()) ct->RemoveObserver(op); SafeDelete(ct); } //SafeDelete(cl); SafeDelete(tv); } bool TextViewOverlay::CopyInStr(EditField *ef, char *buf, int sz) { if (ef == currentfield) { ct->CopyInStr((byte*)buf, sz, 0, ct->Size()); return FALSE; } return TRUE; } bool TextViewOverlay::Size(EditField *ef, int *sz) { if (ef == currentfield) { *sz= ct->End(); return FALSE; } return TRUE; } bool TextViewOverlay::KbdFocus(EditField *ef, bool in, bool wordwrap) { if (gDebug) fprintf(stderr, "TextViewOverlay::KbdFocus(0x%08x, %d) old:0x%08x\n", ef, in, currentfield); if (in) { if (currentfield != 0) { if (gDebug) fprintf(stderr, "KbdFocus(in): oops\n"); return FALSE; } if (ef) ef->Reveal(); const char *es= ef->GetStringForEditing(); currentfield= ef; currentfield->SetFlag(eEditFieldHasOverlay); if (ct == 0) { ct= GetOverlayText(es); } else { ct->ReplaceWithStr((byte*)es); cl->RevealRect(Rectangle(gPoint1), gPoint1); } //ct->SetFont(currentfield->GetFont(), 0, ct->Size()); ct->SetFont(currentfield->GetFont()); if (gToken.Code != eEvtLeftButton) tv->SelectAll(FALSE); ct->AddObserver(currentfield); cl->SetContainer(currentfield); tv->SetNextHandler(currentfield); //cl->Open(); if (wordwrap) tv->SetWordWrap(TRUE); currentfield->SetExtent(currentfield->GetExtent()); currentfield->SetOrigin(currentfield->GetOrigin()); currentfield->ForceRedraw(); } else { if (currentfield != ef) { if (gDebug) { fprintf(stderr, "KbdFocus(out): oops %d\n", currentfield); } return FALSE; } if (!Sync(currentfield)) return FALSE; tv->SetWordWrap(FALSE); currentfield->ForceRedraw(); cl->ClearContainer(currentfield); ct->RemoveObserver(currentfield); currentfield->ResetFlag(eEditFieldHasOverlay); currentfield= 0; } return tv->KbdFocus(in); } Text *TextViewOverlay::GetOverlayText(const char *s) { if (ct == 0) { if (s == 0) s= ""; ct= new GapText((byte*)s); tv= new TextView(currentfield, gFitRect, ct, TRUE); tv->SetDragAndDrop(FALSE); tv->SetFlag(eViewNoPrint); tv->SetStopChars("\r\n\t"); tv->ResetFlag(eVObjKbdFocus); cl= new Clipper(tv); cl->Open(); } return ct; } bool TextViewOverlay::Sync(EditField *ef) { if (currentfield == ef) { noupdate= TRUE; bool valid= currentfield->ValidateString(ct->AsString()); noupdate= FALSE; if (! valid) { ct->ReplaceWithStr((byte*)currentfield->GetStringForEditing()); cl->RevealRect(Rectangle(gPoint1), gPoint1); tv->SelectAll(FALSE); return FALSE; } return TRUE; } return FALSE; } bool TextViewOverlay::Draw(EditField *ef, Rectangle r) { if (currentfield == ef) { cl->DrawAll(r); return FALSE; } return TRUE; } void TextViewOverlay::Update(EditField *ef) { if (currentfield == ef && !noupdate) { ct->ReplaceWithStr((byte*)currentfield->GetStringForEditing()); //if (ct->GetFont() != currentfield->GetFont()) ct->SetFont(currentfield->GetFont()); cl->RevealRect(Rectangle(gPoint1), gPoint1); } } void TextViewOverlay::SetOrigin(EditField *ef, Point at) { if (currentfield == ef) cl->SetOrigin(at); } void TextViewOverlay::SetExtent(EditField *ef, Point e) { if (currentfield == ef) { cl->SetExtent(e); if (tv->GetWordWrap()) tv->SetExtent(Point(e.x, cFit)); } } Command *TextViewOverlay::DispatchEvents(EditField *ef, Point lp, Token &t, Clipper *vf) { if (currentfield == ef) return cl->Input(lp, t, vf); return 0; } void TextViewOverlay::DoSetupMenu(EditField *ef, Menu *m) { if (currentfield == ef) tv->DoSetupMenu(m); } Command *TextViewOverlay::DoMenuCommand(EditField *ef, int c) { if (currentfield == ef) return tv->DoMenuCommand(c); return gNoChanges; } //---- Field ------------------------------------------------------------------- NewMetaImpl(Field,VObject, (TP(font), T(minwidth))); Field::Field(int mw, Font *f) { Init(mw, f); } Field::Field(int id, int mw, Font *f) : VObject(id) { Init(mw, f); } void Field::Init(int mw, Font *f) { font= f; minwidth= mw; SetFlag(eVObjVFixed); } void Field::SetEditable(bool e) { SetFlag(eVObjKbdFocus, e); SetFlag(eEditFieldNoBorder, !e); } const char *Field::GetStringForDrawing() { return AsString(); } void Field::SetFont(Font *fp) { font= fp; } Metric Field::GetMinSize() { int b= (int)gLook->FieldBorderLayout(TestFlag(eEditFieldNoBorder))->GetValue(this, 0); return StaticTextView::MeasureText(GetStringForDrawing(), font, minwidth, GetLines()).Expand(gBorder+Point(b)); } void Field::Draw(Rectangle) { int b= (int)gLook->FieldBorderLayout(TestFlag(eEditFieldNoBorder))->GetValue(this, 0); Ink *ink= Enabled() ? gInkBlack : gLook->DisableInk(); StaticTextView::DrawBoxedText(contentRect.Inset(gBorder+Point(b)), GetStringForDrawing(), font, ink, GetLines() > 1); } int Field::Compare(Object *op) { return StrCmp(AsString(), Guard(op,Field)->AsString(), -1, gStdEncoding->SortMap()); } bool Field::IsEqual(Object *op) { if (op == 0 || ! op->IsKindOf(Field)) return FALSE; return StrCmp(AsString(), op->AsString(), -1, gStdEncoding->SortMap()) == 0; } int Field::GetLines() { return 1; } OStream& Field::PrintOn (OStream &s) { VObject::PrintOn(s); return s << font SP << minwidth SP; } IStream& Field::ReadFrom(IStream &s) { VObject::ReadFrom(s); return s >> font >> minwidth; } //---- EditField --------------------------------------------------------------- NewMetaImpl0(EditField,Field); EditField::EditField(int id, int mw, Font *f) : Field(id, mw, f) { SetFlag(eVObjEnabled | eVObjKbdFocus); } EditField::~EditField() { //if (HasOverlay()) // GetTextViewOverlay()->KbdFocus(this, FALSE, FALSE); } TextViewOverlay *EditField::GetTextViewOverlay() { Manager *m= (Manager*) FindNextHandlerOfClass(Meta(Manager)); if (m) return m->GetTextViewOverlay(); if (gDebug) { fprintf(stderr, "EditField::GetTextViewOverlay\n"); if (GetNextHandler()) fprintf(stderr, "GetNextHandler(): %s\n", GetNextHandler()->ClassName()); else fprintf(stderr, "GetNextHandler(): 0\n"); } if (gTVO == 0) gTVO= new TextViewOverlay; return gTVO; } void EditField::Enable(bool enable, bool redraw) { Field::Enable(enable, redraw); if (!enable) KbdFocus(enable); } const char *EditField::GetStringForEditing() { return AsString(); } void EditField::Draw(Rectangle r) { if (!HasOverlay() || GetTextViewOverlay()->Draw(this, r)) Field::Draw(r); gLook->FieldBorderLayout(TestFlag(eEditFieldNoBorder))->Adorn(this, r, HasOverlay()); } void EditField::Update(bool redraw) { if (HasOverlay()) GetTextViewOverlay()->Update(this); if (redraw) InvalidateRect(contentRect.Inset(gPoint4)); } void EditField::Sync() { if (HasOverlay()) GetTextViewOverlay()->Sync(this); } static bool lock; void EditField::DoSetupMenu(Menu *m) { if (HasOverlay() && !lock) { lock= TRUE; GetTextViewOverlay()->DoSetupMenu(this, m); lock= FALSE; } else Field::DoSetupMenu(m); } Command *EditField::DoMenuCommand(int c) { if (HasOverlay() && !lock) { lock= TRUE; Command *cmd= GetTextViewOverlay()->DoMenuCommand(this, c); lock= FALSE; return cmd; } return Field::DoMenuCommand(c); } Command *EditField::Input(Point lp, Token &t, Clipper *vf) { if (t.IsKey()) { if (t.Code == '\t') { Manager *m= (Manager*) FindNextHandlerOfClass(Meta(Manager)); if (m) m->TabFields(t); return gNoChanges; } if ((t.Code == '\r' || t.Code == '\n') && GetLines() <= 1) { Manager *m= (Manager*) FindNextHandlerOfClass(Meta(Manager)); if (m) m->TabFields(t); return gNoChanges; } } return Field::Input(lp, t, vf); } void EditField::SetOrigin(Point at) { Field::SetOrigin(at); if (HasOverlay()) { int b= (int)gLook->FieldBorderLayout(TestFlag(eEditFieldNoBorder))->GetValue(this, 0); GetTextViewOverlay()->SetOrigin(this, at+Point(b)); } } void EditField::SetExtent(Point e) { Field::SetExtent(e); if (HasOverlay()) { int b= (int)gLook->FieldBorderLayout(TestFlag(eEditFieldNoBorder))->GetValue(this, 0); GetTextViewOverlay()->SetExtent(this, e-Point(2*b)); } } bool EditField::KbdFocus(bool in) { if (HasOverlay() != in) return GetTextViewOverlay()->KbdFocus(this, in, GetLines() > 1); return TRUE; } void EditField::Control(int, int part, void *val) { switch (part) { case cPartViewSize: if (TestFlag(eEditFieldAutoSize)) { int b= (int)gLook->FieldBorderLayout(TestFlag(eEditFieldNoBorder))->GetValue(this, 0); Point ve(*((Point*)val)); SetExtent(ve+2*b); ExtentChanged(this); } break; case cPartSelectionChanged: case cPartOriginChanged: case cPartExtentChanged: case cPartScrollPos: break; case cPartChangedText: Field::Control(GetId(), part, val); break; default: Field::Control(GetId(), part, val); break; } } Command *EditField::DispatchEvents(Point lp, Token &t, Clipper *vf) { Command *cmd; if (HasOverlay() && (cmd= GetTextViewOverlay()->DispatchEvents(this, lp, t, vf))) return cmd; return Field::DispatchEvents(lp, t, vf); } bool EditField::ValidateString(const char*) { // called when content in TextView needs to be checked, eg. focus loss // override for validation, reformatting and completion of string // return FALSE if not acceptable return TRUE; } void EditField::Open(bool mode) { Field::Open(mode); } //---- TextField --------------------------------------------------------------- NewMetaImpl(TextField,EditField, (TP(text))); TextField::TextField(int id, int minWidthInChars, Font *f) : EditField(id, 0, f) { text= strsave(""); minwidth= font->Width('w') * minWidthInChars; } TextField::TextField(int id, const char *t, Font *f) : EditField(id, 0, f) { text= strsave(t ? t : ""); minwidth= font->Width((byte*)text); } TextField::TextField(int id, int minWidthInPixel, const char *t) : EditField(id, minWidthInPixel) { text= strsave(t ? t : ""); } TextField::~TextField() { SafeDelete(text); } const char *TextField::AsString() { return text; } void TextField::SetString(const char *t, bool redraw) { strreplace(&text, t); Update(redraw); Changed(); } void TextField::SetFString(bool redraw, const char *va_(fmt), ...) { va_list ap; va_start(ap,va_(fmt)); strvreplace(&text, va_(fmt), ap); Update(redraw); Changed(); va_end(ap); } void TextField::GetString(char *buf, int sz) { if (!HasOverlay() || GetTextViewOverlay()->CopyInStr(this, buf, sz)) { if (text) strn0cpy(buf, text, sz); else strcpy(buf, ""); } } const char *TextField::GetString() { Sync(); return AsString(); } int TextField::GetTextSize() { int sz= 0; if (!HasOverlay() || GetTextViewOverlay()->Size(this, &sz)) if (text) sz= strlen(text); return sz; } OStream& TextField::PrintOn (OStream &s) { EditField::PrintOn(s); return s.PrintString(text); } IStream& TextField::ReadFrom(IStream &s) { char *t; EditField::ReadFrom(s); s.ReadString(&t); SetString(t, FALSE); return s; } bool TextField::ValidateString(const char *s) { SetString(s, FALSE); Control(GetId(), cPartValueChanged, (void*) s); return TRUE; } //---- MultiLineField ---------------------------------------------------------- NewMetaImpl(MultiLineField,TextField, (T(lines))); MultiLineField::MultiLineField(int id, int l, int mw, const char *t) : TextField(id, mw, t) { lines= Math::Max(0, l); } int MultiLineField::GetLines() { return lines; } OStream& MultiLineField::PrintOn (OStream &s) { TextField::PrintOn(s); return s << lines SP; } IStream& MultiLineField::ReadFrom(IStream &s) { TextField::ReadFrom(s); return s >> lines; } //---- FormatField ------------------------------------------------------------- NewMetaImpl(FormatField,EditField, (TP(format))); FormatField::FormatField(int id, const char *fmt) : EditField(id) { format= strsave(fmt); } FormatField::~FormatField() { SafeDelete(format); } OStream& FormatField::PrintOn(OStream &s) { EditField::PrintOn(s); return s.PrintString(format); } IStream& FormatField::ReadFrom(IStream &s) { EditField::ReadFrom(s); SafeDelete(format); return s.ReadString(&format); } //---- IntField ---------------------------------------------------------------- NewMetaImpl(IntField,FormatField, (T(value), T(minVal), T(maxVal))); IntField::IntField(int id, int mi, int ma, const char *fmt) : FormatField(id, fmt) { Init(0, mi, ma); } IntField::IntField(int id, int val, int mi, int ma, const char *fmt) : FormatField(id, fmt) { Init(val, mi, ma); } void IntField::Init(int val, int mi, int ma) { minVal= mi; maxVal= ma; int lmin= font->Width((byte*)form(format, minVal)); int lmax= font->Width((byte*)form(format, maxVal)); minwidth= Math::Max(lmin, lmax); SetValue(val, FALSE); } const char *IntField::AsString() { return form(format, value); } const char *IntField::GetStringForDrawing() { return form(format, value); } int IntField::GetValue() { Sync(); return value; } bool IntField::SetValue(int newVal, bool redraw) { newVal= Math::Range(minVal, maxVal, newVal); if (newVal != value) { value= newVal; Update(redraw); return TRUE; } return FALSE; } void IntField::GetRange(int &mi, int &ma) { mi= minVal; ma= maxVal; } void IntField::SetRange(int mi, int ma) { minVal= mi; maxVal= ma; SetValue(value); } OStream& IntField::PrintOn(OStream &s) { FormatField::PrintOn(s); return s << value SP << minVal SP << maxVal SP; } IStream& IntField::ReadFrom(IStream &s) { int newval; FormatField::ReadFrom(s); s >> newval >> minVal >> maxVal; SetValue(newval, FALSE); return s; } bool IntField::ValidateString(const char *s) { int newval; char *b= strsave(s); int rc= sscanf(b, "%d", &newval); delete b; if (rc != 1) { ShowAlert(eAlertNote, "\"%s\" is not a number", b); return FALSE; } if (newval < minVal || newval > maxVal) { ShowAlert(eAlertNote, "%d not in range %d-%d", newval, minVal, maxVal); return FALSE; } if (SetValue(newval)) Control(GetId(), cPartValueChanged, &newval); return TRUE; } //---- FloatField -------------------------------------------------------------- NewMetaImpl(FloatField,FormatField, (T(value), T(minVal), T(maxVal))); FloatField::FloatField(int id, double mi, double ma, const char *fmt) : FormatField(id, fmt) { Init(0.0, mi, ma); } FloatField::FloatField(int id, double val, double mi, double ma, const char *fmt) : FormatField(id, fmt) { Init(val, mi, ma); } void FloatField::Init(double val, double mi, double ma) { minVal= mi; maxVal= ma; int lmin= font->Width((byte*)form(format, minVal)); int lmax= font->Width((byte*)form(format, maxVal)); minwidth= Math::Max(lmin, lmax); SetValue(val, FALSE); } const char *FloatField::AsString() { return form(format, value); } const char *FloatField::GetStringForDrawing() { return form(format, value); } double FloatField::GetValue() { Sync(); return value; } bool FloatField::SetValue(double newVal, bool redraw) { newVal= Math::Range(minVal, maxVal, newVal); if (newVal != value) { value= newVal; Update(redraw); return TRUE; } return FALSE; } void FloatField::GetRange(double &mi, double &ma) { mi= minVal; ma= maxVal; } void FloatField::SetRange(double mi, double ma) { minVal= mi; maxVal= ma; SetValue(value); } OStream& FloatField::PrintOn(OStream &s) { FormatField::PrintOn(s); return s << value SP << minVal SP << maxVal SP; } IStream& FloatField::ReadFrom(IStream &s) { double newval; FormatField::ReadFrom(s); s >> newval >> minVal >> maxVal; SetValue(newval, FALSE); return s; } bool FloatField::ValidateString(const char *s) { double newval; char *b= strsave(s); int rc= sscanf(b, "%lf", &newval); delete b; if (rc != 1) { ShowAlert(eAlertNote, "\"%s\" is not a number", s); return FALSE; } if (newval < minVal || newval > maxVal) { ShowAlert(eAlertNote, "%g not in range %g-%g", newval, minVal, maxVal); return FALSE; } if (SetValue(newval)) Control(GetId(), cPartValueChanged, &newval); return TRUE; } //---- RegExpField ------------------------------------------------------------- NewMetaImpl(RegExpField,TextField, (TP(regex), TP(errmsg))); RegExpField::RegExpField(int id, RegularExp *r, int mw, const char *msg, const char *t) : TextField(id, mw, t) { regex= r; errmsg= strsave(msg ? msg : "Illegal format \"%s\" "); } RegExpField::~RegExpField() { SafeDelete(regex); SafeDelete(errmsg); } bool RegExpField::ValidateString(const char *s) { TextField::ValidateString(s); if (strlen(s) && regex->Match(s) != strlen(s)) { ShowAlert(eAlertNote, errmsg, s); return FALSE; } return TRUE; } OStream& RegExpField::PrintOn(OStream &s) { TextField::PrintOn(s); s << regex SP; return s.PrintString(errmsg); } IStream& RegExpField::ReadFrom(IStream &s) { TextField::ReadFrom(s); s >> regex; SafeDelete(errmsg); return s.ReadString(&errmsg); }