Person* chat_view_get_person(ChatView *self) { g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr); auto priv = CHAT_VIEW_GET_PRIVATE(self); return priv->person; }
Call* chat_view_get_call(ChatView *self) { g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr); auto priv = CHAT_VIEW_GET_PRIVATE(self); return priv->call; }
ContactMethod* chat_view_get_cm(ChatView *self) { g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr); auto priv = CHAT_VIEW_GET_PRIVATE(self); return priv->cm; }
static void update_contact_methods(ChatView *self) { g_return_if_fail(IS_CHAT_VIEW(self)); ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self); g_return_if_fail(priv->person); /* model for the combobox for the choice of ContactMethods */ auto cm_model = gtk_list_store_new( 1, G_TYPE_POINTER ); auto cms = priv->person->phoneNumbers(); for (int i = 0; i < cms.size(); ++i) { GtkTreeIter iter; gtk_list_store_append(cm_model, &iter); gtk_list_store_set(cm_model, &iter, 0, cms.at(i), -1); } gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_cm), GTK_TREE_MODEL(cm_model)); g_object_unref(cm_model); auto renderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_cm), renderer, FALSE); gtk_cell_layout_set_cell_data_func( GTK_CELL_LAYOUT(priv->combobox_cm), renderer, (GtkCellLayoutDataFunc)render_contact_method, nullptr, nullptr); /* select the last used cm */ if (!cms.isEmpty()) { auto last_used_cm = cms.at(0); int last_used_cm_idx = 0; for (int i = 1; i < cms.size(); ++i) { auto new_cm = cms.at(i); if (difftime(new_cm->lastUsed(), last_used_cm->lastUsed()) > 0) { last_used_cm = new_cm; last_used_cm_idx = i; } } gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_cm), last_used_cm_idx); } /* show the combo box if there is more than one cm to choose from */ if (cms.size() > 1) gtk_widget_show_all(priv->combobox_cm); else gtk_widget_hide(priv->combobox_cm); }
static void print_text_recording(Media::TextRecording *recording, ChatView *self) { g_return_if_fail(IS_CHAT_VIEW(self)); ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self); /* set the photos of the chat participants */ set_participant_images(self); /* only text messages are supported for now */ auto model = recording->instantTextMessagingModel(); /* new model, disconnect from the old model updates and clear the text buffer */ QObject::disconnect(priv->new_message_connection); /* put all the messages in the im model into the text view */ for (int row = 0; row < model->rowCount(); ++row) { QModelIndex idx = model->index(row, 0); print_message_to_buffer(self, idx); } /* mark all messages as read */ recording->setAllRead(); /* messages modified */ /* connecting on instantMessagingModel and not textMessagingModel */ priv->message_changed_connection = QObject::connect( model, &QAbstractItemModel::dataChanged, [self, priv] (const QModelIndex & topLeft, G_GNUC_UNUSED const QModelIndex & bottomRight) { webkit_chat_container_update_message( WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), topLeft ); } ); /* append new messages */ priv->new_message_connection = QObject::connect( model, &QAbstractItemModel::rowsInserted, [self, priv, model] (const QModelIndex &parent, int first, int last) { for (int row = first; row <= last; ++row) { QModelIndex idx = model->index(row, 0, parent); print_message_to_buffer(self, idx); /* make sure these messages are marked as read */ model->setData(idx, true, static_cast<int>(Media::TextRecording::Role::IsRead)); g_signal_emit(G_OBJECT(self), chat_view_signals[NEW_MESSAGES_DISPLAYED], 0); } } ); }
static void selected_cm_changed(GtkComboBox *box, ChatView *self) { g_return_if_fail(IS_CHAT_VIEW(self)); ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self); auto cms = priv->person->phoneNumbers(); auto active = gtk_combo_box_get_active(box); if (active >= 0 && active < cms.size()) { print_text_recording(cms.at(active)->textRecording(), self); } else { g_warning("no valid ContactMethod selected to display chat conversation"); } }
static void update_name(ChatView *self) { g_return_if_fail(IS_CHAT_VIEW(self)); ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self); g_return_if_fail(priv->person || priv->cm); QString name; if (priv->person) { name = priv->person->roleData(static_cast<int>(Ring::Role::Name)).toString(); } else { name = priv->cm->roleData(static_cast<int>(Ring::Role::Name)).toString(); } gtk_label_set_text(GTK_LABEL(priv->label_peer), name.toUtf8().constData()); }
static void print_text_recording(Media::TextRecording *recording, ChatView *self) { g_return_if_fail(IS_CHAT_VIEW(self)); ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self); /* only text messages are supported for now */ auto model = recording->instantTextMessagingModel(); /* new model, disconnect from the old model updates and clear the text buffer */ QObject::disconnect(priv->new_message_connection); GtkTextBuffer *new_buffer = gtk_text_buffer_new(NULL); gtk_text_view_set_buffer(GTK_TEXT_VIEW(priv->textview_chat), new_buffer); /* add tags to the buffer */ gtk_text_buffer_create_tag(new_buffer, "bold", "weight", PANGO_WEIGHT_BOLD, NULL); g_object_unref(new_buffer); /* put all the messages in the im model into the text view */ for (int row = 0; row < model->rowCount(); ++row) { QModelIndex idx = model->index(row, 0); print_message_to_buffer(idx, new_buffer); } /* mark all messages as read */ recording->setAllRead(); /* append new messages */ priv->new_message_connection = QObject::connect( model, &QAbstractItemModel::rowsInserted, [self, priv, model] (const QModelIndex &parent, int first, int last) { for (int row = first; row <= last; ++row) { QModelIndex idx = model->index(row, 0, parent); print_message_to_buffer(idx, gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview_chat))); /* make sure these messages are marked as read */ model->setData(idx, true, static_cast<int>(Media::TextRecording::Role::IsRead)); g_signal_emit(G_OBJECT(self), chat_view_signals[NEW_MESSAGES_DISPLAYED], 0); } } ); }
static void send_chat(G_GNUC_UNUSED GtkWidget *widget, ChatView *self) { g_return_if_fail(IS_CHAT_VIEW(self)); ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self); /* make sure there is more than just whitespace, but if so, send the original text */ const auto text = QString(gtk_entry_get_text(GTK_ENTRY(priv->entry_chat_input))); if (!text.trimmed().isEmpty()) { QMap<QString, QString> messages; messages["text/plain"] = text; if (priv->call) { // in call message priv->call->addOutgoingMedia<Media::Text>()->send(messages); } else if (priv->person) { // get the chosen cm auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm)); if (active >= 0) { auto cm = priv->person->phoneNumbers().at(active); if (!cm->sendOfflineTextMessage(messages)) g_warning("message failed to send"); // TODO: warn the user about this in the UI } else { g_warning("no ContactMethod chosen; message not sent"); } } else if (priv->cm) { if (!priv->cm->sendOfflineTextMessage(messages)) g_warning("message failed to send"); // TODO: warn the user about this in the UI } else { g_warning("no Call, Person, or ContactMethod set; message not sent"); } /* clear the entry */ gtk_entry_set_text(GTK_ENTRY(priv->entry_chat_input), ""); } }
static gboolean selected_item_changed(RingMainWindow *win) { // g_debug("item changed"); RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win)); /* if we're showing the settings, then nothing needs to be done as the call view is not shown */ if (priv->show_settings) return G_SOURCE_REMOVE; auto idx_selected = RecentModel::instance().selectionModel()->currentIndex(); /* we prioritize showing the call view; but if the call is over we go back to showing the chat view */ if(auto call = RecentModel::instance().getActiveCall(idx_selected)) { /* check if we need to change the view */ auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call)); auto state = call->lifeCycleState(); /* check what the current state is vs what is displayed */ switch(state) { case Call::LifeCycleState::CREATION: case Call::LifeCycleState::FINISHED: /* go back to incoming call view; * it will show that the call failed and offer to hang it up */ case Call::LifeCycleState::INITIALIZATION: { /* show the incoming call view */ if (!IS_INCOMING_CALL_VIEW(current_view)) { auto new_view = incoming_call_view_new(); incoming_call_view_set_call_info(INCOMING_CALL_VIEW(new_view), CallModel::instance().getIndex(call)); gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view); gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view); gtk_widget_show(new_view); } } break; case Call::LifeCycleState::PROGRESS: { /* show the current call view */ if (!IS_CURRENT_CALL_VIEW(current_view)) { auto new_view = current_call_view_new(); g_signal_connect(new_view, "video-double-clicked", G_CALLBACK(video_double_clicked), win); current_call_view_set_call_info(CURRENT_CALL_VIEW(new_view), CallModel::instance().getIndex(call)); gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view); gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view); gtk_widget_show(new_view); } } break; case Call::LifeCycleState::COUNT__: g_warning("LifeCycleState should never be COUNT"); break; } } else if (idx_selected.isValid()) { /* otherwise, the call is over and is already removed from the RecentModel */ auto current_view = gtk_bin_get_child(GTK_BIN(priv->frame_call)); leave_full_screen(win); /* show the chat view */ if (!IS_CHAT_VIEW(current_view)) { auto type = idx_selected.data(static_cast<int>(Ring::Role::ObjectType)).value<Ring::ObjectType>(); auto object = idx_selected.data(static_cast<int>(Ring::Role::Object)); if (type == Ring::ObjectType::Person && object.isValid()) { /* show chat view constructed from Person object */ auto new_view = chat_view_new_person(object.value<Person *>()); gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view); gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view); gtk_widget_show(new_view); } else if (type == Ring::ObjectType::ContactMethod && object.isValid()) { /* show chat view constructed from CM */ auto new_view = chat_view_new_cm(object.value<ContactMethod *>()); gtk_container_remove(GTK_CONTAINER(priv->frame_call), current_view); gtk_container_add(GTK_CONTAINER(priv->frame_call), new_view); gtk_widget_show(new_view); } } } return G_SOURCE_REMOVE; }