[ekiga/jp-opal-roster: 2/2] Added an experimental Opal::Presentity implementation



commit 3fba001cc836ed0629aab263ed617cc472ef25dd
Author: Julien Puydt <jpuydt free fr>
Date:   Fri Oct 25 15:27:44 2013 +0200

    Added an experimental Opal::Presentity implementation
    
    This is heavily based on Local::Presentity's code, but with some little
    improvements here and there (pushing build_node as a static method makes
    a better api, for instance), removed presence fetching/unfetching code
    (which will need to be re-added later, perhaps differently).
    
    It hasn't been added to the build system, and was never compiled.

 lib/engine/components/opal/opal-presentity.cpp |  441 ++++++++++++++++++++++++
 lib/engine/components/opal/opal-presentity.h   |  142 ++++++++
 2 files changed, 583 insertions(+), 0 deletions(-)
---
diff --git a/lib/engine/components/opal/opal-presentity.cpp b/lib/engine/components/opal/opal-presentity.cpp
new file mode 100644
index 0000000..1ee6d19
--- /dev/null
+++ b/lib/engine/components/opal/opal-presentity.cpp
@@ -0,0 +1,441 @@
+
+/*
+ * Ekiga -- A VoIP and Video-Conferencing application
+ * Copyright (C) 2000-2013 Damien Sandras <dsandras seconix com>
+
+ * This program is free software; you can  redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version. This program is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Ekiga is licensed under the GPL license and as a special exception, you
+ * have permission to link or otherwise combine this program with the
+ * programs OPAL, OpenH323 and PWLIB, and distribute the combination, without
+ * applying the requirements of the GNU GPL to the OPAL, OpenH323 and PWLIB
+ * programs, as long as you do follow the requirements of the GNU GPL for all
+ * the rest of the software thus combined.
+ */
+
+
+/*
+ *                         opal-presentity.cpp  -  description
+ *                         ------------------------------------------
+ *   begin                : written in 2013 by Julien Puydt
+ *   copyright            : (c) 2013 by Julien Puydt
+ *   description          : implementation of a presentity for an opal account roster
+ *
+ */
+
+#include <algorithm>
+#include <set>
+#include <glib/gi18n.h>
+
+#include "form-request-simple.h"
+#include "opal-cluster.h"
+#include "robust-xml.h"
+
+#include "opal-presentity.h"
+
+
+// remove leading and trailing spaces and tabs (useful for copy/paste)
+// also, if no protocol specified, add leading "sip:"
+static std::string
+canonize_uri (std::string uri)
+{
+  const size_t begin_str = uri.find_first_not_of (" \t");
+  if (begin_str == std::string::npos)  // there is no content
+    return "";
+
+  const size_t end_str = uri.find_last_not_of (" \t");
+  const size_t range = end_str - begin_str + 1;
+  uri = uri.substr (begin_str, range);
+  const size_t pos = uri.find (":");
+  if (pos == std::string::npos)
+    uri = uri.insert (0, "sip:");
+  return uri;
+}
+
+
+/* we call the presence core for help, which needs a smart pointer
+ * to this... so we make a dummy one!
+ */
+struct null_deleter
+{
+    void operator()(void const *) const
+    {
+    }
+};
+
+
+static xmlNodePtr
+Opal::Presentity::build_node (const std::string name,
+                             const std::string uri,
+                             const std::set<std::string> groups)
+{
+  xmlNodePtr node = xmlNewNode (NULL, BAD_CAST "entry");
+  xmlSetProp (node, BAD_CAST "uri", BAD_CAST uri.c_str ());
+  xmlNewChild (node, NULL,
+              BAD_CAST "name",
+              BAD_CAST robust_xmlEscape (node->doc,
+                                         name).c_str ());
+  for (std::set<std::string>::const_iterator iter = groups.begin ();
+       iter != groups.end ();
+       ++iter)
+    xmlNewChild (node, NULL,
+                BAD_CAST "group",
+                BAD_CAST robust_xmlEscape (node->doc,
+                                           *iter).c_str ());
+
+  return node;
+}
+
+
+Opal::Presentity::Presentity (boost::weak_ptr<Opal::Cluster> cluster_,
+                             boost::weak_ptr<Ekiga::PresenceCore> presence_core_,
+                             boost::shared_ptr<xmlDoc> doc_,
+                             xmlNodePtr node_):
+  cluster(cluster_),
+  presence_core(presence_core_),
+  doc(doc_),
+  node(node_),
+  presence("unknown")
+{
+}
+
+
+Opal::Presentity::~Presentity ()
+{
+}
+
+
+const std::string
+Opal::Presentity::get_name () const
+{
+  std::string name;
+  xmlChar* xml_str = NULL;
+
+  for (xmlNodePtr child = node->children ;
+       child != NULL ;
+       child = child->next) {
+
+    if (child->type == XML_ELEMENT_NODE
+        && child->name != NULL) {
+
+      if (xmlStrEqual (BAD_CAST ("name"), child->name)) {
+
+       xml_str = xmlNodeGetContent (child);
+       if (xml_str != NULL) {
+
+         name = (const char*)xml_str;
+         xmlFree (xml_str);
+       } else {
+
+         name = _("Unnamed");
+       }
+      }
+    }
+  }
+
+  return name;
+}
+
+
+const std::string
+Opal::Presentity::get_presence () const
+{
+  return presence;
+}
+
+
+const std::string
+Opal::Presentity::get_status () const
+{
+  return status;
+}
+
+
+const std::set<std::string>
+Opal::Presentity::get_groups () const
+{
+  std::set<std::string> groups;
+
+  for (xmlNodePtr child = node->children ;
+       child != NULL ;
+       child = child->next) {
+
+    if (child->type == XML_ELEMENT_NODE
+        && child->name != NULL) {
+
+      if (xmlStrEqual (BAD_CAST ("group"), child->name)) {
+
+       xmlChar* xml_str = xmlNodeGetContent (child);
+       if (xml_str != NULL) {
+
+         groups.insert ((const char*) xml_str);
+         xmlFree (xml_str);
+       }
+      }
+    }
+  }
+
+  return groups;
+}
+
+
+const std::string
+Opal::Presentity::get_uri () const
+{
+  std::string uri;
+  xmlChar* xml_str = NULL;
+
+  xml_str = xmlGetProp (node, BAD_CAST "uri");
+  if (xml_str != NULL) {
+
+    uri = (const char*)xml_str;
+    xmlFree (xml_str);
+  }
+
+  return uri;
+}
+
+
+bool
+Opal::Presentity::has_uri (const std::string uri) const
+{
+  return uri == get_uri ();
+}
+
+
+void
+Opal::Presentity::set_presence (const std::string presence_)
+{
+  presence = presence_;
+  updated ();
+}
+
+
+void
+Opal::Presentity::set_status (const std::string status_)
+{
+  status = status_;
+  updated ();
+}
+
+
+bool
+Opal::Presentity::populate_menu (Ekiga::MenuBuilder &builder)
+{
+  bool populated = false;
+  boost::shared_ptr<Ekiga::PresenceCore> pcore = presence_core.lock ();
+
+  if (!pcore)
+    return false;
+
+  populated
+    = pcore->populate_presentity_menu (PresentityPtr(this, null_deleter ()),
+                                      get_uri (), builder);
+
+  if (populated)
+    builder.add_separator ();
+
+  builder.add_action ("edit", _("_Edit"),
+                     boost::bind (&Opal::Presentity::edit_presentity, this));
+  builder.add_action ("remove", _("_Remove"),
+                     boost::bind (&Opal::Presentity::remove, this));
+
+  return true;
+}
+
+
+void
+Opal::Presentity::edit_presentity ()
+{
+  ClusterPtr cluster = cluster.lock ();
+
+  if (!cluster)
+    return;
+
+  boost::shared_ptr<Ekiga::FormRequestSimple> request = boost::shared_ptr<Ekiga::FormRequestSimple> (new 
Ekiga::FormRequestSimple (boost::bind (&Opal::Presentity::edit_presentity_form_submitted, this, _1, _2)));
+
+  std::string name = get_name ();
+  std::string uri = get_uri ();
+  std::set<std::string> groups = get_groups ();
+  std::set<std::string> all_groups = cluster->existing_groups ();
+
+  request->title (_("Edit roster element"));
+  request->instructions (_("Please fill in this form to change an existing "
+                          "element of ekiga's internal roster"));
+  request->text ("name", _("Name:"), name, _("Name of the contact, as shown in your roster"));
+  request->text ("uri", _("Address:"), uri, _("Address, e.g. sip:xyz ekiga net; if you do not specify the 
host part, e.g. sip:xyz, then you can choose it by right-clicking on the contact in roster"));
+
+  request->editable_set ("groups", _("Choose groups:"),
+                        groups, all_groups);
+
+  questions (request);
+}
+
+
+void
+Opal::Presentity::edit_presentity_form_submitted (bool submitted,
+                                                 Ekiga::Form &result)
+{
+  if (!submitted)
+    return;
+
+  const std::string new_name = result.text ("name");
+  const std::set<std::string> groups = get_groups ();
+  const std::set<std::string> new_groups = result.editable_set ("groups");
+  std::string new_uri = result.text ("uri");
+  const std::string uri = get_uri ();
+  std::set<xmlNodePtr> nodes_to_remove;
+
+  new_uri = canonize_uri (new_uri);
+
+  for (xmlNodePtr child = node->children ;
+       child != NULL ;
+       child = child->next) {
+
+    if (child->type == XML_ELEMENT_NODE
+        && child->name != NULL) {
+
+      if (xmlStrEqual (BAD_CAST ("name"), child->name)) {
+
+       robust_xmlNodeSetContent (node, &child, "name", new_name);
+      }
+    }
+  }
+
+  if (uri != new_uri) {
+
+    presence = "unknown";
+    xmlSetProp (node, (const xmlChar*)"uri", (const xmlChar*)new_uri.c_str ());
+  }
+
+  // the first loop looks at groups we were in : are we still in ?
+  for (xmlNodePtr child = node->children ;
+       child != NULL ;
+       child = child->next) {
+
+    if (child->type == XML_ELEMENT_NODE
+        && child->name != NULL) {
+
+      if (xmlStrEqual (BAD_CAST ("group"), child->name)) {
+
+       xmlChar* xml_str = xmlNodeGetContent (child);
+
+       if (xml_str != NULL) {
+
+         if (new_groups.find ((const char*) xml_str) == new_groups.end ()) {
+
+           nodes_to_remove.insert (child); // don't free what we loop on!
+         }
+         xmlFree (xml_str);
+       }
+      }
+    }
+  }
+
+  // ok, now we can clean up!
+  for (std::set<xmlNodePtr>::iterator iter = nodes_to_remove.begin ();
+       iter != nodes_to_remove.end ();
+       ++iter) {
+
+    xmlUnlinkNode (*iter);
+    xmlFreeNode (*iter);
+  }
+
+  // the second loop looking for groups we weren't in but are now
+  for (std::set<std::string>::const_iterator iter = new_groups.begin ();
+       iter != new_groups.end ();
+       iter++) {
+
+    if (std::find (groups.begin (), groups.end (), *iter) == groups.end ()) {
+      xmlNewChild (node, NULL,
+                  BAD_CAST "group",
+                  BAD_CAST robust_xmlEscape (node->doc, *iter).c_str ());
+    }
+  }
+
+  updated ();
+  trigger_saving ();
+}
+
+
+void
+Opal::Presentity::rename_group (const std::string old_name,
+                               const std::string new_name)
+{
+  bool old_name_present = false;
+  bool already_in_new_name = false;
+  std::set<xmlNodePtr> nodes_to_remove;
+
+  /* remove the old name's node
+   * and check if we aren't already in the new name's group
+   */
+  for (xmlNodePtr child = node->children ;
+       child != NULL ;
+       child = child->next) {
+
+    if (child->type == XML_ELEMENT_NODE
+        && child->name != NULL) {
+
+      if (xmlStrEqual (BAD_CAST ("group"), child->name)) {
+
+       xmlChar* xml_str = xmlNodeGetContent (child);
+
+       if (xml_str != NULL) {
+
+         if (!xmlStrcasecmp ((const xmlChar*)old_name.c_str (), xml_str)) {
+           nodes_to_remove.insert (child); // don't free what we loop on!
+            old_name_present = true;
+         }
+
+         if (!xmlStrcasecmp ((const xmlChar*)new_name.c_str (), xml_str)) {
+           already_in_new_name = true;
+         }
+
+         xmlFree (xml_str);
+       }
+      }
+    }
+  }
+
+  // ok, now we can clean up!
+  for (std::set<xmlNodePtr>::iterator iter = nodes_to_remove.begin ();
+       iter != nodes_to_remove.end ();
+       ++iter) {
+
+    xmlUnlinkNode (*iter);
+    xmlFreeNode (*iter);
+  }
+
+  if (old_name_present && !already_in_new_name) {
+
+    xmlNewChild (node, NULL,
+                BAD_CAST "group",
+                BAD_CAST robust_xmlEscape (node->doc,
+                                           new_name).c_str ());
+
+  }
+
+  updated ();
+  trigger_saving ();
+}
+
+
+void
+Opal::Presentity::remove ()
+{
+  xmlUnlinkNode (node);
+  xmlFreeNode (node);
+
+  trigger_saving ();
+  removed ();
+}
diff --git a/lib/engine/components/opal/opal-presentity.h b/lib/engine/components/opal/opal-presentity.h
new file mode 100644
index 0000000..76f7326
--- /dev/null
+++ b/lib/engine/components/opal/opal-presentity.h
@@ -0,0 +1,142 @@
+
+/*
+ * Ekiga -- A VoIP and Video-Conferencing application
+ * Copyright (C) 2000-2013 Damien Sandras <dsandras seconix com>
+
+ * This program is free software; you can  redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version. This program is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Ekiga is licensed under the GPL license and as a special exception, you
+ * have permission to link or otherwise combine this program with the
+ * programs OPAL, OpenH323 and PWLIB, and distribute the combination, without
+ * applying the requirements of the GNU GPL to the OPAL, OpenH323 and PWLIB
+ * programs, as long as you do follow the requirements of the GNU GPL for all
+ * the rest of the software thus combined.
+ */
+
+
+/*
+ *                         opal-presentity.h  -  description
+ *                         ------------------------------------------
+ *   begin                : written in 2013 by Julien Puydt
+ *   copyright            : (c) 2013 by Julien Puydt
+ *   description          : declaration of a presentity for an opal account roster
+ *
+ */
+
+
+
+#ifndef __OPAL_PRESENTITY_H__
+#define __OPAL_PRESENTITY_H__
+
+#include <libxml/tree.h>
+#include <boost/smart_ptr.hpp>
+
+#include "form.h"
+#include "presence-core.h"
+#include "presentity.h"
+
+namespace Opal
+{
+  class Cluster;
+
+  /* This class implements and Ekiga::Presentity, stored as a node
+   * in an XML document -- the code is relative to that node, so could
+   * probably be abstracted, should the need arise!
+   */
+
+  class Presentity: public Ekiga::Presentity
+  {
+  public:
+
+    /* build a node describing a valid presentity, which the caller
+     * will then use to create a valid instance using the ctor */
+    static xmlNodePtr build_node (const std::string name_,
+                                 const std::string uri_,
+                                 const std::set<std::string> groups_);
+
+    Presentity (boost::weak_ptr<Opal::Cluster> cluster_,
+               boost::weak_ptr<Ekiga::PresenceCore> presence_core_,
+               boost::shared_ptr<xmlDoc> doc_,
+               xmlNodePtr node_);
+
+    ~Presentity ();
+
+    /* methods of the general Ekiga::Presentity class: */
+
+    const std::string get_name () const;
+
+    const std::string get_presence () const;
+
+    const std::string get_status () const;
+
+    const std::set<std::string> get_groups () const;
+
+    const std::string get_uri () const;
+
+    bool has_uri (const std::string uri) const;
+
+    bool populate_menu (Ekiga::MenuBuilder &);
+
+    /* setter methods specific for this class of presentity, where we
+     * expect to get presence from elsewhere:
+     */
+
+    void set_presence (const std::string presence_);
+
+    void set_status (const std::string status_);
+
+    // method to rename a group for this presentity
+    void rename_group (const std::string old_name,
+                      const std::string new_name);
+
+    /* Those allow the parent Opal::Heap to manage this presentity
+     * effectively.
+     *
+     * First, the 'trigger_saving' signal is to let the heap know that
+     * the live XML document was modified and hence should be saved.
+     *
+     * The 'remove' method asks the presentity to remove all its data
+     * from the live XML document, emit 'trigger_saving' so those
+     * changes get written in the XML source document, and finally
+     * emit 'removed' so the views (and the heap) forget about it as a
+     * presentity.
+     * 
+     */
+    boost::signals2::signal<void(void)> trigger_saving;
+    void remove ();
+
+  private:
+
+    /* this pair of method is to let the user edit the presentity with
+     * a nice form
+     */
+    void edit_presentity ();
+    void edit_presentity_form_submitted (bool submitted,
+                                        Ekiga::Form& result);
+
+    // FIXME: this is stupid! We only need the cluster to get a list of
+    // existing groups in the edit presentity form!
+    boost::weak_ptr<Opal::Cluster> cluster;
+    boost::weak_ptr<Ekiga::PresenceCore> presence_core;
+    boost::shared_ptr<xmlDoc> doc;
+    xmlNodePtr node;
+
+    std::string presence;
+    std::string status;
+  };
+
+  typedef boost::shared_ptr<Presentity> PresentityPtr;
+
+};
+
+#endif


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