mirror of https://github.com/KLayout/klayout.git
Implemented a solution for issue #1348 (Reload Files popup prevents closing KLayout): now the reload notification is shown as a notification bar at the top of the layout view
This commit is contained in:
parent
7169910607
commit
d40ed70fb9
|
|
@ -590,12 +590,7 @@ MainWindow::file_changed_timer ()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent recursive signals
|
std::set<QString> notified_files;
|
||||||
m_file_changed_timer.blockSignals (true);
|
|
||||||
|
|
||||||
std::set<QString> reloaded_files;
|
|
||||||
|
|
||||||
BEGIN_PROTECTED
|
|
||||||
|
|
||||||
// Make the names unique
|
// Make the names unique
|
||||||
std::sort (m_changed_files.begin (), m_changed_files.end ());
|
std::sort (m_changed_files.begin (), m_changed_files.end ());
|
||||||
|
|
@ -605,68 +600,38 @@ BEGIN_PROTECTED
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString msg;
|
// adds a notification to the involved views that the file has changed
|
||||||
|
|
||||||
if (m_changed_files.size () == 1) {
|
|
||||||
msg = QObject::tr ("The following file has been changed on disk:\n\n");
|
|
||||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
|
||||||
msg += QString::fromUtf8 (" %1\n").arg (*f);
|
|
||||||
}
|
|
||||||
msg += tr ("\nReload this file?");
|
|
||||||
} else {
|
|
||||||
msg = QObject::tr ("The following files have been changed on disk:\n\n");
|
|
||||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
|
||||||
msg += QString::fromUtf8 (" %1\n").arg (*f);
|
|
||||||
}
|
|
||||||
msg += tr ("\nReload these files?");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<QString> changed_files;
|
std::vector<QString> changed_files;
|
||||||
changed_files.swap (m_changed_files);
|
changed_files.swap (m_changed_files);
|
||||||
|
|
||||||
if (QMessageBox::question (this, tr ("Reload Files"), msg, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
std::map<QString, std::vector<std::pair<lay::LayoutViewWidget *, int> > > views_per_file;
|
||||||
|
|
||||||
m_file_changed_timer.blockSignals (false);
|
for (auto v = mp_views.begin (); v != mp_views.end (); ++v) {
|
||||||
|
for (int cv = 0; cv < int ((*v)->view ()->cellviews ()); ++cv) {
|
||||||
std::map<QString, std::pair<lay::LayoutViewWidget *, int> > views_per_file;
|
views_per_file [tl::to_qstring ((*v)->view ()->cellview (cv)->filename ())].push_back (std::make_pair (*v, cv));
|
||||||
|
|
||||||
for (std::vector<lay::LayoutViewWidget *>::iterator v = mp_views.begin (); v != mp_views.end (); ++v) {
|
|
||||||
for (int cv = 0; cv < int ((*v)->view ()->cellviews ()); ++cv) {
|
|
||||||
views_per_file [tl::to_qstring ((*v)->view ()->cellview (cv)->filename ())] = std::make_pair (*v, cv);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::vector<QString>::const_iterator f = changed_files.begin (); f != changed_files.end (); ++f) {
|
|
||||||
std::map<QString, std::pair<lay::LayoutViewWidget *, int> >::const_iterator v = views_per_file.find (*f);
|
|
||||||
if (v != views_per_file.end ()) {
|
|
||||||
v->second.first->view ()->reload_layout (v->second.second);
|
|
||||||
reloaded_files.insert (*f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
END_PROTECTED
|
for (auto f = changed_files.begin (); f != changed_files.end (); ++f) {
|
||||||
|
|
||||||
m_file_changed_timer.blockSignals (false);
|
auto v = views_per_file.find (*f);
|
||||||
|
if (v != views_per_file.end ()) {
|
||||||
|
|
||||||
// While the message box was open, new request might have collected - remove
|
for (auto w = v->second.begin (); w != v->second.end (); ++w) {
|
||||||
// the ones we just reloaded and restart the timer
|
|
||||||
if (! m_changed_files.empty ()) {
|
std::string title;
|
||||||
|
if (w->first->view ()->cellviews () > 1) {
|
||||||
|
title = tl::sprintf (tl::to_string (tr ("Layout file @%d (%s) has changed on disk")), w->second + 1, tl::filename (tl::to_string (*f)));
|
||||||
|
} else {
|
||||||
|
title = tl::sprintf (tl::to_string (tr ("Layout file (%s) has changed on disk")), tl::filename (tl::to_string (*f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
lay::LayoutViewNotification n ("reload", title, tl::Variant (tl::to_string (*f)));
|
||||||
|
n.add_action ("reload", tl::to_string (tr ("Reload")));
|
||||||
|
w->first->add_notification (n);
|
||||||
|
|
||||||
std::vector<QString> changed_files;
|
|
||||||
changed_files.swap (m_changed_files);
|
|
||||||
for (std::vector<QString>::const_iterator f = changed_files.begin (); f != changed_files.end (); ++f) {
|
|
||||||
if (reloaded_files.find (*f) == reloaded_files.end ()) {
|
|
||||||
m_changed_files.push_back (*f);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (! m_changed_files.empty ()) {
|
|
||||||
|
|
||||||
// Wait a little to let more to allow for more reload requests to collect
|
|
||||||
m_file_changed_timer.setInterval (300);
|
|
||||||
m_file_changed_timer.start ();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
#include "tlInternational.h"
|
#include "tlInternational.h"
|
||||||
#include "tlExpression.h"
|
#include "tlExpression.h"
|
||||||
|
|
@ -76,6 +78,7 @@
|
||||||
#include "layEditorOptionsPages.h"
|
#include "layEditorOptionsPages.h"
|
||||||
#include "layUtils.h"
|
#include "layUtils.h"
|
||||||
#include "layPropertiesDialog.h"
|
#include "layPropertiesDialog.h"
|
||||||
|
#include "layQtTools.h"
|
||||||
#include "dbClipboard.h"
|
#include "dbClipboard.h"
|
||||||
#include "dbLayout.h"
|
#include "dbLayout.h"
|
||||||
#include "dbLayoutUtils.h"
|
#include "dbLayoutUtils.h"
|
||||||
|
|
@ -94,12 +97,70 @@
|
||||||
namespace lay
|
namespace lay
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
// LayoutViewNotificationWidget implementation
|
||||||
|
|
||||||
|
LayoutViewNotificationWidget::LayoutViewNotificationWidget (LayoutViewWidget *parent, const LayoutViewNotification *notification)
|
||||||
|
: QFrame (parent), mp_parent (parent), mp_notification (notification)
|
||||||
|
{
|
||||||
|
setBackgroundRole (QPalette::ToolTipBase);
|
||||||
|
setAutoFillBackground (true);
|
||||||
|
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout (this);
|
||||||
|
layout->setContentsMargins (4, 4, 4, 4);
|
||||||
|
|
||||||
|
QLabel *title_label = new QLabel (this);
|
||||||
|
layout->addWidget (title_label, 1);
|
||||||
|
title_label->setText (tl::to_qstring (notification->title ()));
|
||||||
|
title_label->setForegroundRole (QPalette::ToolTipText);
|
||||||
|
title_label->setWordWrap (true);
|
||||||
|
activate_help_links (title_label);
|
||||||
|
|
||||||
|
for (auto a = notification->actions ().begin (); a != notification->actions ().end (); ++a) {
|
||||||
|
|
||||||
|
QPushButton *pb = new QPushButton (this);
|
||||||
|
layout->addWidget (pb);
|
||||||
|
|
||||||
|
pb->setText (tl::to_qstring (a->second));
|
||||||
|
m_action_buttons.insert (std::make_pair (pb, a->first));
|
||||||
|
connect (pb, SIGNAL (clicked ()), this, SLOT (action_triggered ()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QToolButton *close_button = new QToolButton ();
|
||||||
|
close_button->setIcon (QIcon (":clear_edit_16px.png"));
|
||||||
|
close_button->setAutoRaise (true);
|
||||||
|
layout->addWidget (close_button);
|
||||||
|
|
||||||
|
connect (close_button, SIGNAL (clicked ()), this, SLOT (close_triggered ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LayoutViewNotificationWidget::action_triggered ()
|
||||||
|
{
|
||||||
|
auto a = m_action_buttons.find (sender ());
|
||||||
|
if (a != m_action_buttons.end ()) {
|
||||||
|
mp_parent->notification_action (*mp_notification, a->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LayoutViewNotificationWidget::close_triggered ()
|
||||||
|
{
|
||||||
|
mp_parent->remove_notification (*mp_notification);
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// LayoutViewWidget implementation
|
// LayoutViewWidget implementation
|
||||||
|
|
||||||
LayoutViewWidget::LayoutViewWidget (db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options)
|
LayoutViewWidget::LayoutViewWidget (db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options)
|
||||||
: QFrame (parent), mp_view (0)
|
: QFrame (parent), mp_view (0)
|
||||||
{
|
{
|
||||||
|
mp_layout = new QVBoxLayout (this);
|
||||||
|
mp_layout->setContentsMargins (0, 0, 0, 0);
|
||||||
|
mp_layout->setSpacing (0);
|
||||||
|
mp_layout->addStretch (1);
|
||||||
|
|
||||||
// NOTE: construction the LayoutView may trigger events (script code executed etc.) which must
|
// NOTE: construction the LayoutView may trigger events (script code executed etc.) which must
|
||||||
// not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only
|
// not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only
|
||||||
// after the LayoutView is successfully constructed.
|
// after the LayoutView is successfully constructed.
|
||||||
|
|
@ -110,6 +171,11 @@ LayoutViewWidget::LayoutViewWidget (db::Manager *mgr, bool editable, lay::Plugin
|
||||||
LayoutViewWidget::LayoutViewWidget (lay::LayoutView *source, db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options)
|
LayoutViewWidget::LayoutViewWidget (lay::LayoutView *source, db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options)
|
||||||
: QFrame (parent), mp_view (0)
|
: QFrame (parent), mp_view (0)
|
||||||
{
|
{
|
||||||
|
mp_layout = new QVBoxLayout (this);
|
||||||
|
mp_layout->setContentsMargins (0, 0, 0, 0);
|
||||||
|
mp_layout->setSpacing (0);
|
||||||
|
mp_layout->addStretch (1);
|
||||||
|
|
||||||
// NOTE: construction the LayoutView may trigger events (script code executed etc.) which must
|
// NOTE: construction the LayoutView may trigger events (script code executed etc.) which must
|
||||||
// not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only
|
// not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only
|
||||||
// after the LayoutView is successfully constructed.
|
// after the LayoutView is successfully constructed.
|
||||||
|
|
@ -124,6 +190,55 @@ LayoutViewWidget::~LayoutViewWidget ()
|
||||||
delete view;
|
delete view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LayoutViewWidget::add_notification (const LayoutViewNotification ¬ificaton)
|
||||||
|
{
|
||||||
|
if (m_notification_widgets.find (¬ificaton) == m_notification_widgets.end ()) {
|
||||||
|
m_notifications.push_back (notificaton);
|
||||||
|
QWidget *w = new LayoutViewNotificationWidget (this, &m_notifications.back ());
|
||||||
|
m_notification_widgets.insert (std::make_pair (&m_notifications.back (), w));
|
||||||
|
mp_layout->insertWidget (0, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LayoutViewWidget::remove_notification (const LayoutViewNotification ¬ification)
|
||||||
|
{
|
||||||
|
auto nw = m_notification_widgets.find (¬ification);
|
||||||
|
if (nw != m_notification_widgets.end ()) {
|
||||||
|
|
||||||
|
nw->second->deleteLater ();
|
||||||
|
m_notification_widgets.erase (nw);
|
||||||
|
|
||||||
|
for (auto n = m_notifications.begin (); n != m_notifications.end (); ++n) {
|
||||||
|
if (*n == notification) {
|
||||||
|
m_notifications.erase (n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LayoutViewWidget::notification_action (const LayoutViewNotification ¬ification, const std::string &action)
|
||||||
|
{
|
||||||
|
if (action == "reload") {
|
||||||
|
|
||||||
|
std::string fn = notification.parameter ().to_string ();
|
||||||
|
|
||||||
|
for (unsigned int cvi = 0; cvi < mp_view->cellviews (); ++cvi) {
|
||||||
|
const lay::CellView &cv = mp_view->cellview (cvi);
|
||||||
|
if (cv->filename () == fn) {
|
||||||
|
mp_view->reload_layout (cvi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_notification (notification);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LayoutViewWidget::view_deleted (lay::LayoutView *view)
|
LayoutViewWidget::view_deleted (lay::LayoutView *view)
|
||||||
{
|
{
|
||||||
|
|
@ -135,6 +250,14 @@ LayoutViewWidget::view_deleted (lay::LayoutView *view)
|
||||||
mp_view = new LayoutView (view->manager (), view->is_editable (), view->plugin_parent (), this, view->options ());
|
mp_view = new LayoutView (view->manager (), view->is_editable (), view->plugin_parent (), this, view->options ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LayoutViewWidget::resizeEvent (QResizeEvent *)
|
||||||
|
{
|
||||||
|
if (mp_view && mp_view->canvas ()) {
|
||||||
|
mp_view->canvas ()->resize (width (), height ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QSize
|
QSize
|
||||||
LayoutViewWidget::sizeHint () const
|
LayoutViewWidget::sizeHint () const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@
|
||||||
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
class QSpinBox;
|
class QSpinBox;
|
||||||
|
|
||||||
|
|
@ -683,6 +684,91 @@ private:
|
||||||
void activate_editor_option_pages ();
|
void activate_editor_option_pages ();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Descriptor for a notification inside the layout view
|
||||||
|
*
|
||||||
|
* Notifications are popups added at the top of the view to indicate need for reloading for example.
|
||||||
|
* Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload).
|
||||||
|
* Actions are mapped to QPushButtons.
|
||||||
|
*/
|
||||||
|
class LAYVIEW_PUBLIC LayoutViewNotification
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LayoutViewNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ())
|
||||||
|
: m_name (name), m_title (title), m_parameter (parameter)
|
||||||
|
{
|
||||||
|
// .. nothing yet ..
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_action (const std::string &name, const std::string &title)
|
||||||
|
{
|
||||||
|
m_actions.push_back (std::make_pair (name, title));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::pair<std::string, std::string> > &actions () const
|
||||||
|
{
|
||||||
|
return m_actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &name () const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &title () const
|
||||||
|
{
|
||||||
|
return m_title;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tl::Variant ¶meter () const
|
||||||
|
{
|
||||||
|
return m_parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const LayoutViewNotification &other) const
|
||||||
|
{
|
||||||
|
if (m_name != other.name ()) {
|
||||||
|
return m_name < other.name ();
|
||||||
|
}
|
||||||
|
return m_parameter < other.parameter ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const LayoutViewNotification &other) const
|
||||||
|
{
|
||||||
|
if (m_name != other.name ()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_parameter == other.parameter ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_title;
|
||||||
|
tl::Variant m_parameter;
|
||||||
|
std::vector<std::pair<std::string, std::string> > m_actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A widget representing a notification
|
||||||
|
*/
|
||||||
|
class LAYVIEW_PUBLIC LayoutViewNotificationWidget
|
||||||
|
: public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LayoutViewNotificationWidget (LayoutViewWidget *parent, const LayoutViewNotification *notification);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void action_triggered ();
|
||||||
|
void close_triggered ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
LayoutViewWidget *mp_parent;
|
||||||
|
const LayoutViewNotification *mp_notification;
|
||||||
|
std::map<QObject *, std::string> m_action_buttons;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The layout view widget
|
* @brief The layout view widget
|
||||||
*
|
*
|
||||||
|
|
@ -710,6 +796,16 @@ public:
|
||||||
*/
|
*/
|
||||||
~LayoutViewWidget ();
|
~LayoutViewWidget ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a notification
|
||||||
|
*/
|
||||||
|
void add_notification (const LayoutViewNotification ¬ificaton);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes a notification
|
||||||
|
*/
|
||||||
|
void remove_notification (const LayoutViewNotification ¬ificaton);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the LayoutView embedded into this widget
|
* @brief Gets the LayoutView embedded into this widget
|
||||||
*/
|
*/
|
||||||
|
|
@ -817,10 +913,25 @@ signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class LayoutView;
|
friend class LayoutView;
|
||||||
|
friend class LayoutViewNotificationWidget;
|
||||||
|
|
||||||
void view_deleted (lay::LayoutView *view);
|
void view_deleted (lay::LayoutView *view);
|
||||||
|
void notification_action (const LayoutViewNotification ¬ification, const std::string &action);
|
||||||
|
|
||||||
|
void resizeEvent (QResizeEvent *event);
|
||||||
|
|
||||||
|
struct CompareNotificationPointers
|
||||||
|
{
|
||||||
|
bool operator() (const LayoutViewNotification *a, const LayoutViewNotification *b) const
|
||||||
|
{
|
||||||
|
return *a < *b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
LayoutView *mp_view;
|
LayoutView *mp_view;
|
||||||
|
QVBoxLayout *mp_layout;
|
||||||
|
std::list<lay::LayoutViewNotification> m_notifications;
|
||||||
|
std::map<const LayoutViewNotification *, QWidget *, CompareNotificationPointers> m_notification_widgets;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue