mirror of https://github.com/KLayout/klayout.git
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:
parent
faf8acb3d1
commit
ec415d9251
|
|
@ -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
Binary file not shown.
|
After Width: | Height: | Size: 637 B |
|
|
@ -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 ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue