/** * inf_adopted_operation_transform: * @operation: The #InfAdoptedOperation to transform. * @against: The operation to transform against. * @concurrency_id: The concurrency ID for the transformation. * * Performs an inclusion transformation of @operation against @against, * meaning that the effect of @against is included in @operation. * * If inf_adopted_operation_need_concurrency_id() returns %TRUE for @operation * and @against, then @concurrency_id must not be * %INF_ADOPTED_CONCURRENCY_NONE. Otherwise, the parameter is ignored. * * Return Value: The transformed #InfAdoptedOperation, or %NULL if the * transformation failed. **/ InfAdoptedOperation* inf_adopted_operation_transform(InfAdoptedOperation* operation, InfAdoptedOperation* against, InfAdoptedConcurrencyId concurrency_id) { InfAdoptedOperationIface* iface; g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL); g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), NULL); /* Transform against both parts of split operation if we are transforming * against split operation. */ if(INF_ADOPTED_IS_SPLIT_OPERATION(against)) { return inf_adopted_split_operation_transform_other( INF_ADOPTED_SPLIT_OPERATION(against), operation, concurrency_id ); } else { iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); g_assert(iface->transform != NULL); return (*iface->transform)(operation, against, concurrency_id); } }
/** * inf_adopted_operation_need_concurrency_id: * @operation: The #InfAdoptedOperation to transform. * @against: The operation to transform against. * * This function returns whether transforming @operation against @against * is not defined unambiguously. In that case, transformation requires a * so-called concurrency ID which determines which of the two operations * is transformed. * * Returns: Whether transformation of @operation against @against requires a * concurrency ID to be defined. */ gboolean inf_adopted_operation_need_concurrency_id(InfAdoptedOperation* operation, InfAdoptedOperation* against) { InfAdoptedOperationIface* iface; g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), FALSE); g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), FALSE); iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); g_assert(iface->need_concurrency_id != NULL); return iface->need_concurrency_id(operation, against); }
/** * inf_adopted_operation_get_concurrency_id: * @operation: The #InfAdoptedOperation to transform. * @against: The operation to transform against. * * This function returns a concurrency ID for transformation of @operation * against @against. It always returns %INF_ADOPTED_CONCURRENCY_NONE when * inf_adopted_operation_need_concurrency_id() returns %TRUE for * @operation and @against (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 operations in a previous * state. In some cases, a decision can be made based on those previous * operations. This can then be used as concurrency ID to call * inf_adopted_operation_transform(). * * Note that the function is antisymmetric. If it returns * %INF_ADOPTED_CONCURRENCY_SELF, then it returns * %INF_ADOPTED_CONCURRENCY_OTHER for swapped arguments. * * Returns: A concurrency ID between @operation and @against. Can be * %INF_ADOPTED_CONCURRENCY_NONE in case no decision can be made. */ InfAdoptedConcurrencyId inf_adopted_operation_get_concurrency_id(InfAdoptedOperation* operation, InfAdoptedOperation* against) { InfAdoptedOperationIface* iface; InfAdoptedConcurrencyId id; g_return_val_if_fail( INF_ADOPTED_IS_OPERATION(operation), INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( INF_ADOPTED_IS_OPERATION(against), INF_ADOPTED_CONCURRENCY_NONE ); /* Use antisymmetricity if second argument is split operation, so that * subclasses don't need to handle that case explicitely. */ if(!INF_ADOPTED_IS_SPLIT_OPERATION(operation) && INF_ADOPTED_IS_SPLIT_OPERATION(against)) { iface = INF_ADOPTED_OPERATION_GET_IFACE(against); g_assert(iface->get_concurrency_id != NULL); id = iface->get_concurrency_id(against, operation); switch(id) { case INF_ADOPTED_CONCURRENCY_SELF: return INF_ADOPTED_CONCURRENCY_OTHER; case INF_ADOPTED_CONCURRENCY_NONE: return INF_ADOPTED_CONCURRENCY_NONE; case INF_ADOPTED_CONCURRENCY_OTHER: return INF_ADOPTED_CONCURRENCY_SELF; default: g_assert_not_reached(); return INF_ADOPTED_CONCURRENCY_NONE; } } else { iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); g_assert(iface->get_concurrency_id != NULL); return iface->get_concurrency_id(operation, against); } }
/** * inf_text_delete_operation_need_concurrency_id: * @op: A #InfTextDeleteOperation. * @against: Another #InfAdoptedOperation. * * Returns whether transforming @op against @against requires a concurrency ID * (see inf_adopted_operation_need_concurrency_id() for further information). * * Returns: Whether transforming @op against @against requires a concurrency * ID. */ gboolean inf_text_delete_operation_need_concurrency_id(InfTextDeleteOperation* op, InfAdoptedOperation* against) { g_return_val_if_fail(INF_TEXT_IS_DELETE_OPERATION(op), FALSE); g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), FALSE); return FALSE; }
/** * inf_adopted_operation_make_reversible: * @operation: A #InfAdoptedOperation. * @with: Another #InfAdoptedOperation that emerged from @operation by * transforming it. * @buffer: A #InfBuffer. * * Some operations may not be reversible, but can be made reversible with * some extra information such as another operation that collected * enough information while being transformed, and the current buffer. * * This function can only be called when @operation is not yet reversible * and returns a new operation that has the same effect as @operation, but is * reversible. * * For example, an operation that deletes some range of text in a text editor * is not reversible if it only stores the position and length of the range, * but can be made reversible when it looks up what there is at that position * in the buffer. * * Return Value: A reversible #InfAdoptedOperation, or %NULL if @operation * cannot be made reversible with the given transformed operation @with and * @buffer. **/ InfAdoptedOperation* inf_adopted_operation_make_reversible(InfAdoptedOperation* operation, InfAdoptedOperation* with, InfBuffer* buffer) { InfAdoptedOperationIface* iface; g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL); g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(with), NULL); g_return_val_if_fail(INF_IS_BUFFER(buffer), NULL); g_assert(inf_adopted_operation_is_reversible(operation) == FALSE); iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); if(iface->make_reversible != NULL) return (*iface->make_reversible)(operation, with, buffer); else return NULL; }
/** * inf_adopted_operation_copy: * @operation: The #InfAdoptedOperation to copy. * * Returns a copy of @operation. * * Return Value: A copy of @operation. **/ InfAdoptedOperation* inf_adopted_operation_copy(InfAdoptedOperation* operation) { InfAdoptedOperationIface* iface; g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL); iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); g_assert(iface->copy != NULL); return (*iface->copy)(operation); }
/** * inf_adopted_operation_is_reversible: * @operation: A #InfAdoptedOperation. * * Returns whether @operation is reversible. * * Return Value: Whether @operation is reversible. **/ gboolean inf_adopted_operation_is_reversible(InfAdoptedOperation* operation) { InfAdoptedOperationFlags flags; g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), FALSE); flags = inf_adopted_operation_get_flags(operation); if( (flags & INF_ADOPTED_OPERATION_REVERSIBLE) != 0) return TRUE; else return FALSE; }
/** * inf_adopted_operation_get_flags: * @operation: A #InfAdoptedOperation. * * Returns the flags for @operation. * * Return Value: #InfAdoptedOperationFlags for @operation. **/ InfAdoptedOperationFlags inf_adopted_operation_get_flags(InfAdoptedOperation* operation) { InfAdoptedOperationIface* iface; g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), 0); iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); if(iface->get_flags != NULL) return (*iface->get_flags)(operation); else return 0; }
/** * inf_adopted_operation_revert: * @operation: A #InfAdoptedOperation. * * Returns a new #InfAdoptedOperation that undoes the effect of @operation. If * @operation and then its reverse operation are applied to a buffer (in that * order), the buffer remains unchanged. * * @operation must be reversible for this function to be called (i.e. * inf_adopted_operation_is_reversible() must return TRUE). * * Return Value: The reverse operation of @operation. **/ InfAdoptedOperation* inf_adopted_operation_revert(InfAdoptedOperation* operation) { InfAdoptedOperationIface* iface; g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL); g_assert(inf_adopted_operation_is_reversible(operation) == TRUE); iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); /* When inf_adopted_operation_is_reversible() returns TRUE for an operation * it must implement revert. */ g_assert(iface->revert != NULL); return (*iface->revert)(operation); }
/** * inf_adopted_operation_apply: * @operation: A #InfAdoptedOperation. * @by: A #InfAdoptedUser. * @buffer: The #InfBuffer to apply the operation to. * * Applies @operation to @buffer. The operation is considered to be applied by * user @by. **/ void inf_adopted_operation_apply(InfAdoptedOperation* operation, InfAdoptedUser* by, InfBuffer* buffer) { InfAdoptedOperationIface* iface; g_return_if_fail(INF_ADOPTED_IS_OPERATION(operation)); g_return_if_fail(INF_ADOPTED_IS_USER(by)); g_return_if_fail(INF_IS_BUFFER(buffer)); iface = INF_ADOPTED_OPERATION_GET_IFACE(operation); /* apply must be implemented */ g_assert(iface->apply != NULL); (*iface->apply)(operation, by, buffer); }
/** * inf_text_insert_operation_need_concurrency_id: * @op: A #InfTextInsertOperation. * @against: Another #InfAdoptedOperation. * * Returns whether transforming @op against @against requires a concurrency ID * (see inf_adopted_operation_need_concurrency_id() for further information). * * Returns: Whether transforming @op against @against requires a concurrency * ID. */ gboolean inf_text_insert_operation_need_concurrency_id(InfTextInsertOperation* op, InfAdoptedOperation* against) { InfTextInsertOperation* insert_against; g_return_val_if_fail(INF_TEXT_IS_INSERT_OPERATION(op), FALSE); g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), FALSE); if(INF_TEXT_IS_INSERT_OPERATION(against)) { insert_against = INF_TEXT_INSERT_OPERATION(against); if(inf_text_insert_operation_get_position(op) == inf_text_insert_operation_get_position(insert_against)) { return TRUE; } } return FALSE; }
/** * inf_adopted_request_new_do: * @vector: The vector time at which the request was made. * @user_id: The ID of the user that made the request. * @operation: The operation the user performed. * * Creates a new #InfAdoptedRequest with type %INF_ADOPTED_REQUEST_DO. * * Return Value: A new DO request. **/ InfAdoptedRequest* inf_adopted_request_new_do(InfAdoptedStateVector* vector, guint user_id, InfAdoptedOperation* operation) { GObject* object; g_return_val_if_fail(vector != NULL, NULL); g_return_val_if_fail(user_id != 0, NULL); g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL); object = g_object_new( INF_ADOPTED_TYPE_REQUEST, "type", INF_ADOPTED_REQUEST_DO, "vector", vector, "user-id", user_id, "operation", operation, NULL ); return INF_ADOPTED_REQUEST(object); }