Implemented ability to copy rows from Attribute, Statistics and Self-Test Log

tables.
This commit is contained in:
Alexander Shaduri
2017-09-06 20:11:39 +00:00
parent b9160ec173
commit 5192a9d447
4 changed files with 94 additions and 11 deletions
+1 -7
View File
@@ -26,18 +26,12 @@ Don't rely on smartctl return code (2), parse the output instead.
Need usage cases.
Add ability (through ctrl-C?) to copy selected rows from attributes and statistics pages,
in tab-separated format (for easy pasting into libreoffice).
Testing:
If ETA time has elapsed, but it's still only at 10% completion,
ETA 0 is displayed. Fix.
Detect running tests on launch (maybe ask the user too? some tests
may be stuck due to bad firmware, e.g. 3ware/windows).
Parse all test data.
If smartctl outputs gibberish while testing, the GUI hangs.
Support RAID for these controllers:
@@ -1224,7 +1224,7 @@ namespace {
add("Number of Reallocated Logical Sectors", "", "",
"The number of logical sectors that have been reallocated after device manufacture.\n\n"
"If the value is normalized, this is the whole number percentage of the available logical sector reallocation "
"resources that have been used (i.e., 0..100)."
"resources that have been used (i.e., 0-100)."
"\n\n" + s_unc_text);
add("Read Recovery Attempts", "", "",
+82 -2
View File
@@ -237,6 +237,29 @@ GscInfoWindow::GscInfoWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& re
Gdk::ModifierType(0), Gtk::AccelFlags(0));
}
// Context menu in treeviews
{
std::vector<std::string> treeview_names;
treeview_names.push_back("attributes_treeview");
treeview_names.push_back("statistics_treeview");
treeview_names.push_back("selftest_log_treeview");
for (std::size_t i = 0; i < treeview_names.size(); ++i) {
std::string treeview_name = treeview_names[i];
Gtk::TreeView* treeview = lookup_widget<Gtk::TreeView*>(treeview_name);
treeview->signal_button_press_event().connect(
sigc::bind(sigc::bind(sigc::mem_fun(*this, &GscInfoWindow::on_treeview_button_press_event), treeview), &treeview_menus[treeview_name]), false); // before
Gtk::MenuItem* item = Gtk::manage(new Gtk::MenuItem("Copy Selected Data", true));
item->signal_activate().connect(
sigc::bind(sigc::mem_fun(*this, &GscInfoWindow::on_treeview_menu_copy_clicked), treeview) );
treeview_menus[treeview_name].append(*item);
treeview_menus[treeview_name].show_all(); // Show all menu items when the menu pops up
}
}
// ---------------
@@ -647,7 +670,6 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
}
StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none;
label_list_t label_strings; // outside-of-tree properties
@@ -1197,7 +1219,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
continue;
if (iter->generic_name == "sct_unsupported" && iter->value_bool) { // only show if unsupported
label_strings.push_back(PropertyLabel("SCT commands unsupported.", &(*iter)));
label_strings.push_back(PropertyLabel("SCT temperature commands not supported.", &(*iter)));
if (int(iter->warning) > int(max_tab_warning))
max_tab_warning = iter->warning;
continue;
@@ -2194,6 +2216,64 @@ void GscInfoWindow::on_drive_changed(StorageDevice* pdrive)
bool GscInfoWindow::on_treeview_button_press_event(GdkEventButton* button_event, Gtk::Menu* menu, Gtk::TreeView* treeview)
{
if (button_event->type == GDK_BUTTON_PRESS && button_event->button == 3) {
bool selection_empty = treeview->get_selection()->get_selected_rows().empty();
std::vector<Widget*> children = menu->get_children();
for (std::size_t i = 0; i < children.size(); ++i) {
children[i]->set_sensitive(!selection_empty);
}
menu->popup(button_event->button, button_event->time);
return true;
}
return false;
}
void GscInfoWindow::on_treeview_menu_copy_clicked(Gtk::TreeView* treeview)
{
std::string text;
guint num_cols = treeview->get_n_columns();
std::vector<std::string> col_texts;
for (guint i = 0; i < num_cols; ++i) {
Gtk::TreeViewColumn* tcol = treeview->get_column(i);
col_texts.push_back("\"" + hz::string_replace_copy(tcol->get_title(), "\"", "\"\"") + "\"");
}
text += hz::string_join(col_texts, ',') + "\n";
std::vector<Gtk::TreeModel::Path> selection = treeview->get_selection()->get_selected_rows();
Glib::RefPtr<Gtk::ListStore> list_store = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(treeview->get_model());
for (std::size_t i = 0; i < selection.size(); ++i) {
std::vector<std::string> cell_texts;
Gtk::TreeModel::Path path = selection[i];
Gtk::TreeRow row = *(list_store->get_iter(path));
for (guint j = 0; j < num_cols; ++j) { // gather data only from tree columns, not model columns (like tooltips and helper data)
GType type = list_store->get_column_type(j);
if (type == G_TYPE_INT) {
int32_t value = 0;
row.get_value(j, value);
cell_texts.push_back(hz::number_to_string(value));
} else if (type == G_TYPE_STRING) {
std::string value;
row.get_value(j, value);
cell_texts.push_back("\"" + hz::string_replace_copy(value, "\"", "\"\"") + "\"");
}
}
text += hz::string_join(cell_texts, ',') + "\n";
}
Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
if (clipboard) {
clipboard->set_text(text);
}
}
+10 -1
View File
@@ -13,6 +13,7 @@
#define GSC_INFO_WINDOW_H
#include <gtkmm.h>
#include <map>
#include "applib/app_ui_res_utils.h"
#include "applib/storage_device.h"
@@ -100,9 +101,15 @@ class GscInfoWindow : public AppUIResWidget<GscInfoWindow, true> {
void on_test_stop_button_clicked();
// Callback attached to StorageDevice change signal.
/// Callback attached to StorageDevice change signal.
void on_drive_changed(StorageDevice* pdrive);
/// Callback
bool on_treeview_button_press_event(GdkEventButton* button_event, Gtk::Menu* menu, Gtk::TreeView* treeview);
/// Callback
void on_treeview_menu_copy_clicked(Gtk::TreeView* treeview);
private:
@@ -117,6 +124,8 @@ class GscInfoWindow : public AppUIResWidget<GscInfoWindow, true> {
// --------- Data members
std::map<std::string, Gtk::Menu> treeview_menus; ///< Context menus
// tab headers, to perform their coloration
Glib::ustring tab_identity_name; ///< Tab header name
Glib::ustring tab_attributes_name; ///< Tab header name