Refactored gtkmm utils and app builder to simply the code and document it better.

This commit is contained in:
Alexander Shaduri
2021-03-15 17:55:22 +04:00
parent 7855ce9dfb
commit bedd8f362b
13 changed files with 329 additions and 264 deletions
+133 -76
View File
@@ -24,10 +24,9 @@ Copyright:
// These allow easy attaching of gtkbuilder widget signals to member functions
/// Connect member function (callback) to signal \c signal_name on widget
/// \c ui_element, where \c ui_element is the widget's gtkbuilder name.
/// Connect member function (callback) to signal \ref signal_name on widget
/// \ref ui_element, where \ref ui_element is the widget's gtkbuilder name.
/// This allows easy attaching of gtkbuilder widget signals to member functions.
#define APP_BUILDER_CONNECT(ui_element, signal_name, callback) \
if (true) { \
if (!(ui_element)) \
@@ -38,6 +37,7 @@ Copyright:
} else (void)0
/// Connect member function (callback) with a name of \c on_<widget_name>_<signal_name>
/// to signal \c signal_name on widget \c ui_element, where \c ui_element is the
/// widget's gtkbuilder name.
@@ -46,6 +46,7 @@ Copyright:
/// Inherit this when using GtkBuilder-enabled windows (or any other GtkBuilder-enabled objects).
/// \c Child is the child class that inherits all the functionality of having instance lifetime
/// management and other benefits.
@@ -54,106 +55,62 @@ template<class Child, bool MultiInstance, class WidgetType = Gtk::Window>
class AppBuilderWidget : public WidgetType, public hz::InstanceManager<Child, MultiInstance> {
public:
/// Instance class type, which is also the parent class.
friend class Gtk::Builder; // allow construction through gtkbuilder
friend class hz::InstanceManager<Child, MultiInstance>; // allow construction through instance class
friend class Gtk::Builder; // allow construction via GtkBuilder
// friend class hz::InstanceManager<Child, MultiInstance>; // allow construction through instance class
/// Override parent hz::InstanceManager's function because of non-trivial constructor
static Child* create()
{
if constexpr(!MultiInstance) { // for single-instance objects
if (hz::InstanceManager<Child, MultiInstance>::get_single_instance()) {
return hz::InstanceManager<Child, MultiInstance>::get_single_instance();
}
}
/// Disallow
AppBuilderWidget(const AppBuilderWidget& other) = delete;
std::string error_msg;
/// Disallow
AppBuilderWidget(const AppBuilderWidget&& other) = delete;
auto ui_path = hz::data_file_find("ui", std::string(Child::ui_name) + ".glade");
try {
auto ui = Gtk::Builder::create_from_file(ui_path.u8string()); // may throw
/// Disallow
AppBuilderWidget& operator=(const AppBuilderWidget& other) = delete;
Child* o = nullptr;
ui->get_widget_derived({Child::ui_name.data(), Child::ui_name.size()}, o); // Calls Child's constructor
/// Disallow
AppBuilderWidget& operator=(const AppBuilderWidget&& other) = delete;
if (!o) {
debug_out_fatal("app", "Fatal error: Cannot get root widget from UI-resource-created hierarchy.\n");
gui_show_error_dialog(_("Fatal error: Cannot get root widget from UI-resource-created hierarchy."));
return nullptr;
}
/// Default
~AppBuilderWidget() = default;
if constexpr(!MultiInstance) {
hz::InstanceManager<Child, MultiInstance>::set_single_instance(o); // for single-instance objects
}
return o;
}
catch (Glib::Exception& ex) {
error_msg = ex.what();
}
if (!error_msg.empty()) {
debug_out_fatal("app", "Fatal error: Cannot create UI-resource widgets: " << error_msg << "\n");
gui_show_error_dialog(Glib::ustring::compose(_("Fatal error: Cannot create UI-resource widgets: %1"), error_msg));
}
return nullptr;
}
/// Create an instance of this class, returning an existing instance if not MultiInstance.
/// A glade file in "ui" data domain is loaded with Child::ui_name filename base and is available as
/// `get_ui()` in child object.
/// \return nullptr if widget could not be loaded.
static Child* create();
/// Get UI resource
Glib::RefPtr<Gtk::Builder> get_ui()
{
return ui_;
}
Glib::RefPtr<Gtk::Builder> get_ui();
/// Find a widget in UI and return it.
Gtk::Widget* lookup_widget(const Glib::ustring& name)
{
return lookup_widget<Gtk::Widget*>(name);
}
/// \return nullptr if widget was not found.
Gtk::Widget* lookup_widget(const Glib::ustring& name);
/// Find a widget in UI and return it.
/// \return nullptr if widget was not found.
template<typename WidgetPtr>
WidgetPtr lookup_widget(const Glib::ustring& name)
{
WidgetPtr w = nullptr;
return lookup_widget(name, w);
}
WidgetPtr lookup_widget(const Glib::ustring& name);
/// Find a widget in UI and return it.
/// Find a widget in UI and return it in \ref w.
/// \return false if widget was not found.
template<typename Widget>
Widget* lookup_widget(const Glib::ustring& name, Widget*& w)
{
ui_->get_widget(name, w);
return w;
}
bool lookup_widget(const Glib::ustring& name, Widget*& w);
protected:
// protected constructor / destructor, use create() / destroy() instead of new / delete.
/// Protected constructor, use `create()` instead.
/// GtkBuilder needs this constructor in a child.
/// BaseObjectType is a C type, defined in specific Gtk:: widget class.
AppBuilderWidget(typename WidgetType::BaseObjectType* gtkcobj, Glib::RefPtr<Gtk::Builder> ui)
: WidgetType(gtkcobj), ui_(std::move(ui))
{
// manually connecting signals:
// this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::on_main_window_delete));
// signals of GtkBuilder-created objects:
// Gtk::ToolButton* rescan_devices_toolbutton = 0;
// APP_BUILDER_AUTO_CONNECT(rescan_devices_toolbutton, clicked);
// show();
}
/// Virtual destructor
~AppBuilderWidget() = default;
AppBuilderWidget(typename WidgetType::BaseObjectType* gtkcobj, Glib::RefPtr<Gtk::Builder> ui);
private:
@@ -166,6 +123,106 @@ class AppBuilderWidget : public WidgetType, public hz::InstanceManager<Child, Mu
// ------------------------------------------- Implementation
template<class Child, bool MultiInstance, class WidgetType>
Child* AppBuilderWidget<Child, MultiInstance, WidgetType>::create()
{
if constexpr(!MultiInstance) { // for single-instance objects
if (auto* inst = hz::InstanceManager<Child, MultiInstance>::instance()) {
return inst;
}
}
std::string error_msg;
auto ui_path = hz::data_file_find("ui", std::string(Child::ui_name) + ".glade");
try {
auto ui = Gtk::Builder::create_from_file(ui_path.u8string()); // may throw
Child* o = nullptr;
ui->get_widget_derived({Child::ui_name.data(), Child::ui_name.size()}, o); // Calls Child's constructor
if (!o) {
debug_out_fatal("app", "Fatal error: Cannot get root widget from UI-resource-created hierarchy.\n");
gui_show_error_dialog(_("Fatal error: Cannot get root widget from UI-resource-created hierarchy."));
return nullptr;
}
if constexpr(!MultiInstance) {
hz::InstanceManager<Child, MultiInstance>::set_single_instance(o); // for single-instance objects
}
return o;
}
catch (Glib::Exception& ex) {
error_msg = ex.what();
}
if (!error_msg.empty()) {
debug_out_fatal("app", "Fatal error: Cannot create UI-resource widgets: " << error_msg << "\n");
gui_show_error_dialog(Glib::ustring::compose(_("Fatal error: Cannot create UI-resource widgets: %1"), error_msg));
}
return nullptr;
}
template<class Child, bool MultiInstance, class WidgetType>
Glib::RefPtr<Gtk::Builder> AppBuilderWidget<Child, MultiInstance, WidgetType>::get_ui()
{
return ui_;
}
template<class Child, bool MultiInstance, class WidgetType>
template<typename WidgetPtr>
WidgetPtr AppBuilderWidget<Child, MultiInstance, WidgetType>::lookup_widget(const Glib::ustring& name)
{
WidgetPtr w = nullptr;
lookup_widget(name, w);
return w;
}
template<class Child, bool MultiInstance, class WidgetType>
Gtk::Widget* AppBuilderWidget<Child, MultiInstance, WidgetType>::lookup_widget(const Glib::ustring& name)
{
return lookup_widget<Gtk::Widget*>(name);
}
template<class Child, bool MultiInstance, class WidgetType>
template<typename Widget>
bool AppBuilderWidget<Child, MultiInstance, WidgetType>::lookup_widget(const Glib::ustring& name, Widget*& w)
{
ui_->get_widget(name, w);
return w != nullptr;
}
template<class Child, bool MultiInstance, class WidgetType>
AppBuilderWidget<Child, MultiInstance, WidgetType>::AppBuilderWidget(typename WidgetType::BaseObjectType* gtkcobj, Glib::RefPtr<Gtk::Builder> ui)
: WidgetType(gtkcobj), ui_(std::move(ui))
{
// An example of Child's constructor:
// Manually connect signals:
// this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::on_main_window_delete));
// Automatically connect signals of GtkBuilder-created objects to member functions:
// Gtk::ToolButton* rescan_devices_toolbutton = 0;
// APP_BUILDER_AUTO_CONNECT(rescan_devices_toolbutton, clicked);
// show();
}
#endif
+3 -1
View File
@@ -16,8 +16,10 @@ Copyright:
#include <gtkmm.h>
/// \def APP_GTKMM_CHECK_VERSION(major, minor, micro)
/// Similar to GTK_CHECK_VERSION, but for gtkmm, which lacks this for some reason.
/// Similar to GTK_CHECK_VERSION, but for Gtkmm, which lacks this before gtkmm4.
/// This is useful as Gtk and Gtkmm versions may differ.
#ifndef APP_GTKMM_CHECK_VERSION
#define APP_GTKMM_CHECK_VERSION(major, minor, micro) \
(GTKMM_MAJOR_VERSION > (major) \
+8 -18
View File
@@ -20,7 +20,6 @@ Copyright:
// Note: This works only if the column has custom widget set.
Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column)
{
Gtk::Widget* w = column.get_widget();
@@ -37,19 +36,16 @@ Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column)
// Read column header text and create a label with that text, set it as column's custom widget.
Gtk::Widget* app_gtkmm_labelize_column(Gtk::TreeViewColumn& column)
Gtk::Label& app_gtkmm_labelize_column(Gtk::TreeViewColumn& column)
{
Gtk::Label* label = Gtk::manage(new Gtk::Label(column.get_title()));
label->show();
column.set_widget(*label);
return label;
return *label;
}
// A wrapper around set_tooltip_*() for portability across different gtkmm versions.
void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget,
const Glib::ustring& tooltip_text, bool use_markup)
{
@@ -67,7 +63,7 @@ namespace {
/// This has been copied from _g_utf8_make_valid() (glib-2.20.4).
/// _g_utf8_make_valid() is GLib's private function for auto-correcting
/// the potentially invalid utf-8 data.
inline gchar* gsc_g_utf8_make_valid (const gchar* name)
inline gchar* app_make_valid_utf_c (const gchar* name)
{
GString* str = nullptr;
const gchar* remainder = nullptr;
@@ -123,29 +119,23 @@ Glib::ustring app_ustring_from_gchar(gchar* str)
Glib::ustring app_utf8_make_valid(const Glib::ustring& str)
Glib::ustring app_make_valid_utf8(const Glib::ustring& str)
{
char* s = gsc_g_utf8_make_valid(str.c_str());
if (!s) {
return Glib::ustring();
}
Glib::ustring res(s);
g_free(s);
return res;
return app_ustring_from_gchar(app_make_valid_utf_c(str.c_str()));
}
Glib::ustring app_output_make_valid(const Glib::ustring& str)
Glib::ustring app_make_valid_utf8_from_command_output(const std::string& str)
{
#ifdef _WIN32
try {
return app_utf8_make_valid(Glib::locale_to_utf8(str));
return Glib::locale_to_utf8(str); // detects invalid utf-8 sequences
} catch (Glib::ConvertError& e) {
// nothing, try to fix as it is
}
#endif
return app_utf8_make_valid(str);
return app_ustring_from_gchar(app_make_valid_utf_c(str.c_str()));
}
+54 -36
View File
@@ -19,49 +19,26 @@ Copyright:
/// Get column header widget of a tree view column.
/// Note: This works only if the column has custom widget set.
/// \return nullptr on failure.
Gtk::Widget* app_gtkmm_get_column_header(Gtk::TreeViewColumn& column);
/// Read column header text and create a label with that text. Set the label as
/// column's custom widget and return it.
Gtk::Widget* app_gtkmm_labelize_column(Gtk::TreeViewColumn& column);
Gtk::Label& app_gtkmm_labelize_column(Gtk::TreeViewColumn& column);
/// A wrapper around set_tooltip_*() for portability across different gtkmm versions.
/// A wrapper around set_tooltip_*(), calling appropriate method depending on `use_markup`.
void app_gtkmm_set_widget_tooltip(Gtk::Widget& widget,
const Glib::ustring& tooltip_text, bool use_markup = false);
/// Convenience function for creating a TreeViewColumn .
template<typename T>
int app_gtkmm_create_tree_view_column(Gtk::TreeModelColumn<T>& mcol, Gtk::TreeView& treeview,
const Glib::ustring& title, const Glib::ustring& tooltip_text, bool sortable = false, bool cell_markup = false, bool tooltip_markup = false)
{
int num_tree_cols = treeview.append_column(title, mcol);
Gtk::TreeViewColumn* tcol = treeview.get_column(num_tree_cols - 1);
if (tcol) {
if (sortable)
tcol->set_sort_column(mcol);
app_gtkmm_labelize_column(*tcol);
tcol->set_reorderable(true);
tcol->set_resizable(true);
Gtk::Widget* header = app_gtkmm_get_column_header(*tcol);
if (header)
app_gtkmm_set_widget_tooltip(*header, tooltip_text, tooltip_markup);
}
if (cell_markup) {
if (auto cr_type = dynamic_cast<Gtk::CellRendererText*>(treeview.get_column_cell_renderer(num_tree_cols - 1))) {
treeview.get_column(num_tree_cols - 1)->clear_attributes(*cr_type); // clear "text" attribute. "markup" won't work without this.
treeview.get_column(num_tree_cols - 1)->add_attribute(cr_type->property_markup(), mcol); // render col_type as markup.
}
}
return num_tree_cols;
}
/// Convenience function for creating a TreeViewColumn from model column.
/// \return tree column index
template<typename DataType>
int app_gtkmm_create_tree_view_column(const Gtk::TreeModelColumn<DataType>& model_column,
Gtk::TreeView& treeview, const Glib::ustring& header_title, const Glib::ustring& header_tooltip_text,
bool sortable = false, bool use_cell_markup = false, bool header_tooltip_is_markup = false);
@@ -71,14 +48,55 @@ Glib::ustring app_ustring_from_gchar(gchar* str);
/// Convert a possibly invalid utf-8 string to valid utf-8.
/// \param str string to test and fix.
Glib::ustring app_utf8_make_valid(const Glib::ustring& str);
Glib::ustring app_make_valid_utf8(const Glib::ustring& str);
/// Make command output a valid utf-8 string. Essentially, this calls app_utf8_make_valid(),
/// supplying true for \c in_locale under Win32, and false under other systems.
/// Make command output a valid utf-8 string. This function takes command output
/// (in locale encoding under Windows, utf-8 encoding under other OSes), and converts
/// it to valid utf-8.
/// The reason for this is that in Win32 we can't execute commands under C locale,
/// but we do execute them under C in other systems.
Glib::ustring app_output_make_valid(const Glib::ustring& str);
Glib::ustring app_make_valid_utf8_from_command_output(const std::string& str);
// ------------------------------------------- Implementation
template<typename DataType>
int app_gtkmm_create_tree_view_column(const Gtk::TreeModelColumn<DataType>& model_column,
Gtk::TreeView& treeview, const Glib::ustring& header_title, const Glib::ustring& header_tooltip_text,
bool sortable, bool use_cell_markup, bool header_tooltip_is_markup)
{
int num_tree_cols = treeview.append_column(header_title, model_column);
Gtk::TreeViewColumn* tcol = treeview.get_column(num_tree_cols - 1);
if (tcol) {
if (sortable) {
tcol->set_sort_column(model_column);
}
app_gtkmm_labelize_column(*tcol);
tcol->set_reorderable(true);
tcol->set_resizable(true);
Gtk::Widget* header = app_gtkmm_get_column_header(*tcol);
if (header) {
app_gtkmm_set_widget_tooltip(*header, header_tooltip_text, header_tooltip_is_markup);
}
}
if (use_cell_markup) {
if (auto* cr_type = dynamic_cast<Gtk::CellRendererText*>(treeview.get_column_cell_renderer(num_tree_cols - 1))) {
treeview.get_column(num_tree_cols - 1)->clear_attributes(*cr_type); // clear "text" attribute. "markup" won't work without this.
treeview.get_column(num_tree_cols - 1)->add_attribute(cr_type->property_markup(), model_column); // render col_type as markup.
}
}
return num_tree_cols - 1;
}
+1 -1
View File
@@ -79,7 +79,7 @@ void GscAboutDialog::on_response(int response_id)
if (response_id == Gtk::RESPONSE_NONE || response_id == Gtk::RESPONSE_DELETE_EVENT
|| response_id == Gtk::RESPONSE_CANCEL || response_id == Gtk::RESPONSE_CLOSE) {
debug_out_info("app", DBG_FUNC_MSG << "Closing the dialog.\n");
destroy(); // close the window and delete the object
delete this; // close the window and delete the object
}
}
+2 -2
View File
@@ -138,7 +138,7 @@ bool GscAddDeviceWindow::on_delete_event([[maybe_unused]] GdkEventAny* e)
void GscAddDeviceWindow::on_window_cancel_button_clicked()
{
destroy(this);
delete this;
}
@@ -159,7 +159,7 @@ void GscAddDeviceWindow::on_window_ok_button_clicked()
main_window_->add_device(dev, type, params);
}
destroy(this);
delete this;
}
+1 -1
View File
@@ -92,7 +92,7 @@ void gsc_no_info_dialog_show(const std::string& message, const std::string& sec_
if (response == Gtk::RESPONSE_HELP) {
GscTextWindow<SmartctlOutputInstance>* win = GscTextWindow<SmartctlOutputInstance>::create();
win->set_text(output_window_title, output, true, true);
win->set_text_from_command(output_window_title, output);
if (!default_save_filename.empty())
win->set_save_filename(default_save_filename);
+2 -2
View File
@@ -421,7 +421,7 @@ void GscExecutorLogWindow::on_tree_selection_changed()
if (auto* output_textview = this->lookup_widget<Gtk::TextView*>("output_textview")) {
Glib::RefPtr<Gtk::TextBuffer> buffer = output_textview->get_buffer();
if (buffer) {
buffer->set_text(app_output_make_valid(entry->std_output));
buffer->set_text(app_make_valid_utf8_from_command_output(entry->std_output));
Glib::RefPtr<Gtk::TextTag> tag;
Glib::RefPtr<Gtk::TextTagTable> table = buffer->get_tag_table();
@@ -437,7 +437,7 @@ void GscExecutorLogWindow::on_tree_selection_changed()
if (auto* command_entry = this->lookup_widget<Gtk::Entry*>("command_entry")) {
std::string cmd_text = entry->command + " " + entry->parameters;
command_entry->set_text(app_output_make_valid(cmd_text));
command_entry->set_text(app_make_valid_utf8_from_command_output(cmd_text));
}
if (auto* window_save_current_button = this->lookup_widget<Gtk::Button*>("window_save_current_button"))
+46 -46
View File
@@ -722,7 +722,7 @@ void GscInfoWindow::on_view_output_button_clicked()
output = this->drive->get_info_output();
}
win->set_text(_("Smartctl Output"), output, true, true);
win->set_text_from_command(_("Smartctl Output"), output);
std::string filename = drive->get_save_filename();
if (!filename.empty())
@@ -838,7 +838,7 @@ void GscInfoWindow::on_close_window_button_clicked()
if (drive && drive->get_test_is_active()) { // disallow close if test is active.
gui_show_warn_dialog(_("Please wait until all tests are finished."), this);
} else {
destroy(this); // deletes this object and nullifies instance
delete this; // deletes this object and nullifies instance
}
}
@@ -963,63 +963,63 @@ void GscInfoWindow::fill_ui_attributes(const std::vector<StorageProperty>& props
auto* treeview = lookup_widget<Gtk::TreeView*>("attributes_treeview");
Gtk::TreeModelColumnRecord model_columns;
int num_tree_cols = 0;
[[maybe_unused]] int num_tree_col = 0;
// ID (int), Name, Flag (hex), Normalized Value (uint8), Worst (uint8), Thresh (uint8), Raw (int64), Type (string),
// Updated (string), When Failed (string)
Gtk::TreeModelColumn<int32_t> col_id;
model_columns.add(col_id); // we can use the column variable by value after this.
num_tree_cols = app_gtkmm_create_tree_view_column(col_id, *treeview, _("ID"), _("Attribute ID"), true);
num_tree_col = app_gtkmm_create_tree_view_column(col_id, *treeview, _("ID"), _("Attribute ID"), true);
Gtk::TreeModelColumn<Glib::ustring> col_name;
model_columns.add(col_name);
num_tree_cols = app_gtkmm_create_tree_view_column(col_name, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_name, *treeview,
_("Name"), _("Attribute name (this is deduced from ID by smartctl and may be incorrect, as it's highly vendor-specific)"), true);
treeview->set_search_column(col_name.index());
auto* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_cols - 1));
auto* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_col));
if (cr_name)
cr_name->property_weight() = Pango::WEIGHT_BOLD;
Gtk::TreeModelColumn<Glib::ustring> col_failed;
model_columns.add(col_failed);
num_tree_cols = app_gtkmm_create_tree_view_column(col_failed, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_failed, *treeview,
_("Failed"), _("When failed (that is, the normalized value became equal to or less than threshold)"), true, true);
Gtk::TreeModelColumn<std::string> col_value;
model_columns.add(col_value);
num_tree_cols = app_gtkmm_create_tree_view_column(col_value, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_value, *treeview,
C_("value", "Normalized"), _("Normalized value (highly vendor-specific; converted from Raw value by the drive's firmware)"), false);
Gtk::TreeModelColumn<std::string> col_worst;
model_columns.add(col_worst);
num_tree_cols = app_gtkmm_create_tree_view_column(col_worst, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_worst, *treeview,
C_("value", "Worst"), _("The worst normalized value recorded for this attribute during the drive's lifetime (with SMART enabled)"), false);
Gtk::TreeModelColumn<std::string> col_threshold;
model_columns.add(col_threshold);
num_tree_cols = app_gtkmm_create_tree_view_column(col_threshold, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_threshold, *treeview,
C_("value", "Threshold"), _("Threshold for normalized value. Normalized value should be greater than threshold (unless vendor thinks otherwise)."), false);
Gtk::TreeModelColumn<std::string> col_raw;
model_columns.add(col_raw);
num_tree_cols = app_gtkmm_create_tree_view_column(col_raw, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_raw, *treeview,
_("Raw value"), _("Raw value as reported by drive. May or may not be sensible."), false);
Gtk::TreeModelColumn<Glib::ustring> col_type;
model_columns.add(col_type);
num_tree_cols = app_gtkmm_create_tree_view_column(col_type, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_type, *treeview,
_("Type"), _("Alarm condition is reached when normalized value becomes less than or equal to threshold. Type indicates whether it's a signal of drive's pre-failure time or just an old age."), false, true);
// Doesn't carry that much info. Advanced users can look at the flags.
// Gtk::TreeModelColumn<Glib::ustring> col_updated;
// model_columns.add(col_updated);
// num_tree_cols = app_gtkmm_create_tree_view_column(col_updated, *treeview,
// tree_col = app_gtkmm_create_tree_view_column(col_updated, *treeview,
// "Updated", "The attribute is usually updated continuously, or during Offline Data Collection only. This column indicates that.", true);
Gtk::TreeModelColumn<std::string> col_flag_value;
model_columns.add(col_flag_value);
num_tree_cols = app_gtkmm_create_tree_view_column(col_flag_value, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_flag_value, *treeview,
_("Flags"), _("Flags") + "\n\n"s
+ Glib::ustring::compose(_("If given in %1 format, the presence of each letter indicates that the flag is on."), "POSRCK+") + "\n"
+ _("P: pre-failure attribute (if the attribute failed, the drive is failing)") + "\n"
@@ -1044,7 +1044,7 @@ void GscInfoWindow::fill_ui_attributes(const std::vector<StorageProperty>& props
list_store->set_sort_column(col_id, Gtk::SORT_ASCENDING); // default sort
treeview->set_model(list_store);
for (int i = 0; i < num_tree_cols; ++i) {
for (int i = 0; i < int(treeview->get_n_columns()); ++i) {
Gtk::TreeViewColumn* tcol = treeview->get_column(i);
tcol->set_cell_data_func(*(tcol->get_first_cell()),
sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage));
@@ -1111,25 +1111,25 @@ void GscInfoWindow::fill_ui_statistics(const std::vector<StorageProperty>& props
auto* treeview = lookup_widget<Gtk::TreeView*>("statistics_treeview");
Gtk::TreeModelColumnRecord model_columns;
int num_tree_cols = 0;
[[maybe_unused]] int num_tree_col = 0;
Gtk::TreeModelColumn<Glib::ustring> col_description;
model_columns.add(col_description);
num_tree_cols = app_gtkmm_create_tree_view_column(col_description, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_description, *treeview,
_("Description"), _("Entry description"), true);
treeview->set_search_column(col_description.index());
// Gtk::CellRendererText* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_cols - 1));
// Gtk::CellRendererText* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_col));
// if (cr_name)
// cr_name->property_weight() = Pango::WEIGHT_BOLD ;
Gtk::TreeModelColumn<std::string> col_value;
model_columns.add(col_value);
num_tree_cols = app_gtkmm_create_tree_view_column(col_value, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_value, *treeview,
_("Value"), Glib::ustring::compose(_("Value (can be normalized if '%1' flag is present)"), "N"), false);
Gtk::TreeModelColumn<std::string> col_flags;
model_columns.add(col_flags);
num_tree_cols = app_gtkmm_create_tree_view_column(col_flags, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_flags, *treeview,
_("Flags"), _("Flags") + "\n\n"s
+ _("N: value is normalized") + "\n"
+ _("D: supports Device Statistics Notification (DSN)") + "\n"
@@ -1138,7 +1138,7 @@ void GscInfoWindow::fill_ui_statistics(const std::vector<StorageProperty>& props
Gtk::TreeModelColumn<std::string> col_page_offset;
model_columns.add(col_page_offset);
num_tree_cols = app_gtkmm_create_tree_view_column(col_page_offset, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_page_offset, *treeview,
_("Page, Offset"), _("Page and offset of the entry"), false);
Gtk::TreeModelColumn<Glib::ustring> col_tooltip;
@@ -1154,7 +1154,7 @@ void GscInfoWindow::fill_ui_statistics(const std::vector<StorageProperty>& props
treeview->set_model(list_store);
// No sorting (we don't want to screw up the headers).
for (int i = 0; i < num_tree_cols; ++i) {
for (int i = 0; i < int(treeview->get_n_columns()); ++i) {
Gtk::TreeViewColumn* tcol = treeview->get_column(i);
tcol->set_cell_data_func(*(tcol->get_first_cell()),
sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage));
@@ -1291,42 +1291,42 @@ void GscInfoWindow::fill_ui_self_test_log(const std::vector<StorageProperty>& pr
auto* treeview = lookup_widget<Gtk::TreeView*>("selftest_log_treeview");
Gtk::TreeModelColumnRecord model_columns;
int num_tree_cols = 0;
[[maybe_unused]] int num_tree_col = 0;
// Test num., Type, Status, % Completed, Lifetime hours, LBA of the first error
Gtk::TreeModelColumn<uint32_t> col_num;
model_columns.add(col_num); // we can use the column variable by value after this.
num_tree_cols = app_gtkmm_create_tree_view_column(col_num, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_num, *treeview,
_("Test #"), _("Test # (greater may mean newer or older depending on drive model)"), true);
auto* cr_test_num = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_cols - 1));
auto* cr_test_num = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_col));
if (cr_test_num)
cr_test_num->property_weight() = Pango::WEIGHT_BOLD ;
Gtk::TreeModelColumn<std::string> col_type;
model_columns.add(col_type);
num_tree_cols = app_gtkmm_create_tree_view_column(col_type, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_type, *treeview,
_("Type"), _("Type of the test performed"), true);
treeview->set_search_column(col_type.index());
Gtk::TreeModelColumn<std::string> col_status;
model_columns.add(col_status);
num_tree_cols = app_gtkmm_create_tree_view_column(col_status, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_status, *treeview,
_("Status"), _("Test completion status"), true);
Gtk::TreeModelColumn<std::string> col_percent;
model_columns.add(col_percent);
num_tree_cols = app_gtkmm_create_tree_view_column(col_percent, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_percent, *treeview,
_("% Completed"), _("Percentage of the test completed. Instantly-aborted tests have 10%, while unsupported ones <i>may</i> have 100%."), true, false, true);
Gtk::TreeModelColumn<std::string> col_hours;
model_columns.add(col_hours);
num_tree_cols = app_gtkmm_create_tree_view_column(col_hours, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_hours, *treeview,
_("Lifetime hours"), _("During which hour of the drive's (powered on) lifetime did the test complete (or abort)"), true);
Gtk::TreeModelColumn<std::string> col_lba;
model_columns.add(col_lba);
num_tree_cols = app_gtkmm_create_tree_view_column(col_lba, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_lba, *treeview,
_("LBA of the first error"), _("LBA of the first error (if an LBA-related error happened)"), true);
Gtk::TreeModelColumn<Glib::ustring> col_tooltip;
@@ -1342,7 +1342,7 @@ void GscInfoWindow::fill_ui_self_test_log(const std::vector<StorageProperty>& pr
list_store->set_sort_column(col_num, Gtk::SORT_ASCENDING); // default sort
treeview->set_model(list_store);
for (int i = 0; i < num_tree_cols; ++i) {
for (int i = 0; i < int(treeview->get_n_columns()); ++i) {
Gtk::TreeViewColumn* tcol = treeview->get_column(i);
tcol->set_cell_data_func(*(tcol->get_first_cell()),
sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage));
@@ -1402,35 +1402,35 @@ void GscInfoWindow::fill_ui_error_log(const std::vector<StorageProperty>& props)
auto* treeview = lookup_widget<Gtk::TreeView*>("error_log_treeview");
Gtk::TreeModelColumnRecord model_columns;
int num_tree_cols = 0;
[[maybe_unused]] int num_tree_col = 0;
// Error Number, Lifetime Hours, State, Type, Details, [tooltips]
Gtk::TreeModelColumn<uint32_t> col_num;
model_columns.add(col_num); // we can use the column variable by value after this.
num_tree_cols = app_gtkmm_create_tree_view_column(col_num, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_num, *treeview,
_("Error #"), _("Error # in the error log (greater means newer)"), true);
if (auto* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_cols - 1)))
if (auto* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_col)))
cr_name->property_weight() = Pango::WEIGHT_BOLD ;
Gtk::TreeModelColumn<std::string> col_hours;
model_columns.add(col_hours);
num_tree_cols = app_gtkmm_create_tree_view_column(col_hours, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_hours, *treeview,
_("Lifetime hours"), _("During which hour of the drive's (powered on) lifetime did the error happen."), true);
Gtk::TreeModelColumn<std::string> col_state;
model_columns.add(col_state);
num_tree_cols = app_gtkmm_create_tree_view_column(col_state, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_state, *treeview,
C_("power", "State"), _("Power state of the drive when the error occurred"), false);
Gtk::TreeModelColumn<Glib::ustring> col_type;
model_columns.add(col_type);
num_tree_cols = app_gtkmm_create_tree_view_column(col_type, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_type, *treeview,
_("Type"), _("Type of error"), true);
Gtk::TreeModelColumn<std::string> col_details;
model_columns.add(col_details);
num_tree_cols = app_gtkmm_create_tree_view_column(col_details, *treeview,
num_tree_col = app_gtkmm_create_tree_view_column(col_details, *treeview,
_("Details"), _("Additional details (e.g. LBA where the error occurred, etc...)"), true);
Gtk::TreeModelColumn<Glib::ustring> col_tooltip;
@@ -1449,7 +1449,7 @@ void GscInfoWindow::fill_ui_error_log(const std::vector<StorageProperty>& props)
list_store->set_sort_column(col_num, Gtk::SORT_DESCENDING); // default sort
treeview->set_model(list_store);
for (int i = 0; i < num_tree_cols; ++i) {
for (int i = 0; i < int(treeview->get_n_columns()); ++i) {
Gtk::TreeViewColumn* tcol = treeview->get_column(i);
tcol->set_cell_data_func(*(tcol->get_first_cell()),
sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage));
@@ -1607,29 +1607,29 @@ WarningLevel GscInfoWindow::fill_ui_capabilities(const std::vector<StorageProper
auto* treeview = lookup_widget<Gtk::TreeView*>("capabilities_treeview");
Gtk::TreeModelColumnRecord model_columns;
int num_tree_cols = 0;
[[maybe_unused]] int num_tree_col = 0;
// N, Name, Flag, Capabilities, [tooltips]
Gtk::TreeModelColumn<int> col_index;
model_columns.add(col_index); // we can use the column variable by value after this.
num_tree_cols = app_gtkmm_create_tree_view_column(col_index, *treeview, _("#"), _("Entry #"), true);
num_tree_col = app_gtkmm_create_tree_view_column(col_index, *treeview, _("#"), _("Entry #"), true);
Gtk::TreeModelColumn<Glib::ustring> col_name;
model_columns.add(col_name);
num_tree_cols = app_gtkmm_create_tree_view_column(col_name, *treeview, _("Name"), _("Name"), true);
num_tree_col = app_gtkmm_create_tree_view_column(col_name, *treeview, _("Name"), _("Name"), true);
treeview->set_search_column(col_name.index());
auto* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_cols - 1));
auto* cr_name = dynamic_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_col));
if (cr_name)
cr_name->property_weight() = Pango::WEIGHT_BOLD ;
Gtk::TreeModelColumn<std::string> col_flag_value;
model_columns.add(col_flag_value);
num_tree_cols = app_gtkmm_create_tree_view_column(col_flag_value, *treeview, _("Flags"), _("Flags"), false);
num_tree_col = app_gtkmm_create_tree_view_column(col_flag_value, *treeview, _("Flags"), _("Flags"), false);
Gtk::TreeModelColumn<Glib::ustring> col_str_values;
model_columns.add(col_str_values);
num_tree_cols = app_gtkmm_create_tree_view_column(col_str_values, *treeview, _("Capabilities"), _("Capabilities"), false);
num_tree_col = app_gtkmm_create_tree_view_column(col_str_values, *treeview, _("Capabilities"), _("Capabilities"), false);
Gtk::TreeModelColumn<Glib::ustring> col_tooltip;
model_columns.add(col_tooltip);
@@ -1644,7 +1644,7 @@ WarningLevel GscInfoWindow::fill_ui_capabilities(const std::vector<StorageProper
list_store->set_sort_column(col_index, Gtk::SORT_ASCENDING); // default sort
treeview->set_model(list_store);
for (int i = 0; i < num_tree_cols; ++i) {
for (int i = 0; i < int(treeview->get_n_columns()); ++i) {
Gtk::TreeViewColumn* tcol = treeview->get_column(i);
tcol->set_cell_data_func(*(tcol->get_first_cell()),
sigc::bind(sigc::ptr_fun(app_list_cell_renderer_func), col_storage));
+2 -2
View File
@@ -543,9 +543,9 @@ bool app_init_and_loop(int& argc, char**& argv)
debug_out_info("app", "Main loop exited.\n");
// close the main window and delete its object
GscMainWindow::destroy();
delete GscMainWindow::instance();
GscExecutorLogWindow::destroy();
delete GscExecutorLogWindow::instance();
// std::cerr << app_get_debug_buffer_str(); // this will output everything that went through libdebug.
+3 -3
View File
@@ -458,7 +458,7 @@ bool GscPreferencesWindow::on_delete_event([[maybe_unused]] GdkEventAny* e)
void GscPreferencesWindow::on_window_cancel_button_clicked()
{
destroy(this);
delete this;
}
@@ -491,7 +491,7 @@ void GscPreferencesWindow::on_window_ok_button_clicked()
main_window_->show_prefs_updated_message();
}
destroy(this);
delete this;
}
@@ -506,7 +506,7 @@ void GscPreferencesWindow::on_window_reset_all_button_clicked()
rconfig::clear_config();
import_config();
// close the window, because the user might get the impression that "Cancel" will revert.
destroy(this);
delete this;
}
}
+38 -7
View File
@@ -16,6 +16,7 @@ Copyright:
#include <gtkmm.h>
#include <gdk/gdk.h> // GDK_KEY_Escape
#include <memory>
#include <variant>
#include "hz/debug.h"
#include "hz/fs.h"
@@ -72,18 +73,36 @@ class GscTextWindow : public AppBuilderWidget<GscTextWindow<InstanceSwitch>, Ins
}
/// Set the text to display
void set_text_from_command(const Glib::ustring& title, const std::string& contents)
{
this->contents_ = contents; // we save it to prevent its mangling through the widget
this->set_text_helper(title, true, true);
}
void set_text(const Glib::ustring& title, const Glib::ustring& contents,
bool save_visible = false, bool use_monospace = false)
{
this->contents_ = contents;
this->set_text_helper(title, save_visible, use_monospace);
}
/// Set the text to display
void set_text_helper(const Glib::ustring& title,
bool save_visible = false, bool use_monospace = false)
{
this->set_title(title + " - " + default_title_); // something - gsmartcontrol
this->contents_ = contents; // we save it to prevent its mangling through the widget
Gtk::TextView* textview = this->template lookup_widget<Gtk::TextView*>("main_textview");
if (textview) {
Glib::RefPtr<Gtk::TextBuffer> buffer = textview->get_buffer();
buffer->set_text(app_output_make_valid(contents));
if (std::holds_alternative<std::string>(contents_)) {
buffer->set_text(app_make_valid_utf8_from_command_output(std::get<std::string>(contents_)));
} else {
buffer->set_text(std::get<Glib::ustring>(contents_));
}
if (use_monospace) {
Glib::RefPtr<Gtk::TextTag> tag = buffer->create_tag();
@@ -207,7 +226,13 @@ class GscTextWindow : public AppBuilderWidget<GscTextWindow<InstanceSwitch>, Ins
file += ".txt";
}
auto ec = hz::fs_file_put_contents(hz::fs::u8path(file), this->contents_.c_str());
std::string text;
if (std::holds_alternative<std::string>(contents_)) {
text = std::get<std::string>(contents_);
} else {
text = std::get<Glib::ustring>(contents_);
}
auto ec = hz::fs_file_put_contents(hz::fs::u8path(file), text);
if (ec) {
gui_show_error_dialog(_("Cannot save data to file"), ec.message(), this);
}
@@ -228,14 +253,20 @@ class GscTextWindow : public AppBuilderWidget<GscTextWindow<InstanceSwitch>, Ins
/// Button click callback
void on_close_window_button_clicked()
{
this->destroy(this);
delete this;
}
private:
Glib::ustring default_title_; ///< Window title
Glib::ustring contents_; ///< The text to display
/// The text to display
std::variant<
std::string, // command output
Glib::ustring // utf-8 text
> contents_;
std::string save_filename_; ///< Default filename for Save As
};
+36 -69
View File
@@ -12,15 +12,15 @@ Copyright:
#ifndef HZ_INSTANCE_MANAGER_H
#define HZ_INSTANCE_MANAGER_H
#include <memory>
namespace hz {
/**
Inherit this class to have a single- or multi-instance objects, e.g. windows.
This is a multi-instance specialization.
*/
/// Inherit this class template to have a single- or multi-instance objects, e.g. windows.
/// This is a multi-instance implementation.
template<class Child, bool MultiInstance>
class InstanceManager {
protected:
@@ -28,49 +28,27 @@ class InstanceManager {
/// Can't construct / delete this directly! use create() and destroy()
InstanceManager() = default;
/// Can't construct / delete this directly! use create() and destroy()
~InstanceManager() = default;
public:
/// Non-copyable
/// Deleted
InstanceManager(const InstanceManager& other) = delete;
/// Non-copyable
/// Deleted
InstanceManager(const InstanceManager&& other) = delete;
/// Deleted
InstanceManager& operator=(const InstanceManager&) = delete;
/// Deleted
InstanceManager& operator=(const InstanceManager&&) = delete;
/// Create a new instance or return an already created one if single-instance.
/// If single-instance, the call will be serialized.
static Child* create()
{
return new Child();
}
/// Default
~InstanceManager() = default;
/// Destroy an instance. \c instance must be passed if using
/// multi-instance object. If single-instance, \c instance has no effect.
/// If single-instance, the call will be serialized.
static void destroy(Child* instance)
{
if (instance) {
delete instance;
}
}
protected:
// We have these functions for multi-instance variant too to
// support transparently switching between them.
/// Returns true if there is a valid single-instance object.
/// In multi-instance version this always returns false.
static constexpr bool has_single_instance()
{
return false;
}
/// The default multi-instance implementation doesn't support `instance()`
static Child* instance() = delete;
};
@@ -82,59 +60,48 @@ template<class Child>
class InstanceManager<Child, false> {
protected:
/// Can't construct / delete this directly! use create() and destroy()
InstanceManager() = default;
~InstanceManager() = default;
public:
/// Non-construction-copyable
/// Deleted
InstanceManager(const InstanceManager& other) = delete;
/// Non-copyable
/// Deleted
InstanceManager(const InstanceManager&& other) = delete;
/// Deleted
InstanceManager& operator=(const InstanceManager&) = delete;
/// Deleted
InstanceManager& operator=(const InstanceManager&&) = delete;
static Child* create()
/// Default
~InstanceManager() = default;
/// Return a single existing instance of this template instantiation.
/// \return nullptr if no instances were created yet.
static Child* instance()
{
if (instance_) // for single-instance objects
return instance_;
instance_ = new Child();
return instance_;
}
static void destroy()
{
if (instance_) {
delete instance_;
instance_ = nullptr;
}
return instance_.get();
}
protected:
static constexpr bool has_single_instance()
{
return (bool)instance_;
}
static Child* get_single_instance()
{
return instance_;
}
/// Set the instance.
static void set_single_instance(Child* instance)
{
instance_ = instance;
instance_.reset(instance);
}
// if an object is allowed to have a single instance only, these are needed.
static inline Child* instance_ = nullptr; ///< Single instance pointer
private:
static inline std::unique_ptr<Child> instance_; ///< Single instance pointer
};