mirror of https://github.com/KLayout/klayout.git
Non-modal notifications for macro editors too
This commit is contained in:
parent
c4e57d2bce
commit
3d0bb8516b
|
|
@ -2373,6 +2373,15 @@ BEGIN_PROTECTED
|
|||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::close_requested ()
|
||||
{
|
||||
MacroEditorPage *page = dynamic_cast<MacroEditorPage *> (sender ());
|
||||
if (! m_in_exec && page) {
|
||||
tab_close_requested (tabWidget->indexOf (page));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorDialog::tab_close_requested (int index)
|
||||
{
|
||||
|
|
@ -2605,66 +2614,65 @@ BEGIN_PROTECTED
|
|||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> modified_files;
|
||||
for (std::map <lym::Macro *, MacroEditorPage *>::const_iterator m = m_tab_widgets.begin (); m != m_tab_widgets.end (); ++m) {
|
||||
if (m->first->is_modified ()) {
|
||||
modified_files.insert (m->first->path ());
|
||||
std::map<std::string, MacroEditorPage *> path_to_page;
|
||||
for (int i = 0; i < tabWidget->count (); ++i) {
|
||||
MacroEditorPage *page = dynamic_cast<MacroEditorPage *> (tabWidget->widget (i));
|
||||
if (page) {
|
||||
path_to_page.insert (std::make_pair (page->path (), page));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<QString> conflicts;
|
||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
||||
if (modified_files.find (tl::to_string (*f)) != modified_files.end ()) {
|
||||
conflicts.push_back (*f);
|
||||
|
||||
std::string fn = tl::to_string (*f);
|
||||
auto w = path_to_page.find (fn);
|
||||
if (w == path_to_page.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (w->second->macro () && w->second->macro ()->is_modified ()) {
|
||||
|
||||
lay::MacroEditorNotification n ("reload", tl::to_string (tr ("Macro has changed on disk, but was modified")), tl::Variant (fn));
|
||||
n.add_action ("reload", tl::to_string (tr ("Reload and discard changes")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
} else {
|
||||
|
||||
lay::MacroEditorNotification n ("reload", tl::to_string (tr ("Macro has changed on disk")), tl::Variant (fn));
|
||||
n.add_action ("reload", tl::to_string (tr ("Reload")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<QString>::const_iterator f = m_removed_files.begin (); f != m_removed_files.end (); ++f) {
|
||||
if (modified_files.find (tl::to_string (*f)) != modified_files.end ()) {
|
||||
conflicts.push_back (*f);
|
||||
|
||||
std::string fn = tl::to_string (*f);
|
||||
auto w = path_to_page.find (fn);
|
||||
if (w == path_to_page.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (w->second->macro () && w->second->macro ()->is_modified ()) {
|
||||
|
||||
lay::MacroEditorNotification n ("close", tl::to_string (tr ("Macro has been removed on disk, but was modified")), tl::Variant (fn));
|
||||
n.add_action ("close", tl::to_string (tr ("Close tab and discard changes")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
} else {
|
||||
|
||||
lay::MacroEditorNotification n ("close", tl::to_string (tr ("Macro has been removed on disk")), tl::Variant (fn));
|
||||
n.add_action ("close", tl::to_string (tr ("Close tab")));
|
||||
w->second->add_notification (n);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
QString msg;
|
||||
|
||||
if (m_changed_files.size () + m_removed_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 (modified)\n").arg (*f);
|
||||
}
|
||||
for (std::vector<QString>::const_iterator f = m_removed_files.begin (); f != m_removed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (removed)\n").arg (*f);
|
||||
}
|
||||
if (!conflicts.empty ()) {
|
||||
msg += tr ("\nThis file has been modified in the editor as well.\nRefresh this file and discard changes?");
|
||||
} else {
|
||||
msg += tr ("\nRefresh 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 (modified)\n").arg (*f);
|
||||
}
|
||||
for (std::vector<QString>::const_iterator f = m_removed_files.begin (); f != m_removed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (removed)\n").arg (*f);
|
||||
}
|
||||
if (!conflicts.empty ()) {
|
||||
msg += tr("\nSome of these files are modified on disk and in the editor:\n\n");
|
||||
for (std::vector<QString>::const_iterator f = conflicts.begin (); f != conflicts.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1 (conflict)\n").arg (*f);
|
||||
}
|
||||
msg += tr ("\nRefresh these and the other files and discard all changes?");
|
||||
} else {
|
||||
msg += tr ("\nRefresh those files?");
|
||||
}
|
||||
}
|
||||
refresh_file_watcher ();
|
||||
|
||||
m_changed_files.clear ();
|
||||
m_removed_files.clear ();
|
||||
|
||||
if (QMessageBox::question (this, tr ("Refresh Files"), msg, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
reload_macros ();
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
}
|
||||
|
||||
|
|
@ -3523,6 +3531,7 @@ MacroEditorDialog::create_page (lym::Macro *macro)
|
|||
editor->set_font (m_font_family, m_font_size);
|
||||
editor->exec_model ()->set_run_mode (m_in_exec);
|
||||
editor->connect_macro (macro);
|
||||
connect (editor.get (), SIGNAL (close_requested ()), this, SLOT (close_requested ()));
|
||||
connect (editor.get (), SIGNAL (help_requested (const QString &)), this, SLOT (help_requested (const QString &)));
|
||||
connect (editor.get (), SIGNAL (search_requested (const QString &, bool)), this, SLOT (search_requested (const QString &, bool)));
|
||||
connect (editor.get (), SIGNAL (edit_trace (bool)), this, SLOT (add_edit_trace (bool)));
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ private slots:
|
|||
void search_editing ();
|
||||
void search_finished ();
|
||||
void tab_close_requested (int);
|
||||
void close_requested ();
|
||||
void close_all ();
|
||||
void close_all_but_this ();
|
||||
void close_all_left ();
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
#include <QTimer>
|
||||
#include <QListWidget>
|
||||
#include <QApplication>
|
||||
#include <QToolButton>
|
||||
#include <QPushButton>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
|
@ -81,6 +83,59 @@ void MacroEditorTextWidget::paintEvent (QPaintEvent *event)
|
|||
return TextEditWidget::paintEvent (event);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// MacroEditorNotificationWidget implementation
|
||||
|
||||
MacroEditorNotificationWidget::MacroEditorNotificationWidget (MacroEditorPage *parent, const MacroEditorNotification *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
|
||||
MacroEditorNotificationWidget::action_triggered ()
|
||||
{
|
||||
auto a = m_action_buttons.find (sender ());
|
||||
if (a != m_action_buttons.end ()) {
|
||||
mp_parent->notification_action (*mp_notification, a->second);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorNotificationWidget::close_triggered ()
|
||||
{
|
||||
mp_parent->remove_notification (*mp_notification);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// MacroEditorHighlighters implementation
|
||||
|
||||
|
|
@ -491,15 +546,17 @@ void MacroEditorSidePanel::paintEvent (QPaintEvent *)
|
|||
MacroEditorPage::MacroEditorPage (QWidget * /*parent*/, MacroEditorHighlighters *highlighters)
|
||||
: mp_macro (0), mp_highlighters (highlighters), mp_highlighter (0), m_error_line (-1), m_ntab (8), m_nindent (2), m_ignore_cursor_changed_event (false)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout (this);
|
||||
|
||||
mp_layout = new QVBoxLayout (this);
|
||||
mp_layout->setContentsMargins (0, 0, 0, 0);
|
||||
|
||||
mp_readonly_label = new QLabel (this);
|
||||
mp_readonly_label->setText (QObject::tr ("Macro is read-only and cannot be edited"));
|
||||
mp_readonly_label->hide ();
|
||||
layout->addWidget (mp_readonly_label);
|
||||
mp_layout->addWidget (mp_readonly_label);
|
||||
|
||||
QHBoxLayout *hlayout = new QHBoxLayout ();
|
||||
layout->addLayout (hlayout);
|
||||
hlayout->setContentsMargins (4, 4, 4, 4);
|
||||
mp_layout->addLayout (hlayout);
|
||||
|
||||
mp_exec_model = new MacroEditorExecutionModel (this);
|
||||
mp_text = new MacroEditorTextWidget (this);
|
||||
|
|
@ -1021,6 +1078,8 @@ void MacroEditorPage::connect_macro (lym::Macro *macro)
|
|||
|
||||
if (mp_macro) {
|
||||
|
||||
m_path = mp_macro->path ();
|
||||
|
||||
connect (mp_macro, SIGNAL (changed ()), this, SLOT (update ()));
|
||||
|
||||
lym::Macro::Interpreter lang = macro->interpreter ();
|
||||
|
|
@ -1924,5 +1983,54 @@ MacroEditorPage::eventFilter (QObject *watched, QEvent *event)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorPage::add_notification (const MacroEditorNotification ¬ification)
|
||||
{
|
||||
if (m_notification_widgets.find (¬ification) == m_notification_widgets.end ()) {
|
||||
m_notifications.push_back (notification);
|
||||
QWidget *w = new MacroEditorNotificationWidget (this, &m_notifications.back ());
|
||||
m_notification_widgets.insert (std::make_pair (&m_notifications.back (), w));
|
||||
mp_layout->insertWidget (0, w);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroEditorPage::remove_notification (const MacroEditorNotification ¬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
|
||||
MacroEditorPage::notification_action (const MacroEditorNotification ¬ification, const std::string &action)
|
||||
{
|
||||
if (action == "close") {
|
||||
|
||||
remove_notification (notification);
|
||||
|
||||
emit close_requested ();
|
||||
|
||||
} else if (action == "reload") {
|
||||
|
||||
remove_notification (notification);
|
||||
|
||||
mp_macro->load ();
|
||||
mp_macro->reset_modified ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "lymMacro.h"
|
||||
#include "layGenericSyntaxHighlighter.h"
|
||||
#include "tlVariant.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPixmap>
|
||||
|
|
@ -47,10 +48,13 @@ class QSyntaxHighlighter;
|
|||
class QTimer;
|
||||
class QWindow;
|
||||
class QListWidget;
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class MacroEditorPage;
|
||||
|
||||
/**
|
||||
* @brief A collection of highlighters
|
||||
*/
|
||||
|
|
@ -209,7 +213,92 @@ private:
|
|||
bool m_debugging_on;
|
||||
};
|
||||
|
||||
class MacroEditorPage
|
||||
/**
|
||||
* @brief Descriptor for a notification inside the macro editor
|
||||
*
|
||||
* 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 MacroEditorNotification
|
||||
{
|
||||
public:
|
||||
MacroEditorNotification (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 MacroEditorNotification &other) const
|
||||
{
|
||||
if (m_name != other.name ()) {
|
||||
return m_name < other.name ();
|
||||
}
|
||||
return m_parameter < other.parameter ();
|
||||
}
|
||||
|
||||
bool operator==(const MacroEditorNotification &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 MacroEditorNotificationWidget
|
||||
: public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroEditorNotificationWidget (MacroEditorPage *parent, const MacroEditorNotification *notification);
|
||||
|
||||
private slots:
|
||||
void action_triggered ();
|
||||
void close_triggered ();
|
||||
|
||||
private:
|
||||
MacroEditorPage *mp_parent;
|
||||
const MacroEditorNotification *mp_notification;
|
||||
std::map<QObject *, std::string> m_action_buttons;
|
||||
};
|
||||
|
||||
class MacroEditorPage
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -224,6 +313,11 @@ public:
|
|||
return mp_macro;
|
||||
}
|
||||
|
||||
const std::string path () const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
bool is_modified () const
|
||||
{
|
||||
return m_is_modified;
|
||||
|
|
@ -269,10 +363,14 @@ public:
|
|||
|
||||
void set_editor_focus ();
|
||||
|
||||
void add_notification (const MacroEditorNotification ¬ificaton);
|
||||
void remove_notification (const MacroEditorNotification ¬ificaton);
|
||||
|
||||
signals:
|
||||
void help_requested (const QString &s);
|
||||
void search_requested (const QString &s, bool backward);
|
||||
void edit_trace (bool);
|
||||
void close_requested ();
|
||||
|
||||
public slots:
|
||||
void commit ();
|
||||
|
|
@ -288,10 +386,22 @@ protected slots:
|
|||
void hide_completer ();
|
||||
|
||||
private:
|
||||
friend class MacroEditorNotificationWidget;
|
||||
|
||||
struct CompareNotificationPointers
|
||||
{
|
||||
bool operator() (const MacroEditorNotification *a, const MacroEditorNotification *b) const
|
||||
{
|
||||
return *a < *b;
|
||||
}
|
||||
};
|
||||
|
||||
lym::Macro *mp_macro;
|
||||
std::string m_path;
|
||||
MacroEditorExecutionModel *mp_exec_model;
|
||||
MacroEditorTextWidget *mp_text;
|
||||
MacroEditorSidePanel *mp_side_panel;
|
||||
QVBoxLayout *mp_layout;
|
||||
QLabel *mp_readonly_label;
|
||||
bool m_is_modified;
|
||||
MacroEditorHighlighters *mp_highlighters;
|
||||
|
|
@ -305,6 +415,8 @@ private:
|
|||
QTimer *mp_completer_timer;
|
||||
QWidget *mp_completer_popup;
|
||||
QListWidget *mp_completer_list;
|
||||
std::list<MacroEditorNotification> m_notifications;
|
||||
std::map<const MacroEditorNotification *, QWidget *, CompareNotificationPointers> m_notification_widgets;
|
||||
|
||||
void update_extra_selections ();
|
||||
bool return_pressed ();
|
||||
|
|
@ -316,6 +428,7 @@ private:
|
|||
QTextCursor get_completer_cursor (int &pos0, int &pos);
|
||||
bool select_match_here ();
|
||||
void replace_in_selection (const QString &replace, bool first);
|
||||
void notification_action (const MacroEditorNotification ¬ification, const std::string &action);
|
||||
|
||||
bool eventFilter (QObject *watched, QEvent *event);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue