/** * inf_adopted_request_need_concurrency_id: * @request: The request to transform. * @against: The request to transform against. * * Returns whether transforming @request against @against requires a * concurrency ID. You can still call inf_adopted_request_transform() with * a concurrency ID of %INF_ADOPTED_CONCURRENCY_NONE even if this function * returns %TRUE if you don't have another possibility to find a * concurrency ID in which case user IDs are used to determine which request * to transform. * * Both request need to be of type %INF_ADOPTED_REQUEST_DO, and their state * vectors must be the same. * * Returns: Whether transformation of @request against @against requires a * concurrency ID. */ gboolean inf_adopted_request_need_concurrency_id(InfAdoptedRequest* request, InfAdoptedRequest* against) { InfAdoptedRequestPrivate* request_priv; InfAdoptedRequestPrivate* against_priv; g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), FALSE); g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(against), FALSE); 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, FALSE); g_return_val_if_fail(against_priv->type == INF_ADOPTED_REQUEST_DO, FALSE); g_return_val_if_fail(request_priv->user_id != against_priv->user_id, FALSE); g_return_val_if_fail( inf_adopted_state_vector_compare( request_priv->vector, against_priv->vector ) == 0, FALSE ); return inf_adopted_operation_need_concurrency_id( request_priv->operation, against_priv->operation ); }
/** * inf_adopted_request_affects_buffer: * @request: A #InfAdoptedRequest. * * Returns whether this request, when applied, changes the content of the * buffer. If this is a %INF_ADOPTED_REQUEST_UNDO or %INF_ADOPTED_REQUEST_REDO * request, than it always affects the buffer, because only requests that * affect the buffer can be undone or redone. If it is a * %INF_ADOPTED_REQUEST_DO request, than it returns whether its operation * has the %INF_ADOPTED_OPERATION_AFFECTS_BUFFER flag set. * * Returns: Whether @request affects the session's buffer. **/ gboolean inf_adopted_request_affects_buffer(InfAdoptedRequest* request) { InfAdoptedRequestPrivate* priv; g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), FALSE); priv = INF_ADOPTED_REQUEST_PRIVATE(request); switch(priv->type) { case INF_ADOPTED_REQUEST_DO: if(inf_adopted_operation_get_flags(priv->operation) & INF_ADOPTED_OPERATION_AFFECTS_BUFFER) { return TRUE; } else { return FALSE; } case INF_ADOPTED_REQUEST_UNDO: case INF_ADOPTED_REQUEST_REDO: return TRUE; default: g_assert_not_reached(); return FALSE; } }
/** * 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; }
/** * inf_adopted_request_copy: * @request: The #InfAdoptedRequest to copy. * * Creates a copy of @request with an initial reference count of 1. * * Return Value: A new #InfAdoptedRequest. **/ InfAdoptedRequest* inf_adopted_request_copy(InfAdoptedRequest* request) { InfAdoptedRequestPrivate* priv; GObject* object; g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL); priv = INF_ADOPTED_REQUEST_PRIVATE(request); if(priv->type == INF_ADOPTED_REQUEST_DO) { object = g_object_new( INF_ADOPTED_TYPE_REQUEST, "type", priv->type, "vector", priv->vector, "user-id", priv->user_id, "operation", priv->operation, NULL ); } else { object = g_object_new( INF_ADOPTED_TYPE_REQUEST, "type", priv->type, "vector", priv->vector, "user-id", priv->user_id, NULL ); } return INF_ADOPTED_REQUEST(object); }
/** * inf_adopted_request_get_concurrency_id: * @request: The request to transform. * @against: The request to transform against. * * Returns a concurrency ID for transformation of @operation against @against. * It always returns %INF_ADOPTED_CONCURRENCY_NONE when * inf_adopted_request_need_concurrency_id() returns %TRUE (but that's not * necessarily true the other way around), since it is not possible to decide * which operation to transform without any additional information. * * However, the function can be called on the same requests in a previous * state. In some cases, a decision can be made based on these previous * requests. This can be used as a concurrency ID for a call to * inf_adopted_request_transform(). If this does not yield a decision, it is * still possible to call inf_adopted_request_transform() with * %INF_ADOPTED_CONCURRENCY_NONE as concurrency ID in which case an arbitrary * request will be transformed, based on the user IDs of the requests. * * Both requests must be of type %INF_ADOPTED_REQUEST_DO, and their state * vectors must be the same. * * Returns: A concurrency ID between @operation and @against. Can be * %INF_ADOPTED_CONCURRENCY_NONE in case no decision can be made. */ InfAdoptedConcurrencyId inf_adopted_request_get_concurrency_id(InfAdoptedRequest* request, InfAdoptedRequest* against) { InfAdoptedRequestPrivate* request_priv; InfAdoptedRequestPrivate* against_priv; g_return_val_if_fail( INF_ADOPTED_IS_REQUEST(request), INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( INF_ADOPTED_IS_REQUEST(against), INF_ADOPTED_CONCURRENCY_NONE ); 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, INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( against_priv->type == INF_ADOPTED_REQUEST_DO, INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( request_priv->user_id != against_priv->user_id, INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( inf_adopted_state_vector_compare( request_priv->vector, against_priv->vector ) == 0, INF_ADOPTED_CONCURRENCY_NONE ); return inf_adopted_operation_get_concurrency_id( request_priv->operation, against_priv->operation ); }
/** * inf_adopted_session_broadcast_request: * @session: A #InfAdoptedSession. * @request: A #InfAdoptedRequest obtained from @session's algorithm. * * Sends a request to all subscribed connections. The request should originate * from a call to inf_adopted_algorithm_generate_request_noexec(), * inf_adopted_algorithm_generate_request(), * inf_adopted_algorithm_generate_undo() or * inf_adopted_algorithm_generate_redo() with @session's #InfAdoptedAlgorithm. **/ void inf_adopted_session_broadcast_request(InfAdoptedSession* session, InfAdoptedRequest* request) { g_return_if_fail(INF_ADOPTED_IS_SESSION(session)); g_return_if_fail(INF_ADOPTED_IS_REQUEST(request)); inf_adopted_session_broadcast_n_requests(session, request, 1); }
/** * inf_adopted_request_get_request_type: * @request: A #InfAdoptedRequest. * * Returns the request type of @request. * * Return Value: The type of @request. **/ InfAdoptedRequestType inf_adopted_request_get_request_type(InfAdoptedRequest* request) { g_return_val_if_fail( INF_ADOPTED_IS_REQUEST(request), INF_ADOPTED_REQUEST_DO ); return INF_ADOPTED_REQUEST_PRIVATE(request)->type; }
/** * inf_adopted_request_get_operation: * @request: A #InfAdoptedRequest. * * Returns the operation carried by the request. This can only be called if * the request's type is %INF_ADOPTED_REQUEST_DO. * * Return Value: The request's operation. **/ InfAdoptedOperation* inf_adopted_request_get_operation(InfAdoptedRequest* request) { InfAdoptedRequestPrivate* priv; g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL); priv = INF_ADOPTED_REQUEST_PRIVATE(request); g_return_val_if_fail(priv->operation != NULL, NULL); return priv->operation; }
/** * 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; }
/** * inf_adopted_request_get_user_id: * @request: A #InfAdoptedRequest. * * Returns the user ID of the user that issued @request. * * Return Value: The request's user ID. **/ guint inf_adopted_request_get_user_id(InfAdoptedRequest* request) { g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), 0); return INF_ADOPTED_REQUEST_PRIVATE(request)->user_id; }
/** * inf_adopted_request_get_vector: * @request: A #InfAdoptedRequest. * * Returns the vector time the request was made i.e. its operation can be * applied to the buffer. * * Return Value: The state vector of @request. The returned value should * not be freed, it is owned by the #InfAdoptedRequest. **/ InfAdoptedStateVector* inf_adopted_request_get_vector(InfAdoptedRequest* request) { g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL); return INF_ADOPTED_REQUEST_PRIVATE(request)->vector; }