diff --git a/src/lay/SaltManagerDialog.ui b/src/lay/SaltManagerDialog.ui index a51993a12..77651bd95 100644 --- a/src/lay/SaltManagerDialog.ui +++ b/src/lay/SaltManagerDialog.ui @@ -231,8 +231,8 @@ 0 0 - 537 - 284 + 343 + 207 @@ -673,6 +673,13 @@ + + + + Set in code + + + @@ -725,16 +732,19 @@ Delete package - - - - :/import.png:/import.png - + - Import + Unmark all - - Import package + + + + Show marked only + + + + + Show all diff --git a/src/lay/SaltManagerInstallConfirmationDialog.ui b/src/lay/SaltManagerInstallConfirmationDialog.ui index 0920a619b..d8dcf20e4 100644 --- a/src/lay/SaltManagerInstallConfirmationDialog.ui +++ b/src/lay/SaltManagerInstallConfirmationDialog.ui @@ -26,6 +26,15 @@ + + Qt::NoFocus + + + false + + + true + Package diff --git a/src/lay/images/marked_16.png b/src/lay/images/marked_16.png index aa152411b..777a625a7 100644 Binary files a/src/lay/images/marked_16.png and b/src/lay/images/marked_16.png differ diff --git a/src/lay/images/marked_24.png b/src/lay/images/marked_24.png index 8a445139b..cf50321a8 100644 Binary files a/src/lay/images/marked_24.png and b/src/lay/images/marked_24.png differ diff --git a/src/lay/laySalt.cc b/src/lay/laySalt.cc index 2812d2095..4b1b2174d 100644 --- a/src/lay/laySalt.cc +++ b/src/lay/laySalt.cc @@ -49,6 +49,7 @@ Salt &Salt::operator= (const Salt &other) { if (this != &other) { m_root = other.m_root; + invalidate (); } return *this; } diff --git a/src/lay/laySaltDownloadManager.cc b/src/lay/laySaltDownloadManager.cc index c3e587551..08956c7f6 100644 --- a/src/lay/laySaltDownloadManager.cc +++ b/src/lay/laySaltDownloadManager.cc @@ -28,6 +28,7 @@ #include "ui_SaltManagerInstallConfirmationDialog.h" #include +#include 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 registry; + + // remove those registered entries which don't need to be updated + + registry = m_registry; + for (std::map::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 registry = m_registry; + registry = m_registry; for (std::map::const_iterator p = registry.begin (); p != registry.end (); ++p) { for (std::vector::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::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); } } diff --git a/src/lay/laySaltGrain.cc b/src/lay/laySaltGrain.cc index 61ceb8fef..63c7f5689 100644 --- a/src/lay/laySaltGrain.cc +++ b/src/lay/laySaltGrain.cc @@ -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); diff --git a/src/lay/laySaltGrainDetailsTextWidget.cc b/src/lay/laySaltGrainDetailsTextWidget.cc index f71bdda14..310a18a6e 100644 --- a/src/lay/laySaltGrainDetailsTextWidget.cc +++ b/src/lay/laySaltGrainDetailsTextWidget.cc @@ -226,8 +226,11 @@ SaltGrainDetailsTextWidget::details_text () stream << "

" << QObject::tr ("Depends on: ") << "
"; for (std::vector::const_iterator d = g->dependencies ().begin (); d != g->dependencies ().end (); ++d) { stream << "    " << 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)) << "]
"; + 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)) << "]
"; + } } stream << "

"; } diff --git a/src/lay/laySaltGrainPropertiesDialog.cc b/src/lay/laySaltGrainPropertiesDialog.cc index 3059cb653..d78ee94bf 100644 --- a/src/lay/laySaltGrainPropertiesDialog.cc +++ b/src/lay/laySaltGrainPropertiesDialog.cc @@ -169,12 +169,19 @@ SaltGrainPropertiesDialog::update_controls () dependencies->clear (); for (std::vector::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); diff --git a/src/lay/laySaltManagerDialog.cc b/src/lay/laySaltManagerDialog.cc index 978dc9c46..aa3ecd745 100644 --- a/src/lay/laySaltManagerDialog.cc +++ b/src/lay/laySaltManagerDialog.cc @@ -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 (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 (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 (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 (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 (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 (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 (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 () { diff --git a/src/lay/laySaltManagerDialog.h b/src/lay/laySaltManagerDialog.h index c6e4ff893..6e1f40cf4 100644 --- a/src/lay/laySaltManagerDialog.h +++ b/src/lay/laySaltManagerDialog.h @@ -24,6 +24,7 @@ #define HDR_laySaltManagerDialog #include "ui_SaltManagerDialog.h" +#include "tlDeferredExecution.h" #include #include @@ -105,14 +106,32 @@ private slots: */ void search_text_changed (const QString &text); -private: - lay::Salt *mp_salt, *mp_salt_mine; - std::auto_ptr 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 m_remote_grain; + bool m_current_changed_enabled; + SaltGrainPropertiesDialog *mp_properties_dialog; + tl::DeferredMethod dm_update_models; + + SaltGrain *current_grain (); + SaltGrain *mine_current_grain (); + void update_models (); + void update_apply_state (); }; } diff --git a/src/lay/laySaltModel.cc b/src/lay/laySaltModel.cc index 57dd44212..fc1a44ef8 100644 --- a/src/lay/laySaltModel.cc +++ b/src/lay/laySaltModel.cc @@ -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 = ""; text += "

"; @@ -121,9 +143,15 @@ SaltModel::data (const QModelIndex &index, int role) const text += "

"; } - std::map::const_iterator m = m_messages.find (g->name ()); + std::map >::const_iterator m = m_messages.find (g->name ()); if (m != m_messages.end ()) { - text += "

" + tl::escaped_to_html (m->second) + "

"; + if (m->second.first == Warning || m->second.first == Error) { + text += "

" + tl::escaped_to_html (m->second.second) + "

"; + } else if (m->second.first == Info) { + text += "

" + tl::escaped_to_html (m->second.second) + "

"; + } else { + text += "

" + tl::escaped_to_html (m->second.second) + "

"; + } } text += ""; @@ -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 >::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 >::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::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::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); + } + } + } + + } +} + } diff --git a/src/lay/laySaltModel.h b/src/lay/laySaltModel.h index 20a537e2c..d7ba80d92 100644 --- a/src/lay/laySaltModel.h +++ b/src/lay/laySaltModel.h @@ -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 m_marked; - std::map m_messages; + std::set m_disabled; + std::map > m_messages; + std::map m_display_order; + std::vector m_ordered_grains; bool is_marked (const std::string &name) const; + bool is_enabled (const std::string &name) const; + void create_ordered_list (); }; // -------------------------------------------------------------------------------------- diff --git a/src/tl/tlString.cc b/src/tl/tlString.cc index 22d05402f..3179df4ac 100644 --- a/src/tl/tlString.cc +++ b/src/tl/tlString.cc @@ -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 += "<"; diff --git a/src/tl/tlWebDAV.cc b/src/tl/tlWebDAV.cc index 19c2a004e..04706e78e 100644 --- a/src/tl/tlWebDAV.cc +++ b/src/tl/tlWebDAV.cc @@ -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::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; + } }