X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fdialog.cpp;h=3b59f6aba4740d2a6e53ba24490e42c1edcdf398;hb=9dfae1c9b270ee369c6362903a9205b30751b95f;hp=fb1263e4c1124b561a43c6353667bba7cc89f97f;hpb=5265d3cc1517566910718738ee6fa48e2466d3ea;p=xonotic%2Fnetradiant.git diff --git a/radiant/dialog.cpp b/radiant/dialog.cpp index fb1263e4..3b59f6ab 100644 --- a/radiant/dialog.cpp +++ b/radiant/dialog.cpp @@ -1,23 +1,23 @@ /* -Copyright (C) 1999-2007 id Software, Inc. and contributors. -For a list of contributors, see the accompanying CONTRIBUTORS file. + Copyright (C) 1999-2006 Id Software, Inc. and contributors. + For a list of contributors, see the accompanying CONTRIBUTORS file. -This file is part of GtkRadiant. + This file is part of GtkRadiant. -GtkRadiant is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + GtkRadiant is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -GtkRadiant is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + GtkRadiant is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with GtkRadiant; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + You should have received a copy of the GNU General Public License + along with GtkRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ // // Base dialog class, provides a way to run modal dialogs and @@ -26,277 +26,691 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // Leonardo Zide (leo@lokigames.com) // -#include "stdafx.h" +#include "dialog.h" + #include + +#include "debugging/debugging.h" + + +#include "mainframe.h" + #include -#include -typedef struct +#include "stream/stringstream.h" +#include "convert.h" +#include "gtkutil/dialog.h" +#include "gtkutil/button.h" +#include "gtkutil/entry.h" +#include "gtkutil/image.h" + +#include "gtkmisc.h" + + +ui::Entry DialogEntry_new() +{ + auto entry = ui::Entry(ui::New); + entry.show(); + entry.dimensions(64, -1); + return entry; +} + +class DialogEntryRow { +public: + DialogEntryRow(ui::Widget row, ui::Entry entry) : m_row(row), m_entry(entry) + { + } + + ui::Widget m_row; + ui::Entry m_entry; +}; + +DialogEntryRow DialogEntryRow_new(const char *name) +{ + auto alignment = ui::Alignment(0.0, 0.5, 0.0, 0.0); + alignment.show(); + + auto entry = DialogEntry_new(); + alignment.add(entry); + + return DialogEntryRow(ui::Widget(DialogRow_new(name, alignment)), entry); +} + + +ui::SpinButton DialogSpinner_new(double value, double lower, double upper, int fraction) { - GtkObject *object; - void *buffer; - DLG_DATA_TYPE type; -} DLG_DATA; + double step = 1.0 / double(fraction); + unsigned int digits = 0; + for (; fraction > 1; fraction /= 10) { + ++digits; + } + auto spin = ui::SpinButton(ui::Adjustment(value, lower, upper, step, 10, 0), step, digits); + spin.show(); + spin.dimensions(64, -1); + return spin; +} + +class DialogSpinnerRow { +public: + DialogSpinnerRow(ui::Widget row, ui::SpinButton spin) : m_row(row), m_spin(spin) + { + } + + ui::Widget m_row; + ui::SpinButton m_spin; +}; + +DialogSpinnerRow DialogSpinnerRow_new(const char *name, double value, double lower, double upper, int fraction) +{ + auto alignment = ui::Alignment(0.0, 0.5, 0.0, 0.0); + alignment.show(); + + auto spin = DialogSpinner_new(value, lower, upper, fraction); + alignment.add(spin); + + return DialogSpinnerRow(ui::Widget(DialogRow_new(name, alignment)), spin); +} + + +struct BoolToggle { + static void Export(const ui::ToggleButton &self, const Callback &returnz) + { + returnz(self.active()); + } + + static void Import(ui::ToggleButton &self, bool value) + { + self.active(value); + } +}; + +using BoolToggleImportExport = PropertyAdaptor; + +struct IntEntry { + static void Export(const ui::Entry &self, const Callback &returnz) + { + returnz(atoi(gtk_entry_get_text(self))); + } + + static void Import(ui::Entry &self, int value) + { + entry_set_int(self, value); + } +}; + +using IntEntryImportExport = PropertyAdaptor; + +struct IntRadio { + static void Export(const ui::RadioButton &self, const Callback &returnz) + { + returnz(radio_button_get_active(self)); + } + + static void Import(ui::RadioButton &self, int value) + { + radio_button_set_active(self, value); + } +}; + +using IntRadioImportExport = PropertyAdaptor; + +struct IntCombo { + static void Export(const ui::ComboBox &self, const Callback &returnz) + { + returnz(gtk_combo_box_get_active(self)); + } + + static void Import(ui::ComboBox &self, int value) + { + gtk_combo_box_set_active(self, value); + } +}; + +using IntComboImportExport = PropertyAdaptor; + +struct IntAdjustment { + static void Export(const ui::Adjustment &self, const Callback &returnz) + { + returnz(int(gtk_adjustment_get_value(self))); + } + + static void Import(ui::Adjustment &self, int value) + { + gtk_adjustment_set_value(self, value); + } +}; + +using IntAdjustmentImportExport = PropertyAdaptor; + +struct IntSpinner { + static void Export(const ui::SpinButton &self, const Callback &returnz) + { + returnz(gtk_spin_button_get_value_as_int(self)); + } + + static void Import(ui::SpinButton &self, int value) + { + gtk_spin_button_set_value(self, value); + } +}; + +using IntSpinnerImportExport = PropertyAdaptor; + +struct TextEntry { + static void Export(const ui::Entry &self, const Callback &returnz) + { + returnz(gtk_entry_get_text(self)); + } + + static void Import(ui::Entry &self, const char *value) + { + self.text(value); + } +}; + +using TextEntryImportExport = PropertyAdaptor; + +struct SizeEntry { + static void Export(const ui::Entry &self, const Callback &returnz) + { + int value = atoi(gtk_entry_get_text(self)); + if (value < 0) { + value = 0; + } + returnz(value); + } + + static void Import(ui::Entry &self, std::size_t value) + { + entry_set_int(self, int(value)); + } +}; + +using SizeEntryImportExport = PropertyAdaptor; + +struct FloatEntry { + static void Export(const ui::Entry &self, const Callback &returnz) + { + returnz(float(atof(gtk_entry_get_text(self)))); + } + + static void Import(ui::Entry &self, float value) + { + entry_set_float(self, value); + } +}; + +using FloatEntryImportExport = PropertyAdaptor; + +struct FloatSpinner { + static void Export(const ui::SpinButton &self, const Callback &returnz) + { + returnz(float(gtk_spin_button_get_value(self))); + } + + static void Import(ui::SpinButton &self, float value) + { + gtk_spin_button_set_value(self, value); + } +}; + +using FloatSpinnerImportExport = PropertyAdaptor; + + +template +class CallbackDialogData : public DLG_DATA { + Property m_pWidget; + Property m_pData; + +public: + CallbackDialogData(const Property &pWidget, const Property &pData) + : m_pWidget(pWidget), m_pData(pData) + { + } + + void release() + { + delete this; + } + + void importData() const + { + m_pData.get(m_pWidget.set); + } + + void exportData() const + { + m_pWidget.get(m_pData.set); + } +}; + +template +void AddDataCustom(DialogDataList &self, typename Widget::Type widget, Property const &property) +{ + using Self = typename Widget::Type; + using T = typename Widget::Other; + using native = typename std::remove_pointer::type; + struct Wrapper { + static void Export(const native &self, const Callback &returnz) + { + native *p = &const_cast(self); + auto widget = Self::from(p); + Widget::Get::thunk_(widget, returnz); + } + + static void Import(native &self, T value) + { + native *p = &self; + auto widget = Self::from(p); + Widget::Set::thunk_(widget, value); + } + }; + self.push_back(new CallbackDialogData( + make_property>(*static_cast(widget)), + property + )); +} + +template +void AddData(DialogDataList &self, typename Widget::Type widget, D &data) +{ + AddDataCustom(self, widget, make_property>(data)); +} // ============================================================================= // Dialog class -Dialog::Dialog () +Dialog::Dialog() : m_window(ui::null), m_parent(ui::null) +{ +} + +Dialog::~Dialog() +{ + for (DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i) { + (*i)->release(); + } + + ASSERT_MESSAGE(!m_window, "dialog window not destroyed"); +} + +void Dialog::ShowDlg() +{ + ASSERT_MESSAGE(m_window, "dialog was not constructed"); + importData(); + m_window.show(); +} + +void Dialog::HideDlg() +{ + ASSERT_MESSAGE(m_window, "dialog was not constructed"); + exportData(); + m_window.hide(); +} + +static gint delete_event_callback(ui::Widget widget, GdkEvent *event, gpointer data) +{ + reinterpret_cast( data )->HideDlg(); + reinterpret_cast( data )->EndModal(eIDCANCEL); + return TRUE; +} + +void Dialog::Create() +{ + ASSERT_MESSAGE(!m_window, "dialog cannot be constructed"); + + m_window = BuildDialog(); + m_window.connect("delete_event", G_CALLBACK(delete_event_callback), this); +} + +void Dialog::Destroy() +{ + ASSERT_MESSAGE(m_window, "dialog cannot be destroyed"); + + m_window.destroy(); + m_window = ui::Window{ui::null}; +} + + +void Dialog::AddBoolToggleData(ui::ToggleButton widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddIntRadioData(ui::RadioButton widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddTextEntryData(ui::Entry widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddIntEntryData(ui::Entry widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddSizeEntryData(ui::Entry widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddFloatEntryData(ui::Entry widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddFloatSpinnerData(ui::SpinButton widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddIntSpinnerData(ui::SpinButton widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddIntAdjustmentData(ui::Adjustment widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + +void Dialog::AddIntComboData(ui::ComboBox widget, Property const &cb) +{ + AddDataCustom(m_data, widget, cb); +} + + +void Dialog::AddDialogData(ui::ToggleButton widget, bool &data) { - m_pDataList = (GSList*)NULL; - m_nReturn = IDCANCEL; - m_bNeedBuild = true; - m_nLoop = 0; + AddData(m_data, widget, data); } -Dialog::~Dialog () +void Dialog::AddDialogData(ui::RadioButton widget, int &data) { - while (m_pDataList) - { - free (m_pDataList->data); - m_pDataList = g_slist_remove (m_pDataList, m_pDataList->data); - } + AddData(m_data, widget, data); +} - if (m_pWidget != NULL) - gtk_widget_destroy (m_pWidget); +void Dialog::AddDialogData(ui::Entry widget, CopiedString &data) +{ + AddData(m_data, widget, data); } -// i suspect that this is redundant - gtk manages to remember the data stored in its widgets across a hide/show -void Dialog::ShowDlg () +void Dialog::AddDialogData(ui::Entry widget, int &data) { - Create (); - UpdateData (FALSE); - gtk_widget_show (m_pWidget); + AddData(m_data, widget, data); } -void Dialog::HideDlg () +void Dialog::AddDialogData(ui::Entry widget, std::size_t &data) { - UpdateData (TRUE); - gtk_widget_hide (m_pWidget); + AddData(m_data, widget, data); } -static gint delete_event_callback(GtkWidget *widget, GdkEvent* event, gpointer data) +void Dialog::AddDialogData(ui::Entry widget, float &data) { - reinterpret_cast(data)->HideDlg(); - reinterpret_cast(data)->EndModal(IDCANCEL); - return TRUE; + AddData(m_data, widget, data); } -void Dialog::Create () +void Dialog::AddDialogData(ui::SpinButton widget, float &data) { - if (m_bNeedBuild) - { - m_pWidget = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_signal_connect (GTK_OBJECT (m_pWidget), "delete_event", - GTK_SIGNAL_FUNC (delete_event_callback), this); - gtk_signal_connect (GTK_OBJECT (m_pWidget), "destroy", - GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL); - g_object_set_data (G_OBJECT (m_pWidget), "loop", &m_nLoop); - g_object_set_data (G_OBJECT (m_pWidget), "ret", &m_nReturn); + AddData(m_data, widget, data); +} - BuildDialog(); - m_bNeedBuild = false; - } +void Dialog::AddDialogData(ui::SpinButton widget, int &data) +{ + AddData(m_data, widget, data); } -void Dialog::Destroy () +void Dialog::AddDialogData(ui::Adjustment widget, int &data) { - if (m_pWidget != NULL) - { - gtk_widget_destroy (m_pWidget); - m_pWidget = NULL; - } + AddData(m_data, widget, data); } -void Dialog::AddDialogData( GtkObject *object, void *buf, DLG_DATA_TYPE type ) +void Dialog::AddDialogData(ui::ComboBox widget, int &data) { - DLG_DATA *data; + AddData(m_data, widget, data); +} - data = (DLG_DATA*)qmalloc (sizeof(DLG_DATA)); - data->object = object; - data->buffer = buf; - data->type = type; +void Dialog::exportData() +{ + for (DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i) { + (*i)->exportData(); + } +} - m_pDataList = g_slist_append (m_pDataList, data); +void Dialog::importData() +{ + for (DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i) { + (*i)->importData(); + } } -void Dialog::AddModalButton( GtkWidget *widget, int ret ) { - gtk_signal_connect( GTK_OBJECT( widget ), "clicked", - GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( ret ) ); +void Dialog::EndModal(EMessageBoxReturn code) +{ + m_modal.loop = 0; + m_modal.ret = code; } -void Dialog::UpdateData (bool retrieve) - { - DLG_DATA *data; - GSList *lst; - char buf[32]; - - if (retrieve) +EMessageBoxReturn Dialog::DoModal() +{ + importData(); + + PreModal(); + + EMessageBoxReturn ret = modal_dialog_show(m_window, m_modal); + ASSERT_TRUE((bool) m_window); + if (ret == eIDOK) { + exportData(); + } + + m_window.hide(); + + PostModal(m_modal.ret); + + return m_modal.ret; +} + + +ui::CheckButton Dialog::addCheckBox(ui::VBox vbox, const char *name, const char *flag, Property const &cb) +{ + auto check = ui::CheckButton(flag); + check.show(); + AddBoolToggleData(check, cb); + + DialogVBox_packRow(vbox, ui::Widget(DialogRow_new(name, check))); + return check; +} + +ui::CheckButton Dialog::addCheckBox(ui::VBox vbox, const char *name, const char *flag, bool &data) +{ + return addCheckBox(vbox, name, flag, make_property(data)); +} + +void Dialog::addCombo(ui::VBox vbox, const char *name, StringArrayRange values, Property const &cb) +{ + auto alignment = ui::Alignment(0.0, 0.5, 0.0, 0.0); + alignment.show(); { - for (lst = m_pDataList; lst != NULL; lst = g_slist_next (lst)) - { - data = (DLG_DATA*)lst->data; - - switch (data->type) - { - case DLG_CHECK_BOOL: - *(bool*)data->buffer = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->object)); - break; - case DLG_RADIO_INT: - { - GSList *radio = gtk_radio_button_group (GTK_RADIO_BUTTON (data->object)); - *(int*)data->buffer = g_slist_length (radio) - 1; - for (; radio; radio = g_slist_next (radio)) - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio->data))) - break; - else - (*(int*)data->buffer)--; - } break; - case DLG_ENTRY_TEXT: - { - const char *txt; - Str* str; - str = (Str*)data->buffer; - txt = gtk_entry_get_text (GTK_ENTRY (data->object)); - *str = txt; - } break; - case DLG_ENTRY_FLOAT: - *(float*)data->buffer = atof (gtk_entry_get_text (GTK_ENTRY (data->object))); - break; - case DLG_ENTRY_INT: - *(int*)data->buffer = atoi (gtk_entry_get_text (GTK_ENTRY (data->object))); - break; - case DLG_SPIN_FLOAT: - *(float*)data->buffer = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (data->object)); - break; - case DLG_SPIN_INT: - *(int*)data->buffer = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (data->object)); - break; - case DLG_ADJ_INT: - *(int*)data->buffer = (int) GTK_ADJUSTMENT (data->object)->value; - break; - case DLG_COMBO_INT: - { - GList *lst = GTK_LIST (GTK_COMBO (data->object)->list)->children; - char *label; - const char *entry; - int i; - - *(int*)data->buffer = -1; - entry = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (data->object)->entry)); - - for (i = 0; lst != NULL; lst = g_list_next (lst)) - { - gtk_label_get (GTK_LABEL (GTK_BIN (lst->data)->child), &label); - - if (strcmp (label, entry) == 0) - { - *(int*)data->buffer = i; - break; - } - i++; - } - } - break; - case DLG_COMBO_BOX_INT: { - *(int*)data->buffer = gtk_combo_box_get_active( GTK_COMBO_BOX( data->object ) ); - } - break; + auto combo = ui::ComboBoxText(ui::New); + for (StringArrayRange::Iterator i = values.first; i != values.last; ++i) { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), *i); } - } + + AddIntComboData(combo, cb); + + combo.show(); + alignment.add(combo); } - else - { - for (lst = m_pDataList; lst != NULL; lst = g_slist_next (lst)) - { - data = (DLG_DATA*)lst->data; - switch (data->type) + auto row = DialogRow_new(name, alignment); + DialogVBox_packRow(vbox, row); +} + +void Dialog::addCombo(ui::VBox vbox, const char *name, int &data, StringArrayRange values) +{ + addCombo(vbox, name, values, make_property(data)); +} + +void +Dialog::addSlider(ui::VBox vbox, const char *name, int &data, gboolean draw_value, const char *low, const char *high, + double value, double lower, double upper, double step_increment, double page_increment) +{ +#if 0 + if ( draw_value == FALSE ) { + auto hbox2 = ui::HBox( FALSE, 0 ); + hbox2.show(); + vbox.pack_start( hbox2 , FALSE, FALSE, 0 ); { - case DLG_CHECK_BOOL: - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->object), *(bool*)data->buffer); - break; - case DLG_RADIO_INT: - { - GSList *radio = gtk_radio_button_group (GTK_RADIO_BUTTON (data->object)); - gpointer btn = g_slist_nth_data (radio, g_slist_length (radio) - (*(int*)data->buffer) - 1); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (btn), TRUE); - } break; - case DLG_ENTRY_TEXT: - { - Str* str; - str = (Str*)data->buffer; - const char *txt = str->GetBuffer (); - gtk_entry_set_text (GTK_ENTRY (data->object), txt); - } break; - case DLG_ENTRY_FLOAT: - sprintf (buf, "%g", (*(float*)data->buffer)); - gtk_entry_set_text (GTK_ENTRY (data->object), buf); - break; - case DLG_ENTRY_INT: - sprintf (buf, "%d", (*(int*)data->buffer)); - gtk_entry_set_text (GTK_ENTRY (data->object), buf); - break; - case DLG_SPIN_FLOAT: - gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->object), (*(float*)data->buffer)); - break; - case DLG_SPIN_INT: - gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->object), (*(int*)data->buffer)); - break; - case DLG_ADJ_INT: - gtk_adjustment_set_value (GTK_ADJUSTMENT (data->object), (*(int*)data->buffer)); - break; - case DLG_COMBO_INT: { - GList *lst = GTK_LIST (GTK_COMBO (data->object)->list)->children; - char *entry = ""; - - if (*(int*)data->buffer != -1) - { - lst = g_list_nth (lst, *(int*)data->buffer); - if (lst != NULL) - gtk_label_get (GTK_LABEL (GTK_BIN (lst->data)->child), &entry); - } - - gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (data->object)->entry), entry); - } - break; - case DLG_COMBO_BOX_INT: { - gtk_combo_box_set_active( GTK_COMBO_BOX( data->object ), *(int*)data->buffer ); - } - break; + ui::Widget label = ui::Label( low ); + label.show(); + hbox2.pack_start( label, FALSE, FALSE, 0 ); + } + { + ui::Widget label = ui::Label( high ); + label.show(); + hbox2.pack_end(label, FALSE, FALSE, 0); } - } } +#endif + + // adjustment + auto adj = ui::Adjustment(value, lower, upper, step_increment, page_increment, 0); + AddIntAdjustmentData(adj, make_property(data)); + + // scale + auto alignment = ui::Alignment(0.0, 0.5, 1.0, 0.0); + alignment.show(); + + ui::Widget scale = ui::HScale(adj); + gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_LEFT); + scale.show(); + alignment.add(scale); + + gtk_scale_set_draw_value(GTK_SCALE(scale), draw_value); + gtk_scale_set_digits(GTK_SCALE(scale), 0); + + auto row = DialogRow_new(name, alignment); + DialogVBox_packRow(vbox, row); } -void Dialog::EndModal( int code ) { - m_nLoop = 0; - m_nReturn = code; +void Dialog::addRadio(ui::VBox vbox, const char *name, StringArrayRange names, Property const &cb) +{ + auto alignment = ui::Alignment(0.0, 0.5, 0.0, 0.0); + alignment.show();; + { + RadioHBox radioBox = RadioHBox_new(names); + alignment.add(radioBox.m_hbox); + AddIntRadioData(radioBox.m_radio, cb); + } + + auto row = DialogRow_new(name, alignment); + DialogVBox_packRow(vbox, row); } -int Dialog::DoModal() +void Dialog::addRadio(ui::VBox vbox, const char *name, int &data, StringArrayRange names) { - Create(); - UpdateData( FALSE ); + addRadio(vbox, name, names, make_property(data)); +} - PreModal(); +void Dialog::addRadioIcons(ui::VBox vbox, const char *name, StringArrayRange icons, Property const &cb) +{ + auto table = ui::Table(2, icons.last - icons.first, FALSE); + table.show(); + + gtk_table_set_row_spacings(table, 5); + gtk_table_set_col_spacings(table, 5); + + GSList *group = 0; + ui::RadioButton radio{ui::null}; + for (StringArrayRange::Iterator icon = icons.first; icon != icons.last; ++icon) { + guint pos = static_cast( icon - icons.first ); + auto image = new_local_image(*icon); + image.show(); + table.attach(image, {pos, pos + 1, 0, 1}, {0, 0}); - gtk_grab_add( m_pWidget ); - gtk_widget_show( m_pWidget ); + radio = ui::RadioButton::from(gtk_radio_button_new(group)); + radio.show(); + table.attach(radio, {pos, pos + 1, 1, 2}, {0, 0}); + + group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)); + } - m_nLoop = 1; - while ( m_nLoop ) { - gtk_main_iteration(); - } + AddIntRadioData(radio, cb); - if ( m_pWidget != NULL ) { - UpdateData( TRUE ); + DialogVBox_packRow(vbox, DialogRow_new(name, table)); +} + +void Dialog::addRadioIcons(ui::VBox vbox, const char *name, int &data, StringArrayRange icons) +{ + addRadioIcons(vbox, name, icons, make_property(data)); +} - gtk_grab_remove( m_pWidget ); - gtk_widget_hide( m_pWidget ); - } - PostModal( m_nReturn ); +ui::Widget Dialog::addIntEntry(ui::VBox vbox, const char *name, Property const &cb) +{ + DialogEntryRow row(DialogEntryRow_new(name)); + AddIntEntryData(row.m_entry, cb); + DialogVBox_packRow(vbox, row.m_row); + return row.m_row; +} + +ui::Widget Dialog::addSizeEntry(ui::VBox vbox, const char *name, Property const &cb) +{ + DialogEntryRow row(DialogEntryRow_new(name)); + AddSizeEntryData(row.m_entry, cb); + DialogVBox_packRow(vbox, row.m_row); + return row.m_row; +} + +ui::Widget Dialog::addFloatEntry(ui::VBox vbox, const char *name, Property const &cb) +{ + DialogEntryRow row(DialogEntryRow_new(name)); + AddFloatEntryData(row.m_entry, cb); + DialogVBox_packRow(vbox, row.m_row); + return row.m_row; +} - return m_nReturn; +ui::Widget +Dialog::addPathEntry(ui::VBox vbox, const char *name, bool browse_directory, Property const &cb) +{ + PathEntry pathEntry = PathEntry_new(); + pathEntry.m_button.connect("clicked", G_CALLBACK( + browse_directory ? button_clicked_entry_browse_directory : button_clicked_entry_browse_file), + pathEntry.m_entry); + + AddTextEntryData(pathEntry.m_entry, cb); + + auto row = DialogRow_new(name, ui::Widget(pathEntry.m_frame)); + DialogVBox_packRow(vbox, row); + + return row; +} + +ui::Widget Dialog::addPathEntry(ui::VBox vbox, const char *name, CopiedString &data, bool browse_directory) +{ + return addPathEntry(vbox, name, browse_directory, make_property(data)); +} + +ui::SpinButton +Dialog::addSpinner(ui::VBox vbox, const char *name, double value, double lower, double upper, Property const &cb) +{ + DialogSpinnerRow row(DialogSpinnerRow_new(name, value, lower, upper, 1)); + AddIntSpinnerData(row.m_spin, cb); + DialogVBox_packRow(vbox, row.m_row); + return row.m_spin; +} + +ui::SpinButton Dialog::addSpinner(ui::VBox vbox, const char *name, int &data, double value, double lower, double upper) +{ + return addSpinner(vbox, name, value, lower, upper, make_property(data)); +} + +ui::SpinButton +Dialog::addSpinner(ui::VBox vbox, const char *name, double value, double lower, double upper, Property const &cb) +{ + DialogSpinnerRow row(DialogSpinnerRow_new(name, value, lower, upper, 10)); + AddFloatSpinnerData(row.m_spin, cb); + DialogVBox_packRow(vbox, row.m_row); + return row.m_spin; }