[ekiga/ds-gtk-application] UI: Added preliminary support for Actions in the main window.



commit b95db083f6067b0c31f46045971b81182b5fcb7b
Author: Damien Sandras <dsandras beip be>
Date:   Mon Mar 3 20:40:50 2014 +0100

    UI: Added preliminary support for Actions in the main window.
    
    Mainly in the call history view.
    
    Also converted the actions toolbar into a header bar.
    
    This is still a POC.

 .../components/call-history/history-contact.cpp    |    8 ++-
 .../components/call-history/history-contact.h      |    2 +
 lib/engine/components/opal/sip-endpoint.cpp        |   64 +++++++++++++++++---
 lib/engine/components/opal/sip-endpoint.h          |   26 +++++++--
 .../gui/gtk-frontend/call-history-view-gtk.cpp     |   58 ++++++++++++------
 .../gui/gtk-frontend/call-history-view-gtk.h       |    3 +-
 lib/engine/gui/gtk-frontend/main_window.cpp        |   52 +++++++++++-----
 7 files changed, 164 insertions(+), 49 deletions(-)
---
diff --git a/lib/engine/components/call-history/history-contact.cpp 
b/lib/engine/components/call-history/history-contact.cpp
index dea5433..a9d2cf8 100644
--- a/lib/engine/components/call-history/history-contact.cpp
+++ b/lib/engine/components/call-history/history-contact.cpp
@@ -210,8 +210,14 @@ History::Contact::get_call_start () const
   return call_start;
 }
 
-const std::string 
+const std::string
 History::Contact::get_call_duration () const
 {
   return call_duration;
 }
+
+const std::string
+History::Contact::get_uri () const
+{
+  return uri;
+}
diff --git a/lib/engine/components/call-history/history-contact.h 
b/lib/engine/components/call-history/history-contact.h
index e436aa8..db8cec3 100644
--- a/lib/engine/components/call-history/history-contact.h
+++ b/lib/engine/components/call-history/history-contact.h
@@ -99,6 +99,8 @@ namespace History
 
     const std::string get_call_duration () const;
 
+    const std::string get_uri () const;
+
   private:
 
     boost::weak_ptr<Ekiga::ContactCore> contact_core;
diff --git a/lib/engine/components/opal/sip-endpoint.cpp b/lib/engine/components/opal/sip-endpoint.cpp
index 9b35600..f2589fb 100644
--- a/lib/engine/components/opal/sip-endpoint.cpp
+++ b/lib/engine/components/opal/sip-endpoint.cpp
@@ -38,6 +38,7 @@
 
 #include <glib/gi18n.h>
 #include "config.h"
+#include "contact-action.h"
 #include "sip-endpoint.h"
 #include "chat-core.h"
 
@@ -118,6 +119,7 @@ Opal::Sip::EndPoint::EndPoint (Opal::CallManager & _manager,
   manager (_manager)
 {
   boost::shared_ptr<Ekiga::ChatCore> chat_core = core.get<Ekiga::ChatCore> ("chat-core");
+  boost::shared_ptr<Ekiga::ContactCore> contact_core = core.get<Ekiga::ContactCore> ("contact-core");
 
   protocol_name = "sip";
   uri_prefix = "sip:";
@@ -145,6 +147,9 @@ Opal::Sip::EndPoint::EndPoint (Opal::CallManager & _manager,
 
   settings = boost::shared_ptr<Ekiga::Settings> (new Ekiga::Settings (SIP_SCHEMA));
   settings->changed.connect (boost::bind (&EndPoint::setup, this, _1));
+
+  /* Ready to take actions */
+  register_actions (contact_core);
 }
 
 
@@ -180,17 +185,41 @@ Opal::Sip::EndPoint::populate_menu (const std::string& fullname,
 {
   if (0 == GetConnectionCount ())
     builder.add_action ("phone-pick-up", _("Call"),
-                       boost::bind (&Opal::Sip::EndPoint::on_dial, this, uri));
+                        boost::bind (&Opal::Sip::EndPoint::on_dial, this, uri));
   else
     builder.add_action ("mail-forward", _("Transfer"),
-                       boost::bind (&Opal::Sip::EndPoint::on_transfer, this, uri));
+                        boost::bind (&Opal::Sip::EndPoint::on_transfer, this, uri));
   builder.add_action ("im-message-new", _("Message"),
-                     boost::bind (&Opal::Sip::EndPoint::on_message, this, uri, fullname));
+                      boost::bind (&Opal::Sip::EndPoint::on_message, this, uri, fullname));
 
   return true;
 }
 
 
+void
+Opal::Sip::EndPoint::register_actions (boost::shared_ptr<Ekiga::ContactCore> ccore)
+{
+  ccore->add_action (Ekiga::ActionPtr (
+    new Ekiga::ContactAction ("sip-call", _("Call"),
+                              boost::bind (&Opal::Sip::EndPoint::on_dial,
+                                           this, _1, _2),
+                              boost::bind (&Opal::Sip::EndPoint::is_valid_uri,
+                                           this, _2))));
+  ccore->add_action (Ekiga::ActionPtr (
+    new Ekiga::ContactAction ("sip-transfer", _("Transfer"),
+                              boost::bind (&Opal::Sip::EndPoint::on_transfer,
+                                           this, _1, _2),
+                              boost::bind (&Opal::Sip::EndPoint::can_transfer,
+                                           this, _2))));
+  ccore->add_action (Ekiga::ActionPtr (
+    new Ekiga::ContactAction ("sip-message", _("Message"),
+                              boost::bind (&Opal::Sip::EndPoint::on_message,
+                                           this, _1, _2),
+                              boost::bind (&Opal::Sip::EndPoint::is_valid_uri,
+                                           this, _2))));
+}
+
+
 bool
 Opal::Sip::EndPoint::send_message (const std::string & _uri,
                                   const std::string & _message)
@@ -896,20 +925,25 @@ Opal::Sip::EndPoint::OnDialogInfoReceived (const SIPDialogNotification & info)
 }
 
 
-void Opal::Sip::EndPoint::on_dial (std::string uri)
+void Opal::Sip::EndPoint::on_dial (Ekiga::ContactPtr contact,
+                                   const std::string & uri)
 {
+  // FIXME: I really think Ekiga::Contacts are TelephonyContacts and, as such,
+  // should have an uri. All methods should act on Contacts, the Ekiga central
+  // point of things.
   manager.dial (uri);
 }
 
 
-void Opal::Sip::EndPoint::on_message (std::string uri,
-                                      std::string name)
+void Opal::Sip::EndPoint::on_message (Ekiga::ContactPtr contact,
+                                      const std::string & uri)
 {
-  dialect->start_chat_with (uri, name);
+  dialect->start_chat_with (uri, contact->get_name ());
 }
 
 
-void Opal::Sip::EndPoint::on_transfer (std::string uri)
+void Opal::Sip::EndPoint::on_transfer (Ekiga::ContactPtr contact,
+                                       const std::string & uri)
 {
   /* FIXME : we don't handle several calls here */
   for (PSafePtr<OpalConnection> connection(connectionsActive, PSafeReference); connection != NULL; 
++connection)
@@ -939,3 +973,17 @@ Opal::Sip::EndPoint::update_aor_map (std::map<std::string, std::string> _account
   PWaitAndSignal m(aorMutex);
   accounts = _accounts;
 }
+
+
+bool
+Opal::Sip::EndPoint::is_valid_uri (const std::string & uri)
+{
+  return (!uri.empty () && (uri.find ("sip:") == 0 || uri.find (':') == string::npos));
+}
+
+
+bool
+Opal::Sip::EndPoint::can_transfer (const std::string & uri)
+{
+  return (GetConnectionCount () > 0 && is_valid_uri (uri));
+}
diff --git a/lib/engine/components/opal/sip-endpoint.h b/lib/engine/components/opal/sip-endpoint.h
index b6b806a..ba3ce0f 100644
--- a/lib/engine/components/opal/sip-endpoint.h
+++ b/lib/engine/components/opal/sip-endpoint.h
@@ -76,7 +76,7 @@ namespace Opal {
 
       /* Set up endpoint: all options or a specific setting */
       void setup (std::string setting = "");
-      
+
       /* Service */
       const std::string get_name () const
       { return "opal-sip-endpoint"; }
@@ -90,6 +90,10 @@ namespace Opal {
                          Ekiga::MenuBuilder& builder);
 
 
+      /* Register actions on the ContactCore */
+      void register_actions (boost::shared_ptr<Ekiga::ContactCore> contact_core);
+
+
       /* Chat subsystem */
       bool send_message (const std::string & uri,
                          const std::string & message);
@@ -166,13 +170,25 @@ namespace Opal {
       SIPURL GetRegisteredPartyName (const SIPURL & host,
                                     const OpalTransport & transport);
 
+      /* Return true if URI can be handled by the endpoint,
+       * false otherwise.
+       */
+      bool is_valid_uri (const std::string & uri);
+
+      /* Return true if the endpoint is currently handling one or more calls,
+       * and there is an active call to transfer, false otherwise.
+       */
+      bool can_transfer (const std::string & uri);
+
 
       /* Callbacks */
     private:
-      void on_dial (std::string uri);
-      void on_message (std::string uri,
-                      std::string name);
-      void on_transfer (std::string uri);
+      void on_dial (Ekiga::ContactPtr contact,
+                    const std::string & uri);
+      void on_message (Ekiga::ContactPtr contact,
+                       const std::string & uri);
+      void on_transfer (Ekiga::ContactPtr contact,
+                        const std::string & uri);
 
       void push_message_in_main (const std::string uri,
                                 const std::string name,
diff --git a/lib/engine/gui/gtk-frontend/call-history-view-gtk.cpp 
b/lib/engine/gui/gtk-frontend/call-history-view-gtk.cpp
index 5f28a48..fe0700c 100644
--- a/lib/engine/gui/gtk-frontend/call-history-view-gtk.cpp
+++ b/lib/engine/gui/gtk-frontend/call-history-view-gtk.cpp
@@ -43,6 +43,15 @@
 #include "menu-builder-tools.h"
 #include "menu-builder-gtk.h"
 #include "gm-cell-renderer-bitext.h"
+#include "live-object-menu.h"
+
+
+struct null_deleter
+{
+  void operator()(void const *) const
+  {
+  }
+};
 
 
 struct _CallHistoryViewGtkPrivate
@@ -52,6 +61,8 @@ struct _CallHistoryViewGtkPrivate
   {}
 
   boost::shared_ptr<History::Book> book;
+  boost::shared_ptr<Ekiga::ActorMenu> menu;
+  boost::shared_ptr<Ekiga::ContactActorMenu> contact_menu;
   GtkListStore* store;
   GtkTreeView* tree;
   boost::signals2::scoped_connection connection;
@@ -155,15 +166,16 @@ on_clicked (GtkWidget *tree,
            GdkEventButton *event,
            gpointer data)
 {
-  History::Book *book = NULL;
+  GtkWidget *menu = NULL;
+  GtkBuilder *builder = NULL;
   GtkTreeModel *model = NULL;
   GtkTreePath *path = NULL;
   GtkTreeIter iter;
-  Ekiga::Contact *contact = NULL;
+  History::Contact *contact = NULL;
 
-  book = (History::Book*)data;
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+  CallHistoryViewGtk *self = CALL_HISTORY_VIEW_GTK (data);
 
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
 
   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree),
                                     (gint) event->x, (gint) event->y,
@@ -175,23 +187,26 @@ on_clicked (GtkWidget *tree,
                          COLUMN_CONTACT, &contact,
                          -1);
 
-
       if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
 
-       MenuBuilderGtk builder;
-       if (contact != NULL)
-         contact->populate_menu (builder);
-       if (!builder.empty())
-         builder.add_separator ();
-       builder.add_action ("gtk-clear", _("Clear List"),
-                           boost::bind (&History::Book::clear, book));
-       gtk_widget_show_all (builder.menu);
-       gtk_menu_popup (GTK_MENU (builder.menu), NULL, NULL,
+        builder = gtk_builder_new ();
+
+        self->priv->contact_menu->set_data (Ekiga::ContactPtr (contact, null_deleter ()),
+                                            contact->get_uri ());
+        std::string menu_content = self->priv->contact_menu->as_xml () + self->priv->menu->as_xml ();
+        gtk_builder_add_from_string (builder,
+                                     Ekiga::ActorMenu::get_xml_menu ("popup", menu_content, true).c_str (),
+                                     -1, NULL);
+        menu = gtk_menu_new_from_model (G_MENU_MODEL (gtk_builder_get_object (builder, "popup")));
+        gtk_widget_insert_action_group (menu, "win", G_ACTION_GROUP (g_application_get_default ()));
+       gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
                        NULL, NULL, event->button, event->time);
-       g_object_ref_sink (builder.menu);
+       g_object_ref (menu);
+        g_object_unref (builder);
       }
       if (event->type == GDK_2BUTTON_PRESS) {
 
+        std::cout << "FIXME" << std::endl << std::flush;
        if (contact != NULL) {
 
          Ekiga::TriggerMenuBuilder builder;
@@ -297,7 +312,8 @@ call_history_view_gtk_class_init (CallHistoryViewGtkClass* klass)
 /* public api */
 
 GtkWidget *
-call_history_view_gtk_new (boost::shared_ptr<History::Book> book)
+call_history_view_gtk_new (boost::shared_ptr<History::Book> book,
+                           boost::shared_ptr<Ekiga::ContactCore> ccore)
 {
   CallHistoryViewGtk* self = NULL;
 
@@ -349,7 +365,7 @@ call_history_view_gtk_new (boost::shared_ptr<History::Book> book)
   g_signal_connect (selection, "changed",
                    G_CALLBACK (on_selection_changed), self);
   g_signal_connect (self->priv->tree, "event-after",
-                   G_CALLBACK (on_clicked), &(*book));
+                   G_CALLBACK (on_clicked), self);
 
   /* connect to the signal */
   self->priv->connection = book->updated.connect (boost::bind (&on_book_updated, self));
@@ -357,7 +373,13 @@ call_history_view_gtk_new (boost::shared_ptr<History::Book> book)
   /* initial populate */
   on_book_updated(self);
 
-  return (GtkWidget*)self;
+  /* register book actions */
+  self->priv->menu =
+    boost::shared_ptr<Ekiga::ActorMenu> (new Ekiga::ActorMenu (*book));
+  self->priv->contact_menu =
+    boost::shared_ptr<Ekiga::ContactActorMenu> (new Ekiga::ContactActorMenu (*ccore));
+
+  return GTK_WIDGET (self);
 }
 
 void
diff --git a/lib/engine/gui/gtk-frontend/call-history-view-gtk.h 
b/lib/engine/gui/gtk-frontend/call-history-view-gtk.h
index b34d552..58ae367 100644
--- a/lib/engine/gui/gtk-frontend/call-history-view-gtk.h
+++ b/lib/engine/gui/gtk-frontend/call-history-view-gtk.h
@@ -50,7 +50,8 @@ typedef struct _CallHistoryViewGtkClass CallHistoryViewGtkClass;
  */
 
 /* creating the widget, connected to an History::Book object */
-GtkWidget *call_history_view_gtk_new (boost::shared_ptr<History::Book> book);
+GtkWidget *call_history_view_gtk_new (boost::shared_ptr<History::Book> book,
+                                      boost::shared_ptr<Ekiga::ContactCore> ccore);
 
 
 /* Whatever is selected, we want the view to populate the given menu builder
diff --git a/lib/engine/gui/gtk-frontend/main_window.cpp b/lib/engine/gui/gtk-frontend/main_window.cpp
index feed254..a585d55 100644
--- a/lib/engine/gui/gtk-frontend/main_window.cpp
+++ b/lib/engine/gui/gtk-frontend/main_window.cpp
@@ -826,6 +826,11 @@ ekiga_main_window_init_actions_toolbar (EkigaMainWindow *mw)
   GtkWidget *box = NULL;
   GtkWidget *button = NULL;
 
+  GtkIconSize toolbar_size;
+  gint toolbar_size_px = 0;
+  gint menu_size_px = 0;
+  gint margin_px = 0;
+
   g_return_if_fail (EKIGA_IS_MAIN_WINDOW (mw));
 
   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (mw)),
@@ -834,23 +839,31 @@ ekiga_main_window_init_actions_toolbar (EkigaMainWindow *mw)
   gtk_style_context_set_junction_sides (gtk_widget_get_style_context (GTK_WIDGET (mw)),
                                         GTK_JUNCTION_BOTTOM);
 
-  mw->priv->actions_toolbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  mw->priv->actions_toolbar = gtk_header_bar_new ();
 
-  button = gtk_toggle_button_new ();
+  /* Compute the image button margin */
+  g_object_get (gtk_settings_get_default (), "gtk-toolbar-icon-size", &toolbar_size, NULL);
+  gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &menu_size_px, NULL);
+  gtk_icon_size_lookup (toolbar_size, &toolbar_size_px, NULL);
+  margin_px = (gint) floor ((toolbar_size_px - menu_size_px) / 2.0);
+
+  /* Start packing buttons */
+  mw->priv->preview_button = gtk_toggle_button_new ();
   image = gtk_image_new_from_icon_name ("camera-web-symbolic", GTK_ICON_SIZE_MENU);
-  g_object_set (G_OBJECT (image), "margin", 3, NULL);
-  gtk_button_set_image (GTK_BUTTON (button), image);
-  gtk_widget_set_tooltip_text (GTK_WIDGET (button),
+  g_object_set (G_OBJECT (image), "margin", margin_px, NULL);
+  gtk_button_set_image (GTK_BUTTON (mw->priv->preview_button), image);
+  gtk_widget_set_tooltip_text (GTK_WIDGET (mw->priv->preview_button),
                                _("Display images from your camera device"));
-  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "win.enable-preview");
-  gtk_box_pack_start (GTK_BOX (mw->priv->actions_toolbar), button, FALSE, FALSE, 0);
-  gtk_widget_set_margin_left (button, 6);
-  gtk_widget_set_margin_right (button, 6);
+  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (mw->priv->preview_button),
+                                           "win.enable-preview");
+  gtk_header_bar_pack_start (GTK_HEADER_BAR (mw->priv->actions_toolbar), mw->priv->preview_button);
+  gtk_widget_set_margin_left (mw->priv->preview_button, 6);
+  gtk_widget_set_margin_right (mw->priv->preview_button, 6);
 
   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
   button = gtk_toggle_button_new ();
   image = gtk_image_new_from_icon_name ("avatar-default-symbolic", GTK_ICON_SIZE_MENU);
-  g_object_set (G_OBJECT (image), "margin", 3, NULL);
+  g_object_set (G_OBJECT (image), "margin", margin_px, NULL);
   gtk_button_set_image (GTK_BUTTON (button), image);
   gtk_widget_set_tooltip_text (GTK_WIDGET (button),
                                _("View the contacts list"));
@@ -859,7 +872,7 @@ ekiga_main_window_init_actions_toolbar (EkigaMainWindow *mw)
 
   button = gtk_toggle_button_new ();
   image = gtk_image_new_from_icon_name ("input-dialpad-symbolic", GTK_ICON_SIZE_MENU);
-  g_object_set (G_OBJECT (image), "margin", 3, NULL);
+  g_object_set (G_OBJECT (image), "margin", margin_px, NULL);
   gtk_button_set_image (GTK_BUTTON (button), image);
   gtk_widget_set_tooltip_text (GTK_WIDGET (button),
                                _("View the dialpad"));
@@ -868,7 +881,7 @@ ekiga_main_window_init_actions_toolbar (EkigaMainWindow *mw)
 
   button = gtk_toggle_button_new ();
   image = gtk_image_new_from_icon_name ("document-open-recent-symbolic", GTK_ICON_SIZE_MENU);
-  g_object_set (G_OBJECT (image), "margin", 3, NULL);
+  g_object_set (G_OBJECT (image), "margin", margin_px, NULL);
   gtk_button_set_image (GTK_BUTTON (button), image);
   gtk_widget_set_tooltip_text (GTK_WIDGET (button),
                                _("View the call history"));
@@ -880,17 +893,17 @@ ekiga_main_window_init_actions_toolbar (EkigaMainWindow *mw)
   gtk_style_context_add_class (gtk_widget_get_style_context (box),
                                GTK_STYLE_CLASS_LINKED);
 
-  gtk_box_pack_start (GTK_BOX (mw->priv->actions_toolbar), box, FALSE, FALSE, 0);
+  gtk_header_bar_pack_start (GTK_HEADER_BAR (mw->priv->actions_toolbar), box);
   gtk_widget_set_margin_left (box, 6);
   gtk_widget_set_margin_right (box, 6);
 
   button = gtk_menu_button_new ();
   image = gtk_image_new_from_icon_name ("emblem-system-symbolic", GTK_ICON_SIZE_MENU);
-  g_object_set (G_OBJECT (image), "margin", 3, NULL);
+  g_object_set (G_OBJECT (image), "margin", margin_px, NULL);
   gtk_button_set_image (GTK_BUTTON (button), image);
   gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button),
                                   G_MENU_MODEL (gtk_builder_get_object (mw->priv->builder, "menubar")));
-  gtk_box_pack_end (GTK_BOX (mw->priv->actions_toolbar), button, FALSE, FALSE, 0);
+  gtk_header_bar_pack_end (GTK_HEADER_BAR (mw->priv->actions_toolbar), button);
   gtk_widget_set_margin_left (button, 6);
   gtk_widget_set_margin_right (button, 6);
 }
@@ -906,6 +919,10 @@ ekiga_main_window_init_menu (EkigaMainWindow *mw)
     "    </section>"
     "    <section>"
     "      <item>"
+    "        <attribute name='label' translatable='yes'>_Aontact</attribute>"
+    "        <attribute name='action'>win.clear</attribute>"
+    "      </item>"
+    "      <item>"
     "        <attribute name='label' translatable='yes'>_Add Contact</attribute>"
     "        <attribute name='action'>win.add</attribute>"
     "      </item>"
@@ -1008,7 +1025,8 @@ ekiga_main_window_init_history (EkigaMainWindow *mw)
   boost::shared_ptr<History::Book> history_book
     = mw->priv->history_source->get_book ();
 
-  mw->priv->call_history_view = call_history_view_gtk_new (history_book);
+  mw->priv->call_history_view = call_history_view_gtk_new (history_book,
+                                                           mw->priv->contact_core);
 
   label = gtk_label_new (_("Call history"));
   mw->priv->call_history_page_number =
@@ -1139,6 +1157,8 @@ ekiga_main_window_dispose (GObject* gobject)
     g_object_unref (mw->priv->roster_view);
     mw->priv->roster_view = NULL;
   }
+  g_object_unref (mw->priv->builder);
+  mw->priv->builder = NULL;
 
   G_OBJECT_CLASS (ekiga_main_window_parent_class)->dispose (gobject);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]