/** * 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; }
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); }
/** * 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ä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; }
/** * 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; }
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(¶ms[0].value, G_TYPE_STRING); g_value_init(¶ms[1].value, INF_ADOPTED_TYPE_STATE_VECTOR); g_value_init(¶ms[2].value, G_TYPE_UINT); g_value_set_static_string(¶ms[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(¶ms[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(¶ms[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 ); } }