1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "bspf.hxx"
19#include "Dialog.hxx"
20#include "DialogContainer.hxx"
21#include "EditTextWidget.hxx"
22#include "GuiObject.hxx"
23#include "OSystem.hxx"
24#include "FrameBuffer.hxx"
25#include "FBSurface.hxx"
26#include "Font.hxx"
27#include "Widget.hxx"
28#include "InputTextDialog.hxx"
29
30// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
31InputTextDialog::InputTextDialog(GuiObject* boss, const GUI::Font& font,
32 const StringList& labels, const string& title)
33 : Dialog(boss->instance(), boss->parent(), font, title),
34 CommandSender(boss),
35 myEnableCenter(false),
36 myErrorFlag(false),
37 myXOrig(0),
38 myYOrig(0)
39{
40 initialize(font, font, labels);
41}
42
43// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
44InputTextDialog::InputTextDialog(GuiObject* boss, const GUI::Font& lfont,
45 const GUI::Font& nfont,
46 const StringList& labels, const string& title)
47 : Dialog(boss->instance(), boss->parent(), lfont, title),
48 CommandSender(boss),
49 myEnableCenter(false),
50 myErrorFlag(false),
51 myXOrig(0),
52 myYOrig(0)
53{
54 initialize(lfont, nfont, labels);
55}
56
57// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
58void InputTextDialog::initialize(const GUI::Font& lfont, const GUI::Font& nfont,
59 const StringList& labels)
60{
61 const int fontWidth = lfont.getMaxCharWidth(),
62 fontHeight = lfont.getFontHeight(),
63 lineHeight = lfont.getLineHeight();
64 uInt32 xpos, ypos, i, lwidth = 0, maxIdx = 0;
65 WidgetArray wid;
66
67 // Calculate real dimensions
68 _w = fontWidth * 41;
69 _h = lineHeight * 4 + int(labels.size()) * (lineHeight + 5) + _th;
70
71 // Determine longest label
72 for(i = 0; i < labels.size(); ++i)
73 {
74 if(labels[i].length() > lwidth)
75 {
76 lwidth = int(labels[i].length());
77 maxIdx = i;
78 }
79 }
80 lwidth = lfont.getStringWidth(labels[maxIdx]);
81
82 // Create editboxes for all labels
83 ypos = lineHeight + _th;
84 for(i = 0; i < labels.size(); ++i)
85 {
86 xpos = 10;
87 new StaticTextWidget(this, lfont, xpos, ypos + 2,
88 lwidth, fontHeight,
89 labels[i], TextAlign::Left);
90
91 xpos += lwidth + fontWidth;
92 EditTextWidget* w = new EditTextWidget(this, nfont, xpos, ypos,
93 _w - xpos - 10, lineHeight, "");
94 wid.push_back(w);
95
96 myInput.push_back(w);
97 ypos += lineHeight + 5;
98 }
99
100 xpos = 10;
101 myMessage = new StaticTextWidget(this, lfont, xpos, ypos, _w - 2*xpos, fontHeight,
102 "", TextAlign::Left);
103 myMessage->setTextColor(kTextColorEm);
104
105 addToFocusList(wid);
106
107 // Add OK and Cancel buttons
108 wid.clear();
109 addOKCancelBGroup(wid, lfont);
110 addBGroupToFocusList(wid);
111}
112
113// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114void InputTextDialog::show()
115{
116 myEnableCenter = true;
117 open();
118}
119
120// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121void InputTextDialog::show(uInt32 x, uInt32 y, const Common::Rect& bossRect)
122{
123 uInt32 scale = instance().frameBuffer().hidpiScaleFactor();
124 myXOrig = bossRect.x() + x * scale;
125 myYOrig = bossRect.y() + y * scale;
126
127 // Only show dialog if we're inside the visible area
128 if(!bossRect.contains(myXOrig, myYOrig))
129 return;
130
131 myEnableCenter = false;
132 open();
133}
134
135// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
136void InputTextDialog::center()
137{
138 if(!myEnableCenter)
139 {
140 // First set position according to original coordinates
141 surface().setDstPos(myXOrig, myYOrig);
142
143 // Now make sure that the entire menu can fit inside the screen bounds
144 // If not, we reset its position
145 if(!instance().frameBuffer().screenRect().contains(
146 myXOrig, myXOrig, surface().dstRect()))
147 surface().setDstPos(myXOrig, myYOrig);
148 }
149 else
150 Dialog::center();
151}
152
153// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
154void InputTextDialog::setMessage(const string& title)
155{
156 myMessage->setLabel(title);
157 myErrorFlag = true;
158}
159
160// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
161const string& InputTextDialog::getResult(int idx)
162{
163 if(uInt32(idx) < myInput.size())
164 return myInput[idx]->getText();
165 else
166 return EmptyString;
167}
168
169// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
170void InputTextDialog::setText(const string& str, int idx)
171{
172 if(uInt32(idx) < myInput.size())
173 myInput[idx]->setText(str);
174}
175
176// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177void InputTextDialog::setTextFilter(const EditableWidget::TextFilter& f, int idx)
178{
179 if(uInt32(idx) < myInput.size())
180 myInput[idx]->setTextFilter(f);
181}
182
183// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184void InputTextDialog::setFocus(int idx)
185{
186 if(uInt32(idx) < myInput.size())
187 Dialog::setFocus(getFocusList()[idx]);
188}
189
190// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191void InputTextDialog::handleCommand(CommandSender* sender, int cmd,
192 int data, int id)
193{
194 switch(cmd)
195 {
196 case GuiObject::kOKCmd:
197 case EditableWidget::kAcceptCmd:
198 {
199 // Send a signal to the calling class that a selection has been made
200 // Since we aren't derived from a widget, we don't have a 'data' or 'id'
201 if(myCmd)
202 sendCommand(myCmd, 0, 0);
203
204 // We don't close, but leave the parent to do it
205 // If the data isn't valid, the parent may wait until it is
206 break;
207 }
208
209 case EditableWidget::kChangedCmd:
210 // Erase the invalid message once editing is restarted
211 if(myErrorFlag)
212 {
213 myMessage->setLabel("");
214 myErrorFlag = false;
215 }
216 break;
217
218 case EditableWidget::kCancelCmd:
219 Dialog::handleCommand(sender, GuiObject::kCloseCmd, data, id);
220 break;
221
222 default:
223 Dialog::handleCommand(sender, cmd, data, id);
224 break;
225 }
226}
227