1 #include "ui.h"
2 
3 #include "flist.h"
4 #include "inline_video.h"
5 #include "macros.h"
6 #include "messages.h"
7 #include "settings.h"
8 
9 #include "layout/background.h"
10 #include "layout/create.h"
11 #include "layout/friend.h"
12 #include "layout/group.h"
13 #include "layout/notify.h"
14 #include "layout/settings.h"
15 #include "layout/sidebar.h"
16 
17 #include "native/image.h"
18 #include "native/ui.h"
19 
20 #include "ui/button.h"
21 #include "ui/contextmenu.h"
22 #include "ui/draw.h"
23 #include "ui/dropdown.h"
24 #include "ui/edit.h"
25 #include "ui/panel.h"
26 #include "ui/scrollable.h"
27 #include "ui/switch.h"
28 #include "ui/text.h"
29 #include "ui/tooltip.h"
30 
31 struct utox_mouse mouse;
32 
33 uint8_t cursor;
34 bool mdown;
35 
36 char search_data[1024]; // TODO this is NOT where this belongs
37 
38 double ui_scale;
39 
40 /* These remain for legacy reasons, PANEL_MAIN calls these by default when not given it's own function to call */
background_draw(PANEL * UNUSED (p),int UNUSED (x),int UNUSED (y),int UNUSED (width),int UNUSED (height))41 static void background_draw(PANEL *UNUSED(p), int UNUSED(x), int UNUSED(y), int UNUSED(width), int UNUSED(height)) {
42     return;
43 }
44 
background_mmove(PANEL * UNUSED (p),int UNUSED (x),int UNUSED (y),int UNUSED (width),int UNUSED (height),int UNUSED (mx),int UNUSED (my),int UNUSED (dx),int UNUSED (dy))45 static bool background_mmove(PANEL *UNUSED(p), int UNUSED(x), int UNUSED(y), int UNUSED(width), int UNUSED(height),
46                       int UNUSED(mx), int UNUSED(my), int UNUSED(dx), int UNUSED(dy)) {
47     return false;
48 }
49 
background_mdown(PANEL * UNUSED (p))50 static bool background_mdown(PANEL *UNUSED(p)) {
51     return false;
52 }
53 
background_mright(PANEL * UNUSED (p))54 static bool background_mright(PANEL *UNUSED(p)) {
55     return false;
56 }
57 
background_mwheel(PANEL * UNUSED (p),int UNUSED (height),double UNUSED (d),bool UNUSED (smooth))58 static bool background_mwheel(PANEL *UNUSED(p), int UNUSED(height), double UNUSED(d), bool UNUSED(smooth)) {
59     return false;
60 }
61 
background_mup(PANEL * UNUSED (p))62 static bool background_mup(PANEL *UNUSED(p)) {
63     return false;
64 }
65 
background_mleave(PANEL * UNUSED (p))66 static bool background_mleave(PANEL *UNUSED(p)) {
67     return false;
68 }
69 
70 /***** MAYBE_I18NAL_STRING helpers start *****/
71 
maybe_i18nal_string_set_plain(MAYBE_I18NAL_STRING * mis,char * str,uint16_t length)72 void maybe_i18nal_string_set_plain(MAYBE_I18NAL_STRING *mis, char *str, uint16_t length) {
73     mis->i18nal       = UI_STRING_ID_INVALID;
74     mis->plain.length = length;
75     mis->plain.str    = str;
76 }
77 
maybe_i18nal_string_set_i18nal(MAYBE_I18NAL_STRING * mis,UTOX_I18N_STR string_id)78 void maybe_i18nal_string_set_i18nal(MAYBE_I18NAL_STRING *mis, UTOX_I18N_STR string_id) {
79     mis->plain.str    = NULL;
80     mis->plain.length = 0;
81     mis->i18nal       = string_id;
82 }
83 
maybe_i18nal_string_get(MAYBE_I18NAL_STRING * mis)84 STRING *maybe_i18nal_string_get(MAYBE_I18NAL_STRING *mis) {
85     if (mis->plain.str) {
86         return &mis->plain;
87     }
88 
89     return SPTRFORLANG(settings.language, mis->i18nal);
90 }
91 
maybe_i18nal_string_is_valid(MAYBE_I18NAL_STRING * mis)92 bool maybe_i18nal_string_is_valid(MAYBE_I18NAL_STRING *mis) {
93     return (mis->plain.str || ((UI_STRING_ID_INVALID != mis->i18nal) && (mis->i18nal < NUM_STRS)));
94 }
95 
96 /***********************************************************************
97  *                                                                     *
98  * Panel layout size set functions.                                    *
99  *                                                                     *
100  **********************************************************************/
sidepanel_USERBADGE(void)101 static void sidepanel_USERBADGE(void) {
102     // Converting DEFINES to magic because this will be moved to layout/
103     // and will then get a different format/selection
104 }
105 
sidepanel_FLIST(void)106 static void sidepanel_FLIST(void) {
107     scrollbar_flist.panel.y      = 0;
108     // scrollbar_flist.panel.width  = 230; // TODO remove?
109     scrollbar_flist.panel.height = -1;
110 
111     panel_flist.x      = 0;
112     panel_flist.y      = 70;
113     panel_flist.width  = 230; // TODO remove?
114     panel_flist.height = ROSTER_BOTTOM;
115 
116 
117     button_add_new_contact.panel.disabled = true;
118 }
119 
120 
settings_PROFILE(void)121 static void settings_PROFILE(void) {
122     panel_settings_profile.y = 32;
123 
124 }
125 
settings_UI(void)126 static void settings_UI(void) {
127     panel_settings_ui.y            = 32;
128 
129 }
130 
settings_AV(void)131 static void settings_AV(void) {
132     panel_settings_av.y = 32;
133 
134 
135     #ifndef AUDIO_FILTERING
136         const uint16_t start_draw_y = 30;
137         const uint16_t preview_button_pos_y = 245;
138     #else
139         const uint16_t start_draw_y = 60;
140         const uint16_t preview_button_pos_y = 275;
141         CREATE_SWITCH(audio_filtering, 10, 40, _BM_SWITCH_WIDTH, _BM_SWITCH_HEIGHT);
142     #endif
143 
144 
145     const uint16_t draw_y_vect = 30;
146     CREATE_DROPDOWN(audio_in,  10, (start_draw_y + draw_y_vect + 5), 24, 360);
147     CREATE_DROPDOWN(audio_out, 10, (start_draw_y + draw_y_vect + 57), 24, 360);
148     CREATE_EDIT(video_fps,     10, (start_draw_y + draw_y_vect + 110), 360, 24);
149     CREATE_DROPDOWN(video,     10, (start_draw_y + draw_y_vect + 162), 24, 360);
150 
151     CREATE_BUTTON(callpreview,  10, (preview_button_pos_y + 35), _BM_LBUTTON_WIDTH, _BM_LBUTTON_HEIGHT);
152     CREATE_BUTTON(videopreview, 70, (preview_button_pos_y + 35), _BM_LBUTTON_WIDTH, _BM_LBUTTON_HEIGHT);
153 }
154 
settings_NOTIFY(void)155 static void settings_NOTIFY(void) {
156     panel_settings_notifications.y = 32;
157 
158 }
159 
settings_ADV(void)160 static void settings_ADV(void) {
161     panel_settings_adv.y = 32;
162 
163 
164     const int show_nospam_x = 30 + UN_SCALE(MAX(UTOX_STR_WIDTH(SHOW_UI_PASSWORD), UTOX_STR_WIDTH(HIDE_UI_PASSWORD)));
165     CREATE_BUTTON(show_nospam, show_nospam_x, 177, _BM_SBUTTON_WIDTH, _BM_SBUTTON_HEIGHT);
166 
167 
168     const int revert_nospam_x = 30 + UN_SCALE(UTOX_STR_WIDTH(RANDOMIZE_NOSPAM));
169     CREATE_BUTTON(revert_nospam, revert_nospam_x, 265, _BM_SBUTTON_WIDTH, _BM_SBUTTON_HEIGHT);
170 
171 }
172 
ui_set_scale(uint8_t scale)173 void ui_set_scale(uint8_t scale) {
174     if (scale >= 5 && scale <= 25) {
175         ui_scale = scale;
176     } else if (scale != 0) {
177         return ui_set_scale(10);
178     }
179 }
180 
ui_rescale(uint8_t scale)181 void ui_rescale(uint8_t scale) {
182     ui_set_scale(scale);
183 
184     flist_re_scale();
185     setscale_fonts();
186     setfont(FONT_SELF_NAME);
187 
188     /* DEFAULT positions */
189 
190     panel_main.y = 0;
191 
192     scrollbar_settings.panel.y        = 32;  /* TODO magic numbers are bad */
193     scrollbar_settings.content_height = 300; /* TODO magic numbers are bad */
194 
195     panel_settings_master.y        = 0;
196     panel_settings_devices.y       = 32;
197     panel_settings_adv.y           = 32;
198 
199     scrollbar_friend.panel.y      = MAIN_TOP;
200     scrollbar_friend.panel.height = CHAT_BOX_TOP;
201     messages_friend.y             = MAIN_TOP;
202     messages_friend.height        = CHAT_BOX_TOP - 10;
203     messages_friend.width         = -SCROLL_WIDTH;
204 
205     scrollbar_group.panel.y      = MAIN_TOP;
206     scrollbar_group.panel.height = CHAT_BOX_TOP;
207     messages_group.y             = MAIN_TOP;
208     messages_group.height        = CHAT_BOX_TOP;
209     messages_group.width         = -SCROLL_WIDTH;
210 
211     setfont(FONT_SELF_NAME);
212 
213     sidepanel_USERBADGE();
214     sidepanel_FLIST();
215 
216     settings_PROFILE();
217     settings_UI();
218     settings_AV();
219     settings_NOTIFY();
220     settings_ADV();
221 
222     // FIXME for testing, remove
223     CREATE_BUTTON(notify_create, 2, 2, BM_SBUTTON_WIDTH, BM_SBUTTON_HEIGHT);
224     CREATE_BUTTON(notify_one, 0, -50, 40, 50);
225     CREATE_BUTTON(notify_two, 200, -50, 40, 50);
226     CREATE_BUTTON(notify_three, -40, -50, 40, 50);
227 
228     CREATE_BUTTON(move_notify, -40, -40, 40, 40);
229 
230 
231     /* Setting pages */
232     uint32_t settings_x = 4;
233     CREATE_BUTTON(settings_sub_profile,         settings_x, 0, 12, 28);
234     settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(PROFILE_BUTTON));
235 
236 #ifdef ENABLE_MULTIDEVICE
237     CREATE_BUTTON(settings_sub_devices,         settings_x, 0, 12, 28);
238     settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(DEVICES_BUTTON));
239 #endif
240 
241     CREATE_BUTTON(settings_sub_ui,              settings_x, 0, 12, 28);
242     settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(USER_INTERFACE_BUTTON));
243 
244     CREATE_BUTTON(settings_sub_av,              settings_x, 0, 12, 28);
245     settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(AUDIO_VIDEO_BUTTON));
246 
247     CREATE_BUTTON(settings_sub_notifications,   settings_x, 0, 12, 28);
248     settings_x += 20 + UN_SCALE(UTOX_STR_WIDTH(NOTIFICATIONS_BUTTON));
249 
250     CREATE_BUTTON(settings_sub_adv,             settings_x, 0, 12, 28);
251 
252 
253     /* Devices */
254     CREATE_BUTTON(add_new_device_to_self, -10 - BM_SBUTTON_WIDTH, 28, BM_SBUTTON_WIDTH, BM_SBUTTON_HEIGHT);
255 
256     CREATE_EDIT(add_new_device_to_self, 10, 27, 0 - UTOX_STR_WIDTH(ADD) - BM_SBUTTON_WIDTH, 24);
257 
258 
259 
260     setfont(FONT_TEXT);
261 
262 
263     CREATE_EDIT(chat_msg_group, 6, -46, -10 - BM_CHAT_SEND_WIDTH, 40);
264 
265 
266     setscale();
267 }
268 
269 /* Use the preprocessor to build function prototypes for all user interactions
270  * These are functions that are (must be) defined elsewhere. The preprocessor in this case creates the prototypes that
271  * will then be used by panel_draw_core to call the correct function
272 */
273 #define MAKE_FUNC(ret, x, ...)                                                                                          \
274     static ret (*x##func[])(void *p, ##__VA_ARGS__) = {                                                                 \
275         (void *)background_##x, (void *)messages_##x, (void *)inline_video_##x, (void *)flist_##x,  (void *)button_##x, \
276         (void *)switch_##x,     (void *)dropdown_##x, (void *)edit_##x,         (void *)scroll_##x,                     \
277     };
278 
279 MAKE_FUNC(void, draw, int x, int y, int width, int height);
280 MAKE_FUNC(bool, mmove, int x, int y, int width, int height, int mx, int my, int dx, int dy);
281 MAKE_FUNC(bool, mdown);
282 MAKE_FUNC(bool, mright);
283 MAKE_FUNC(bool, mwheel, int height, double d);
284 MAKE_FUNC(bool, mup);
285 MAKE_FUNC(bool, mleave);
286 
287 #undef MAKE_FUNC
288 
289 /* Use the preprocessor to add code to adjust the x,y cords for panels or sub panels.
290  * If neg value place x/y from the right/bottom of panel.
291  *
292  * change the relative
293  *
294  * if w/h <0 use parent panel width (maybe?)    */
295 #define FIX_XY_CORDS_FOR_SUBPANELS()                                       \
296     {                                                                      \
297         int relx = (p->x < 0) ? width + SCALE(p->x) : SCALE(p->x);                       \
298         int rely = (p->y < 0) ? height + SCALE(p->y) : SCALE(p->y);                      \
299         x += relx;                                                         \
300         y += rely;                                                         \
301         width  = (p->width <= 0) ? width + SCALE(p->width) - relx : SCALE(p->width);     \
302         height = (p->height <= 0) ? height + SCALE(p->height) - rely : SCALE(p->height); \
303     }
304 
panel_update(PANEL * p,int x,int y,int width,int height)305 static void panel_update(PANEL *p, int x, int y, int width, int height) {
306     FIX_XY_CORDS_FOR_SUBPANELS();
307 
308     switch (p->type) {
309         case PANEL_NONE: {
310             if (p == &panel_settings_devices) {
311                 #ifdef ENABLE_MULTIDEVICE
312                 devices_update_ui();
313                 #endif
314             }
315             break;
316         }
317 
318         case PANEL_MESSAGES: {
319             if (p->object) {
320                 MESSAGES *m = p->object;
321                 m->width    = width;
322                 messages_updateheight(m, width);
323             }
324             break;
325         }
326 
327         default: {
328             break;
329         }
330     }
331 
332     PANEL **pp = p->child;
333     if (pp) {
334         if (p->update) {
335             p->update(width, height, ui_scale);
336         }
337 
338         PANEL *subp;
339         while ((subp = *pp++)) {
340             panel_update(subp, x, y, width, height);
341         }
342     }
343 }
344 
draw_avatar_image(NATIVE_IMAGE * image,int x,int y,uint32_t width,uint32_t height,uint32_t targetwidth,uint32_t targetheight)345 void draw_avatar_image(NATIVE_IMAGE *image, int x, int y, uint32_t width, uint32_t height, uint32_t targetwidth,
346                        uint32_t targetheight)
347 {
348     /* get smallest of width or height */
349     const double scale = (width > height) ? (double)targetheight / height : (double)targetwidth / width;
350 
351     image_set_scale(image, scale);
352     image_set_filter(image, FILTER_BILINEAR);
353 
354     /* set position to show the middle of the image in the center  */
355     const int xpos = (int)((double)width * scale / 2 - (double)targetwidth / 2);
356     const int ypos = (int)((double)height * scale / 2 - (double)targetheight / 2);
357 
358     draw_image(image, x, y, targetwidth, targetheight, xpos, ypos);
359 
360     image_set_scale(image, 1.0);
361     image_set_filter(image, FILTER_NEAREST);
362 }
363 
ui_size(int width,int height)364 void ui_size(int width, int height) {
365     panel_update(&panel_root, 0, 0, width, height);
366     tooltip_reset();
367 
368     panel_side_bar.disabled = false;
369     panel_main.x = panel_flist.width;
370 
371     if (settings.magic_flist_enabled) {
372         if (width <= panel_flist.width * 2 || height > width) {
373             panel_side_bar.disabled = true;
374             panel_main.x = 0;
375         }
376     }
377 }
378 
ui_mouseleave(void)379 void ui_mouseleave(void) {
380     panel_mleave(&panel_root);
381     tooltip_reset();
382     redraw();
383 }
384 
panel_draw_core(PANEL * p,int x,int y,int width,int height)385 static void panel_draw_core(PANEL *p, int x, int y, int width, int height) {
386     FIX_XY_CORDS_FOR_SUBPANELS();
387 
388     if (p->content_scroll) {
389         pushclip(x, y, width, height);
390         y -= scroll_gety(p->content_scroll, height);
391     }
392 
393     if (p->type) {
394         drawfunc[p->type - 1](p, x, y, width, height);
395     } else {
396         if (p->drawfunc) {
397             p->drawfunc(x, y, width, height);
398         }
399     }
400 
401     PANEL **pp = p->child;
402     if (pp) {
403         PANEL *subp;
404         while ((subp = *pp++)) {
405             if (!subp->disabled) {
406                 panel_draw_core(subp, x, y, width, height);
407             }
408         }
409     }
410 
411     if (p->content_scroll) {
412         popclip();
413     }
414 }
415 
panel_draw(PANEL * p,int x,int y,int width,int height)416 void panel_draw(PANEL *p, int x, int y, int width, int height) {
417     FIX_XY_CORDS_FOR_SUBPANELS();
418 
419     panel_draw_core(p, x, y, width, height);
420 
421     // popclip();
422 
423     dropdown_drawactive();
424     contextmenu_draw();
425     tooltip_draw();
426 
427     enddraw(x, y, width, height);
428 }
429 
panel_mmove(PANEL * p,int x,int y,int width,int height,int mx,int my,int dx,int dy)430 bool panel_mmove(PANEL *p, int x, int y, int width, int height, int mx, int my, int dx, int dy) {
431     if (p == &panel_root) {
432         mouse.x = mx;
433         mouse.y = my;
434     }
435 
436     mx -= (p->x < 0) ? width + SCALE(p->x) : SCALE(p->x);
437     my -= (p->y < 0) ? height + SCALE(p->y) : SCALE(p->y);
438     FIX_XY_CORDS_FOR_SUBPANELS();
439 
440     int mmy = my;
441 
442     if (p->content_scroll) {
443         const int scroll_y = scroll_gety(p->content_scroll, height);
444         if (my < 0) {
445             mmy = -1;
446         } else if (my >= height) {
447             mmy = 1024 * 1024 * 1024; // large value
448         } else {
449             mmy = my + scroll_y;
450         }
451         y -= scroll_y;
452         my += scroll_y;
453     }
454 
455     bool draw = p->type ? mmovefunc[p->type - 1](p, x, y, width, height, mx, mmy, dx, dy) : false;
456     // Has to be called before children mmove
457     if (p == &panel_root) {
458         draw |= tooltip_mmove();
459     }
460     PANEL **pp = p->child;
461     if (pp) {
462         PANEL *subp;
463         while ((subp = *pp++)) {
464             if (!subp->disabled) {
465                 draw |= panel_mmove(subp, x, y, width, height, mx, my, dx, dy);
466             }
467         }
468     }
469 
470     if (p == &panel_root) {
471         draw |= contextmenu_mmove(mx, my, dx, dy);
472         if (draw) {
473             redraw();
474         }
475     }
476 
477     return draw;
478 }
479 
panel_mdown_sub(PANEL * p)480 static bool panel_mdown_sub(PANEL *p) {
481     if (p->type && mdownfunc[p->type - 1](p)) {
482         return true;
483     }
484 
485     PANEL **pp = p->child;
486     if (pp) {
487         PANEL *subp;
488         while ((subp = *pp++)) {
489             if (!subp->disabled) {
490                 if (panel_mdown_sub(subp)) {
491                     return true;
492                 }
493             }
494         }
495     }
496 
497     return false;
498 }
499 
panel_mdown(PANEL * p)500 void panel_mdown(PANEL *p) {
501     if (contextmenu_mdown() || tooltip_mdown()) {
502         redraw();
503         return;
504     }
505 
506     bool draw = edit_active();
507 
508     PANEL **pp = p->child;
509 
510     if (pp) {
511         PANEL *subp;
512         while ((subp = *pp++)) {
513             if (!subp->disabled) {
514                 draw |= panel_mdown_sub(subp);
515             }
516         }
517     }
518 
519     if (draw) {
520         redraw();
521     }
522 }
523 
panel_dclick(PANEL * p,bool triclick)524 bool panel_dclick(PANEL *p, bool triclick) {
525     bool draw = false;
526     if (p->type == PANEL_EDIT) {
527         draw = edit_dclick((EDIT *)p, triclick);
528     } else if (p->type == PANEL_MESSAGES) {
529         draw = messages_dclick(p, triclick);
530     }
531 
532     PANEL **pp = p->child;
533     if (pp) {
534         PANEL *subp;
535         while ((subp = *pp++)) {
536             if (!subp->disabled) {
537                 draw = panel_dclick(subp, triclick);
538                 if (draw) {
539                     break;
540                 }
541             }
542         }
543     }
544 
545     if (draw && p == &panel_root) {
546         redraw();
547     }
548 
549     return draw;
550 }
551 
panel_mright(PANEL * p)552 bool panel_mright(PANEL *p) {
553     bool draw = p->type ? mrightfunc[p->type - 1](p) : false;
554     PANEL **pp = p->child;
555     if (pp) {
556         PANEL *subp;
557         while ((subp = *pp++)) {
558             if (!subp->disabled) {
559                 draw |= panel_mright(subp);
560             }
561         }
562     }
563 
564     if (draw && p == &panel_root) {
565         redraw();
566     }
567 
568     return draw;
569 }
570 
panel_mwheel(PANEL * p,int x,int y,int width,int height,double d,bool smooth)571 bool panel_mwheel(PANEL *p, int x, int y, int width, int height, double d, bool smooth) {
572     FIX_XY_CORDS_FOR_SUBPANELS();
573 
574     bool draw = p->type ? mwheelfunc[p->type - 1](p, height, d) : false;
575     PANEL **pp = p->child;
576     if (pp) {
577         PANEL *subp;
578         while ((subp = *pp++)) {
579             if (!subp->disabled) {
580                 draw |= panel_mwheel(subp, x, y, width, height, d, smooth);
581             }
582         }
583     }
584 
585     if (draw && p == &panel_root) {
586         redraw();
587     }
588 
589     return draw;
590 }
591 
panel_mup(PANEL * p)592  bool panel_mup(PANEL *p) {
593     if (p == &panel_root && contextmenu_mup()) {
594         tooltip_mup();
595         redraw();
596         return true;
597     }
598 
599     bool draw = p->type ? mupfunc[p->type - 1](p) : false;
600     PANEL **pp = p->child;
601     if (pp) {
602         PANEL *subp;
603         while ((subp = *pp++)) {
604             if (!subp->disabled) {
605                 draw |= panel_mup(subp);
606             }
607         }
608     }
609 
610     if (p == &panel_root) {
611         tooltip_mup();
612         if (draw) {
613             redraw();
614         }
615     }
616 
617     return draw;
618 }
619 
panel_mleave(PANEL * p)620 bool panel_mleave(PANEL *p) {
621     bool    draw = p->type ? mleavefunc[p->type - 1](p) : false;
622     PANEL **pp   = p->child;
623     if (pp) {
624         PANEL *subp;
625         while ((subp = *pp++)) {
626             if (!subp->disabled) {
627                 draw |= panel_mleave(subp);
628             }
629         }
630     }
631 
632     if (p == &panel_root) {
633         draw |= contextmenu_mleave();
634         if (draw) {
635             redraw();
636         }
637     }
638 
639     return draw;
640 }
641