Format large numbers according to locale (usually with commas).

Save Open/Save paths across windows and program restarts.
This commit is contained in:
Alexander Shaduri
2017-08-26 13:18:32 +00:00
parent 897a4efa96
commit 9a70345f4a
7 changed files with 79 additions and 9 deletions
-2
View File
@@ -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.
+39 -1
View File
@@ -11,11 +11,15 @@
#include <map>
#include <ostream> // not iosfwd - it doesn't work
#include <sstream>
#include <iomanip>
#include <locale>
#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<std::s
std::string StorageErrorBlock::format_lifetime_hours() const
{
std::stringstream ss;
ss.imbue(std::locale(""));
ss << std::fixed << lifetime_hours;
return ss.str();
}
std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b)
{
os << "Error number " << b.error_num << ": "
@@ -91,6 +119,16 @@ std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b)
std::string StorageSelftestEntry::format_lifetime_hours() const
{
std::stringstream ss;
ss.imbue(std::locale(""));
ss << std::fixed << lifetime_hours;
return ss.str();
}
std::ostream& operator<< (std::ostream& os, const StorageSelftestEntry& b)
{
os << "Test entry " << b.test_num << ": "
+12 -1
View File
@@ -124,6 +124,10 @@ class StorageAttribute {
{ }
/// Format raw value with commas (if it's a number)
std::string format_raw_value() const;
int32_t id; ///< Attribute ID (most vendors agree on this)
std::string flag; ///< Some have it in 0xXXXX format, others in "PO--C-" format (some WD drives?).
hz::OptionalValue<uint8_t> 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<std::string>& 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.
};
@@ -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())) {
+9 -5
View File
@@ -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<uint32_t> col_hours;
Gtk::TreeModelColumn<std::string> 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<uint32_t> col_hours;
Gtk::TreeModelColumn<std::string> 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();
+4
View File
@@ -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;
}
+6
View File
@@ -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<GscTextWindow<InstanceSwitch>, 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<GscTextWindow<InstanceSwitch>, 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);