[glom] Formatting: Choices: Add a Display as radio buttons option.



commit a9e0ee4403c2028b1e09ec53f8262bca35f0ceac
Author: Murray Cumming <murrayc murrayc com>
Date:   Tue Mar 23 10:43:55 2010 +0100

    Formatting: Choices: Add a Display as radio buttons option.
    
    * glom/glom_developer.glade: formatting: choices: Add a Show As Radio
        Buttons checkbox.
    * glom/libglom/data_structure/layout/fieldformatting.[h|cc]:
        set/get_choices_restricted(): Add a show_as_radio_buttons output parameter.
    * glom/libglom/document/document.cc:
        load_after_layout_item_formatting(), save_after_layout_item_formatting():
        load and save the new formatting detail.
    * glom/mode_design/layout/layout_item_dialogs/box_formatting.[h|cc]:
        Handle the new checkbox and make sure that it is only sensitive when
        the choices are restricted, because radio buttons provide no way to enter
        freeform data.
    * glom/utility_widgets/comboglomchoicesbase.h: Make set_choices() and
        set_choices_with_second() virtual.
        * glom/utility_widgets/combo_as_radio_buttons.[h|cc]: A new widget that is
        a vbbox of radio buttons, with a combo-like API.
    * Makefile_glom.am: Mention the new files.
    * glom/utility_widgets/datawidget.cc: Refactor some repeated code into
        create_combo_widget_for_field() and create the radiobuttons widget when
        necessary.

 ChangeLog                                          |   24 ++
 Makefile_glom.am                                   |    2 +
 glom/glom_developer.glade                          |   49 ++--
 .../data_structure/layout/fieldformatting.cc       |   10 +-
 .../data_structure/layout/fieldformatting.h        |   13 +-
 glom/libglom/document/document.cc                  |   10 +-
 .../layout/layout_item_dialogs/box_formatting.cc   |   17 +-
 .../layout/layout_item_dialogs/box_formatting.h    |    1 +
 glom/utility_widgets/combo_as_radio_buttons.cc     |  302 ++++++++++++++++++++
 glom/utility_widgets/combo_as_radio_buttons.h      |  102 +++++++
 glom/utility_widgets/comboentryglom.cc             |    2 +-
 glom/utility_widgets/comboglomchoicesbase.h        |    4 +-
 glom/utility_widgets/datawidget.cc                 |   44 ++--
 glom/utility_widgets/db_adddel/db_adddel.cc        |    4 +-
 14 files changed, 536 insertions(+), 48 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 6941b6d..a2bdb23 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2010-03-23  Murray Cumming  <murrayc murrayc com>
+
+    Formatting: Choices: Add a Display as radio buttons option.
+
+	* glom/glom_developer.glade: formatting: choices: Add a Show As Radio 
+    Buttons checkbox. 
+	* glom/libglom/data_structure/layout/fieldformatting.[h|cc]:
+    set/get_choices_restricted(): Add a show_as_radio_buttons output parameter.
+	* glom/libglom/document/document.cc: 
+    load_after_layout_item_formatting(), save_after_layout_item_formatting():
+    load and save the new formatting detail.
+	* glom/mode_design/layout/layout_item_dialogs/box_formatting.[h|cc]:
+    Handle the new checkbox and make sure that it is only sensitive when 
+    the choices are restricted, because radio buttons provide no way to enter 
+    freeform data.
+	* glom/utility_widgets/comboglomchoicesbase.h: Make set_choices() and 
+    set_choices_with_second() virtual.
+    * glom/utility_widgets/combo_as_radio_buttons.[h|cc]: A new widget that is 
+    a vbbox of radio buttons, with a combo-like API.
+	* Makefile_glom.am: Mention the new files.
+	* glom/utility_widgets/datawidget.cc: Refactor some repeated code into 
+    create_combo_widget_for_field() and create the radiobuttons widget when 
+    necessary.
+
 2010-03-22  Murray Cumming  <murrayc murrayc com>
 
     Fix the distcheck. Don't use deprecated gtkmm API.
diff --git a/Makefile_glom.am b/Makefile_glom.am
index b223b07..98dcf1c 100644
--- a/Makefile_glom.am
+++ b/Makefile_glom.am
@@ -128,6 +128,8 @@ glom_glom_SOURCES =							\
 	glom/utility_widgets/comboglom.h				\
 	glom/utility_widgets/comboglomchoicesbase.cc			\
 	glom/utility_widgets/comboglomchoicesbase.h			\
+	glom/utility_widgets/combo_as_radio_buttons.cc			\
+	glom/utility_widgets/combo_as_radio_buttons.h			\
 	glom/utility_widgets/datawidget.cc				\
 	glom/utility_widgets/datawidget.h				\
 	glom/utility_widgets/dialog_choose_date.cc			\
diff --git a/glom/glom_developer.glade b/glom/glom_developer.glade
index b560739..20d720e 100644
--- a/glom/glom_developer.glade
+++ b/glom/glom_developer.glade
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="2.16"/>
   <!-- interface-requires gtksourceview 0.0 -->
@@ -6491,8 +6491,8 @@ Which user should be added to this group?</property>
             <child>
               <object class="GtkVBox" id="vbox_numeric_format">
                 <property name="visible">True</property>
-                <property name="orientation">vertical</property>
                 <property name="border_width">6</property>
+                <property name="orientation">vertical</property>
                 <property name="spacing">6</property>
                 <child>
                   <object class="GtkCheckButton" id="checkbutton_format_thousands">
@@ -7035,6 +7035,21 @@ Which user should be added to this group?</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkCheckButton" id="checkbutton_choices_restrict_as_radio_buttons">
+                    <property name="label" translatable="yes">Display as radio buttons</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
               </object>
               <packing>
                 <property name="position">2</property>
@@ -9263,23 +9278,23 @@ What name should this module have?</property>
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkButton" id="button_add_related">
+                          <object class="GtkButton" id="button_add_related_calendar">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="can_default">True</property>
                             <property name="receives_default">False</property>
-                            <property name="tooltip_text" translatable="yes">Add a related records portal. This is a list of records in a related table. Remember to edit this layout item to specify the relationship to use, and the fields to show from the related table.</property>
+                            <property name="tooltip_text" translatable="yes">Add a related records calendar portal. This is a calendar showing records from a related table. Remember to edit this layout item to specify the relationship to use, and the fields to show from the related table.</property>
                             <child>
-                              <object class="GtkAlignment" id="alignment22">
+                              <object class="GtkAlignment" id="alignment1">
                                 <property name="visible">True</property>
                                 <property name="xscale">0</property>
                                 <property name="yscale">0</property>
                                 <child>
-                                  <object class="GtkHBox" id="hbox29">
+                                  <object class="GtkHBox" id="hbox1">
                                     <property name="visible">True</property>
                                     <property name="spacing">2</property>
                                     <child>
-                                      <object class="GtkImage" id="image19">
+                                      <object class="GtkImage" id="image1">
                                         <property name="visible">True</property>
                                         <property name="stock">gtk-add</property>
                                       </object>
@@ -9290,9 +9305,9 @@ What name should this module have?</property>
                                       </packing>
                                     </child>
                                     <child>
-                                      <object class="GtkLabel" id="label74">
+                                      <object class="GtkLabel" id="label1">
                                         <property name="visible">True</property>
-                                        <property name="label" translatable="yes">Add Related Records</property>
+                                        <property name="label" translatable="yes">Add Related Calendar</property>
                                         <property name="use_underline">True</property>
                                       </object>
                                       <packing>
@@ -9314,23 +9329,23 @@ What name should this module have?</property>
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkButton" id="button_add_related_calendar">
+                          <object class="GtkButton" id="button_add_related">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="can_default">True</property>
                             <property name="receives_default">False</property>
-                            <property name="tooltip_text" translatable="yes">Add a related records calendar portal. This is a calendar showing records from a related table. Remember to edit this layout item to specify the relationship to use, and the fields to show from the related table.</property>
+                            <property name="tooltip_text" translatable="yes">Add a related records portal. This is a list of records in a related table. Remember to edit this layout item to specify the relationship to use, and the fields to show from the related table.</property>
                             <child>
-                              <object class="GtkAlignment" id="alignment1">
+                              <object class="GtkAlignment" id="alignment22">
                                 <property name="visible">True</property>
                                 <property name="xscale">0</property>
                                 <property name="yscale">0</property>
                                 <child>
-                                  <object class="GtkHBox" id="hbox1">
+                                  <object class="GtkHBox" id="hbox29">
                                     <property name="visible">True</property>
                                     <property name="spacing">2</property>
                                     <child>
-                                      <object class="GtkImage" id="image1">
+                                      <object class="GtkImage" id="image19">
                                         <property name="visible">True</property>
                                         <property name="stock">gtk-add</property>
                                       </object>
@@ -9341,9 +9356,9 @@ What name should this module have?</property>
                                       </packing>
                                     </child>
                                     <child>
-                                      <object class="GtkLabel" id="label1">
+                                      <object class="GtkLabel" id="label74">
                                         <property name="visible">True</property>
-                                        <property name="label" translatable="yes">Add Related Calendar</property>
+                                        <property name="label" translatable="yes">Add Related Records</property>
                                         <property name="use_underline">True</property>
                                       </object>
                                       <packing>
@@ -10381,8 +10396,8 @@ What name should this module have?</property>
     <property name="page_increment">2</property>
   </object>
   <object class="GtkAdjustment" id="adjustment2">
-    <property name="value">3</property>
     <property name="upper">100</property>
+    <property name="value">3</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
diff --git a/glom/libglom/data_structure/layout/fieldformatting.cc b/glom/libglom/data_structure/layout/fieldformatting.cc
index 4a27cb2..4a6e40b 100644
--- a/glom/libglom/data_structure/layout/fieldformatting.cc
+++ b/glom/libglom/data_structure/layout/fieldformatting.cc
@@ -30,6 +30,7 @@ namespace Glom
 
 FieldFormatting::FieldFormatting()
 : m_choices_restricted(false),
+  m_choices_restricted_as_radio_buttons(false),
   m_choices_custom(false),
   m_choices_related(false),
   m_text_format_multiline(false),
@@ -43,6 +44,7 @@ FieldFormatting::FieldFormatting(const FieldFormatting& src)
   m_numeric_format(src.m_numeric_format),
   m_choices_custom_list(src.m_choices_custom_list),
   m_choices_restricted(src.m_choices_restricted),
+  m_choices_restricted_as_radio_buttons(src.m_choices_restricted_as_radio_buttons),
   m_choices_custom(src.m_choices_custom),
   m_choices_related(src.m_choices_related),
   m_text_format_multiline(src.m_text_format_multiline),
@@ -66,6 +68,7 @@ bool FieldFormatting::operator==(const FieldFormatting& src) const
     (m_numeric_format == src.m_numeric_format) &&
     (m_choices_custom_list == src.m_choices_custom_list) &&
     (m_choices_restricted == src.m_choices_restricted) &&
+    (m_choices_restricted_as_radio_buttons == src.m_choices_restricted_as_radio_buttons) &&
     (m_choices_custom == src.m_choices_custom) &&
     (m_choices_related == src.m_choices_related) &&
     (m_choices_related_field == src.m_choices_related_field) &&
@@ -87,6 +90,7 @@ FieldFormatting& FieldFormatting::operator=(const FieldFormatting& src)
 
   m_choices_custom_list = src.m_choices_custom_list;
   m_choices_restricted = src.m_choices_restricted;
+  m_choices_restricted_as_radio_buttons = src.m_choices_restricted_as_radio_buttons;
   m_choices_custom = src.m_choices_custom;
   m_choices_related = src.m_choices_related;
   m_choices_related_field = src.m_choices_related_field;
@@ -195,14 +199,16 @@ void FieldFormatting::set_choices_custom(const type_list_values& choices)
   m_choices_custom_list = choices;
 }
 
-bool FieldFormatting::get_choices_restricted() const
+bool FieldFormatting::get_choices_restricted(bool& as_radio_buttons) const
 {
+  as_radio_buttons = m_choices_restricted_as_radio_buttons;
   return m_choices_restricted;
 }
 
-void FieldFormatting::set_choices_restricted(bool val)
+void FieldFormatting::set_choices_restricted(bool val, bool as_radio_buttons)
 {
   m_choices_restricted = val;
+  m_choices_restricted_as_radio_buttons = as_radio_buttons;
 }
 
 bool FieldFormatting::get_has_custom_choices() const
diff --git a/glom/libglom/data_structure/layout/fieldformatting.h b/glom/libglom/data_structure/layout/fieldformatting.h
index 87ed2df..73b9463 100644
--- a/glom/libglom/data_structure/layout/fieldformatting.h
+++ b/glom/libglom/data_structure/layout/fieldformatting.h
@@ -54,8 +54,16 @@ public:
   virtual type_list_values get_choices_custom() const;
   virtual void set_choices_custom(const type_list_values& choices);
 
-  bool get_choices_restricted() const;
-  void set_choices_restricted(bool val = true);
+  /** Discover whether the entered data should only be one of the available 
+   * choices.
+   * @param [out] as_radio_buttons: Whether the choices should be displayed as 
+   * radio buttons instead of a combo box.
+   */
+  bool get_choices_restricted(bool& as_radio_buttons) const;
+
+  /** See get_choices_restricted().
+   */
+  void set_choices_restricted(bool val = true, bool as_radio_buttons = false);
 
   void get_choices(sharedptr<Relationship>& relationship_name, Glib::ustring& field, Glib::ustring& field_second) const;
   void set_choices(const sharedptr<Relationship>& relationship_name, const Glib::ustring& field, const Glib::ustring& field_second);
@@ -142,6 +150,7 @@ private:
 
   type_list_values m_choices_custom_list; //A drop-down list of possible values for the field.
   bool m_choices_restricted;
+  bool m_choices_restricted_as_radio_buttons;
   bool m_choices_custom, m_choices_related;
 
   bool m_text_format_multiline;
diff --git a/glom/libglom/document/document.cc b/glom/libglom/document/document.cc
index 12922c0..891fe41 100644
--- a/glom/libglom/document/document.cc
+++ b/glom/libglom/document/document.cc
@@ -190,6 +190,7 @@ namespace Glom
 #define GLOM_ATTRIBUTE_FORMAT_HORIZONTAL_ALIGNMENT_RIGHT "right"
 
 #define GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED "choices_restricted"
+#define GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED_AS_RADIO_BUTTONS "choices_restricted_radiobuttons"
 #define GLOM_ATTRIBUTE_FORMAT_CHOICES_CUSTOM "choices_custom"
 #define GLOM_ATTRIBUTE_FORMAT_CHOICES_CUSTOM_LIST "custom_choice_list"
 #define GLOM_NODE_FORMAT_CUSTOM_CHOICE "custom_choice"
@@ -1939,7 +1940,9 @@ void Document::load_after_layout_item_formatting(const xmlpp::Element* element,
   //Choices:
   if(!field_name.empty())
   {
-    format.set_choices_restricted( get_node_attribute_value_as_bool(element, GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED) );
+    format.set_choices_restricted( 
+      get_node_attribute_value_as_bool(element, GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED),
+      get_node_attribute_value_as_bool(element, GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED_AS_RADIO_BUTTONS) );
     format.set_has_custom_choices( get_node_attribute_value_as_bool(element, GLOM_ATTRIBUTE_FORMAT_CHOICES_CUSTOM) );
 
     if(format.get_has_custom_choices())
@@ -2934,7 +2937,10 @@ void Document::save_before_layout_item_formatting(xmlpp::Element* nodeItem, cons
     set_node_attribute_value_as_bool(nodeItem, GLOM_ATTRIBUTE_FORMAT_USE_ALT_NEGATIVE_COLOR,
       format.m_numeric_format.m_alt_foreground_color_for_negatives);
 
-    set_node_attribute_value_as_bool(nodeItem, GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED, format.get_choices_restricted());
+    bool as_radio_buttons = false;
+    const bool choices_restricted = format.get_choices_restricted(as_radio_buttons);
+    set_node_attribute_value_as_bool(nodeItem, GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED, choices_restricted);
+    set_node_attribute_value_as_bool(nodeItem, GLOM_ATTRIBUTE_FORMAT_CHOICES_RESTRICTED_AS_RADIO_BUTTONS, as_radio_buttons);
     set_node_attribute_value_as_bool(nodeItem, GLOM_ATTRIBUTE_FORMAT_CHOICES_CUSTOM, format.get_has_custom_choices());
   }
 
diff --git a/glom/mode_design/layout/layout_item_dialogs/box_formatting.cc b/glom/mode_design/layout/layout_item_dialogs/box_formatting.cc
index 51ba6d0..c38b0ae 100644
--- a/glom/mode_design/layout/layout_item_dialogs/box_formatting.cc
+++ b/glom/mode_design/layout/layout_item_dialogs/box_formatting.cc
@@ -51,6 +51,7 @@ Box_Formatting::Box_Formatting(BaseObjectType* cobject, const Glib::RefPtr<Gtk::
   m_radiobutton_choices_custom(0),
   m_radiobutton_choices_related(0),
   m_checkbutton_choices_restricted(0),
+  m_checkbutton_choices_restricted_as_radio_buttons(0),
   m_adddel_choices_custom(0),
   m_col_index_custom_choices(0),
   m_for_print_layout(false),
@@ -110,6 +111,7 @@ Box_Formatting::Box_Formatting(BaseObjectType* cobject, const Glib::RefPtr<Gtk::
   m_adddel_choices_custom->set_auto_add();
 
   builder->get_widget("checkbutton_choices_restrict", m_checkbutton_choices_restricted);
+  builder->get_widget("checkbutton_choices_restrict_as_radio_buttons", m_checkbutton_choices_restricted_as_radio_buttons); 
 
   builder->get_widget_derived("combobox_choices_related_relationship", m_combo_choices_relationship);
   builder->get_widget_derived("combobox_choices_related_field", m_combo_choices_field);
@@ -124,6 +126,7 @@ Box_Formatting::Box_Formatting(BaseObjectType* cobject, const Glib::RefPtr<Gtk::
   m_checkbox_format_text_color_foreground->signal_toggled().connect( sigc::mem_fun(*this, &Box_Formatting::on_checkbox) );
   m_checkbox_format_text_color_background->signal_toggled().connect( sigc::mem_fun(*this, &Box_Formatting::on_checkbox) );
   m_checkbox_format_color_negatives->signal_toggled().connect( sigc::mem_fun(*this, &Box_Formatting::on_checkbox) );
+  m_checkbutton_choices_restricted->signal_toggled().connect( sigc::mem_fun(*this, &Box_Formatting::on_checkbox) );
 
   show_all_children();
 }
@@ -214,7 +217,10 @@ void Box_Formatting::set_formatting(const FieldFormatting& format, bool show_num
   //Choices:
   if(m_field)
   {
-    m_checkbutton_choices_restricted->set_active(format.get_choices_restricted());
+    bool as_radio_buttons = false; //TODO
+    m_checkbutton_choices_restricted->set_active(
+      format.get_choices_restricted(as_radio_buttons));
+    m_checkbutton_choices_restricted_as_radio_buttons->set_active(as_radio_buttons);
 
     const Document* document = get_document();
 
@@ -292,7 +298,9 @@ bool Box_Formatting::get_formatting(FieldFormatting& format) const
   //Choices:
   if(m_field)
   {
-    m_format.set_choices_restricted(m_checkbutton_choices_restricted->get_active());
+    m_format.set_choices_restricted(
+      m_checkbutton_choices_restricted->get_active(), 
+      m_checkbutton_choices_restricted_as_radio_buttons->get_active());
 
     sharedptr<Relationship> choices_relationship = m_combo_choices_relationship->get_selected_relationship();
     m_format.set_choices(choices_relationship,
@@ -401,6 +409,11 @@ void Box_Formatting::enforce_constraints()
   m_colorbutton_foreground->set_sensitive( m_for_print_layout || m_checkbox_format_text_color_foreground->get_active() );
   m_colorbutton_background->set_sensitive( m_for_print_layout || m_checkbox_format_text_color_background->get_active() );
 
+  //Choices:
+  //Radio buttons only make sense when the items are restricted, instead of free-form:
+  m_checkbutton_choices_restricted_as_radio_buttons->set_sensitive(
+     m_checkbutton_choices_restricted->get_active());
+     
   if(m_show_numeric)
     m_vbox_numeric_format->show();
   else
diff --git a/glom/mode_design/layout/layout_item_dialogs/box_formatting.h b/glom/mode_design/layout/layout_item_dialogs/box_formatting.h
index de83a7d..3a6a5a0 100644
--- a/glom/mode_design/layout/layout_item_dialogs/box_formatting.h
+++ b/glom/mode_design/layout/layout_item_dialogs/box_formatting.h
@@ -90,6 +90,7 @@ private:
   Gtk::RadioButton* m_radiobutton_choices_custom;
   Gtk::RadioButton* m_radiobutton_choices_related;
   Gtk::CheckButton* m_checkbutton_choices_restricted;
+  Gtk::CheckButton* m_checkbutton_choices_restricted_as_radio_buttons;
   AddDel_WithButtons* m_adddel_choices_custom;
   guint m_col_index_custom_choices;
   ComboBox_Relationship* m_combo_choices_relationship;
diff --git a/glom/utility_widgets/combo_as_radio_buttons.cc b/glom/utility_widgets/combo_as_radio_buttons.cc
new file mode 100644
index 0000000..4122749
--- /dev/null
+++ b/glom/utility_widgets/combo_as_radio_buttons.cc
@@ -0,0 +1,302 @@
+/* Glom
+ *
+ * Copyright (C) 2010 Murray Cumming
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "combo_as_radio_buttons.h"
+#include <libglom/data_structure/glomconversions.h>
+#include <gtkmm/messagedialog.h>
+#include "../dialog_invalid_data.h"
+#include <libglom/data_structure/glomconversions.h>
+#include <glom/application.h>
+#include <glibmm/i18n.h>
+//#include <sstream> //For stringstream
+
+#include <iostream>   // for cout, endl
+
+namespace Glom
+{
+
+ComboAsRadioButtons::ComboAsRadioButtons()
+: ComboGlomChoicesBase()
+{
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  setup_menu();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  init();
+}
+
+ComboAsRadioButtons::ComboAsRadioButtons(const sharedptr<LayoutItem_Field>& field_second)
+: ComboGlomChoicesBase(field_second)
+{
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  setup_menu();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  init();
+}
+
+void ComboAsRadioButtons::init()
+{
+  if(m_with_second)
+  {
+    //TODO
+  }
+
+  //if(m_glom_type == Field::TYPE_NUMERIC)
+   // get_entry()->set_alignment(1.0); //Align numbers to the right.
+}
+
+void ComboAsRadioButtons::set_choices_with_second(const type_list_values_with_second& list_values)
+{
+  //Clear existing buttons:
+  for(type_map_buttons::iterator iter = m_map_buttons.begin(); 
+    iter != m_map_buttons.end(); ++iter)
+  {
+     Gtk::RadioButton* button = iter->second;
+     delete button;
+  }
+  m_map_buttons.clear();
+
+  //Add new buttons:
+  Gtk::RadioButton::Group group;
+  for(type_list_values_with_second::const_iterator iter = list_values.begin(); iter != list_values.end(); ++iter)
+  {
+    sharedptr<const LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::cast_dynamic(get_layout_item());
+    if(layout_item)
+    {
+      const Glib::ustring value_first = Conversions::get_text_for_gda_value(layout_item->get_glom_type(), iter->first, layout_item->get_formatting_used().m_numeric_format);
+      Glib::ustring title = value_first;
+      if(m_with_second)
+      {
+        const Glib::ustring value_second = Conversions::get_text_for_gda_value(m_layoutitem_second->get_glom_type(), iter->second, m_layoutitem_second->get_formatting_used().m_numeric_format);
+        title += " - " + value_second; //TODO: Find a better way to join them?
+      }
+      
+      Gtk::RadioButton* button = new Gtk::RadioButton(group, title);
+      m_map_buttons[value_first] = button;
+      pack_start(*button);
+      button->show();
+      button->signal_toggled().connect(
+        sigc::mem_fun(*this, &ComboAsRadioButtons::on_radiobutton_toggled));
+
+      //TODO: This doesn't seem be be emitted:
+      button->signal_button_press_event().connect(
+        sigc::mem_fun(*this, &ComboAsRadioButtons::on_radiobutton_button_press_event), false);
+    }
+  } 
+}
+
+void ComboAsRadioButtons::set_choices(const FieldFormatting::type_list_values& list_values)
+{
+  //Clear existing buttons:
+  for(type_map_buttons::iterator iter = m_map_buttons.begin(); 
+    iter != m_map_buttons.end(); ++iter)
+  {
+     Gtk::RadioButton* button = iter->second;
+     delete button;
+  }
+  m_map_buttons.clear();
+
+  //Add new buttons:
+  Gtk::RadioButton::Group group;
+  for(FieldFormatting::type_list_values::const_iterator iter = list_values.begin(); iter != list_values.end(); ++iter)
+  {
+    sharedptr<const LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::cast_dynamic(get_layout_item());
+    if(layout_item)
+    {
+      const Glib::ustring value_first = Conversions::get_text_for_gda_value(layout_item->get_glom_type(), *iter, layout_item->get_formatting_used().m_numeric_format);
+      
+      Gtk::RadioButton* button = new Gtk::RadioButton(group, value_first);
+      m_map_buttons[value_first] = button;
+      pack_start(*button);
+      button->show();
+      button->signal_toggled().connect(
+        sigc::mem_fun(*this, &ComboAsRadioButtons::on_radiobutton_toggled));
+    }
+  } 
+}
+
+
+
+ComboAsRadioButtons::~ComboAsRadioButtons()
+{
+  for(type_map_buttons::iterator iter = m_map_buttons.begin(); 
+    iter != m_map_buttons.end(); ++iter)
+  {
+     Gtk::RadioButton* button = iter->second;
+     delete button;
+  }
+  m_map_buttons.clear();	
+}
+
+void ComboAsRadioButtons::check_for_change()
+{
+  Glib::ustring new_text = get_text();
+  if(new_text == m_old_text)
+    return;
+
+  //Validate the input:
+  bool success = false;
+
+  sharedptr<const LayoutItem_Field> layout_item = sharedptr<const LayoutItem_Field>::cast_dynamic(get_layout_item());
+  const Gnome::Gda::Value value = Conversions::parse_value(layout_item->get_glom_type(), new_text, layout_item->get_formatting_used().m_numeric_format, success);
+
+  if(success)
+  {
+    //Actually show the canonical text:
+    set_value(value);
+    m_signal_edited.emit(); //The text was edited, so tell the client code.
+  }
+  else
+  {
+    //Tell the user and offer to revert or try again:
+    const bool revert = glom_show_dialog_invalid_data(layout_item->get_glom_type());
+    if(revert)
+    {
+      set_text(m_old_text);
+    }
+    else
+      grab_focus(); //Force the user back into the same field, so that the field can be checked again and eventually corrected or reverted.
+  }
+}
+
+void ComboAsRadioButtons::set_value(const Gnome::Gda::Value& value)
+{
+  sharedptr<const LayoutItem_Field> layout_item = sharedptr<const LayoutItem_Field>::cast_dynamic(get_layout_item());
+  if(!layout_item)
+    return;
+
+  set_text(Conversions::get_text_for_gda_value(layout_item->get_glom_type(), value, layout_item->get_formatting_used().m_numeric_format));
+
+  //Show a different color if the value is numeric, if that's specified:
+  if(layout_item->get_glom_type() == Field::TYPE_NUMERIC)
+  {
+    //TODO
+  }
+}
+
+void ComboAsRadioButtons::set_text(const Glib::ustring& text)
+{
+  m_old_text = text;
+
+  type_map_buttons::iterator iter = m_map_buttons.find(text);
+  if(iter != m_map_buttons.end())
+  {
+    Gtk::RadioButton* button = iter->second;
+    if(button)
+    {
+      button->set_active();
+      return;
+    }
+  }
+
+  //std::cerr << "ComboAsRadioButtons::set_text(): no item found for: " << text << std::endl;
+}
+
+Gnome::Gda::Value ComboAsRadioButtons::get_value() const
+{
+  sharedptr<const LayoutItem_Field> layout_item = sharedptr<const LayoutItem_Field>::cast_dynamic(get_layout_item());
+  bool success = false;
+
+  const Glib::ustring text = get_text();
+  return Conversions::parse_value(layout_item->get_glom_type(), text, layout_item->get_formatting_used().m_numeric_format, success);
+}
+
+Glib::ustring ComboAsRadioButtons::get_text() const
+{
+  //Get the active row:
+  for(type_map_buttons::const_iterator iter = m_map_buttons.begin();
+    iter != m_map_buttons.end(); ++iter)
+  {
+    Gtk::CheckButton* button = iter->second;
+    if(button && button->get_active())
+    {
+      return iter->first;
+    }
+  }
+      
+  return Glib::ustring();
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void ComboAsRadioButtons::show_context_menu(GdkEventButton *event)
+{
+  std::cout << "ComboAsRadioButtons::show_context_menu()" << std::endl;
+  Application* pApp = get_application();
+  if(pApp)
+  {
+    //Enable/Disable items.
+    //We did this earlier, but get_application is more likely to work now:
+    pApp->add_developer_action(m_refContextLayout); //So that it can be disabled when not in developer mode.
+    pApp->add_developer_action(m_refContextAddField);
+    pApp->add_developer_action(m_refContextAddRelatedRecords);
+    pApp->add_developer_action(m_refContextAddGroup);
+
+    pApp->update_userlevel_ui(); //Update our action's sensitivity.
+
+    //Only show this popup in developer mode, so operators still see the default GtkEntry context menu.
+    //TODO: It would be better to add it somehow to the standard context menu.
+    if(pApp->get_userlevel() == AppState::USERLEVEL_DEVELOPER)
+    {
+      GdkModifierType mods;
+      gdk_window_get_pointer( Gtk::Widget::gobj()->window, 0, 0, &mods );
+      if(mods & GDK_BUTTON3_MASK)
+      {
+        //Give user choices of actions on this item:
+        m_pMenuPopup->popup(event->button, event->time);
+      }
+    }
+  }
+}
+
+bool ComboAsRadioButtons::on_radiobutton_button_press_event(GdkEventButton *event)
+{
+  show_context_menu(event);
+  return false; //Let other signal handlers handle it too.
+}
+
+bool ComboAsRadioButtons::on_button_press_event(GdkEventButton *event)
+{
+  show_context_menu(event);
+
+  return Gtk::VBox::on_button_press_event(event);
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+Application* ComboAsRadioButtons::get_application()
+{
+  Gtk::Container* pWindow = get_toplevel();
+  //TODO: This only works when the child widget is already in its parent.
+
+  return dynamic_cast<Application*>(pWindow);
+}
+
+
+void ComboAsRadioButtons::on_radiobutton_toggled()
+{
+  check_for_change();
+}
+
+void ComboAsRadioButtons::set_read_only(bool /* read_only */)
+{
+  //TODO
+}
+
+} //namespace Glom
diff --git a/glom/utility_widgets/combo_as_radio_buttons.h b/glom/utility_widgets/combo_as_radio_buttons.h
new file mode 100644
index 0000000..ac949f7
--- /dev/null
+++ b/glom/utility_widgets/combo_as_radio_buttons.h
@@ -0,0 +1,102 @@
+/* Glom
+ *
+ * Copyright (C) 2010 Murray Cumming
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GLOM_UTILITY_WIDGETS_COMBO_AS_RADIO_BUTTONS_H
+#define GLOM_UTILITY_WIDGETS_COMBO_AS_RADIO_BUTTONS_H
+
+#include "config.h" // For GLOM_ENABLE_CLIENT_ONLY
+
+#include <gtkmm.h>
+#include <libglom/data_structure/field.h>
+#include "comboglomchoicesbase.h"
+
+#ifdef GLOM_ENABLE_MAEMO
+#include <hildonmm/picker-button.h>
+#include <hildonmm/touch-selector-entry.h>
+#endif //GLOM_ENABLE_MAEMO
+
+namespace Glom
+{
+
+class Application;
+
+/** A set of radio buttons, with an API similar to a ComboBox with restricted values.
+ * Use this only when the user should only be allowed to enter values that are in the choices.
+ */
+class ComboAsRadioButtons
+: 
+  public Gtk::VBox,
+  public ComboGlomChoicesBase
+{
+public:
+
+  ///You must call set_layout_item() to specify the field type and formatting of the main column.
+  ComboAsRadioButtons();
+
+  ///You must call set_layout_item() to specify the field type and formatting of the main column.
+  explicit ComboAsRadioButtons(const sharedptr<LayoutItem_Field>& field_second);
+
+  virtual ~ComboAsRadioButtons();
+  
+  virtual void set_choices(const FieldFormatting::type_list_values& list_values);
+  virtual void set_choices_with_second(const type_list_values_with_second& list_values);
+
+  virtual void set_read_only(bool read_only = true);
+
+
+  //Override this so we can store the text to compare later.
+  //This is not virtual, so you must not use it via Gtk::Entry.
+  void set_text(const Glib::ustring& text); //override
+
+  Glib::ustring get_text() const;
+
+  /** Set the text from a Gnome::Gda::Value.
+   */
+  virtual void set_value(const Gnome::Gda::Value& value);
+
+  virtual Gnome::Gda::Value get_value() const;
+
+private:
+  void init();
+
+  void on_radiobutton_toggled();
+
+  void check_for_change();
+
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  virtual bool on_button_press_event(GdkEventButton *event);
+  virtual bool on_radiobutton_button_press_event(GdkEventButton *event);
+  void show_context_menu(GdkEventButton *event);
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  virtual Application* get_application();
+
+
+  Glib::ustring m_old_text;
+
+  typedef std::map<Glib::ustring, Gtk::RadioButton*> type_map_buttons;
+  type_map_buttons m_map_buttons;
+};
+
+} //namespace Glom
+
+#endif //GLOM_UTILITY_WIDGETS_COMBOENTRY_GLOM_H
+
diff --git a/glom/utility_widgets/comboentryglom.cc b/glom/utility_widgets/comboentryglom.cc
index 3ffa341..1b8a7b0 100644
--- a/glom/utility_widgets/comboentryglom.cc
+++ b/glom/utility_widgets/comboentryglom.cc
@@ -91,7 +91,7 @@ void ComboEntryGlom::init()
   // Only in the latest hildonmm: Glib::RefPtr<Hildon::TouchSelectorColumn> column =
   //  m_maemo_selector.append_column(m_refModel);
   Glib::RefPtr<Hildon::TouchSelectorColumn> column = Glib::wrap(hildon_touch_selector_append_column(
-      HILDON_TOUCH_SELECTOR(m_maemo_selector.gobj()), GTK_TREE_MODEL(Glib::unwrap(m_refModel)), 0, static_cast<char*>(0)), true);
+    HILDON_TOUCH_SELECTOR(m_maemo_selector.gobj()), GTK_TREE_MODEL(Glib::unwrap(m_refModel)), 0, static_cast<char*>(0)), true);
       
   column->pack_start(m_Columns.m_col_first, false);
   //Only in the latest hildonmm: column->set_text_column(m_Columns.m_col_first);
diff --git a/glom/utility_widgets/comboglomchoicesbase.h b/glom/utility_widgets/comboglomchoicesbase.h
index 02f7b5d..6ac1bbc 100644
--- a/glom/utility_widgets/comboglomchoicesbase.h
+++ b/glom/utility_widgets/comboglomchoicesbase.h
@@ -39,10 +39,10 @@ public:
 
   virtual ~ComboGlomChoicesBase();
 
-  void set_choices(const FieldFormatting::type_list_values& list_values);
+  virtual void set_choices(const FieldFormatting::type_list_values& list_values);
 
   typedef std::list< std::pair<Gnome::Gda::Value, Gnome::Gda::Value> > type_list_values_with_second;
-  void set_choices_with_second(const type_list_values_with_second& list_values);
+  virtual void set_choices_with_second(const type_list_values_with_second& list_values);
 
 protected:
   void init();
diff --git a/glom/utility_widgets/datawidget.cc b/glom/utility_widgets/datawidget.cc
index 68e0667..281b3bf 100644
--- a/glom/utility_widgets/datawidget.cc
+++ b/glom/utility_widgets/datawidget.cc
@@ -25,6 +25,7 @@
 #include "labelglom.h"
 #include "comboentryglom.h"
 #include "comboglom.h"
+#include "combo_as_radio_buttons.h"
 #include "textviewglom.h"
 #include "imageglom.h"
 #include <libglom/data_structure/glomconversions.h>
@@ -45,11 +46,26 @@
 namespace Glom
 {
 
-/*
-DataWidget::DataWidget(Field::glom_field_type glom_type, const Glib::ustring& title)
-: m_glom_type(glom_type),
-  m_pMenuPopup(0)
-*/
+static ComboGlomChoicesBase* create_combo_widget_for_field(const sharedptr<LayoutItem_Field>& field, const sharedptr<LayoutItem_Field>& layout_item_second = sharedptr<LayoutItem_Field>())
+{
+  std::cout << "create_combo_widget_for_field(): field=" << field->get_name() << std::endl;
+  ComboGlomChoicesBase* result = 0;
+  bool as_radio_buttons = false; //TODO: Use this.
+  const bool restricted = field->get_formatting_used().get_choices_restricted(as_radio_buttons);
+  if(restricted)
+  {
+    std::cout << "  restricted" << std::endl;
+  
+    if(as_radio_buttons)
+      result = Gtk::manage(new ComboAsRadioButtons(layout_item_second));
+    else
+      result = Gtk::manage(new ComboGlom(layout_item_second));
+  }
+  else
+    result = Gtk::manage(new ComboEntryGlom(layout_item_second));
+
+  return result;
+}
 
 DataWidget::DataWidget(const sharedptr<LayoutItem_Field>& field, const Glib::ustring& table_name, const Document* document)
 :  m_child(0),
@@ -103,15 +119,11 @@ DataWidget::DataWidget(const sharedptr<LayoutItem_Field>& field, const Glib::ust
     //Use a Combo if there is a drop-down of choices (A "value list"), else an Entry:
     if(field->get_formatting_used().get_has_choices())
     {
-      ComboGlomChoicesBase* combo = 0; //Gtk::manage(new ComboEntryGlom());
+      std::cout << "DEBUG: has choices" << std::endl;
+      ComboGlomChoicesBase* combo = create_combo_widget_for_field(field);
 
       if(field->get_formatting_used().get_has_custom_choices())
       {
-        if(field->get_formatting_used().get_choices_restricted())
-          combo = Gtk::manage(new ComboGlom());
-        else
-          combo = Gtk::manage(new ComboEntryGlom());
-
         //set_choices() needs this, for the numeric layout:
         combo->set_layout_item( get_layout_item(), table_name);
 
@@ -136,17 +148,11 @@ DataWidget::DataWidget(const sharedptr<LayoutItem_Field>& field, const Glib::ust
             layout_field_second->set_full_field_details(field_second);
             //We use the default formatting for this field->
 
-            if(field->get_formatting_used().get_choices_restricted())
-              combo = Gtk::manage(new ComboGlom(layout_field_second));
-            else
-              combo = Gtk::manage(new ComboEntryGlom(layout_field_second));
+            combo = create_combo_widget_for_field(field, layout_field_second);
           }
           else
           {
-            if(field->get_formatting_used().get_choices_restricted())
-              combo = Gtk::manage(new ComboGlom());
-            else
-              combo = Gtk::manage(new ComboEntryGlom());
+            combo = create_combo_widget_for_field(field);
           }
 
           //set_choices() needs this, for the numeric layout:
diff --git a/glom/utility_widgets/db_adddel/db_adddel.cc b/glom/utility_widgets/db_adddel/db_adddel.cc
index b8d0b62..6efbbba 100644
--- a/glom/utility_widgets/db_adddel/db_adddel.cc
+++ b/glom/utility_widgets/db_adddel/db_adddel.cc
@@ -727,7 +727,9 @@ Gtk::CellRenderer* DbAddDel::construct_specified_columns_cellrenderer(const shar
         if(item_field->get_formatting_used().get_has_choices())
         {
           CellRendererList* rendererList = Gtk::manage( new CellRendererList() );
-          rendererList->set_restrict_values_to_list(item_field->get_formatting_used().get_choices_restricted());
+          bool as_radio_buttons = false; //Can't really be done in a list, so we ignore it.
+          rendererList->set_restrict_values_to_list(
+            item_field->get_formatting_used().get_choices_restricted(as_radio_buttons));
 
           pCellRenderer = rendererList;
         }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]