Android release v6.7_preview1
[xcsoar.git] / src / Widget / RowFormWidget.cpp
blob1718271510b1fdeeb11a16eda056fbb4b351fbbd
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "RowFormWidget.hpp"
25 #include "Form/Edit.hpp"
26 #include "Form/Panel.hpp"
27 #include "Form/Button.hpp"
28 #include "Form/HLine.hpp"
29 #include "Look/DialogLook.hpp"
30 #include "Dialogs/DialogSettings.hpp"
31 #include "UIGlobals.hpp"
32 #include "Screen/Layout.hpp"
33 #include "Screen/LargeTextWindow.hpp"
34 #include "Screen/Font.hpp"
35 #include "Form/DataField/Boolean.hpp"
36 #include "Form/DataField/Integer.hpp"
37 #include "Form/DataField/Float.hpp"
38 #include "Form/DataField/Angle.hpp"
39 #include "Form/DataField/Enum.hpp"
40 #include "Form/DataField/String.hpp"
41 #include "Form/DataField/Password.hpp"
42 #include "Form/DataField/FileReader.hpp"
43 #include "Form/DataField/Time.hpp"
44 #include "Form/DataField/RoughTime.hpp"
45 #include "Time/RoughTime.hpp"
46 #include "Language/Language.hpp"
47 #include "Profile/Profile.hpp"
48 #include "Units/Units.hpp"
49 #include "Units/Descriptor.hpp"
50 #include "LocalPath.hpp"
51 #include "Math/Angle.hpp"
52 #include "Util/ConvertString.hpp"
54 #include <windef.h>
55 #include <assert.h>
56 #include <limits.h>
58 unsigned
59 RowFormWidget::Row::GetMinimumHeight(bool vertical) const
61 switch (type) {
62 case Type::DUMMY:
63 return 0;
65 case Type::WIDGET:
66 return widget->GetMinimumSize().cy;
68 case Type::GENERIC:
69 break;
71 case Type::EDIT:
72 if (vertical && GetControl().HasCaption())
73 return 2 * Layout::GetMinimumControlHeight();
75 /* fall through */
77 case Type::BUTTON:
78 return Layout::GetMinimumControlHeight();
80 case Type::MULTI_LINE:
81 return Layout::GetMinimumControlHeight();
83 case Type::REMAINING:
84 return Layout::GetMinimumControlHeight();
87 return window->GetHeight();
90 unsigned
91 RowFormWidget::Row::GetMaximumHeight(bool vertical) const
93 switch (type) {
94 case Type::DUMMY:
95 return 0;
97 case Type::WIDGET:
98 return widget->GetMaximumSize().cy;
100 case Type::GENERIC:
101 break;
103 case Type::EDIT:
104 if (vertical && GetControl().HasCaption())
105 return 2 * Layout::GetMinimumControlHeight();
107 return GetControl().IsReadOnly()
108 /* rows that are not clickable don't need to be extra-large */
109 ? Layout::GetMinimumControlHeight()
110 : Layout::GetMaximumControlHeight();
112 case Type::BUTTON:
113 return Layout::GetMaximumControlHeight();
115 case Type::MULTI_LINE:
116 return Layout::GetMinimumControlHeight() * 3;
118 case Type::REMAINING:
119 return 4096;
122 return window->GetHeight();
125 RowFormWidget::RowFormWidget(const DialogLook &_look, bool _vertical)
126 :look(_look), vertical(_vertical)
130 RowFormWidget::~RowFormWidget()
132 if (IsDefined()) {
133 PanelControl *panel = (PanelControl *)GetWindow();
134 delete panel;
137 /* destroy all rows */
138 for (auto &i : rows)
139 i.Delete();
142 void
143 RowFormWidget::SetRowAvailable(unsigned i, bool available)
145 Row &row = rows[i];
146 if (available == row.available)
147 return;
149 row.available = available;
150 UpdateLayout();
153 void
154 RowFormWidget::SetRowVisible(unsigned i, bool visible)
156 Row &row = rows[i];
157 if (visible == row.visible)
158 return;
160 row.visible = visible;
161 if (!visible)
162 row.GetWindow().Hide();
163 else if (row.IsAvailable(UIGlobals::GetDialogSettings().expert))
164 row.GetWindow().Show();
167 void
168 RowFormWidget::SetExpertRow(unsigned i)
170 Row &row = rows[i];
171 assert(!row.expert);
172 row.expert = true;
175 void
176 RowFormWidget::Add(Row::Type type, Window *window)
178 assert(IsDefined());
179 #ifndef USE_GDI
180 assert(window->GetParent() == GetWindow());
181 #endif
182 assert(window->IsVisible());
183 /* cannot append rows after a REMAINING row */
184 assert(rows.empty() || rows.back().type != Row::Type::REMAINING);
186 rows.push_back(Row(type, window));
189 WndProperty *
190 RowFormWidget::CreateEdit(const TCHAR *label, const TCHAR *help,
191 bool read_only)
193 assert(IsDefined());
195 const PixelRect edit_rc =
196 InitialControlRect(Layout::GetMinimumControlHeight());
198 WindowStyle style;
199 if (!read_only)
200 style.TabStop();
202 PanelControl &panel = *(PanelControl *)GetWindow();
203 WndProperty *edit =
204 new WndProperty(panel, look, label,
205 edit_rc, (*label == '\0') ? 0 : 100,
206 style);
207 edit->SetReadOnly(read_only);
209 if (help != NULL)
210 edit->SetHelpText(help);
212 return edit;
215 WndProperty *
216 RowFormWidget::Add(const TCHAR *label, const TCHAR *help, bool read_only)
218 WndProperty *edit = CreateEdit(label, help, read_only);
219 Add(Row::Type::EDIT, edit);
220 return edit;
223 void
224 RowFormWidget::AddReadOnly(const TCHAR *label, const TCHAR *help,
225 const TCHAR *text)
227 WndProperty *control = Add(label, help, true);
228 if (text != NULL)
229 control->SetText(text);
232 void
233 RowFormWidget::AddReadOnly(const TCHAR *label, const TCHAR *help,
234 const TCHAR *display_format,
235 fixed value)
237 WndProperty *edit = Add(label, help, true);
238 DataFieldFloat *df = new DataFieldFloat(display_format, display_format,
239 fixed(0), fixed(0),
240 value, fixed(1), false, NULL);
241 edit->SetDataField(df);
244 void
245 RowFormWidget::AddReadOnly(const TCHAR *label, const TCHAR *help,
246 const TCHAR *display_format,
247 UnitGroup unit_group, fixed value)
249 WndProperty *edit = Add(label, help, true);
250 const Unit unit = Units::GetUserUnitByGroup(unit_group);
251 value = Units::ToUserUnit(value, unit);
252 DataFieldFloat *df = new DataFieldFloat(display_format, display_format,
253 fixed(0), fixed(0),
254 value, fixed(1), false, NULL);
255 df->SetUnits(Units::GetUnitName(unit));
256 edit->SetDataField(df);
259 void
260 RowFormWidget::AddReadOnly(const TCHAR *label, const TCHAR *help,
261 bool value)
263 WndProperty *edit = Add(label, help, true);
264 DataFieldBoolean *df = new DataFieldBoolean(value, _("On"), _("Off"),
265 nullptr);
266 edit->SetDataField(df);
269 WndProperty *
270 RowFormWidget::AddFloat(const TCHAR *label, const TCHAR *help,
271 const TCHAR *display_format,
272 const TCHAR *edit_format,
273 fixed min_value, fixed max_value,
274 fixed step, bool fine,
275 UnitGroup unit_group, fixed value,
276 DataField::DataAccessCallback callback)
278 WndProperty *edit = Add(label, help);
279 const Unit unit = Units::GetUserUnitByGroup(unit_group);
280 value = Units::ToUserUnit(value, unit);
281 DataFieldFloat *df = new DataFieldFloat(edit_format, display_format,
282 min_value, max_value,
283 value, step, fine, callback);
284 df->SetUnits(Units::GetUnitName(unit));
285 edit->SetDataField(df);
286 return edit;
289 WndProperty *
290 RowFormWidget::Add(const TCHAR *label, const TCHAR *help,
291 DataField *df)
293 WndProperty *edit = Add(label, help);
294 edit->SetDataField(df);
295 return edit;
298 WndProperty *
299 RowFormWidget::AddBoolean(const TCHAR *label, const TCHAR *help,
300 bool value,
301 DataField::DataAccessCallback callback)
303 WndProperty *edit = Add(label, help);
304 DataFieldBoolean *df = new DataFieldBoolean(value, _("On"), _("Off"),
305 callback);
306 edit->SetDataField(df);
307 return edit;
310 WndProperty *
311 RowFormWidget::AddInteger(const TCHAR *label, const TCHAR *help,
312 const TCHAR *display_format,
313 const TCHAR *edit_format,
314 int min_value, int max_value, int step, int value,
315 DataField::DataAccessCallback callback)
317 WndProperty *edit = Add(label, help);
318 DataFieldInteger *df = new DataFieldInteger(edit_format, display_format,
319 min_value, max_value,
320 value, step, callback);
321 edit->SetDataField(df);
322 return edit;
325 WndProperty *
326 RowFormWidget::AddFloat(const TCHAR *label, const TCHAR *help,
327 const TCHAR *display_format,
328 const TCHAR *edit_format,
329 fixed min_value, fixed max_value,
330 fixed step, bool fine,
331 fixed value,
332 DataField::DataAccessCallback callback)
334 WndProperty *edit = Add(label, help);
335 DataFieldFloat *df = new DataFieldFloat(edit_format, display_format,
336 min_value, max_value,
337 value, step, fine, callback);
338 edit->SetDataField(df);
339 return edit;
342 WndProperty *
343 RowFormWidget::AddAngle(const TCHAR *label, const TCHAR *help,
344 Angle value, unsigned step, bool fine,
345 DataFieldListener *listener)
347 WndProperty *edit = Add(label, help);
348 AngleDataField *df = new AngleDataField(value, step, fine, listener);
349 edit->SetDataField(df);
350 return edit;
353 WndProperty *
354 RowFormWidget::AddEnum(const TCHAR *label, const TCHAR *help,
355 const StaticEnumChoice *list, unsigned value,
356 DataField::DataAccessCallback callback)
358 assert(list != NULL);
360 WndProperty *edit = Add(label, help);
361 DataFieldEnum *df = new DataFieldEnum(callback);
363 if (list[0].help != NULL)
364 df->EnableItemHelp(true);
366 df->AddChoices(list);
367 df->Set(value);
369 edit->SetDataField(df);
370 return edit;
373 WndProperty *
374 RowFormWidget::AddEnum(const TCHAR *label, const TCHAR *help,
375 DataField::DataAccessCallback callback)
377 WndProperty *edit = Add(label, help);
378 DataFieldEnum *df = new DataFieldEnum(callback);
380 edit->SetDataField(df);
381 return edit;
384 WndProperty *
385 RowFormWidget::AddText(const TCHAR *label, const TCHAR *help,
386 const TCHAR *content)
388 WndProperty *edit = Add(label, help);
389 DataFieldString *df = new DataFieldString(content, NULL);
391 edit->SetDataField(df);
392 return edit;
395 WndProperty *
396 RowFormWidget::AddPassword(const TCHAR *label, const TCHAR *help,
397 const TCHAR *content)
399 WndProperty *edit = Add(label, help);
400 PasswordDataField *df = new PasswordDataField(content);
402 edit->SetDataField(df);
403 return edit;
406 WndProperty *
407 RowFormWidget::AddTime(const TCHAR *label, const TCHAR *help,
408 int min_value, int max_value, unsigned step,
409 int value, unsigned max_tokens,
410 DataField::DataAccessCallback callback)
412 WndProperty *edit = Add(label, help);
413 DataFieldTime *df = new DataFieldTime(min_value, max_value, value,
414 step, callback);
415 df->SetMaxTokenNumber(max_tokens);
416 edit->SetDataField(df);
417 return edit;
420 WndProperty *
421 RowFormWidget::AddRoughTime(const TCHAR *label, const TCHAR *help,
422 RoughTime value, RoughTimeDelta time_zone,
423 DataFieldListener *listener)
425 WndProperty *edit = Add(label, help);
426 RoughTimeDataField *df = new RoughTimeDataField(value, time_zone, listener);
427 edit->SetDataField(df);
428 return edit;
431 void
432 RowFormWidget::AddSpacer()
434 assert(IsDefined());
436 HLine *window = new HLine(GetLook());
437 ContainerWindow &panel = *(ContainerWindow *)GetWindow();
438 const PixelRect rc = InitialControlRect(Layout::Scale(3));
439 window->Create(panel, rc);
440 Add(window);
443 WndProperty *
444 RowFormWidget::AddFileReader(const TCHAR *label, const TCHAR *help,
445 const char *registry_key, const TCHAR *filters,
446 bool nullable)
448 WndProperty *edit = Add(label, help);
449 DataFieldFileReader *df = new DataFieldFileReader(NULL);
450 edit->SetDataField(df);
452 if (nullable)
453 df->AddNull();
455 df->ScanMultiplePatterns(filters);
457 if (registry_key != nullptr) {
458 TCHAR path[MAX_PATH];
459 if (Profile::GetPath(registry_key, path))
460 df->Lookup(path);
463 edit->RefreshDisplay();
465 return edit;
468 void
469 RowFormWidget::AddMultiLine(const TCHAR *text)
471 assert(IsDefined());
473 const PixelRect rc =
474 InitialControlRect(Layout::GetMinimumControlHeight());
476 LargeTextWindowStyle style;
477 if (IsEmbedded() || Layout::scale_1024 < 2048)
478 /* sunken edge doesn't fit well on the tiny screen of an embedded
479 device */
480 style.Border();
481 else
482 style.SunkenEdge();
484 PanelControl &panel = *(PanelControl *)GetWindow();
485 LargeTextWindow *ltw = new LargeTextWindow();
486 ltw->Create(panel, rc, style);
487 ltw->SetFont(*look.text_font);
489 if (text != nullptr)
490 ltw->SetText(text);
492 Add(Row::Type::MULTI_LINE, ltw);
495 void
496 RowFormWidget::AddButton(const TCHAR *label, ActionListener &listener, int id)
498 assert(IsDefined());
500 const PixelRect button_rc =
501 InitialControlRect(Layout::GetMinimumControlHeight());
503 ButtonWindowStyle button_style;
504 button_style.TabStop();
505 button_style.multiline();
507 ContainerWindow &panel = *(ContainerWindow *)GetWindow();
509 WndButton *button = new WndButton(panel, look, label, button_rc, button_style, listener, id);
511 Add(Row::Type::BUTTON, button);
514 void
515 RowFormWidget::SetMultiLineText(unsigned i, const TCHAR *text)
517 assert(text != nullptr);
518 assert(rows[i].type == Row::Type::MULTI_LINE);
520 LargeTextWindow &ltw = *(LargeTextWindow *)rows[i].window;
521 ltw.SetText(text);
524 void
525 RowFormWidget::LoadValue(unsigned i, int value)
527 WndProperty &control = GetControl(i);
528 DataFieldInteger &df = *(DataFieldInteger *)control.GetDataField();
529 assert(df.GetType() == DataField::Type::INTEGER);
530 df.Set(value);
531 control.RefreshDisplay();
534 void
535 RowFormWidget::LoadValue(unsigned i, bool value)
537 WndProperty &control = GetControl(i);
538 DataFieldBoolean &df = *(DataFieldBoolean *)control.GetDataField();
539 assert(df.GetType() == DataField::Type::BOOLEAN);
540 df.Set(value);
541 control.RefreshDisplay();
544 void
545 RowFormWidget::LoadValueEnum(unsigned i, unsigned value)
547 WndProperty &control = GetControl(i);
548 DataFieldEnum &df = *(DataFieldEnum *)control.GetDataField();
549 assert(df.GetType() == DataField::Type::ENUM);
550 df.Set(value);
551 control.RefreshDisplay();
554 void
555 RowFormWidget::LoadValue(unsigned i, fixed value)
557 WndProperty &control = GetControl(i);
558 DataFieldFloat &df = *(DataFieldFloat *)control.GetDataField();
559 assert(df.GetType() == DataField::Type::REAL);
560 df.Set(value);
561 control.RefreshDisplay();
564 void
565 RowFormWidget::LoadValue(unsigned i, Angle value)
567 WndProperty &control = GetControl(i);
568 AngleDataField &df = *(AngleDataField *)control.GetDataField();
569 assert(df.GetType() == DataField::Type::ANGLE);
570 df.SetValue(value);
571 control.RefreshDisplay();
574 void
575 RowFormWidget::LoadValue(unsigned i, fixed value, UnitGroup unit_group)
577 const Unit unit = Units::GetUserUnitByGroup(unit_group);
578 WndProperty &control = GetControl(i);
579 DataFieldFloat &df = *(DataFieldFloat *)control.GetDataField();
580 assert(df.GetType() == DataField::Type::REAL);
581 df.Set(Units::ToUserUnit(value, unit));
582 df.SetUnits(Units::GetUnitName(unit));
583 control.RefreshDisplay();
586 void
587 RowFormWidget::LoadValue(unsigned i, RoughTime value)
589 WndProperty &control = GetControl(i);
590 RoughTimeDataField &df = *(RoughTimeDataField *)control.GetDataField();
591 df.SetValue(value);
592 control.RefreshDisplay();
595 void
596 RowFormWidget::LoadValueTime(unsigned i, int value)
598 WndProperty &control = GetControl(i);
599 DataFieldTime &df = *(DataFieldTime *)control.GetDataField();
600 assert(df.GetType() == DataField::Type::TIME);
601 df.Set(value);
602 control.RefreshDisplay();
605 bool
606 RowFormWidget::GetValueBoolean(unsigned i) const
608 const DataFieldBoolean &df =
609 (const DataFieldBoolean &)GetDataField(i);
610 assert(df.GetType() == DataField::Type::BOOLEAN);
611 return df.GetAsBoolean();
615 RowFormWidget::GetValueInteger(unsigned i) const
617 return GetDataField(i).GetAsInteger();
620 fixed
621 RowFormWidget::GetValueFloat(unsigned i) const
623 const DataFieldFloat &df =
624 (const DataFieldFloat &)GetDataField(i);
625 assert(df.GetType() == DataField::Type::REAL);
626 return df.GetAsFixed();
629 Angle
630 RowFormWidget::GetValueAngle(unsigned i) const
632 const AngleDataField &df =
633 (const AngleDataField &)GetDataField(i);
634 assert(df.GetType() == DataField::Type::ANGLE);
635 return df.GetValue();
638 unsigned
639 RowFormWidget::GetValueIntegerAngle(unsigned i) const
641 const AngleDataField &df =
642 (const AngleDataField &)GetDataField(i);
643 assert(df.GetType() == DataField::Type::ANGLE);
644 return df.GetIntegerValue();
647 RoughTime
648 RowFormWidget::GetValueRoughTime(unsigned i) const
650 const RoughTimeDataField &df =
651 (const RoughTimeDataField &)GetDataField(i);
652 assert(df.GetType() == DataField::Type::ROUGH_TIME);
653 return df.GetValue();
656 bool
657 RowFormWidget::SaveValue(unsigned i, bool &value, bool negated) const
659 bool new_value = GetValueBoolean(i);
660 if (negated)
661 new_value = !new_value;
662 if (new_value == value)
663 return false;
665 value = new_value;
666 return true;
669 bool
670 RowFormWidget::SaveValue(unsigned i, int &value) const
672 int new_value = GetValueInteger(i);
673 if (new_value == value)
674 return false;
676 value = new_value;
677 return true;
680 bool
681 RowFormWidget::SaveValue(unsigned i, uint8_t &value) const
683 int new_value = GetValueInteger(i);
684 if (new_value == value || new_value < 0)
685 return false;
687 value = (uint8_t)new_value;
688 return true;
691 bool
692 RowFormWidget::SaveValue(unsigned i, uint16_t &value) const
694 int new_value = GetValueInteger(i);
695 if (new_value == value || new_value < 0)
696 return false;
698 value = (uint16_t)new_value;
699 return true;
702 bool
703 RowFormWidget::SaveValue(unsigned i, fixed &value) const
705 fixed new_value = GetValueFloat(i);
706 if (new_value == value)
707 return false;
709 value = new_value;
710 return true;
713 bool
714 RowFormWidget::SaveValue(unsigned i, Angle &value_r) const
716 unsigned old_value = AngleDataField::Import(value_r);
717 unsigned new_value = GetValueIntegerAngle(i);
718 if (new_value == old_value)
719 return false;
721 value_r = GetValueAngle(i);
722 return true;
725 bool
726 RowFormWidget::SaveValue(unsigned i, RoughTime &value_r) const
728 const auto new_value = GetValueRoughTime(i);
729 if (new_value == value_r)
730 return false;
732 value_r = new_value;
733 return true;
736 bool
737 RowFormWidget::SaveValue(unsigned i, TCHAR *string, size_t max_size) const
739 const TCHAR *new_value = GetDataField(i).GetAsString();
740 assert(new_value != NULL);
742 if (_tcscmp(string, new_value) == 0)
743 return false;
745 CopyString(string, new_value, max_size);
746 return true;
749 bool
750 RowFormWidget::SaveValue(unsigned i, const char *registry_key,
751 TCHAR *string, size_t max_size) const
753 if (!SaveValue(i, string, max_size))
754 return false;
756 Profile::Set(registry_key, string);
757 return true;
760 bool
761 RowFormWidget::SaveValue(unsigned i, const char *registry_key,
762 bool &value, bool negated) const
764 if (!SaveValue(i, value, negated))
765 return false;
767 Profile::Set(registry_key, value);
768 return true;
771 bool
772 RowFormWidget::SaveValue(unsigned i, const char *registry_key,
773 int &value) const
775 if (!SaveValue(i, value))
776 return false;
778 Profile::Set(registry_key, value);
779 return true;
782 bool
783 RowFormWidget::SaveValue(unsigned i, const char *registry_key,
784 uint8_t &value) const
786 if (!SaveValue(i, value))
787 return false;
789 Profile::Set(registry_key, value);
790 return true;
793 bool
794 RowFormWidget::SaveValue(unsigned i, const char *registry_key,
795 uint16_t &value) const
797 if (!SaveValue(i, value))
798 return false;
800 Profile::Set(registry_key, value);
801 return true;
804 bool
805 RowFormWidget::SaveValue(unsigned i, const char *registry_key,
806 fixed &value) const
808 if (!SaveValue(i, value))
809 return false;
811 Profile::Set(registry_key, value);
812 return true;
815 bool
816 RowFormWidget::SaveValue(unsigned i, UnitGroup unit_group, fixed &value) const
818 const DataFieldFloat &df =
819 (const DataFieldFloat &)GetDataField(i);
820 assert(df.GetType() == DataField::Type::REAL);
822 const Unit unit = Units::GetUserUnitByGroup(unit_group);
823 fixed new_value = df.GetAsFixed();
824 fixed old_value = Units::ToUserUnit(value, unit);
826 if (fabs(new_value - old_value) < df.GetStep() / 100)
827 return false;
829 value = Units::ToSysUnit(new_value, unit);
830 return true;
833 bool
834 RowFormWidget::SaveValue(unsigned i, UnitGroup unit_group,
835 const char *registry_key, fixed &value) const
837 const DataFieldFloat &df =
838 (const DataFieldFloat &)GetDataField(i);
839 assert(df.GetType() == DataField::Type::REAL);
841 const Unit unit = Units::GetUserUnitByGroup(unit_group);
842 fixed new_value = df.GetAsFixed();
843 fixed old_value = Units::ToUserUnit(value, unit);
845 if (fabs(new_value - old_value) < df.GetStep() / 100)
846 return false;
848 value = Units::ToSysUnit(new_value, unit);
849 Profile::Set(registry_key, value);
850 return true;
853 bool
854 RowFormWidget::SaveValue(unsigned i, UnitGroup unit_group,
855 const char *registry_key, unsigned int &value) const
857 const DataFieldFloat &df =
858 (const DataFieldFloat &)GetDataField(i);
859 assert(df.GetType() == DataField::Type::INTEGER ||
860 df.GetType() == DataField::Type::REAL);
862 const Unit unit = Units::GetUserUnitByGroup(unit_group);
863 fixed new_value = df.GetAsFixed();
864 fixed old_value = Units::ToUserUnit(fixed(value), unit);
866 if (fabs(new_value - old_value) < df.GetStep() / 100)
867 return false;
869 value = iround(Units::ToSysUnit(new_value, unit));
870 Profile::Set(registry_key, value);
871 return true;
874 bool
875 RowFormWidget::SaveValueFileReader(unsigned i, const char *registry_key)
877 const DataFieldFileReader *dfe =
878 (const DataFieldFileReader *)GetControl(i).GetDataField();
879 TCHAR new_value[MAX_PATH];
880 _tcscpy(new_value, dfe->GetPathFile());
881 ContractLocalPath(new_value);
883 const WideToUTF8Converter new_value2(new_value);
884 if (!new_value2.IsValid())
885 return false;
887 const char *old_value = Profile::Get(registry_key, "");
888 if (StringIsEqual(old_value, new_value2))
889 return false;
891 Profile::Set(registry_key, new_value2);
892 return true;
895 unsigned
896 RowFormWidget::GetRecommendedCaptionWidth() const
898 const bool expert = UIGlobals::GetDialogSettings().expert;
900 unsigned w = 0;
901 for (const auto &i : rows) {
902 if (!i.IsAvailable(expert))
903 continue;
905 if (i.type == Row::Type::EDIT) {
906 unsigned x = i.GetControl().GetRecommendedCaptionWidth();
907 if (x > w)
908 w = x;
912 return w;
915 void
916 RowFormWidget::UpdateLayout()
918 PixelRect current_rect = GetWindow()->GetClientRect();
919 const unsigned total_width = current_rect.right - current_rect.left;
920 const unsigned total_height = current_rect.bottom - current_rect.top;
921 current_rect.bottom = current_rect.top;
923 const bool expert = UIGlobals::GetDialogSettings().expert;
925 /* first row traversal: count the number of "elastic" rows and
926 determine the minimum total height */
927 unsigned min_height = 0;
928 unsigned n_elastic = 0;
929 unsigned caption_width = 0;
931 for (const auto &i : rows) {
932 if (!i.IsAvailable(expert))
933 continue;
935 min_height += i.GetMinimumHeight(vertical);
936 if (i.IsElastic(vertical))
937 ++n_elastic;
939 if (!vertical && i.type == Row::Type::EDIT) {
940 unsigned cw = i.GetControl().GetRecommendedCaptionWidth();
941 if (cw > caption_width)
942 caption_width = cw;
946 if (!vertical && caption_width * 3 > total_width * 2)
947 caption_width = total_width * 2 / 3;
949 /* how much excess height in addition to the minimum height? */
950 unsigned excess_height = min_height < total_height
951 ? total_height - min_height
952 : 0;
954 /* second row traversal: now move and resize the rows */
955 for (auto &i : rows) {
956 if (!i.IsAvailable(expert)) {
957 if (i.type == Row::Type::WIDGET)
958 i.GetWidget().Hide();
959 else if (i.type != Row::Type::DUMMY)
960 i.GetWindow().Hide();
962 continue;
965 /* determine this row's height */
966 UPixelScalar height = i.GetMinimumHeight(vertical);
967 if (excess_height > 0 && i.IsElastic(vertical)) {
968 assert(n_elastic > 0);
970 /* distribute excess height among all elastic rows */
971 unsigned grow_height = excess_height / n_elastic;
972 if (grow_height > 0) {
973 height += grow_height;
974 const UPixelScalar max_height = i.GetMaximumHeight(vertical);
975 if (height > max_height) {
976 /* never grow beyond declared maximum height */
977 height = max_height;
978 grow_height = max_height - height;
981 excess_height -= grow_height;
984 --n_elastic;
987 if (i.type == Row::Type::WIDGET) {
988 Widget &widget = i.GetWidget();
990 /* TODO: visible check - hard to implement without remembering
991 the control position, because Widget::Show() wants a
992 PixelRect parameter */
994 NextControlRect(current_rect, height);
996 if (!i.initialised) {
997 i.initialised = true;
998 widget.Initialise(*(ContainerWindow *)GetWindow(), current_rect);
1001 if (!i.prepared) {
1002 i.prepared = true;
1003 widget.Prepare(*(ContainerWindow *)GetWindow(), current_rect);
1006 widget.Show(current_rect);
1007 continue;
1010 Window &window = i.GetWindow();
1012 if (i.visible)
1013 window.Show();
1015 if (i.type == Row::Type::EDIT &&
1016 i.GetControl().HasCaption()) {
1017 if (vertical)
1018 i.GetControl().SetCaptionWidth(-1);
1019 else if (caption_width > 0)
1020 i.GetControl().SetCaptionWidth(caption_width);
1023 /* finally move and resize */
1024 NextControlRect(current_rect, height);
1025 window.Move(current_rect);
1028 assert(excess_height == 0 || n_elastic == 0);
1031 PixelSize
1032 RowFormWidget::GetMinimumSize() const
1034 const unsigned value_width =
1035 look.text_font->TextSize(_T("Foo Bar Foo Bar")).cx;
1037 const bool expert = UIGlobals::GetDialogSettings().expert;
1039 const unsigned edit_width = vertical
1040 ? std::max(GetRecommendedCaptionWidth(), value_width)
1041 : (GetRecommendedCaptionWidth() + value_width);
1043 PixelSize size(edit_width, 0u);
1044 for (const auto &i : rows)
1045 if (i.IsAvailable(expert))
1046 size.cy += i.GetMinimumHeight(vertical);
1048 return size;
1051 PixelSize
1052 RowFormWidget::GetMaximumSize() const
1054 const unsigned value_width =
1055 look.text_font->TextSize(_T("Foo Bar Foo Bar")).cx * 2;
1057 const unsigned edit_width = vertical
1058 ? std::max(GetRecommendedCaptionWidth(), value_width)
1059 : (GetRecommendedCaptionWidth() + value_width);
1061 PixelSize size(edit_width, 0u);
1062 for (const auto &i : rows)
1063 size.cy += i.GetMaximumHeight(vertical);
1065 return size;
1068 void
1069 RowFormWidget::Initialise(ContainerWindow &parent, const PixelRect &rc)
1071 assert(!IsDefined());
1072 assert(rows.empty());
1074 WindowStyle style;
1075 style.Hide();
1076 style.ControlParent();
1078 SetWindow(new PanelControl(parent, look, rc, style));
1081 void
1082 RowFormWidget::Show(const PixelRect &rc)
1084 PanelControl &panel = *(PanelControl *)GetWindow();
1085 panel.Move(rc);
1087 UpdateLayout();
1089 panel.Show();
1092 void
1093 RowFormWidget::Move(const PixelRect &rc)
1095 PanelControl &panel = *(PanelControl *)GetWindow();
1096 panel.Move(rc);
1098 UpdateLayout();
1101 bool
1102 RowFormWidget::SetFocus()
1104 if (rows.empty())
1105 return false;
1107 PanelControl &panel = *(PanelControl *)GetWindow();
1108 return panel.FocusFirstControl();