1 #include "friend.h"
2 
3 #include "settings.h"
4 #include "sidebar.h"
5 
6 #include "../avatar.h"
7 #include "../debug.h"
8 #include "../flist.h"
9 #include "../friend.h"
10 #include "../macros.h"
11 #include "../settings.h"
12 #include "../theme.h"
13 #include "../tox.h"
14 
15 #include "../native/dialog.h"
16 
17 #include "../ui/draw.h"
18 #include "../ui/edit.h"
19 #include "../ui/scrollable.h"
20 #include "../ui/svg.h"
21 #include "../ui/text.h"
22 
23 #include "../main.h" // add friend status // TODO this is stupid wrong
24 
25 #include <string.h>
26 
27 /* Header for friend chat window */
draw_friend(int x,int y,int w,int height)28 static void draw_friend(int x, int y, int w, int height) {
29     FRIEND *f = flist_get_friend();
30     if (!f) {
31         LOG_ERR("Friend", "Could not get selected friend.\n");
32         return;
33     }
34 
35     // draw avatar or default image
36     if (friend_has_avatar(f)) {
37         draw_avatar_image(f->avatar->img, x + SCALE(10), SCALE(10), f->avatar->width, f->avatar->height,
38                           BM_CONTACT_WIDTH, BM_CONTACT_WIDTH);
39     } else {
40         drawalpha(BM_CONTACT, x + SCALE(10), SCALE(10), BM_CONTACT_WIDTH, BM_CONTACT_WIDTH, COLOR_MAIN_TEXT);
41     }
42 
43     setcolor(COLOR_MAIN_TEXT);
44     setfont(FONT_TITLE);
45 
46     if (f->alias) {
47         drawtextrange(x + SCALE(60), settings.window_width - SCALE(128), SCALE(18), f->alias, f->alias_length);
48     } else {
49         drawtextrange(x + SCALE(60), settings.window_width - SCALE(128), SCALE(18), f->name, f->name_length);
50     }
51 
52     setcolor(COLOR_MAIN_TEXT_SUBTEXT);
53     setfont(FONT_STATUS);
54     drawtextrange(x + SCALE(60), settings.window_width - SCALE(128), SCALE(32), f->status_message,
55                   f->status_length);
56 
57     if (f->typing) {
58         int typing_y = ((y + height) + SCALE(CHAT_BOX_TOP - 14));
59         setfont(FONT_MISC);
60         // @TODO: separate these colors if needed
61         setcolor(COLOR_MAIN_TEXT_HINT);
62         if (f->alias) {
63             drawtextwidth_right(x, SCALE(MESSAGES_X - NAME_OFFSET - 16), typing_y, f->alias, f->alias_length);
64         } else {
65             drawtextwidth_right(x, SCALE(MESSAGES_X - NAME_OFFSET - 16), typing_y, f->name, f->name_length);
66         }
67         drawtextwidth(x + SCALE(MESSAGES_X - 18), x + w, typing_y, S(IS_TYPING), SLEN(IS_TYPING));
68     }
69 }
70 
71 /* Draw an invite to be a friend window */
draw_friend_request(int x,int y,int w,int h)72 static void draw_friend_request(int x, int y, int w, int h) {
73     FREQUEST *req = flist_get_frequest();
74     if (!req) {
75         LOG_ERR("Layout Friend", "Unable to draw a friend request without a friend request.");
76         return;
77     }
78 
79     setcolor(COLOR_MAIN_TEXT);
80     setfont(FONT_SELF_NAME);
81     drawstr(x + SCALE(10), SCALE(20), FRIENDREQUEST);
82 
83     if (req->msg && req->length) {
84         setfont(FONT_TEXT);
85         utox_draw_text_multiline_within_box(x + SCALE(10), y + SCALE(70), w + x, y, y + h, font_small_lineheight,
86                                             req->msg, req->length, ~0, ~0, 0, 0, true);
87     }
88 }
89 
draw_friend_settings(int x,int y,int UNUSED (width),int UNUSED (height))90 static void draw_friend_settings(int x, int y, int UNUSED(width), int UNUSED(height)) {
91     setcolor(COLOR_MAIN_TEXT);
92     setfont(FONT_SELF_NAME);
93 
94     drawstr(x + SCALE(10), y + SCALE(MAIN_TOP + 10), FRIEND_PUBLIC_KEY);
95     drawstr(x + SCALE(10), y + SCALE(MAIN_TOP + 60), FRIEND_ALIAS);
96     drawstr(x + SCALE(20) + BM_SWITCH_WIDTH, y + SCALE(MAIN_TOP + 112), FRIEND_AUTOACCEPT);
97 }
98 
draw_friend_deletion(int x,int UNUSED (y),int UNUSED (w),int UNUSED (height))99 static void draw_friend_deletion(int x, int UNUSED(y), int UNUSED(w), int UNUSED(height)) {
100     FRIEND *f = flist_get_friend();
101     if (!f) {
102         LOG_ERR("Friend", "Could not get selected friend.");
103         return;
104     }
105 
106     setcolor(COLOR_MAIN_TEXT);
107     setfont(FONT_SELF_NAME);
108 
109     int length = f->name_length + 2;
110     char str[length];
111     snprintf(str, length, "%.*s?", (int)f->name_length, f->name);
112 
113     const int push = UTOX_STR_WIDTH(DELETE_MESSAGE);
114     drawstr(x + SCALE(10), SCALE(70), DELETE_MESSAGE);
115     drawtextrange(push + x + SCALE(10), settings.window_width, SCALE(70), str, length - 1);
116 }
117 
118 /* Draw add a friend window */
draw_add_friend(int x,int UNUSED (y),int UNUSED (w),int height)119 static void draw_add_friend(int x, int UNUSED(y), int UNUSED(w), int height) {
120     setcolor(COLOR_MAIN_TEXT);
121     setfont(FONT_SELF_NAME);
122     drawstr(x + SCALE(10), SCALE(20), ADDFRIENDS);
123 
124     setcolor(COLOR_MAIN_TEXT_SUBTEXT);
125     setfont(FONT_TEXT);
126     drawstr(x + SCALE(10), SCALE(MAIN_TOP + 10), TOXID);
127 
128     drawstr(x + SCALE(10), SCALE(MAIN_TOP + 58), MESSAGE);
129 
130     if (!addfriend_status) {
131         return;
132     }
133 
134     setfont(FONT_MISC);
135     setcolor(C_RED);
136 
137     STRING *str;
138 
139     switch (addfriend_status) {
140         case ADDF_SENT:
141             str = SPTR(REQ_SENT);
142             break;
143         case ADDF_BADNAME:
144             str = SPTR(REQ_INVALID_ID);
145             break;
146         case ADDF_NONAME:
147             str = SPTR(REQ_EMPTY_ID);
148             break;
149         case ADDF_TOOLONG: // if message length is too long.
150             str = SPTR(REQ_LONG_MSG);
151             break;
152         case ADDF_NOMESSAGE: // if no message (message length must be >= 1 byte).
153             str = SPTR(REQ_NO_MSG);
154             break;
155         case ADDF_OWNKEY: // if user's own key.
156             str = SPTR(REQ_SELF_ID);
157             break;
158         case ADDF_ALREADYSENT: // if friend request already sent or already a friend.
159             str = SPTR(REQ_ALREADY_FRIENDS);
160             break;
161         case ADDF_BADCHECKSUM: // if bad checksum in address.
162             str = SPTR(REQ_BAD_CHECKSUM);
163             break;
164         case ADDF_SETNEWNOSPAM: // if the friend was already there but the nospam was different.
165             str = SPTR(REQ_BAD_NOSPAM);
166             break;
167         case ADDF_NOMEM: // if increasing the friend list size fails.
168             str = SPTR(REQ_NO_MEMORY);
169             break;
170         case ADDF_NOFREQUESTSENT:
171             str = SPTR(REQ_ADDED_NO_FREQ_SENT);
172             break;
173         case ADDF_UNKNOWN: // for unknown error.
174         case ADDF_NONE:    // this case must never be rendered, but if it does, assume it's an error
175         default:
176             str = SPTR(REQ_UNKNOWN);
177             break;
178     }
179 
180     utox_draw_text_multiline_within_box(x + SCALE(10), MAIN_TOP + SCALE(166),
181                                         settings.window_width - BM_SBUTTON_WIDTH - SCALE(10), 0, height,
182                                         font_small_lineheight, str->str, str->length, 0xFFFF, 0, 0, 0, 1);
183 }
184 
185 SCROLLABLE scrollbar_friend = {
186     .panel = { .type = PANEL_SCROLLABLE, },
187     .color = C_SCROLL,
188 };
189 
190 PANEL messages_friend = {
191     .type = PANEL_MESSAGES,
192     .content_scroll = &scrollbar_friend,
193 };
194 
195 PANEL
196 panel_friend = {
197     .type = PANEL_NONE,
198     .disabled = 1,
199     .child = (PANEL*[]) {
200         &panel_friend_chat,
201         &panel_friend_video,
202         &panel_friend_settings,
203         &panel_friend_confirm_deletion,
204         NULL
205     }
206 },
207 panel_friend_chat = {
208     .type = PANEL_NONE,
209     .disabled = 0,
210     .drawfunc = draw_friend,
211     .child = (PANEL*[]) {
212         (PANEL*)&scrollbar_friend,
213         (PANEL*)&edit_chat_msg_friend, // this needs to be one of the first, to get events before the others
214         (PANEL*)&messages_friend,
215         (PANEL*)&button_call_decline,
216         (PANEL*)&button_call_audio,
217         (PANEL*)&button_call_video,
218         (PANEL*)&button_send_file,
219         (PANEL*)&button_send_screenshot,
220         (PANEL*)&button_chat_send_friend,
221         NULL
222     }
223 },
224 panel_friend_video = {
225     .type = PANEL_INLINE_VIDEO,
226     .disabled = 1,
227     .child = (PANEL*[]) {
228         NULL
229     }
230 },
231 panel_friend_settings = {
232     .type = PANEL_NONE,
233     .disabled = 1,
234     .drawfunc = draw_friend_settings,
235     .child = (PANEL*[]) {
236         (PANEL*)&edit_friend_pubkey,
237         (PANEL*)&edit_friend_alias,
238         (PANEL*)&switch_friend_autoaccept_ft,
239         (PANEL*)&button_export_chatlog,
240         NULL
241     }
242 },
243 panel_friend_confirm_deletion = {
244     .type = PANEL_NONE,
245     .disabled = true,
246     .drawfunc = draw_friend_deletion,
247     .child = (PANEL*[]) {
248         (PANEL *)&button_confirm_deletion,
249         (PANEL *)&button_deny_deletion,
250         NULL
251     }
252 
253 },
254 panel_friend_request = {
255     .type = PANEL_NONE,
256     .disabled = 1,
257     .drawfunc = draw_friend_request,
258     .child = (PANEL*[]) {
259         (PANEL*)&button_accept_friend,
260         (PANEL*)&button_ignore_friend,
261         NULL
262     }
263 },
264 panel_add_friend = {
265     .type = PANEL_NONE,
266     .disabled = 1,
267     .drawfunc = draw_add_friend,
268     .child = (PANEL*[]) {
269         (PANEL*)&button_send_friend_request,
270         (PANEL*)&edit_add_new_friend_id,
271         (PANEL*)&edit_add_new_friend_msg,
272         NULL
273     }
274 };
275 
button_add_new_contact_on_mup(void)276 static void button_add_new_contact_on_mup(void) {
277     if (tox_thread_init == UTOX_TOX_THREAD_INIT_SUCCESS) {
278         /* Only change if we're logged in! */
279         edit_setstr(&edit_add_new_friend_id, (char *)edit_search.data, edit_search.length);
280         edit_setstr(&edit_search, (char *)"", 0);
281         flist_selectaddfriend();
282         edit_setfocus(&edit_add_new_friend_msg);
283     }
284 }
285 
button_send_friend_request_on_mup(void)286 static void button_send_friend_request_on_mup(void) {
287     friend_add(edit_add_new_friend_id.data, edit_add_new_friend_id.length, edit_add_new_friend_msg.data, edit_add_new_friend_msg.length);
288     edit_resetfocus();
289 }
290 
291 #include "../tox.h"
292 
button_call_decline_on_mup(void)293 static void button_call_decline_on_mup(void) {
294     FRIEND *f = flist_get_friend();
295     if (!f) {
296         LOG_ERR("Friend", "Could not get selected friend.");
297         return;
298     }
299 
300     if (f->call_state_friend) {
301         LOG_TRACE("Layout Friend", "Declining call: %u", f->number);
302         postmessage_toxcore(TOX_CALL_DISCONNECT, f->number, 0, NULL);
303     }
304 }
305 
306 #include "../av/utox_av.h"
307 #include "../av/audio.h"
308 #include "../ui/button.h"
button_call_decline_update(BUTTON * b)309 static void button_call_decline_update(BUTTON *b) {
310     FRIEND *f = flist_get_friend();
311     if (!f) {
312         LOG_ERR("Friend", "Could not get selected friend.");
313         return;
314     }
315 
316     if (UTOX_AVAILABLE_AUDIO(f->number) && !UTOX_SENDING_AUDIO(f->number)) {
317         button_setcolors_danger(b);
318         b->nodraw = b->disabled = b->panel.disabled = false;
319     } else {
320         button_setcolors_disabled(b);
321         b->nodraw = b->disabled = b->panel.disabled = true;
322     }
323 }
324 
button_call_audio_on_mup(void)325 static void button_call_audio_on_mup(void) {
326     FRIEND *f = flist_get_friend();
327     if (!f) {
328         LOG_ERR("Friend", "Could not get selected friend.");
329         return;
330     }
331 
332     if (f->call_state_self) {
333         if (UTOX_SENDING_AUDIO(f->number)) {
334             LOG_TRACE("Layout Friend", "Ending call: %u", f->number);
335             /* var 3/4 = bool send video */
336             postmessage_toxcore(TOX_CALL_DISCONNECT, f->number, 0, NULL);
337         } else {
338             LOG_TRACE("Layout Friend", "Canceling call: friend = %d", f->number);
339             postmessage_toxcore(TOX_CALL_DISCONNECT, f->number, 0, NULL);
340         }
341     } else if (UTOX_AVAILABLE_AUDIO(f->number)) {
342         LOG_TRACE("Layout Friend", "Accept Call: %u", f->number);
343         postmessage_toxcore(TOX_CALL_ANSWER, f->number, 0, NULL);
344     } else if (f->online) {
345         postmessage_toxcore(TOX_CALL_SEND, f->number, 0, NULL);
346         LOG_TRACE("Layout Friend", "Calling friend: %u", f->number);
347     }
348 }
349 
button_call_audio_update(BUTTON * b)350 static void button_call_audio_update(BUTTON *b) {
351     FRIEND *f = flist_get_friend();
352     if (!f) {
353         LOG_ERR("Friend", "Could not get selected friend.");
354         return;
355     }
356 
357     if (UTOX_SENDING_AUDIO(f->number)) {
358         button_setcolors_danger(b);
359         b->disabled = false;
360     } else if (UTOX_AVAILABLE_AUDIO(f->number)) {
361         button_setcolors_warning(b);
362         b->disabled = false;
363     } else {
364         if (f->online) {
365             button_setcolors_success(b);
366             b->disabled = false;
367         } else {
368             button_setcolors_disabled(b);
369             b->disabled = true;
370         }
371     }
372 }
373 
374 #include "../av/video.h"
button_call_video_on_mup(void)375 static void button_call_video_on_mup(void) {
376     FRIEND *f = flist_get_friend();
377     if (!f) {
378         LOG_ERR("Friend", "Could not get selected friend.");
379         return;
380     }
381 
382     if (f->call_state_self) {
383         if (SELF_ACCEPT_VIDEO(f->number)) {
384             LOG_TRACE("Layout Friend", "Canceling call (video): %u", f->number);
385             postmessage_toxcore(TOX_CALL_PAUSE_VIDEO, f->number, 1, NULL);
386         } else if (UTOX_SENDING_AUDIO(f->number)) {
387             LOG_TRACE("Layout Friend", "Audio call inprogress, adding video");
388             postmessage_toxcore(TOX_CALL_RESUME_VIDEO, f->number, 1, NULL);
389         } else {
390             LOG_TRACE("Layout Friend", "Ending call (video): %u", f->number);
391             postmessage_toxcore(TOX_CALL_DISCONNECT, f->number, 1, NULL);
392         }
393     } else if (f->call_state_friend) {
394         LOG_TRACE("Layout Friend", "Accept Call (video): %u %u", f->number, f->call_state_friend);
395         postmessage_toxcore(TOX_CALL_ANSWER, f->number, 1, NULL);
396     } else if (f->online) {
397         postmessage_toxcore(TOX_CALL_SEND, f->number, 1, NULL);
398         LOG_TRACE("Layout Friend", "Calling friend (video): %u", f->number);
399     }
400 }
401 
button_call_video_update(BUTTON * b)402 static void button_call_video_update(BUTTON *b) {
403     FRIEND *f = flist_get_friend();
404     if (!f) {
405         LOG_ERR("Friend", "Could not get selected friend.");
406         return;
407     }
408 
409     if (SELF_SEND_VIDEO(f->number)) {
410         button_setcolors_danger(b);
411         b->disabled = false;
412     } else if (FRIEND_SENDING_VIDEO(f->number)) {
413         button_setcolors_warning(b);
414         b->disabled = false;
415     } else {
416         if (f->online) {
417             button_setcolors_success(b);
418             b->disabled = false;
419         } else {
420             button_setcolors_disabled(b);
421             b->disabled = true;
422         }
423     }
424 }
425 
button_accept_friend_on_mup(void)426 static void button_accept_friend_on_mup(void) {
427     FREQUEST *req = flist_get_frequest();
428     postmessage_toxcore(TOX_FRIEND_ACCEPT, 0, 0, req);
429     panel_friend_request.disabled = true;
430 }
431 
button_ignore_friend_on_mup(void)432 static void button_ignore_friend_on_mup(void){
433     FREQUEST *req = flist_get_frequest();
434     if (!req) {
435         LOG_ERR("Friend", "Could not get selected friend request");
436         return;
437     }
438 
439     flist_delete_sitem();
440     panel_friend_request.disabled = true;
441 }
442 
button_menu_update(BUTTON * b)443 static void button_menu_update(BUTTON *b) {
444     b->c1  = COLOR_BKGRND_MENU;
445     b->c2  = COLOR_BKGRND_MENU_HOVER;
446     b->c3  = COLOR_BKGRND_MENU_ACTIVE;
447     b->ct1 = COLOR_MENU_TEXT;
448     b->ct2 = COLOR_MENU_TEXT;
449     if (b->mousedown || b->disabled) {
450         b->ct1 = COLOR_MENU_TEXT_ACTIVE;
451         b->ct2 = COLOR_MENU_TEXT_ACTIVE;
452     }
453     b->cd = COLOR_BKGRND_MENU_ACTIVE;
454 }
455 
456 BUTTON button_add_new_contact = {
457     .panel = {
458         .type   = PANEL_BUTTON,
459         .x      = SIDEBAR_BUTTON_LEFT,
460         .y      = ROSTER_BOTTOM,
461         .width  = SIDEBAR_BUTTON_WIDTH,
462         .height = SIDEBAR_BUTTON_HEIGHT,
463     },
464     .bm_icon      = BM_ADD,
465     .icon_w       = _BM_ADD_WIDTH,
466     .icon_h       = _BM_ADD_WIDTH,
467     .update       = button_menu_update,
468     .on_mup       = button_add_new_contact_on_mup,
469     .disabled     = true,
470     .nodraw       = true,
471     .tooltip_text = {.i18nal = STR_ADDFRIENDS },
472 };
473 
474 BUTTON button_send_friend_request = {
475     .panel = {
476         .type   = PANEL_BUTTON,
477         .x      = -10 - _BM_SBUTTON_WIDTH,
478         .y      = MAIN_TOP + 168,
479         .width  = _BM_SBUTTON_WIDTH,
480         .height = _BM_SBUTTON_HEIGHT,
481     },
482     .bm_fill         = BM_SBUTTON,
483     .button_text = {.i18nal = STR_ADD },
484     .update      = button_setcolors_success,
485     .on_mup      = button_send_friend_request_on_mup,
486     .disabled    = false,
487 };
488 
489 BUTTON button_call_decline = {
490     .panel = {
491         .type   = PANEL_BUTTON,
492         .x      = -186,
493         .y      =   10,
494         .width  = _BM_LBUTTON_WIDTH,
495         .height = _BM_LBUTTON_HEIGHT,
496     },
497     .bm_fill      = BM_LBUTTON,
498     .bm_icon      = BM_DECLINE,
499     .icon_w       = _BM_LBICON_WIDTH,
500     .icon_h       = _BM_LBICON_HEIGHT,
501     .on_mup       = button_call_decline_on_mup,
502     .update       = button_call_decline_update,
503     .tooltip_text = {.i18nal = STR_CALL_DECLINE },
504     .nodraw       = true,
505     .disabled     = true,
506 };
507 
508 BUTTON button_call_audio = {
509     .panel = {
510         .type   = PANEL_BUTTON,
511         .x      = -124,
512         .y      =   10,
513         .width  = _BM_LBUTTON_WIDTH,
514         .height = _BM_LBUTTON_HEIGHT,
515     },
516     .bm_fill      = BM_LBUTTON,
517     .bm_icon      = BM_CALL,
518     .icon_w       = _BM_LBICON_WIDTH,
519     .icon_h       = _BM_LBICON_HEIGHT,
520     .on_mup       = button_call_audio_on_mup,
521     .update       = button_call_audio_update,
522     .tooltip_text = {.i18nal = STR_CALL_START_AUDIO },
523 };
524 
525 BUTTON button_call_video = {
526     .panel = {
527         .type   = PANEL_BUTTON,
528         .x      = -62,
529         .y      =  10,
530         .width  = _BM_LBUTTON_WIDTH,
531         .height = _BM_LBUTTON_HEIGHT,
532     },
533     .bm_fill      = BM_LBUTTON,
534     .bm_icon      = BM_VIDEO,
535     .icon_w       = _BM_LBICON_WIDTH,
536     .icon_h       = _BM_LBICON_HEIGHT,
537     .on_mup       = button_call_video_on_mup,
538     .update       = button_call_video_update,
539     .tooltip_text = {.i18nal = STR_CALL_START_VIDEO },
540 };
541 
button_send_file_on_mup(void)542 static void button_send_file_on_mup(void) {
543     FRIEND *f = flist_get_friend();
544     if (!f) {
545         LOG_ERR("Friend", "Could not get selected friend.");
546         return;
547     }
548 
549     if (f->online) {
550         openfilesend();
551     }
552 }
553 
button_send_file_update(BUTTON * b)554 static void button_send_file_update(BUTTON *b) {
555     FRIEND *f = flist_get_friend();
556     if (!f) {
557         LOG_ERR("Friend", "Could not get selected friend.");
558         return;
559     }
560 
561     if (f->online) {
562         b->disabled = false;
563         button_setcolors_success(b);
564     } else {
565         b->disabled = true;
566         button_setcolors_disabled(b);
567     }
568 }
569 
570 BUTTON button_send_file = {
571     .panel = {
572         .type   = PANEL_BUTTON,
573         .x      =   6,
574         .y      = -46,
575         .width  = _BM_CHAT_BUTTON_WIDTH,
576         .height = _BM_CHAT_BUTTON_HEIGHT,
577     },
578     .bm_fill      = BM_CHAT_BUTTON_LEFT,
579     .bm_icon      = BM_FILE,
580     .icon_w       = _BM_FILE_WIDTH,
581     .icon_h       = _BM_FILE_HEIGHT,
582     .on_mup       = button_send_file_on_mup,
583     .update       = button_send_file_update,
584     .disabled     = true,
585     .tooltip_text = {.i18nal = STR_SEND_FILE },
586 };
587 
588 #include "../screen_grab.h"
button_send_screenshot_on_mup(void)589 static void button_send_screenshot_on_mup(void) {
590     FRIEND *f = flist_get_friend();
591     if (f != NULL && f->online) {
592         utox_screen_grab_desktop(0);
593     }
594 }
595 
button_send_screenshot_update(BUTTON * b)596 static void button_send_screenshot_update(BUTTON *b) {
597     FRIEND *f = flist_get_friend();
598     if (!f) {
599         LOG_ERR("Friend", "Could not get selected friend.");
600         return;
601     }
602 
603     if (f->online) {
604         b->disabled = false;
605         button_setcolors_success(b);
606     } else {
607         b->disabled = true;
608         button_setcolors_disabled(b);
609     }
610 }
611 
612 BUTTON button_send_screenshot = {
613     .panel = {
614         .type   = PANEL_BUTTON,
615         .x      =   8 + _BM_CHAT_BUTTON_WIDTH,
616         .y      = -46,
617         .width  = _BM_CHAT_BUTTON_WIDTH,
618         .height = _BM_CHAT_BUTTON_HEIGHT,
619     },
620     .bm_fill      = BM_CHAT_BUTTON_RIGHT,
621     .bm_icon      = BM_CHAT_BUTTON_OVERLAY_SCREENSHOT,
622     .icon_w       = _BM_CHAT_BUTTON_OVERLAY_WIDTH,
623     .icon_h       = _BM_CHAT_BUTTON_OVERLAY_HEIGHT,
624     .update       = button_send_screenshot_update,
625     .on_mup       = button_send_screenshot_on_mup,
626     .tooltip_text = {.i18nal = STR_SENDSCREENSHOT },
627 };
628 
629 BUTTON button_accept_friend = {
630     .panel = {
631         .type = PANEL_BUTTON,
632         .x = -140,
633         .y = -80,
634         .width = _BM_SBUTTON_WIDTH,
635         .height = _BM_SBUTTON_HEIGHT,
636     },
637     .bm_fill         = BM_SBUTTON,
638     .button_text = {.i18nal = STR_ADD },
639     .update      = button_setcolors_success,
640     .on_mup      = button_accept_friend_on_mup,
641 };
642 
643 BUTTON button_ignore_friend = {
644     .panel = {
645         .type = PANEL_BUTTON,
646         .x = -80,
647         .y = -80,
648         .width = _BM_SBUTTON_WIDTH,
649         .height = _BM_SBUTTON_HEIGHT,
650     },
651     .bm_fill     = BM_SBUTTON,
652     .button_text = {.i18nal = STR_IGNORE},
653     .update     = button_setcolors_danger,
654     .on_mup     = button_ignore_friend_on_mup,
655 };
656 
switchfxn_autoaccept_ft(void)657 static void switchfxn_autoaccept_ft(void) {
658     FRIEND *f = flist_get_friend();
659     if (f) {
660         f->ft_autoaccept = !f->ft_autoaccept;
661         utox_write_metadata(f);
662     }
663 }
664 
665 #include "../ui/switch.h"
666 
667 UISWITCH switch_friend_autoaccept_ft = {
668     .panel = {
669         .type   = PANEL_SWITCH,
670         .x      =  10,
671         .y      = 168,
672         .width  = _BM_SWITCH_WIDTH,
673         .height = _BM_SWITCH_HEIGHT,
674     },
675     .style_outer    = BM_SWITCH,
676     .style_toggle   = BM_SWITCH_TOGGLE,
677     .style_icon_off = BM_NO,
678     .style_icon_on  = BM_YES,
679     .update         = switch_update,
680     .on_mup         = switchfxn_autoaccept_ft,
681     .tooltip_text   = {.i18nal = STR_FRIEND_AUTOACCEPT },
682 };
683 
684 
edit_add_new_contact(EDIT * UNUSED (edit))685 static void edit_add_new_contact(EDIT *UNUSED(edit)) {
686     friend_add(edit_add_new_friend_id.data, edit_add_new_friend_id.length, edit_add_new_friend_msg.data, edit_add_new_friend_msg.length);
687 }
688 
689 static char e_friend_pubkey_str[TOX_PUBLIC_KEY_SIZE * 2];
690 EDIT edit_friend_pubkey = {
691     .panel = {
692         .type   = PANEL_EDIT,
693         .x      =  10,
694         .y      =  88,
695         .width  = -10,
696         .height =  24,
697     },
698     // .length == .data_size, because .data is not \0-terminated
699     .length            = sizeof e_friend_pubkey_str,
700     .data_size         = sizeof e_friend_pubkey_str,
701     .data              = e_friend_pubkey_str,
702     .readonly          = true,
703     .noborder          = false,
704     .select_completely = true,
705 };
706 
707 
edit_friend_alias_onenter(EDIT * UNUSED (edit))708 static void edit_friend_alias_onenter(EDIT *UNUSED(edit)) {
709     FRIEND *f = flist_get_friend();
710     if (!f) {
711         LOG_ERR("Friend", "Could not get selected friend.");
712         return;
713     }
714 
715     friend_set_alias(f, (uint8_t *)edit_friend_alias.data, edit_friend_alias.length);
716 
717     utox_write_metadata(f);
718 }
719 
720 static char e_friend_alias_str[128];
721 EDIT edit_friend_alias = {
722     .panel = {
723         .type   = PANEL_EDIT,
724         .x      =  10,
725         .y      = 138,
726         .width  = -10,
727         .height =  24,
728     },
729     .data_size   = sizeof e_friend_alias_str,
730     .data        = e_friend_alias_str,
731     .onenter     = edit_friend_alias_onenter,
732     .onlosefocus = edit_friend_alias_onenter,
733     .empty_str   = {.plain = STRING_INIT("") }, // set dynamically to the friend's name
734 };
735 
736 
737 
edit_add_new_friend_id_ontab(EDIT * UNUSED (edit))738 static void edit_add_new_friend_id_ontab(EDIT *UNUSED(edit)) {
739     edit_setfocus(&edit_add_new_friend_msg);
740 }
741 
742 static char e_add_new_friend_id_data[TOX_ADDRESS_SIZE * 4];
743 EDIT edit_add_new_friend_id = {
744     .panel = {
745         .type   = PANEL_EDIT,
746         .x      =  10,
747         .y      =  28 + MAIN_TOP,
748         .width  = -10,
749         .height =  24,
750     },
751     .data_size = sizeof e_add_new_friend_id_data,
752     .data      = e_add_new_friend_id_data,
753     .onenter   = edit_add_new_contact,
754     .ontab      = edit_add_new_friend_id_ontab,
755     .onshifttab = edit_add_new_friend_id_ontab,
756 };
757 
758 SCROLLABLE e_add_new_friend_msg_scroll = {
759     .panel = { .type = PANEL_SCROLLABLE, },
760     .d     = 1.0,
761     .color = C_SCROLL,
762 };
763 
edit_add_new_friend_msg_ontab(EDIT * UNUSED (edit))764 static void edit_add_new_friend_msg_ontab(EDIT *UNUSED(edit)) {
765     edit_setfocus(&edit_add_new_friend_id);
766 }
767 
768 static char e_add_new_friend_msg_data[1024];
769 EDIT edit_add_new_friend_msg = {
770     .panel = {
771         .type   = PANEL_EDIT,
772         .x      =  10,
773         .y      =  76 + MAIN_TOP,
774         .width  = -10,
775         .height =  84,
776     },
777     .multiline = 1,
778     .scroll    = &e_add_new_friend_msg_scroll,
779     .data      = e_add_new_friend_msg_data,
780     .data_size = sizeof e_add_new_friend_msg_data,
781     .empty_str = {.i18nal = STR_DEFAULT_FRIEND_REQUEST_MESSAGE },
782     .ontab      = edit_add_new_friend_msg_ontab,
783     .onshifttab = edit_add_new_friend_msg_ontab,
784 };
785 
786 #include "../commands.h"
e_chat_msg_onenter(EDIT * edit)787 static void e_chat_msg_onenter(EDIT *edit) {
788     char *   text   = edit->data;
789     uint16_t length = edit->length;
790 
791     if (length <= 0) {
792         return;
793     }
794 
795     uint16_t command_length = 0; //, argument_length = 0;
796     char *   command = NULL, *argument = NULL;
797 
798     command_length = utox_run_command(text, length, &command, &argument, 1);
799 
800     // TODO: Magic number
801     if (command_length == UINT16_MAX) {
802         edit->length = 0;
803         return;
804     }
805 
806     // LOG_TRACE("Layout Friend", "cmd %u", command_length);
807 
808     bool action = false;
809     if (command_length) {
810         length = length - command_length - 2; /* first / and then the SPACE */
811         text   = argument;
812         if ((command_length == 2) && (!memcmp(command, "me", 2))) {
813             if (argument) {
814                 action = true;
815             } else {
816                 return;
817             }
818         }
819     }
820 
821     if (!text) {
822         return;
823     }
824 
825     FRIEND *f = flist_get_friend();
826     if (f) {
827         /* Display locally */
828         if (action) {
829             message_add_type_action(&f->msg, 1, text, length, 1, 1);
830         } else {
831             message_add_type_text(&f->msg, 1, text, length, 1, 1);
832         }
833     }
834     edit->length      = 0;
835 }
836 
e_chat_msg_onchange(EDIT * UNUSED (edit))837 static void e_chat_msg_onchange(EDIT *UNUSED(edit)) {
838     FRIEND *f = flist_get_friend();
839     if (f) {
840         if (!f->online) {
841             return;
842         }
843 
844         postmessage_toxcore(TOX_SEND_TYPING, f->number, 0, NULL);
845     }
846 }
847 
848 SCROLLABLE e_chat_msg_friend_scroll = {
849     .panel = { .type = PANEL_SCROLLABLE, },
850     .d     = 1.0,
851     .color = C_SCROLL,
852 };
853 
854 static char e_chat_msg_friend_data[65535];
855 EDIT edit_chat_msg_friend = {
856     .panel = {
857         .type   = PANEL_EDIT,
858         .x      =  10 + _BM_CHAT_BUTTON_WIDTH * 2, /* Make space for the left button */
859         .y      = -46,
860         .width  = -64,
861         .height =  40, /* text is 8 high. 8 * 2.5 = 20. */
862     },
863     .data        = e_chat_msg_friend_data,
864     .data_size   = sizeof e_chat_msg_friend_data,
865     .multiline   = true,
866     .onenter     = e_chat_msg_onenter,
867     .onchange    = e_chat_msg_onchange,
868     .scroll      = &e_chat_msg_friend_scroll,
869 };
870 
871 /* Button to send chat message */
button_chat_send_friend_on_mup(void)872 static void button_chat_send_friend_on_mup(void) {
873     // TODO clear the chat bar with a /slash command
874     e_chat_msg_onenter(&edit_chat_msg_friend);
875     // reset focus to the chat window on send to prevent segfault. May break on android.
876     edit_setfocus(&edit_chat_msg_friend);
877 }
878 
button_chat_send_friend_update(BUTTON * b)879 static void button_chat_send_friend_update(BUTTON *b) {
880     FRIEND *f = flist_get_friend();
881     if (f) {
882         if (f->online) {
883             button_setcolors_success(b);
884         } else {
885             button_setcolors_disabled(b);
886         }
887     }
888 }
889 
890 BUTTON button_chat_send_friend = {
891     .panel = {
892         .type   = PANEL_BUTTON,
893         .x      =  -6 - _BM_CHAT_SEND_WIDTH,
894         .y      = -46,
895         .width  = _BM_CHAT_SEND_WIDTH,
896         .height = _BM_CHAT_SEND_HEIGHT,
897     },
898     .bm_fill      = BM_CHAT_SEND,
899     .bm_icon      = BM_CHAT_SEND_OVERLAY,
900     .icon_w       = _BM_CHAT_SEND_OVERLAY_WIDTH,
901     .icon_h       = _BM_CHAT_SEND_OVERLAY_HEIGHT,
902     .on_mup       = button_chat_send_friend_on_mup,
903     .update       = button_chat_send_friend_update,
904     .tooltip_text = {.i18nal = STR_SENDMESSAGE },
905 };
906 
button_confirm_deletion_on_mup(void)907 static void button_confirm_deletion_on_mup(void) {
908     flist_delete_rmouse_item();
909 }
910 
button_deny_deletion_on_mup(void)911 static void button_deny_deletion_on_mup(void) {
912     panel_friend_confirm_deletion.disabled = true;
913     panel_friend_chat.disabled             = false;
914 }
915 
916 BUTTON button_confirm_deletion = {
917     .panel = {
918         .type   = PANEL_BUTTON,
919         .x      = 10,
920         .y      = MAIN_TOP + 40,
921         .width  = _BM_SBUTTON_WIDTH,
922         .height = _BM_SBUTTON_HEIGHT,
923     },
924     .bm_fill      = BM_SBUTTON,
925     .update       = button_setcolors_danger,
926     .tooltip_text = {.i18nal = STR_DELETE},
927     .button_text  = {.i18nal = STR_DELETE},
928     .on_mup       = button_confirm_deletion_on_mup,
929 };
930 
931 BUTTON button_deny_deletion = {
932     .panel = {
933         .type   = PANEL_BUTTON,
934         .x      = 110,
935         .y      = MAIN_TOP + 40,
936         .width  = _BM_SBUTTON_WIDTH,
937         .height = _BM_SBUTTON_HEIGHT,
938     },
939     .bm_fill      = BM_SBUTTON,
940     .update       = button_setcolors_success,
941     .tooltip_text = {.i18nal = STR_KEEP},
942     .button_text  = {.i18nal = STR_KEEP},
943     .on_mup       = button_deny_deletion_on_mup,
944 };
945