enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / route_group_menu.cc
index 42d09ab9602f631f539996080d276637a5083c1d..5ccafaec398c70467c9313d65c8943ccd4449126 100644 (file)
 
 #include <gtkmm/menu.h>
 #include <gtkmm/stock.h>
+
+#include "gtkmm2ext/utils.h"
+#include "gtkmm2ext/doi.h"
+
 #include "ardour/session.h"
 #include "ardour/route_group.h"
+#include "ardour/route.h"
 #include "route_group_menu.h"
 #include "route_group_dialog.h"
-#include "i18n.h"
+
+#include "pbd/i18n.h"
 
 using namespace Gtk;
 using namespace ARDOUR;
@@ -31,73 +37,121 @@ using namespace PBD;
 
 RouteGroupMenu::RouteGroupMenu (Session* s, PropertyList* plist)
        : SessionHandlePtr (s)
+       , _menu (0)
        , _default_properties (plist)
        , _inhibit_group_selected (false)
-       , _selected_route_group (0)
 {
-       rebuild (0);
+
 }
 
 RouteGroupMenu::~RouteGroupMenu()
 {
-       delete _default_properties; 
+       delete _menu;
+       delete _default_properties;
 }
 
+/** @param s Routes to operate on */
 void
-RouteGroupMenu::rebuild (RouteGroup* curr)
+RouteGroupMenu::build (WeakRouteList const & s)
 {
+       assert (!s.empty ());
+
        using namespace Menu_Helpers;
 
-       _selected_route_group = curr;
+       _subject = s;
+
+       /* FInd all the route groups that our subjects are members of */
+       std::set<RouteGroup*> groups;
+       for (WeakRouteList::const_iterator i = _subject.begin (); i != _subject.end(); ++i) {
+               boost::shared_ptr<Route> r = i->lock ();
+               if (r) {
+                       groups.insert (r->route_group ());
+               }
+       }
 
        _inhibit_group_selected = true;
 
-       items().clear ();
+       delete _menu;
 
-       items().push_back (MenuElem (_("New group..."), sigc::mem_fun (*this, &RouteGroupMenu::new_group)));
-       items().push_back (SeparatorElem ());
+       /* Note: don't use manage() here, otherwise if our _menu object is attached as a submenu
+          and its parent is then destroyed, our _menu object will be deleted and we'll have no
+          way of knowing about it.  Without manage(), when the above happens our _menu's gobject
+          will be destroyed and its value set to 0, so we know.
+       */
+       _menu = new Menu;
 
-       RadioMenuItem::Group group;
-       items().push_back (RadioMenuElem (group, _("No group"), sigc::bind (sigc::mem_fun (*this, &RouteGroupMenu::set_group), (RouteGroup *) 0)));
+       MenuList& items = _menu->items ();
+
+       items.push_back (MenuElem (_("New Group..."), sigc::mem_fun (*this, &RouteGroupMenu::new_group)));
+       items.push_back (SeparatorElem ());
 
-       if (curr == 0) {
-               static_cast<RadioMenuItem*> (&items().back())->set_active ();
+       RadioMenuItem::Group group;
+       items.push_back (RadioMenuElem (group, _("No Group")));
+       RadioMenuItem* i = static_cast<RadioMenuItem *> (&items.back ());
+       i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteGroupMenu::set_group), (RouteGroup *) 0));
+
+       if (groups.size() == 1 && *groups.begin() == 0) {
+               i->set_active ();
+       } else if (groups.size() > 1) {
+               i->set_inconsistent ();
        }
 
        if (_session) {
-               _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &RouteGroupMenu::add_item), curr, &group));
+               _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &RouteGroupMenu::add_item), groups, &group));
        }
 
        _inhibit_group_selected = false;
 }
 
+/** @param rg Route group to add.
+ *  @param groups Active route groups (may included 0 for `no group')
+ *  @param group Radio item group to add radio items to.
+ */
 void
-RouteGroupMenu::add_item (RouteGroup* rg, RouteGroup* curr, RadioMenuItem::Group* group)
+RouteGroupMenu::add_item (RouteGroup* rg, std::set<RouteGroup*> const & groups, RadioMenuItem::Group* group)
 {
        using namespace Menu_Helpers;
 
-       items().push_back (RadioMenuElem (*group, rg->name(), sigc::bind (sigc::mem_fun(*this, &RouteGroupMenu::set_group), rg)));
+       MenuList& items = _menu->items ();
 
-       if (rg == curr) {
-               static_cast<RadioMenuItem*> (&items().back())->set_active ();
+       items.push_back (RadioMenuElem (*group, rg->name()));
+       RadioMenuItem* i = static_cast<RadioMenuItem*> (&items.back ());
+       i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteGroupMenu::set_group), rg));
+
+       if (groups.size() == 1 && *groups.begin() == rg) {
+               /* there's only one active group, and it's this one */
+               i->set_active ();
+       } else if (groups.size() > 1) {
+               /* there are >1 active groups */
+               i->set_inconsistent ();
        }
 }
 
+/** Called when a group is selected from the menu.
+ *  @param Group, or 0 for none.
+ */
 void
 RouteGroupMenu::set_group (RouteGroup* g)
 {
-       if (g == _selected_route_group) {
-               /* cut off the signal_toggled that GTK emits for an option that is being un-selected
-                  when a new option is being selected instead
-               */
+       if (_inhibit_group_selected) {
                return;
        }
-       
-       if (!_inhibit_group_selected) {
-               GroupSelected (g);
-       }
 
-       _selected_route_group = g;
+       for (WeakRouteList::const_iterator i = _subject.begin(); i != _subject.end(); ++i) {
+               boost::shared_ptr<Route> r = i->lock ();
+               if (!r || r->route_group () == g) {
+                       /* lock of weak_ptr failed, or the group for this route is already right */
+                       continue;
+               }
+
+               if (g) {
+                       g->add (r);
+               } else {
+                       if (r->route_group ()) {
+                               r->route_group()->remove (r);
+                       }
+               }
+       }
 }
 
 void
@@ -108,15 +162,39 @@ RouteGroupMenu::new_group ()
        }
 
        RouteGroup* g = new RouteGroup (*_session, "");
-       g->apply_changes (*_default_properties);
+       RouteGroupDialog* d = new RouteGroupDialog (g, true);
 
-       RouteGroupDialog d (g, Gtk::Stock::NEW);
-       int const r = d.do_run ();
+       d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &RouteGroupMenu::new_group_dialog_finished), d));
+       d->present ();
+}
 
-       if (r == Gtk::RESPONSE_OK) {
-               _session->add_route_group (g);
-               set_group (g);
+void
+RouteGroupMenu::new_group_dialog_finished (int r, RouteGroupDialog* d)
+{
+       if (r == RESPONSE_OK) {
+               _session->add_route_group (d->group());
+               set_group (d->group());
        } else {
-               delete g;
+               delete d->group ();
+       }
+
+       delete_when_idle (d);
+}
+
+Gtk::Menu *
+RouteGroupMenu::menu ()
+{
+       /* Our menu's gobject can be 0 if it was attached as a submenu whose
+          parent was subsequently deleted.
+       */
+       assert (_menu && _menu->gobj());
+       return _menu;
+}
+
+void
+RouteGroupMenu::detach ()
+{
+       if (_menu && _menu->gobj ()) {
+               Gtkmm2ext::detach_menu (*_menu);
        }
 }