/** * 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"); }
static void inf_user_table_check_local_cb(GObject* object, GParamSpec* pspec, gpointer user_data) { InfUserTable* user_table; InfUserTablePrivate* priv; GSList* item; user_table = INF_USER_TABLE(user_data); priv = INF_USER_TABLE_PRIVATE(user_table); item = g_slist_find(priv->locals, INF_USER(object)); if(inf_user_table_is_local(INF_USER(object)) && item == NULL) { g_signal_emit( G_OBJECT(user_table), user_table_signals[ADD_LOCAL_USER], 0, INF_USER(object) ); } if(!inf_user_table_is_local(INF_USER(object)) && item != NULL) { g_signal_emit( G_OBJECT(user_table), user_table_signals[REMOVE_LOCAL_USER], 0, INF_USER(object) ); } }
void TextBuffer::eraseText( unsigned int pos, unsigned int len, User *user ) { qDebug() << "erasing text:" << slice(pos, len)->text() << pos << len; inf_text_buffer_text_erased( INF_TEXT_BUFFER(gobject()), pos, slice(pos, len)->infChunk(), INF_USER(user->gobject()) ); inf_text_buffer_erase_text( INF_TEXT_BUFFER(gobject()), pos, len, INF_USER(user->gobject()) ); }
void TextBuffer::insertChunk( unsigned int pos, const TextChunk &chunk, User *user ) { InfTextBufferIface* iface = INF_TEXT_BUFFER_GET_IFACE(INF_TEXT_BUFFER(gobject())); inf_text_buffer_text_inserted( INF_TEXT_BUFFER(gobject()), pos, chunk.infChunk(), INF_USER(user->gobject()) ); inf_text_buffer_insert_chunk( INF_TEXT_BUFFER(gobject()), pos, chunk.infChunk(), INF_USER(user->gobject()) ); }
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); }
void TextBuffer::insertText( unsigned int pos, const QByteArray &data, unsigned int length, User *user ) { inf_text_buffer_insert_text( INF_TEXT_BUFFER(gobject()), pos, data.data(), data.length(), length, INF_USER(user->gobject()) ); // TODO test me, I don't think this is used anywhere in kobby QInfinity::TextChunk chunk( encoding() ); chunk.insertText( 0, data, data.length(), user->id() ); inf_text_buffer_text_inserted( INF_TEXT_BUFFER(gobject()), pos, chunk.infChunk(), INF_USER(user->gobject()) ); }
static void inf_text_default_delete_operation_apply(InfAdoptedOperation* operation, InfAdoptedUser* by, InfBuffer* buffer) { InfTextDefaultDeleteOperationPrivate* priv; g_assert(INF_TEXT_IS_DEFAULT_DELETE_OPERATION(operation)); g_assert(INF_TEXT_IS_BUFFER(buffer)); priv = INF_TEXT_DEFAULT_DELETE_OPERATION_PRIVATE(operation); #ifdef DELETE_OPERATION_CHECK_TEXT_MATCH g_assert( inf_text_default_delete_operation_text_match( INF_TEXT_DEFAULT_DELETE_OPERATION(operation), INF_TEXT_BUFFER(buffer) ) ); #endif /* DELETE_OPERATION_CHECK_TEXT_MATCH */ inf_text_buffer_erase_text( INF_TEXT_BUFFER(buffer), priv->position, inf_text_chunk_get_length(priv->chunk), INF_USER(by) ); }
static void inf_user_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) { InfUser* user; InfUserPrivate* priv; user = INF_USER(object); priv = INF_USER_PRIVATE(user); switch(prop_id) { case PROP_ID: g_value_set_uint(value, priv->id); break; case PROP_NAME: g_value_set_string(value, priv->name); break; case PROP_STATUS: g_value_set_enum(value, priv->status); break; case PROP_FLAGS: g_value_set_flags(value, priv->flags); break; case PROP_CONNECTION: g_value_set_object(value, G_OBJECT(priv->connection)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } }
static void inf_user_table_dispose_foreach_func(gpointer key, gpointer value, gpointer user_data) { inf_user_table_unref_user(INF_USER_TABLE(user_data), INF_USER(value)); }
static void inf_text_gtk_viewport_user_removed(InfTextGtkViewport* viewport, InfTextUser* user) { InfTextGtkViewportPrivate* priv; InfTextGtkViewportUser* viewport_user; priv = INF_TEXT_GTK_VIEWPORT_PRIVATE(viewport); if(user == priv->active_user) { priv->active_user = NULL; g_object_notify(G_OBJECT(viewport), "active-user"); } else { inf_signal_handlers_disconnect_by_func( user, G_CALLBACK(inf_text_gtk_viewport_user_notify_status_cb), viewport ); if(inf_user_get_status(INF_USER(user)) == INF_USER_ACTIVE) { viewport_user = inf_text_gtk_viewport_find_user(viewport, user); g_assert(viewport_user != NULL); inf_text_gtk_viewport_remove_user(viewport_user); } } }
static void inf_text_gtk_viewport_user_notify_status_cb(GObject* object, GParamSpec* pspec, gpointer user_data) { InfTextGtkViewport* viewport; InfTextGtkViewportPrivate* priv; InfTextUser* user; InfTextGtkViewportUser* viewport_user; viewport = INF_TEXT_GTK_VIEWPORT(user_data); priv = INF_TEXT_GTK_VIEWPORT_PRIVATE(viewport); user = INF_TEXT_USER(object); g_assert(user != priv->active_user); viewport_user = inf_text_gtk_viewport_find_user(viewport, user); if(inf_user_get_status(INF_USER(user)) == INF_USER_ACTIVE) { if(!viewport_user) inf_text_gtk_viewport_add_user(viewport, user); } else { if(viewport_user) inf_text_gtk_viewport_remove_user(viewport_user); } }
static void inf_user_dispose(GObject* object) { InfUser* user; user = INF_USER(object); G_OBJECT_CLASS(parent_class)->dispose(object); }
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 inf_user_table_foreach_user_func(gpointer key, gpointer value, gpointer user_data) { InfUserTableForeachUserData* data; data = (InfUserTableForeachUserData*)user_data; data->func(INF_USER(value), data->user_data); }
static void inf_user_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) { InfUser* user; InfUserPrivate* priv; user = INF_USER(object); priv = INF_USER_PRIVATE(user); /* TODO: Check if properties are still valid. * There are several combinations possible: * * Status | Flags | Connection | Desc * UNAVAIL | 0 | unset | User not available, was non-local last time * AVAIL | 0 | unset | INVALID * UNAVAIL | LOCAL | unset | User not available, was local last time * AVAIL | LOCAL | unset | User is available, and local * UNAVAIL | 0 | set | INVALID * AVAIL | 0 | set | User is available, non-local * UNAVAIL | LOCAL | set | INVALID * AVAIL | LOCAL | set | INVALID */ switch(prop_id) { case PROP_ID: priv->id = g_value_get_uint(value); break; case PROP_NAME: g_free(priv->name); priv->name = g_value_dup_string(value); break; case PROP_STATUS: g_signal_emit( object, user_signals[SET_STATUS], 0, g_value_get_enum(value) ); break; case PROP_FLAGS: priv->flags = g_value_get_flags(value); break; case PROP_CONNECTION: if(priv->connection != NULL) g_object_unref(priv->connection); priv->connection = INF_XML_CONNECTION(g_value_dup_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } }
static gboolean inf_user_table_lookup_user_by_name_func(gpointer key, gpointer value, gpointer data) { const gchar* user_name; user_name = inf_user_get_name(INF_USER(value)); if(strcmp(user_name, (const gchar*)data) == 0) return TRUE; return FALSE; }
/** * inf_user_table_lookup_user_by_id: * @user_table: A #InfUserTable. * @id: User ID to lookup. * * Returns the #InfUser with the given User ID in @user_table. * * Return Value: A #InfUser, or %NULL. **/ InfUser* inf_user_table_lookup_user_by_id(InfUserTable* user_table, guint id) { InfUserTablePrivate* priv; g_return_val_if_fail(INF_IS_USER_TABLE(user_table), NULL); priv = INF_USER_TABLE_PRIVATE(user_table); return INF_USER(g_hash_table_lookup(priv->table, GUINT_TO_POINTER(id))); }
static void inf_user_finalize(GObject* object) { InfUser* user; InfUserPrivate* priv; user = INF_USER(object); priv = INF_USER_PRIVATE(user); g_free(priv->name); G_OBJECT_CLASS(parent_class)->finalize(object); }
/** * inf_user_table_foreach_local_user: * @user_table: A #InfUserTable. * @func: The function to call for each user. * @user_data: User data to pass to the function. * * Calls the given function for each local user in the user_table. A local * user is a user that has the %INF_USER_LOCAL flag set and that has not * status %INF_USER_UNAVAILABLE. You should not add or remove users while this * function is being executed. **/ void inf_user_table_foreach_local_user(InfUserTable* user_table, InfUserTableForeachUserFunc func, gpointer user_data) { InfUserTablePrivate* priv; GSList* item; g_return_if_fail(INF_IS_USER_TABLE(user_table)); g_return_if_fail(func != NULL); priv = INF_USER_TABLE_PRIVATE(user_table); for(item = priv->locals; item != NULL; item = g_slist_next(item)) func(INF_USER(item->data), user_data); }
static void inf_user_init(GTypeInstance* instance, gpointer g_class) { InfUser* user; InfUserPrivate* priv; user = INF_USER(instance); user->priv = INF_USER_GET_PRIVATE(user); priv = INF_USER_PRIVATE(user); priv->id = 0; priv->name = NULL; priv->status = INF_USER_UNAVAILABLE; priv->flags = 0; priv->connection = NULL; }
static void inf_text_default_insert_operation_apply(InfAdoptedOperation* operation, InfAdoptedUser* by, InfBuffer* buffer) { InfTextDefaultInsertOperationPrivate* priv; g_assert(INF_TEXT_IS_DEFAULT_INSERT_OPERATION(operation)); g_assert(INF_TEXT_IS_BUFFER(buffer)); priv = INF_TEXT_DEFAULT_INSERT_OPERATION_PRIVATE(operation); inf_text_buffer_insert_chunk( INF_TEXT_BUFFER(buffer), priv->position, priv->chunk, INF_USER(by) ); }
static void inf_text_remote_delete_operation_apply(InfAdoptedOperation* operation, InfAdoptedUser* by, InfBuffer* buffer) { InfTextRemoteDeleteOperationPrivate* priv; g_assert(INF_TEXT_IS_REMOTE_DELETE_OPERATION(operation)); g_assert(INF_TEXT_IS_BUFFER(buffer)); priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(operation); inf_text_buffer_erase_text( INF_TEXT_BUFFER(buffer), priv->position, priv->length, INF_USER(by) ); }
static void inf_text_gtk_viewport_user_added(InfTextGtkViewport* viewport, InfTextUser* user) { InfTextGtkViewportPrivate* priv; priv = INF_TEXT_GTK_VIEWPORT_PRIVATE(viewport); /* Active user is guaranteed to be contained in user table, so if user was * just added then it can't be set as active user already. */ g_assert(user != priv->active_user); g_signal_connect( user, "notify::status", G_CALLBACK(inf_text_gtk_viewport_user_notify_status_cb), viewport ); if(inf_user_get_status(INF_USER(user)) == INF_USER_ACTIVE) inf_text_gtk_viewport_add_user(viewport, user); }
static gboolean inf_text_remote_delete_operation_apply(InfAdoptedOperation* operation, InfAdoptedUser* by, InfBuffer* buffer, GError** error) { InfTextRemoteDeleteOperationPrivate* priv; g_assert(INF_TEXT_IS_REMOTE_DELETE_OPERATION(operation)); g_assert(INF_TEXT_IS_BUFFER(buffer)); priv = INF_TEXT_REMOTE_DELETE_OPERATION_PRIVATE(operation); if(priv->position + priv->length > inf_text_buffer_get_length(INF_TEXT_BUFFER(buffer))) { g_set_error( error, g_quark_from_static_string("INF_TEXT_OPERATION_ERROR"), INF_TEXT_OPERATION_ERROR_INVALID_DELETE, "%s", _("Attempt to remove text from after the end of the document") ); return FALSE; } else { inf_text_buffer_erase_text( INF_TEXT_BUFFER(buffer), priv->position, priv->length, INF_USER(by) ); return TRUE; } }
void on_active_user_changed(InfUser* user) { if(m_active_user != NULL) { if(m_active) deactivate_user(); g_signal_handler_disconnect(G_OBJECT(m_active_user), m_notify_status_handle); } m_active_user = user; if(user != NULL) { InfUserStatus user_status = inf_user_get_status(INF_USER(user)); g_assert(user_status != INF_USER_UNAVAILABLE); m_notify_status_handle = g_signal_connect( G_OBJECT(user), "notify::status", G_CALLBACK(&on_user_notify_status_static), this ); if( (user_status == INF_USER_ACTIVE && !m_active)) { deactivate_user(); } else if(user_status == INF_USER_INACTIVE && m_active) { activate_user(); } } }
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; }
void Gobby::OperationOpen::read_finish() { // If the last character is a newline character, remove it. GtkTextIter end_iter, test_iter; gtk_text_buffer_get_end_iter(m_content, &end_iter); test_iter = end_iter; if(gtk_text_iter_backward_char(&test_iter)) { if(gtk_text_iter_get_char(&test_iter) == '\n') { gtk_text_buffer_delete( m_content, &test_iter, &end_iter); } } gtk_text_buffer_set_modified(m_content, FALSE); GtkTextIter insert_iter; GtkTextMark* insert = gtk_text_buffer_get_insert(m_content); gtk_text_buffer_get_iter_at_mark(m_content, &insert_iter, insert); InfUser* user = INF_USER(g_object_new( INF_TEXT_TYPE_USER, "id", 1, "flags", INF_USER_LOCAL, "name", m_preferences.user.name.get().c_str(), /* The user is made active when the user * switches to the document. */ "status", INF_USER_INACTIVE, "hue", m_preferences.user.hue.get(), "caret-position", gtk_text_iter_get_offset(&insert_iter), static_cast<void*>(NULL))); InfUserTable* user_table = inf_user_table_new(); inf_user_table_add_user(user_table, user); g_object_unref(user); InfTextGtkBuffer* text_gtk_buffer = inf_text_gtk_buffer_new(m_content, user_table); g_object_unref(user_table); ConnectionManager& connection_manager = get_browser().get_connection_manager(); InfCommunicationManager* communication_manager = connection_manager.get_communication_manager(); InfBrowser* browser = m_parent.get_browser(); InfIo* io; g_object_get(G_OBJECT(browser), "io", &io, NULL); InfTextSession* session = inf_text_session_new_with_user_table( communication_manager, INF_TEXT_BUFFER(text_gtk_buffer), io, user_table, INF_SESSION_RUNNING, NULL, NULL); g_object_unref(io); g_object_unref(text_gtk_buffer); InfRequest* request = inf_browser_add_note( m_parent.get_browser(), m_parent.get_browser_iter(), m_name.c_str(), "InfText", NULL, INF_SESSION(session), TRUE, on_request_finished_static, this); g_object_unref(session); if(request != NULL) { m_request = request; g_object_ref(m_request); // TODO: We can remove the node watch here, but need to have // the browser available in on_request_finished then. Maybe // just disconnect the signal, or bind it. } }
static gboolean infd_note_plugin_text_read_user(InfUserTable* user_table, xmlNodePtr node, GError** error) { guint id; gdouble hue; xmlChar* name; gboolean result; InfUser* user; if(!inf_xml_util_get_attribute_uint_required(node, "id", &id, error)) return FALSE; if(!inf_xml_util_get_attribute_double_required(node, "hue", &hue, error)) return FALSE; name = inf_xml_util_get_attribute_required(node, "name", error); if(name == NULL) return FALSE; if(inf_user_table_lookup_user_by_id(user_table, id) != NULL) { g_set_error( error, g_quark_from_static_string("INF_NOTE_PLUGIN_TEXT_ERROR"), INFD_NOTE_PLUGIN_TEXT_ERROR_USER_EXISTS, "User with ID %u exists already", id ); result = FALSE; } else { if(inf_user_table_lookup_user_by_name(user_table, (const gchar*)name)) { g_set_error( error, g_quark_from_static_string("INF_NOTE_PLUGIN_TEXT_ERROR"), INFD_NOTE_PLUGIN_TEXT_ERROR_USER_EXISTS, "User with name `%s' exists already", (const gchar*)name ); result = FALSE; } else { user = INF_USER( g_object_new( INF_TEXT_TYPE_USER, "id", id, "name", name, "hue", hue, NULL ) ); inf_user_table_add_user(user_table, user); g_object_unref(user); result = TRUE; } } xmlFree(name); return result; }
static gboolean perform_test(guint max_total_log_size, InfTextChunk* initial, GSList* users, GSList* requests, GError** error) { InfTextBuffer* buffer; InfCommunicationManager* manager; InfIo* io; InfTextSession* session; InfAdoptedAlgorithm* algorithm; InfUserTable* user_table; InfTextUser* user; gchar* user_name; GSList* item; xmlNodePtr request; gboolean result; GError* local_error; guint verify_user_id; InfAdoptedUser* verify_user; guint verify_log_size; gint verify_can_undo; gint verify_can_redo; InfAdoptedRequestLog* log; guint log_size; buffer = INF_TEXT_BUFFER(inf_text_default_buffer_new("UTF-8")); inf_text_buffer_insert_chunk(buffer, 0, initial, NULL); manager = inf_communication_manager_new(); io = INF_IO(inf_standalone_io_new()); user_table = inf_user_table_new(); local_error = NULL; for(item = users; item != NULL; item = g_slist_next(item)) { user_name = g_strdup_printf("User_%u", GPOINTER_TO_UINT(item->data)); user = INF_TEXT_USER( g_object_new( INF_TEXT_TYPE_USER, "id", GPOINTER_TO_UINT(item->data), "name", user_name, "status", INF_USER_ACTIVE, "flags", 0, NULL ) ); g_free(user_name); inf_user_table_add_user(user_table, INF_USER(user)); g_object_unref(user); } session = INF_TEXT_SESSION( g_object_new( INF_TEXT_TYPE_SESSION, "communication-manager", manager, "buffer", buffer, "io", io, "user_table", user_table, "max-total-log-size", max_total_log_size, NULL ) ); algorithm = inf_adopted_session_get_algorithm(INF_ADOPTED_SESSION(session)); g_object_unref(io); g_object_unref(manager); g_object_unref(user_table); g_object_unref(buffer); for(item = requests; item != NULL; item = item->next) { request = (xmlNodePtr)item->data; if(strcmp((const char*)request->name, "request") == 0) { /* Request */ result = inf_communication_object_received( INF_COMMUNICATION_OBJECT(session), NULL, request, &local_error ); if(local_error != NULL) goto fail; } else { /* TODO: Make an extra function out of this: */ /* Verify */ result = inf_xml_util_get_attribute_uint_required( request, "user", &verify_user_id, &local_error ); if(result == FALSE) goto fail; verify_user = INF_ADOPTED_USER( inf_user_table_lookup_user_by_id(user_table, verify_user_id) ); if(verify_user == NULL) { g_set_error( error, inf_test_text_cleanup_error_quark(), INF_TEST_TEXT_CLEANUP_USER_UNAVAILABLE, "User ID '%u' not available", verify_user_id ); goto fail; } result = inf_xml_util_get_attribute_uint( request, "log-size", &verify_log_size, &local_error ); if(local_error) goto fail; if(result) { log = inf_adopted_user_get_request_log(INF_ADOPTED_USER(verify_user)); log_size = inf_adopted_request_log_get_end(log) - inf_adopted_request_log_get_begin(log); if(verify_log_size != log_size) { g_set_error( error, inf_test_text_cleanup_error_quark(), INF_TEST_TEXT_CLEANUP_VERIFY_FAILED, "Log size does not match; got %u, but expected %u", log_size, verify_log_size ); goto fail; } } result = inf_xml_util_get_attribute_int( request, "can-undo", &verify_can_undo, &local_error ); if(local_error) goto fail; if(result) { result = inf_adopted_algorithm_can_undo(algorithm, verify_user); if(result != verify_can_undo) { g_set_error( error, inf_test_text_cleanup_error_quark(), INF_TEST_TEXT_CLEANUP_VERIFY_FAILED, "can-undo does not match; got %d, but expected %d", (guint)result, verify_can_undo ); goto fail; } } result = inf_xml_util_get_attribute_int( request, "can-redo", &verify_can_redo, &local_error ); if(local_error) goto fail; if(result) { result = inf_adopted_algorithm_can_redo(algorithm, verify_user); if(result != verify_can_redo) { g_set_error( error, inf_test_text_cleanup_error_quark(), INF_TEST_TEXT_CLEANUP_VERIFY_FAILED, "can-redo does not match; got %d, but expected %d", (guint)result, verify_can_redo ); goto fail; } } } } g_object_unref(session); return TRUE; fail: g_object_unref(session); if(local_error) g_propagate_error(error, local_error); return FALSE; }