/**
 * inf_text_gtk_viewport_set_active_user:
 * @viewport: A #InfTextGtkViewport.
 * @user: A user from @viewport's user table, or %NULL.
 *
 * Sets the user for which perspective to draw the viewport. The cursor
 * position for teh active user is not draws since it is assumed that the
 * viewport's "real" scrollbars match the active user's position.
 */
void
inf_text_gtk_viewport_set_active_user(InfTextGtkViewport* viewport,
                                      InfTextUser* user)
{
  InfTextGtkViewportPrivate* priv;
  InfTextUser* active_user;

  g_return_if_fail(INF_TEXT_GTK_IS_VIEWPORT(viewport));
  g_return_if_fail(user == NULL || INF_TEXT_IS_USER(user));

  priv = INF_TEXT_GTK_VIEWPORT_PRIVATE(viewport);
  g_return_if_fail(
    user == NULL ||
    inf_user_table_lookup_user_by_id(
      priv->user_table,
      inf_user_get_id(INF_USER(user))
    ) == INF_USER(user)
  );

  if(priv->active_user != NULL)
  {
    active_user = priv->active_user;
    priv->active_user = NULL;

    inf_text_gtk_viewport_user_added(viewport, active_user);
  }

  if(user != NULL)
  {
    inf_text_gtk_viewport_user_removed(viewport, user);
  }

  priv->active_user = user;
  g_object_notify(G_OBJECT(viewport), "active-user");
}
Example #2
0
/**
 * inf_text_buffer_insert_text:
 * @buffer: A #InfTextBuffer.
 * @pos: A character offset into @buffer.
 * @text (type=guint8*) (array length=bytes) (transfer none): A pointer to
 * the text to insert.
 * @len: The length (in characters) of @text.
 * @bytes: The length (in bytes) of @text.
 * @user: (allow-none): A #InfUser that has inserted the new text, or %NULL.
 *
 * Inserts @text into @buffer as written by @author. @text must be encoded in
 * the character encoding of the buffer, see inf_text_buffer_get_encoding().
 **/
void
inf_text_buffer_insert_text(InfTextBuffer* buffer,
                            guint pos,
                            gconstpointer text,
                            gsize bytes,
                            guint len,
                            InfUser* user)
{
  InfTextBufferInterface* iface;
  InfTextChunk* chunk;

  g_return_if_fail(INF_TEXT_IS_BUFFER(buffer));
  g_return_if_fail(text != NULL);
  g_return_if_fail(user == NULL || INF_IS_USER(user));

  iface = INF_TEXT_BUFFER_GET_IFACE(buffer);
  g_return_if_fail(iface->insert_text != NULL);

  chunk = inf_text_chunk_new(inf_text_buffer_get_encoding(buffer));

  inf_text_chunk_insert_text(
    chunk,
    0,
    text,
    bytes,
    len,
    user == NULL ? 0 : inf_user_get_id(user)
  );

  iface->insert_text(buffer, pos, chunk, user);

  inf_text_chunk_free(chunk);
}
Example #3
0
void Gobby::ChatSessionView::set_active_user(InfUser* user)
{
	g_assert(
		user == NULL ||
		inf_user_table_lookup_user_by_id(
			inf_session_get_user_table(INF_SESSION(m_session)),
			inf_user_get_id(INF_USER(user)))
		== INF_USER(user));

	inf_gtk_chat_set_active_user(m_chat, user);
	active_user_changed(user);
}
static void
inf_adopted_session_execute_request_cb(InfAdoptedAlgorithm* algorithm,
                                       InfAdoptedUser* user,
                                       InfAdoptedRequest* request,
                                       gboolean apply,
                                       gpointer user_data)
{
  InfAdoptedSession* session;
  InfAdoptedSessionPrivate* priv;
  GSList* item;
  InfAdoptedSessionLocalUser* local;
  guint id;

  session = INF_ADOPTED_SESSION(user_data);
  priv = INF_ADOPTED_SESSION_PRIVATE(session);

  if(inf_adopted_request_affects_buffer(request))
  {
    id = inf_adopted_request_get_user_id(request);

    /* A request has been executed, meaning we are no longer up to date. Send
     * a noop in some time, so that others know what we already processed. */
    for(item = priv->local_users; item != NULL; item = g_slist_next(item))
    {
      local = (InfAdoptedSessionLocalUser*)item->data;
      if(local->noop_time == 0)
        /* Except we issued the request ourselves, of course. */
        if(inf_user_get_id(INF_USER(local->user)) != id)
          inf_adopted_session_start_noop_timer(session, local);
    }
  }

  /* Mark inactive users active if they do something */
  /* Note: This behaviour is implicitly performed by both client and server,
   * and requires no further network traffic. However, users explictely have
   * to be set inactive. */
  if(inf_adopted_request_get_request_type(request) != INF_ADOPTED_REQUEST_DO ||
     !INF_ADOPTED_IS_NO_OPERATION(inf_adopted_request_get_operation(request)))
  {
    /* TODO: We should offer a virtual function to flush all requests for
     * local users, either here or even in InfSession via a vfunc, so that
     * we don't accidentally make local users active by a delayed request. */
    if(inf_user_get_status(INF_USER(user)) == INF_USER_INACTIVE)
      g_object_set(G_OBJECT(user), "status", INF_USER_ACTIVE, NULL);
  }
}
static void
infd_note_plugin_text_session_write_foreach_user_func(InfUser* user,
                                                      gpointer user_data)
{
  xmlNodePtr parent;
  xmlNodePtr node;

  parent = (xmlNodePtr)user_data;
  node = xmlNewChild(parent, NULL, (const xmlChar*)"user", NULL);

  inf_xml_util_set_attribute_uint(node, "id", inf_user_get_id(user));
  inf_xml_util_set_attribute(node, "name", inf_user_get_name(user));
  inf_xml_util_set_attribute_double(
    node,
    "hue",
    inf_text_user_get_hue(INF_TEXT_USER(user))
  );
}
static void
inf_adopted_session_synchronization_complete_foreach_user_func(InfUser* user,
                                                               gpointer data)
{
  InfAdoptedRequestLog* log;
  log = inf_adopted_user_get_request_log(INF_ADOPTED_USER(user));

  /* Set begin index of empty request logs. Algorithm relies on
   * inf_adopted_request_log_get_begin() to return the index of the request
   * that will first be added to the request log. */
  if(inf_adopted_request_log_is_empty(log))
  {
    inf_adopted_request_log_set_begin(
      log,
      inf_adopted_state_vector_get(
        inf_adopted_user_get_vector(INF_ADOPTED_USER(user)),
        inf_user_get_id(user)
      )
    );
  }
}
Example #7
0
static void
inf_user_table_add_user_handler(InfUserTable* user_table,
                                InfUser* user)
{
  InfUserTablePrivate* priv;
  guint id;

  priv = INF_USER_TABLE_PRIVATE(user_table);
  id = inf_user_get_id(user);

  g_assert(id > 0);
  g_assert(g_hash_table_lookup(priv->table, GUINT_TO_POINTER(id)) == NULL);

  g_hash_table_insert(priv->table, GUINT_TO_POINTER(id), user);
  g_object_ref(user);

  g_signal_connect(
    G_OBJECT(user),
    "notify::status",
    G_CALLBACK(inf_user_table_check_local_cb),
    user_table
  );

  g_signal_connect(
    G_OBJECT(user),
    "notify::flags",
    G_CALLBACK(inf_user_table_check_local_cb),
    user_table
  );

  if(inf_user_table_is_local(user))
  {
    g_signal_emit(
      G_OBJECT(user_table),
      user_table_signals[ADD_LOCAL_USER],
      0,
      user
    );
  }
}
Example #8
0
static void
inf_user_table_remove_user_handler(InfUserTable* user_table,
                                   InfUser* user)
{
  InfUserTablePrivate* priv;
  guint id;

  priv = INF_USER_TABLE_PRIVATE(user_table);
  id = inf_user_get_id(user);

  if(inf_user_table_is_local(user))
  {
    g_signal_emit(
      G_OBJECT(user_table),
      user_table_signals[REMOVE_LOCAL_USER],
      0,
      user
    );
  }

  inf_user_table_unref_user(user_table, user);
  g_assert(g_hash_table_lookup(priv->table, GUINT_TO_POINTER(id)) == user);
  g_hash_table_remove(priv->table, GUINT_TO_POINTER(id));
}
static InfCommunicationScope
inf_adopted_session_process_xml_run(InfSession* session,
                                    InfXmlConnection* connection,
                                    const xmlNodePtr xml,
                                    GError** error)
{
  InfAdoptedSessionPrivate* priv;
  InfAdoptedSessionClass* session_class;
  InfAdoptedRequest* request;
  InfAdoptedUser* user;

  gboolean has_num;
  guint num;
  GError* local_error;
  InfAdoptedRequest* copy_req;
  guint i;

  priv = INF_ADOPTED_SESSION_PRIVATE(session);

  if(strcmp((const char*)xml->name, "request") == 0)
  {
    session_class = INF_ADOPTED_SESSION_GET_CLASS(session);
    g_assert(session_class->xml_to_request != NULL);

    user = inf_adopted_session_user_from_request_xml(
      INF_ADOPTED_SESSION(session),
      xml,
      error
    );

    if(user == NULL)
      return INF_COMMUNICATION_SCOPE_PTP;

    if(inf_user_get_status(INF_USER(user)) == INF_USER_UNAVAILABLE ||
       inf_user_get_connection(INF_USER(user)) != connection)
    {
      g_set_error(
        error,
        inf_user_error_quark(),
        INF_USER_ERROR_NOT_JOINED,
        "%s",
        _("User did not join from this connection")
      );

      return INF_COMMUNICATION_SCOPE_PTP;
    }

    local_error = NULL;
    has_num = inf_xml_util_get_attribute_uint(xml, "num", &num, &local_error);
    if(local_error != NULL)
    {
      g_propagate_error(error, local_error);
      return INF_COMMUNICATION_SCOPE_PTP;
    }
    
    request = session_class->xml_to_request(
      INF_ADOPTED_SESSION(session),
      xml,
      inf_adopted_user_get_vector(user),
      FALSE,
      error
    );

    if(request == NULL)
      return INF_COMMUNICATION_SCOPE_PTP;

    inf_adopted_algorithm_receive_request(priv->algorithm, request);

    /* Apply the request more than once if num is given. This is mostly used
     * for multiple undos and redos, but is in general allowed for any
     * request. */
    if(has_num)
    {
      for(i = 1; i < num; ++i)
      {
        /* TODO: This is a bit of a hack since requests are normally
         * immutable. It avoids an additional vector copy here though. */
        copy_req = inf_adopted_request_copy(request);

        inf_adopted_state_vector_add(
          inf_adopted_request_get_vector(copy_req),
          inf_user_get_id(INF_USER(user)),
          i
        );

        inf_adopted_algorithm_receive_request(priv->algorithm, copy_req);
        g_object_unref(copy_req);
      }
    }

    g_object_unref(request);

    /* Requests can always be forwarded since user is given. */
    return INF_COMMUNICATION_SCOPE_GROUP;
  }

  return INF_SESSION_CLASS(parent_class)->process_xml_run(
    session,
    connection,
    xml,
    error
  );
}
static gboolean
infinoted_plugin_transformation_protection_check_request_cb(InfAdoptedSession* session,
                                                            InfAdoptedRequest* request,
                                                            InfAdoptedUser* user,
                                                            gpointer user_data)
{
  InfinotedPluginTransformationProtectionSessionInfo* info;
  guint vdiff;
  InfXmlConnection* connection;
  gchar* request_str;
  gchar* current_str;
  gchar* remote_id;
  gchar* path;

  info = (InfinotedPluginTransformationProtectionSessionInfo*)user_data;

  vdiff = inf_adopted_state_vector_vdiff(
    inf_adopted_request_get_vector(request),
    inf_adopted_algorithm_get_current(
      inf_adopted_session_get_algorithm(session)
    )
  );

  if(vdiff > info->plugin->max_vdiff)
  {
    connection = inf_user_get_connection(INF_USER(user));

    /* Local requests do not need to be transformed, so always have a
     * zero vdiff. */
    g_assert(connection != NULL);

    /* Kill the connection */
    infd_session_proxy_unsubscribe(
      INFD_SESSION_PROXY(info->proxy),
      connection
    );

    /* Write a log message */
    path = inf_browser_get_path(
      INF_BROWSER(
        infinoted_plugin_manager_get_directory(info->plugin->manager)
      ),
      &info->iter
    );

    request_str = inf_adopted_state_vector_to_string(
      inf_adopted_request_get_vector(request)
    );

    current_str = inf_adopted_state_vector_to_string(
      inf_adopted_algorithm_get_current(
        inf_adopted_session_get_algorithm(session)
      )
    );

    g_object_get(G_OBJECT(connection), "remote-id", &remote_id, NULL);

    infinoted_log_warning(
      infinoted_plugin_manager_get_log(info->plugin->manager),
      _("In document \"%s\": Attempt to transform request \"%s\" to current state \"%s\" "
        "(vdiff=%u) by user \"%s\" (id=%u, conn=%s). Maximum allowed is %u; the "
        "connection has been unsubscribed."),
      path,
      request_str,
      current_str,
      vdiff,
      inf_user_get_name(INF_USER(user)),
      inf_user_get_id(INF_USER(user)),
      remote_id,
      info->plugin->max_vdiff
    );

    g_free(path);
    g_free(request_str);
    g_free(current_str);
    g_free(remote_id);

    /* Prevent the request from being transformed */
    return TRUE;
  }

  return FALSE;
}