Hello,

Included in the second version of the roomlist patch based on the
previous feedback.

I believe all the points were addressed, although some oddities
remain:

1.  Conversation.cpp was updated to store the Conversation in the
PurpleConversation's ui_data, as suggested.  The roomlist ui_ops
utilize this ui_data.  But, findConversation was left as it was,
because it's depended on by other code.  I looked at removing it, but
it would take some non-trivial refactoring.

2.  PurpleConvChatBuddy's ui_data now refer's to the Buddy object as
suggested.  But, when the "remove_users" ui_op is called, only the
user string is passed along, and the PurpleConvChatBuddy object has
already been destroyed (verified by looking at libpurple source) along
with it's ui_data.  So, a username->Buddy* map was necessary to handle
deletes.  It's a little strange to use this map for some ops, and
purple_conv_chat_cb_find() for others, but I don't see a way around
this, other than storing the name in Buddy and then iterating through
"children" widgets and looking them up that way.  Either way,
duplicate information is stored.

3.  I'm using Container::moveWidgetBefore/After to rearrange the
buddies for sorting purposes on events.  Strangely, I had to set
ListBox::reposition_widgets to "true" before calling this.  I'm not
sure if this is the intended way to do this, or if there's a "refresh"
bug in Container or ListBox.  There's also some refresh "flickering"
when widgets get moved around, so I wouldn't be surprised if I'm
working around a bug or not using the api correctly.

If this looks good, the next steps will be to:

1. add the ability to focus and navigate the room list
2. figure out how to display to the user that the list can be
   scrolled.  (a previous email suggested a scroll bar.  The buddy
   list uses a dangling treeview branch to signify additional entries
   off screen).
3. map a key to start a conversation with the room list buddy
4. add a context menu for the buddy with additional operations

Feedback?

I'm also awaiting the go-ahead to push non roomlist related commits
from [1].

Wade

1. https://github.com/wberrier/centerim5/commits/mob

------------------------

>From d59ffc8827d79cbe7a9f5f220ae47be1dcbc3642 Mon Sep 17 00:00:00 2001
From: Wade Berrier <wberr...@gmail.com>
Date: Tue, 10 Mar 2015 22:45:23 -0600
Subject: [PATCH] Conversations: Add rough room listing implementation for chat
 conversations

Still some shortcomings:
-width of room list should only be large enough to accomodate names
-should be able to focus into list and scroll
-eventually have a context menu on each room buddy
---
 src/CMakeLists.txt           |   2 +
 src/CenterIM.cpp             |   3 +
 src/Conversation.cpp         |  37 +++++-
 src/Conversation.h           |   8 ++
 src/ConversationRoomList.cpp | 282 +++++++++++++++++++++++++++++++++++++++++++
 src/ConversationRoomList.h   | 117 ++++++++++++++++++
 src/Conversations.cpp        |  78 +++++++++++-
 src/Conversations.h          |  16 +++
 src/Makefile.am              |   2 +
 9 files changed, 536 insertions(+), 9 deletions(-)
 create mode 100644 src/ConversationRoomList.cpp
 create mode 100644 src/ConversationRoomList.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index aa58d36..9abb85c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,6 +9,7 @@ set(centerim5_SOURCES
   CenterMain.cpp
   Connections.cpp
   Conversation.cpp
+  ConversationRoomList.cpp
   Conversations.cpp
   Footer.cpp
   GeneralMenu.cpp
@@ -31,6 +32,7 @@ set(centerim5_HEADERS
   CenterIM.h
   Connections.h
   Conversation.h
+  ConversationRoomList.h
   Conversations.h
   Footer.h
   GeneralMenu.h
diff --git a/src/CenterIM.cpp b/src/CenterIM.cpp
index 3c8c5c6..9f1f222 100644
--- a/src/CenterIM.cpp
+++ b/src/CenterIM.cpp
@@ -685,6 +685,9 @@ void CenterIM::loadDefaultColorSchemeConfig()
   COLORSCHEME->setColorPair("conversation", "horizontalline", "line",
       CppConsUI::Curses::Color::BLUE, CppConsUI::Curses::Color::DEFAULT,
       CppConsUI::Curses::Attr::BOLD);
+  COLORSCHEME->setColorPair("conversation", "verticalline", "line",
+      CppConsUI::Curses::Color::BLUE, CppConsUI::Curses::Color::DEFAULT,
+      CppConsUI::Curses::Attr::BOLD);
   COLORSCHEME->setColorPair("conversation", "textedit", "text",
       CppConsUI::Curses::Color::CYAN, CppConsUI::Curses::Color::DEFAULT);
 
diff --git a/src/Conversation.cpp b/src/Conversation.cpp
index ac352b1..995dda2 100644
--- a/src/Conversation.cpp
+++ b/src/Conversation.cpp
@@ -30,7 +30,7 @@
 
 Conversation::Conversation(PurpleConversation *conv_)
 : Window(0, 0, 80, 24), conv(conv_), filename(NULL), logfile(NULL)
-, input_text_length(0)
+, input_text_length(0), room_list(NULL), room_list_line(NULL)
 {
   g_assert(conv);
 
@@ -48,6 +48,16 @@ Conversation::Conversation(PurpleConversation *conv_)
   addWidget(*view, 1, 0);
   addWidget(*input, 1, 1);
   addWidget(*line, 0, height);
+
+  PurpleConversationType type = purple_conversation_get_type(conv_);
+  if (type == PURPLE_CONV_TYPE_CHAT) {
+      room_list = new ConversationRoomList(1, 1, conv_);
+      room_list_line = new CppConsUI::VerticalLine(1);
+
+      addWidget(*room_list, 1, 0);
+      addWidget(*room_list_line, 1, 0);
+  }
+
   input->grabFocus();
 
   // open logfile
@@ -84,10 +94,10 @@ void Conversation::moveResize(int newx, int newy, int neww, 
int newh)
 {
   Window::moveResize(newx, newy, neww, newh);
 
-  int percentage = purple_prefs_get_int(CONF_PREFIX "/chat/partitioning");
-  percentage = CLAMP(percentage, 0, 100);
+  int view_percentage = purple_prefs_get_int(CONF_PREFIX "/chat/partitioning");
+  view_percentage = CLAMP(view_percentage, 0, 100);
 
-  int view_height = (height * percentage) / 100;
+  int view_height = (height * view_percentage) / 100;
   if (view_height < 1)
     view_height = 1;
 
@@ -95,9 +105,26 @@ void Conversation::moveResize(int newx, int newy, int neww, 
int newh)
   if (input_height < 1)
     input_height = 1;
 
-  view->moveResize(1, 0, width - 2, view_height);
+  int roomlist_percentage = purple_prefs_get_int(CONF_PREFIX 
"/chat/roomlist_partitioning");
+  roomlist_percentage = CLAMP(roomlist_percentage, 0, 100);
+
+  int view_width = width - 2;
+  if(room_list) {
+      view_width = (view_width * roomlist_percentage) / 100;
+  }
+
+  view->moveResize(1, 0, view_width, view_height);
+
   input->moveResize(1, view_height + 1, width - 2, input_height);
   line->moveResize(0, view_height, width, 1);
+
+  // place the room list if a conversation window
+  if(room_list) {
+    // +2 accounts for borders
+    room_list_line->moveResize(view_width + 1, 0, 1, view_height);
+    // Give it some padding to make it line up
+    room_list->moveResize(view_width + 3, 0, width - view_width - 3, 
view_height);
+  }
 }
 
 bool Conversation::restoreFocus()
diff --git a/src/Conversation.h b/src/Conversation.h
index a5ef382..7d94f9e 100644
--- a/src/Conversation.h
+++ b/src/Conversation.h
@@ -23,8 +23,10 @@
 #define __CONVERSATION_H__
 
 #include "Log.h"
+#include "ConversationRoomList.h"
 
 #include <cppconsui/AbstractLine.h>
+#include <cppconsui/VerticalLine.h>
 #include <cppconsui/TextEdit.h>
 #include <cppconsui/TextView.h>
 #include <cppconsui/Window.h>
@@ -55,6 +57,8 @@ public:
 
   PurpleConversation *getPurpleConversation() const { return conv; };
 
+  ConversationRoomList *getRoomList() const { return room_list; };
+
 protected:
   class ConversationLine
   : public CppConsUI::AbstractLine
@@ -79,6 +83,10 @@ protected:
   CppConsUI::TextEdit *input;
   ConversationLine *line;
 
+  // Only PURPLE_CONV_TYPE_CHAT have a room list
+  ConversationRoomList *room_list;
+  CppConsUI::VerticalLine *room_list_line;
+
   PurpleConversation *conv;
 
   char *filename;
diff --git a/src/ConversationRoomList.cpp b/src/ConversationRoomList.cpp
new file mode 100644
index 0000000..02592a2
--- /dev/null
+++ b/src/ConversationRoomList.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2015 by Wade Berrier <wberr...@gmail.com>
+ * Copyright (C) 2010-2015 by CenterIM developers
+ *
+ * This file is part of CenterIM.
+ *
+ * CenterIM 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.
+ *
+ * CenterIM 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ConversationRoomList.h"
+
+// Move the widget to a position according to sorting function
+void ConversationRoomList::moveToSortedPosition(Buddy *new_buddy)
+{
+  Buddy * buddy = NULL;
+  Children::iterator iter;
+
+  g_assert(new_buddy);
+
+  for(iter = children.begin(); iter != children.end(); ++iter) {
+
+    buddy = dynamic_cast<Buddy*>(iter->widget);
+
+    // Should never happen...
+    g_assert(buddy);
+
+    // Since new_buddy is in list already, must skip
+    if(*buddy == *new_buddy)
+      continue;
+
+    // Break once insertion point is found
+    if(!Buddy::less_than_op_away_name(*buddy, *new_buddy))
+      break;
+  }
+
+  // Don't change anything if wanting to put into the same position
+  // (also returns on single node case)
+  if(*buddy == *new_buddy) {
+    return;
+  }
+
+  // TODO: HACK? Seems to be required to have the ListBox
+  //   reorder the widgets after calling moveWidget*
+  //   Doesn't seem right...
+  reposition_widgets = true;
+
+  // Insert after if at end
+  if(iter == children.end()) {
+    moveWidgetAfter(*new_buddy, *buddy);
+  }
+  else {
+    moveWidgetBefore(*new_buddy, *buddy);
+  }
+
+}
+
+void ConversationRoomList::add_users(GList *cbuddies, gboolean new_arrivals)
+{
+  GList *l;
+  PurpleConvChatBuddy *pbuddy;
+  for (l = cbuddies; l != NULL; l = l->next) {
+
+    pbuddy = static_cast<PurpleConvChatBuddy*>(l->data);
+
+    Buddy * buddy = new Buddy(pbuddy);
+    buddy->setButtonText();
+    buddy_map_[pbuddy->name] = buddy;
+
+    appendWidget(*buddy);
+
+    moveToSortedPosition(buddy);
+  }
+}
+
+void ConversationRoomList::rename_user(const char *old_name,
+  const char *new_name, const char *new_alias)
+{
+  // The old PurpleConvChatBuddy is still valid while
+  // this function is executing
+  PurpleConvChatBuddy * old_pbuddy = purple_conv_chat_cb_find(
+    conv_->u.chat, old_name);
+
+  PurpleConvChatBuddy * new_pbuddy = purple_conv_chat_cb_find(
+    conv_->u.chat, new_name);
+
+  g_assert(old_pbuddy);
+  g_assert(new_pbuddy);
+
+  Buddy * buddy = static_cast<Buddy *>(old_pbuddy->ui_data);
+
+  g_assert(buddy);
+
+  // Update buddy
+  buddy->setPurpleBuddy(new_pbuddy);
+
+  // Update buddy map
+  buddy_map_.erase(old_name);
+  buddy_map_[new_name] = buddy;
+
+  // Move and then update
+  moveToSortedPosition(buddy);
+  buddy->setButtonText();
+}
+
+void ConversationRoomList::remove_users(GList *users)
+{
+  GList *l;
+  const char *name;
+  for (l = users; l != NULL; l = l->next) {
+
+    name = static_cast<const char *>(l->data);
+
+    // NOTE: can't remove purple_conv_chat_cb_find, because the user
+    //   and PurpleConvChatBuddy has already been removed
+
+    BuddyMapIter iter = buddy_map_.find(name);
+
+    if(buddy_map_.end() != iter) {
+
+        buddy_map_.erase(iter);
+        // NOTE: this deletes the buddy object
+        removeWidget(*iter->second);
+    }
+  }
+}
+
+void ConversationRoomList::update_user(const char *user)
+{
+  PurpleConvChatBuddy * pbuddy = purple_conv_chat_cb_find(conv_->u.chat, user);
+
+  g_assert(pbuddy);
+
+  Buddy * buddy = static_cast<Buddy *>(pbuddy->ui_data);
+
+  g_assert(buddy);
+
+  // Move and then update
+  moveToSortedPosition(buddy);
+  buddy->setButtonText();
+}
+
+ConversationRoomList::Buddy::Buddy(PurpleConvChatBuddy *pbuddy)
+  : CppConsUI::Button(AUTOSIZE, 1, "")
+  , pbuddy_(pbuddy)
+{
+  // Set ui data
+  pbuddy_->ui_data = static_cast<void*>(this);
+}
+
+ConversationRoomList::Buddy::~Buddy()
+{
+}
+
+void ConversationRoomList::Buddy::readFlags(bool &is_op, bool &is_typing,
+  bool &is_away) const
+{
+  g_assert(pbuddy_);
+
+  PurpleConvChatBuddyFlags flags = pbuddy_->flags;
+
+  // TODO: how about founder?  Does that matter?
+
+  is_op = (
+    ((flags & PURPLE_CBFLAGS_OP)      != 0)
+    );
+
+  is_typing = (
+    ((flags & PURPLE_CBFLAGS_TYPING)  != 0)
+    );
+
+#if PURPLE_VERSION_CHECK(2, 8, 0)
+  is_away = (
+    ((flags & PURPLE_CBFLAGS_AWAY)    != 0)
+    );
+#else
+  is_away = false;
+#endif
+}
+
+void ConversationRoomList::Buddy::setButtonText()
+{
+  setText(displayText().c_str());
+}
+
+std::string ConversationRoomList::Buddy::displayText() const
+{
+  std::string ret;
+
+  bool is_op(false);
+  bool is_typing(false);
+  bool is_away(false);
+
+  readFlags(is_op, is_typing, is_away);
+
+  // Does this even do anything?
+  ret += std::string("[") + (is_away ? "a" : "o") + "] ";
+
+  if(is_op) ret += "@";
+
+  ret += displayName();
+
+  if(is_typing)
+    ret += "*";
+
+  // TODO: elide long names?
+
+  return ret;
+}
+
+std::string ConversationRoomList::Buddy::displayName() const
+{
+  g_assert(pbuddy_);
+
+  // prefer alias
+  // NOTE: pbuddy_->alias_key isn't used yet... (according to docs)
+  if(pbuddy_->alias != NULL)
+    return pbuddy_->alias;
+  else
+    return pbuddy_->name;
+}
+
+bool ConversationRoomList::Buddy::operator==(const Buddy &rhs)
+{
+  return pbuddy_ == rhs.pbuddy_;
+}
+
+void ConversationRoomList::Buddy::setPurpleBuddy(PurpleConvChatBuddy *pbuddy)
+{
+  pbuddy_ = pbuddy;
+  pbuddy_->ui_data = static_cast<void*>(this);
+}
+
+bool ConversationRoomList::Buddy::less_than_op_away_name(const Buddy &lhs,
+  const Buddy &rhs)
+{
+  // Sort order:
+  //
+  // 1. ops first
+  // 2. online (vs away)
+  // 3. name/alias
+
+  bool lhs_is_op, lhs_is_typing, lhs_is_away;
+  bool rhs_is_op, rhs_is_typing, rhs_is_away;
+
+  lhs.readFlags(lhs_is_op, lhs_is_typing, lhs_is_away);
+  rhs.readFlags(rhs_is_op, rhs_is_typing, rhs_is_away);
+
+  // Probably a more elegant way to do this
+  if(lhs_is_op && !rhs_is_op) {
+    return true;
+  }
+  else if(!lhs_is_op && rhs_is_op) {
+    return false;
+  }
+  // equal op or non-op status
+  else {
+    if(lhs_is_away && !rhs_is_away) {
+      return false;
+    }
+    else if(!lhs_is_away && rhs_is_away) {
+      return true;
+    }
+    // on equal online/away status
+    else {
+      return lhs.displayName() < rhs.displayName();
+    }
+  }
+}
+
+/* vim: set tabstop=2 shiftwidth=2 textwidth=78 expandtab : */
diff --git a/src/ConversationRoomList.h b/src/ConversationRoomList.h
new file mode 100644
index 0000000..0547bae
--- /dev/null
+++ b/src/ConversationRoomList.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 by Wade Berrier <wberr...@gmail.com>
+ * Copyright (C) 2010-2015 by CenterIM developers
+ *
+ * This file is part of CenterIM.
+ *
+ * CenterIM 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.
+ *
+ * CenterIM 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __CONVERSATION_ROOM_LIST_H__
+#define __CONVERSATION_ROOM_LIST_H__
+
+#include <cppconsui/ListBox.h>
+#include <cppconsui/Button.h>
+#include <libpurple/purple.h>
+
+#include <map>
+
+class ConversationRoomList
+: public CppConsUI::ListBox
+{
+public:
+  ConversationRoomList(int w, int h, PurpleConversation *conv)
+      : CppConsUI::ListBox(w, h)
+      , conv_(conv)
+  {}
+  virtual ~ConversationRoomList() {}
+
+  // Purple chatroom interfaces
+  void add_users(GList *cbuddies, gboolean new_arrivals);
+  void rename_user(const char *old_name, const char *new_name,
+    const char *new_alias);
+  void remove_users(GList *users);
+  void update_user(const char *user);
+
+protected:
+
+  PurpleConversation * conv_;
+
+  // Represents a widget as well as pointer to libpurple data
+  class Buddy : public CppConsUI::Button
+  {
+  public:
+
+    Buddy(PurpleConvChatBuddy *pbuddy);
+
+    virtual ~Buddy();
+
+    // set button text with displayName
+    void setButtonText();
+
+    // Use pbuddy info to generate button displayText
+    std::string displayText() const;
+
+    // Prefer alias if it exists, and fall back on name
+    std::string displayName() const;
+
+    // TODO
+    //void onActivate(Button &);
+
+    // Update purple buddy (for rename case)
+    void setPurpleBuddy(PurpleConvChatBuddy *pbuddy);
+
+    // Sorting method for: op/away/display_name
+    // if less than: give priority
+    // The idea that if more sorting methods are desired,
+    // they can be swapped out at runtime based on config
+    static bool less_than_op_away_name(const Buddy &lhs, const Buddy &rhs);
+
+    bool operator==(const Buddy &rhs);
+
+  private:
+
+    void readFlags(bool &is_op, bool &is_typing, bool &is_away) const;
+
+    // NOTE: when remove_users op is called, this pointer is invalidated!
+    PurpleConvChatBuddy *pbuddy_;
+
+    // Force public constructor
+    Buddy();
+
+    Buddy(const Buddy&);
+    Buddy& operator=(const Buddy&);
+  };
+
+  // Move buddy to sorted position
+  void moveToSortedPosition(Buddy *buddy);
+
+  // Have to keep this mapping to remove users
+  // because when libpurple calls remove_user, the user is already
+  // gone, along with the "ui_data"
+  // Otherwise could store "name" in Buddy and iterate through "children"
+  std::map<std::string, Buddy*> buddy_map_;
+  typedef std::map<std::string, Buddy*>::iterator BuddyMapIter;
+
+private:
+
+  ConversationRoomList(const ConversationRoomList&);
+  ConversationRoomList& operator=(const ConversationRoomList&);
+
+};
+
+#endif
+
+/* vim: set tabstop=2 shiftwidth=2 textwidth=78 expandtab : */
diff --git a/src/Conversations.cpp b/src/Conversations.cpp
index 3eb7657..bda66db 100644
--- a/src/Conversations.cpp
+++ b/src/Conversations.cpp
@@ -114,6 +114,7 @@ Conversations::Conversations()
   // init prefs
   purple_prefs_add_none(CONF_PREFIX "/chat");
   purple_prefs_add_int(CONF_PREFIX "/chat/partitioning", 80);
+  purple_prefs_add_int(CONF_PREFIX "/chat/roomlist_partitioning", 80);
   purple_prefs_add_bool(CONF_PREFIX "/chat/beep_on_msg", false);
 
   // send_typing caching
@@ -127,10 +128,11 @@ Conversations::Conversations()
   //centerim_conv_ui_ops.write_chat = ;
   //centerim_conv_ui_ops.write_im = ;
   centerim_conv_ui_ops.write_conv = write_conv_;
-  //centerim_conv_ui_ops.chat_add_users = ;
-  //centerim_conv_ui_ops.chat_rename_user = ;
-  //centerim_conv_ui_ops.chat_remove_users = ;
-  //centerim_conv_ui_ops.chat_update_user = ;
+  centerim_conv_ui_ops.chat_add_users = chat_add_users_;
+  centerim_conv_ui_ops.chat_rename_user = chat_rename_user_;
+  centerim_conv_ui_ops.chat_remove_users = chat_remove_users_;
+  centerim_conv_ui_ops.chat_update_user = chat_update_user_;
+
   centerim_conv_ui_ops.present = present_;
   //centerim_conv_ui_ops.has_focus = ;
   //centerim_conv_ui_ops.custom_smiley_add = ;
@@ -279,6 +281,8 @@ void Conversations::create_conversation(PurpleConversation 
*conv)
 
   Conversation *conversation = new Conversation(conv);
 
+  conv->ui_data = static_cast<void*>(conversation);
+
   ConvChild c;
   c.purple_conv = conv;
   c.conv = conversation;
@@ -347,6 +351,72 @@ void Conversations::write_conv(PurpleConversation *conv, 
const char *name,
   conversations[i].conv->write(name, alias, message, flags, mtime);
 }
 
+void Conversations::chat_add_users(PurpleConversation *conv, GList *cbuddies,
+    gboolean new_arrivals)
+{
+  g_return_if_fail(conv);
+
+  Conversation * conversation = static_cast<Conversation*>(conv->ui_data);
+
+  // unhandled conversation type
+  if (conversation == NULL)
+    return;
+
+  if(conversation->getRoomList())
+  {
+    conversation->getRoomList()->add_users(cbuddies, new_arrivals);
+  }
+}
+
+void Conversations::chat_rename_user(PurpleConversation *conv, const char 
*old_name,
+  const char *new_name, const char *new_alias)
+{
+  g_return_if_fail(conv);
+
+  Conversation * conversation = static_cast<Conversation*>(conv->ui_data);
+
+  // unhandled conversation type
+  if (conversation == NULL)
+    return;
+
+  if(conversation->getRoomList())
+  {
+    conversation->getRoomList()->rename_user(old_name, new_name, new_alias);
+  }
+}
+
+void Conversations::chat_remove_users(PurpleConversation *conv, GList *users)
+{
+  g_return_if_fail(conv);
+
+  Conversation * conversation = static_cast<Conversation*>(conv->ui_data);
+
+  // unhandled conversation type
+  if (conversation == NULL)
+    return;
+
+  if(conversation->getRoomList())
+  {
+    conversation->getRoomList()->remove_users(users);
+  }
+}
+
+void Conversations::chat_update_user(PurpleConversation *conv, const char 
*user)
+{
+  g_return_if_fail(conv);
+
+  Conversation * conversation = static_cast<Conversation*>(conv->ui_data);
+
+  // unhandled conversation type
+  if (conversation == NULL)
+    return;
+
+  if(conversation->getRoomList())
+  {
+    conversation->getRoomList()->update_user(user);
+  }
+}
+
 void Conversations::present(PurpleConversation *conv)
 {
   g_return_if_fail(conv);
diff --git a/src/Conversations.h b/src/Conversations.h
index 2127af5..7eb06c7 100644
--- a/src/Conversations.h
+++ b/src/Conversations.h
@@ -112,6 +112,16 @@ private:
       time_t mtime)
     { CONVERSATIONS->write_conv(conv, name, alias, message, flags,
         mtime); }
+  static void chat_add_users_(PurpleConversation *conv, GList *cbuddies,
+      gboolean new_arrivals)
+  { CONVERSATIONS->chat_add_users(conv, cbuddies, new_arrivals); }
+  static void chat_rename_user_(PurpleConversation *conv,
+      const char *old_name, const char *new_name, const char *new_alias)
+  { CONVERSATIONS->chat_rename_user(conv, old_name, new_name, new_alias); }
+  static void chat_remove_users_(PurpleConversation *conv, GList *users)
+  { CONVERSATIONS->chat_remove_users(conv, users); }
+  static void chat_update_user_(PurpleConversation *conv, const char *user)
+  { CONVERSATIONS->chat_update_user(conv, user); }
   static void present_(PurpleConversation *conv)
     { CONVERSATIONS->present(conv); }
 
@@ -120,6 +130,12 @@ private:
   void write_conv(PurpleConversation *conv, const char *name,
     const char *alias, const char *message, PurpleMessageFlags flags,
     time_t mtime);
+  void chat_add_users(PurpleConversation *conv, GList *cbuddies,
+    gboolean new_arrivals);
+  void chat_rename_user(PurpleConversation *conv, const char *old_name,
+    const char *new_name, const char *new_alias);
+  void chat_remove_users(PurpleConversation *conv, GList *users);
+  void chat_update_user(PurpleConversation *conv, const char *user);
   void present(PurpleConversation *conv);
 
   static void buddy_typing_(PurpleAccount *account, const char *who,
diff --git a/src/Makefile.am b/src/Makefile.am
index 7e0ea88..c4c96f4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,6 +19,8 @@ centerim5_SOURCES = \
        Connections.h \
        Conversation.cpp \
        Conversation.h \
+       ConversationRoomList.cpp \
+       ConversationRoomList.h \
        Conversations.cpp \
        Conversations.h \
        Footer.cpp \
-- 
1.8.3.1



-- 
_______________________________________________
Centerim-devel mailing list
Centerim-devel@centerim.org
http://centerim.org/mailman/listinfo/centerim-devel
http://www.centerim.org/

Reply via email to