void Gobby::EditCommands::on_active_user_changed(InfUser* active_user) { g_assert(m_current_view != NULL); if(active_user != NULL) { InfTextSession* session = m_current_view->get_session(); InfAdoptedAlgorithm* algorithm = inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION(session)); GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); m_header.action_edit_undo->set_sensitive( inf_adopted_algorithm_can_undo( algorithm, INF_ADOPTED_USER(active_user))); m_header.action_edit_redo->set_sensitive( inf_adopted_algorithm_can_redo( algorithm, INF_ADOPTED_USER(active_user))); m_header.action_edit_cut->set_sensitive( gtk_text_buffer_get_has_selection(buffer)); m_header.action_edit_paste->set_sensitive(true); } else { m_header.action_edit_undo->set_sensitive(false); m_header.action_edit_redo->set_sensitive(false); m_header.action_edit_cut->set_sensitive(false); m_header.action_edit_paste->set_sensitive(false); } }
static void on_join_finished(InfcUserRequest* request, InfUser* user, gpointer user_data) { InfTestGtkBrowserWindow* test; InfAdoptedSession* session; InfAdoptedAlgorithm* algorithm; gboolean undo; gboolean redo; test = (InfTestGtkBrowserWindow*)user_data; inf_text_gtk_buffer_set_active_user(test->buffer, INF_TEXT_USER(user)); gtk_text_view_set_editable(GTK_TEXT_VIEW(test->textview), TRUE); test->user = user; session = INF_ADOPTED_SESSION(infc_session_proxy_get_session(test->proxy)); algorithm = inf_adopted_session_get_algorithm(session); undo = inf_adopted_algorithm_can_undo(algorithm, INF_ADOPTED_USER(user)); redo = inf_adopted_algorithm_can_redo(algorithm, INF_ADOPTED_USER(user)); gtk_widget_set_sensitive(test->undo_button, undo); gtk_widget_set_sensitive(test->redo_button, redo); }
static InfAdoptedUser* inf_adopted_session_user_from_request_xml(InfAdoptedSession* session, xmlNodePtr xml, GError** error) { InfUserTable* user_table; InfUser* user; guint user_id; user_table = inf_session_get_user_table(INF_SESSION(session)); if(!inf_xml_util_get_attribute_uint_required(xml, "user", &user_id, error)) return FALSE; /* User ID 0 means no user */ if(user_id == 0) return NULL; user = inf_user_table_lookup_user_by_id(user_table, user_id); if(user == NULL) { g_set_error( error, inf_adopted_session_error_quark, INF_ADOPTED_SESSION_ERROR_NO_SUCH_USER, _("No such user with user ID '%u'"), user_id ); return NULL; } g_assert(INF_ADOPTED_IS_USER(user)); return INF_ADOPTED_USER(user); }
void Gobby::EditCommands::on_redo() { g_assert(m_current_view != NULL); gulong i_ = g_signal_connect_after(m_current_view->get_text_buffer(), "insert-text", G_CALLBACK(recaret_i), NULL); gulong e_ = g_signal_connect_after(m_current_view->get_text_buffer(), "delete-range", G_CALLBACK(recaret_e), NULL); inf_adopted_session_redo( INF_ADOPTED_SESSION(m_current_view->get_session()), INF_ADOPTED_USER(m_current_view->get_active_user()), m_current_view->get_undo_grouping().get_redo_size() ); g_signal_handler_disconnect(m_current_view->get_text_buffer(), i_); g_signal_handler_disconnect(m_current_view->get_text_buffer(), e_); if(check) { GtkTextIter check_iter; gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), &check_iter, check); gtk_text_buffer_select_range(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), &check_iter, &check_iter); gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), check); check = NULL; } m_current_view->scroll_to_cursor_position(0.0); }
void Gobby::EditCommands::on_can_redo_changed(InfAdoptedUser* user, bool can_redo) { g_assert(m_current_view != NULL); if(INF_ADOPTED_USER(m_current_view->get_active_user()) == user) m_header.action_edit_redo->set_sensitive(can_redo); }
static void inf_adopted_session_constructor_foreach_local_user_func(InfUser* user, gpointer user_data) { g_assert(INF_ADOPTED_IS_USER(user)); inf_adopted_session_local_user_added( INF_ADOPTED_SESSION(user_data), INF_ADOPTED_USER(user) ); }
static void on_redo_button_clicked(GtkButton* button, gpointer user_data) { InfTestGtkBrowserWindow* test; InfAdoptedSession* session; test = (InfTestGtkBrowserWindow*)user_data; session = INF_ADOPTED_SESSION(infc_session_proxy_get_session(test->proxy)); inf_adopted_session_redo(session, INF_ADOPTED_USER(test->user)); }
static gboolean inf_adopted_session_process_xml_sync(InfSession* session, InfXmlConnection* connection, const xmlNodePtr xml, GError** error) { InfAdoptedSessionClass* session_class; InfAdoptedRequest* request; InfAdoptedUser* user; InfAdoptedRequestLog* log; if(strcmp((const char*)xml->name, "sync-request") == 0) { session_class = INF_ADOPTED_SESSION_GET_CLASS(session); g_assert(session_class->xml_to_request != NULL); request = session_class->xml_to_request( INF_ADOPTED_SESSION(session), xml, NULL, /* TODO: Diff to previous request, if any. */ TRUE, error ); if(request == NULL) return FALSE; user = INF_ADOPTED_USER( inf_user_table_lookup_user_by_id( inf_session_get_user_table(session), inf_adopted_request_get_user_id(request) ) ); log = inf_adopted_user_get_request_log(user); if(inf_adopted_session_validate_request(log, request, error) == FALSE) { g_object_unref(request); return FALSE; } inf_adopted_request_log_add_request(log, request); g_object_unref(request); return TRUE; } return INF_SESSION_CLASS(parent_class)->process_xml_sync( session, connection, xml, error ); }
static void inf_adopted_session_add_local_user_cb(InfUserTable* user_table, InfUser* user, gpointer user_data) { g_assert(INF_ADOPTED_IS_USER(user)); inf_adopted_session_local_user_added( INF_ADOPTED_SESSION(user_data), INF_ADOPTED_USER(user) ); }
/* 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_synchronization_complete_foreach_user_func(InfUser* user, gpointer data) { InfAdoptedRequestLog* log; log = inf_adopted_user_get_request_log(INF_ADOPTED_USER(user)); /* Set begin index of empty request logs. Algorithm relies on * inf_adopted_request_log_get_begin() to return the index of the request * that will first be added to the request log. */ if(inf_adopted_request_log_is_empty(log)) { inf_adopted_request_log_set_begin( log, inf_adopted_state_vector_get( inf_adopted_user_get_vector(INF_ADOPTED_USER(user)), inf_user_get_id(user) ) ); } }
Gobby::TextUndoGrouping::TextUndoGrouping(InfAdoptedAlgorithm* algorithm, InfTextUser* user, GtkTextBuffer* buffer): m_buffer(buffer), m_grouping(inf_text_undo_grouping_new()) { g_object_ref(m_buffer); inf_adopted_undo_grouping_set_algorithm( INF_ADOPTED_UNDO_GROUPING(m_grouping), algorithm, INF_ADOPTED_USER(user)); m_begin_user_action_handle = g_signal_connect( G_OBJECT(m_buffer), "begin-user-action", G_CALLBACK(on_begin_user_action_static), this); m_end_user_action_handle = g_signal_connect( G_OBJECT(m_buffer), "end-user-action", G_CALLBACK(on_end_user_action_static), this); }
static void inf_adopted_session_remove_local_user_cb(InfUserTable* user_table, InfUser* user, gpointer user_data) { InfAdoptedSession* session; InfAdoptedSessionPrivate* priv; InfAdoptedSessionLocalUser* local; session = INF_ADOPTED_SESSION(user_data); priv = INF_ADOPTED_SESSION_PRIVATE(session); local = inf_adopted_session_lookup_local_user( session, INF_ADOPTED_USER(user) ); g_assert(local != NULL); inf_adopted_session_stop_noop_timer(session, local); inf_adopted_state_vector_free(local->last_send_vector); priv->local_users = g_slist_remove(priv->local_users, local); g_slice_free(InfAdoptedSessionLocalUser, local); }
static void inf_adopted_session_to_xml_sync_foreach_user_func(InfUser* user, gpointer user_data) { InfAdoptedRequestLog* log; InfAdoptedSessionToXmlSyncForeachData* data; InfAdoptedSessionClass* session_class; guint i; guint end; xmlNodePtr xml; InfAdoptedRequest* request; g_assert(INF_ADOPTED_IS_USER(user)); data = (InfAdoptedSessionToXmlSyncForeachData*)user_data; log = inf_adopted_user_get_request_log(INF_ADOPTED_USER(user)); end = inf_adopted_request_log_get_end(log); session_class = INF_ADOPTED_SESSION_GET_CLASS(data->session); g_assert(session_class->request_to_xml != NULL); for(i = inf_adopted_request_log_get_begin(log); i < end; ++ i) { request = inf_adopted_request_log_get_request(log, i); xml = xmlNewChild( data->parent_xml, NULL, (const xmlChar*)"sync-request", NULL ); /* TODO: Diff to previous request? */ session_class->request_to_xml(data->session, xml, request, NULL, TRUE); xmlAddChild(data->parent_xml, xml); } }
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; }