fetch messages for channels and display them
This commit is contained in:
		
							parent
							
								
									8718ef85a0
								
							
						
					
					
						commit
						c3f4f5a180
					
				| @ -14,6 +14,7 @@ SET(SOURCES | |||||||
|   "include/dc/channel.h" |   "include/dc/channel.h" | ||||||
|   "include/dc/guild.h" |   "include/dc/guild.h" | ||||||
|   "include/dc/loop.h" |   "include/dc/loop.h" | ||||||
|  |   "include/dc/message.h" | ||||||
|   "include/dc/refable.h" |   "include/dc/refable.h" | ||||||
|   "include/dc/util.h" |   "include/dc/util.h" | ||||||
|   "src/account.c" |   "src/account.c" | ||||||
| @ -25,6 +26,7 @@ SET(SOURCES | |||||||
|   "src/channel.c" |   "src/channel.c" | ||||||
|   "src/guild.c" |   "src/guild.c" | ||||||
|   "src/loop.c" |   "src/loop.c" | ||||||
|  |   "src/message.c" | ||||||
|   "src/refable.c" |   "src/refable.c" | ||||||
|   "src/util.c" |   "src/util.c" | ||||||
|   ) |   ) | ||||||
|  | |||||||
| @ -77,6 +77,11 @@ bool dc_api_create_channel(dc_api_t api, dc_account_t login, | |||||||
|                            dc_account_t *recipients, size_t nrecp, |                            dc_account_t *recipients, size_t nrecp, | ||||||
|                            dc_channel_t *channel); |                            dc_channel_t *channel); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Fetch 50 messages for the given channel. | ||||||
|  |  */ | ||||||
|  | bool dc_api_get_messages(dc_api_t api, dc_account_t login, dc_channel_t c); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Fetch a list of friends of the login account "login". The friends are stored |  * Fetch a list of friends of the login account "login". The friends are stored | ||||||
|  * within the login object. |  * within the login object. | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <jansson.h> | #include <jansson.h> | ||||||
| 
 | 
 | ||||||
| #include <dc/account.h> | #include <dc/account.h> | ||||||
|  | #include <dc/message.h> | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * A discord channel. Exactly what it says on the tin. A place where one |  * A discord channel. Exactly what it says on the tin. A place where one | ||||||
| @ -49,10 +50,16 @@ typedef struct dc_channel_ *dc_channel_t; | |||||||
| dc_channel_t dc_channel_new(void); | dc_channel_t dc_channel_new(void); | ||||||
| dc_channel_t dc_channel_from_json(json_t *j); | dc_channel_t dc_channel_from_json(json_t *j); | ||||||
| 
 | 
 | ||||||
|  | char const *dc_channel_id(dc_channel_t c); | ||||||
|  | 
 | ||||||
| dc_channel_type_t dc_channel_type(dc_channel_t c); | dc_channel_type_t dc_channel_type(dc_channel_t c); | ||||||
| void dc_channel_set_type(dc_channel_t c, dc_channel_type_t t); | void dc_channel_set_type(dc_channel_t c, dc_channel_type_t t); | ||||||
| 
 | 
 | ||||||
| size_t dc_channel_recipients(dc_channel_t c); | size_t dc_channel_recipients(dc_channel_t c); | ||||||
| dc_account_t dc_channel_nthrecipient(dc_channel_t c, size_t i); | dc_account_t dc_channel_nthrecipient(dc_channel_t c, size_t i); | ||||||
| 
 | 
 | ||||||
|  | size_t dc_channel_messages(dc_channel_t c); | ||||||
|  | dc_message_t dc_channel_nthmessage(dc_channel_t c, size_t i); | ||||||
|  | void dc_channel_addmessages(dc_channel_t c, dc_message_t *m, size_t s); | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
							
								
								
									
										24
									
								
								libdc/include/dc/message.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								libdc/include/dc/message.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | #ifndef DC_MESSAGE_H | ||||||
|  | #define DC_MESSAGE_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <jansson.h> | ||||||
|  | 
 | ||||||
|  | #include <dc/account.h> | ||||||
|  | 
 | ||||||
|  | struct dc_message_; | ||||||
|  | typedef struct dc_message_ *dc_message_t; | ||||||
|  | 
 | ||||||
|  | dc_message_t dc_message_new(void); | ||||||
|  | dc_message_t dc_message_from_json(json_t *j); | ||||||
|  | json_t *dc_message_to_json(dc_message_t m); | ||||||
|  | 
 | ||||||
|  | char const *dc_message_id(dc_message_t m); | ||||||
|  | char const *dc_message_channel_id(dc_message_t m); | ||||||
|  | char const *dc_message_timestamp(dc_message_t m); | ||||||
|  | char const *dc_message_content(dc_message_t m); | ||||||
|  | dc_account_t dc_message_author(dc_message_t m); | ||||||
|  | 
 | ||||||
|  | int dc_message_compare(dc_message_t *a, dc_message_t *b); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -2,6 +2,47 @@ | |||||||
| 
 | 
 | ||||||
| #include "internal.h" | #include "internal.h" | ||||||
| 
 | 
 | ||||||
|  | bool dc_api_get_messages(dc_api_t api, dc_account_t login, dc_channel_t c) | ||||||
|  | { | ||||||
|  |     bool ret = false; | ||||||
|  |     char *url = NULL; | ||||||
|  |     json_t *reply = NULL, *i = NULL; | ||||||
|  |     GPtrArray *msgs = NULL; | ||||||
|  |     size_t idx = 0; | ||||||
|  | 
 | ||||||
|  |     return_if_true(api == NULL || login == NULL || c == NULL, false); | ||||||
|  | 
 | ||||||
|  |     msgs = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref); | ||||||
|  |     goto_if_true(msgs == NULL, cleanup); | ||||||
|  | 
 | ||||||
|  |     asprintf(&url, "channels/%s/messages", dc_channel_id(c)); | ||||||
|  |     goto_if_true(url == NULL, cleanup); | ||||||
|  | 
 | ||||||
|  |     reply = dc_api_call_sync(api, "GET", TOKEN(login), url, NULL); | ||||||
|  |     goto_if_true(reply == NULL, cleanup); | ||||||
|  |     goto_if_true(!json_is_array(reply), cleanup); | ||||||
|  | 
 | ||||||
|  |     json_array_foreach(reply, idx, i) { | ||||||
|  |         dc_message_t m = dc_message_from_json(i); | ||||||
|  |         g_ptr_array_add(msgs, m); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dc_channel_addmessages(c, (dc_message_t*)msgs->pdata, msgs->len); | ||||||
|  |     ret = true; | ||||||
|  | 
 | ||||||
|  | cleanup: | ||||||
|  | 
 | ||||||
|  |     if (msgs != NULL) { | ||||||
|  |         g_ptr_array_unref(msgs); | ||||||
|  |         msgs = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json_decref(reply); | ||||||
|  |     free(url); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool dc_api_create_channel(dc_api_t api, dc_account_t login, | bool dc_api_create_channel(dc_api_t api, dc_account_t login, | ||||||
|                            dc_account_t *recipients, size_t nrecp, |                            dc_account_t *recipients, size_t nrecp, | ||||||
|                            dc_channel_t *channel) |                            dc_channel_t *channel) | ||||||
|  | |||||||
| @ -43,6 +43,8 @@ struct dc_channel_ | |||||||
|     /*  application ID of the group DM creator if it is bot-created
 |     /*  application ID of the group DM creator if it is bot-created
 | ||||||
|      */ |      */ | ||||||
|     char *application_id; |     char *application_id; | ||||||
|  | 
 | ||||||
|  |     GPtrArray *messages; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void dc_channel_free(dc_channel_t c) | static void dc_channel_free(dc_channel_t c) | ||||||
| @ -62,6 +64,11 @@ static void dc_channel_free(dc_channel_t c) | |||||||
|         c->recipients = NULL; |         c->recipients = NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (c->messages != NULL) { | ||||||
|  |         g_ptr_array_unref(c->messages); | ||||||
|  |         c->messages = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     free(c); |     free(c); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -76,6 +83,10 @@ dc_channel_t dc_channel_new(void) | |||||||
|         (GDestroyNotify)dc_unref |         (GDestroyNotify)dc_unref | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|  |     c->messages = g_ptr_array_new_with_free_func( | ||||||
|  |         (GDestroyNotify)dc_unref | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|     return dc_ref(c); |     return dc_ref(c); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -214,6 +225,12 @@ json_t *dc_channel_to_json(dc_channel_t c) | |||||||
|     return j; |     return j; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | char const *dc_channel_id(dc_channel_t c) | ||||||
|  | { | ||||||
|  |     return_if_true(c == NULL, NULL); | ||||||
|  |     return c->id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| dc_channel_type_t dc_channel_type(dc_channel_t c) | dc_channel_type_t dc_channel_type(dc_channel_t c) | ||||||
| { | { | ||||||
|     return_if_true(c == NULL, -1); |     return_if_true(c == NULL, -1); | ||||||
| @ -238,3 +255,30 @@ dc_account_t dc_channel_nthrecipient(dc_channel_t c, size_t i) | |||||||
|     return_if_true(i >= c->recipients->len, NULL); |     return_if_true(i >= c->recipients->len, NULL); | ||||||
|     return g_ptr_array_index(c->recipients, i); |     return g_ptr_array_index(c->recipients, i); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | size_t dc_channel_messages(dc_channel_t c) | ||||||
|  | { | ||||||
|  |     return_if_true(c == NULL || c->messages == NULL, 0); | ||||||
|  |     return c->messages->len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | dc_message_t dc_channel_nthmessage(dc_channel_t c, size_t i) | ||||||
|  | { | ||||||
|  |     return_if_true(c == NULL || c->messages == NULL, NULL); | ||||||
|  |     return_if_true(i >= c->messages->len, NULL); | ||||||
|  |     return g_ptr_array_index(c->messages, i); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dc_channel_addmessages(dc_channel_t c, dc_message_t *m, size_t s) | ||||||
|  | { | ||||||
|  |     return_if_true(c == NULL || c->messages == NULL,); | ||||||
|  |     return_if_true(m == NULL || s == 0,); | ||||||
|  | 
 | ||||||
|  |     size_t i = 0; | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < s; i++) { | ||||||
|  |         g_ptr_array_add(c->messages, dc_ref(m[i])); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     g_ptr_array_sort(c->messages, (GCompareFunc)dc_message_compare); | ||||||
|  | } | ||||||
|  | |||||||
| @ -30,4 +30,6 @@ | |||||||
| #define return_if_true(v,r) do { if (v) return r; } while(0) | #define return_if_true(v,r) do { if (v) return r; } while(0) | ||||||
| #define goto_if_true(v,l) do { if (v) goto l; } while(0) | #define goto_if_true(v,l) do { if (v) goto l; } while(0) | ||||||
| 
 | 
 | ||||||
|  | #define TOKEN(l) (dc_account_token(l)) | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
							
								
								
									
										155
									
								
								libdc/src/message.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								libdc/src/message.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,155 @@ | |||||||
|  | #include <dc/message.h> | ||||||
|  | #include "internal.h" | ||||||
|  | 
 | ||||||
|  | struct dc_message_ | ||||||
|  | { | ||||||
|  |     dc_refable_t ref; | ||||||
|  | 
 | ||||||
|  |     char *id; | ||||||
|  |     char *timestamp; | ||||||
|  |     char *content; | ||||||
|  |     char *channel_id; | ||||||
|  | 
 | ||||||
|  |     time_t ts; | ||||||
|  | 
 | ||||||
|  |     dc_account_t author; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void dc_message_parse_timestamp(dc_message_t m); | ||||||
|  | 
 | ||||||
|  | static void dc_message_free(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL,); | ||||||
|  | 
 | ||||||
|  |     free(m->id); | ||||||
|  |     free(m->timestamp); | ||||||
|  |     free(m->content); | ||||||
|  |     free(m->channel_id); | ||||||
|  | 
 | ||||||
|  |     dc_unref(m->author); | ||||||
|  | 
 | ||||||
|  |     free(m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | dc_message_t dc_message_new(void) | ||||||
|  | { | ||||||
|  |     dc_message_t m = calloc(1, sizeof(struct dc_message_)); | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     m->ref.cleanup = (dc_cleanup_t)dc_message_free; | ||||||
|  |     return dc_ref(m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | dc_message_t dc_message_from_json(json_t *j) | ||||||
|  | { | ||||||
|  |     dc_message_t m = NULL; | ||||||
|  |     json_t *val = NULL; | ||||||
|  |     return_if_true(j == NULL || !json_is_object(j), NULL); | ||||||
|  | 
 | ||||||
|  |     m = dc_message_new(); | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     val = json_object_get(j, "id"); | ||||||
|  |     goto_if_true(val == NULL || !json_is_string(val), error); | ||||||
|  |     m->id = strdup(json_string_value(val)); | ||||||
|  | 
 | ||||||
|  |     val = json_object_get(j, "timestamp"); | ||||||
|  |     goto_if_true(val == NULL || !json_is_string(val), error); | ||||||
|  |     m->timestamp = strdup(json_string_value(val)); | ||||||
|  | 
 | ||||||
|  |     dc_message_parse_timestamp(m); | ||||||
|  | 
 | ||||||
|  |     val = json_object_get(j, "content"); | ||||||
|  |     goto_if_true(val == NULL || !json_is_string(val), error); | ||||||
|  |     m->content = strdup(json_string_value(val)); | ||||||
|  | 
 | ||||||
|  |     val = json_object_get(j, "channel_id"); | ||||||
|  |     goto_if_true(val == NULL || !json_is_string(val), error); | ||||||
|  |     m->channel_id = strdup(json_string_value(val)); | ||||||
|  | 
 | ||||||
|  |     val = json_object_get(j, "author"); | ||||||
|  |     goto_if_true(val == NULL || !json_is_object(val), error); | ||||||
|  |     m->author = dc_account_from_json(val); | ||||||
|  | 
 | ||||||
|  |     return m; | ||||||
|  | 
 | ||||||
|  | error: | ||||||
|  | 
 | ||||||
|  |     dc_unref(m); | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | json_t *dc_message_to_json(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     json_t *j = json_object(); | ||||||
|  |     return_if_true(j == NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     if (m->id != NULL) { | ||||||
|  |         json_object_set_new(j, "id", json_string(m->id)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m->timestamp != NULL) { | ||||||
|  |         json_object_set_new(j, "timestamp", json_string(m->timestamp)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m->channel_id != NULL) { | ||||||
|  |         json_object_set_new(j, "channel_id", json_string(m->channel_id)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m->author != NULL) { | ||||||
|  |         json_t *a = dc_account_to_json(m->author); | ||||||
|  |         json_object_set_new(j, "author", a); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json_object_set_new(j, "content", json_string(m->content)); | ||||||
|  | 
 | ||||||
|  |     return j; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dc_message_parse_timestamp(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL || m->timestamp == NULL,); | ||||||
|  |     struct tm t = {0}; | ||||||
|  | 
 | ||||||
|  |     strptime(m->timestamp, "%Y-%m-%dT%H:%M:%S", &t); | ||||||
|  |     m->ts = timegm(&t); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int dc_message_compare(dc_message_t *a, dc_message_t *b) | ||||||
|  | { | ||||||
|  |     return_if_true(a == NULL || *a == NULL || | ||||||
|  |                    b == NULL || *b == NULL, 0); | ||||||
|  |     return (*a)->ts - (*b)->ts; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char const *dc_message_id(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  |     return m->id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char const *dc_message_channel_id(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  |     return m->channel_id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char const *dc_message_timestamp(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  |     return m->timestamp; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char const *dc_message_content(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  |     return m->content; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | dc_account_t dc_message_author(dc_message_t m) | ||||||
|  | { | ||||||
|  |     return_if_true(m == NULL, NULL); | ||||||
|  |     return m->author; | ||||||
|  | } | ||||||
| @ -58,6 +58,8 @@ wchar_t *util_readkey(int esc, WINDOW *win); | |||||||
| 
 | 
 | ||||||
| void exit_main(void); | void exit_main(void); | ||||||
| 
 | 
 | ||||||
|  | wchar_t *s_convert(char const *s); | ||||||
|  | 
 | ||||||
| int strwidth(char const *string); | int strwidth(char const *string); | ||||||
| char *read_char(FILE *stream); | char *read_char(FILE *stream); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,6 +39,11 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | |||||||
|         goto cleanup; |         goto cleanup; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (!dc_api_get_friends(api, acc)) { | ||||||
|  |         LOG(n, L"login: %ls: failed to load friends", av[1]); | ||||||
|  |         goto cleanup; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     current_account = dc_ref(acc); |     current_account = dc_ref(acc); | ||||||
|     LOG(n, L"login: %ls: authentication successful", av[1]); |     LOG(n, L"login: %ls: authentication successful", av[1]); | ||||||
|     ret = true; |     ret = true; | ||||||
|  | |||||||
| @ -48,12 +48,16 @@ bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | |||||||
|     if (c == NULL) { |     if (c == NULL) { | ||||||
|         /* no? create a new window and switch to it
 |         /* no? create a new window and switch to it
 | ||||||
|          */ |          */ | ||||||
| 
 |  | ||||||
|         if (!dc_api_create_channel(api, current_account, &f, 1, &c)) { |         if (!dc_api_create_channel(api, current_account, &f, 1, &c)) { | ||||||
|             LOG(n, L"msg: failed to create channel"); |             LOG(n, L"msg: failed to create channel"); | ||||||
|             goto cleanup; |             goto cleanup; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (!dc_api_get_messages(api, current_account, c)) { | ||||||
|  |             LOG(n, L"msg: failed to fetch messages in channel"); | ||||||
|  |             goto cleanup; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         v = ncdc_textview_new(); |         v = ncdc_textview_new(); | ||||||
|         goto_if_true(v == NULL, cleanup); |         goto_if_true(v == NULL, cleanup); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -133,16 +133,11 @@ wchar_t const *ncdc_textview_nthline(ncdc_textview_t v, size_t idx) | |||||||
|     return g_ptr_array_index(v->par, idx); |     return g_ptr_array_index(v->par, idx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ncdc_textview_render(ncdc_textview_t v, WINDOW *win, int lines, int cols) | static void | ||||||
|  | ncdc_textview_render_par(ncdc_textview_t v, WINDOW *win, int lines, int cols) | ||||||
| { | { | ||||||
|     ssize_t i = 0, needed_lines = 0, atline = 0; |     ssize_t i = 0, needed_lines = 0, atline = 0; | ||||||
| 
 | 
 | ||||||
|     werase(win); |  | ||||||
| 
 |  | ||||||
|     if (v->par == NULL || v->par->len == 0) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for (i = v->par->len-1; i >= 0; i--) { |     for (i = v->par->len-1; i >= 0; i--) { | ||||||
|         wchar_t const *w = ncdc_textview_nthline(v, i); |         wchar_t const *w = ncdc_textview_nthline(v, i); | ||||||
|         size_t sz = wcslen(w); |         size_t sz = wcslen(w); | ||||||
| @ -159,3 +154,92 @@ void ncdc_textview_render(ncdc_textview_t v, WINDOW *win, int lines, int cols) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static wchar_t *ncdc_textview_format(dc_message_t m) | ||||||
|  | { | ||||||
|  |     wchar_t *c = NULL, *author = NULL, *message = NULL; | ||||||
|  |     size_t clen = 0; | ||||||
|  |     FILE *f = open_wmemstream(&c, &clen); | ||||||
|  |     wchar_t *ret = NULL; | ||||||
|  |     dc_account_t a = dc_message_author(m); | ||||||
|  | 
 | ||||||
|  |     return_if_true(f == NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     author = s_convert(dc_account_fullname(a)); | ||||||
|  |     goto_if_true(author == NULL, cleanup); | ||||||
|  | 
 | ||||||
|  |     message = s_convert(dc_message_content(m)); | ||||||
|  |     goto_if_true(message == NULL, cleanup); | ||||||
|  | 
 | ||||||
|  |     fwprintf(f, L"< %ls> %ls", author, message); | ||||||
|  | 
 | ||||||
|  |     fclose(f); | ||||||
|  |     f = NULL; | ||||||
|  | 
 | ||||||
|  |     ret = c; | ||||||
|  |     c = NULL; | ||||||
|  | 
 | ||||||
|  | cleanup: | ||||||
|  | 
 | ||||||
|  |     if (f != NULL) { | ||||||
|  |         fclose(f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     free(author); | ||||||
|  |     free(message); | ||||||
|  |     free(c); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | ncdc_textview_render_msgs(ncdc_textview_t v, WINDOW *win, int lines, int cols) | ||||||
|  | { | ||||||
|  |     ssize_t i = 0, atline = 0, msgs = 0; | ||||||
|  | 
 | ||||||
|  |     msgs = dc_channel_messages(v->channel); | ||||||
|  |     atline = lines; | ||||||
|  | 
 | ||||||
|  |     for (i = msgs-1; i >= 0; i--) { | ||||||
|  |         dc_message_t m = dc_channel_nthmessage(v->channel, i); | ||||||
|  |         wchar_t *s = ncdc_textview_format(m); | ||||||
|  |         wchar_t const *end = s, *last = NULL; | ||||||
|  |         size_t len = 0; | ||||||
|  |         size_t needed_lines = 0; | ||||||
|  | 
 | ||||||
|  |         /* count each line, and, see if it is longer than COLS
 | ||||||
|  |          */ | ||||||
|  |         while ((end = wcschr(end, '\n')) != NULL) { | ||||||
|  |             ++needed_lines; | ||||||
|  | 
 | ||||||
|  |             len = wcswidth(last, (end - last)); | ||||||
|  |             needed_lines += (len % cols); | ||||||
|  |             last = end; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (last == NULL) { | ||||||
|  |             last = s; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         len = wcswidth(last, wcslen(last)); | ||||||
|  |         needed_lines += (len / cols) + 1; | ||||||
|  | 
 | ||||||
|  |         if ((atline - needed_lines) >= 0) { | ||||||
|  |             atline -= needed_lines; | ||||||
|  |             mvwaddwstr(win, atline, 0, s); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         free(s); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ncdc_textview_render(ncdc_textview_t v, WINDOW *win, int lines, int cols) | ||||||
|  | { | ||||||
|  |     werase(win); | ||||||
|  | 
 | ||||||
|  |     if (v->par != NULL && v->par->len > 0) { | ||||||
|  |         ncdc_textview_render_par(v, win, lines, cols); | ||||||
|  |     } else if (v->channel != NULL) { | ||||||
|  |         ncdc_textview_render_msgs(v, win, lines, cols); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -168,3 +168,17 @@ char *w_convert(wchar_t const *w) | |||||||
|     wcstombs(ptr, w, sz); |     wcstombs(ptr, w, sz); | ||||||
|     return ptr; |     return ptr; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | wchar_t *s_convert(char const *w) | ||||||
|  | { | ||||||
|  |     size_t sz = 0; | ||||||
|  |     wchar_t *ptr = NULL; | ||||||
|  | 
 | ||||||
|  |     sz = mbstowcs(NULL, w, 0); | ||||||
|  | 
 | ||||||
|  |     ptr = calloc(sz+1, sizeof(wchar_t)); | ||||||
|  |     return_if_true(ptr == NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     mbstowcs(ptr, w, sz); | ||||||
|  |     return ptr; | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user