From 801ef78990738f36cc2a838bd695764696f26db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Mon, 1 Aug 2022 18:49:42 +0200 Subject: [PATCH] Fixed issue-1131 (do not show non-existing files in MRU lists) (#1133) * Fixed issue-1131 (do not show files in MRU lists which do no longer exist) The solution consists of an extension of the Action system allowing to dynamically hide or disable items. This currently works for menu items only. This feature is used to dynamically *disable* (as of now, not hiding) items from the four MRU lists corresponding to non-existing files. In addition, a "clear list" menu has been added to the MRU lists. * Small enhancement: file names can be URIs --- src/lay/lay/layMainWindow.cc | 148 +++++++++++++---------- src/lay/lay/layMainWindow.h | 4 + src/laybasic/laybasic/gsiDeclLayMenu.cc | 46 ++++++- src/laybasic/laybasic/layAbstractMenu.cc | 108 ++++++++++++----- src/laybasic/laybasic/layAbstractMenu.h | 36 +++++- testdata/ruby/layMenuTest.rb | 50 +++++++- 6 files changed, 296 insertions(+), 96 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index be6e1bb57..129c913fc 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -51,6 +51,8 @@ #include "tlStream.h" #include "tlExceptions.h" #include "tlExpression.h" +#include "tlFileUtils.h" +#include "tlUri.h" #include "dbMemStatistics.h" #include "dbManager.h" #include "dbStream.h" @@ -2902,29 +2904,28 @@ MainWindow::add_mru (const std::string &fn_rel) add_mru (fn_rel, m_initial_technology); } -const size_t max_mru = 16; +const size_t max_mru = 16; // TODO: make configurable? void MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) { - std::vector > new_mru (m_mru); + std::vector > new_mru; std::string fn (tl::InputStream::absolute_path (fn_rel)); - for (std::vector >::iterator mru = new_mru.begin (); mru != new_mru.end (); ++mru) { - if (mru->first == fn) { - new_mru.erase (mru); - break; + for (auto mru = m_mru.begin (); mru != m_mru.end (); ++mru) { + if (mru->first != fn /* delete non-existing files: && tl::is_readable (mru->first) */) { + new_mru.push_back (*mru); } } new_mru.push_back (std::make_pair (fn, tech)); if (new_mru.size () > max_mru) { - new_mru.erase (new_mru.begin ()); + new_mru.erase (new_mru.begin (), new_mru.end () - max_mru); } std::string config_str; - for (std::vector >::const_iterator mru = new_mru.begin (); mru != new_mru.end (); ++mru) { + for (auto mru = new_mru.begin (); mru != new_mru.end (); ++mru) { if (! config_str.empty ()) { config_str += " "; } @@ -2952,20 +2953,19 @@ MainWindow::add_to_other_mru (const std::string &fn_rel, const std::string &cfg) tl_assert (false); } - std::vector new_mru = *mru_ptr; + std::vector new_mru; std::string fn (tl::InputStream::absolute_path (fn_rel)); - for (std::vector::iterator mru = new_mru.begin (); mru != new_mru.end (); ++mru) { - if (*mru == fn) { - new_mru.erase (mru); - break; + for (auto mru = mru_ptr->begin (); mru != mru_ptr->end (); ++mru) { + if (*mru != fn /* delete non-existing files: && tl::is_readable (*mru) */) { + new_mru.push_back (*mru); } } new_mru.push_back (fn); if (new_mru.size () > max_mru) { - new_mru.erase (new_mru.begin ()); + new_mru.erase (new_mru.begin (), new_mru.end () - max_mru); } std::string config_str; @@ -2986,72 +2986,45 @@ class OpenRecentAction : public lay::Action { public: - OpenRecentAction (lay::MainWindow *mw, size_t n) - : lay::Action (), mp_mw (mw), m_n (n) + OpenRecentAction (lay::MainWindow *mw, size_t n, void (lay::MainWindow::*open_meth) (size_t), bool (lay::MainWindow::*avail_meth) (size_t)) + : lay::Action (), mp_mw (mw), m_n (n), m_open_meth (open_meth), m_avail_meth (avail_meth) { } void triggered () { - mp_mw->open_recent (m_n); + (mp_mw->*m_open_meth) (m_n); + } + + bool wants_enabled () const + { + return (mp_mw->*m_avail_meth) (m_n); } private: lay::MainWindow *mp_mw; size_t m_n; + void (lay::MainWindow::*m_open_meth) (size_t); + bool (lay::MainWindow::*m_avail_meth) (size_t); }; -class OpenRecentSessionAction +class ClearRecentAction : public lay::Action { public: - OpenRecentSessionAction (lay::MainWindow *mw, size_t n) - : lay::Action (), mp_mw (mw), m_n (n) - { } + ClearRecentAction (lay::MainWindow *mw, const std::string &cfg) + : lay::Action (), mp_mw (mw), m_cfg (cfg) + { + set_title (tl::to_string (tr ("Clear List"))); + } void triggered () { - mp_mw->open_recent_session (m_n); + mp_mw->configure (m_cfg, std::string ()); } private: lay::MainWindow *mp_mw; - size_t m_n; -}; - -class OpenRecentLayerPropertiesAction - : public lay::Action -{ -public: - OpenRecentLayerPropertiesAction (lay::MainWindow *mw, size_t n) - : lay::Action (), mp_mw (mw), m_n (n) - { } - - void triggered () - { - mp_mw->open_recent_layer_properties (m_n); - } - -private: - lay::MainWindow *mp_mw; - size_t m_n; -}; - -class OpenRecentBookmarksAction - : public lay::Action -{ -public: - OpenRecentBookmarksAction (lay::MainWindow *mw, size_t n) - : lay::Action (), mp_mw (mw), m_n (n) - { } - - void triggered () - { - mp_mw->open_recent_bookmarks (m_n); - } - -private: - lay::MainWindow *mp_mw; - size_t m_n; + std::string m_cfg; }; } @@ -3074,11 +3047,14 @@ MainWindow::do_update_mru_menus () for (std::vector >::iterator mru = m_mru.end (); mru != m_mru.begin (); ) { --mru; size_t i = std::distance (m_mru.begin (), mru); - Action *action = new OpenRecentAction (this, i); + Action *action = new OpenRecentAction (this, i, &lay::MainWindow::open_recent, &lay::MainWindow::is_available_recent); action->set_title (mru->first); menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); } + menu ()->insert_separator (mru_menu + ".end", "clear_sep"); + menu ()->insert_item (mru_menu + ".end", "clear_recent", new ClearRecentAction (this, cfg_mru)); + } else { open_recent_action->set_enabled (false); } @@ -3100,11 +3076,14 @@ MainWindow::do_update_mru_menus () for (std::vector::iterator mru = m_mru_sessions.end (); mru != m_mru_sessions.begin (); ) { --mru; size_t i = std::distance (m_mru_sessions.begin (), mru); - Action *action = new OpenRecentSessionAction (this, i); + Action *action = new OpenRecentAction (this, i, &lay::MainWindow::open_recent_session, &lay::MainWindow::is_available_recent_session); action->set_title (*mru); menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); } + menu ()->insert_separator (mru_menu + ".end", "clear_sep"); + menu ()->insert_item (mru_menu + ".end", "clear_recent", new ClearRecentAction (this, cfg_mru_sessions)); + } else { open_recent_action->set_enabled (false); } @@ -3126,11 +3105,14 @@ MainWindow::do_update_mru_menus () for (std::vector::iterator mru = m_mru_layer_properties.end (); mru != m_mru_layer_properties.begin (); ) { --mru; size_t i = std::distance (m_mru_layer_properties.begin (), mru); - Action *action = new OpenRecentLayerPropertiesAction (this, i); + Action *action = new OpenRecentAction (this, i, &lay::MainWindow::open_recent_layer_properties, &lay::MainWindow::is_available_recent_layer_properties); action->set_title (*mru); menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); } + menu ()->insert_separator (mru_menu + ".end", "clear_sep"); + menu ()->insert_item (mru_menu + ".end", "clear_recent", new ClearRecentAction (this, cfg_mru_layer_properties)); + } else { open_recent_action->set_enabled (false); } @@ -3152,11 +3134,14 @@ MainWindow::do_update_mru_menus () for (std::vector::iterator mru = m_mru_bookmarks.end (); mru != m_mru_bookmarks.begin (); ) { --mru; size_t i = std::distance (m_mru_bookmarks.begin (), mru); - Action *action = new OpenRecentBookmarksAction (this, i); + Action *action = new OpenRecentAction (this, i, &lay::MainWindow::open_recent_bookmarks, &lay::MainWindow::is_available_recent_bookmarks); action->set_title (*mru); menu ()->insert_item (mru_menu + ".end", tl::sprintf ("open_recent_%d", i + 1), action); } + menu ()->insert_separator (mru_menu + ".end", "clear_sep"); + menu ()->insert_item (mru_menu + ".end", "clear_recent", new ClearRecentAction (this, cfg_mru_bookmarks)); + } else { open_recent_action->set_enabled (false); } @@ -3218,6 +3203,25 @@ MainWindow::open_recent (size_t n) END_PROTECTED } +static bool +is_file_available (const std::string &fn) +{ + tl::URI uri (fn); + if (uri.scheme ().empty ()) { + return tl::is_readable (fn); + } else if (uri.scheme () == "file") { + return tl::is_readable (uri.path ()); + } else { + return true; + } +} + +bool +MainWindow::is_available_recent (size_t n) +{ + return (n < m_mru.size () && is_file_available (m_mru [n].first)); +} + void MainWindow::open_recent_session (size_t n) { @@ -3232,6 +3236,12 @@ MainWindow::open_recent_session (size_t n) END_PROTECTED } +bool +MainWindow::is_available_recent_session (size_t n) +{ + return (n < m_mru_sessions.size () && is_file_available (m_mru_sessions [n])); +} + void MainWindow::open_recent_layer_properties (size_t n) { @@ -3246,6 +3256,12 @@ MainWindow::open_recent_layer_properties (size_t n) END_PROTECTED } +bool +MainWindow::is_available_recent_layer_properties (size_t n) +{ + return (n < m_mru_layer_properties.size () && is_file_available (m_mru_layer_properties [n])); +} + void MainWindow::open_recent_bookmarks (size_t n) { @@ -3264,6 +3280,12 @@ MainWindow::open_recent_bookmarks (size_t n) END_PROTECTED } +bool +MainWindow::is_available_recent_bookmarks (size_t n) +{ + return (n < m_mru_bookmarks.size () && is_file_available (m_mru_bookmarks [n])); +} + void MainWindow::open (int mode) { diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 362894ab2..21f6d9996 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -637,6 +637,10 @@ public slots: void open_recent_session (size_t n); void open_recent_layer_properties (size_t n); void open_recent_bookmarks (size_t n); + bool is_available_recent(size_t n); + bool is_available_recent_session (size_t n); + bool is_available_recent_layer_properties (size_t n); + bool is_available_recent_bookmarks (size_t n); void view_selected (int index); void view_title_changed (lay::LayoutView *view); diff --git a/src/laybasic/laybasic/gsiDeclLayMenu.cc b/src/laybasic/laybasic/gsiDeclLayMenu.cc index 559ddf8e7..51f3c244e 100644 --- a/src/laybasic/laybasic/gsiDeclLayMenu.cc +++ b/src/laybasic/laybasic/gsiDeclLayMenu.cc @@ -39,7 +39,27 @@ public: on_triggered_event (); } + virtual bool wants_visible () const + { + if (wants_visible_cb.can_issue ()) { + return wants_visible_cb.issue (&lay::Action::wants_visible); + } else { + return true; + } + } + + virtual bool wants_enabled () const + { + if (wants_enabled_cb.can_issue ()) { + return wants_enabled_cb.issue (&lay::Action::wants_enabled); + } else { + return true; + } + } + gsi::Callback triggered_cb; + gsi::Callback wants_visible_cb; + gsi::Callback wants_enabled_cb; tl::Event on_triggered_event; }; @@ -291,10 +311,16 @@ Class decl_ActionBase ("lay", "ActionBase", ) + method ("is_effective_visible?", &lay::Action::is_effective_visible, "@brief Gets a value indicating whether the item is really visible\n" - "This is the combined visibility from \\is_visible? and \\is_hidden?." + "This is the combined visibility from \\is_visible? and \\is_hidden? and dynamic visibility (\\wants_visible)." "\n" "This attribute has been introduced in version 0.25.\n" ) + + method ("is_effective_enabled?", &lay::Action::is_effective_enabled, + "@brief Gets a value indicating whether the item is really enabled\n" + "This is the combined value from \\is_enabled? and dynamic value (\\wants_enabled)." + "\n" + "This attribute has been introduced in version 0.28.\n" + ) + method ("separator=", &lay::Action::set_separator, gsi::arg ("separator"), "@brief Makes an item a separator or not\n" "\n" @@ -368,6 +394,24 @@ Class decl_Action (decl_ActionBase, "lay", "Action", gsi::callback ("triggered", &ActionStub::triggered, &ActionStub::triggered_cb, "@brief This method is called if the menu item is selected" ) + + gsi::callback ("wants_visible", &ActionStub::wants_visible, &ActionStub::wants_visible_cb, + "@brief Returns a value whether the action wants to become visible\n" + "This is a dynamic query for visibility which the system uses to dynamically show or hide " + "menu items, for example in the MRU lists. This visibility information is evaluated in addition " + "to \\is_visible? and \\is_hidden? and contributes to the effective visibility status from " + "\\is_effective_visible?.\n" + "\n" + "This feature has been introduced in version 0.28.\n" + ) + + gsi::callback ("wants_enabled", &ActionStub::wants_enabled, &ActionStub::wants_enabled_cb, + "@brief Returns a value whether the action wants to become enabled\n" + "This is a dynamic query for enabled state which the system uses to dynamically show or hide " + "menu items. This information is evaluated in addition " + "to \\is_enabled? and contributes to the effective enabled status from " + "\\is_effective_enabled?.\n" + "\n" + "This feature has been introduced in version 0.28.\n" + ) + gsi::event ("on_triggered", &ActionStub::on_triggered_event, "@brief This event is called if the menu item is selected\n" "\n" diff --git a/src/laybasic/laybasic/layAbstractMenu.cc b/src/laybasic/laybasic/layAbstractMenu.cc index 9e264218d..8a6f6888c 100644 --- a/src/laybasic/laybasic/layAbstractMenu.cc +++ b/src/laybasic/laybasic/layAbstractMenu.cc @@ -387,7 +387,7 @@ Action::Action () : #if defined(HAVE_QT) // catch the destroyed signal to tell if the QAction object is deleted. if (mp_action) { - connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (destroyed (QObject *))); + connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *))); connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ())); } #endif @@ -413,7 +413,7 @@ Action::Action (QAction *action, bool owned) sp_actionHandles->insert (this); // catch the destroyed signal to tell if the QAction object is deleted. - connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (destroyed (QObject *))); + connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *))); connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ())); } @@ -436,7 +436,8 @@ Action::Action (QMenu *menu, bool owned) sp_actionHandles->insert (this); // catch the destroyed signal to tell if the QAction object is deleted. - connect (mp_menu, SIGNAL (destroyed (QObject *)), this, SLOT (destroyed (QObject *))); + connect (mp_menu, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *))); + connect (mp_menu, SIGNAL (aboutToShow ()), this, SLOT (menu_about_to_show ())); connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ())); } #endif @@ -466,7 +467,7 @@ Action::Action (const std::string &title) : #if defined(HAVE_QT) // catch the destroyed signal to tell if the QAction object is deleted. if (mp_action) { - connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (destroyed (QObject *))); + connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *))); connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ())); } #endif @@ -536,6 +537,30 @@ Action::configure_from_title (const std::string &s) } } +#if defined(HAVE_QT) +void +Action::menu_about_to_show () +{ + BEGIN_PROTECTED + + if (! mp_dispatcher || ! mp_dispatcher->menu ()) { + return; + } + AbstractMenuItem *item = mp_dispatcher->menu ()->find_item_for_action (this); + if (! item ) { + return; + } + + for (auto i = item->children.begin (); i != item->children.end (); ++i) { + if (i->action ()) { + i->action ()->sync_qaction (); + } + } + + END_PROTECTED +} +#endif + #if defined(HAVE_QT) void Action::qaction_triggered () @@ -578,7 +603,7 @@ Action::menu () const } void -Action::destroyed (QObject *obj) +Action::was_destroyed (QObject *obj) { if (obj == mp_action) { mp_action = 0; @@ -591,17 +616,24 @@ Action::destroyed (QObject *obj) } #endif +void +Action::sync_qaction () +{ +#if defined(HAVE_QT) + if (mp_action) { + mp_action->setVisible (is_effective_visible ()); + mp_action->setShortcut (get_key_sequence ()); + mp_action->setEnabled (is_effective_enabled ()); + } +#endif +} + void Action::set_visible (bool v) { if (m_visible != v) { m_visible = v; -#if defined(HAVE_QT) - if (mp_action) { - mp_action->setVisible (is_effective_visible ()); - mp_action->setShortcut (get_key_sequence ()); - } -#endif + sync_qaction (); } } @@ -610,12 +642,7 @@ Action::set_hidden (bool h) { if (m_hidden != h) { m_hidden = h; -#if defined(HAVE_QT) - if (mp_action) { - mp_action->setVisible (is_effective_visible ()); - mp_action->setShortcut (get_key_sequence ()); - } -#endif + sync_qaction (); } } @@ -634,7 +661,7 @@ Action::is_hidden () const bool Action::is_effective_visible () const { - return m_visible && !m_hidden; + return m_visible && !m_hidden && wants_visible (); } void @@ -792,11 +819,7 @@ Action::is_checked () const bool Action::is_enabled () const { -#if defined(HAVE_QT) - return qaction () ? qaction ()->isEnabled () : m_enabled; -#else return m_enabled; -#endif } bool @@ -808,12 +831,16 @@ Action::is_separator () const void Action::set_enabled (bool b) { -#if defined(HAVE_QT) - if (qaction ()) { - qaction ()->setEnabled (b); + if (m_enabled != b) { + m_enabled = b; + sync_qaction (); } -#endif - m_enabled = b; +} + +bool +Action::is_effective_enabled () const +{ + return m_enabled && wants_enabled (); } void @@ -1508,6 +1535,33 @@ AbstractMenu::delete_items (Action *action) } } +const AbstractMenuItem * +AbstractMenu::find_item_for_action (const Action *action, const AbstractMenuItem *from) const +{ + return (const_cast (this))->find_item_for_action (action, const_cast (from)); +} + +AbstractMenuItem * +AbstractMenu::find_item_for_action (const Action *action, AbstractMenuItem *from) +{ + if (! from) { + from = const_cast (&root ()); + } + + if (from->action () == action) { + return from; + } + + for (auto i = from->children.begin (); i != from->children.end (); ++i) { + AbstractMenuItem *item = find_item_for_action (action, i.operator-> ()); + if (item) { + return item; + } + } + + return 0; +} + const AbstractMenuItem * AbstractMenu::find_item_exact (const std::string &path) const { diff --git a/src/laybasic/laybasic/layAbstractMenu.h b/src/laybasic/laybasic/layAbstractMenu.h index 1a51ff398..ebecc4eba 100644 --- a/src/laybasic/laybasic/layAbstractMenu.h +++ b/src/laybasic/laybasic/layAbstractMenu.h @@ -318,6 +318,34 @@ public: return false; } + /** + * @brief Gets a value indicating the action is visible (dynamic calls) + * In addition to static visibility (visible/hidden), an Action object can request to + * become invisible dynamically based on conditions. This will work for menu-items + * for which the system will query the status before the menu is shown. + */ + virtual bool wants_visible () const + { + return true; + } + + /** + * @brief Gets a value indicating the action is enabled (dynamic calls) + * In addition to static visibility (visible/hidden), an Action object can request to + * become invisible dynamically based on conditions. This will work for menu-items + * for which the system will query the status before the menu is shown. + */ + virtual bool wants_enabled () const + { + return true; + } + + /** + * @brief Gets the effective enabled state + * This is the combined value from is_enabled and wants_enabled. + */ + bool is_effective_enabled () const; + #if defined(HAVE_QT) /** * @brief Get the underlying QAction object @@ -342,8 +370,9 @@ public: #if defined(HAVE_QT) protected slots: - void destroyed (QObject *obj); + void was_destroyed (QObject *obj); void qaction_triggered (); + void menu_about_to_show (); #endif private: @@ -381,6 +410,7 @@ private: #endif void configure_from_title (const std::string &s); + void sync_qaction (); // no copying Action (const Action &); @@ -801,7 +831,7 @@ public: * @param group The group name * @param A vector of all members (as actions) of the group */ - std::vector group_actions(const std::string &name); + std::vector group_actions (const std::string &name); /** * @brief Get the configure actions for a given configuration name @@ -844,6 +874,8 @@ private: std::vector::iterator> > find_item (tl::Extractor &extr); const AbstractMenuItem *find_item_exact (const std::string &path) const; AbstractMenuItem *find_item_exact (const std::string &path); + const AbstractMenuItem *find_item_for_action (const Action *action, const AbstractMenuItem *from = 0) const; + AbstractMenuItem *find_item_for_action (const Action *action, AbstractMenuItem *from = 0); #if defined(HAVE_QT) void build (QMenu *menu, std::list &items); void build (QToolBar *tbar, std::list &items); diff --git a/testdata/ruby/layMenuTest.rb b/testdata/ruby/layMenuTest.rb index 256349b09..0df95a72a 100644 --- a/testdata/ruby/layMenuTest.rb +++ b/testdata/ruby/layMenuTest.rb @@ -227,20 +227,20 @@ RESULT $a1.visible = false assert_equal( menu.action( "my_menu.new_item" ).is_visible?, false ) assert_equal( menu.action( "my_menu.new_item" ).is_checked?, false ) - assert_equal( menu.action( "my_menu.new_item" ).is_enabled?, false ) + assert_equal( menu.action( "my_menu.new_item" ).is_enabled?, true ) $a1.checked = true assert_equal( menu.action( "file_menu.#3" ).is_visible?, false ) assert_equal( menu.action( "file_menu.#3" ).is_checked?, false ) assert_equal( menu.action( "file_menu.#3" ).is_checkable?, false ) - assert_equal( menu.action( "file_menu.#3" ).is_enabled?, false ) + assert_equal( menu.action( "file_menu.#3" ).is_enabled?, true ) $a1.checked = false $a1.checkable = true; assert_equal( menu.action( "my_menu.new_item" ).is_visible?, false ) assert_equal( menu.action( "my_menu.new_item" ).is_checked?, false ) assert_equal( menu.action( "my_menu.new_item" ).is_checkable?, true ) - assert_equal( menu.action( "my_menu.new_item" ).is_enabled?, false ) + assert_equal( menu.action( "my_menu.new_item" ).is_enabled?, true ) $a1.checked = true assert_equal( menu.action( "file_menu.#0" ).is_checked?, true ) @@ -440,6 +440,50 @@ RESULT end + class MyAction < RBA::Action + attr_accessor :dyn_visible, :dyn_enabled + def initialize + self.dyn_visible = true + self.dyn_enabled = true + end + def wants_visible + self.dyn_visible + end + def wants_enabled + self.dyn_enabled + end + end + + def test_7 + + a = MyAction::new + + assert_equal(a.is_effective_visible?, true) + a.hidden = true + assert_equal(a.is_effective_visible?, false) + a.hidden = false + assert_equal(a.is_effective_visible?, true) + a.visible = false + assert_equal(a.is_effective_visible?, false) + a.visible = true + assert_equal(a.is_effective_visible?, true) + a.dyn_visible = false + assert_equal(a.is_effective_visible?, false) + a.dyn_visible = true + assert_equal(a.is_effective_visible?, true) + + assert_equal(a.is_effective_enabled?, true) + a.enabled = false + assert_equal(a.is_effective_enabled?, false) + a.enabled = true + assert_equal(a.is_effective_enabled?, true) + a.dyn_enabled = false + assert_equal(a.is_effective_enabled?, false) + a.dyn_enabled = true + assert_equal(a.is_effective_enabled?, true) + + end + end load("test_epilogue.rb")