fix computation of rectangle bounding box
[ardour.git] / gtk2_ardour / processor_box.cc
index 2f8513fd2a83bc948ce7d2c8c680dbbb98665364..a22e9917525d169b551f162c7ec254d2c5ba16ff 100644 (file)
@@ -113,7 +113,7 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr<Processo
 
                _button.set_active (_processor->active());
                _button.show ();
-               
+
                _processor->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_active_changed, this), gui_context());
                _processor->PropertyChanged.connect (name_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_property_changed, this, _1), gui_context());
 
@@ -213,6 +213,7 @@ void
 ProcessorEntry::set_enum_width (Width w)
 {
        _width = w;
+       _button.set_text (name (_width));
 }
 
 void
@@ -247,7 +248,20 @@ ProcessorEntry::processor_property_changed (const PropertyChange& what_changed)
 void
 ProcessorEntry::setup_tooltip ()
 {
-       ARDOUR_UI::instance()->set_tip (_button, name (Wide));
+       if (_processor) {
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_processor);
+               if (pi) {
+                       if (pi->plugin()->has_editor()) {
+                               ARDOUR_UI::instance()->set_tip (_button,
+                                               string_compose (_("<b>%1</b>\nDouble-click to show GUI.\nAlt+double-click to show generic GUI."), name (Wide)));
+                       } else {
+                               ARDOUR_UI::instance()->set_tip (_button,
+                                               string_compose (_("<b>%1</b>\nDouble-click to show generic GUI."), name (Wide)));
+                       }
+                       return;
+               }
+       }
+       ARDOUR_UI::instance()->set_tip (_button, string_compose ("<b>%1</b>", name (Wide)));
 }
 
 string
@@ -642,10 +656,15 @@ PluginInsertProcessorEntry::SplittingIcon::on_expose_event (GdkEventExpose* ev)
 {
        cairo_t* cr = gdk_cairo_create (get_window()->gobj());
 
-       cairo_set_line_width (cr, 1);
+       cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+       cairo_clip (cr);
+
+       cairo_set_line_width (cr, 1.5);
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
 
-       double const width = ev->area.width;
-       double const height = ev->area.height;
+       Gtk::Allocation a = get_allocation();
+       double const width = a.get_width();
+       double const height = a.get_height();
 
        Gdk::Color const bg = get_style()->get_bg (STATE_NORMAL);
        cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
@@ -656,12 +675,19 @@ PluginInsertProcessorEntry::SplittingIcon::on_expose_event (GdkEventExpose* ev)
        Gdk::Color const fg = get_style()->get_fg (STATE_NORMAL);
        cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
 
-       cairo_move_to (cr, width * 0.3, height);
-       cairo_line_to (cr, width * 0.3, height * 0.5);
-       cairo_line_to (cr, width * 0.7, height * 0.5);
-       cairo_line_to (cr, width * 0.7, height);
-       cairo_move_to (cr, width * 0.5, height * 0.5);
-       cairo_line_to (cr, width * 0.5, 0);
+       const float si_l = rint(width * 0.3) + .5;
+       const float si_c = rint(width * 0.5) + .5;
+       const float si_r = rint(width * 0.7) + .5;
+       const float si_m = rint(height * 0.5) + .5;
+
+       cairo_move_to (cr, si_l, height);
+       cairo_line_to (cr, si_l, si_m);
+       cairo_line_to (cr, si_r, si_m);
+       cairo_line_to (cr, si_r, height);
+
+       cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+       cairo_move_to (cr, si_c, si_m);
+       cairo_line_to (cr, si_c, 0);
        cairo_stroke (cr);
 
        return true;
@@ -918,6 +944,7 @@ ProcessorBox::show_processor_menu (int arg)
        const bool sensitive = !processor_display.selection().empty();
        ActionManager::set_sensitive (ActionManager::plugin_selection_sensitive_actions, sensitive);
        edit_action->set_sensitive (one_processor_can_be_edited ());
+       edit_generic_action->set_sensitive (one_processor_can_be_edited ());
 
        boost::shared_ptr<PluginInsert> pi;
        if (single_selection) {
@@ -925,7 +952,7 @@ ProcessorBox::show_processor_menu (int arg)
        }
 
        /* allow editing with an Ardour-generated UI for plugin inserts with editors */
-       edit_generic_action->set_sensitive (pi && pi->plugin()->has_editor ());
+       edit_action->set_sensitive (pi && pi->plugin()->has_editor ());
 
        /* disallow rename for multiple selections, for plugin inserts and for the fader */
        rename_action->set_sensitive (single_selection && !pi && !boost::dynamic_pointer_cast<Amp> (single_selection->processor ()));
@@ -1050,13 +1077,14 @@ ProcessorBox::processor_button_press_event (GdkEventButton *ev, ProcessorEntry*
        if (processor && (Keyboard::is_edit_event (ev) || (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS))) {
 
                if (_session->engine().connected()) {
-
                        /* XXX giving an error message here is hard, because we may be in the midst of a button press */
 
-                       if (Config->get_use_plugin_own_gui ()) {
-                               edit_processor (processor);
-                       } else {
+                       if (!one_processor_can_be_edited ()) return true;
+
+                       if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
                                generic_edit_processor (processor);
+                       } else {
+                               edit_processor (processor);
                        }
                }
 
@@ -1364,7 +1392,7 @@ ProcessorBox::redisplay_processors ()
 
        _route->foreach_processor (sigc::mem_fun (*this, &ProcessorBox::add_processor_to_display));
 
-       for (list<ProcessorWindowProxy*>::iterator i = _processor_window_info.begin(); i != _processor_window_info.end(); ++i) {
+       for (ProcessorWindowProxies::iterator i = _processor_window_info.begin(); i != _processor_window_info.end(); ++i) {
                (*i)->marked = false;
        }
 
@@ -1372,16 +1400,43 @@ ProcessorBox::redisplay_processors ()
 
        /* trim dead wood from the processor window proxy list */
 
-       list<ProcessorWindowProxy*>::iterator i = _processor_window_info.begin();
+       ProcessorWindowProxies::iterator i = _processor_window_info.begin();
        while (i != _processor_window_info.end()) {
-               list<ProcessorWindowProxy*>::iterator j = i;
+               ProcessorWindowProxies::iterator j = i;
                ++j;
 
-               if (!(*i)->marked) {
+               if (!(*i)->valid()) {
+
                        WM::Manager::instance().remove (*i);
                        delete *i;
                        _processor_window_info.erase (i);
-               }
+                       
+               } else if (!(*i)->marked) {
+
+                       /* this processor is no longer part of this processor
+                        * box.
+                        *
+                        * that could be because it was deleted or it could be
+                        * because the route being displayed in the parent
+                        * strip changed.
+                        *
+                        * The latter only happens with the editor mixer strip.
+                        */
+
+                       if (is_editor_mixer_strip()) {
+                               
+                               /* editor mixer strip .. DO NOTHING
+                                *
+                                * note: the processor window stays visible if
+                                * it is already visible
+                                */
+                       } else {
+                               (*i)->hide ();
+                               WM::Manager::instance().remove (*i);
+                               delete *i;
+                               _processor_window_info.erase (i);
+                       } 
+               } 
 
                i = j;
        }
@@ -1400,7 +1455,7 @@ ProcessorBox::maybe_add_processor_to_ui_list (boost::weak_ptr<Processor> w)
                return;
        }
 
-       list<ProcessorWindowProxy*>::iterator i = _processor_window_info.begin ();
+       ProcessorWindowProxies::iterator i = _processor_window_info.begin ();
        while (i != _processor_window_info.end()) {
 
                boost::shared_ptr<Processor> t = (*i)->processor().lock ();
@@ -2016,7 +2071,7 @@ ProcessorBox::processor_can_be_edited (boost::shared_ptr<Processor> processor)
        }
 
        if (
-               (boost::dynamic_pointer_cast<Send> (processor) && !boost::dynamic_pointer_cast<InternalSend> (processor))||
+               boost::dynamic_pointer_cast<Send> (processor) ||
                boost::dynamic_pointer_cast<Return> (processor) ||
                boost::dynamic_pointer_cast<PluginInsert> (processor) ||
                boost::dynamic_pointer_cast<PortInsert> (processor)
@@ -2049,6 +2104,18 @@ ProcessorBox::get_editor_window (boost::shared_ptr<Processor> processor, bool us
        boost::shared_ptr<PortInsert> port_insert;
        Window* gidget = 0;
 
+       /* This method may or may not return a Window, but if it does not it
+        * will modify the parent mixer strip appearance layout to allow
+        * "editing" the @param processor that was passed in.
+        *
+        * So for example, if the processor is an Amp (gain), the parent strip
+        * will be forced back into a model where the fader controls the main gain.
+        * If the processor is a send, then we map the send controls onto the
+        * strip.
+        * 
+        * Plugins and others will return a window for control.
+        */
+
        if (boost::dynamic_pointer_cast<AudioTrack>(_route) != 0) {
 
                if (boost::dynamic_pointer_cast<AudioTrack> (_route)->freeze_state() == AudioTrack::Frozen) {
@@ -2069,17 +2136,8 @@ ProcessorBox::get_editor_window (boost::shared_ptr<Processor> processor, bool us
                }
 
                if (boost::dynamic_pointer_cast<InternalSend> (processor) == 0) {
-                       SendUIWindow* w = new SendUIWindow (send, _session);
-                       w->show ();
-               } else {
-                       /* assign internal send to main fader */
-                       if (_parent_strip) {
-                               if (_parent_strip->current_delivery() == send) {
-                                       _parent_strip->revert_to_default_display ();
-                               } else {
-                                       _parent_strip->show_send(send);
-                               }
-                       } 
+
+                       gidget = new SendUIWindow (send, _session);
                }
 
        } else if ((retrn = boost::dynamic_pointer_cast<Return> (processor)) != 0) {
@@ -2132,7 +2190,7 @@ ProcessorBox::get_editor_window (boost::shared_ptr<Processor> processor, bool us
        } else if ((port_insert = boost::dynamic_pointer_cast<PortInsert> (processor)) != 0) {
 
                if (!_session->engine().connected()) {
-                       MessageDialog msg ( _("Not connected to JACK - no I/O changes are possible"));
+                       MessageDialog msg ( _("Not connected to audio engine - no I/O changes are possible"));
                        msg.run ();
                        return 0;
                }
@@ -2183,10 +2241,10 @@ ProcessorBox::register_actions ()
 
        act = ActionManager::register_action (popup_act_grp, X_("newinsert"), _("New Insert"),
                        sigc::ptr_fun (ProcessorBox::rb_choose_insert));
-       ActionManager::jack_sensitive_actions.push_back (act);
+       ActionManager::engine_sensitive_actions.push_back (act);
        act = ActionManager::register_action (popup_act_grp, X_("newsend"), _("New External Send ..."),
                        sigc::ptr_fun (ProcessorBox::rb_choose_send));
-       ActionManager::jack_sensitive_actions.push_back (act);
+       ActionManager::engine_sensitive_actions.push_back (act);
 
        ActionManager::register_action (popup_act_grp, X_("newaux"), _("New Aux Send ..."));
 
@@ -2235,7 +2293,7 @@ ProcessorBox::register_actions ()
                sigc::ptr_fun (ProcessorBox::rb_edit));
 
        edit_generic_action = ActionManager::register_action (
-               popup_act_grp, X_("edit-generic"), _("Edit with basic controls..."),
+               popup_act_grp, X_("edit-generic"), _("Edit with generic controls..."),
                sigc::ptr_fun (ProcessorBox::rb_edit_generic));
 
        ActionManager::add_action_group (popup_act_grp);
@@ -2427,17 +2485,38 @@ ProcessorBox::rb_edit ()
        _current_processor_box->for_selected_processors (&ProcessorBox::edit_processor);
 }
 
+bool
+ProcessorBox::edit_aux_send (boost::shared_ptr<Processor> processor)
+{
+       if (boost::dynamic_pointer_cast<InternalSend> (processor) == 0) {
+               return false;
+       }
+
+       if (_parent_strip) {
+               boost::shared_ptr<Send> send = boost::dynamic_pointer_cast<Send> (processor);
+               if (_parent_strip->current_delivery() == send) {
+                       _parent_strip->revert_to_default_display ();
+               } else {
+                       _parent_strip->show_send(send);
+               }
+       }
+       return true;
+}
+
 void
 ProcessorBox::edit_processor (boost::shared_ptr<Processor> processor)
 {
        if (!processor) {
                return;
        }
-       
+       if (edit_aux_send (processor)) {
+               return;
+       }
+
        ProcessorWindowProxy* proxy = find_window_proxy (processor);
 
        if (proxy) {
-               proxy->set_custom_ui_mode (Config->get_use_plugin_own_gui ());
+               proxy->set_custom_ui_mode (true);
                proxy->toggle ();
        }
 }
@@ -2448,7 +2527,10 @@ ProcessorBox::generic_edit_processor (boost::shared_ptr<Processor> processor)
        if (!processor) {
                return;
        }
-       
+       if (edit_aux_send (processor)) {
+               return;
+       }
+
        ProcessorWindowProxy* proxy = find_window_proxy (processor);
 
        if (proxy) {
@@ -2511,7 +2593,13 @@ ProcessorBox::generate_processor_title (boost::shared_ptr<PluginInsert> pi)
                maker += " ...";
        }
 
-       return string_compose(_("%1: %2 (by %3)"), _route->name(), pi->name(), maker);
+       SessionObject* owner = pi->owner();
+
+       if (owner) {
+               return string_compose(_("%1: %2 (by %3)"), owner->name(), pi->name(), maker);
+       } else {
+               return string_compose(_("%2 (by %3)"), pi->name(), maker);
+       }
 }
 
 /** @param p Processor.
@@ -2624,6 +2712,12 @@ ProcessorBox::update_gui_object_state (ProcessorEntry* entry)
        entry->add_control_state (proc);
 }
 
+bool
+ProcessorBox::is_editor_mixer_strip() const
+{
+       return _parent_strip && !_parent_strip->mixer_owned();
+}
+
 ProcessorWindowProxy::ProcessorWindowProxy (string const & name, ProcessorBox* box, boost::weak_ptr<Processor> processor)
        : WM::ProxyBase (name, string())
        , marked (false)
@@ -2631,8 +2725,34 @@ ProcessorWindowProxy::ProcessorWindowProxy (string const & name, ProcessorBox* b
        , _processor (processor)
        , is_custom (false)
        , want_custom (false)
+       , _valid (true)
 {
+       boost::shared_ptr<Processor> p = _processor.lock ();
+       if (!p) {
+               return;
+       }
+       p->DropReferences.connect (going_away_connection, MISSING_INVALIDATOR, boost::bind (&ProcessorWindowProxy::processor_going_away, this), gui_context());
+}
 
+ProcessorWindowProxy::~ProcessorWindowProxy()
+{
+       /* processor window proxies do not own the windows they create with
+        * ::get(), so set _window to null before the normal WindowProxy method
+        * deletes it.
+        */
+       _window = 0;
+}
+
+void
+ProcessorWindowProxy::processor_going_away ()
+{
+       delete _window;
+       _window = 0;
+       _valid = false;
+       /* should be no real reason to do this, since the object that would
+          send DropReferences is about to be deleted, but lets do it anyway.
+       */
+       going_away_connection.disconnect();
 }
 
 ARDOUR::SessionHandlePtr*
@@ -2642,6 +2762,12 @@ ProcessorWindowProxy::session_handle()
        return 0;
 }
 
+bool
+ProcessorWindowProxy::valid() const
+{
+       return _valid;
+}
+
 XMLNode&
 ProcessorWindowProxy::get_state () const
 {