Skip to content

Instantly share code, notes, and snippets.

@rlapz
Last active October 29, 2023 10:53
Show Gist options
  • Save rlapz/1a2ae7398b5af949b6fc6f0ea1105ddb to your computer and use it in GitHub Desktop.
Save rlapz/1a2ae7398b5af949b6fc6f0ea1105ddb to your computer and use it in GitHub Desktop.
#!/bin/sh
set -e
cc -g -Wall -Wextra moetr.c $(pkg-config --cflags gtk4 libsoup-3.0 json-glib-1.0)\
$(pkg-config --libs gtk4 libsoup-3.0 json-glib-1.0) -o moetr
#include <gtk/gtk.h>
#include <libsoup/soup.h>
#include <json-glib/json-glib.h>
/*
* default configs
*/
#define DEFAULT_WINDOW_W 450
#define DEFAULT_WINDOW_H 500
#define DEFAULT_WINDOW_TITLE "MoeTr - A Simple Language Translator"
//#define HTTP_API_HOST "translate.googleapis.com:811"
#define HTTP_API_HOST "translate.googleapis.com"
#define HTTP_API_QUERY "/translate_a/single?client=gtx&ie=UTF-8&oe=UTF-8"
#define HTTP_API_URI_FMT_DETAIL "http://" HTTP_API_HOST HTTP_API_QUERY \
"&dt=bd&dt=ex&dt=ld&dt=md&dt=rw&dt=rm&dt=ss&dt="\
"t&dt=at&dt=gt&dt=qca&sl=%s&tl=%s&hl=%s&q="
/*
* helpers
*/
#define SIGNAL_CONNECT(INSTANCE, SIGNAL, FUNC, UDATA)\
g_signal_connect(INSTANCE, SIGNAL, G_CALLBACK(FUNC), UDATA)
/*
* global vars
*/
static const char *langs[] = {
"auto - Automatic", /* only for "src" lang,
skipped for "trg" lang (without 0th index) */
"af - Afrikaans", "sq - Albanian", "am - Amharic",
"ar - Arabic", "hy - Armenian", "az - Azerbaijani",
"eu - Basque", "be - Belarusian", "bn - Bengali",
"bs - Bosnian", "bg - Bulgarian", "ca - Catalan",
"ceb - Cebuano", "zh-CN - Chinese Simplified", "zh-TW - Chinese Traditional",
"co - Corsican", "hr - Croatian", "cs - Czech",
"da - Danish", "nl - Dutch", "en - English",
"eo - Esperanto", "et - Estonian", "fi - Finnish",
"fr - French", "fy - Frisian", "gl - Galician",
"ka - Georgian", "de - German", "el - Greek",
"gu - Gujarati", "ht - Haitian Crole", "ha - Hausan",
"haw - Hawaiian", "iw - Hebrew", "hi - Hindi",
"hmn - Hmong", "hu - Hungarian", "is - Icelandic",
"ig - Igbo", "id - Indonesian", "ga - Irish",
"it - Italian", "ja - Japanese", "jw - Javanese",
"kn - Kannada", "kk - Kazakh", "km - Khmer",
"rw - Kinyarwanda", "ko - Korean", "ku - Kurdish",
"ky - Kyrgyz", "lo - Lao", "la - Latin",
"lv - Latvian", "lt - Lithunian", "lb - Luxembourgish",
"mk - Macedonian", "mg - Malagasy", "ms - Malay",
"ml - Malayam", "mt - Maltese", "mi - Maori",
"mr - Marathi", "mn - Mongolian", "my - Myanmar",
"ne - Nepali", "no - Norwegian", "ny - Nyanja",
"or - Odia", "ps - Pashto", "fa - Persian",
"pl - Polish", "pt - Portuguese", "pa - Punjabi",
"ro - Romanian", "ru - Russian", "sm - Samoan",
"gd - Scots Gaelic", "sr - Serbian", "st - Sesotho",
"sn - Shona", "sd - Sindhi", "si - Sinhala",
"sk - Slovak", "sl - Slovenian", "so - Somali",
"es - Spanish", "su - Sundanese", "sw - Swahili",
"sv - Swedish", "tl - Tagalog", "tg - Tajik",
"ta - Tamil", "tt - Tatar", "te - Telugu",
"th - Thai", "tr - Turkish", "tk - Turkmen",
"uk - Ukranian", "ur - Urdu", "ug - Uyghur",
"uz - Uzbek", "vi - Vietnamese", "cy - Welsh",
"xh - Xhosa", "yi - Yiddish", "yo - Yaruba",
"zu - Zulu",
NULL,
};
/*
* lang
*/
struct lang {
char key[6];
const char *val;
};
static struct lang *lang_get_by_id(struct lang *self, guint id);
static const char *lang_get_val(struct lang *self, const char key[]);
/*
* txtbuf
*/
struct txtbuf {
char *name;
GtkTextBuffer *body;
};
static void txtbuf_init(struct txtbuf *self);
static void txtbuf_set_body_text(struct txtbuf *self, const char body[]);
static void txtbuf_set_name(struct txtbuf *self, const char name[]);
static GtkTextBuffer *txtbuf_get_body(struct txtbuf *self);
static char *txtbuf_get_body_text(struct txtbuf *self);
static char *txtbuf_get_name(struct txtbuf *self);
static void txtbuf_copy_body_to_name(struct txtbuf *self);
static void txtbuf_deinit(struct txtbuf *self);
/*
* HTTP
*/
typedef void (*HttpCallback)(void *udata, GBytes *resp, GError *err);
struct http {
SoupSession *session;
SoupMessage *message;
GCancellable *cancellable;
HttpCallback callback;
void *callback_udata;
};
static void http_init(struct http *self, HttpCallback callback, void *udata);
static void http_request(struct http *self, const char uri[]);
static void http_request_cancel(struct http *self);
static void http_clear(struct http *self);
static void http_deinit(struct http *self);
/*
* moetr
*/
struct moetr {
gboolean is_ready;
GtkApplication *app;
GtkWidget *lst_src;
GtkWidget *lst_trg;
GtkWidget *txt_trg;
GtkWidget *txt_src;
GtkWidget *btn_open;
GtkWidget *btn_switch;
GtkWidget *btn_clear;
GtkWidget *btn_translate;
GtkWidget *btn_speak_src;
GtkWidget *btn_speak_trg;
struct txtbuf txtbuf_src;
struct txtbuf txtbuf_trg;
struct lang lang_src;
struct lang lang_trg;
struct http http;
};
static void moetr_init(struct moetr *self);
static void moetr_deinit(struct moetr *self);
static int moetr_run(struct moetr *self, int argc, char *argv[]);
static char *moetr_build_request(struct moetr *self, const char text[]);
static void moetr_translate(struct moetr *self);
static void moetr_write(struct moetr *self, const char resp_json[]);
/*
* callbacks
*/
static void on_app_activate(GtkApplication *self, struct moetr *moe);
static void on_txt_buffer_trg_changed(GtkTextBuffer *self, struct moetr *moe);
static void on_txt_buffer_src_changed(GtkTextBuffer *self, struct moetr *moe);
static void on_btn_open_clicked(GtkButton *self, struct moetr *moe);
static void on_btn_switch_clicked(GtkButton *self, struct moetr *moe);
static void on_btn_clear_clicked(GtkButton *self, struct moetr *moe);
static void on_btn_translate_clicked(GtkButton *self, struct moetr *moe);
static void on_btn_speak_src_clicked(GtkButton *self, struct moetr *moe);
static void on_btn_speak_trg_clicked(GtkButton *self, struct moetr *moe);
static void on_http_response_ready(GObject *self, GAsyncResult *res, void *udata);
static void on_moetr_http_response(void *udata, GBytes *resp, GError *err);
/*=====================================================================*
* IMPLS *
*=====================================================================*/
/*
* lang
*/
static struct lang *
lang_get_by_id(struct lang *self, guint id)
{
if (id >= G_N_ELEMENTS(langs) - 1)
return NULL;
const char *const lang = langs[id];
if (lang == NULL)
return NULL;
const char *const dash = strstr(lang, " - ");
if (*(dash + 3) == '\0')
return NULL;
char tmp[64];
size_t const l_size = (dash - lang) + 1;
if (l_size >= sizeof(tmp))
return NULL;
g_strlcpy(tmp, lang, l_size);
g_strlcpy(self->key, g_strstrip(tmp), sizeof(self->key));
self->val = (dash + 3);
return self;
}
static const char *
lang_get_val(struct lang *self, const char key[])
{
const size_t len = strlen(key);
if ((len < 2) || (len > 5))
return NULL;
const size_t size = G_N_ELEMENTS(langs) - 1;
for (size_t i = 0; i < size; i++) {
if (g_ascii_strncasecmp(langs[i], key, len) == 0) {
if (lang_get_by_id(self, i) == NULL)
return NULL;
return self->val;
}
}
return NULL;
}
/*
* HTTP
*/
static void
http_init(struct http *self, HttpCallback callback, void *udata)
{
self->message = NULL;
self->callback = callback;
self->callback_udata = udata;
self->session = soup_session_new();
self->cancellable = g_cancellable_new();
}
static void
http_request(struct http *self, const char uri[])
{
self->message = soup_message_new(SOUP_METHOD_GET, uri);
soup_session_send_and_read_async(self->session, self->message, G_PRIORITY_DEFAULT,
self->cancellable, on_http_response_ready, self);
}
static void
http_request_cancel(struct http *self)
{
g_cancellable_cancel(self->cancellable);
}
static void
http_clear(struct http *self)
{
if (self->message != NULL) {
g_object_unref(self->message);
self->message = NULL;
}
g_cancellable_reset(self->cancellable);
}
static void
http_deinit(struct http *self)
{
http_clear(self);
g_object_unref(self->session);
g_object_unref(self->cancellable);
}
/*
* txtbuf
*/
static void
txtbuf_init(struct txtbuf *self)
{
self->name = NULL;
self->body = gtk_text_buffer_new(NULL);
}
static void
txtbuf_set_body_text(struct txtbuf *self, const char body[])
{
if (body != NULL)
gtk_text_buffer_set_text(self->body, body, -1);
}
static void
txtbuf_set_name(struct txtbuf *self, const char name[])
{
g_free(self->name);
self->name = g_strdup(name);
}
static GtkTextBuffer *
txtbuf_get_body(struct txtbuf *self)
{
return self->body;
}
static char *
txtbuf_get_body_text(struct txtbuf *self)
{
GtkTextIter start, end;
gtk_text_buffer_get_bounds(self->body, &start, &end);
return gtk_text_buffer_get_text(self->body, &start, &end, FALSE);
}
static char *
txtbuf_get_name(struct txtbuf *self)
{
return self->name;
}
static void
txtbuf_copy_body_to_name(struct txtbuf *self)
{
g_free(self->name);
self->name = g_strstrip(txtbuf_get_body_text(self));
}
static void
txtbuf_deinit(struct txtbuf *self)
{
g_free(self->name);
g_object_unref(self->body);
}
/*
* moetr
*/
static void
moetr_init(struct moetr *self)
{
self->is_ready = FALSE;
self->app = gtk_application_new("org.rlapz.moetr", G_APPLICATION_DEFAULT_FLAGS);
txtbuf_init(&self->txtbuf_src);
txtbuf_init(&self->txtbuf_trg);
http_init(&self->http, on_moetr_http_response, self);
SIGNAL_CONNECT(self->app, "activate", on_app_activate, self);
}
static void
moetr_deinit(struct moetr *self)
{
g_object_unref(self->app);
txtbuf_deinit(&self->txtbuf_src);
txtbuf_deinit(&self->txtbuf_trg);
http_deinit(&self->http);
}
static int
moetr_run(struct moetr *self, int argc, char *argv[])
{
return g_application_run(G_APPLICATION(self->app), argc, argv);
}
static char *
moetr_build_request(struct moetr *self, const char text[])
{
GString *const str = g_string_new(NULL);
if (str == NULL)
return NULL;
const char *const _langs[] = { self->lang_src.key, self->lang_trg.key };
g_string_append_printf(str, HTTP_API_URI_FMT_DETAIL, _langs[0], _langs[1], _langs[1]);
g_string_append_uri_escaped(str, text, NULL, TRUE);
return g_string_free_and_steal(str);
}
static char *
__moetr_parse_response_detail(const char trg_txt[], JsonNode *node)
{
gboolean is_err = TRUE;
GString *const str = g_string_new(NULL);
JsonArray *const arr0 = json_node_get_array(node);
if (arr0 == NULL)
goto out0;
g_print("%u\n", json_array_get_length(arr0));
/* TODO */
g_string_append(str, trg_txt);
is_err = FALSE;
out0:
if (is_err) {
g_print("error occured: %s\n", "invalid response");
g_string_printf(str, "%s", trg_txt);
}
return g_string_free_and_steal(str);
}
static char *
__moetr_parse_response(struct moetr *self, const char resp_json[])
{
char *ret = NULL;
const char *trg_txt;
gboolean is_err = TRUE;
GError *err = NULL;
JsonParser *const parser = json_parser_new();
if (!json_parser_load_from_data(parser, resp_json, -1, &err)) {
g_print("error occured: %s\n", err->message);
g_error_free(err);
is_err = FALSE;
goto out0;
}
JsonNode *const root = json_parser_get_root(parser);
if (root == NULL)
goto out1;
JsonArray *const arr_top = json_node_get_array(root);
if (root == NULL)
goto out1;
JsonNode *const node0 = json_array_get_element(arr_top, 0);
if (node0 == NULL)
goto out1;
JsonArray *const arr0 = json_node_get_array(node0);
if (arr0 == NULL)
goto out1;
JsonNode *const node1 = json_array_get_element(arr0, 0);
if (node1 == NULL)
goto out1;
JsonArray *const arr2 = json_node_get_array(node1);
if (arr2 == NULL)
goto out1;
JsonNode *const node_trg = json_array_get_element(arr2, 0);
if (node_trg == NULL)
goto out1;
trg_txt = json_node_get_string(node_trg);
if (trg_txt == NULL)
goto out1;
txtbuf_set_name(&self->txtbuf_trg, trg_txt);
ret = __moetr_parse_response_detail(trg_txt, root);
is_err = FALSE;
out1:
g_object_unref(parser);
out0:
if (is_err)
g_print("error occured: %s\n", "invalid response");
return ret;
}
static void
moetr_translate(struct moetr *self)
{
gtk_widget_grab_focus(self->txt_src);
if (gtk_text_buffer_get_char_count(txtbuf_get_body(&self->txtbuf_src)) == 0)
return;
guint id = gtk_drop_down_get_selected(GTK_DROP_DOWN(self->lst_src));
if (lang_get_by_id(&self->lang_src, id) == NULL)
return;
/* "trg" always > 0 */
id = gtk_drop_down_get_selected(GTK_DROP_DOWN(self->lst_trg));
if (lang_get_by_id(&self->lang_trg, id + 1) == NULL)
return;
txtbuf_copy_body_to_name(&self->txtbuf_src);
char *const uri = moetr_build_request(self, txtbuf_get_name(&self->txtbuf_src));
if (uri == NULL)
return;
self->is_ready = FALSE;
gtk_button_set_label(GTK_BUTTON(self->btn_translate), "Cancel");
gtk_text_view_set_editable(GTK_TEXT_VIEW(self->txt_src), FALSE);
http_clear(&self->http);
http_request(&self->http, uri);
g_free(uri);
txtbuf_set_body_text(&self->txtbuf_trg, "");
}
static void
moetr_write(struct moetr *self, const char resp_json[])
{
char *const res = __moetr_parse_response(self, resp_json);
if (res == NULL) {
txtbuf_set_body_text(&self->txtbuf_trg, "");
} else {
txtbuf_set_body_text(&self->txtbuf_trg, g_strstrip(res));
g_free(res);
}
// debug
g_print("%s\n\n", resp_json);
}
/*
* callbacks
*/
static void
on_app_activate(GtkApplication *self, struct moetr *moe)
{
moe->lst_src = gtk_drop_down_new_from_strings(langs);
moe->lst_trg = gtk_drop_down_new_from_strings(&langs[1]);
moe->btn_open = g_object_new(GTK_TYPE_BUTTON,
//"label", "Open",
"icon-name", "document-open",
"tooltip-text", "Open text file to be translated",
"margin-end", 10,
NULL);
moe->btn_switch = g_object_new(GTK_TYPE_BUTTON,
//"label", "Switch",
"icon-name", "media-playlist-repeat",
"tooltip-text", "Switch the languages",
NULL);
moe->btn_clear = g_object_new(GTK_TYPE_BUTTON,
//"label", "Clear",
"icon-name", "edit-clear",
"tooltip-text", "Clear",
NULL);
moe->btn_translate = g_object_new(GTK_TYPE_BUTTON,
"label", "Translate",
//"icon-name", "view-refresh",
"tooltip-text", "Translate now",
NULL);
moe->btn_speak_src = g_object_new(GTK_TYPE_BUTTON,
//"label", "<>",
"sensitive", FALSE,
"icon-name", "media-playback-start",
"tooltip-text", "Play audio of the source text",
NULL);
moe->btn_speak_trg = g_object_new(GTK_TYPE_BUTTON,
//"label", "<>",
"sensitive", FALSE,
"icon-name", "media-playback-start",
"tooltip-text", "Play audio of the target text",
NULL);
moe->txt_src = g_object_new(GTK_TYPE_TEXT_VIEW,
"buffer", txtbuf_get_body(&moe->txtbuf_src),
"wrap-mode", GTK_WRAP_WORD_CHAR,
"tooltip-text", "Input text to be translated",
"top-margin", 5,
"bottom-margin", 5,
"right-margin", 5,
"left-margin", 5,
NULL);
moe->txt_trg = g_object_new(GTK_TYPE_TEXT_VIEW,
"buffer", txtbuf_get_body(&moe->txtbuf_trg),
"wrap-mode", GTK_WRAP_WORD_CHAR,
"editable", FALSE,
"tooltip-text", "Translated text (not editable)",
"top-margin", 5,
"bottom-margin", 5,
"right-margin", 5,
"left-margin", 5,
NULL);
/* configs */
gtk_widget_set_tooltip_text(moe->lst_src, "Source language list");
gtk_widget_set_tooltip_text(moe->lst_trg, "Target language list");
gtk_widget_set_margin_end(moe->lst_trg, 10);
/* box_btns */
GtkBox *const box_btns = g_object_new(GTK_TYPE_BOX,
"spacing", 5,
"margin-top", 5,
"margin-bottom", 5,
"margin-start", 5,
"margin-end", 5,
"halign", GTK_ALIGN_CENTER,
NULL);
gtk_box_append(box_btns, moe->btn_speak_src);
gtk_box_append(box_btns, moe->btn_speak_trg);
gtk_box_append(box_btns, moe->btn_clear);
gtk_box_append(box_btns, moe->btn_translate);
/* scrolled window */
void *const scr_trg = g_object_new(GTK_TYPE_SCROLLED_WINDOW,
"child", moe->txt_trg,
"margin-bottom", 3,
NULL);
void *const scr_src = g_object_new(GTK_TYPE_SCROLLED_WINDOW,
"child", moe->txt_src,
"margin-top", 3,
NULL);
/* paned */
GtkPaned *const paned = g_object_new(GTK_TYPE_PANED,
"orientation", GTK_ORIENTATION_VERTICAL,
"start-child", scr_trg,
"end_child", scr_src,
"position", 300,
"margin-start", 5,
"margin-end", 5,
"vexpand", TRUE,
NULL);
/* box_rds */
GtkBox *const box_rds = g_object_new(GTK_TYPE_BOX,
"halign", GTK_ALIGN_CENTER,
"margin-top", 5,
"margin-bottom", 5,
"margin-start", 5,
"margin-end", 5,
NULL);
gtk_box_append(box_rds, moe->btn_open);
gtk_box_append(box_rds, moe->lst_src);
gtk_box_append(box_rds, g_object_new(GTK_TYPE_LABEL,
"label", "->",
"margin-start", 1,
"margin-end", 1,
NULL));
gtk_box_append(box_rds, moe->lst_trg);
gtk_box_append(box_rds, moe->btn_switch);
/* box_main */
GtkBox *const box_main = g_object_new(GTK_TYPE_BOX,
"orientation", GTK_ORIENTATION_VERTICAL,
"spacing", 2,
NULL);
gtk_box_append(box_main, GTK_WIDGET(box_rds));
gtk_box_append(box_main, GTK_WIDGET(paned));
gtk_box_append(box_main, GTK_WIDGET(box_btns));
/* set all widget signals */
SIGNAL_CONNECT(txtbuf_get_body(&moe->txtbuf_trg), "changed",
on_txt_buffer_trg_changed, moe);
SIGNAL_CONNECT(txtbuf_get_body(&moe->txtbuf_src), "changed",
on_txt_buffer_src_changed, moe);
SIGNAL_CONNECT(moe->btn_open, "clicked", on_btn_open_clicked, moe);
SIGNAL_CONNECT(moe->btn_clear, "clicked", on_btn_clear_clicked, moe);
SIGNAL_CONNECT(moe->btn_translate, "clicked", on_btn_translate_clicked, moe);
SIGNAL_CONNECT(moe->btn_switch, "clicked", on_btn_switch_clicked, moe);
SIGNAL_CONNECT(moe->btn_speak_src, "clicked", on_btn_speak_src_clicked, moe);
SIGNAL_CONNECT(moe->btn_speak_trg, "clicked", on_btn_speak_trg_clicked, moe);
/* setup the window */
GtkWindow *const window = GTK_WINDOW(gtk_application_window_new(self));
gtk_window_set_child(window, GTK_WIDGET(box_main));
gtk_window_set_title(window, DEFAULT_WINDOW_TITLE);
gtk_window_set_default_size(window, DEFAULT_WINDOW_W, DEFAULT_WINDOW_H);
gtk_window_present(window);
moe->is_ready = TRUE;
gtk_widget_grab_focus(moe->txt_src);
}
static void
on_txt_buffer_trg_changed(GtkTextBuffer *self, struct moetr *moe)
{
if (gtk_text_buffer_get_char_count(self) == 0)
gtk_widget_set_sensitive(moe->btn_speak_trg, FALSE);
else
gtk_widget_set_sensitive(moe->btn_speak_trg, TRUE);
}
static void
on_txt_buffer_src_changed(GtkTextBuffer *self, struct moetr *moe)
{
if (gtk_text_buffer_get_char_count(self) == 0)
gtk_widget_set_sensitive(moe->btn_speak_src, FALSE);
else
gtk_widget_set_sensitive(moe->btn_speak_src, TRUE);
}
static void
on_btn_open_clicked(GtkButton *self, struct moetr *moe)
{
gtk_widget_grab_focus(moe->txt_src);
if (moe->is_ready == FALSE)
return;
(void)self;
}
static void
on_btn_switch_clicked(GtkButton *self, struct moetr *moe)
{
gtk_widget_grab_focus(moe->txt_src);
if (moe->is_ready == FALSE)
return;
guint src = gtk_drop_down_get_selected(GTK_DROP_DOWN(moe->lst_src));
if (src == 0)
return;
src--;
guint trg = gtk_drop_down_get_selected(GTK_DROP_DOWN(moe->lst_trg));
if (src == trg)
return;
trg++;
gtk_drop_down_set_selected(GTK_DROP_DOWN(moe->lst_src), trg);
gtk_drop_down_set_selected(GTK_DROP_DOWN(moe->lst_trg), src);
const char *const name = g_strstrip(txtbuf_get_name(&moe->txtbuf_trg));
txtbuf_set_body_text(&moe->txtbuf_src, name);
txtbuf_set_name(&moe->txtbuf_src, name);
moetr_translate(moe);
(void)self;
}
static void
on_btn_clear_clicked(GtkButton *self, struct moetr *moe)
{
if (moe->is_ready == FALSE)
return;
txtbuf_set_body_text(&moe->txtbuf_src, "");
txtbuf_set_name(&moe->txtbuf_src, "");
txtbuf_set_body_text(&moe->txtbuf_trg, "");
txtbuf_set_name(&moe->txtbuf_trg, "");
gtk_widget_grab_focus(moe->txt_src);
(void)self;
}
static void
on_btn_translate_clicked(GtkButton *self, struct moetr *moe)
{
if (moe->is_ready)
moetr_translate(moe);
else
http_request_cancel(&moe->http);
(void)self;
}
static void
on_btn_speak_src_clicked(GtkButton *self, struct moetr *moe)
{
gtk_widget_grab_focus(moe->txt_src);
if (moe->is_ready == FALSE)
return;
(void)self;
}
static void
on_btn_speak_trg_clicked(GtkButton *self, struct moetr *moe)
{
gtk_widget_grab_focus(moe->txt_src);
if (moe->is_ready == FALSE)
return;
(void)self;
}
static void
on_http_response_ready(GObject *self, GAsyncResult *res, void *udata)
{
GError *err = NULL;
struct http *const http = (struct http *)udata;
GBytes *const resp = soup_session_send_and_read_finish(SOUP_SESSION(self), res, &err);
SoupMessageHeaders *const hdrs = soup_message_get_response_headers(http->message);
const char *const content_type = soup_message_headers_get_content_type(hdrs, NULL);
if (err != NULL)
goto out0;
if (!content_type || g_ascii_strcasecmp(content_type, "application/json") != 0)
err = g_error_new(1, 1, "invalid response type");
out0:
http->callback(http->callback_udata, resp, err);
}
static void
on_moetr_http_response(void *udata, GBytes *resp, GError *err)
{
struct moetr *const moe = (struct moetr *)udata;
if (err != NULL) {
g_print("error occured: %s\n", err->message);
txtbuf_set_body_text(&moe->txtbuf_trg, "");
g_error_free(err);
} else {
moetr_write(moe, g_bytes_get_data(resp, NULL));
}
gtk_widget_grab_focus(moe->txt_src);
g_bytes_unref(resp);
moe->is_ready = TRUE;
gtk_button_set_label(GTK_BUTTON(moe->btn_translate), "Translate");
gtk_text_view_set_editable(GTK_TEXT_VIEW(moe->txt_src), TRUE);
}
/*
* main
*/
int
main(int argc, char *argv[])
{
struct moetr moe;
moetr_init(&moe);
const int ret = moetr_run(&moe, argc, argv);
moetr_deinit(&moe);
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment