示例#1
0
/**
 * inf_adopted_request_mirror:
 * @request: A #InfAdoptedRequest.
 * @by: The number of requests between the original and the mirrored
 * operation.
 *
 * Mirrors @request as described in "Reducing the Problems of Group Undo" by
 * Matthias Ressel and Rul Gunzenhäuser
 * (http://portal.acm.org/citation.cfm?doid=320297.320312).
 *
 * Note that @by is the total amount of requests between the original and
 * mirrored request, and thus equivalent to 2j-1 in the paper's definition.
 *
 * @request must be of type %INF_ADOPTED_REQUEST_DO and its operation must
 * be reversible.
 *
 * Returns: The mirrored request as a new #InfAdoptedRequest.
 **/
InfAdoptedRequest*
inf_adopted_request_mirror(InfAdoptedRequest* request,
                           guint by)
{
    InfAdoptedRequestPrivate* priv;
    InfAdoptedOperation* new_operation;
    InfAdoptedStateVector* new_vector;
    InfAdoptedRequest* new_request;

    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL);
    g_return_val_if_fail(by % 2 == 1, NULL);

    priv = INF_ADOPTED_REQUEST_PRIVATE(request);
    g_return_val_if_fail(priv->type == INF_ADOPTED_REQUEST_DO, NULL);
    g_return_val_if_fail(
        inf_adopted_operation_is_reversible(priv->operation),
        NULL
    );

    new_operation = inf_adopted_operation_revert(priv->operation);
    new_vector = inf_adopted_state_vector_copy(priv->vector);
    inf_adopted_state_vector_add(new_vector, priv->user_id, by);

    new_request = inf_adopted_request_new_do(
                      new_vector,
                      priv->user_id,
                      new_operation
                  );

    g_object_unref(new_operation);
    inf_adopted_state_vector_free(new_vector);
    return new_request;
}
示例#2
0
void Gobby::UserJoinCommands::UserJoinInfo::
	add_text_user_properties(std::vector<GParameter>& params,
	                         TextSessionView& view)
{
	InfTextSession* session = view.get_session();

	GParameter hue_param = { "hue", { 0 } };
	g_value_init(&hue_param.value, G_TYPE_DOUBLE);
	g_value_set_double(&hue_param.value,
	                   m_commands.m_preferences.user.hue);
	params.push_back(hue_param);

	GParameter vector_param = { "vector", { 0 } };
	g_value_init(&vector_param.value, INF_ADOPTED_TYPE_STATE_VECTOR);

	g_value_take_boxed(&vector_param.value, inf_adopted_state_vector_copy(
		inf_adopted_algorithm_get_current(
			inf_adopted_session_get_algorithm(
				INF_ADOPTED_SESSION(session)))));
	params.push_back(vector_param);

	GParameter caret_param = { "caret-position", { 0 } };
	g_value_init(&caret_param.value, G_TYPE_UINT);

	GtkTextBuffer* buffer = GTK_TEXT_BUFFER(view.get_text_buffer());
	GtkTextMark* mark = gtk_text_buffer_get_insert(buffer);
	GtkTextIter caret_iter;

	gtk_text_buffer_get_iter_at_mark(buffer, &caret_iter, mark);
	g_value_set_uint(&caret_param.value,
	                 gtk_text_iter_get_offset(&caret_iter));
	params.push_back(caret_param);
}
/* Breadcasts a request N times - makes only sense for undo and redo requests,
 * so that's the only thing we offer API for. */
static void
inf_adopted_session_broadcast_n_requests(InfAdoptedSession* session,
                                         InfAdoptedRequest* request,
                                         guint n)
{
  InfAdoptedSessionPrivate* priv;
  InfAdoptedSessionClass* session_class;
  InfUserTable* user_table;
  guint user_id;
  InfUser* user;
  InfAdoptedSessionLocalUser* local;
  xmlNodePtr xml;

  priv = INF_ADOPTED_SESSION_PRIVATE(session);
  session_class = INF_ADOPTED_SESSION_GET_CLASS(session);
  g_assert(session_class->request_to_xml != NULL);

  user_table = inf_session_get_user_table(INF_SESSION(session));
  user_id = inf_adopted_request_get_user_id(request);
  user = inf_user_table_lookup_user_by_id(user_table, user_id);
  g_assert(user != NULL);

  local = inf_adopted_session_lookup_local_user(
    session,
    INF_ADOPTED_USER(user)
  );
  g_assert(local != NULL);

  xml = xmlNewNode(NULL, (const xmlChar*)"request");

  session_class->request_to_xml(
    session,
    xml,
    request,
    local->last_send_vector,
    FALSE
  );

  if(n > 1) inf_xml_util_set_attribute_uint(xml, "num", n);
  inf_session_send_to_subscriptions(INF_SESSION(session), xml);

  inf_adopted_state_vector_free(local->last_send_vector);
  local->last_send_vector = inf_adopted_state_vector_copy(
    inf_adopted_request_get_vector(request)
  );

  /* Add this request to last send vector if it increases vector time
   * (-> affects buffer). */
  if(inf_adopted_request_affects_buffer(request) == TRUE)
    inf_adopted_state_vector_add(local->last_send_vector, user_id, n);

  inf_adopted_session_stop_noop_timer(session, local);
}
static void
inf_adopted_session_local_user_added(InfAdoptedSession* session,
                                     InfAdoptedUser* user)
{
  InfAdoptedSessionPrivate* priv;
  InfSessionStatus status;
  InfAdoptedSessionLocalUser* local;
  InfAdoptedStateVector* current_state;

  priv = INF_ADOPTED_SESSION_PRIVATE(session);
  status = inf_session_get_status(INF_SESSION(session));

  /* Cannot be local while synchronizing */
  g_assert(status == INF_SESSION_RUNNING);

  local = g_slice_new(InfAdoptedSessionLocalUser);
  local->user = user;

  local->last_send_vector = inf_adopted_state_vector_copy(
    inf_adopted_user_get_vector(user)
  );

  /* Set current vector for local user, this is kept up-to-date by
   * InfAdoptedAlgorithm. TODO: Also do this in InfAdoptedAlgorithm? */
  inf_adopted_user_set_vector(
    user,
    inf_adopted_state_vector_copy(
      inf_adopted_algorithm_get_current(priv->algorithm)
    )
  );

  local->noop_time = 0;

  priv->local_users = g_slist_prepend(priv->local_users, local);

  /* Start noop timer if user is not up to date */
  current_state = inf_adopted_algorithm_get_current(priv->algorithm);
  if(inf_adopted_state_vector_compare(current_state, local->last_send_vector))
    inf_adopted_session_start_noop_timer(session, local);
}
示例#5
0
/**
 * inf_adopted_request_fold:
 * @request: A #InfAdoptedRequest.
 * @into: The direction into which to fold.
 * @by: The number of operations between the original and the fold request.
 *
 * Folds @request as described in "Reducing the Problems of Group Undo" by
 * Matthias Ressel and Rul Gunzenh&auml;user
 * (http://portal.acm.org/citation.cfm?doid=320297.320312).
 *
 * Note that @by is the total amount of requests between the original and
 * the fold request, and thus equivalent to 2j in the paper's definition.
 *
 * @into must not be the same user as the one that issued @request.
 *
 * Returns: The folded request as a new #InfAdoptedRequest.
 **/
InfAdoptedRequest*
inf_adopted_request_fold(InfAdoptedRequest* request,
                         guint into,
                         guint by)
{
    InfAdoptedRequestPrivate* priv;
    InfAdoptedStateVector* new_vector;
    InfAdoptedRequest* new_request;

    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL);
    g_return_val_if_fail(into != 0, NULL);
    g_return_val_if_fail(by % 2 == 0, NULL);

    priv = INF_ADOPTED_REQUEST_PRIVATE(request);
    g_return_val_if_fail(priv->user_id != into, NULL);

    new_vector = inf_adopted_state_vector_copy(priv->vector);
    inf_adopted_state_vector_add(new_vector, into, by);

    if(priv->type == INF_ADOPTED_REQUEST_DO)
    {
        new_request = INF_ADOPTED_REQUEST(
                          g_object_new(
                              INF_ADOPTED_TYPE_REQUEST,
                              "type", priv->type,
                              "operation", priv->operation,
                              "vector", new_vector,
                              "user-id", priv->user_id,
                              NULL
                          )
                      );
    }
    else
    {
        new_request = INF_ADOPTED_REQUEST(
                          g_object_new(
                              INF_ADOPTED_TYPE_REQUEST,
                              "type", priv->type,
                              "vector", new_vector,
                              "user-id", priv->user_id
                          )
                      );
    }

    inf_adopted_state_vector_free(new_vector);
    return new_request;
}
示例#6
0
/**
 * inf_adopted_request_transform:
 * @request: The request to transform.
 * @against: The request to transform against.
 * @concurrency_id: A concurrency ID for the transformation.
 *
 * Transforms the operation of @request against the operation of @against.
 * Both requests must be of type %INF_ADOPTED_REQUEST_DO, and their state
 * vectors must be the same.
 *
 * @concurrency_id can be %INF_ADOPTED_CONCURRENCY_NONE even if the
 * transformation requires a concurrency ID (see
 * inf_adopted_request_need_concurrency_id()). In that case, it is assumed
 * that it does not matter which operation to transform, and user IDs are
 * used to determine a concurrency ID for the transformation.
 *
 * Returns: A new #InfAdoptedRequest, the result of the transformation.
 **/
InfAdoptedRequest*
inf_adopted_request_transform(InfAdoptedRequest* request,
                              InfAdoptedRequest* against,
                              InfAdoptedConcurrencyId concurrency_id)
{
    InfAdoptedRequestPrivate* request_priv;
    InfAdoptedRequestPrivate* against_priv;
    InfAdoptedOperation* new_operation;
    InfAdoptedStateVector* new_vector;
    InfAdoptedRequest* new_request;

    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL);
    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(against), NULL);

    request_priv = INF_ADOPTED_REQUEST_PRIVATE(request);
    against_priv = INF_ADOPTED_REQUEST_PRIVATE(against);

    g_return_val_if_fail(request_priv->type == INF_ADOPTED_REQUEST_DO, NULL);
    g_return_val_if_fail(against_priv->type == INF_ADOPTED_REQUEST_DO, NULL);
    g_return_val_if_fail(request_priv->user_id != against_priv->user_id, NULL);

    g_return_val_if_fail(
        inf_adopted_state_vector_compare(
            request_priv->vector,
            against_priv->vector
        ) == 0, NULL
    );

    if(concurrency_id != INF_ADOPTED_CONCURRENCY_NONE)
    {
        new_operation = inf_adopted_operation_transform(
                            request_priv->operation,
                            against_priv->operation,
                            concurrency_id
                        );
    }
    else if(request_priv->user_id > against_priv->user_id)
    {
        new_operation = inf_adopted_operation_transform(
                            request_priv->operation,
                            against_priv->operation,
                            INF_ADOPTED_CONCURRENCY_OTHER
                        );
    }
    else
    {
        new_operation = inf_adopted_operation_transform(
                            request_priv->operation,
                            against_priv->operation,
                            INF_ADOPTED_CONCURRENCY_SELF
                        );
    }

    new_vector = inf_adopted_state_vector_copy(request_priv->vector);
    inf_adopted_state_vector_add(new_vector, against_priv->user_id, 1);

    new_request = inf_adopted_request_new_do(
                      new_vector,
                      request_priv->user_id,
                      new_operation
                  );

    g_object_unref(new_operation);
    inf_adopted_state_vector_free(new_vector);
    return new_request;
}
示例#7
0
static void
request_join(InfTestGtkBrowserWindow* test,
             const gchar* user_name)
{
  InfcUserRequest* request;
  InfAdoptedStateVector* v;
  GError* error;
  GtkTextBuffer* buffer;
  GtkTextMark* mark;
  GtkTextIter iter;
  GParameter params[3] = {
    { "name", { 0 } },
    { "vector", { 0 } },
    { "caret-position", { 0 } }
  };

  g_value_init(&params[0].value, G_TYPE_STRING);
  g_value_init(&params[1].value, INF_ADOPTED_TYPE_STATE_VECTOR);
  g_value_init(&params[2].value, G_TYPE_UINT);

  g_value_set_static_string(&params[0].value, user_name);

  /* Use current state vector. Infinote should already do this. */
  v = inf_adopted_state_vector_copy(
    inf_adopted_algorithm_get_current(
      inf_adopted_session_get_algorithm(
        INF_ADOPTED_SESSION(infc_session_proxy_get_session(test->proxy))
      )
    )
  );

  g_value_take_boxed(&params[1].value, v);

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(test->textview));
  mark = gtk_text_buffer_get_insert(buffer);
  gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
  g_value_set_uint(&params[2].value, gtk_text_iter_get_offset(&iter));

  error = NULL;
  request = infc_session_proxy_join_user(test->proxy, params, 3, &error);

  /* TODO: Free GValues? */

  if(request == NULL)
  {
    set_error(test, "Failed to request user join", error->message);
  }
  else
  {
    g_signal_connect_after(
      G_OBJECT(request),
      "failed",
      G_CALLBACK(on_join_failed),
      test
    );

    g_signal_connect_after(
      G_OBJECT(request),
      "finished",
      G_CALLBACK(on_join_finished),
      test
    );
  }
}