WIP: various enhancements for salt manager

* double-click
* salt mine context menu
* bug fixes
* etc.
This commit is contained in:
Matthias Koefferlein 2017-03-27 23:55:26 +02:00
parent cb589dc2d3
commit 7228efc7bd
15 changed files with 566 additions and 107 deletions

View File

@ -231,8 +231,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>537</width>
<height>284</height>
<width>343</width>
<height>207</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
@ -673,6 +673,13 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="apply_label">
<property name="text">
<string>Set in code</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
@ -725,16 +732,19 @@
<string>Delete package</string>
</property>
</action>
<action name="actionImport">
<property name="icon">
<iconset resource="layResources.qrc">
<normaloff>:/import.png</normaloff>:/import.png</iconset>
</property>
<action name="actionUnmarkAll">
<property name="text">
<string>Import</string>
<string>Unmark all</string>
</property>
<property name="toolTip">
<string>Import package</string>
</action>
<action name="actionShowMarkedOnly">
<property name="text">
<string>Show marked only</string>
</property>
</action>
<action name="actionShowAll">
<property name="text">
<string>Show all</string>
</property>
</action>
</widget>

View File

@ -26,6 +26,15 @@
</item>
<item>
<widget class="QTreeWidget" name="list">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Package</string>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 889 B

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 424 B

View File

@ -49,6 +49,7 @@ Salt &Salt::operator= (const Salt &other)
{
if (this != &other) {
m_root = other.m_root;
invalidate ();
}
return *this;
}

View File

@ -28,6 +28,7 @@
#include "ui_SaltManagerInstallConfirmationDialog.h"
#include <QTreeWidgetItem>
#include <QMessageBox>
namespace lay
{
@ -47,10 +48,17 @@ public:
void add_info (const std::string &name, bool update, const std::string &version, const std::string &url)
{
QTreeWidgetItem *item = new QTreeWidgetItem (list);
item->setFlags (item->flags () & ~Qt::ItemIsSelectable);
item->setText (0, tl::to_qstring (name));
item->setText (1, update ? tr ("UPDATE") : tr ("INSTALL"));
item->setText (2, tl::to_qstring (version));
item->setText (3, tl::to_qstring (url));
for (int column = 0; column < list->colorCount (); ++column) {
item->setData (column, Qt::ForegroundRole, update ? Qt::blue : Qt::black);
}
}
};
@ -72,11 +80,25 @@ SaltDownloadManager::compute_dependencies (const lay::Salt &salt, const lay::Sal
{
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Computing package dependencies ..")));
std::map<std::string, Descriptor> registry;
// remove those registered entries which don't need to be updated
registry = m_registry;
for (std::map<std::string, Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
const SaltGrain *g = salt.grain_by_name (p->first);
if (g && SaltGrain::compare_versions (p->second.version, g->version ()) == 0 && p->second.url == g->url ()) {
m_registry.erase (p->first);
}
}
// add further entries as derived from the dependencies
while (needs_iteration ()) {
fetch_missing (salt_mine, progress);
std::map<std::string, Descriptor> registry = m_registry;
registry = m_registry;
for (std::map<std::string, Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
for (std::vector<SaltGrain::Dependency>::const_iterator d = p->second.grain.dependencies ().begin (); d != p->second.grain.dependencies ().end (); ++d) {
@ -147,7 +169,12 @@ SaltDownloadManager::fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProg
p->second.url = g->url ();
}
p->second.grain = SaltGrain::from_url (p->second.url);
try {
p->second.grain = SaltGrain::from_url (p->second.url);
} catch (tl::Exception &ex) {
throw tl::Exception (tl::to_string (QObject::tr ("Error fetching spec file for package '%1': %2").arg (tl::to_qstring (p->first)).arg (tl::to_qstring (ex.msg ()))));
}
p->second.downloaded = true;
}
@ -158,13 +185,20 @@ SaltDownloadManager::fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProg
bool
SaltDownloadManager::show_confirmation_dialog (QWidget *parent, const lay::Salt &salt)
{
// Stop with a warning if there is nothing to do
if (m_registry.empty()) {
QMessageBox::warning (parent, tr ("Nothing to do"), tr ("No packages need update or are marked for installation"));
return false;
}
lay::ConfirmationDialog dialog (parent);
// First the packages to update
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
const lay::SaltGrain *g = salt.grain_by_name (p->first);
if (g) {
dialog.add_info (p->first, true, g->version () + "->" + p->second.version, p->second.url);
// \342\206\222 is UTF-8 "right arrow"
dialog.add_info (p->first, true, g->version () + " \342\206\222 " + p->second.version, p->second.url);
}
}

View File

@ -392,6 +392,10 @@ SaltGrain::from_path (const std::string &path)
SaltGrain
SaltGrain::from_url (const std::string &url)
{
if (url.empty ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No download link available")));
}
tl::InputHttpStream http (SaltGrain::spec_url (url));
tl::InputStream stream (http);

View File

@ -226,8 +226,11 @@ SaltGrainDetailsTextWidget::details_text ()
stream << "<p><b>" << QObject::tr ("Depends on: ") << "</b><br/>";
for (std::vector<lay::SaltGrain::Dependency>::const_iterator d = g->dependencies ().begin (); d != g->dependencies ().end (); ++d) {
stream << "&nbsp;&nbsp;&nbsp;&nbsp;" << tl::to_qstring (tl::escaped_to_html (d->name)) << " ";
stream << tl::to_qstring (tl::escaped_to_html (d->version)) << " - ";
stream << "[" << tl::to_qstring (tl::escaped_to_html (d->url)) << "]<br/>";
stream << tl::to_qstring (tl::escaped_to_html (d->version));
if (! d->url.empty ()) {
stream << " - ";
stream << "[" << tl::to_qstring (tl::escaped_to_html (d->url)) << "]<br/>";
}
}
stream << "</p>";
}

View File

@ -169,12 +169,19 @@ SaltGrainPropertiesDialog::update_controls ()
dependencies->clear ();
for (std::vector<SaltGrain::Dependency>::const_iterator d = m_grain.dependencies ().begin (); d != m_grain.dependencies ().end (); ++d) {
QTreeWidgetItem *item = new QTreeWidgetItem (dependencies);
item->setFlags (item->flags () | Qt::ItemIsEditable);
item->setData (0, Qt::UserRole, tl::to_qstring (d->name));
dependency_changed (item, 0);
item->setData (1, Qt::UserRole, tl::to_qstring (d->version));
dependency_changed (item, 1);
item->setData (2, Qt::UserRole, tl::to_qstring (d->url));
dependency_changed (item, 2);
dependencies->addTopLevelItem (item);
}
update_icon ();
@ -249,9 +256,11 @@ SaltGrainPropertiesDialog::dependency_changed (QTreeWidgetItem *item, int column
}
m_update_enabled = false;
std::string name = tl::to_string (item->data (0, Qt::UserRole).toString ().simplified ());
SaltGrain *g = mp_salt->grain_by_name (name);
if (column == 0 && mp_salt) {
std::string name = tl::to_string (item->data (0, Qt::UserRole).toString ().simplified ());
item->setData (0, Qt::EditRole, tl::to_qstring (name));
// set URL and version for known grains
@ -265,30 +274,48 @@ SaltGrainPropertiesDialog::dependency_changed (QTreeWidgetItem *item, int column
} else {
SaltGrain *g = 0;
for (lay::Salt::flat_iterator i = mp_salt->begin_flat (); i != mp_salt->end_flat (); ++i) {
if ((*i)->name () == name) {
g = *i;
}
}
if (g) {
item->setData (1, Qt::UserRole, tl::to_qstring (g->version ()));
item->setData (2, Qt::UserRole, tl::to_qstring (g->url ()));
item->setData (2, Qt::UserRole, QString ());
// placeholder texts:
item->setData (1, Qt::EditRole, tl::to_qstring (g->version ()));
item->setData (2, Qt::EditRole, tl::to_qstring (g->url ()));
if (! g->url ().empty ()) {
item->setData (2, Qt::EditRole, tl::to_qstring ("(" + g->url () + ")"));
} else {
item->setData (2, Qt::EditRole, tr ("(from repository)"));
}
} else {
item->setData (1, Qt::UserRole, QString ());
item->setData (2, Qt::UserRole, QString ());
// placeholder texts:
item->setData (1, Qt::EditRole, QString ());
item->setData (2, Qt::EditRole, tr ("(unknown packet)"));
item->setData (2, Qt::EditRole, tr ("(from repository)"));
}
}
} else if (column > 0) {
item->setData (column, Qt::EditRole, item->data (column, Qt::UserRole).toString ());
} else if (column == 1) {
QString text = item->data (column, Qt::UserRole).toString ();
if (! text.isEmpty ()) {
item->setData (1, Qt::EditRole, text);
} else if (g) {
item->setData (1, Qt::EditRole, tl::to_qstring (g->version ()));
}
} else if (column == 2) {
QString text = item->data (column, Qt::UserRole).toString ();
if (! text.isEmpty ()) {
item->setData (2, Qt::EditRole, text);
} else if (g) {
if (! g->url ().empty ()) {
item->setData (2, Qt::EditRole, tl::to_qstring ("(" + g->url () + ")"));
} else {
item->setData (2, Qt::EditRole, tr ("(from repository)"));
}
}
}
m_update_enabled = true;
@ -531,10 +558,7 @@ SaltGrainPropertiesDialog::accept ()
}
dep_seen.insert (d->name);
if (! dep.is_valid_name (d->name)) {
dependencies_alert->warn () << tr ("'%1' is not a name of a package loaded already").arg (tl::to_qstring (d->name)) << tl::endl
<< tr ("You need to specify the details (version, URL) manually");
} else {
if (dep.is_valid_name (d->name)) {
try {
dep.check_circular (dep.grain_for_name (m_grain.name ()), dep.grain_for_name (d->name));
} catch (tl::Exception &ex) {
@ -549,11 +573,7 @@ SaltGrainPropertiesDialog::accept ()
<< tr ("If the dependency package has a version itself, the version is automatically set to it's current version.");
}
if (d->url.empty ()) {
dependencies_alert->warn () << tr ("No download URL specified for dependency '%1'").arg (tl::to_qstring (d->name)) << tl::endl
<< tr ("A download URL should be specified to ensure the package dependencies can be resolved.") << tl::endl
<< tr ("If the dependency package was downloaded itself, the URL is automatically set to the download source.");
} else {
if (!d->url.empty ()) {
SaltGrain gdep;
try {
gdep = SaltGrain::from_url (d->url);

View File

@ -144,7 +144,7 @@ lay::Salt *get_salt_mine ()
SaltManagerDialog::SaltManagerDialog (QWidget *parent)
: QDialog (parent),
m_current_changed_enabled (true)
m_current_changed_enabled (true), dm_update_models (this, &SaltManagerDialog::update_models)
{
Ui::SaltManagerDialog::setupUi (this);
mp_properties_dialog = new lay::SaltGrainPropertiesDialog (this);
@ -165,15 +165,6 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent)
salt_mine_view->setModel (mine_model);
salt_mine_view->setItemDelegate (new SaltItemDelegate (this));
// Establish a message saying that an update is available
for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) {
SaltGrain *gm = mp_salt_mine->grain_by_name ((*g)->name ());
if (gm && SaltGrain::compare_versions (gm->version (), (*g)->version ()) > 0) {
model->set_message ((*g)->name (), tl::to_string (tr ("An update to version %1 is available").arg (tl::to_qstring (gm->version ()))));
mine_model->set_message ((*g)->name (), tl::to_string (tr ("The installed version is outdated (%1)").arg (tl::to_qstring ((*g)->version ()))));
}
}
mode_tab->setCurrentIndex (mp_salt->is_empty () ? 1 : 0);
connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (mode_changed ()));
@ -181,11 +172,12 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent)
connect (mp_salt, SIGNAL (collections_changed ()), this, SLOT (salt_changed ()));
connect (mp_salt_mine, SIGNAL (collections_changed ()), this, SLOT (salt_mine_changed ()));
salt_changed ();
salt_mine_changed ();
update_models ();
connect (salt_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_changed ()));
connect (salt_view, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (edit_properties ()));
connect (salt_mine_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (mine_current_changed ()), Qt::QueuedConnection);
connect (salt_mine_view, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (mark_clicked ()));
search_installed_edit->set_clear_button_enabled (true);
search_new_edit->set_clear_button_enabled (true);
@ -193,6 +185,18 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent)
connect (search_new_edit, SIGNAL (textChanged (const QString &)), this, SLOT (search_text_changed (const QString &)));
connect (mark_button, SIGNAL (clicked ()), this, SLOT (mark_clicked ()));
salt_mine_view->addAction (actionUnmarkAll);
QAction *a = new QAction (this);
a->setSeparator (true);
salt_mine_view->addAction (a);
salt_mine_view->addAction (actionShowMarkedOnly);
salt_mine_view->addAction (actionShowAll);
salt_mine_view->setContextMenuPolicy (Qt::ActionsContextMenu);
connect (actionUnmarkAll, SIGNAL (triggered ()), this, SLOT (unmark_all ()));
connect (actionShowMarkedOnly, SIGNAL (triggered ()), this, SLOT (show_marked_only ()));
connect (actionShowAll, SIGNAL (triggered ()), this, SLOT (show_all ()));
}
void
@ -201,11 +205,55 @@ SaltManagerDialog::mode_changed ()
// keeps the splitters in sync
if (mode_tab->currentIndex () == 1) {
splitter_new->setSizes (splitter->sizes ());
show_all ();
} else if (mode_tab->currentIndex () == 0) {
splitter->setSizes (splitter_new->sizes ());
}
}
void
SaltManagerDialog::show_all ()
{
search_new_edit->clear ();
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
if (! model) {
return;
}
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
--i;
salt_mine_view->setRowHidden (i, false);
}
}
void
SaltManagerDialog::show_marked_only ()
{
search_new_edit->clear ();
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
if (! model) {
return;
}
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
--i;
SaltGrain *g = model->grain_from_index (model->index (i, 0, QModelIndex ()));
salt_mine_view->setRowHidden (i, !(g && model->is_marked (g->name ())));
}
}
void
SaltManagerDialog::unmark_all ()
{
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
if (model) {
model->clear_marked ();
update_apply_state ();
}
}
void
SaltManagerDialog::search_text_changed (const QString &text)
{
@ -258,6 +306,36 @@ SaltManagerDialog::mark_clicked ()
}
model->set_marked (g->name (), !model->is_marked (g->name ()));
update_apply_state ();
}
void
SaltManagerDialog::update_apply_state ()
{
int marked = 0;
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
if (! model) {
return;
}
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
--i;
QModelIndex index = model->index (i, 0, QModelIndex ());
SaltGrain *g = model->grain_from_index (index);
if (g && model->is_marked (g->name ())) {
marked += 1;
}
}
apply_button->setEnabled (marked > 0);
if (marked == 0) {
apply_label->setText (QString ());
} else if (marked == 1) {
apply_label->setText (tr ("One package selected"));
} else if (marked > 1) {
apply_label->setText (tr ("%1 packages selected").arg (marked));
}
}
void
@ -290,6 +368,7 @@ BEGIN_PROTECTED
manager.compute_dependencies (*mp_salt, *mp_salt_mine);
if (manager.show_confirmation_dialog (this, *mp_salt)) {
unmark_all ();
manager.execute (*mp_salt);
}
@ -363,16 +442,38 @@ END_PROTECTED
void
SaltManagerDialog::salt_changed ()
{
dm_update_models ();
}
void
SaltManagerDialog::salt_mine_changed ()
{
dm_update_models ();
}
void
SaltManagerDialog::update_models ()
{
SaltModel *model = dynamic_cast <SaltModel *> (salt_view->model ());
if (! model) {
return;
}
tl_assert (model != 0);
// NOTE: the disabling of the event handler prevents us from
// letting the model connect to the salt's signal directly.
m_current_changed_enabled = false;
model->clear_messages ();
// Establish a message saying that an update is available
for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) {
SaltGrain *gm = mp_salt_mine->grain_by_name ((*g)->name ());
if (gm && SaltGrain::compare_versions (gm->version (), (*g)->version ()) > 0) {
model->set_message ((*g)->name (), SaltModel::Warning, tl::to_string (tr ("An update to version %1 is available").arg (tl::to_qstring (gm->version ()))));
}
}
model->update ();
m_current_changed_enabled = true;
if (mp_salt->is_empty ()) {
@ -392,7 +493,42 @@ SaltManagerDialog::salt_changed ()
}
SaltModel *mine_model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
tl_assert (mine_model != 0);
// NOTE: the disabling of the event handler prevents us from
// letting the model connect to the salt's signal directly.
m_current_changed_enabled = false;
mine_model->clear_order ();
mine_model->clear_messages ();
mine_model->enable_all ();
// Establish a message saying that an update is available
for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) {
SaltGrain *gm = mp_salt_mine->grain_by_name ((*g)->name ());
if (gm && SaltGrain::compare_versions (gm->version (), (*g)->version ()) > 0) {
mine_model->set_message ((*g)->name (), SaltModel::Warning, tl::to_string (tr ("The installed version is outdated (%1)").arg (tl::to_qstring ((*g)->version ()))));
mine_model->set_order ((*g)->name (), -1);
} else if (gm) {
mine_model->set_message ((*g)->name (), SaltModel::None, tl::to_string (tr ("This package is already installed and up to date")));
mine_model->set_order ((*g)->name (), 1);
mine_model->set_enabled ((*g)->name (), false);
}
}
mine_model->update ();
m_current_changed_enabled = true;
// select the first grain
if (mine_model->rowCount (QModelIndex ()) > 0) {
salt_mine_view->setCurrentIndex (mine_model->index (0, 0, QModelIndex ()));
}
mine_current_changed ();
current_changed ();
update_apply_state ();
}
void
@ -417,28 +553,6 @@ SaltManagerDialog::current_grain ()
return model ? model->grain_from_index (salt_view->currentIndex ()) : 0;
}
void
SaltManagerDialog::salt_mine_changed ()
{
SaltModel *model = dynamic_cast <SaltModel *> (salt_mine_view->model ());
if (! model) {
return;
}
// NOTE: the disabling of the event handler prevents us from
// letting the model connect to the salt's signal directly.
m_current_changed_enabled = false;
model->update ();
m_current_changed_enabled = true;
// select the first grain
if (model->rowCount (QModelIndex ()) > 0) {
salt_mine_view->setCurrentIndex (model->index (0, 0, QModelIndex ()));
}
mine_current_changed ();
}
void
SaltManagerDialog::mine_current_changed ()
{

View File

@ -24,6 +24,7 @@
#define HDR_laySaltManagerDialog
#include "ui_SaltManagerDialog.h"
#include "tlDeferredExecution.h"
#include <QDialog>
#include <memory>
@ -105,14 +106,32 @@ private slots:
*/
void search_text_changed (const QString &text);
private:
lay::Salt *mp_salt, *mp_salt_mine;
std::auto_ptr<lay::SaltGrain> m_remote_grain;
bool m_current_changed_enabled;
lay::SaltGrainPropertiesDialog *mp_properties_dialog;
/**
* @brief Called to show the marked items only
*/
void show_marked_only ();
lay::SaltGrain *current_grain ();
lay::SaltGrain *mine_current_grain ();
/**
* @brief Called to show all items again
*/
void show_all ();
/**
* @brief Called to unmark all items
*/
void unmark_all ();
private:
Salt *mp_salt, *mp_salt_mine;
std::auto_ptr<SaltGrain> m_remote_grain;
bool m_current_changed_enabled;
SaltGrainPropertiesDialog *mp_properties_dialog;
tl::DeferredMethod<SaltManagerDialog> dm_update_models;
SaltGrain *current_grain ();
SaltGrain *mine_current_grain ();
void update_models ();
void update_apply_state ();
};
}

View File

@ -46,6 +46,9 @@ SaltItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option,
QStyleOptionViewItemV4 optionV4 = option;
initStyleOption (&optionV4, index);
bool is_enabled = (optionV4.state & QStyle::State_Enabled);
optionV4.state |= QStyle::State_Enabled;
QStyle *style = optionV4.widget ? optionV4.widget->style () : QApplication::style ();
QTextDocument doc;
@ -58,6 +61,8 @@ SaltItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option,
if (optionV4.state & QStyle::State_Selected) {
ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Active, QPalette::HighlightedText));
} else if (! is_enabled) {
ctx.palette.setColor (QPalette::Text, optionV4.palette.color (QPalette::Disabled, QPalette::Text));
}
QRect textRect = style->subElementRect (QStyle::SE_ItemViewItemText, &optionV4);
@ -93,15 +98,32 @@ SaltItemDelegate::sizeHint (const QStyleOptionViewItem &option, const QModelInde
SaltModel::SaltModel (QObject *parent, lay::Salt *salt)
: QAbstractItemModel (parent), mp_salt (salt)
{
// .. nothing yet ..
create_ordered_list ();
}
QVariant
Qt::ItemFlags
SaltModel::flags (const QModelIndex &index) const
{
Qt::ItemFlags f = QAbstractItemModel::flags (index);
const lay::SaltGrain *g = grain_from_index (index);
if (g && ! is_enabled (g->name ())) {
f &= ~Qt::ItemIsSelectable;
f &= ~Qt::ItemIsEnabled;
}
return f;
}
QVariant
SaltModel::data (const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole) {
const lay::SaltGrain *g = mp_salt->begin_flat ()[index.row ()];
const lay::SaltGrain *g = grain_from_index (index);
if (!g) {
return QVariant ();
}
std::string text = "<html><body>";
text += "<h4>";
@ -121,9 +143,15 @@ SaltModel::data (const QModelIndex &index, int role) const
text += "</p>";
}
std::map<std::string, std::string>::const_iterator m = m_messages.find (g->name ());
std::map<std::string, std::pair<Severity, std::string> >::const_iterator m = m_messages.find (g->name ());
if (m != m_messages.end ()) {
text += "<p><font color=\"#ff0000\"><b>" + tl::escaped_to_html (m->second) + "</b></font></p>";
if (m->second.first == Warning || m->second.first == Error) {
text += "<p><font color=\"#ff0000\"><b>" + tl::escaped_to_html (m->second.second) + "</b></font></p>";
} else if (m->second.first == Info) {
text += "<p><font color=\"#c0c0c0\">" + tl::escaped_to_html (m->second.second) + "</font></p>";
} else {
text += "<p>" + tl::escaped_to_html (m->second.second) + "</p>";
}
}
text += "</body></html>";
@ -134,7 +162,10 @@ SaltModel::data (const QModelIndex &index, int role) const
int icon_dim = 64;
const lay::SaltGrain *g = mp_salt->begin_flat ()[index.row ()];
const lay::SaltGrain *g = grain_from_index (index);
if (!g) {
return QVariant ();
}
QImage img;
if (g->icon ().isNull ()) {
@ -160,10 +191,21 @@ SaltModel::data (const QModelIndex &index, int role) const
painter.drawImage (0, 0, warn);
}
if (m_messages.find (g->name ()) != m_messages.end ()) {
QPainter painter (&img);
QImage warn (":/warn_16.png");
painter.drawImage (0, 0, warn);
std::map<std::string, std::pair<Severity, std::string> >::const_iterator m = m_messages.find (g->name ());
if (m != m_messages.end ()) {
if (m->second.first == Warning) {
QPainter painter (&img);
QImage warn (":/warn_16.png");
painter.drawImage (0, 0, warn);
} else if (m->second.first == Error) {
QPainter painter (&img);
QImage warn (":/error_16.png");
painter.drawImage (0, 0, warn);
} else if (m->second.first == Info) {
QPainter painter (&img);
QImage warn (":/info_16.png");
painter.drawImage (0, 0, warn);
}
}
return QPixmap::fromImage (img);
@ -179,7 +221,7 @@ SaltModel::index (int row, int column, const QModelIndex &parent) const
if (parent.isValid ()) {
return QModelIndex ();
} else {
return createIndex (row, column, mp_salt->begin_flat () [row]);
return createIndex (row, column, m_ordered_grains [row]);
}
}
@ -201,7 +243,7 @@ SaltModel::rowCount (const QModelIndex &parent) const
if (parent.isValid ()) {
return 0;
} else {
return mp_salt->end_flat () - mp_salt->begin_flat ();
return int (m_ordered_grains.size ());
}
}
@ -221,32 +263,149 @@ SaltModel::is_marked (const std::string &name) const
return m_marked.find (name) != m_marked.end ();
}
bool
SaltModel::is_enabled (const std::string &name) const
{
return m_disabled.find (name) == m_disabled.end ();
}
void
SaltModel::set_marked (const std::string &name, bool marked)
{
if (! marked) {
m_marked.erase (name);
} else {
m_marked.insert (name);
if (marked != is_marked (name)) {
if (! marked) {
m_marked.erase (name);
} else {
m_marked.insert (name);
}
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
void
SaltModel::set_message (const std::string &name, const std::string &message)
SaltModel::clear_marked ()
{
if (message.empty ()) {
m_messages.erase (name);
} else {
m_messages.insert (std::make_pair (name, message));
if (! m_marked.empty ()) {
m_marked.clear ();
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
void
void
SaltModel::set_enabled (const std::string &name, bool enabled)
{
if (enabled != is_enabled (name)) {
if (enabled) {
m_disabled.erase (name);
} else {
m_disabled.insert (name);
}
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
}
void
SaltModel::enable_all ()
{
if (! m_disabled.empty ()) {
m_disabled.clear ();
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
}
void
SaltModel::clear_order ()
{
m_display_order.clear ();
}
void
SaltModel::reset_order (const std::string &name)
{
m_display_order.erase (name);
}
void
SaltModel::set_order (const std::string &name, int order)
{
m_display_order[name] = order;
}
void
SaltModel::set_message (const std::string &name, Severity severity, const std::string &message)
{
bool needs_update = false;
if (message.empty ()) {
if (m_messages.find (name) != m_messages.end ()) {
m_messages.erase (name);
needs_update = true;
}
} else {
std::map<std::string, std::pair<Severity, std::string> >::iterator m = m_messages.find (name);
if (m == m_messages.end () || m->second.second != message || m->second.first != severity) {
m_messages.insert (std::make_pair (name, std::make_pair (severity, message)));
needs_update = true;
}
}
if (needs_update) {
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
}
void
SaltModel::clear_messages ()
{
if (! m_messages.empty ()) {
m_messages.clear ();
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex ()) - 1, 0, QModelIndex ()));
}
}
void
SaltModel::update ()
{
create_ordered_list ();
reset ();
}
void
SaltModel::create_ordered_list ()
{
m_ordered_grains.clear ();
if (m_display_order.empty ()) {
for (Salt::flat_iterator i = mp_salt->begin_flat (); i != mp_salt->end_flat (); ++i) {
m_ordered_grains.push_back (*i);
}
} else {
int min_order = m_display_order.begin ()->second;
int max_order = min_order;
min_order = std::min (min_order, 0);
max_order = std::max (max_order, 0);
for (std::map<std::string, int>::const_iterator i = m_display_order.begin (); i != m_display_order.end (); ++i) {
min_order = std::min (min_order, i->second);
max_order = std::max (max_order, i->second);
}
for (int o = min_order; o <= max_order; ++o) {
for (Salt::flat_iterator i = mp_salt->begin_flat (); i != mp_salt->end_flat (); ++i) {
std::map<std::string, int>::const_iterator d = m_display_order.find ((*i)->name ());
int oi = 0;
if (d != m_display_order.end ()) {
oi = d->second;
}
if (oi == o) {
m_ordered_grains.push_back (*i);
}
}
}
}
}
}

View File

@ -47,6 +47,17 @@ class SaltModel
Q_OBJECT
public:
/**
* @brief An enum describing the severity of a message
*/
enum Severity
{
None = 0,
Info = 1,
Warning = 2,
Error = 3
};
/**
* @brief Constructor
*/
@ -57,6 +68,11 @@ public:
*/
QVariant data (const QModelIndex &index, int role) const;
/**
* @brief Implementation of the QAbstractItemModel interface
*/
Qt::ItemFlags flags (const QModelIndex &index) const;
/**
* @brief Implementation of the QAbstractItemModel interface
*/
@ -93,18 +109,70 @@ public:
*/
void set_marked (const std::string &name, bool marked);
/**
* @brief Clears the marked state of all grains
*/
void clear_marked ();
/**
* @brief Enables or disables the grain with the given name
*/
void set_enabled (const std::string &name, bool enabled);
/**
* @brief Enables all grains
*/
void enable_all ();
/**
* @brief Installs a message on the grain with the given name
* Installing an empty message basically removes the message.
*/
void set_message (const std::string &name, const std::string &message);
void set_message (const std::string &name, Severity severity, const std::string &message);
/**
* @brief Removes a message
*/
void reset_message (const std::string &name)
{
set_message (name, None, std::string ());
}
/**
* @brief Clears all messages
*/
void clear_messages ();
/**
* @brief Sets the display order
* Specifying a display order for a name will make the grain appear
* before or after other grains.
* "update" needs to be called before the order becomes active.
* Non-assigned items are considered to have order (0).
*/
void set_order (const std::string &name, int order);
/**
* @brief Resets any display order
*/
void reset_order (const std::string &name);
/**
* @brief Resets all display order specs
*/
void clear_order ();
public:
lay::Salt *mp_salt;
std::set<std::string> m_marked;
std::map<std::string, std::string> m_messages;
std::set<std::string> m_disabled;
std::map<std::string, std::pair<Severity, std::string> > m_messages;
std::map<std::string, int> m_display_order;
std::vector<SaltGrain *> m_ordered_grains;
bool is_marked (const std::string &name) const;
bool is_enabled (const std::string &name) const;
void create_ordered_list ();
};
// --------------------------------------------------------------------------------------

View File

@ -416,6 +416,20 @@ tl::to_word_or_quoted_string (const std::string &s, const char *non_term)
void
tl::escape_to_html (std::string &out, const std::string &in, bool replace_newlines)
{
for (const char *cp = in.c_str (); *cp; ++cp) {
if (*cp == '<') {
out += "&lt;";

View File

@ -273,8 +273,10 @@ WebDAVObject::download (const std::string &url, const std::string &target)
bool has_errors = false;
{
tl::info << tl::to_string (QObject::tr ("Downloading %1 files now").arg (items.size ()));
tl::info << tl::to_string (QObject::tr ("Downloading %1 file(s) now ..").arg (items.size ()));
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Downloading file(s) from %1").arg (tl::to_qstring (url))), items.size (), 1);
for (std::list<DownloadItem>::const_iterator i = items.begin (); i != items.end (); ++i) {
tl::info << QObject::tr ("Downloading '%1' to '%2' ..").arg (tl::to_qstring (i->url)).arg (tl::to_qstring (i->path));
@ -307,6 +309,8 @@ WebDAVObject::download (const std::string &url, const std::string &target)
has_errors = true;
}
++progress;
}
}