[gparted] modern-gtk2: Introduce OptionComboBox class (!17)



commit 5407e8346b241d7fb7868f0fbe70fd37b1d08108
Author: Luca Bacci <luca bacci982 gmail com>
Date:   Mon Jul 30 10:24:18 2018 +0200

    modern-gtk2: Introduce OptionComboBox class (!17)
    
    Gtk::OptionMenu is a combobox type widget that is constructed from a
    Gtk::Menu rather than a Gtk::TreeModel.  However Gtk::OptionMenu was
    deprecated in gtkmm 2.4.1.
    
    In GParted the Gtk::OptionMenu widget is used for:
    - partition alignment combobox
    - partition type combobox
    - file system combobox
    
    While they consist only of text we cannot use Gtk::ComboBoxText because
    it doesn't expose functionality in its interface to make items inactive.
    
    Create OptionComboBox helper class that builds a combobox consisting of
    only text items, much like Gtk::ComboBoxText, but has the added
    functionality to set items as inactive.
    
    References:
    https://developer.gnome.org/gtkmm/2.24/classGtk_1_1OptionMenu.html#details
    https://gitlab.gnome.org/GNOME/gtkmm/blob/GTKMM_2_10_1/ChangeLog#L3515
    https://gitlab.gnome.org/GNOME/gtkmm/commit/bba503b0473413e474d9b6d297226479d29fd47f
    https://developer.gnome.org/gtkmm/2.24/classGtk_1_1ComboBoxText.html
    
    Closes !17 - Gtk2 modernisation

 include/Makefile.am      |   1 +
 include/OptionComboBox.h | 195 ++++++++++++++++++++++++
 po/POTFILES.in           |   1 +
 src/Makefile.am          |   1 +
 src/OptionComboBox.cc    | 377 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 575 insertions(+)
---
diff --git a/include/Makefile.am b/include/Makefile.am
index 18290bba..1601a5bf 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -39,6 +39,7 @@ EXTRA_DIST = \
        OperationLabelFileSystem.h      \
        OperationNamePartition.h        \
        OperationResizeMove.h           \
+       OptionComboBox.h                \
        Partition.h                     \
        PartitionLUKS.h                 \
        PartitionVector.h               \
diff --git a/include/OptionComboBox.h b/include/OptionComboBox.h
new file mode 100644
index 00000000..c7b6d031
--- /dev/null
+++ b/include/OptionComboBox.h
@@ -0,0 +1,195 @@
+/* Copyright (C) 2018 Luca Bacci
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GPARTED_OPTIONCOMBOBOX_H
+#define GPARTED_OPTIONCOMBOBOX_H
+
+
+#include <glibmm/ustring.h>
+#include <gtkmm/combobox.h>
+#include <gtkmm/liststore.h>
+#include <gtkmm/treerowreference.h>
+
+
+namespace GParted
+{
+
+class OptionStore_Item;
+class OptionStore_Item_Collection;
+typedef const OptionStore_Item            OptionStore_Item_Const;
+typedef const OptionStore_Item_Collection OptionStore_Item_Collection_Const;
+class OptionStore;
+
+
+// OptionStore_Item is the class that represents a single item (row).
+// It lets you manipulate the data of a single item with a convenient
+// high level interface.
+class OptionStore_Item
+{
+public:
+       explicit OptionStore_Item(const Glib::RefPtr<OptionStore>& ref_model,
+                                 const Gtk::TreeModel::iterator& iter);
+       explicit OptionStore_Item(const Glib::RefPtr<OptionStore>& ref_model,
+                                 const Gtk::TreeModel::Path& path);
+       explicit OptionStore_Item(const Gtk::TreeModel::RowReference& rowreference);
+
+       void set(const Glib::ustring& text, bool sensitive = true);
+       void set_text(const Glib::ustring& text);
+       void set_sensitive(bool sensitive = true);
+
+       Glib::ustring text() const;
+       bool          sensitive() const;
+
+       operator Gtk::TreeModel::iterator()
+       {
+               return to_iterator_();
+       }
+       operator Gtk::TreeModel::const_iterator() const
+       {
+               return to_iterator_();
+       }
+
+private:
+       Gtk::TreeModel::iterator to_iterator_() const;
+       friend class OptionStore_Item_Collection;
+
+private:
+       mutable Gtk::TreeModel::RowReference m_rowreference;
+};
+
+
+// OptionStore_Item_Collection lets you operate on OptionStore model
+// with an STL-like container interface.  You can act on it like a
+// sequence of items.
+// Usually you get an OptionStore_Item_Collection by calling the items()
+// method on a OptionStore model.  You can also call the items() method
+// on an OptionComboBox; it simply redirects the call to the associated
+// OptionStore model.
+class OptionStore_Item_Collection
+{
+public:
+       explicit OptionStore_Item_Collection(const Glib::RefPtr<OptionStore>& ref_model);
+
+       void push_front(const Glib::ustring& text, bool sensitive = true);
+       void push_back(const Glib::ustring& text, bool sensitive = true);
+
+       void insert(const OptionStore_Item& item,
+                   const Glib::ustring& text,
+                   bool sensitive = true);
+       void insert(unsigned position,
+                   const Glib::ustring& text,
+                   bool sensitive = true);
+
+       void pop_front();
+       void pop_back();
+
+       void erase(const OptionStore_Item& item);
+       void erase(unsigned position);
+       void clear();
+
+       OptionStore_Item front();
+       OptionStore_Item back();
+
+       OptionStore_Item at(unsigned position);
+       OptionStore_Item operator[](unsigned position);
+
+       unsigned size() const;
+
+       OptionStore_Item_Const front() const;
+       OptionStore_Item_Const back() const;
+
+       OptionStore_Item_Const at(unsigned position) const;
+       OptionStore_Item_Const operator[] (unsigned position) const;
+
+private:
+       Glib::RefPtr<OptionStore> m_ref_model;
+};
+
+
+// OptionStore is the model that backs the data for OptionComboBox.
+// It's a specialized Gtk::ListStore with a string slot for row text
+// and a boolean slot for row sensitivity (if it's false the row is
+// "grayed out").
+// Although you can call any Gtk::ListStore method (from which
+// OptionStore inherits) it is usually convenient to call the items()
+// method to get a OptionStore_Item_Collection object that provides
+// a nice, high level interface.
+class OptionStore
+ : public Gtk::ListStore
+{
+public:
+       typedef Gtk::ListStore Base;
+
+       typedef OptionStore_Item Item;
+       typedef OptionStore_Item_Const Item_Const;
+       typedef OptionStore_Item_Collection Item_Collection;
+       typedef OptionStore_Item_Collection_Const Item_Collection_Const;
+
+       static
+       Glib::RefPtr<OptionStore> create();
+
+       Item_Collection       items();
+       Item_Collection_Const items() const;
+
+       struct Slots
+       {
+               static Gtk::TreeModelColumn<Glib::ustring> text;
+               static Gtk::TreeModelColumn<bool> sensitive;
+
+       private:
+               static Gtk::TreeModel::ColumnRecord record_;
+               friend class OptionStore;
+       };
+
+protected:
+       explicit OptionStore();
+};
+
+
+// OptionComboBox is a specialized ComboBox that shows a list of rows,
+// some of which may be selectively grayed out.  It is commonly used to
+// display a list of options where not all of the options can be
+// selected in all cases.
+class OptionComboBox
+ : public Gtk::ComboBox
+{
+public:
+       typedef Gtk::ComboBox Base;
+
+       explicit OptionComboBox();
+       explicit OptionComboBox(const Glib::RefPtr<OptionStore>& ref_model);
+
+       void set_model(const Glib::RefPtr<OptionStore>& ref_model);
+
+       Glib::RefPtr<OptionStore>   get_model();
+       OptionStore_Item_Collection items();
+       OptionStore_Item            get_active();
+
+       Glib::RefPtr<const OptionStore>   get_model()  const;
+       OptionStore_Item_Collection_Const items()      const;
+       OptionStore_Item_Const            get_active() const;
+
+protected:
+       void pack_cell_renderers();
+
+protected:
+       Glib::RefPtr<OptionStore> m_ref_model;
+};
+
+
+}//GParted
+
+#endif /* GPARTED_OPTIONCOMBOBOX_H */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f7ede23b..9d08b7db 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,6 +36,7 @@ src/OperationFormat.cc
 src/OperationLabelFileSystem.cc
 src/OperationNamePartition.cc
 src/OperationResizeMove.cc
+src/OptionComboBox.cc
 src/Partition.cc
 src/PartitionLUKS.cc
 src/PartitionVector.cc
diff --git a/src/Makefile.am b/src/Makefile.am
index 099adbd4..33f8c5bd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -49,6 +49,7 @@ gpartedbin_SOURCES = \
        OperationLabelFileSystem.cc     \
        OperationNamePartition.cc       \
        OperationResizeMove.cc          \
+       OptionComboBox.cc               \
        Partition.cc                    \
        PartitionLUKS.cc                \
        PartitionVector.cc              \
diff --git a/src/OptionComboBox.cc b/src/OptionComboBox.cc
new file mode 100644
index 00000000..8e8f70ed
--- /dev/null
+++ b/src/OptionComboBox.cc
@@ -0,0 +1,377 @@
+/* Copyright (C) 2018 Luca Bacci
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "OptionComboBox.h"
+
+#include <glibmm/ustring.h>
+#include <gtkmm/cellrenderertext.h>
+#include <stdexcept>
+
+
+namespace GParted
+{
+
+// NOTE: As stated in Gtkmm3 documentation, slots can be shared for all model instances.
+// See: Class Reference for Gtk::TreeModelColumn and Gtk::TreeModelColumnRecord.
+//     https://developer.gnome.org/gtkmm/3.22/classGtk_1_1TreeModelColumnRecord.html#details
+Gtk::TreeModelColumn<Glib::ustring> OptionStore::Slots::text;
+Gtk::TreeModelColumn<bool>          OptionStore::Slots::sensitive;
+
+Gtk::TreeModel::ColumnRecord        OptionStore::Slots::record_;
+
+
+OptionStore_Item::OptionStore_Item(const Glib::RefPtr<OptionStore>& ref_model,
+                                   const Gtk::TreeModel::iterator& iter)
+ : m_rowreference(ref_model, ref_model->get_path(iter))
+{}
+
+
+OptionStore_Item::OptionStore_Item(const Glib::RefPtr<OptionStore>& ref_model,
+                                   const Gtk::TreeModel::Path& path)
+ : m_rowreference(ref_model, path)
+{}
+
+
+OptionStore_Item::OptionStore_Item(const Gtk::TreeModel::RowReference& rowreference)
+ : m_rowreference(rowreference)
+{}
+
+
+void OptionStore_Item::set(const Glib::ustring& text,
+                           bool sensitive)
+{
+       Gtk::TreeModel::iterator iter = *this;
+       (*iter)[OptionStore::Slots::text] = text;
+       (*iter)[OptionStore::Slots::sensitive] = sensitive;
+}
+
+
+void OptionStore_Item::set_text(const Glib::ustring& text)
+{
+       Gtk::TreeModel::iterator iter = *this;
+       (*iter)[OptionStore::Slots::text] = text;
+}
+
+
+void OptionStore_Item::set_sensitive(bool sensitive)
+{
+       Gtk::TreeModel::iterator iter = *this;
+       (*iter)[OptionStore::Slots::sensitive] = sensitive;
+}
+
+
+Glib::ustring OptionStore_Item::text() const
+{
+       Gtk::TreeModel::const_iterator iter = *this;
+       return (*iter)[OptionStore::Slots::text];
+}
+
+
+bool OptionStore_Item::sensitive() const
+{
+       Gtk::TreeModel::const_iterator iter = *this;
+       return (*iter)[OptionStore::Slots::sensitive];
+}
+
+
+Gtk::TreeModel::iterator OptionStore_Item::to_iterator_() const
+{
+       Gtk::TreeModel::Path path = m_rowreference.get_path();
+
+       Glib::RefPtr<OptionStore> ref_model =
+               Glib::RefPtr<OptionStore>::cast_dynamic(m_rowreference.get_model());
+
+       if (!ref_model)
+               throw std::runtime_error ("incompatible Gtk::TreeModel type.");
+
+       return ref_model->get_iter(path);
+}
+
+
+OptionStore_Item_Collection::OptionStore_Item_Collection(const Glib::RefPtr<OptionStore>& ref_model)
+ : m_ref_model(ref_model)
+{}
+
+
+void OptionStore_Item_Collection::push_front(const Glib::ustring& text,
+                                             bool sensitive)
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->prepend();
+       (*iter)[OptionStore::Slots::text] = text;
+       (*iter)[OptionStore::Slots::sensitive] = sensitive;
+}
+
+
+void OptionStore_Item_Collection::push_back(const Glib::ustring& text,
+                                            bool sensitive)
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->append();
+       (*iter)[OptionStore::Slots::text] = text;
+       (*iter)[OptionStore::Slots::sensitive] = sensitive;
+}
+
+
+void OptionStore_Item_Collection::insert(const OptionStore_Item& item,
+                                         const Glib::ustring& text,
+                                         bool sensitive)
+{
+       Gtk::TreeModel::iterator previous_iter = item.to_iterator_();
+       Gtk::TreeModel::iterator iter = m_ref_model->insert(previous_iter);
+       (*iter)[OptionStore::Slots::text] = text;
+       (*iter)[OptionStore::Slots::sensitive] = sensitive;
+}
+
+
+void OptionStore_Item_Collection::insert(unsigned position,
+                                         const Glib::ustring& text,
+                                         bool sensitive )
+{
+       Gtk::TreeModel::iterator previous_iter = m_ref_model->children()[position];
+       Gtk::TreeModel::iterator iter = m_ref_model->insert(previous_iter);
+       (*iter)[OptionStore::Slots::text] = text;
+       (*iter)[OptionStore::Slots::sensitive] = sensitive;
+}
+
+
+void OptionStore_Item_Collection::pop_front()
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children().begin();
+       m_ref_model->erase(iter);
+}
+
+
+void OptionStore_Item_Collection::pop_back()
+{
+       // NOTE: To get an iterator to the last item we do not use Gtk::TreeNodeChildren::rbegin()
+       // because Gtk::TreeModel::reverse_iterator is broken and has been deprecated in Gtkmm3.
+       //     https://bugzilla.gnome.org/show_bug.cgi?id=554889
+
+       Gtk::TreeModel::iterator iter = m_ref_model->children().begin();
+
+       for (unsigned i = 1; i < m_ref_model->children().size(); ++i)
+               ++iter;
+
+       m_ref_model->erase(iter);
+}
+
+
+void OptionStore_Item_Collection::erase(const OptionStore_Item& item)
+{
+       Gtk::TreeModel::iterator iter = item.to_iterator_();
+       m_ref_model->erase(iter);
+}
+
+
+void OptionStore_Item_Collection::erase(unsigned position)
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children()[position];
+       m_ref_model->erase(iter);
+}
+
+
+void OptionStore_Item_Collection::clear()
+{
+       m_ref_model->clear();
+}
+
+
+OptionStore_Item OptionStore_Item_Collection::front()
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children().begin();
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+OptionStore_Item OptionStore_Item_Collection::back()
+{
+       // NOTE: To get an iterator to the last item we do not use Gtk::TreeNodeChildren::rbegin()
+       // because Gtk::TreeModel::reverse_iterator is broken and has been deprecated in Gtkmm3.
+       //     https://bugzilla.gnome.org/show_bug.cgi?id=554889
+
+       Gtk::TreeModel::iterator iter = m_ref_model->children().begin();
+
+       for (unsigned i = 1; i < m_ref_model->children().size(); ++i)
+               ++iter;
+
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+OptionStore_Item OptionStore_Item_Collection::at(unsigned position)
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children()[position];
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+OptionStore_Item OptionStore_Item_Collection::operator[](unsigned position)
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children()[position];
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+unsigned OptionStore_Item_Collection::size() const
+{
+       return m_ref_model->children().size();
+}
+
+
+OptionStore_Item_Const OptionStore_Item_Collection::front() const
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children().begin();
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+OptionStore_Item_Const OptionStore_Item_Collection::back() const
+{
+       // NOTE: To get an iterator to the last item we do not use Gtk::TreeNodeChildren::rbegin()
+       // because Gtk::TreeModel::reverse_iterator is broken and has been deprecated in Gtkmm3.
+       //     https://bugzilla.gnome.org/show_bug.cgi?id=554889
+
+       Gtk::TreeModel::iterator iter = m_ref_model->children().begin();
+
+       for (unsigned i = 1; i < m_ref_model->children().size(); ++i)
+               ++iter;
+
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+OptionStore_Item_Const OptionStore_Item_Collection::at(unsigned position) const
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children()[position];
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+OptionStore_Item_Const OptionStore_Item_Collection::operator[](unsigned position) const
+{
+       Gtk::TreeModel::iterator iter = m_ref_model->children()[position];
+       return OptionStore_Item(m_ref_model, iter);
+}
+
+
+OptionStore::OptionStore()
+ : Glib::ObjectBase("GParted_OptionStore")
+{
+       if (!Slots::record_.size())
+       {
+               Slots::record_.add(Slots::text);
+               Slots::record_.add(Slots::sensitive);
+       }
+
+       set_column_types(Slots::record_);
+}
+
+
+Glib::RefPtr<OptionStore> OptionStore::create()
+{
+       return Glib::RefPtr<OptionStore>(new OptionStore());
+}
+
+
+OptionStore_Item_Collection OptionStore::items()
+{
+       return OptionStore_Item_Collection(Glib::RefPtr<OptionStore>(this));
+}
+
+
+OptionStore_Item_Collection_Const OptionStore::items() const
+{
+       OptionStore *this_ = const_cast<OptionStore*>(this);
+
+       return OptionStore_Item_Collection(Glib::RefPtr<OptionStore>(this_));
+}
+
+
+OptionComboBox::OptionComboBox()
+ : Glib::ObjectBase("GParted_OptionComboBox")
+{
+       OptionComboBox::set_model(OptionStore::create());
+
+       pack_cell_renderers();
+}
+
+
+OptionComboBox::OptionComboBox(const Glib::RefPtr<OptionStore>& ref_model)
+ : Glib::ObjectBase("GParted_OptionComboBox")
+{
+       OptionComboBox::set_model(ref_model);
+
+       pack_cell_renderers();
+}
+
+
+void OptionComboBox::pack_cell_renderers()
+{
+       Gtk::CellLayout::clear();
+
+       Gtk::CellRendererText *cell = manage(new Gtk::CellRendererText());
+       pack_start(*cell);
+       add_attribute(*cell, "text", OptionStore::Slots::text);
+       add_attribute(*cell, "sensitive", OptionStore::Slots::sensitive);
+}
+
+
+OptionStore_Item_Collection OptionComboBox::items()
+{
+       return OptionStore_Item_Collection(m_ref_model);
+}
+
+
+OptionStore_Item_Collection_Const OptionComboBox::items() const
+{
+       return OptionStore_Item_Collection(m_ref_model);
+}
+
+
+void OptionComboBox::set_model(const Glib::RefPtr<OptionStore>& ref_model)
+{
+       Base::set_model(ref_model);
+       m_ref_model = ref_model;
+}
+
+
+Glib::RefPtr<OptionStore> OptionComboBox::get_model()
+{
+       return m_ref_model;
+}
+
+
+Glib::RefPtr<const OptionStore> OptionComboBox::get_model() const
+{
+       return m_ref_model;
+}
+
+
+OptionStore_Item OptionComboBox::get_active()
+{
+       return OptionStore_Item(m_ref_model, Base::get_active());
+}
+
+
+OptionStore_Item_Const OptionComboBox::get_active() const
+{
+       // NOTE: (for Gtkmm4) having a const_iterator, you can get an iterator with
+       //     iter = m_ref_model->get_iter(m_ref_model->get_path(const_iter));
+
+       return OptionStore_Item(m_ref_model, Base::get_active());
+}
+
+
+}//GParted


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