1 //CUIWnd.cpp
2 
3 #include "CUIWnd.h"
4 
5 #include "../client/human/HumanClientApp.h"
6 #include "ClientUI.h"
7 #include "CUIControls.h"
8 #include "Sound.h"
9 #include "../util/i18n.h"
10 #include "../util/Logger.h"
11 #include "../util/OptionsDB.h"
12 #include "../util/Directories.h"
13 #include "../util/Logger.h"
14 
15 #include <GG/GUI.h>
16 
17 #include <limits>
18 #include <boost/algorithm/string.hpp>
19 
20 
21 namespace {
PlayMinimizeSound()22     void PlayMinimizeSound()
23     { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.window.maximize.sound.path"), true); }
PlayMaximizeSound()24     void PlayMaximizeSound()
25     { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.window.minimize.sound.path"), true); }
PlayCloseSound()26     void PlayCloseSound()
27     { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.window.close.sound.path"), true); }
28 
AddOptions(OptionsDB & db)29     void AddOptions(OptionsDB& db) {
30         db.AddFlag('w', "window-reset", UserStringNop("OPTIONS_DB_WINDOW_RESET"), false);
31     }
32     bool temp_bool = RegisterOptions(&AddOptions);
33 
34     const double BUTTON_DIMMING_SCALE_FACTOR = 0.75;
35 
36     const GG::X::value_type INVALID_POS = std::numeric_limits<GG::X::value_type>::min();
37     const GG::X INVALID_X = GG::X(INVALID_POS);
38     const GG::Y INVALID_Y = GG::Y(INVALID_POS);
39 }
40 
41 ////////////////////////////////////////////////
42 // CUI_MinRestoreButton
43 ////////////////////////////////////////////////
CUI_MinRestoreButton()44 CUI_MinRestoreButton::CUI_MinRestoreButton() :
45     GG::Button("", nullptr, ClientUI::WndInnerBorderColor()),
46     m_mode(Mode::MINIMIZE)
47 {
48     LeftClickedSignal.connect(boost::bind(&CUI_MinRestoreButton::Toggle, this));
49 }
50 
Render()51 void CUI_MinRestoreButton::Render() {
52     GG::Pt ul = UpperLeft();
53     GG::Pt lr = LowerRight();
54     GG::Clr color_to_use = ClientUI::WndInnerBorderColor();
55     if (State() != BN_ROLLOVER)
56         AdjustBrightness(color_to_use, BUTTON_DIMMING_SCALE_FACTOR);
57     if (m_mode == Mode::MINIMIZE) {
58         // draw a dash to signify the minimize command
59         GG::Y middle_y = (lr.y + ul.y) / 2;
60         glColor(color_to_use);
61         GG::Line(ul.x, middle_y, lr.x, middle_y);
62     } else {
63         // draw a square to signify the restore command
64         GG::FlatRectangle(ul, lr, GG::CLR_ZERO, ClientUI::WndInnerBorderColor(), 1);
65     }
66 }
67 
Toggle()68 void CUI_MinRestoreButton::Toggle() {
69     if (m_mode == Mode::MINIMIZE) {
70         PlayMinimizeSound();
71         m_mode = Mode::RESTORE;
72     } else {
73         PlayMaximizeSound();
74         m_mode = Mode::MINIMIZE;
75     }
76 }
77 
78 
79 ////////////////////////////////////////////////
80 // CUI_PinButton
81 ////////////////////////////////////////////////
CUI_PinButton()82 CUI_PinButton::CUI_PinButton() :
83     GG::Button("", nullptr, ClientUI::WndInnerBorderColor())
84 {
85     LeftClickedSignal.connect(-1,
86         &PlayCloseSound);
87     SetUnpressedGraphic(GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png"   )));
88     SetPressedGraphic  (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png"  )));
89     SetRolloverGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin_mouseover.png")));
90 }
91 
Toggle(bool pinned)92 void CUI_PinButton::Toggle(bool pinned) {
93     if (!pinned) {
94         SetUnpressedGraphic(GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png")));
95         SetPressedGraphic  (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png")));
96         SetRolloverGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin_mouseover.png")));
97     } else {
98         SetUnpressedGraphic(GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pinned.png")));
99         SetPressedGraphic  (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pinned.png")));
100         SetRolloverGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pinned_mouseover.png")));
101     }
102 }
103 
104 ////////////////////////////////////////////////
105 // CUIWnd
106 ////////////////////////////////////////////////
107 GG::WndFlag MINIMIZABLE(1 << 10);
108 GG::WndFlag CLOSABLE(1 << 11);
109 GG::WndFlag PINABLE(1 << 12);
110 
111 namespace {
RegisterWndFlags()112     bool RegisterWndFlags() {
113         GG::FlagSpec<GG::WndFlag>::instance().insert(MINIMIZABLE, "MINIMIZABLE");
114         GG::FlagSpec<GG::WndFlag>::instance().insert(CLOSABLE, "CLOSABLE");
115         GG::FlagSpec<GG::WndFlag>::instance().insert(PINABLE, "PINABLE");
116         return true;
117     }
118     bool dummy = RegisterWndFlags();
119 
WindowNameFromOption(const std::string & option_name)120     std::string WindowNameFromOption(const std::string& option_name) {
121         std::string::size_type prefix_len { std::string("ui.").length() };
122 
123         // Determine end of window name from start of window mode
124         std::string::size_type mode_substr_pos { option_name.find(".fullscreen", prefix_len + 1) };
125         if (mode_substr_pos == std::string::npos) {
126             mode_substr_pos = option_name.find(".windowed", prefix_len + 1);
127             if (mode_substr_pos == std::string::npos) {
128                 ErrorLogger() << "Could not determine window name from option " << option_name;
129                 return {};
130             }
131         }
132 
133         std::string::size_type name_len = mode_substr_pos - prefix_len;
134         return option_name.substr(prefix_len, name_len);
135     }
136 }
137 
138 const GG::Y CUIWnd::BUTTON_TOP_OFFSET(3);
139 const GG::X CUIWnd::MINIMIZED_WND_WIDTH(50);
140 const GG::X CUIWnd::BORDER_LEFT(5);
141 const GG::X CUIWnd::BORDER_RIGHT(5);
142 const GG::Y CUIWnd::BORDER_BOTTOM(5);
143 const int CUIWnd::OUTER_EDGE_ANGLE_OFFSET = 8;
144 const int CUIWnd::INNER_BORDER_ANGLE_OFFSET = 15;
145 const int CUIWnd::TITLE_OFFSET = 2;
146 const int CUIWnd::RESIZE_HASHMARK1_OFFSET = 9;
147 const int CUIWnd::RESIZE_HASHMARK2_OFFSET = 4;
148 
CUIWnd(const std::string & wnd_name,GG::X x,GG::Y y,GG::X w,GG::Y h,GG::Flags<GG::WndFlag> flags,const std::string & config_name,bool visible)149 CUIWnd::CUIWnd(const std::string& wnd_name,
150                GG::X x, GG::Y y,
151                GG::X w, GG::Y h,
152                GG::Flags<GG::WndFlag> flags,
153                const std::string& config_name,
154                bool visible) :
155     GG::Wnd(x, y, w, h, flags & ~GG::RESIZABLE),
156     m_resizable(flags & GG::RESIZABLE),
157     m_closable(flags & CLOSABLE),
158     m_minimizable(flags & MINIMIZABLE),
159     m_pinable(flags & PINABLE),
160     m_drag_offset(-GG::X1, -GG::Y1),
161     m_config_name(AddWindowOptions(config_name, x, y, w, h, visible, false, false))
162 {
163     SetName(wnd_name);
164     if (!m_config_name.empty()) {
165         // Default position was already supplied
166         GetOptionsDB().Set<bool>("ui." + m_config_name + ".initialized", true);
167     }
168 }
169 
CUIWnd(const std::string & wnd_name,GG::Flags<GG::WndFlag> flags,const std::string & config_name,bool visible)170 CUIWnd::CUIWnd(const std::string& wnd_name, GG::Flags<GG::WndFlag> flags, const std::string& config_name, bool visible) :
171     GG::Wnd(INVALID_X, INVALID_Y, GG::X1, GG::Y1, flags & ~GG::RESIZABLE),
172     m_resizable(flags & GG::RESIZABLE),
173     m_closable(flags & CLOSABLE),
174     m_minimizable(flags & MINIMIZABLE),
175     m_pinable(flags & PINABLE),
176     m_drag_offset(-GG::X1, -GG::Y1),
177     m_config_name(AddWindowOptions(config_name, INVALID_POS, INVALID_POS, 1, 1, visible, false, false))
178 { SetName(wnd_name); }
179 
CompleteConstruction()180 void CUIWnd::CompleteConstruction() {
181     GG::Wnd::CompleteConstruction();
182     Init();
183     ValidatePosition();
184     SetDefaultedOptions();
185 }
186 
Init()187 void CUIWnd::Init() {
188     InitButtons();
189     SetChildClippingMode(ClipToClientAndWindowSeparately);
190 
191     if (!m_config_name.empty()) {
192         LoadOptions();
193         HumanClientApp::GetApp()->FullscreenSwitchSignal.connect(
194             boost::bind(&CUIWnd::LoadOptions, this));
195     }
196 
197     // User-dragable windows recalculate their position only when told to (e.g.
198     // auto-reposition is set or user clicks a 'reset windows' button).
199     // Non-user-dragable windows are given the chance to position themselves on
200     // every resize event.
201     if (Dragable() || m_resizable)
202         HumanClientApp::GetApp()->RepositionWindowsSignal.connect(
203             boost::bind(&CUIWnd::ResetDefaultPosition, this));
204     else
205         HumanClientApp::GetApp()->WindowResizedSignal.connect(
206             boost::bind(&CUIWnd::ResetDefaultPosition, this));
207 }
208 
InitSizeMove(const GG::Pt & ul,const GG::Pt & lr)209 void CUIWnd::InitSizeMove(const GG::Pt& ul, const GG::Pt& lr) {
210     OptionsDB& db = GetOptionsDB();
211 
212     if (!m_config_name.empty()) {
213         std::string option_prefix = "ui." + m_config_name;
214         if (db.OptionExists(option_prefix + ".initialized")) {
215             std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
216                                       ".fullscreen" : ".windowed";
217             // If the window has already had its default position specified
218             // (either in the ctor or a previous call to this function), apply
219             // this position to the window.
220             if (db.Get<bool>(option_prefix + ".initialized") ||
221                 db.Get<int>(option_prefix + window_mode + ".left") == INVALID_X)
222             {
223                 SetDefaultedOptions();
224                 SizeMove(ul, lr);
225                 SaveDefaultedOptions();
226             }
227             db.Set<bool>(option_prefix + ".initialized", true);
228         } else {
229             ErrorLogger() << "CUIWnd::InitSizeMove() : attempted to check if window using name \"" << m_config_name
230                           << "\" was initialized but the options do not appear to be registered in the OptionsDB.";
231         }
232     } else {
233         SizeMove(ul, lr);
234     }
235 }
236 
~CUIWnd()237 CUIWnd::~CUIWnd() {
238     try {
239         if (!m_config_name.empty() && GetOptionsDB().OptionExists("ui." + m_config_name + ".initialized"))
240             GetOptionsDB().Remove("ui." + m_config_name + ".initialized");
241     } catch (std::exception& e) { // catch std::runtime_error, boost::bad_any_cast
242         ErrorLogger() << "CUIWnd::~CUIWnd() : caught exception while removing \"ui." << m_config_name
243                       << ".initialized\": " << e.what();
244     }
245     m_vertex_buffer.clear();
246 }
247 
ValidatePosition()248 void CUIWnd::ValidatePosition()
249 { SizeMove(RelativeUpperLeft(), RelativeLowerRight()); }
250 
SizeMove(const GG::Pt & ul,const GG::Pt & lr)251 void CUIWnd::SizeMove(const GG::Pt& ul, const GG::Pt& lr) {
252     GG::Pt old_sz = Size();
253     if (m_config_save) {    // can write position/size to OptionsDB
254 
255         GG::Pt available_size;
256         if (const auto&& parent = Parent()) {
257             // Keep this CUIWnd entirely inside its parent.
258             available_size = parent->ClientSize();
259         } else if (const HumanClientApp* app = HumanClientApp::GetApp()) {
260             // Keep this CUIWnd entirely inside the application window.
261             available_size = GG::Pt(app->AppWidth(), app->AppHeight());
262         } else {
263             available_size = GG::Pt(GG::X(HumanClientApp::MaximumPossibleWidth()),
264                                     GG::Y(HumanClientApp::MaximumPossibleHeight()));
265             ErrorLogger() << "CUIWnd::SizeMove() could not get app instance!";
266         }
267 
268         // Limit window size to be no larger than the containing window.
269         GG::Pt new_size(std::max(std::min(lr.x - ul.x, available_size.x), MinimizedSize().x),
270                         std::max(std::min(lr.y - ul.y, available_size.y),
271                                  TopBorder() + INNER_BORDER_ANGLE_OFFSET + BORDER_BOTTOM + 50));
272 
273         // Clamp position of this window to keep its entire area visible in the
274         // containing window.
275         GG::Pt new_ul(std::min(available_size.x - new_size.x, std::max(GG::X0, ul.x)),
276                       std::min(available_size.y - new_size.y, std::max(GG::Y0, ul.y)));
277 
278         Wnd::SizeMove(new_ul, new_ul + new_size);
279 
280     } else {    // don't write position/size to OptionsDB
281         Wnd::SizeMove(ul, lr);
282     }
283 
284     if (Size() != old_sz)
285         PositionButtons();
286 
287     SaveOptions();
288     m_vertex_buffer.clear();    // force buffer re-init on next Render call
289 }
290 
Render()291 void CUIWnd::Render() {
292     if (m_vertex_buffer.empty())
293         InitBuffers();
294 
295     glDisable(GL_TEXTURE_2D);
296     glLineWidth(1.0f);
297 
298     glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
299 
300     glEnableClientState(GL_VERTEX_ARRAY);
301     glDisableClientState(GL_COLOR_ARRAY);
302     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
303 
304     m_vertex_buffer.activate();
305 
306     // within m_vertex_buffer:
307     // [0] is the start and range for minimized background triangle fan and minimized border line loop
308     // [1] is ... the background fan / outer border line loop
309     // [2] is ... the inner border line loop
310     // [3] is ... the resize tab line list
311 
312     if (m_minimized) {
313         glColor(ClientUI::WndColor());
314         glDrawArrays(GL_TRIANGLE_FAN,   m_buffer_indices[0].first, m_buffer_indices[0].second);
315         glColor(ClientUI::WndOuterBorderColor());
316         glDrawArrays(GL_LINE_LOOP,      m_buffer_indices[0].first, m_buffer_indices[0].second);
317 
318     } else {
319         bool flashing = m_flashing && static_cast<int>(GG::GUI::GetGUI()->Ticks()) % (m_flash_duration * 2) > m_flash_duration;
320         auto focus_wnd = GG::GUI::GetGUI()->FocusWnd();
321         bool highlight = (focus_wnd.get() == this || this->IsAncestorOf(focus_wnd));
322 
323         flashing ? glColor(GG::LightenClr(ClientUI::WndColor())) : glColor(ClientUI::WndColor());
324         glDrawArrays(GL_TRIANGLE_FAN,   m_buffer_indices[1].first, m_buffer_indices[1].second);
325         flashing || highlight ? glColor(GG::LightenClr(ClientUI::WndOuterBorderColor())) : glColor(ClientUI::WndOuterBorderColor());
326         glDrawArrays(GL_LINE_LOOP,      m_buffer_indices[1].first, m_buffer_indices[1].second);
327         flashing ? glColor(GG::LightenClr(ClientUI::WndInnerBorderColor())) : glColor(ClientUI::WndInnerBorderColor());
328         glDrawArrays(GL_LINE_LOOP,      m_buffer_indices[2].first, m_buffer_indices[2].second);
329 
330         if (m_resizable) {
331             GG::Clr tab_lines_colour = m_mouse_in_resize_tab ? ClientUI::WndInnerBorderColor() : ClientUI::WndOuterBorderColor();
332             glColor(tab_lines_colour);
333             glDrawArrays(GL_LINES,      m_buffer_indices[3].first, m_buffer_indices[3].second);
334         }
335     }
336 
337     glEnable(GL_TEXTURE_2D);
338 
339     glPopClientAttrib();
340 
341     GG::Pt ul = UpperLeft();
342     GG::Pt lr = LowerRight();
343     GG::BeginScissorClipping(ul, lr);
344     glColor(ClientUI::TextColor());
345     std::shared_ptr<GG::Font> font = ClientUI::GetTitleFont();
346     font->RenderText(GG::Pt(ul.x + BORDER_LEFT, ul.y + TITLE_OFFSET), Name());
347     GG::EndScissorClipping();
348 }
349 
LButtonDown(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)350 void CUIWnd::LButtonDown(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
351     if (!InResizeTab(pt))
352         return;
353     m_drag_offset = pt - LowerRight();
354 }
355 
InResizeTab(const GG::Pt & pt) const356 bool CUIWnd::InResizeTab(const GG::Pt& pt) const {
357     if (!m_resizable || m_minimized)
358         return false;
359 
360     GG::Pt cl_lr = LowerRight() - GG::Pt(BORDER_RIGHT, BORDER_BOTTOM);
361     GG::Pt dist_from_lr = cl_lr - pt;
362     if (Value(dist_from_lr.x) + Value(dist_from_lr.y) <= INNER_BORDER_ANGLE_OFFSET)
363         return true;
364 
365     return false;
366 }
367 
LDrag(const GG::Pt & pt,const GG::Pt & move,GG::Flags<GG::ModKey> mod_keys)368 void CUIWnd::LDrag(const GG::Pt& pt, const GG::Pt& move, GG::Flags<GG::ModKey> mod_keys) {
369     if (m_pinned)
370         return;
371 
372     if (m_drag_offset != GG::Pt(-GG::X1, -GG::Y1)) { // resize-dragging
373         // drag offset: position of cursor relative to lower-right of window when left button was pressed
374         // pt: position of cursor relative to upper-left of screen
375         GG::Pt requested_lr = pt - m_drag_offset;
376 
377         GG::Pt max_lr;
378         if (const auto&& parent = Parent()) {
379             max_lr = parent->ClientLowerRight();
380         } else {
381             max_lr.x = GG::GUI::GetGUI()->AppWidth();
382             max_lr.y = GG::GUI::GetGUI()->AppHeight();
383         }
384 
385         GG::X new_x = std::min(max_lr.x, requested_lr.x);
386         GG::Y new_y = std::min(max_lr.y, requested_lr.y);
387         GG::Pt new_lr(new_x, new_y);
388 
389         Resize(new_lr - UpperLeft());
390 
391     } else { // normal-dragging
392         GG::Wnd::LDrag(pt, move, mod_keys);
393     }
394 }
395 
LButtonUp(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)396 void CUIWnd::LButtonUp(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
397     m_drag_offset = GG::Pt(-GG::X1, -GG::Y1);
398     SaveOptions();
399 }
400 
MouseEnter(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)401 void CUIWnd::MouseEnter(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
402     m_mouse_in_resize_tab = InResizeTab(pt);
403     Wnd::MouseEnter(pt, mod_keys);
404 }
405 
MouseHere(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)406 void CUIWnd::MouseHere(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
407     m_mouse_in_resize_tab = InResizeTab(pt);
408     Wnd::MouseHere(pt, mod_keys);
409 }
410 
MouseLeave()411 void CUIWnd::MouseLeave() {
412     m_mouse_in_resize_tab = false;
413     Wnd::MouseLeave();
414 }
415 
ClientUpperLeft() const416 GG::Pt CUIWnd::ClientUpperLeft() const
417 { return m_minimized ? UpperLeft() : UpperLeft() + GG::Pt(BORDER_LEFT, TopBorder()); }
418 
ClientLowerRight() const419 GG::Pt CUIWnd::ClientLowerRight() const
420 { return m_minimized ? LowerRight() : LowerRight() - GG::Pt(BORDER_RIGHT, BORDER_BOTTOM); }
421 
InWindow(const GG::Pt & pt) const422 bool CUIWnd::InWindow(const GG::Pt& pt) const {
423     GG::Pt lr = LowerRight();
424     if (m_resizable) {
425         return UpperLeft() <= pt && pt < lr;
426     } else {
427         GG::Pt dist_from_lr = lr - pt;
428         bool inside_lower_right_corner = OUTER_EDGE_ANGLE_OFFSET < Value(dist_from_lr.x) + Value(dist_from_lr.y);
429         return (UpperLeft() <= pt && pt < lr && inside_lower_right_corner);
430     }
431 }
432 
PositionButtons()433 void CUIWnd::PositionButtons() {
434     // The buttons are to be positioned based on the presence of other buttons
435     GG::Pt button_ul = GG::Pt(Width() - ClientUI::Pts() - BORDER_RIGHT, BUTTON_TOP_OFFSET) + UpperLeft() - ClientUpperLeft();
436 
437     if (m_close_button) {
438         m_close_button->MoveTo(GG::Pt(button_ul.x, button_ul.y));
439         button_ul -= GG::Pt(m_close_button->Width(), GG::Y0) + GG::Pt(GG::X(ClientUI::TitlePts()/3), GG::Y0);
440     }
441 
442     if (m_minimize_button) {
443         m_minimize_button->MoveTo(GG::Pt(button_ul.x, button_ul.y));
444         button_ul -= GG::Pt(m_minimize_button->Width(), GG::Y0) + GG::Pt(GG::X(ClientUI::TitlePts()/3), GG::Y0);
445     }
446 
447     if (m_pin_button)
448         m_pin_button->MoveTo(GG::Pt(button_ul.x, button_ul.y));
449 }
450 
InitButtons()451 void CUIWnd::InitButtons() {
452     boost::filesystem::path button_texture_dir = ClientUI::ArtDir() / "icons" / "buttons";
453 
454     // create the close button
455     if (m_closable) {
456         m_close_button = Wnd::Create<CUIButton>(
457             GG::SubTexture(ClientUI::GetTexture(button_texture_dir / "close.png")),
458             GG::SubTexture(ClientUI::GetTexture(button_texture_dir / "close_clicked.png")),
459             GG::SubTexture(ClientUI::GetTexture(button_texture_dir / "close_mouseover.png")));
460         m_close_button->SetColor(ClientUI::WndInnerBorderColor());
461         m_close_button->LeftClickedSignal.connect(-1,
462             &PlayCloseSound);
463         m_close_button->Resize(GG::Pt(GG::X(ClientUI::TitlePts()), GG::Y(ClientUI::TitlePts())));
464         m_close_button->LeftClickedSignal.connect(boost::bind(&CUIWnd::CloseClicked, this));
465         AttachChild(m_close_button);
466         m_close_button->NonClientChild(true);
467     }
468 
469     // create the minimize button
470     if (m_minimizable) {
471         m_minimize_button = Wnd::Create<CUI_MinRestoreButton>();
472         m_minimize_button->Resize(GG::Pt(GG::X(ClientUI::TitlePts()), GG::Y(ClientUI::TitlePts())));
473         m_minimize_button->LeftClickedSignal.connect(boost::bind(&CUIWnd::MinimizeClicked, this));
474         AttachChild(m_minimize_button);
475         m_minimize_button->NonClientChild(true);
476     }
477 
478     // create the pin button
479     if (m_pinable) {
480         m_pin_button = Wnd::Create<CUI_PinButton>();
481         m_pin_button->Resize(GG::Pt(GG::X(ClientUI::TitlePts()), GG::Y(ClientUI::TitlePts())));
482         m_pin_button->LeftClickedSignal.connect(boost::bind(&CUIWnd::PinClicked, this));
483         AttachChild(m_pin_button);
484         m_pin_button->NonClientChild(true);
485     }
486 
487     // All buttons were created at the same spot, position them to the correct spot
488     PositionButtons();
489 }
490 
MinimizedSize() const491 GG::Pt CUIWnd::MinimizedSize() const
492 { return GG::Pt(MINIMIZED_WND_WIDTH, TopBorder()); }
493 
LeftBorder() const494 GG::X CUIWnd::LeftBorder() const
495 { return BORDER_LEFT; }
496 
TopBorder() const497 GG::Y CUIWnd::TopBorder() const
498 { return GG::Y(ClientUI::TitlePts() + TITLE_OFFSET*4); }
499 
RightBorder() const500 GG::X CUIWnd::RightBorder() const
501 { return BORDER_RIGHT; }
502 
BottomBorder() const503 GG::Y CUIWnd::BottomBorder() const
504 { return BORDER_BOTTOM; }
505 
InnerBorderAngleOffset() const506 int CUIWnd::InnerBorderAngleOffset() const
507 { return INNER_BORDER_ANGLE_OFFSET; }
508 
CloseClicked()509 void CUIWnd::CloseClicked() {
510     m_done = true;
511     if (auto&& parent = Parent())
512         parent->DetachChild(this);
513     else
514         GG::GUI::GetGUI()->Remove(shared_from_this());
515 
516     //m_minimized_buffer.clear();
517     //m_outer_border_buffer.clear();
518     //m_inner_border_buffer.clear();
519     //m_resize_corner_lines_buffer.clear();
520 }
521 
PinClicked()522 void CUIWnd::PinClicked() {
523     m_pinned = !m_pinned;
524     m_resizable = !m_pinned;
525     m_pin_button->Toggle(m_pinned); // Change the icon on the pin button
526     m_vertex_buffer.clear();        // force buffer re-init on next Render call
527     SaveOptions();
528 }
529 
MinimizeClicked()530 void CUIWnd::MinimizeClicked() {
531     if (!m_minimized) {
532         m_minimized = true;
533         m_original_size = Size();
534         SetMinSize(MinimizedSize());
535         Resize(MinimizedSize());
536 
537         // hide all children, re-showing only position/size controls
538         Hide();
539         Show();
540         if (m_close_button)
541             m_close_button->Show();
542         if (m_minimize_button)
543             m_minimize_button->Show();
544         if (m_pin_button)
545             m_pin_button->Show();
546 
547     } else {
548         m_minimized = false;
549         SetMinSize(GG::Pt(MinimizedSize().x, TopBorder() + INNER_BORDER_ANGLE_OFFSET + BORDER_BOTTOM + 10));
550         Resize(GG::Pt(m_original_size));
551         Show();
552     }
553     SaveOptions();
554 }
555 
InitBuffers()556 void CUIWnd::InitBuffers() {
557     m_vertex_buffer.clear();
558     m_vertex_buffer.reserve(17);
559     m_buffer_indices.resize(4);
560     std::size_t previous_buffer_size = m_vertex_buffer.size();
561 
562     GG::Pt ul = UpperLeft(), lr = LowerRight();
563     GG::Pt cl_ul = ClientUpperLeft(), cl_lr = ClientLowerRight();
564 
565     // within m_vertex_buffer:
566     // [0] is the start and range for minimized background triangle fan and minimized border line loop
567     // [1] is ... the background fan / outer border line loop
568     // [2] is ... the inner border line loop
569     // [3] is ... the resize tab line list
570 
571     // minimized background fan and border line loop
572     m_vertex_buffer.store(Value(ul.x),  Value(ul.y));
573     m_vertex_buffer.store(Value(lr.x),  Value(ul.y));
574     m_vertex_buffer.store(Value(lr.x),  Value(lr.y));
575     m_vertex_buffer.store(Value(ul.x),  Value(lr.y));
576     m_buffer_indices[0].first = previous_buffer_size;
577     m_buffer_indices[0].second = m_vertex_buffer.size() - previous_buffer_size;
578     previous_buffer_size = m_vertex_buffer.size();
579 
580     // outer border, with optional corner cutout
581     m_vertex_buffer.store(Value(ul.x),  Value(ul.y));
582     m_vertex_buffer.store(Value(lr.x),  Value(ul.y));
583     if (!m_resizable) {
584         m_vertex_buffer.store(Value(lr.x),                            Value(lr.y) - OUTER_EDGE_ANGLE_OFFSET);
585         m_vertex_buffer.store(Value(lr.x) - OUTER_EDGE_ANGLE_OFFSET,  Value(lr.y));
586     } else {
587         m_vertex_buffer.store(Value(lr.x),  Value(lr.y));
588     }
589     m_vertex_buffer.store(Value(ul.x),      Value(lr.y));
590     m_buffer_indices[1].first = previous_buffer_size;
591     m_buffer_indices[1].second = m_vertex_buffer.size() - previous_buffer_size;
592     previous_buffer_size = m_vertex_buffer.size();
593 
594     // inner border, with optional corner cutout
595     m_vertex_buffer.store(Value(cl_ul.x),       Value(cl_ul.y));
596     m_vertex_buffer.store(Value(cl_lr.x),       Value(cl_ul.y));
597     if (m_resizable) {
598         m_vertex_buffer.store(Value(cl_lr.x),                             Value(cl_lr.y) - INNER_BORDER_ANGLE_OFFSET);
599         m_vertex_buffer.store(Value(cl_lr.x) - INNER_BORDER_ANGLE_OFFSET, Value(cl_lr.y));
600     } else {
601         m_vertex_buffer.store(Value(cl_lr.x),   Value(cl_lr.y));
602     }
603     m_vertex_buffer.store(Value(cl_ul.x),       Value(cl_lr.y));
604     m_buffer_indices[2].first = previous_buffer_size;
605     m_buffer_indices[2].second = m_vertex_buffer.size() - previous_buffer_size;
606     previous_buffer_size = m_vertex_buffer.size();
607 
608     // resize hash marks
609     m_vertex_buffer.store(Value(cl_lr.x),                           Value(cl_lr.y) - RESIZE_HASHMARK1_OFFSET);
610     m_vertex_buffer.store(Value(cl_lr.x) - RESIZE_HASHMARK1_OFFSET, Value(cl_lr.y));
611     m_vertex_buffer.store(Value(cl_lr.x),                           Value(cl_lr.y) - RESIZE_HASHMARK2_OFFSET);
612     m_vertex_buffer.store(Value(cl_lr.x) - RESIZE_HASHMARK2_OFFSET, Value(cl_lr.y));
613     m_buffer_indices[3].first = previous_buffer_size;
614     m_buffer_indices[3].second = m_vertex_buffer.size() - previous_buffer_size;
615     //previous_buffer_size = m_vertex_buffer.size();
616 
617     m_vertex_buffer.createServerBuffer();
618 
619     //TraceLogger() << "CUIWnd vertex buffer final size: " << previous_buffer_size << std::endl;
620 }
621 
Hide()622 void CUIWnd::Hide() {
623     GG::Wnd::Hide();
624     SaveOptions();
625 }
626 
Show()627 void CUIWnd::Show() {
628     GG::Wnd::Show();
629     SaveOptions();
630 }
631 
ResetDefaultPosition()632 void CUIWnd::ResetDefaultPosition() {
633     GG::Rect default_position = CalculatePosition();
634     if (default_position.ul.x != INVALID_X) // do nothing if not overridden
635         InitSizeMove(default_position.ul, default_position.lr);
636 }
637 
CalculatePosition() const638 GG::Rect CUIWnd::CalculatePosition() const
639 { return GG::Rect(INVALID_X, INVALID_Y, INVALID_X, INVALID_Y); }
640 
SetDefaultedOptions()641 void CUIWnd::SetDefaultedOptions() {
642     OptionsDB& db = GetOptionsDB();
643     std::set<std::string> window_options;
644     db.FindOptions(window_options, "ui." + m_config_name);
645     for (auto& option : window_options) {
646         if (db.IsDefaultValue(option))
647             m_defaulted_options.emplace(option);
648     }
649 }
650 
SaveDefaultedOptions()651 void CUIWnd::SaveDefaultedOptions() {
652     OptionsDB& db = GetOptionsDB();
653     std::string config_prefix = "ui." + m_config_name;
654     std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
655                               ".fullscreen" : ".windowed";
656     GG::Pt size;
657     if (m_minimized)
658         size = m_original_size;
659     else
660         size = Size();
661 
662     std::string config_name = config_prefix + window_mode + ".left";
663     int int_value = Value(RelativeUpperLeft().x);
664     if (m_defaulted_options.count(config_name))
665         db.SetDefault<int>(config_name, int_value);
666 
667     config_name = config_prefix + window_mode + ".top";
668     int_value = Value(RelativeUpperLeft().y);
669     if (m_defaulted_options.count(config_name))
670         db.SetDefault<int>(config_name, int_value);
671 
672     config_name = config_prefix + window_mode + ".width";
673     int_value = Value(size.x);
674     if (m_defaulted_options.count(config_name))
675         db.SetDefault<int>(config_name, int_value);
676 
677     config_name = config_prefix + window_mode + ".height";
678     int_value = Value(size.y);
679     if (m_defaulted_options.count(config_name))
680         db.SetDefault<int>(config_name, int_value);
681 
682     if (!Modal()) {
683         config_name = config_prefix + ".visible";
684         bool bool_value = Visible();
685         if (m_defaulted_options.count(config_name))
686             db.SetDefault<bool>(config_name, bool_value);
687 
688         config_name = config_prefix + ".pinned";
689         bool_value = m_pinned;
690         if (m_defaulted_options.count(config_name))
691             db.SetDefault<bool>(config_name, bool_value);
692 
693         config_name = config_prefix + ".minimized";
694         bool_value = m_minimized;
695         if (m_defaulted_options.count(config_name))
696             db.SetDefault<bool>(config_name, bool_value);
697     }
698 }
699 
SaveOptions() const700 void CUIWnd::SaveOptions() const {
701     OptionsDB& db = GetOptionsDB();
702 
703     // The default empty string means 'do not save/load properties'
704     // Also do not save while the window is being dragged.
705     auto gui = GG::GUI::GetGUI();
706     std::string option_prefix = "ui." + m_config_name;
707     if (m_config_name.empty() || !m_config_save || !gui || gui->DragWnd(this, 0)) {
708         return;
709     } else if (!db.OptionExists(option_prefix + ".initialized")) {
710         ErrorLogger() << "CUIWnd::SaveOptions() : attempted to save window options using name \"" << m_config_name << "\" but the options do not appear to be registered in the OptionsDB.";
711         return;
712     } else if (!db.Get<bool>(option_prefix + ".initialized")) {
713         // Don't save until the window has been given its proper default values
714         return;
715     }
716 
717     GG::Pt size;
718     if (m_minimized)
719         size = m_original_size;
720     else
721         size = Size();
722 
723     std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
724                               ".fullscreen" : ".windowed";
725 
726     db.Set<int>(option_prefix + window_mode + ".left",      Value(RelativeUpperLeft().x));
727     db.Set<int>(option_prefix + window_mode + ".top",       Value(RelativeUpperLeft().y));
728     db.Set<int>(option_prefix + window_mode + ".width",     Value(size.x));
729     db.Set<int>(option_prefix + window_mode + ".height",    Value(size.y));
730 
731     if (!Modal()) {
732         db.Set<bool>(option_prefix + ".visible", Visible());
733         db.Set<bool>(option_prefix + ".pinned", m_pinned);
734         db.Set<bool>(option_prefix + ".minimized", m_minimized);
735     }
736 
737     db.Commit();
738 }
739 
LoadOptions()740 void CUIWnd::LoadOptions() {
741     OptionsDB& db = GetOptionsDB();
742 
743     // The default empty string means 'do not save/load properties'
744     std::string option_prefix = "ui." + m_config_name;
745     if (m_config_name.empty()) {
746         return;
747     } else if (!db.OptionExists(option_prefix + ".initialized")) {
748         ErrorLogger() << "CUIWnd::LoadOptions() : attempted to load window options using name \"" << m_config_name << "\" but the options do not appear to be registered in the OptionsDB.";
749         return;
750     }
751 
752     // These functions are only called in certain circumstances, could pass in
753     // things like the fullscreen/windowed mode instead of using global program
754     // state like this?
755     std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
756                               ".fullscreen" : ".windowed";
757     GG::Pt ul   = GG::Pt(GG::X(db.Get<int>(option_prefix + window_mode + ".left")),
758                          GG::Y(db.Get<int>(option_prefix + window_mode + ".top")));
759     GG::Pt size = GG::Pt(GG::X(db.Get<int>(option_prefix + window_mode + ".width")),
760                          GG::Y(db.Get<int>(option_prefix + window_mode + ".height")));
761 
762     m_config_save = false;
763 
764     if (m_minimized) {
765         MinimizeClicked();
766     }
767 
768     if (ul.x == INVALID_X || ul.y == INVALID_Y) {
769         // If no options have been saved yet, allow the window to calculate its
770         // own position.  Note that when this is first called from the CUIWnd
771         // constructor it will call CUIWnd::CalculatePosition() (a no-op) but
772         // afterwards will call derived overrides.  This will still do nothing
773         // for windows that don't override CalculatePosition() but they should
774         // be positioned by their owners in any case.
775         ResetDefaultPosition();
776     } else {
777         SizeMove(ul, ul + size);
778     }
779 
780     if (!Modal()) {
781         if (db.Get<bool>(option_prefix + ".visible")) {
782             Show();
783         } else {
784             Hide();
785         }
786 
787         if (db.Get<bool>(option_prefix + ".pinned") != m_pinned) {
788             PinClicked();
789         }
790 
791         if (db.Get<bool>(option_prefix + ".minimized") != m_minimized) {
792             MinimizeClicked();
793         }
794     }
795 
796     m_config_save = true;
797 }
798 
AddWindowOptions(const std::string & config_name,int left,int top,int width,int height,bool visible,bool pinned,bool minimized)799 const std::string CUIWnd::AddWindowOptions(const std::string& config_name,
800                                            int left, int top,
801                                            int width, int height,
802                                            bool visible, bool pinned, bool minimized)
803 {
804     OptionsDB& db = GetOptionsDB();
805     std::string new_name = "";
806 
807     if (db.OptionExists("ui." + config_name + ".fullscreen.left")) {
808         // If the option has already been added, a window was previously created with this name...
809         if (config_name.empty()) {
810             // Should never happen, but just in case.
811             db.Remove("ui..fullscreen.left");
812             db.Remove("ui..fullscreen.top");
813             db.Remove("ui..windowed.left");
814             db.Remove("ui..windowed.top");
815             db.Remove("ui..fullscreen.width");
816             db.Remove("ui..fullscreen.height");
817             db.Remove("ui..windowed.width");
818             db.Remove("ui..windowed.height");
819             db.Remove("ui..visible");
820             db.Remove("ui..pinned");
821             db.Remove("ui..minimized");
822             ErrorLogger() << "CUIWnd::AddWindowOptions() : Found window options with a blank name, removing those options.";
823         } else if (db.OptionExists("ui." + config_name + ".initialized")) {
824             // If the window's still there, shouldn't use the same name (but the window can still be created so don't throw)
825             ErrorLogger() << "CUIWnd::AddWindowOptions() : Attempted to create a window with config_name = " << config_name << " but one already exists with that name.";
826         } else {
827             // Old window has been destroyed, use the properties it had.
828             db.Add<bool>("ui." + config_name + ".initialized",      UserStringNop("OPTIONS_DB_UI_WINDOWS_EXISTS"),          false,      Validator<bool>(),              false);
829             new_name = config_name;
830         }
831     } else if (!config_name.empty()) {
832         const int max_width_plus_one = HumanClientApp::MaximumPossibleWidth() + 1;
833         const int max_height_plus_one = HumanClientApp::MaximumPossibleHeight() + 1;
834 
835         db.Add<bool>("ui." + config_name + ".initialized",      UserStringNop("OPTIONS_DB_UI_WINDOWS_EXISTS"),          false,      Validator<bool>(),              false);
836 
837         db.Add<int> ("ui." + config_name + ".fullscreen.left", UserStringNop("OPTIONS_DB_UI_WINDOWS_LEFT"), left, OrValidator<int>(RangedValidator<int>(0, max_width_plus_one), DiscreteValidator<int>(INVALID_POS)));
838         db.Add<int> ("ui." + config_name + ".fullscreen.top", UserStringNop("OPTIONS_DB_UI_WINDOWS_TOP"), top, OrValidator<int>(RangedValidator<int>(0, max_height_plus_one), DiscreteValidator<int>(INVALID_POS)));
839         db.Add<int> ("ui." + config_name + ".windowed.left", UserStringNop("OPTIONS_DB_UI_WINDOWS_LEFT_WINDOWED"), left, OrValidator<int>(RangedValidator<int>(0, max_width_plus_one), DiscreteValidator<int>(INVALID_POS)));
840         db.Add<int> ("ui." + config_name + ".windowed.top", UserStringNop("OPTIONS_DB_UI_WINDOWS_TOP_WINDOWED"), top, OrValidator<int>(RangedValidator<int>(0, max_height_plus_one), DiscreteValidator<int>(INVALID_POS)));
841 
842         db.Add<int> ("ui." + config_name + ".fullscreen.width", UserStringNop("OPTIONS_DB_UI_WINDOWS_WIDTH"), width, RangedValidator<int>(0, max_width_plus_one));
843         db.Add<int> ("ui." + config_name + ".fullscreen.height", UserStringNop("OPTIONS_DB_UI_WINDOWS_HEIGHT"), height, RangedValidator<int>(0, max_height_plus_one));
844         db.Add<int> ("ui." + config_name + ".windowed.width", UserStringNop("OPTIONS_DB_UI_WINDOWS_WIDTH_WINDOWED"), width, RangedValidator<int>(0, max_width_plus_one));
845         db.Add<int> ("ui." + config_name + ".windowed.height", UserStringNop("OPTIONS_DB_UI_WINDOWS_HEIGHT_WINDOWED"), height, RangedValidator<int>(0, max_height_plus_one));
846 
847         db.Add<bool>("ui." + config_name + ".visible",          UserStringNop("OPTIONS_DB_UI_WINDOWS_VISIBLE"),         visible,    Validator<bool>());
848         db.Add<bool>("ui." + config_name + ".pinned",           UserStringNop("OPTIONS_DB_UI_WINDOWS_PINNED"),          pinned,     Validator<bool>());
849         db.Add<bool>("ui." + config_name + ".minimized",        UserStringNop("OPTIONS_DB_UI_WINDOWS_MINIMIZED"),       minimized,  Validator<bool>());
850 
851         new_name = config_name;
852     }
853 
854     return new_name;
855 }
856 
AddWindowOptions(const std::string & config_name,GG::X left,GG::Y top,GG::X width,GG::Y height,bool visible,bool pinned,bool minimized)857 const std::string CUIWnd::AddWindowOptions(const std::string& config_name,
858                                            GG::X left, GG::Y top,
859                                            GG::X width, GG::Y height,
860                                            bool visible, bool pinned, bool minimized)
861 {
862     return AddWindowOptions(config_name,
863                             Value(left), Value(top),
864                             Value(width), Value(height),
865                             visible, pinned, minimized);
866 }
867 
InvalidateWindowOptions(const std::string & config_name)868 void CUIWnd::InvalidateWindowOptions(const std::string& config_name) {
869     OptionsDB& db = GetOptionsDB();
870     std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ? ".fullscreen" : ".windowed";
871     std::string edge_option_prefix = "ui." + config_name + window_mode;
872 
873     if (db.OptionExists("ui." + config_name + ".initialized")) {
874         // Should be removed in window dtor.
875         ErrorLogger() << "CUIWnd::RemoveWindowOptions() : attempted to remove window options using name \"" << config_name << "\" but they appear to be in use by a window.";
876         return;
877     } else if (!db.OptionExists(edge_option_prefix + ".left")) {
878         ErrorLogger() << "CUIWnd::RemoveWindowOptions() : attempted to remove window options using name \"" << config_name << "\" but the options do not appear to be registered in the OptionsDB.";
879         return;
880     }
881 
882     db.Set<int>(edge_option_prefix + ".left", INVALID_POS);
883     db.Set<int>(edge_option_prefix + ".top",  INVALID_POS);
884     db.Set<bool>("ui." + config_name + ".visible",      db.GetDefault<bool>("ui." + config_name + ".visible"));
885     db.Set<bool>("ui." + config_name + ".pinned",       db.GetDefault<bool>("ui." + config_name + ".pinned"));
886     db.Set<bool>("ui." + config_name + ".minimized",    db.GetDefault<bool>("ui." + config_name + ".minimized"));
887 }
888 
InvalidateUnusedOptions()889 void CUIWnd::InvalidateUnusedOptions() {
890     OptionsDB& db = GetOptionsDB();
891     std::string prefix("ui.");
892     std::string suffix_exist(".left");
893 
894     // Remove unrecognized options from the DB so that their values aren't
895     // applied when they are eventually registered.
896     db.RemoveUnrecognized(prefix);
897 
898     // Removed registered options for windows that aren't currently
899     // instantiated so they fall back on defaults when they are re-constructed.
900     std::set<std::string> window_options;
901     db.FindOptions(window_options, prefix);
902     for (const std::string& option : window_options) {
903         if (!boost::algorithm::find_last(option, suffix_exist))
904             continue;
905         // If the ".left" option is registered, the rest are implied to be there.
906         if ((option.rfind(suffix_exist) == (option.length() - suffix_exist.length())) && db.OptionExists(option)) {
907             auto window_name = WindowNameFromOption(option);
908             // If the ".initialized" option isn't present under this name, remove the options.
909             if (!window_name.empty() && !window_options.count(prefix + window_name + ".initialized"))
910                 InvalidateWindowOptions(window_name);
911         }
912     }
913 
914     db.Commit();
915 }
916 
SetParent(const std::shared_ptr<GG::Wnd> & wnd)917 void CUIWnd::SetParent(const std::shared_ptr<GG::Wnd>& wnd) {
918     GG::Wnd::SetParent(wnd);
919     m_vertex_buffer.clear();    // force buffer re-init on next Render call, so background is properly positioned for new parent-relative position
920 }
921 
922 ///////////////////////////////////////
923 // class CUIEditWnd
924 ///////////////////////////////////////
925 const GG::X CUIEditWnd::BUTTON_WIDTH(75);
926 const int CUIEditWnd::CONTROL_MARGIN = 5;
927 
CUIEditWnd(GG::X w,const std::string & prompt_text,const std::string & edit_text,GG::Flags<GG::WndFlag> flags)928 CUIEditWnd::CUIEditWnd(GG::X w, const std::string& prompt_text, const std::string& edit_text, GG::Flags<GG::WndFlag> flags/* = Wnd::MODAL*/) :
929     CUIWnd(prompt_text, GG::X0, GG::Y0, w, GG::Y1, flags)
930 {
931     m_edit = GG::Wnd::Create<CUIEdit>(edit_text);
932 }
933 
CompleteConstruction()934 void CUIEditWnd::CompleteConstruction() {
935     CUIWnd::CompleteConstruction();
936 
937     m_ok_bn = GG::Wnd::Create<CUIButton>(UserString("OK"));
938     m_cancel_bn = GG::Wnd::Create<CUIButton>(UserString("CANCEL"));
939 
940     m_edit->MoveTo(GG::Pt(LeftBorder() + 3, TopBorder() + 3));
941     m_edit->Resize(GG::Pt(ClientWidth() - 2 * BUTTON_WIDTH - 2 * CONTROL_MARGIN - 6 - LeftBorder() - RightBorder(), m_edit->MinUsableSize().y));
942 
943     m_ok_bn->MoveTo(GG::Pt(m_edit->Right() + CONTROL_MARGIN, TopBorder() + 3));
944     m_ok_bn->Resize(GG::Pt(BUTTON_WIDTH, m_ok_bn->MinUsableSize().y));
945     m_ok_bn->OffsetMove(GG::Pt(GG::X0, (m_edit->Height() - m_ok_bn->Height()) / 2));
946 
947     m_cancel_bn->MoveTo(GG::Pt(m_ok_bn->Right() + CONTROL_MARGIN, TopBorder() + 3));
948     m_cancel_bn->Resize(GG::Pt(BUTTON_WIDTH, m_cancel_bn->MinUsableSize().y));
949     m_cancel_bn->OffsetMove(GG::Pt(GG::X0, (m_edit->Height() - m_ok_bn->Height()) / 2));
950 
951     Resize(GG::Pt(Width(), std::max(m_edit->Bottom(), m_cancel_bn->Bottom()) + BottomBorder() + 3));
952     MoveTo(GG::Pt((GG::GUI::GetGUI()->AppWidth() - Width()) / 2, (GG::GUI::GetGUI()->AppHeight() - Height()) / 2));
953 
954     AttachChild(m_edit);
955     AttachChild(m_ok_bn);
956     AttachChild(m_cancel_bn);
957 
958     m_ok_bn->LeftClickedSignal.connect(boost::bind(&CUIEditWnd::OkClicked, this));
959     m_cancel_bn->LeftClickedSignal.connect(boost::bind(&CUIWnd::CloseClicked, static_cast<CUIWnd*>(this)));
960 
961     m_edit->SelectAll();
962 }
963 
ModalInit()964 void CUIEditWnd::ModalInit()
965 { GG::GUI::GetGUI()->SetFocusWnd(m_edit); }
966 
KeyPress(GG::Key key,std::uint32_t key_code_point,GG::Flags<GG::ModKey> mod_keys)967 void CUIEditWnd::KeyPress(GG::Key key, std::uint32_t key_code_point, GG::Flags<GG::ModKey> mod_keys) {
968     switch (key) {
969     case GG::GGK_RETURN: if (!m_ok_bn->Disabled()) OkClicked(); break;
970     case GG::GGK_ESCAPE: CloseClicked(); break;
971     default: break;
972     }
973 }
974 
Result() const975 const std::string& CUIEditWnd::Result() const
976 { return m_result; }
977 
OkClicked()978 void CUIEditWnd::OkClicked() {
979     m_result = m_edit->Text();
980     CloseClicked();
981 }
982