talkatu/talkatu

bf428b4a68da
Move TalkatuHistory from a GtkTextView base to GtkListBox

Make TalkatuInput return the local time if a timestamp has not yet been set.

Add an toggle button for setting the message's edited state

Added TalkatuHistoryRow which is what will be used to display message

Add TalkatuHistoryRow to the documentation.

Add TalkatuScrolledWindow that subclasses GtkScrolledWindow but autoscrolls and has keybindings callbacks for page up and page down.

Testing Done:
Compile, docs, and manual testing in the demo. The i18n stuff is broken for unrelated reasons, which I'll fix in another review request.

Bugs closed: TALKATU-78

Reviewed at https://reviews.imfreedom.org/r/56/
/*
* talkatu
* Copyright (C) 2017-2019 Gary Kramlich <grim@reaperworld.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "talkatudemowindow.h"
struct _TalkatuDemoWindow {
GtkWindow parent;
GtkWidget *history;
GtkWidget *editor;
GtkWidget *typing;
GtkRadioToolButton *toggle_plain;
GtkRadioToolButton *toggle_whole;
GtkRadioToolButton *toggle_html;
GtkRadioToolButton *toggle_markdown;
GtkToggleToolButton *toggle_toolbar;
GtkToggleToolButton *toggle_send_button;
GtkToggleToolButton *toggle_edited;
GtkTextBuffer *buffer_plain;
GtkTextBuffer *buffer_whole;
GtkTextBuffer *buffer_html;
GtkTextBuffer *buffer_markdown;
GtkWidget *author_button;
GtkWidget *author_popover;
GtkWidget *author_item;
};
G_DEFINE_TYPE(TalkatuDemoWindow, talkatu_demo_window, GTK_TYPE_WINDOW);
static void
talkatu_demo_window_insert_html_cb(GtkButton *toggle, gpointer data) {
TalkatuDemoWindow *window = TALKATU_DEMO_WINDOW(data);
GtkWidget *dialog = NULL;
GtkFileFilter *filter = NULL;
gint res;
dialog = gtk_file_chooser_dialog_new(
_("insert html..."),
GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_OPEN,
"OK", GTK_RESPONSE_ACCEPT,
"Cancel", GTK_RESPONSE_REJECT,
NULL
);
filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "HTML files");
gtk_file_filter_add_pattern(filter, "*.html");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
res = gtk_dialog_run(GTK_DIALOG(dialog));
if(res == GTK_RESPONSE_ACCEPT) {
gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
gchar *contents = NULL;
gsize len;
if(g_file_get_contents(filename, &contents, &len, NULL)) {
GtkTextMark *mark = NULL;
GtkTextIter iter;
mark = gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(window->buffer_html));
gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(window->buffer_html), &iter, mark);
talkatu_html_buffer_insert_html(TALKATU_HTML_BUFFER(window->buffer_html), &iter, contents, len);
g_free(contents);
}
g_free(filename);
}
gtk_widget_destroy(dialog);
}
static void
talkatu_demo_window_insert_markdown_cb(GtkButton *toggle, gpointer data) {
TalkatuDemoWindow *window = TALKATU_DEMO_WINDOW(data);
GtkWidget *dialog = NULL;
GtkFileFilter *filter = NULL;
gint res;
dialog = gtk_file_chooser_dialog_new(
_("insert markdown..."),
GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_OPEN,
"OK", GTK_RESPONSE_ACCEPT,
"Cancel", GTK_RESPONSE_REJECT,
NULL
);
filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "Markdown files");
gtk_file_filter_add_pattern(filter, "*.md");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
res = gtk_dialog_run(GTK_DIALOG(dialog));
if(res == GTK_RESPONSE_ACCEPT) {
gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
gchar *contents = NULL;
gsize len;
if(g_file_get_contents(filename, &contents, &len, NULL)) {
GtkTextMark *mark = NULL;
GtkTextIter iter;
mark = gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(window->buffer_markdown));
gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(window->buffer_markdown), &iter, mark);
talkatu_markdown_buffer_insert_markdown(TALKATU_MARKDOWN_BUFFER(window->buffer_markdown), &iter, contents, len);
g_free(contents);
}
g_free(filename);
}
gtk_widget_destroy(dialog);
}
static void
talkatu_demo_window_buffer_changed_cb(GtkToggleButton *toggle, gpointer data) {
TalkatuDemoWindow *window = TALKATU_DEMO_WINDOW(data);
GtkWidget *view = talkatu_editor_get_input(TALKATU_EDITOR(window->editor));
if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(window->toggle_plain))) {
g_message("switching to plain buffer");
gtk_text_view_set_buffer(GTK_TEXT_VIEW(view), window->buffer_plain);
} else if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(window->toggle_whole))) {
g_message("switching to whole buffer");
gtk_text_view_set_buffer(GTK_TEXT_VIEW(view), window->buffer_whole);
} else if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(window->toggle_html))) {
g_message("switching to html buffer");
gtk_text_view_set_buffer(GTK_TEXT_VIEW(view), window->buffer_html);
} else if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(window->toggle_markdown))) {
g_message("switching to markdown buffer");
gtk_text_view_set_buffer(GTK_TEXT_VIEW(view), window->buffer_markdown);
}
}
static void
talkatu_demo_window_buffer_modified_cb(GtkTextBuffer *buffer, gpointer data) {
TalkatuDemoWindow *window = TALKATU_DEMO_WINDOW(data);
GtkWidget *input = talkatu_editor_get_input(TALKATU_EDITOR(window->editor));
gchar *author = NULL;
author = talkatu_message_get_author(TALKATU_MESSAGE(input));
if(gtk_text_buffer_get_char_count(buffer) > 0) {
talkatu_typing_label_start_typing(TALKATU_TYPING_LABEL(window->typing),
author);
} else {
talkatu_typing_label_finish_typing(TALKATU_TYPING_LABEL(window->typing),
author);
}
g_free(author);
}
static void
talkatu_demo_window_view_open_url_cb(TalkatuView *view, const gchar *url, gpointer data) {
GError *error = NULL;
gboolean success = FALSE;
#if GTK_CHECK_VERSION(3, 22, 0)
success = gtk_show_uri_on_window(
GTK_WINDOW(data),
url,
GDK_CURRENT_TIME,
&error
);
#else
success = gtk_show_uri(
gtk_widget_get_screen(view),
url,
GDK_CURRENT_TIME,
&error
);
#endif
if(!success) {
g_message(
"failed to open uri '%s': %s",
url,
(error) ? error->message : NULL
);
g_error_free(error);
}
}
static void
talkatu_demo_window_view_send_message_cb(TalkatuInput *input, gpointer data) {
TalkatuDemoWindow *window = TALKATU_DEMO_WINDOW(data);
static guint64 id = 0;
if(TALKATU_IS_HTML_BUFFER(input)) {
talkatu_message_set_content_type(TALKATU_MESSAGE(input),
TALKATU_CONTENT_TYPE_HTML);
} else if(TALKATU_IS_MARKDOWN_BUFFER(input)) {
talkatu_message_set_content_type(TALKATU_MESSAGE(input),
TALKATU_CONTENT_TYPE_MARKDOWN);
}
talkatu_message_set_id(TALKATU_MESSAGE(input), id++);
talkatu_history_write_message(
TALKATU_HISTORY(window->history),
TALKATU_MESSAGE(input)
);
talkatu_message_set_contents(TALKATU_MESSAGE(input), "");
talkatu_message_clear_attachments(TALKATU_MESSAGE(input));
}
static gboolean
talkatu_demo_window_closed_cb(GtkWidget *w, GdkEvent *e, gpointer d) {
gtk_main_quit();
return FALSE;
}
static void
talkatu_demo_window_author_toggled_cb(GtkToolButton *button, gpointer data) {
GtkPopover *popover = GTK_POPOVER(data);
#if GTK_CHECK_VERSION(3,22,0)
gtk_popover_popup(popover);
#else
gtk_widget_show(popover);
#endif
}
static void
talkatu_demo_window_author_popover_closed_cb(GtkPopover *popover, gpointer data) {
GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON(data);
gtk_toggle_tool_button_set_active(button, FALSE);
}
static void
talkatu_demo_window_author_changed(GtkRadioButton *item, gpointer data) {
TalkatuDemoWindow *window = TALKATU_DEMO_WINDOW(data);
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(item))) {
TalkatuEditor *editor = TALKATU_EDITOR(window->editor);
GtkWidget *input = talkatu_editor_get_input(editor);
talkatu_message_set_author(TALKATU_MESSAGE(input),
gtk_button_get_label(GTK_BUTTON(item)));
}
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
static void
talkatu_demo_window_init(TalkatuDemoWindow *window) {
gtk_widget_init_template(GTK_WIDGET(window));
/* activate the first menu item to make sure its label gets stored
* correctly.
*/
talkatu_demo_window_author_changed(GTK_RADIO_BUTTON(window->author_item), window);
g_object_bind_property(
window->editor, "show-toolbar",
window->toggle_toolbar, "active",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL
);
g_object_bind_property(
window->editor, "show-send-button",
window->toggle_send_button, "active",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL
);
g_object_bind_property(
talkatu_editor_get_input(TALKATU_EDITOR(window->editor)), "edited",
window->toggle_edited, "active",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL
);
}
static void
talkatu_demo_window_class_init(TalkatuDemoWindowClass *klass) {
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
gtk_widget_class_set_template_from_resource(
widget_class,
"/org/bitbucket/rw_grim/talkatu/ui/demo/demo.ui"
);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, history);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, editor);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, typing);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, buffer_plain);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, buffer_whole);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, buffer_html);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, buffer_markdown);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, toggle_plain);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, toggle_whole);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, toggle_html);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, toggle_markdown);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, author_button);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, author_popover);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, author_item);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, toggle_toolbar);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, toggle_send_button);
gtk_widget_class_bind_template_child(widget_class, TalkatuDemoWindow, toggle_edited);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_closed_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_buffer_changed_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_buffer_modified_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_view_open_url_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_view_send_message_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_insert_html_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_insert_markdown_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_author_toggled_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_author_popover_closed_cb);
gtk_widget_class_bind_template_callback(widget_class, talkatu_demo_window_author_changed);
}
/******************************************************************************
* Public API
*****************************************************************************/
GtkWidget *
talkatu_demo_window_new(void) {
return GTK_WIDGET(g_object_new(TALKATU_DEMO_TYPE_WINDOW, NULL));
}