WIP: functionality of package properties editor

Side effect: the log dialog now has an icon
indicating whether there are errors or warnings.

A new utility widget has been introduced to
attach log messages (warnings/errors) to
dialogs. This widget is a QToolButton that
is invisible initially. It provides warning
and errors channels and can be fed messages.
If errors or warnings are fed, the tool button
becomes visible. If (directly) embedded inside
a QFrame, the frame's background will turn
red to indicate the region of interest.

The button can be pushed to read the log.

The button is called lay::AlertLogButton
and is found in layLogViewerDialog.h.

TODO: move to layBasic.
This commit is contained in:
Matthias Koefferlein 2017-03-20 22:29:22 +01:00
parent faf8acb3d1
commit ec415d9251
11 changed files with 1174 additions and 520 deletions

View File

@ -1,117 +1,201 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogViewerDialog</class>
<widget class="QDialog" name="LogViewerDialog" >
<property name="geometry" >
<widget class="QDialog" name="LogViewerDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<height>579</height>
<width>516</width>
<height>287</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Log Viewer</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="spacing" >
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="3" >
<widget class="QPushButton" name="clear_pb" >
<property name="text" >
<string>Clear</string>
</property>
</widget>
</item>
<item row="0" column="4" >
<widget class="QPushButton" name="separator_pb" >
<property name="text" >
<string>Separator</string>
</property>
</widget>
</item>
<item row="0" column="5" >
<widget class="QPushButton" name="copy_pb" >
<property name="text" >
<string>Copy</string>
</property>
</widget>
</item>
<item row="0" column="2" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>101</width>
<height>22</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>Verbosity</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QComboBox" name="verbosity_cbx" >
<item row="0" column="1">
<widget class="QComboBox" name="verbosity_cbx">
<item>
<property name="text" >
<property name="text">
<string>Silent</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Information</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Details</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Verbose</string>
</property>
</item>
<item>
<property name="text" >
<property name="text">
<string>Noisy</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0" colspan="6" >
<widget class="QListView" name="log_view" >
<property name="resizeMode" >
<item row="0" column="4">
<widget class="QPushButton" name="separator_pb">
<property name="text">
<string>Separator</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QPushButton" name="copy_pb">
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="clear_pb">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="verbosity_label">
<property name="text">
<string>Verbosity</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="6">
<widget class="QListView" name="log_view">
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="uniformItemSizes" >
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="6" >
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" >
<item row="0" column="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Close</set>
<property name="sizeHint" stdset="0">
<size>
<width>101</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="6">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="attn_frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="layResources.qrc">:/warn.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>There are errors or warnings</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<tabstops>
<tabstop>verbosity_cbx</tabstop>
<tabstop>clear_pb</tabstop>
<tabstop>separator_pb</tabstop>
<tabstop>copy_pb</tabstop>
<tabstop>log_view</tabstop>
</tabstops>
<resources>
<include location="layResources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
@ -119,11 +203,11 @@
<receiver>LogViewerDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
@ -135,11 +219,11 @@
<receiver>LogViewerDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>

File diff suppressed because it is too large Load Diff

BIN
src/lay/images/warn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

View File

@ -27,6 +27,7 @@
#include <QMutexLocker>
#include <QTimer>
#include <QClipboard>
#include <QFrame>
#include <stdio.h>
@ -97,25 +98,30 @@ LogReceiver::begin ()
// -----------------------------------------------------------------
// LogFile implementation
LogFile::LogFile (size_t max_entries)
: m_error_receiver (this, 0, &LogFile::error),
m_warn_receiver (this, 0, &LogFile::warn),
m_log_receiver (this, 10, &LogFile::info),
m_info_receiver (this, 0, &LogFile::info),
LogFile::LogFile (size_t max_entries, bool register_global)
: m_error_receiver (this, 0, &LogFile::add_error),
m_warn_receiver (this, 0, &LogFile::add_warn),
m_log_receiver (this, 10, &LogFile::add_info),
m_info_receiver (this, 0, &LogFile::add_info),
m_max_entries (max_entries),
m_generation_id (0),
m_last_generation_id (0)
m_last_generation_id (0),
m_has_errors (false),
m_has_warnings (false),
m_last_attn (false)
{
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
m_timer.setSingleShot (true);
m_timer.setSingleShot (false);
m_timer.setInterval (100);
m_timer.start ();
tl::info.add (&m_info_receiver, false);
tl::log.add (&m_log_receiver, false);
tl::error.add (&m_error_receiver, false);
tl::warn.add (&m_warn_receiver, false);
if (register_global) {
tl::info.add (&m_info_receiver, false);
tl::log.add (&m_log_receiver, false);
tl::error.add (&m_error_receiver, false);
tl::warn.add (&m_warn_receiver, false);
}
}
void
@ -123,8 +129,23 @@ LogFile::clear ()
{
QMutexLocker locker (&m_lock);
m_messages.clear ();
++m_generation_id;
if (!m_messages.empty ()) {
m_messages.clear ();
m_has_errors = m_has_warnings = false;
++m_generation_id;
}
}
bool
LogFile::has_errors () const
{
return m_has_errors;
}
bool
LogFile::has_warnings () const
{
return m_has_warnings;
}
void
@ -159,9 +180,13 @@ void
LogFile::timeout ()
{
bool changed = false;
bool attn = false, last_attn = false;
m_lock.lock ();
if (m_generation_id != m_last_generation_id) {
attn = m_has_errors || m_has_warnings;
last_attn = m_last_attn;
m_last_attn = attn;
m_last_generation_id = m_generation_id;
changed = true;
}
@ -169,9 +194,10 @@ LogFile::timeout ()
if (changed) {
emit layoutChanged ();
if (last_attn != attn) {
emit attention_changed (attn);
}
}
m_timer.start ();
}
void
@ -183,6 +209,12 @@ LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continu
m_messages.pop_front ();
}
if (mode == LogFileEntry::Warning || mode == LogFileEntry::WarningContinued) {
m_has_warnings = true;
} else if (mode == LogFileEntry::Error || mode == LogFileEntry::ErrorContinued) {
m_has_errors = true;
}
m_messages.push_back (LogFileEntry (mode, msg, continued));
++m_generation_id;
@ -251,21 +283,36 @@ LogFile::data(const QModelIndex &index, int role) const
// -----------------------------------------------------------------
// LogViewerDialog implementation
LogViewerDialog::LogViewerDialog (QWidget *parent)
LogViewerDialog::LogViewerDialog (QWidget *parent, bool register_global, bool interactive)
: QDialog (parent),
m_file (50000) // TODO: make this variable ..
m_file (50000, register_global) // TODO: make this variable ..
{
setupUi (this);
// For non-global log views, the verbosity selector does not make sense
if (!register_global) {
verbosity_cbx->hide ();
verbosity_label->hide ();
} else {
verbosity_cbx->setCurrentIndex (std::min (4, tl::verbosity () / 10));
connect (verbosity_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (verbosity_changed (int)));
}
if (!interactive) {
clear_pb->hide ();
separator_pb->hide ();
copy_pb->hide ();
} else {
connect (clear_pb, SIGNAL (clicked ()), &m_file, SLOT (clear ()));
connect (separator_pb, SIGNAL (clicked ()), &m_file, SLOT (separator ()));
connect (copy_pb, SIGNAL (clicked ()), &m_file, SLOT (copy ()));
}
attn_frame->hide ();
log_view->setModel (&m_file);
verbosity_cbx->setCurrentIndex (std::min (4, tl::verbosity () / 10));
connect (&m_file, SIGNAL (layoutChanged ()), log_view, SLOT (scrollToBottom ()));
connect (verbosity_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (verbosity_changed (int)));
connect (clear_pb, SIGNAL (clicked ()), &m_file, SLOT (clear ()));
connect (separator_pb, SIGNAL (clicked ()), &m_file, SLOT (separator ()));
connect (copy_pb, SIGNAL (clicked ()), &m_file, SLOT (copy ()));
connect (&m_file, SIGNAL (attention_changed (bool)), attn_frame, SLOT (setVisible (bool)));
}
void
@ -274,5 +321,56 @@ LogViewerDialog::verbosity_changed (int index)
tl::verbosity (index * 10 + 1);
}
// -----------------------------------------------------------------
// AlertLog implementation
AlertLogButton::AlertLogButton (QWidget *parent)
: QToolButton (parent)
{
mp_logger = new LogViewerDialog (this, false, false);
hide ();
connect (&mp_logger->file (), SIGNAL (attention_changed (bool)), this, SLOT (attention_changed (bool)));
connect (this, SIGNAL (clicked ()), mp_logger, SLOT (exec ()));
}
void
AlertLogButton::attention_changed (bool attn)
{
setVisible (attn);
// as a special service, enlarge and color any surrounding frame red -
// this feature allows putting the alert button together with other entry fields into a frame and
// make this frame highlighted on error or warning.
QFrame *frame = dynamic_cast<QFrame *> (parent ());
if (frame) {
if (frame->layout ()) {
int l = 0, t = 0, r = 0, b = 0;
frame->layout ()->getContentsMargins (&l, &t, &r, &b);
if (attn) {
l += 3; t += 3; r += 2; b += 2;
} else {
l -= 3; t -= 3; r -= 2; b -= 2;
}
frame->layout ()->setContentsMargins (l, t, r, b);
}
if (attn) {
frame->setAutoFillBackground (true);
QPalette palette = frame->palette ();
palette.setColor (QPalette::Window, QColor (255, 160, 160));
frame->setPalette (palette);
} else {
frame->setAutoFillBackground (false);
frame->setPalette (QPalette ());
}
}
}
}

View File

@ -26,11 +26,13 @@
#include "ui_LogViewerDialog.h"
#include "tlLog.h"
#include "layCommon.h"
#include <QTimer>
#include <QMutex>
#include <QDialog>
#include <QAbstractListModel>
#include <QToolButton>
#include <deque>
#include <string>
@ -40,6 +42,9 @@ namespace lay
class LogFile;
/**
* @brief A helper class describing one log entry
*/
class LogFileEntry
{
public:
@ -70,7 +75,10 @@ private:
bool m_continued;
};
class LogReceiver
/**
* @brief The log receiver abstraction that connects a channel with the LogFile object
*/
class LAY_PUBLIC LogReceiver
: public tl::Channel
{
public:
@ -91,40 +99,110 @@ private:
QMutex m_lock;
};
class LogFile
/**
* @brief A log collection ("log file")
*
* The log collector collects warnings, errors and info messages
* and presents this collection as a QAbstractListModel view
* viewing inside a QTreeWidget or the LogViewerDialog.
*
* The log collector can either be used standalone or as a
* global receiver that will collect the global log
* messages.
*/
class LAY_PUBLIC LogFile
: public QAbstractListModel
{
Q_OBJECT
public:
LogFile (size_t max_entries);
void error (const std::string &msg, bool continued)
{
add (continued ? LogFileEntry::ErrorContinued : LogFileEntry::Error, msg, continued);
}
void info (const std::string &msg, bool continued)
{
add (continued ? LogFileEntry::InfoContinued : LogFileEntry::Info, msg, continued);
}
void warn (const std::string &msg, bool continued)
{
add (continued ? LogFileEntry::WarningContinued : LogFileEntry::Warning, msg, continued);
}
/**
* @brief Constructs a log file receiver
* If "register_global" is true, the receiver will register itself as a global log receiver.
* Otherwise it's a private one that can be used with the "error", "warn" and "info" channels
* provided by the respective methods.
*/
LogFile (size_t max_entries, bool register_global = true);
/**
* @brief Implementation of the QAbstractItemModel interface
*/
int rowCount(const QModelIndex &parent) const;
/**
* @brief Implementation of the QAbstractItemModel interface
*/
QVariant data(const QModelIndex &index, int role) const;
private slots:
void timeout ();
/**
* @brief Gets a value indicating whether errors are present
*/
bool has_errors () const;
/**
* @brief Gets a value indicating whether warnings are present
*/
bool has_warnings () const;
public slots:
/**
* @brief Clears the log
*/
void clear ();
/**
* @brief Adds a separator
*/
void separator ();
/**
* @brief copies the contents to the clipboard
*/
void copy ();
public:
/**
* @brief Gets the error channel
*/
tl::Channel &error ()
{
return m_error_receiver;
}
/**
* @brief Gets the warning channel
*/
tl::Channel &warn ()
{
return m_warn_receiver;
}
/**
* @brief Gets the info channel
*/
tl::Channel &info ()
{
return m_info_receiver;
}
/**
* @brief Gets the log channel
*/
tl::Channel &log ()
{
return m_log_receiver;
}
private slots:
void timeout ();
signals:
/**
* @brief This signal is emitted if the log's attention state has changed
* Attention state is "true" if either errors or warnings are present.
*/
void attention_changed (bool f);
private:
QTimer m_timer;
mutable QMutex m_lock;
LogReceiver m_error_receiver;
@ -135,18 +213,63 @@ public:
size_t m_max_entries;
size_t m_generation_id;
size_t m_last_generation_id;
bool m_has_errors, m_has_warnings;
bool m_last_attn;
/**
* @brief Adds an error
*/
void add_error (const std::string &msg, bool continued)
{
add (continued ? LogFileEntry::ErrorContinued : LogFileEntry::Error, msg, continued);
}
/**
* @brief Adds a info message
*/
void add_info (const std::string &msg, bool continued)
{
add (continued ? LogFileEntry::InfoContinued : LogFileEntry::Info, msg, continued);
}
/**
* @brief Adds a warning
*/
void add_warn (const std::string &msg, bool continued)
{
add (continued ? LogFileEntry::WarningContinued : LogFileEntry::Warning, msg, continued);
}
/**
* @brief Adds anything
*/
void add (LogFileEntry::mode_type mode, const std::string &msg, bool continued);
};
class LogViewerDialog
/**
* @brief A dialog presenting the log file
*/
class LAY_PUBLIC LogViewerDialog
: public QDialog,
public Ui::LogViewerDialog
{
Q_OBJECT
public:
LogViewerDialog (QWidget *parent);
/**
* @brief The constructor
* If "register_global" is true, the log is registered globally
* and will receiver global log messages.
*/
LogViewerDialog (QWidget *parent, bool register_global = true, bool interactive = true);
/**
* @brief Gets the log file object
*/
LogFile &file ()
{
return m_file;
}
public slots:
void verbosity_changed (int l);
@ -155,7 +278,93 @@ private:
LogFile m_file;
};
/**
* @brief A tool button that collects logs and makes itself visible once attention is required
*/
class LAY_PUBLIC AlertLogButton
: public QToolButton
{
Q_OBJECT
public:
/**
* @brief Constructor
*/
AlertLogButton (QWidget *parent);
/**
* @brief Gets the error channel
*/
tl::Channel &error () const
{
return mp_logger->file ().error ();
}
/**
* @brief Gets the warn channel
*/
tl::Channel &warn () const
{
return mp_logger->file ().warn ();
}
/**
* @brief Gets the info channel
*/
tl::Channel &info () const
{
return mp_logger->file ().info ();
}
/**
* @brief Gets the log channel
*/
tl::Channel &log () const
{
return mp_logger->file ().log ();
}
/**
* @brief Gets the error status of the log
*/
bool has_errors () const
{
return mp_logger->file ().has_errors ();
}
/**
* @brief Gets the warning status of the log
*/
bool has_warnings () const
{
return mp_logger->file ().has_warnings ();
}
/**
* @brief Gets the attention status of the log
* (either warnings or errors are present)
*/
bool needs_attention () const
{
return has_errors () || has_warnings ();
}
public slots:
/**
* @brief Clears the log (and makes the button invisible)
*/
void clear ()
{
mp_logger->file ().clear ();
}
private slots:
void attention_changed (bool);
private:
LogViewerDialog *mp_logger;
};
}
#endif

View File

@ -115,6 +115,7 @@
<file alias="yellow_flag.png">images/yellow_flag.png</file>
<file alias="salt.png">images/salt.png</file>
<file alias="salt_icon.png">images/salt_icon.png</file>
<file alias="warn.png">images/warn.png</file>
</qresource>
<qresource prefix="/syntax">
<file alias="ruby.xml">syntax/ruby.xml</file>

View File

@ -185,6 +185,28 @@ SaltGrain::compare_versions (const std::string &v1, const std::string &v2)
}
}
bool
SaltGrain::valid_version (const std::string &v)
{
tl::Extractor ex (v.c_str ());
while (! ex.at_end ()) {
int n = 0;
if (! ex.try_read (n)) {
return false;
}
if (! ex.at_end ()) {
if (*ex != '.') {
return false;
} else {
++ex;
}
}
}
return true;
}
struct TimeConverter
{
std::string to_string (const QDateTime &time) const

View File

@ -352,6 +352,11 @@ public:
*/
static int compare_versions (const std::string &v1, const std::string &v2);
/**
* @brief Gets a value indicating whether the given version string is a valid version
*/
static bool valid_version (const std::string &v);
/**
* @brief Detects a grain from the given directory
* This method will return a grain constructed from the given directory.

View File

@ -31,6 +31,8 @@
#include <QItemDelegate>
#include <QPainter>
#include <QCompleter>
#include <QMessageBox>
#include <memory>
namespace lay
@ -149,6 +151,10 @@ void
SaltGrainPropertiesDialog::update_controls ()
{
setWindowTitle (m_title + tl::to_qstring (" - " + m_grain.name ()));
license_alert->clear ();
version_alert->clear ();
doc_url_alert->clear ();
dependencies_alert->clear ();
version->setText (tl::to_qstring (m_grain.version ()));
title->setText (tl::to_qstring (m_grain.title ()));
@ -300,15 +306,27 @@ BEGIN_PROTECTED
const int max_dim = 256;
QString fileName = QFileDialog::getOpenFileName (this, tr ("Pick Icon Image File"), m_image_dir, tr ("Images (*.png *.jpg)"));
QString fileName = QFileDialog::getOpenFileName (this, tr ("Pick Icon Image File"), m_image_dir, tr ("Images (*.png *.jpg);;All Files (*)"));
if (! fileName.isNull ()) {
bool ok = true;
QImage img = QImage (fileName);
if (img.width () > max_dim || img.height () > max_dim) {
throw tl::Exception (tl::to_string (tr ("Icon image too big -\nmust be %1x%2 pixels max, but is %3x%4").arg (max_dim).arg (max_dim).arg (img.width ()).arg (img.height ())));
if (QMessageBox::warning (this, tr ("Image Too Big"),
tr ("Icon image too big - must be %1x%2 pixels max, but is %3x%4.\n\nScale image?").arg (max_dim).arg (max_dim).arg (img.width ()).arg (img.height ()),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
ok = false;
} else {
img = img.scaled (max_dim, max_dim, Qt::KeepAspectRatio);
}
}
m_grain.set_icon (img);
m_image_dir = QFileInfo (fileName).path ();
update_icon ();
if (ok) {
m_grain.set_icon (img);
m_image_dir = QFileInfo (fileName).path ();
update_icon ();
}
}
END_PROTECTED
@ -328,15 +346,27 @@ BEGIN_PROTECTED
const int max_dim = 1024;
QString fileName = QFileDialog::getOpenFileName (this, tr ("Pick Showcase Image File"), m_image_dir, tr ("Images (*.png *.jpg)"));
QString fileName = QFileDialog::getOpenFileName (this, tr ("Pick Showcase Image File"), m_image_dir, tr ("Images (*.png *.jpg);;All Files (*)"));
if (! fileName.isNull ()) {
bool ok = true;
QImage img = QImage (fileName);
if (img.width () > max_dim || img.height () > max_dim) {
throw tl::Exception (tl::to_string (tr ("Showcase image too big -\nmust be %1x%2 pixels max, but is %3x%4").arg (max_dim).arg (max_dim).arg (img.width ()).arg (img.height ())));
if (QMessageBox::warning (this, tr ("Image Too Big"),
tr ("Showcase image too big - must be %1x%2 pixels max, but is %3x%4.\n\nScale image?").arg (max_dim).arg (max_dim).arg (img.width ()).arg (img.height ()),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
ok = false;
} else {
img = img.scaled (max_dim, max_dim, Qt::KeepAspectRatio);
}
}
m_grain.set_screenshot (img);
m_image_dir = QFileInfo (fileName).path ();
update_screenshot ();
if (ok) {
m_grain.set_screenshot (img);
m_image_dir = QFileInfo (fileName).path ();
update_screenshot ();
}
}
END_PROTECTED
@ -367,6 +397,44 @@ SaltGrainPropertiesDialog::remove_dependency_clicked ()
}
}
void
SaltGrainPropertiesDialog::accept ()
{
update_data ();
// Perform some checks
license_alert->clear ();
if (m_grain.license ().empty ()) {
license_alert->warn () << tr ("License field is empty. Please consider specifying a license model.");
}
version_alert->clear ();
if (m_grain.version ().empty ()) {
version_alert->warn () << tr ("Version field is empty. Please consider specifying a version number.");
} else if (! SaltGrain::valid_version (m_grain.version ())) {
version_alert->error () << tr ("'%1' is not a valid version string. A version string needs to be numeric (like '1.2.3' or '4.5'').").arg (tl::to_qstring (m_grain.version ()));
}
doc_url_alert->clear ();
// @@@ TODO
dependencies_alert->clear ();
// @@@ TODO
if (!license_alert->needs_attention () &&
!doc_url_alert->needs_attention () &&
!dependencies_alert->needs_attention () &&
!version_alert->needs_attention ()) {
QDialog::accept ();
} else {
if (QMessageBox::warning (this, tr ("Issues Encountered"),
tr ("Some issues have been found when inspecting the package details.\nThe respective fields are marked with warning icons.\n\nIgnore these issues and commit the package details?"),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
QDialog::accept ();
}
}
}
bool
SaltGrainPropertiesDialog::exec_dialog (lay::SaltGrain *grain, lay::Salt *salt)
{
@ -379,7 +447,6 @@ SaltGrainPropertiesDialog::exec_dialog (lay::SaltGrain *grain, lay::Salt *salt)
bool res = exec ();
if (res) {
update_data ();
*grain = m_grain;
}

View File

@ -74,6 +74,9 @@ private slots:
void remove_dependency_clicked ();
void dependency_changed (QTreeWidgetItem *item, int column);
protected:
void accept ();
private:
lay::SaltGrain m_grain;
lay::Salt *mp_salt;

View File

@ -156,6 +156,12 @@ TEST (1)
TEST (2)
{
EXPECT_EQ (lay::SaltGrain::valid_version (""), true);
EXPECT_EQ (lay::SaltGrain::valid_version ("1"), true);
EXPECT_EQ (lay::SaltGrain::valid_version ("1.2"), true);
EXPECT_EQ (lay::SaltGrain::valid_version ("\t1 . 2.\n3"), true);
EXPECT_EQ (lay::SaltGrain::valid_version ("x"), false);
EXPECT_EQ (lay::SaltGrain::valid_version ("1.2x"), false);
EXPECT_EQ (lay::SaltGrain::compare_versions ("", ""), 0);
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", "2"), -1);
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", ""), 1);