diff --git a/gsmartcontrol/TODO b/gsmartcontrol/TODO index 3f2c81f..2ee5545 100644 --- a/gsmartcontrol/TODO +++ b/gsmartcontrol/TODO @@ -30,8 +30,6 @@ TODO: Implement PolKit support. -Remember window size / position (at least the main window size/pos and info size) - Format large numbers with thousands separators (from locale) includes LBAs and seek error rates. diff --git a/gsmartcontrol/src/applib/storage_property.cpp b/gsmartcontrol/src/applib/storage_property.cpp index 5cd2074..5406be3 100644 --- a/gsmartcontrol/src/applib/storage_property.cpp +++ b/gsmartcontrol/src/applib/storage_property.cpp @@ -11,11 +11,15 @@ #include #include // not iosfwd - it doesn't work +#include +#include +#include #include "hz/string_num.h" // number_to_string #include "hz/stream_cast.h" // stream_cast<> #include "hz/format_unit.h" // format_time_length #include "hz/string_algo.h" // string_join +#include "hz/string_num.h" // number_to_string #include "storage_property.h" @@ -34,6 +38,20 @@ std::ostream& operator<< (std::ostream& os, const StorageCapability& p) +std::string StorageAttribute::format_raw_value() const +{ + // If it's fully a number, format it with commas + if (hz::number_to_string(raw_value_int) == raw_value) { + std::stringstream ss; + ss.imbue(std::locale("")); + ss << std::fixed << raw_value_int; + return ss.str(); + } + return raw_value; +} + + + std::ostream& operator<< (std::ostream& os, const StorageAttribute& p) { // os << p.name << ": " @@ -42,7 +60,7 @@ std::ostream& operator<< (std::ostream& os, const StorageAttribute& p) } else { os << "-"; } - os << " (" << p.raw_value_int << ")"; + os << " (" << p.format_raw_value() << ")"; return os; } @@ -81,6 +99,16 @@ std::string StorageErrorBlock::get_readable_error_types(const std::vector value; ///< Normalized value. May be unset ("---"). @@ -155,6 +159,9 @@ class StorageErrorBlock { /// Get readable error types from reported types static std::string get_readable_error_types(const std::vector& types); + /// Format lifetime hours with comma + std::string format_lifetime_hours() const; + uint32_t error_num; ///< Error number uint32_t lifetime_hours; ///< When the error occurred (in lifetime hours) std::string device_state; ///< Device state during the error - "active or idle", standby, etc... @@ -249,13 +256,17 @@ class StorageSelftestEntry { } + /// Format lifetime hours with comma + std::string format_lifetime_hours() const; + + uint32_t test_num; ///< Test number. always starts from 1. larger means older or newer, depending on model. 0 for capability. std::string type; ///< Extended offline, Short offline, Conveyance offline, etc... . capability: unused. std::string status_str; ///< Self-test routine in progress, Completed without error, etc... (as reported by log or capability) status_t status; ///< same as status_str, but from enum int8_t remaining_percent; ///< Remaining %. 0% for completed, 90% for started. -1 if n/a. uint32_t lifetime_hours; ///< When the test happened (in lifetime hours). capability: unused. - std::string lba_of_first_error; ///< LBA of the first error. "-" or value (format?). capability: unused. + std::string lba_of_first_error; ///< LBA of the first error. "-" or value (format? usually hex). capability: unused. }; diff --git a/gsmartcontrol/src/gsc_executor_log_window.cpp b/gsmartcontrol/src/gsc_executor_log_window.cpp index 04c1453..222fa58 100644 --- a/gsmartcontrol/src/gsc_executor_log_window.cpp +++ b/gsmartcontrol/src/gsc_executor_log_window.cpp @@ -17,6 +17,7 @@ #include "applib/app_gtkmm_utils.h" // app_gtkmm_create_tree_view_column #include "applib/app_gtkmm_features.h" #include "hz/scoped_ptr.h" +#include "rconfig/rconfig_mini.h" #include "gsc_executor_log_window.h" #include "gsc_init.h" // app_get_debug_buffer_str() @@ -192,6 +193,9 @@ void GscExecutorLogWindow::on_window_save_current_button_clicked() CmdexSyncCommandInfoRefPtr entry = (*iter)[col_entry]; static std::string last_dir; + if (last_dir.empty()) { + rconfig::get_data("gui/drive_data_open_save_dir", last_dir); + } int result = 0; #if GTK_CHECK_VERSION(3, 20, 0) @@ -234,6 +238,7 @@ void GscExecutorLogWindow::on_window_save_current_button_clicked() file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif + rconfig::set_data("gui/drive_data_open_save_dir", last_dir); hz::File f(file); if (!f.put_contents(entry->std_output)) { @@ -283,6 +288,9 @@ void GscExecutorLogWindow::on_window_save_all_button_clicked() static std::string last_dir; + if (last_dir.empty()) { + rconfig::get_data("gui/drive_data_open_save_dir", last_dir); + } int result = 0; #if GTK_CHECK_VERSION(3, 20, 0) @@ -325,6 +333,7 @@ void GscExecutorLogWindow::on_window_save_all_button_clicked() file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif + rconfig::set_data("gui/drive_data_open_save_dir", last_dir); hz::File f(file); if (!f.put_contents(exss.str())) { diff --git a/gsmartcontrol/src/gsc_info_window.cpp b/gsmartcontrol/src/gsc_info_window.cpp index a9c5c83..b425ae3 100644 --- a/gsmartcontrol/src/gsc_info_window.cpp +++ b/gsmartcontrol/src/gsc_info_window.cpp @@ -715,7 +715,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests row[col_value] = (iter->value_attribute.value.defined() ? hz::number_to_string(iter->value_attribute.value.value()) : "-"); row[col_worst] = (iter->value_attribute.worst.defined() ? hz::number_to_string(iter->value_attribute.worst.value()) : "-"); row[col_threshold] = (iter->value_attribute.threshold.defined() ? hz::number_to_string(iter->value_attribute.threshold.value()) : "-"); - row[col_raw] = iter->value_attribute.raw_value; + row[col_raw] = iter->value_attribute.format_raw_value(); row[col_type] = attr_type; row[col_updated] = StorageAttribute::get_update_type_name(iter->value_attribute.update_type); row[col_failed] = fail_time; @@ -762,7 +762,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests if (cr_name) cr_name->property_weight() = Pango::WEIGHT_BOLD ; - Gtk::TreeModelColumn col_hours; + Gtk::TreeModelColumn col_hours; model_columns.add(col_hours); num_tree_cols = 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); @@ -853,7 +853,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests Gtk::TreeRow row = *(list_store->append()); row[col_num] = iter->value_error_block.error_num; - row[col_hours] = iter->value_error_block.lifetime_hours; + row[col_hours] = iter->value_error_block.format_lifetime_hours(); row[col_state] = iter->value_error_block.device_state; row[col_type] = StorageErrorBlock::get_readable_error_types(iter->value_error_block.reported_types); row[col_details] = (type_details.empty() ? "-" : type_details); // e.g. OBS has no details @@ -917,7 +917,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests num_tree_cols = app_gtkmm_create_tree_view_column(col_percent, *treeview, "% Completed", "Percentage of the test completed. Instantly-aborted tests have 10%, while unsupported ones _may_ have 100%.", true); - Gtk::TreeModelColumn col_hours; + Gtk::TreeModelColumn col_hours; model_columns.add(col_hours); num_tree_cols = 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); @@ -972,7 +972,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests row[col_type] = iter->value_selftest_entry.type; row[col_status] = iter->value_selftest_entry.get_status_str(); row[col_percent] = hz::number_to_string(100 - iter->value_selftest_entry.remaining_percent) + "%"; - row[col_hours] = iter->value_selftest_entry.lifetime_hours; + row[col_hours] = iter->value_selftest_entry.format_lifetime_hours(); row[col_lba] = iter->value_selftest_entry.lba_of_first_error; // There are no descriptions in self-test log entries, so don't display // "No description available" for all of them. @@ -1345,6 +1345,9 @@ void GscInfoWindow::on_view_output_button_clicked() void GscInfoWindow::on_save_info_button_clicked() { static std::string last_dir; + if (last_dir.empty()) { + rconfig::get_data("gui/drive_data_open_save_dir", last_dir); + } int result = 0; std::string filename = drive->get_save_filename(); @@ -1395,6 +1398,7 @@ void GscInfoWindow::on_save_info_button_clicked() file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif + rconfig::set_data("gui/drive_data_open_save_dir", last_dir); hz::File f(file); std::string data = this->drive->get_full_output(); diff --git a/gsmartcontrol/src/gsc_main_window.cpp b/gsmartcontrol/src/gsc_main_window.cpp index ec03a8e..5efaff3 100644 --- a/gsmartcontrol/src/gsc_main_window.cpp +++ b/gsmartcontrol/src/gsc_main_window.cpp @@ -1291,6 +1291,9 @@ void GscMainWindow::show_add_device_chooser() void GscMainWindow::show_load_virtual_file_chooser() { static std::string last_dir; + if (last_dir.empty()) { + rconfig::get_data("gui/drive_data_open_save_dir", last_dir); + } int result = 0; #if GTK_CHECK_VERSION(3, 20, 0) @@ -1331,6 +1334,7 @@ void GscMainWindow::show_load_virtual_file_chooser() file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif + rconfig::set_data("gui/drive_data_open_save_dir", last_dir); this->add_virtual_drive(file); break; } diff --git a/gsmartcontrol/src/gsc_text_window.h b/gsmartcontrol/src/gsc_text_window.h index 00cfdd3..bb7a7e0 100644 --- a/gsmartcontrol/src/gsc_text_window.h +++ b/gsmartcontrol/src/gsc_text_window.h @@ -18,6 +18,7 @@ #include "hz/debug.h" #include "hz/fs_file.h" #include "hz/scoped_ptr.h" +#include "rconfig/rconfig_mini.h" #include "applib/app_gtkmm_features.h" #include "applib/app_ui_res_utils.h" @@ -141,6 +142,9 @@ class GscTextWindow : public AppUIResWidget, Insta void on_save_as_button_clicked() { static std::string last_dir; + if (last_dir.empty()) { + rconfig::get_data("gui/drive_data_open_save_dir", last_dir); + } int result = 0; #if GTK_CHECK_VERSION(3, 20, 0) @@ -189,6 +193,8 @@ class GscTextWindow : public AppUIResWidget, Insta file = dialog.get_filename(); // in fs encoding last_dir = dialog.get_current_folder(); // save for the future #endif + rconfig::set_data("gui/drive_data_open_save_dir", last_dir); + hz::File f(file); if (!f.put_contents(this->contents_)) { // this will send to debug_ too. gui_show_error_dialog("Cannot save data to file", f.get_error_utf8(), this);