mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-25 05:15:33 +00:00
Removed Help window.
Moved most of the documentation to website. The Help menu items now open the website pages.
This commit is contained in:
@@ -6,7 +6,7 @@ SUBDIRS = data debian.dist src
|
||||
|
||||
|
||||
# These files are actually needed for compilation.
|
||||
noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt README.txt
|
||||
noinst_DATA = AUTHORS.txt LICENSE_gsmartcontrol.txt
|
||||
|
||||
|
||||
# For the files to be bundled with the distribution, specify them in
|
||||
|
||||
+8
-322
@@ -29,174 +29,35 @@ https://gsmartcontrol.sourceforge.io/
|
||||
|
||||
|
||||
|
||||
Features
|
||||
|
||||
* automatically reports and highlights any anomalies;
|
||||
|
||||
* allows enabling/disabling SMART;
|
||||
|
||||
* allows enabling/disabling Automatic Offline Data Collection - a short
|
||||
self-check that the drive will perform automatically every four hours with no
|
||||
impact on performance;
|
||||
|
||||
* supports configuration of global and per-drive options for smartctl;
|
||||
|
||||
* performs SMART self-tests;
|
||||
|
||||
* displays drive identity information, capabilities, attributes,
|
||||
self-test/error logs, device statistics, etc...;
|
||||
|
||||
* can read in smartctl output from a saved file, interpreting it as a
|
||||
read-only virtual device;
|
||||
|
||||
* works on most smartctl-supported operating systems;
|
||||
|
||||
* has extensive help information.
|
||||
|
||||
|
||||
|
||||
What Is SMART?
|
||||
|
||||
Short answer: SMART is a technology which provides hard disk drives with
|
||||
methods to predict certain kinds of failures with certain chance of success.
|
||||
|
||||
Long answer: read below.
|
||||
|
||||
Self-Monitoring, Analysis, and Reporting Technology, or SMART, is a
|
||||
monitoring system for hard drives and SSDs to detect and report various
|
||||
indicators of reliability, in the hope of anticipating failures. SMART is
|
||||
implemented inside the drives, providing several ways of monitoring the drive
|
||||
health. It may present information about general health, various drive
|
||||
attributes (for example, number of unreadable sectors), error logs, and so on.
|
||||
It may also provide ways to instruct the drive to run various self-tests,
|
||||
which may report valuable information. It may even automatically scan the disk
|
||||
surface in when the drive is idle, repairing the defects while reallocating
|
||||
the data to more safe areas.
|
||||
|
||||
While having SMART sounds really good, there are some nuances to consider. One
|
||||
of the common pitfalls is that it may create a false sense of security. That
|
||||
is, a perfectly good SMART data is NOT an indication that the drive won't fail
|
||||
the next minute. The reverse is also true - some drives may function perfectly
|
||||
even with not-so-good-looking SMART data. However, as studies indicate, given
|
||||
a large population of drives, some SMART attributes may reliably predict
|
||||
drive failures within up to two months.
|
||||
|
||||
Another common mistake is to assume that the attribute values are the real
|
||||
physical values, as experienced by the drive. As manufacturers do not
|
||||
necessarily agree on precise attribute definitions and measurement units, the
|
||||
exact meaning of the attributes may vary greatly across different drive
|
||||
models.
|
||||
|
||||
At present SMART is implemented individually by manufacturers. While some
|
||||
aspects are standardized for compatibility, others are not. In fact, most
|
||||
manufacturers refer the users to their own health monitoring utilities and
|
||||
advice against taking SMART data seriously. Nevertheless, SMART may prove an
|
||||
effective measure against data loss.
|
||||
|
||||
Yet another issue is that quite often the drives have bugs which prevent
|
||||
correct SMART usage. This is usually due to buggy firmware, or the
|
||||
manufacturer ignoring the standards. Luckily, smartmontools usually detects
|
||||
these bugs and works around them.
|
||||
|
||||
|
||||
|
||||
Software Requirements
|
||||
|
||||
Note: If using the official Windows package, no additional software is required.
|
||||
|
||||
You need to have the following software installed:
|
||||
|
||||
* pcre - http://www.pcre.org .
|
||||
|
||||
* smartmontools - see https://www.smartmontools.org/ .
|
||||
|
||||
Build requirements:
|
||||
* pcre 1 - http://www.pcre.org .
|
||||
* GTK+, version 3.4 or higher - see http://www.gtk.org .
|
||||
|
||||
* Gtkmm, version 3.4 or higher - see http://www.gtkmm.org .
|
||||
|
||||
Most of these packages are probably already provided by your distribution.
|
||||
Here are the packages you need to have to build GSmartControl on some Linux
|
||||
distributions:
|
||||
|
||||
openSUSE, SLES, SLED:
|
||||
gcc-c++, pcre-devel, gtkmm3-devel.
|
||||
|
||||
Fedora, CentOS, RHEL:
|
||||
gcc-c++, pcre-devel, gtkmm30-devel.
|
||||
|
||||
Ubuntu, Debian GNU/Linux:
|
||||
g++, libpcre3-dev, libgtkmm-3.0-dev.
|
||||
|
||||
Note that usually you need to specify only these packages - the rest is
|
||||
installed automatically by the package manager's dependency resolver. Keep in
|
||||
mind that you also need smartmontools to run the program.
|
||||
|
||||
Runtime requirements:
|
||||
* smartmontools - see https://www.smartmontools.org/ .
|
||||
* xterm
|
||||
|
||||
The following operating systems are supported:
|
||||
|
||||
* Linux - All the popular configurations should work.
|
||||
|
||||
* FreeBSD - Tested with DesktopBSD / x86.
|
||||
|
||||
* NetBSD - Tested with NetBSD / x86.
|
||||
|
||||
* OpenBSD - Tested with OpenBSD / x86-64.
|
||||
|
||||
* DragonFlyBSD - Code written but no testing has been performed yet. Expected
|
||||
to work without any issues.
|
||||
|
||||
to work without any issues.
|
||||
* Windows Vista SP2 (32-bit and 64-bit), Windows 7 SP1, Windows Server 2008,
|
||||
Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc...
|
||||
for physical drives 0, 1, etc... .
|
||||
|
||||
Windows 8.1, Windows 10. The Windows port uses pd0, pd1, etc...
|
||||
for physical drives 0, 1, etc... .
|
||||
* Mac OS X.
|
||||
|
||||
* Solaris.
|
||||
|
||||
* QNX - Code written but no testing has been performed yet.
|
||||
|
||||
|
||||
|
||||
Installation
|
||||
|
||||
Short answer: build and install via: ./configure; make; make install
|
||||
|
||||
Run gsmartcontrol-root to invoke gsmartcontrol with your desktop's su
|
||||
mechanism, or use the desktop menu entry.
|
||||
|
||||
|
||||
Long answer: read below.
|
||||
|
||||
First, check if you can find a pre-built package for your distribution or
|
||||
operating system - they usually provide the best integration and the easiest
|
||||
installation procedure. For Linux, one option is to try the openSUSE Build
|
||||
Service - it provides ready-to-install packages for various popular Linux
|
||||
distributions (openSUSE, Fedora, etc...). See
|
||||
http://download.opensuse.org/repositories/home:/alex_sh/ .
|
||||
|
||||
If you want to compile from source, check that you have all the required
|
||||
dependencies (see Software Requirements section). Then the usual
|
||||
|
||||
./configure; make; make install
|
||||
|
||||
will build and install it. Installation usually requires administrative
|
||||
privileges, but you don't need to install the program in order to run it
|
||||
directly from the build directory.
|
||||
|
||||
|
||||
|
||||
Smartctl Options
|
||||
|
||||
GSmartControl tries its best to guard the user from having to specify smartctl
|
||||
options. However, this is not always possible due to drive firmware bugs,
|
||||
unimplemented features, and so on. The smartctl manual page contains all the
|
||||
information you may need when dealing with smartctl.
|
||||
See https://www.smartmontools.org/browser/trunk/smartmontools/smartctl.8.in
|
||||
|
||||
Additional information is available at https://www.smartmontools.org/
|
||||
|
||||
|
||||
|
||||
Command Line Options
|
||||
|
||||
GSmartControl inherits options from GTK+ and other libraries, so be sure to
|
||||
@@ -235,181 +96,6 @@ Example: --add-device /dev/sda --add-device /dev/twa0::3ware,2 --add-device
|
||||
|
||||
|
||||
|
||||
Permission Problems
|
||||
|
||||
Short answer: you need to be root (that's Administrator in Windows).
|
||||
In X11, use kdesu, gnomesu, sux, xdg-su or similar.
|
||||
|
||||
Long answer: read below.
|
||||
|
||||
Most operating systems prohibit direct access to hardware to users with
|
||||
non-administrative privileges. Unfortunately, to access SMART data, smartctl
|
||||
needs to directly access the hard drive.
|
||||
|
||||
The provided X11 desktop and menu icons should show the "Please enter the root
|
||||
password" dialog boxes, and, after correct information is entered, should run
|
||||
this program with root privileges. The dialogs should be available in most
|
||||
commonly used X11 desktop environments.
|
||||
|
||||
Another way is to use the included gsmartcontrol-root script, which finds the
|
||||
available su program and runs gsmartcontrol with it.
|
||||
|
||||
Yet another way is to manually invoke the program with kdesu, gnomesu, sux or
|
||||
similar programs. For example,
|
||||
|
||||
kdesu -u root -c gsmartcontrol
|
||||
|
||||
will ask for root password and run gsmartcontrol with root privileges. Replace
|
||||
"kdesu" with "gnomesu" if using Gnome. The "sux" or "xdg-su" commands may also
|
||||
help, if neither KDE or GNOME are available.
|
||||
|
||||
Please don't set the "setuid" flag on smartctl binary. It is considered a
|
||||
security risk.
|
||||
|
||||
|
||||
|
||||
Enable SMART Permanently
|
||||
|
||||
Specifications say that once you set a SMART-related property, it will be
|
||||
preserved across reboots. So, when you, say, enable SMART and Automatic
|
||||
Offline Data Collection, both will stay enabled until you disable them.
|
||||
|
||||
However, BIOS, your operating system, your other operating systems (if
|
||||
present), and various startup programs may affect that. For example, BIOS may
|
||||
enable SMART each time you start your computer, so if you disabled SMART
|
||||
previously, it will be re-enabled on reboot.
|
||||
|
||||
The easiest way to work around this is to set the desired settings on system
|
||||
startup. You may use smartctl or smartd to do that. For example, to enable
|
||||
both SMART and Automatic Offline Data Collection on /dev/sda, one would write
|
||||
the following to the system startup script (e.g. boot.local, rc.local or
|
||||
similar on Linux):
|
||||
|
||||
smartctl -s on -o on /dev/sda
|
||||
|
||||
For more information, see smartctl and smartd documentation.
|
||||
|
||||
|
||||
|
||||
Known Issues
|
||||
|
||||
Only ATA drives (both PATA and SATA), various USB to ATA bridges and drives
|
||||
behind some RAID controllers are supported for now. The main reasons are:
|
||||
|
||||
* We can't support drives which don't work with smartmontools. This affects
|
||||
drives which don't support SMART or don't export SMART data correctly (e.g.
|
||||
some USB enclosures, RAIDs, etc...).
|
||||
|
||||
* Smartctl's output for SCSI drives is completely different compared to ATA.
|
||||
Also, SCSI drives are rarely found in desktop systems and the servers rarely
|
||||
have X11 / Gtkmm running, so this is a low priority task.
|
||||
|
||||
* I only have ATA drives, so testing anything else is very difficult.
|
||||
|
||||
Immediate Offline Tests are not supported. I haven't found a way to reliably
|
||||
monitor them yet. Besides, they run automatically anyway if Automatic Offline
|
||||
Data Collection is enabled.
|
||||
|
||||
Testing is only supported on drives which correctly report their progress
|
||||
information in capabilities.
|
||||
|
||||
Not all drives support disabling Automatic Offline Data Collection, even if
|
||||
they report otherwise. Unfortunately, there's no way to detect such drives.
|
||||
|
||||
The texts probably contain a lot of grammatical errors, English being my third
|
||||
language and all.
|
||||
|
||||
|
||||
|
||||
Reporting Bugs
|
||||
|
||||
If it is a SMART or drive-related problem, please try to test it with smartctl
|
||||
first. Chances are, the problem you're experiencing is not tied to
|
||||
GSmartControl, but is a drive firmware or smartctl problem. For example, to
|
||||
see a complete information about your /dev/sda drive, type the following in a
|
||||
terminal emulator (as root, using sudo or su):
|
||||
|
||||
smartctl -x /dev/sda
|
||||
|
||||
Note: If using Windows, the device name should be /dev/pd1 for the second
|
||||
physical drive, etc... . Run cmd as administrator first.
|
||||
|
||||
If you still think it's a GSmartControl issue, please collect the following
|
||||
information about your system. Without it, it may be very hard or impossible
|
||||
to fix the bug.
|
||||
|
||||
* Which operating system you use (for example, openSUSE Leap 42.3).
|
||||
|
||||
* Which version of GTK and Gtkmm you have installed. Finding this out is very
|
||||
distribution-specific. For example, on openSUSE it would be
|
||||
"rpm -q gtk3 gtkmm3". Some distributions have gtkmm30 instead. You may also
|
||||
search them in your distribution's graphical package manager, if there is one.
|
||||
|
||||
* Execution log from the program, if possible. To obtain it, run the program
|
||||
with -v option, e.g. (type the following in a terminal emulator or Run
|
||||
dialog):
|
||||
|
||||
gsmartcontrol-root -v
|
||||
|
||||
Note: On Windows, -v switch is on by default.
|
||||
|
||||
Perform the steps needed to reproduce the bug, then go to
|
||||
"Options -> View Execution Log", and click "Save All".
|
||||
|
||||
* Detailed description of steps you performed when the bug occurred.
|
||||
|
||||
Once you have this information, send an email to me, Alexander Shaduri
|
||||
<ashaduri 'at' gmail.com>. Note that I may refer you to smartmontools support
|
||||
if it's a bug in smartmontools and not GSmartControl. Normally, I won't
|
||||
redirect your support request to them myself, because they may ask questions
|
||||
which only you have the answers to.
|
||||
|
||||
Please read the "License and Copyright" and "Patch Licensing" sections before
|
||||
sending any patches.
|
||||
|
||||
|
||||
|
||||
License and Copyright
|
||||
|
||||
For license information, see LICENSE_gsmartcontrol.txt file.
|
||||
|
||||
You may notice that GSmartControl is not licensed under "GNU GPL version X or
|
||||
later", but under "GNU GPL version X and Y". I firmly believe that it's unwise
|
||||
to license a piece of code under non-existent licenses, whatever anyone else
|
||||
might say. The reason for this is that one simply CANNOT know that, say, in 20
|
||||
years FSF won't be bought by some corporation who will release GPL version Z
|
||||
which will completely reverse the reasons GPL was created for.
|
||||
|
||||
The removal of "or later" clause somewhat imposes responsibility on the
|
||||
copyright holders to review every future version of the license once it's
|
||||
released, and, if deemed acceptable, re-license the code under the new license
|
||||
(possibly retaining the old licenses). Unfortunately, this is a necessary
|
||||
inconvenience we will have to deal with.
|
||||
|
||||
|
||||
|
||||
Patch Licensing
|
||||
|
||||
Due to reasons described in "License and Copyright" section, to make it
|
||||
possible to re-license the code without tracking down all the people who ever
|
||||
wrote a patch, the copyrights of all minor patches must be assigned to the
|
||||
central copyright holder of the project. If the patch is major enough (that
|
||||
is, it forms a significant part of the program source code), the author may
|
||||
retain the copyright, if he or she chooses to do so. However, unless the
|
||||
author plans to maintain his part of the source code, he / she is humbly asked
|
||||
to consider assigning away his / her copyright. A simple "I disclaim all
|
||||
copyright to this patch" by the author is sufficient. All credits will be
|
||||
mentioned in product documentation, whatever the size of the patch is.
|
||||
|
||||
Please note that centralization of copyright is needed to maintain a
|
||||
reasonably healthy legal status of the project. Also note that this method is
|
||||
not unique - FSF and many other organizations require exactly the same thing.
|
||||
|
||||
Some contributors may have reasonable doubts about the future status of this
|
||||
project. Let me assure you that this project will never have more restrictive
|
||||
license than GPLv2. If, some time in the future, the GPL is somehow
|
||||
invalidated in court, the project will be re-licensed under similar (in
|
||||
spirit) license, if possible, or a license less restrictive than GPL (for
|
||||
example, the three-clause BSD license).
|
||||
|
||||
|
||||
|
||||
+1
-7
@@ -20,8 +20,6 @@ local scope destruction also works if using stack variables.
|
||||
|
||||
TODO:
|
||||
|
||||
Move README.txt contents to the site, make Help menu open it.
|
||||
|
||||
|
||||
Don't rely on smartctl return code (2), parse the output instead.
|
||||
This will allow us to show the Info page.
|
||||
@@ -36,7 +34,7 @@ Testing:
|
||||
If smartctl outputs gibberish while testing, the GUI hangs.
|
||||
|
||||
|
||||
Support RAID for these controllers:
|
||||
Support RAID for these controllers (supported by smartctl 6.5):
|
||||
https://www.smartmontools.org/wiki/Supported_RAID-Controllers
|
||||
|
||||
Adaptec (Windows, Linux using "-d aacraid")
|
||||
@@ -47,7 +45,3 @@ Support RAID for these controllers:
|
||||
HP CCISS (FreeBSD)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -309,12 +309,11 @@ AC_SUBST(WINDOWS_ARCH)
|
||||
RES_FILES="src/res/gsc_about_dialog.ui \
|
||||
src/res/gsc_add_device_window.ui \
|
||||
src/res/gsc_executor_log_window.ui \
|
||||
src/res/gsc_help_window.ui \
|
||||
src/res/gsc_info_window.ui \
|
||||
src/res/gsc_main_window.ui \
|
||||
src/res/gsc_preferences_window.ui \
|
||||
src/res/gsc_text_window.ui \
|
||||
AUTHORS.txt LICENSE_gsmartcontrol.txt README.txt"
|
||||
AUTHORS.txt LICENSE_gsmartcontrol.txt"
|
||||
|
||||
# Unfortunately, _SOURCES can't contain substitutions, so we use LIBADD.
|
||||
RES_LIBADD="" # .o file to put into .a, libres_a_LIBADD, libres_a_DEPENDENCIES.
|
||||
|
||||
@@ -31,9 +31,9 @@ gsmartcontrol_LDADD = $(top_builddir)/src/applib/libapplib.a \
|
||||
$(top_builddir)/src/res/libres.a $(WINRES_OBJ)
|
||||
|
||||
noinst_HEADERS = global_macros.h gsc_about_dialog.h gsc_add_device_window.h gsc_executor_error_dialog.h \
|
||||
gsc_executor_log_window.h gsc_help_window.h gsc_info_window.h gsc_init.h gsc_main_window.h \
|
||||
gsc_executor_log_window.h gsc_info_window.h gsc_init.h gsc_main_window.h \
|
||||
gsc_main_window_iconview.h gsc_preferences_window.h gsc_settings.h gsc_text_window.h
|
||||
gsmartcontrol_SOURCES = $(WIN_MAIN_SRC) gsc_about_dialog.cpp gsc_add_device_window.cpp \
|
||||
gsc_executor_error_dialog.cpp gsc_executor_log_window.cpp gsc_help_window.cpp gsc_info_window.cpp \
|
||||
gsc_executor_error_dialog.cpp gsc_executor_log_window.cpp gsc_info_window.cpp \
|
||||
gsc_init.cpp gsc_main.cpp gsc_main_window.cpp gsc_preferences_window.cpp
|
||||
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
/**************************************************************************
|
||||
Copyright:
|
||||
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
|
||||
License: See LICENSE_gsmartcontrol.txt
|
||||
***************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup gsc
|
||||
/// \weakgroup gsc
|
||||
/// @{
|
||||
|
||||
#include <vector>
|
||||
#include <gtkmm.h>
|
||||
#include <gdk/gdk.h> // GDK_KEY_Escape
|
||||
#include "applib/app_gtkmm_features.h"
|
||||
|
||||
#include "hz/string_algo.h"
|
||||
#include "applib/app_gtkmm_utils.h" // app_gtkmm_create_tree_view_column
|
||||
#include "applib/app_pcrecpp.h"
|
||||
|
||||
#include "gsc_help_window.h"
|
||||
|
||||
|
||||
|
||||
HZ_RES_DATA_INIT_NAMED(README_txt, "README.txt", ReadmeTextResData);
|
||||
|
||||
|
||||
|
||||
GscHelpWindow::GscHelpWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui)
|
||||
: AppUIResWidget<GscHelpWindow, false>(gtkcobj, ref_ui), selection_callback_enabled(true)
|
||||
{
|
||||
// Connect callbacks
|
||||
|
||||
APP_GTKMM_CONNECT_VIRTUAL(delete_event); // make sure the event handler is called
|
||||
|
||||
Gtk::Button* window_close_button = 0;
|
||||
APP_UI_RES_AUTO_CONNECT(window_close_button, clicked);
|
||||
|
||||
|
||||
// Accelerators
|
||||
|
||||
Glib::RefPtr<Gtk::AccelGroup> accel_group = this->get_accel_group();
|
||||
if (window_close_button) {
|
||||
window_close_button->add_accelerator("clicked", accel_group, GDK_KEY_Escape,
|
||||
Gdk::ModifierType(0), Gtk::AccelFlags(0));
|
||||
}
|
||||
|
||||
|
||||
// --------------- Make a treeview
|
||||
|
||||
Gtk::TreeView* treeview = this->lookup_widget<Gtk::TreeView*>("topics_treeview");
|
||||
if (treeview) {
|
||||
Gtk::TreeModelColumnRecord model_columns;
|
||||
// Topic
|
||||
model_columns.add(col_topic);
|
||||
app_gtkmm_create_tree_view_column(col_topic, *treeview, "Topic", "Topic");
|
||||
|
||||
// create a TreeModel (ListStore)
|
||||
list_store = Gtk::ListStore::create(model_columns);
|
||||
treeview->set_model(list_store);
|
||||
|
||||
selection = treeview->get_selection();
|
||||
selection->signal_changed().connect(sigc::mem_fun(*this,
|
||||
&self_type::on_tree_selection_changed) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// --------------- Parse help text
|
||||
|
||||
/*
|
||||
README.txt File Format
|
||||
|
||||
The whole text is converted to unix newline format before parsing.
|
||||
Sections are separated by 3 newlines (two empty lines).
|
||||
The first line of the section is its header.
|
||||
When splitting the file to sections and headers, any leading or trailing
|
||||
whitespace is removed.
|
||||
If there is a single newline inside a section, it is converted to
|
||||
space to enable correct wrapping.
|
||||
If there are two consequent newlines, they are left as they are,
|
||||
essentially making a paragraph break.
|
||||
*/
|
||||
|
||||
std::string readme = hz::string_any_to_unix_copy(ReadmeTextResData().get_string());
|
||||
|
||||
|
||||
// Paragraphs are delimited by 3 empty lines
|
||||
std::vector<std::string> topics;
|
||||
hz::string_split(readme, "\n\n\n\n", topics, true); // skip empty
|
||||
|
||||
|
||||
// Add to treeview and textview
|
||||
|
||||
Gtk::TextView* content = this->lookup_widget<Gtk::TextView*>("content_textview");
|
||||
|
||||
if (treeview && content) {
|
||||
Glib::RefPtr<Gtk::TextBuffer> buffer = content->get_buffer();
|
||||
|
||||
buffer->create_mark("Begin", buffer->begin(), true);
|
||||
|
||||
for (unsigned int i = 0; i < topics.size(); ++i) {
|
||||
std::string topic = hz::string_trim_copy(topics[i]);
|
||||
|
||||
// The first line of topic is its title
|
||||
std::vector<std::string> topic_split;
|
||||
hz::string_split(topic, "\n\n", topic_split, true, 2); // skip empty, get 2 elements only
|
||||
|
||||
if (topic_split.size() < 2) {
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract topic title in topic " << i << "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string topic_title = hz::string_trim_copy(topic_split[0]);
|
||||
std::string topic_body = hz::string_trim_copy(topic_split[1]);
|
||||
|
||||
buffer->create_mark(topic_title, buffer->end(), true); // set topic mark to the end of what's there
|
||||
|
||||
// add the title and make it bold
|
||||
buffer->insert(buffer->end(), "\n" + topic_title);
|
||||
|
||||
Gtk::TextIter first = buffer->end(), last = first;
|
||||
first.backward_lines(1);
|
||||
|
||||
Glib::RefPtr<Gtk::TextTag> tag = buffer->create_tag();
|
||||
tag->property_weight() = Pango::WEIGHT_BOLD;
|
||||
tag->property_size_points() = 14;
|
||||
|
||||
buffer->apply_tag(tag, first, last);
|
||||
|
||||
// add the rest
|
||||
|
||||
// single newlines to spaces, to allow proper wrapping.
|
||||
app_pcre_replace("/([^\\n])\\n([^\\n])/", "\\1 \\2", topic_body);
|
||||
buffer->insert(buffer->end(), "\n\n" + topic_body + "\n\n");
|
||||
|
||||
|
||||
// Add to treeview
|
||||
|
||||
Gtk::TreeRow row = *(list_store->append());
|
||||
row[col_topic] = topic_title;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---------------
|
||||
|
||||
|
||||
// show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscHelpWindow::set_topic(const Glib::ustring& topic)
|
||||
{
|
||||
this->selection_callback_enabled = false; // temporarily disable it
|
||||
|
||||
// scroll to it
|
||||
|
||||
Gtk::TextView* content = this->lookup_widget<Gtk::TextView*>("content_textview");
|
||||
if (content) {
|
||||
Glib::RefPtr<Gtk::TextBuffer> buffer = content->get_buffer();
|
||||
|
||||
Glib::RefPtr<Gtk::TextMark> mark = buffer->get_mark(topic);
|
||||
if (mark)
|
||||
content->scroll_to(mark, 0., 0., 0.);
|
||||
}
|
||||
|
||||
// select it in tree view
|
||||
Gtk::TreeView* treeview = this->lookup_widget<Gtk::TreeView*>("topics_treeview");
|
||||
|
||||
if (treeview && !list_store->children().empty()) {
|
||||
for (Gtk::TreeIter iter = list_store->children().begin(); iter != list_store->children().end(); ++iter) {
|
||||
if (iter->get_value(col_topic) == topic) {
|
||||
selection->select(*iter);
|
||||
// about 30% from top
|
||||
treeview->scroll_to_cell(list_store->get_path(iter), *(treeview->get_column(0)), 0.3f, 0.f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->selection_callback_enabled = true; // enable it back
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GscHelpWindow::on_delete_event_before(GdkEventAny* e)
|
||||
{
|
||||
destroy(this);
|
||||
return true; // event handled, don't call default virtual handler
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscHelpWindow::on_window_close_button_clicked()
|
||||
{
|
||||
destroy(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscHelpWindow::on_tree_selection_changed()
|
||||
{
|
||||
if (!this->selection_callback_enabled)
|
||||
return;
|
||||
|
||||
if (selection->count_selected_rows()) {
|
||||
Gtk::TreeIter iter = selection->get_selected();
|
||||
Gtk::TreeRow row = *iter;
|
||||
|
||||
set_topic(row[col_topic]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// @}
|
||||
@@ -1,79 +0,0 @@
|
||||
/**************************************************************************
|
||||
Copyright:
|
||||
(C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
|
||||
License: See LICENSE_gsmartcontrol.txt
|
||||
***************************************************************************/
|
||||
/// \file
|
||||
/// \author Alexander Shaduri
|
||||
/// \ingroup gsc
|
||||
/// \weakgroup gsc
|
||||
/// @{
|
||||
|
||||
#ifndef GSC_HELP_WINDOW_H
|
||||
#define GSC_HELP_WINDOW_H
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
#include "applib/app_ui_res_utils.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/// The Help window.
|
||||
/// Use create() / destroy() with this class instead of new / delete!
|
||||
class GscHelpWindow : public AppUIResWidget<GscHelpWindow, false> {
|
||||
public:
|
||||
|
||||
// name of glade/ui file without a .glade/.ui extension and quotes
|
||||
APP_UI_RES_DATA_INIT(gsc_help_window);
|
||||
|
||||
|
||||
/// Constructor, gtkbuilder/glade needs this.
|
||||
GscHelpWindow(BaseObjectType* gtkcobj, const app_ui_res_ref_t& ref_ui);
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~GscHelpWindow()
|
||||
{ }
|
||||
|
||||
|
||||
/// Set the current help topic
|
||||
void set_topic(const Glib::ustring& topic);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// ---------- overriden virtual methods
|
||||
|
||||
/// Destroy this object on delete event (by default it calls hide()).
|
||||
/// Reimplemented from Gtk::Window.
|
||||
bool on_delete_event_before(GdkEventAny* e);
|
||||
|
||||
|
||||
// ---------- other callbacks
|
||||
|
||||
/// Button click callback
|
||||
void on_window_close_button_clicked();
|
||||
|
||||
/// Callback
|
||||
void on_tree_selection_changed();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
Glib::RefPtr<Gtk::ListStore> list_store; ///< List store
|
||||
Glib::RefPtr<Gtk::TreeSelection> selection; ///< Tree selection
|
||||
|
||||
Gtk::TreeModelColumn<Glib::ustring> col_topic; /// Tree column
|
||||
|
||||
bool selection_callback_enabled; ///< Helper for set_topic(), temporarily disables the tree selection changed callback
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "hz/fs_file.h" // hz::File
|
||||
#include "hz/debug.h"
|
||||
#include "hz/scoped_ptr.h"
|
||||
#include "hz/launch_url.h"
|
||||
#include "rconfig/rconfig_mini.h"
|
||||
#include "applib/storage_detector.h"
|
||||
#include "applib/smartctl_parser.h"
|
||||
@@ -29,7 +30,6 @@
|
||||
#include "gsc_init.h" // app_quit()
|
||||
#include "gsc_about_dialog.h"
|
||||
#include "gsc_info_window.h"
|
||||
#include "gsc_help_window.h"
|
||||
#include "gsc_preferences_window.h"
|
||||
#include "gsc_executor_log_window.h"
|
||||
#include "gsc_executor_error_dialog.h" // gsc_executor_error_dialog_show
|
||||
@@ -267,10 +267,8 @@ bool GscMainWindow::create_widgets()
|
||||
" </menu>"
|
||||
|
||||
" <menu action='help_menu'>"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_general_help) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_permission_problems) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_how_to_enable_smart) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_report_bug) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_online_documentation) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_support) "' />"
|
||||
" <menuitem action='" APP_ACTION_NAME(action_about) "' />"
|
||||
" </menu>"
|
||||
|
||||
@@ -380,21 +378,13 @@ bool GscMainWindow::create_widgets()
|
||||
|
||||
actiongroup_main->add(Gtk::Action::create("help_menu", "_Help"));
|
||||
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_general_help), Gtk::Stock::HELP, "A_ll Help Topics");
|
||||
actiongroup_main->add((action_map[action_general_help] = action), Gtk::AccelKey("F1"),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_general_help));
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_online_documentation), Gtk::Stock::HELP);
|
||||
actiongroup_main->add((action_map[action_online_documentation] = action), Gtk::AccelKey("F1"),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_online_documentation));
|
||||
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_permission_problems), "Resolving _Permission Problems");
|
||||
actiongroup_main->add((action_map[action_permission_problems] = action),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_permission_problems));
|
||||
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_how_to_enable_smart), "How to _Enable SMART Permanently");
|
||||
actiongroup_main->add((action_map[action_how_to_enable_smart] = action),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_how_to_enable_smart));
|
||||
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_report_bug), "_Reporting Bugs");
|
||||
actiongroup_main->add((action_map[action_report_bug] = action),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_report_bug));
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_support), "Support");
|
||||
actiongroup_main->add((action_map[action_support] = action),
|
||||
sigc::bind(sigc::mem_fun(*this, &self_type::on_action_activated), action_support));
|
||||
|
||||
action = Gtk::Action::create(APP_ACTION_NAME(action_about), Gtk::Stock::ABOUT);
|
||||
actiongroup_main->add((action_map[action_about] = action),
|
||||
@@ -623,51 +613,15 @@ void GscMainWindow::on_action_activated(GscMainWindow::action_t action_type)
|
||||
break;
|
||||
}
|
||||
|
||||
case action_general_help:
|
||||
case action_online_documentation:
|
||||
{
|
||||
GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close
|
||||
// win->set_transient_for(*this); // for "destroy with parent", always-on-top
|
||||
win->show();
|
||||
while(g_main_context_pending(NULL)) {
|
||||
g_main_context_iteration(NULL, false);
|
||||
}
|
||||
win->set_topic("Begin"); // special mark, top of the text.
|
||||
hz::launch_url(gobj(), "https://gsmartcontrol.sourceforge.io/documentation.html");
|
||||
break;
|
||||
}
|
||||
|
||||
case action_permission_problems:
|
||||
case action_support:
|
||||
{
|
||||
GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close
|
||||
// win->set_transient_for(*this); // for "destroy with parent", always-on-top
|
||||
win->show();
|
||||
while(g_main_context_pending(NULL)) {
|
||||
g_main_context_iteration(NULL, false);
|
||||
}
|
||||
win->set_topic("Permission Problems");
|
||||
break;
|
||||
}
|
||||
|
||||
case action_how_to_enable_smart:
|
||||
{
|
||||
GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close
|
||||
// win->set_transient_for(*this); // for "destroy with parent", always-on-top
|
||||
win->show();
|
||||
while(g_main_context_pending(NULL)) {
|
||||
g_main_context_iteration(NULL, false);
|
||||
}
|
||||
win->set_topic("Enable SMART Permanently");
|
||||
break;
|
||||
}
|
||||
|
||||
case action_report_bug:
|
||||
{
|
||||
GscHelpWindow* win = GscHelpWindow::create(); // destroyed on close
|
||||
// win->set_transient_for(*this); // for "destroy with parent", always-on-top
|
||||
win->show();
|
||||
while(g_main_context_pending(NULL)) {
|
||||
g_main_context_iteration(NULL, false);
|
||||
}
|
||||
win->set_topic("Reporting Bugs");
|
||||
hz::launch_url(gobj(), "https://gsmartcontrol.sourceforge.io/support.html");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,10 +97,8 @@ class GscMainWindow : public AppUIResWidget<GscMainWindow, false> {
|
||||
action_update_drivedb,
|
||||
action_preferences,
|
||||
|
||||
action_general_help,
|
||||
action_permission_problems,
|
||||
action_how_to_enable_smart,
|
||||
action_report_bug,
|
||||
action_online_documentation,
|
||||
action_support,
|
||||
action_about
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ libres_a_SOURCES =
|
||||
EXTRA_libres_a_SOURCES = gsc_about_dialog.ui.cpp \
|
||||
gsc_add_device_window.ui.cpp \
|
||||
gsc_executor_log_window.ui.cpp \
|
||||
gsc_help_window.ui.cpp \
|
||||
gsc_info_window.ui.cpp \
|
||||
gsc_main_window.ui.cpp \
|
||||
gsc_preferences_window.ui.cpp \
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.20.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.4"/>
|
||||
<object class="GtkWindow" id="gsc_help_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Help - GSmartControl</property>
|
||||
<property name="default_width">900</property>
|
||||
<property name="default_height">650</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkPaned" id="hpaned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="position">200</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Topics</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="topics_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Topics</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="content_textview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<property name="is_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="wrap_mode">word</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="window_close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="has_default">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
Reference in New Issue
Block a user