1 /***********************************************************************
2  Freeciv - Copyright (C) 2006 - The Freeciv Project
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 /* SDL2 */
19 #ifdef SDL2_PLAIN_INCLUDE
20 #include <SDL.h>
21 #else  /* SDL2_PLAIN_INCLUDE */
22 #include <SDL2/SDL.h>
23 #endif /* SDL2_PLAIN_INCLUDE */
24 
25 /* utility */
26 #include "log.h"
27 
28 /* gui-sdl2 */
29 #include "colors.h"
30 #include "graphics.h"
31 #include "gui_id.h"
32 #include "gui_tilespec.h"
33 #include "mapview.h"
34 #include "themespec.h"
35 
36 #include "widget.h"
37 #include "widget_p.h"
38 
39 struct UP_DOWN {
40   struct widget *pBegin;
41   struct widget *pEnd;
42   struct widget *pBeginWidgetLIST;
43   struct widget *pEndWidgetLIST;
44   struct ScrollBar *pVscroll;
45   float old_y;
46   int step;
47   int prev_x;
48   int prev_y;
49   int offset; /* number of pixels the mouse is away from the slider origin */
50 };
51 
52 #define UpperAdd(pNew_Widget, pAdd_Dock)	\
53 do {						\
54   pNew_Widget->prev = pAdd_Dock;		\
55   pNew_Widget->next = pAdd_Dock->next;		\
56   if (pAdd_Dock->next) {			\
57     pAdd_Dock->next->prev = pNew_Widget;	\
58   }						\
59   pAdd_Dock->next = pNew_Widget;		\
60 } while(FALSE)
61 
62 static int (*baseclass_redraw)(struct widget *pwidget);
63 
64 /* =================================================== */
65 /* ===================== VSCROOLBAR ================== */
66 /* =================================================== */
67 
68 /**************************************************************************
69   Create background image for vscrollbars
70   then return pointer to this image.
71 
72   Graphic is taken from pVert_theme surface and blit to new created image.
73 
74   height depend of 'High' parameter.
75 **************************************************************************/
create_vertical_surface(SDL_Surface * pVert_theme,enum widget_state state,Uint16 High)76 static SDL_Surface *create_vertical_surface(SDL_Surface *pVert_theme,
77                                             enum widget_state state, Uint16 High)
78 {
79   SDL_Surface *pVerSurf = NULL;
80   SDL_Rect src, des;
81   Uint16 i;
82   Uint16 start_y;
83   Uint16 tile_count_midd;
84   Uint8 tile_len_end;
85   Uint8 tile_len_midd;
86 
87   tile_len_end = pVert_theme->h / 16;
88 
89   start_y = 0 + state * (pVert_theme->h / 4);
90 
91   tile_len_midd = pVert_theme->h / 4 - tile_len_end * 2;
92 
93   tile_count_midd = (High - tile_len_end * 2) / tile_len_midd;
94 
95   /* correction */
96   if (tile_len_midd * tile_count_midd + tile_len_end * 2 < High) {
97     tile_count_midd++;
98   }
99 
100   if (!tile_count_midd) {
101     pVerSurf = create_surf(pVert_theme->w, tile_len_end * 2, SDL_SWSURFACE);
102   } else {
103     pVerSurf = create_surf(pVert_theme->w, High, SDL_SWSURFACE);
104   }
105 
106   src.x = 0;
107   src.y = start_y;
108   src.w = pVert_theme->w;
109   src.h = tile_len_end;
110   alphablit(pVert_theme, &src, pVerSurf, NULL, 255);
111 
112   src.y = start_y + tile_len_end;
113   src.h = tile_len_midd;
114 
115   des.x = 0;
116 
117   for (i = 0; i < tile_count_midd; i++) {
118     des.y = tile_len_end + i * tile_len_midd;
119     alphablit(pVert_theme, &src, pVerSurf, &des, 255);
120   }
121 
122   src.y = start_y + tile_len_end + tile_len_midd;
123   src.h = tile_len_end;
124   des.y = pVerSurf->h - tile_len_end;
125   alphablit(pVert_theme, &src, pVerSurf, &des, 255);
126 
127   return pVerSurf;
128 }
129 
130 /**************************************************************************
131   Blit vertical scrollbar gfx to surface its on.
132 **************************************************************************/
redraw_vert(struct widget * pVert)133 static int redraw_vert(struct widget *pVert)
134 {
135   int ret;
136   SDL_Rect dest = pVert->size;
137   SDL_Surface *pVert_Surf;
138 
139   ret = (*baseclass_redraw)(pVert);
140   if (ret != 0) {
141     return ret;
142   }
143 
144   pVert_Surf = create_vertical_surface(pVert->theme,
145                                        get_wstate(pVert),
146                                        pVert->size.h);
147   ret =
148       blit_entire_src(pVert_Surf, pVert->dst->surface, dest.x, dest.y);
149 
150   FREESURFACE(pVert_Surf);
151 
152   return ret;
153 }
154 
155 /**************************************************************************
156   Create ( malloc ) VScrollBar widget structure.
157 
158   Theme graphic is taken from pVert_theme surface;
159 
160   This function determinate future size of VScrollBar
161   ( width = 'pVert_theme->w', height = 'height' ) and
162   save this in: pWidget->size rectangle ( SDL_Rect )
163 
164   Return pointer to created widget.
165 **************************************************************************/
create_vertical(SDL_Surface * pVert_theme,struct gui_layer * pDest,Uint16 height,Uint32 flags)166 struct widget *create_vertical(SDL_Surface *pVert_theme, struct gui_layer *pDest,
167                                Uint16 height, Uint32 flags)
168 {
169   struct widget *pVer = widget_new();
170 
171   pVer->theme = pVert_theme;
172   pVer->size.w = pVert_theme->w;
173   pVer->size.h = height;
174   set_wflag(pVer, (WF_FREE_STRING | WF_FREE_GFX | flags));
175   set_wstate(pVer, FC_WS_DISABLED);
176   set_wtype(pVer, WT_VSCROLLBAR);
177   pVer->mod = KMOD_NONE;
178   pVer->dst = pDest;
179 
180   baseclass_redraw = pVer->redraw;
181   pVer->redraw = redraw_vert;
182 
183   return pVer;
184 }
185 
186 /**************************************************************************
187   Draw vertical scrollbar.
188 **************************************************************************/
draw_vert(struct widget * pVert,Sint16 x,Sint16 y)189 int draw_vert(struct widget *pVert, Sint16 x, Sint16 y)
190 {
191   pVert->size.x = x;
192   pVert->size.y = y;
193   pVert->gfx = crop_rect_from_surface(pVert->dst->surface, &pVert->size);
194 
195   return redraw_vert(pVert);
196 }
197 
198 /* =================================================== */
199 /* ===================== HSCROOLBAR ================== */
200 /* =================================================== */
201 
202 /**************************************************************************
203   Create background image for hscrollbars
204   then return pointer to this image.
205 
206   Graphic is taken from pHoriz_theme surface and blit to new created image.
207 
208   height depend of 'Width' parameter.
209 
210   Type of image depend of "state" parameter.
211     state = 0 - normal
212     state = 1 - selected
213     state = 2 - pressed
214     state = 3 - disabled
215 **************************************************************************/
create_horizontal_surface(SDL_Surface * pHoriz_theme,Uint8 state,Uint16 Width)216 static SDL_Surface *create_horizontal_surface(SDL_Surface *pHoriz_theme,
217                                               Uint8 state, Uint16 Width)
218 {
219   SDL_Surface *pHorSurf = NULL;
220   SDL_Rect src, des;
221   Uint16 i;
222   Uint16 start_x;
223   Uint16 tile_count_midd;
224   Uint8 tile_len_end;
225   Uint8 tile_len_midd;
226 
227   tile_len_end = pHoriz_theme->w / 16;
228 
229   start_x = 0 + state * (pHoriz_theme->w / 4);
230 
231   tile_len_midd = pHoriz_theme->w / 4 - tile_len_end * 2;
232 
233   tile_count_midd = (Width - tile_len_end * 2) / tile_len_midd;
234 
235   /* correction */
236   if (tile_len_midd * tile_count_midd + tile_len_end * 2 < Width) {
237     tile_count_midd++;
238   }
239 
240   if (!tile_count_midd) {
241     pHorSurf = create_surf(tile_len_end * 2, pHoriz_theme->h, SDL_SWSURFACE);
242   } else {
243     pHorSurf = create_surf(Width, pHoriz_theme->h, SDL_SWSURFACE);
244   }
245 
246   src.y = 0;
247   src.x = start_x;
248   src.h = pHoriz_theme->h;
249   src.w = tile_len_end;
250   alphablit(pHoriz_theme, &src, pHorSurf, NULL, 255);
251 
252   src.x = start_x + tile_len_end;
253   src.w = tile_len_midd;
254 
255   des.y = 0;
256 
257   for (i = 0; i < tile_count_midd; i++) {
258     des.x = tile_len_end + i * tile_len_midd;
259     alphablit(pHoriz_theme, &src, pHorSurf, &des, 255);
260   }
261 
262   src.x = start_x + tile_len_end + tile_len_midd;
263   src.w = tile_len_end;
264   des.x = pHorSurf->w - tile_len_end;
265   alphablit(pHoriz_theme, &src, pHorSurf, &des, 255);
266 
267   return pHorSurf;
268 }
269 
270 /**************************************************************************
271   Blit horizontal scrollbar gfx to surface its on.
272 **************************************************************************/
redraw_horiz(struct widget * pHoriz)273 static int redraw_horiz(struct widget *pHoriz)
274 {
275   int ret;
276   SDL_Rect dest = pHoriz->size;
277   SDL_Surface *pHoriz_Surf;
278 
279   ret = (*baseclass_redraw)(pHoriz);
280   if (ret != 0) {
281     return ret;
282   }
283 
284   pHoriz_Surf = create_horizontal_surface(pHoriz->theme,
285                                           get_wstate(pHoriz),
286                                           pHoriz->size.w);
287   ret = blit_entire_src(pHoriz_Surf, pHoriz->dst->surface, dest.x, dest.y);
288 
289   FREESURFACE(pHoriz_Surf);
290 
291   return ret;
292 }
293 
294 /**************************************************************************
295   Create ( malloc ) HScrollBar widget structure.
296 
297   Theme graphic is taken from pHoriz_theme surface;
298 
299   This function determinate future size of HScrollBar
300   ( width = 'width', height = 'pHoriz_theme->h' ) and
301   save this in: pWidget->size rectangle ( SDL_Rect )
302 
303   Return pointer to created widget.
304 **************************************************************************/
create_horizontal(SDL_Surface * pHoriz_theme,struct gui_layer * pDest,Uint16 width,Uint32 flags)305 struct widget *create_horizontal(SDL_Surface *pHoriz_theme,
306                                  struct gui_layer *pDest,
307                                  Uint16 width, Uint32 flags)
308 {
309   struct widget *pHor = widget_new();
310 
311   pHor->theme = pHoriz_theme;
312   pHor->size.w = width;
313   pHor->size.h = pHoriz_theme->h;
314   set_wflag(pHor, WF_FREE_STRING | flags);
315   set_wstate(pHor, FC_WS_DISABLED);
316   set_wtype(pHor, WT_HSCROLLBAR);
317   pHor->mod = KMOD_NONE;
318   pHor->dst = pDest;
319 
320   baseclass_redraw = pHor->redraw;
321   pHor->redraw = redraw_horiz;
322 
323   return pHor;
324 }
325 
326 /**************************************************************************
327   Draw horizontal scrollbar.
328 **************************************************************************/
draw_horiz(struct widget * pHoriz,Sint16 x,Sint16 y)329 int draw_horiz(struct widget *pHoriz, Sint16 x, Sint16 y)
330 {
331   pHoriz->size.x = x;
332   pHoriz->size.y = y;
333   pHoriz->gfx = crop_rect_from_surface(pHoriz->dst->surface, &pHoriz->size);
334 
335   return redraw_horiz(pHoriz);
336 }
337 
338 /* =================================================== */
339 /* =====================            ================== */
340 /* =================================================== */
341 
342 /**************************************************************************
343   Get step of the scrollbar.
344 **************************************************************************/
get_step(struct ScrollBar * pScroll)345 static int get_step(struct ScrollBar *pScroll)
346 {
347   float step = pScroll->max - pScroll->min;
348 
349   step *= (float) (1.0 - (float) (pScroll->active * pScroll->step) /
350                    (float)pScroll->count);
351   step /= (float)(pScroll->count - pScroll->active * pScroll->step);
352   step *= (float)pScroll->step;
353   step++;
354 
355   return (int)step;
356 }
357 
358 /**************************************************************************
359   Get current active position of the scrollbar.
360 **************************************************************************/
get_position(struct ADVANCED_DLG * pDlg)361 static int get_position(struct ADVANCED_DLG *pDlg)
362 {
363   struct widget *pBuf = pDlg->pActiveWidgetList;
364   int count = pDlg->pScroll->active * pDlg->pScroll->step - 1;
365   int step = get_step(pDlg->pScroll);
366 
367   /* find last seen widget */
368   while (count) {
369     if (pBuf == pDlg->pBeginActiveWidgetList) {
370       break;
371     }
372     count--;
373     pBuf = pBuf->prev;
374   }
375 
376   count = 0;
377   if (pBuf != pDlg->pBeginActiveWidgetList) {
378     do {
379       count++;
380       pBuf = pBuf->prev;
381     } while (pBuf != pDlg->pBeginActiveWidgetList);
382   }
383 
384   if (pDlg->pScroll->pScrollBar) {
385     return pDlg->pScroll->max - pDlg->pScroll->pScrollBar->size.h -
386       count * (float)step / pDlg->pScroll->step;
387   } else {
388     return pDlg->pScroll->max - count * (float)step / pDlg->pScroll->step;
389   }
390 }
391 
392 /**************************************************************************
393   			Vertical ScrollBar
394 **************************************************************************/
395 
396 static struct widget *up_scroll_widget_list(struct ScrollBar *pVscroll,
397                                             struct widget *pBeginActiveWidgetLIST,
398                                             struct widget *pBeginWidgetLIST,
399                                             struct widget *pEndWidgetLIST);
400 
401 static struct widget *down_scroll_widget_list(struct ScrollBar *pVscroll,
402                                               struct widget *pBeginActiveWidgetLIST,
403                                               struct widget *pBeginWidgetLIST,
404                                               struct widget *pEndWidgetLIST);
405 
406 static struct widget *vertic_scroll_widget_list(struct ScrollBar *pVscroll,
407                                                 struct widget *pBeginActiveWidgetLIST,
408                                                 struct widget *pBeginWidgetLIST,
409                                                 struct widget *pEndWidgetLIST);
410 
411 /**************************************************************************
412   User interacted with up button of advanced dialog.
413 **************************************************************************/
std_up_advanced_dlg_callback(struct widget * pWidget)414 static int std_up_advanced_dlg_callback(struct widget *pWidget)
415 {
416   if (PRESSED_EVENT(Main.event)) {
417     struct ADVANCED_DLG *pDlg = pWidget->private_data.adv_dlg;
418     struct widget *pBegin = up_scroll_widget_list(
419                           pDlg->pScroll,
420                           pDlg->pActiveWidgetList,
421                           pDlg->pBeginActiveWidgetList,
422                           pDlg->pEndActiveWidgetList);
423 
424     if (pBegin) {
425       pDlg->pActiveWidgetList = pBegin;
426     }
427 
428     unselect_widget_action();
429     selected_widget = pWidget;
430     set_wstate(pWidget, FC_WS_SELECTED);
431     widget_redraw(pWidget);
432     widget_flush(pWidget);
433   }
434 
435   return -1;
436 }
437 
438 /**************************************************************************
439   User interacted with down button of advanced dialog.
440 **************************************************************************/
std_down_advanced_dlg_callback(struct widget * pWidget)441 static int std_down_advanced_dlg_callback(struct widget *pWidget)
442 {
443   if (PRESSED_EVENT(Main.event)) {
444     struct ADVANCED_DLG *pDlg = pWidget->private_data.adv_dlg;
445     struct widget *pBegin = down_scroll_widget_list(
446                               pDlg->pScroll,
447                               pDlg->pActiveWidgetList,
448                               pDlg->pBeginActiveWidgetList,
449                               pDlg->pEndActiveWidgetList);
450 
451     if (pBegin) {
452       pDlg->pActiveWidgetList = pBegin;
453     }
454 
455     unselect_widget_action();
456     selected_widget = pWidget;
457     set_wstate(pWidget, FC_WS_SELECTED);
458     widget_redraw(pWidget);
459     widget_flush(pWidget);
460   }
461 
462   return -1;
463 }
464 
465 /**************************************************************************
466   FIXME : fix main funct : vertic_scroll_widget_list(...)
467 **************************************************************************/
std_vscroll_advanced_dlg_callback(struct widget * pScrollBar)468 static int std_vscroll_advanced_dlg_callback(struct widget *pScrollBar)
469 {
470   if (PRESSED_EVENT(Main.event)) {
471     struct ADVANCED_DLG *pDlg = pScrollBar->private_data.adv_dlg;
472     struct widget *pBegin = vertic_scroll_widget_list(
473                               pDlg->pScroll,
474                               pDlg->pActiveWidgetList,
475                               pDlg->pBeginActiveWidgetList,
476                               pDlg->pEndActiveWidgetList);
477 
478     if (pBegin) {
479       pDlg->pActiveWidgetList = pBegin;
480     }
481 
482     unselect_widget_action();
483     set_wstate(pScrollBar, FC_WS_SELECTED);
484     selected_widget = pScrollBar;
485     redraw_vert(pScrollBar);
486     widget_flush(pScrollBar);
487   }
488 
489   return -1;
490 }
491 
492 /**************************************************************************
493   Create a new vertical scrollbar to active widgets list.
494 **************************************************************************/
create_vertical_scrollbar(struct ADVANCED_DLG * pDlg,Uint8 step,Uint8 active,bool create_scrollbar,bool create_buttons)495 Uint32 create_vertical_scrollbar(struct ADVANCED_DLG *pDlg,
496                                  Uint8 step, Uint8 active,
497                                  bool create_scrollbar, bool create_buttons)
498 {
499   Uint16 count = 0;
500   struct widget *pBuf = NULL, *pWindow = NULL;
501 
502   fc_assert_ret_val(pDlg != NULL, 0);
503 
504   pWindow = pDlg->pEndWidgetList;
505 
506   if (!pDlg->pScroll) {
507     pDlg->pScroll = fc_calloc(1, sizeof(struct ScrollBar));
508 
509     pBuf = pDlg->pEndActiveWidgetList;
510     while (pBuf && (pBuf != pDlg->pBeginActiveWidgetList->prev)) {
511       pBuf = pBuf->prev;
512       count++;
513     }
514 
515     pDlg->pScroll->count = count;
516   }
517 
518   pDlg->pScroll->active = active;
519   pDlg->pScroll->step = step;
520 
521   if (create_buttons) {
522     /* create up button */
523     pBuf = create_themeicon_button(current_theme->UP_Icon, pWindow->dst,
524                                    NULL, WF_RESTORE_BACKGROUND);
525 
526     pBuf->ID = ID_BUTTON;
527     pBuf->private_data.adv_dlg = pDlg;
528     pBuf->action = std_up_advanced_dlg_callback;
529     set_wstate(pBuf, FC_WS_NORMAL);
530 
531     pDlg->pScroll->pUp_Left_Button = pBuf;
532     DownAdd(pBuf, pDlg->pBeginWidgetList);
533     pDlg->pBeginWidgetList = pBuf;
534 
535     count = pBuf->size.w;
536 
537     /* create down button */
538     pBuf = create_themeicon_button(current_theme->DOWN_Icon, pWindow->dst,
539                                    NULL, WF_RESTORE_BACKGROUND);
540 
541     pBuf->ID = ID_BUTTON;
542     pBuf->private_data.adv_dlg = pDlg;
543     pBuf->action = std_down_advanced_dlg_callback;
544     set_wstate(pBuf, FC_WS_NORMAL);
545 
546     pDlg->pScroll->pDown_Right_Button = pBuf;
547     DownAdd(pBuf, pDlg->pBeginWidgetList);
548     pDlg->pBeginWidgetList = pBuf;
549   }
550 
551   if (create_scrollbar) {
552     /* create vscrollbar */
553     pBuf = create_vertical(current_theme->Vertic, pWindow->dst,
554                            adj_size(10), WF_RESTORE_BACKGROUND);
555 
556     pBuf->ID = ID_SCROLLBAR;
557     pBuf->private_data.adv_dlg = pDlg;
558     pBuf->action = std_vscroll_advanced_dlg_callback;
559     set_wstate(pBuf, FC_WS_NORMAL);
560 
561     pDlg->pScroll->pScrollBar = pBuf;
562     DownAdd(pBuf, pDlg->pBeginWidgetList);
563     pDlg->pBeginWidgetList = pBuf;
564 
565     if (!count) {
566       count = pBuf->size.w;
567     }
568   }
569 
570   return count;
571 }
572 
573 /**************************************************************************
574   Setup area for the vertical scrollbar.
575 **************************************************************************/
setup_vertical_scrollbar_area(struct ScrollBar * pScroll,Sint16 start_x,Sint16 start_y,Uint16 height,bool swap_start_x)576 void setup_vertical_scrollbar_area(struct ScrollBar *pScroll,
577                                    Sint16 start_x, Sint16 start_y,
578                                    Uint16 height, bool swap_start_x)
579 {
580   bool buttons_exist;
581 
582   fc_assert_ret(pScroll != NULL);
583 
584   buttons_exist = (pScroll->pDown_Right_Button && pScroll->pUp_Left_Button);
585 
586   if (buttons_exist) {
587     /* up */
588     pScroll->pUp_Left_Button->size.y = start_y;
589     if (swap_start_x) {
590       pScroll->pUp_Left_Button->size.x = start_x -
591         pScroll->pUp_Left_Button->size.w;
592     } else {
593       pScroll->pUp_Left_Button->size.x = start_x;
594     }
595     pScroll->min = start_y + pScroll->pUp_Left_Button->size.h;
596     /* -------------------------- */
597     /* down */
598     pScroll->pDown_Right_Button->size.y = start_y + height -
599       pScroll->pDown_Right_Button->size.h;
600     if (swap_start_x) {
601       pScroll->pDown_Right_Button->size.x = start_x -
602         pScroll->pDown_Right_Button->size.w;
603     } else {
604       pScroll->pDown_Right_Button->size.x = start_x;
605     }
606     pScroll->max = pScroll->pDown_Right_Button->size.y;
607   }
608   /* --------------- */
609   /* scrollbar */
610   if (pScroll->pScrollBar) {
611     if (swap_start_x) {
612       pScroll->pScrollBar->size.x = start_x - pScroll->pScrollBar->size.w + 2;
613     } else {
614       pScroll->pScrollBar->size.x = start_x;
615     }
616 
617     if (buttons_exist) {
618       pScroll->pScrollBar->size.y = start_y +
619         pScroll->pUp_Left_Button->size.h;
620       if (pScroll->count > pScroll->active * pScroll->step) {
621         pScroll->pScrollBar->size.h = scrollbar_size(pScroll);
622       } else {
623         pScroll->pScrollBar->size.h = pScroll->max - pScroll->min;
624       }
625     } else {
626       pScroll->pScrollBar->size.y = start_y;
627       pScroll->pScrollBar->size.h = height;
628       pScroll->min = start_y;
629       pScroll->max = start_y + height;
630     }
631   }
632 }
633 
634 /* =================================================== */
635 /* ============ Vertical Scroll Group List =========== */
636 /* =================================================== */
637 
638 /**************************************************************************
639   scroll pointers on list.
640   dir == directions: up == -1, down == 1.
641 **************************************************************************/
vertical_scroll_widget_list(struct widget * pActiveWidgetLIST,struct widget * pBeginWidgetLIST,struct widget * pEndWidgetLIST,int active,int step,int dir)642 static struct widget *vertical_scroll_widget_list(struct widget *pActiveWidgetLIST,
643                                                   struct widget *pBeginWidgetLIST,
644                                                   struct widget *pEndWidgetLIST,
645                                                   int active, int step, int dir)
646 {
647   struct widget *pBegin = pActiveWidgetLIST;
648   struct widget *pBuf = pActiveWidgetLIST;
649   struct widget *pTmp = NULL;
650   int count = active; /* row */
651   int count_step = step; /* col */
652 
653   if (dir < 0) {
654     /* up */
655     bool real = TRUE;
656 
657     if (pBuf != pEndWidgetLIST) {
658       /*
659        move pointers to positions and unhide scrolled widgets
660        B = pBuf - new top
661        T = pTmp - current top == pActiveWidgetLIST
662        [B] [ ] [ ]
663        -----------
664        [T] [ ] [ ]
665        [ ] [ ] [ ]
666        -----------
667        [ ] [ ] [ ]
668     */
669       pTmp = pBuf; /* now pBuf == pActiveWidgetLIST == current Top */
670       while (count_step > 0) {
671       	pBuf = pBuf->next;
672 	clear_wflag(pBuf, WF_HIDDEN);
673 	count_step--;
674       }
675       count_step = step;
676 
677       /* setup new ActiveWidget pointer */
678       pBegin = pBuf;
679 
680       /*
681        scroll pointers up
682        B = pBuf
683        T = pTmp
684        [B0] [B1] [B2]
685        -----------
686        [T0] [T1] [T2]   => B position = T position
687        [T3] [T4] [T5]
688        -----------
689        [  ] [  ] [  ]
690 
691        start from B0 and go down list
692        B0 = T0, B1 = T1, B2 = T2
693        T0 = T3, T1 = T4, T2 = T5
694        etc...
695     */
696 
697       /* pBuf == pBegin == new top widget */
698 
699       while (count > 0) {
700         if (real) {
701           pBuf->size.x = pTmp->size.x;
702           pBuf->size.y = pTmp->size.y;
703           pBuf->gfx = pTmp->gfx;
704 
705           if ((pBuf->size.w != pTmp->size.w) || (pBuf->size.h != pTmp->size.h)) {
706             widget_undraw(pTmp);
707             widget_mark_dirty(pTmp);
708             if (get_wflags(pBuf) & WF_RESTORE_BACKGROUND) {
709               refresh_widget_background(pBuf);
710             }
711           }
712 
713           pTmp->gfx = NULL;
714 
715           if (count == 1) {
716             set_wflag(pTmp, WF_HIDDEN);
717           }
718           if (pTmp == pBeginWidgetLIST) {
719             real = FALSE;
720           }
721           pTmp = pTmp->prev;
722         } else {
723           /*
724             unsymmetric list support.
725             This is big problem because we can't take position from unexisting
726             list memeber. We must put here some hypothetical positions
727 
728             [B0] [B1] [B2]
729             --------------
730             [T0] [T1]
731 
732           */
733           if (active > 1) {
734             /* this works well if active > 1 but is buggy when active == 1 */
735             pBuf->size.y += pBuf->size.h;
736           } else {
737             /* this works well if active == 1 but may be broken if "next"
738                element has other "y" position */
739             pBuf->size.y = pBuf->next->size.y;
740           }
741           pBuf->gfx = NULL;
742         }
743 
744         pBuf = pBuf->prev;
745         count_step--;
746         if (!count_step) {
747           count_step = step;
748           count--;
749         }
750       }
751     }
752   } else {
753     /* down */
754     count = active * step; /* row * col */
755 
756     /*
757        find end
758        B = pBuf
759        A - start (pBuf == pActiveWidgetLIST)
760        [ ] [ ] [ ]
761        -----------
762        [A] [ ] [ ]
763        [ ] [ ] [ ]
764        -----------
765        [B] [ ] [ ]
766     */
767     while (count && pBuf != pBeginWidgetLIST->prev) {
768       pBuf = pBuf->prev;
769       count--;
770     }
771 
772     if (!count && pBuf != pBeginWidgetLIST->prev) {
773       /*
774        move pointers to positions and unhide scrolled widgets
775        B = pBuf
776        T = pTmp
777        A - start (pActiveWidgetLIST)
778        [ ] [ ] [ ]
779        -----------
780        [A] [ ] [ ]
781        [ ] [ ] [T]
782        -----------
783        [ ] [ ] [B]
784     */
785       pTmp = pBuf->next;
786       count_step = step - 1;
787       while (count_step && pBuf != pBeginWidgetLIST) {
788         clear_wflag(pBuf, WF_HIDDEN);
789         pBuf = pBuf->prev;
790         count_step--;
791       }
792       clear_wflag(pBuf, WF_HIDDEN);
793 
794       /*
795         Unsymmetric list support.
796         correct pTmp and undraw empty fields
797         B = pBuf
798         T = pTmp
799         A - start (pActiveWidgetLIST)
800         [ ] [ ] [ ]
801         -----------
802         [A] [ ] [ ]
803         [ ] [T] [U]  <- undraw U
804         -----------
805         [ ] [B]
806       */
807       count = count_step;
808       while (count) {
809         /* hack - clear area under unexisting list members */
810         widget_undraw(pTmp);
811         widget_mark_dirty(pTmp);
812         FREESURFACE(pTmp->gfx);
813         if (active == 1) {
814           set_wflag(pTmp, WF_HIDDEN);
815         }
816         pTmp = pTmp->next;
817         count--;
818       }
819 
820       /* reset counters */
821       count = active;
822       if (count_step) {
823         count_step = step - count_step;
824       } else {
825         count_step = step;
826       }
827 
828       /*
829         scroll pointers down
830         B = pBuf
831         T = pTmp
832         [  ] [  ] [  ]
833         -----------
834         [  ] [  ] [  ]
835         [T2] [T1] [T0]   => B position = T position
836         -----------
837         [B2] [B1] [B0]
838       */
839       while (count) {
840         pBuf->size.x = pTmp->size.x;
841         pBuf->size.y = pTmp->size.y;
842         pBuf->gfx = pTmp->gfx;
843 
844         if ((pBuf->size.w != pTmp->size.w) || (pBuf->size.h != pTmp->size.h)) {
845           widget_undraw(pTmp);
846           widget_mark_dirty(pTmp);
847           if (get_wflags(pBuf) & WF_RESTORE_BACKGROUND) {
848             refresh_widget_background(pBuf);
849           }
850         }
851 
852         pTmp->gfx = NULL;
853 
854         if (count == 1) {
855           set_wflag(pTmp, WF_HIDDEN);
856         }
857 
858         pTmp = pTmp->next;
859         pBuf = pBuf->next;
860         count_step--;
861         if (!count_step) {
862           count_step = step;
863           count--;
864         }
865       }
866       /* setup new ActiveWidget pointer */
867       pBegin = pBuf->prev;
868     }
869   }
870 
871   return pBegin;
872 }
873 
874 /**************************************************************************
875   Callback for the scroll-down event loop.
876 **************************************************************************/
inside_scroll_down_loop(void * pData)877 static void inside_scroll_down_loop(void *pData)
878 {
879   struct UP_DOWN *pDown = (struct UP_DOWN *)pData;
880 
881   if (pDown->pEnd != pDown->pBeginWidgetLIST) {
882     if (pDown->pVscroll->pScrollBar
883         && pDown->pVscroll->pScrollBar->size.y <=
884         pDown->pVscroll->max - pDown->pVscroll->pScrollBar->size.h) {
885 
886       /* draw bcgd */
887       widget_undraw(pDown->pVscroll->pScrollBar);
888       widget_mark_dirty(pDown->pVscroll->pScrollBar);
889 
890       if (pDown->pVscroll->pScrollBar->size.y + pDown->step >
891           pDown->pVscroll->max - pDown->pVscroll->pScrollBar->size.h) {
892         pDown->pVscroll->pScrollBar->size.y =
893           pDown->pVscroll->max - pDown->pVscroll->pScrollBar->size.h;
894       } else {
895         pDown->pVscroll->pScrollBar->size.y += pDown->step;
896       }
897     }
898 
899     pDown->pBegin = vertical_scroll_widget_list(pDown->pBegin,
900                   pDown->pBeginWidgetLIST, pDown->pEndWidgetLIST,
901                   pDown->pVscroll->active, pDown->pVscroll->step, 1);
902 
903     pDown->pEnd = pDown->pEnd->prev;
904 
905     redraw_group(pDown->pBeginWidgetLIST, pDown->pEndWidgetLIST, TRUE);
906 
907     if (pDown->pVscroll->pScrollBar) {
908       /* redraw scrollbar */
909       if (get_wflags(pDown->pVscroll->pScrollBar) & WF_RESTORE_BACKGROUND) {
910         refresh_widget_background(pDown->pVscroll->pScrollBar);
911       }
912       redraw_vert(pDown->pVscroll->pScrollBar);
913 
914       widget_mark_dirty(pDown->pVscroll->pScrollBar);
915     }
916 
917     flush_dirty();
918   }
919 }
920 
921 /**************************************************************************
922   Callback for the scroll-up event loop.
923 **************************************************************************/
inside_scroll_up_loop(void * pData)924 static void inside_scroll_up_loop(void *pData)
925 {
926   struct UP_DOWN *pUp = (struct UP_DOWN *)pData;
927 
928   if (pUp && pUp->pBegin != pUp->pEndWidgetLIST) {
929 
930     if (pUp->pVscroll->pScrollBar
931         && (pUp->pVscroll->pScrollBar->size.y >= pUp->pVscroll->min)) {
932 
933       /* draw bcgd */
934       widget_undraw(pUp->pVscroll->pScrollBar);
935       widget_mark_dirty(pUp->pVscroll->pScrollBar);
936 
937       if (((pUp->pVscroll->pScrollBar->size.y - pUp->step) < pUp->pVscroll->min)) {
938         pUp->pVscroll->pScrollBar->size.y = pUp->pVscroll->min;
939       } else {
940         pUp->pVscroll->pScrollBar->size.y -= pUp->step;
941       }
942     }
943 
944     pUp->pBegin = vertical_scroll_widget_list(pUp->pBegin,
945                         pUp->pBeginWidgetLIST, pUp->pEndWidgetLIST,
946                         pUp->pVscroll->active, pUp->pVscroll->step, -1);
947 
948     redraw_group(pUp->pBeginWidgetLIST, pUp->pEndWidgetLIST, TRUE);
949 
950     if (pUp->pVscroll->pScrollBar) {
951       /* redraw scroolbar */
952       if (get_wflags(pUp->pVscroll->pScrollBar) & WF_RESTORE_BACKGROUND) {
953         refresh_widget_background(pUp->pVscroll->pScrollBar);
954       }
955       redraw_vert(pUp->pVscroll->pScrollBar);
956       widget_mark_dirty(pUp->pVscroll->pScrollBar);
957     }
958 
959     flush_dirty();
960   }
961 }
962 
963 /**************************************************************************
964   Handle mouse motion events of the vertical scrollbar event loop.
965 **************************************************************************/
scroll_mouse_motion_handler(SDL_MouseMotionEvent * pMotionEvent,void * pData)966 static Uint16 scroll_mouse_motion_handler(SDL_MouseMotionEvent *pMotionEvent,
967                                           void *pData)
968 {
969   struct UP_DOWN *pMotion = (struct UP_DOWN *)pData;
970   int yrel;
971   int y;
972   int normalized_y;
973   int net_slider_area;
974   int net_count;
975   float scroll_step;
976 
977   yrel = pMotionEvent->y - pMotion->prev_y;
978   pMotion->prev_x = pMotionEvent->x;
979   pMotion->prev_y = pMotionEvent->y;
980 
981   y = pMotionEvent->y - pMotion->pVscroll->pScrollBar->dst->dest_rect.y;
982 
983   normalized_y = (y - pMotion->offset);
984 
985   net_slider_area = (pMotion->pVscroll->max - pMotion->pVscroll->min - pMotion->pVscroll->pScrollBar->size.h);
986   net_count = round((float)pMotion->pVscroll->count / pMotion->pVscroll->step) - pMotion->pVscroll->active + 1;
987   scroll_step = (float)net_slider_area / net_count;
988 
989   if ((yrel != 0)
990       && ((normalized_y >= pMotion->pVscroll->min)
991           || ((normalized_y < pMotion->pVscroll->min)
992               && (pMotion->pVscroll->pScrollBar->size.y > pMotion->pVscroll->min)))
993       && ((normalized_y <= pMotion->pVscroll->max - pMotion->pVscroll->pScrollBar->size.h)
994           || ((normalized_y > pMotion->pVscroll->max)
995               && (pMotion->pVscroll->pScrollBar->size.y < (pMotion->pVscroll->max - pMotion->pVscroll->pScrollBar->size.h)))) ) {
996     int count;
997 
998     /* draw bcgd */
999     widget_undraw(pMotion->pVscroll->pScrollBar);
1000     widget_mark_dirty(pMotion->pVscroll->pScrollBar);
1001 
1002     if ((pMotion->pVscroll->pScrollBar->size.y + yrel) >
1003         (pMotion->pVscroll->max - pMotion->pVscroll->pScrollBar->size.h)) {
1004 
1005       pMotion->pVscroll->pScrollBar->size.y =
1006         (pMotion->pVscroll->max - pMotion->pVscroll->pScrollBar->size.h);
1007 
1008     } else if ((pMotion->pVscroll->pScrollBar->size.y + yrel) < pMotion->pVscroll->min) {
1009       pMotion->pVscroll->pScrollBar->size.y = pMotion->pVscroll->min;
1010     } else {
1011       pMotion->pVscroll->pScrollBar->size.y += yrel;
1012     }
1013 
1014     count = round((pMotion->pVscroll->pScrollBar->size.y - pMotion->old_y) / scroll_step);
1015 
1016     if (count != 0) {
1017       int i = count;
1018 
1019       while (i != 0) {
1020         pMotion->pBegin = vertical_scroll_widget_list(pMotion->pBegin,
1021                                                       pMotion->pBeginWidgetLIST,
1022                                                       pMotion->pEndWidgetLIST,
1023                                                       pMotion->pVscroll->active,
1024                                                       pMotion->pVscroll->step, i);
1025         if (i > 0) {
1026           i--;
1027         } else {
1028           i++;
1029         }
1030       }	/* while (i != 0) */
1031 
1032       pMotion->old_y = pMotion->pVscroll->min +
1033         ((round((pMotion->old_y - pMotion->pVscroll->min) / scroll_step) + count) * scroll_step);
1034 
1035       redraw_group(pMotion->pBeginWidgetLIST, pMotion->pEndWidgetLIST, TRUE);
1036     }
1037 
1038     /* redraw slider */
1039     if (get_wflags(pMotion->pVscroll->pScrollBar) & WF_RESTORE_BACKGROUND) {
1040       refresh_widget_background(pMotion->pVscroll->pScrollBar);
1041     }
1042     redraw_vert(pMotion->pVscroll->pScrollBar);
1043     widget_mark_dirty(pMotion->pVscroll->pScrollBar);
1044 
1045     flush_dirty();
1046   }
1047 
1048   return ID_ERROR;
1049 }
1050 
1051 /**************************************************************************
1052   Callback for scrollbar event loops' mouse up events.
1053 **************************************************************************/
scroll_mouse_button_up(SDL_MouseButtonEvent * pButtonEvent,void * pData)1054 static Uint16 scroll_mouse_button_up(SDL_MouseButtonEvent *pButtonEvent,
1055                                      void *pData)
1056 {
1057   return (Uint16)ID_SCROLLBAR;
1058 }
1059 
1060 /**************************************************************************
1061   Scroll widgets down.
1062 **************************************************************************/
down_scroll_widget_list(struct ScrollBar * pVscroll,struct widget * pBeginActiveWidgetLIST,struct widget * pBeginWidgetLIST,struct widget * pEndWidgetLIST)1063 static struct widget *down_scroll_widget_list(struct ScrollBar *pVscroll,
1064                                               struct widget *pBeginActiveWidgetLIST,
1065                                               struct widget *pBeginWidgetLIST,
1066                                               struct widget *pEndWidgetLIST)
1067 {
1068   struct UP_DOWN pDown;
1069   struct widget *pBegin = pBeginActiveWidgetLIST;
1070   int step = pVscroll->active * pVscroll->step - 1;
1071 
1072   while (step--) {
1073     pBegin = pBegin->prev;
1074   }
1075 
1076   pDown.step = get_step(pVscroll);
1077   pDown.pBegin = pBeginActiveWidgetLIST;
1078   pDown.pEnd = pBegin;
1079   pDown.pBeginWidgetLIST = pBeginWidgetLIST;
1080   pDown.pEndWidgetLIST = pEndWidgetLIST;
1081   pDown.pVscroll = pVscroll;
1082 
1083   gui_event_loop((void *)&pDown, inside_scroll_down_loop, NULL, NULL, NULL,
1084                  NULL, NULL, NULL, NULL, scroll_mouse_button_up, NULL);
1085 
1086   return pDown.pBegin;
1087 }
1088 
1089 /**************************************************************************
1090   Scroll widgets up.
1091 **************************************************************************/
up_scroll_widget_list(struct ScrollBar * pVscroll,struct widget * pBeginActiveWidgetLIST,struct widget * pBeginWidgetLIST,struct widget * pEndWidgetLIST)1092 static struct widget *up_scroll_widget_list(struct ScrollBar *pVscroll,
1093                                             struct widget *pBeginActiveWidgetLIST,
1094                                             struct widget *pBeginWidgetLIST,
1095                                             struct widget *pEndWidgetLIST)
1096 {
1097   struct UP_DOWN pUp;
1098 
1099   pUp.step = get_step(pVscroll);
1100   pUp.pBegin = pBeginActiveWidgetLIST;
1101   pUp.pBeginWidgetLIST = pBeginWidgetLIST;
1102   pUp.pEndWidgetLIST = pEndWidgetLIST;
1103   pUp.pVscroll = pVscroll;
1104 
1105   gui_event_loop((void *)&pUp, inside_scroll_up_loop, NULL, NULL, NULL,
1106                  NULL, NULL, NULL, NULL, scroll_mouse_button_up, NULL);
1107 
1108   return pUp.pBegin;
1109 }
1110 
1111 /**************************************************************************
1112   Scroll vertical widget list with the mouse movement.
1113 **************************************************************************/
vertic_scroll_widget_list(struct ScrollBar * pVscroll,struct widget * pBeginActiveWidgetLIST,struct widget * pBeginWidgetLIST,struct widget * pEndWidgetLIST)1114 static struct widget *vertic_scroll_widget_list(struct ScrollBar *pVscroll,
1115                                                 struct widget *pBeginActiveWidgetLIST,
1116                                                 struct widget *pBeginWidgetLIST,
1117                                                 struct widget *pEndWidgetLIST)
1118 {
1119   struct UP_DOWN pMotion;
1120 
1121   pMotion.step = get_step(pVscroll);
1122   pMotion.pBegin = pBeginActiveWidgetLIST;
1123   pMotion.pBeginWidgetLIST = pBeginWidgetLIST;
1124   pMotion.pEndWidgetLIST = pEndWidgetLIST;
1125   pMotion.pVscroll = pVscroll;
1126   pMotion.old_y = pVscroll->pScrollBar->size.y;
1127   SDL_GetMouseState(&pMotion.prev_x, &pMotion.prev_y);
1128   pMotion.offset = pMotion.prev_y - pVscroll->pScrollBar->dst->dest_rect.y - pVscroll->pScrollBar->size.y;
1129 
1130   MOVE_STEP_X = 0;
1131   MOVE_STEP_Y = 3;
1132   /* Filter mouse motion events */
1133   SDL_SetEventFilter(FilterMouseMotionEvents, NULL);
1134   gui_event_loop((void *)&pMotion, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1135                  scroll_mouse_button_up, scroll_mouse_motion_handler);
1136   /* Turn off Filter mouse motion events */
1137   SDL_SetEventFilter(NULL, NULL);
1138   MOVE_STEP_X = DEFAULT_MOVE_STEP;
1139   MOVE_STEP_Y = DEFAULT_MOVE_STEP;
1140 
1141   return pMotion.pBegin;
1142 }
1143 
1144 /* ==================================================================== */
1145 
1146 /**************************************************************************
1147   Add new widget to scrolled list and set draw position of all changed widgets.
1148   dir :
1149     TRUE - upper add => pAdd_Dock->next = pNew_Widget.
1150     FALSE - down add => pAdd_Dock->prev = pNew_Widget.
1151   start_x, start_y - positions of first seen widget (pActiveWidgetList).
1152   pDlg->pScroll ( scrollbar ) must exist.
1153   It isn't full secure to multi widget list.
1154 **************************************************************************/
add_widget_to_vertical_scroll_widget_list(struct ADVANCED_DLG * pDlg,struct widget * pNew_Widget,struct widget * pAdd_Dock,bool dir,Sint16 start_x,Sint16 start_y)1155 bool add_widget_to_vertical_scroll_widget_list(struct ADVANCED_DLG *pDlg,
1156                                                struct widget *pNew_Widget,
1157                                                struct widget *pAdd_Dock,
1158                                                bool dir,
1159                                                Sint16 start_x, Sint16 start_y)
1160 {
1161   struct widget *pBuf = NULL;
1162   struct widget *pEnd = NULL, *pOld_End = NULL;
1163   int count = 0;
1164   bool last = FALSE, seen = TRUE;
1165 
1166   fc_assert_ret_val(pNew_Widget != NULL, FALSE);
1167   fc_assert_ret_val(pDlg != NULL, FALSE);
1168   fc_assert_ret_val(pDlg->pScroll != NULL, FALSE);
1169 
1170   if (!pAdd_Dock) {
1171     pAdd_Dock = pDlg->pBeginWidgetList; /* last item */
1172   }
1173 
1174   pDlg->pScroll->count++;
1175 
1176   if (pDlg->pScroll->count > (pDlg->pScroll->active * pDlg->pScroll->step)) {
1177     /* -> scrollbar needed */
1178 
1179     if (pDlg->pActiveWidgetList) {
1180       /* -> scrollbar is already visible */
1181       int i = 0;
1182 
1183       /* find last active widget */
1184       pOld_End = pAdd_Dock;
1185       while (pOld_End != pDlg->pActiveWidgetList) {
1186         pOld_End = pOld_End->next;
1187         i++;
1188         if (pOld_End == pDlg->pEndActiveWidgetList) {
1189           /* implies (pOld_End == pDlg->pActiveWidgetList)? */
1190           seen = FALSE;
1191           break;
1192         }
1193       }
1194 
1195       if (seen) {
1196         count = (pDlg->pScroll->active * pDlg->pScroll->step) - 1;
1197         if (i > count) {
1198           seen = FALSE;
1199         } else {
1200           while (count > 0) {
1201             pOld_End = pOld_End->prev;
1202             count--;
1203           }
1204           if (pOld_End == pAdd_Dock) {
1205             last = TRUE;
1206           }
1207         }
1208       }
1209 
1210     } else {
1211       last = TRUE;
1212       pDlg->pActiveWidgetList = pDlg->pEndActiveWidgetList;
1213       show_scrollbar(pDlg->pScroll);
1214     }
1215   }
1216 
1217   count = 0;
1218 
1219   /* add Pointer to list */
1220   if (dir) {
1221     /* upper add */
1222     UpperAdd(pNew_Widget, pAdd_Dock);
1223 
1224     if (pAdd_Dock == pDlg->pEndWidgetList) {
1225       pDlg->pEndWidgetList = pNew_Widget;
1226     }
1227     if (pAdd_Dock == pDlg->pEndActiveWidgetList) {
1228       pDlg->pEndActiveWidgetList = pNew_Widget;
1229     }
1230     if (pAdd_Dock == pDlg->pActiveWidgetList) {
1231       pDlg->pActiveWidgetList = pNew_Widget;
1232     }
1233   } else {
1234     /* down add */
1235     DownAdd(pNew_Widget, pAdd_Dock);
1236 
1237     if (pAdd_Dock == pDlg->pBeginWidgetList) {
1238       pDlg->pBeginWidgetList = pNew_Widget;
1239     }
1240 
1241     if (pAdd_Dock == pDlg->pBeginActiveWidgetList) {
1242       pDlg->pBeginActiveWidgetList = pNew_Widget;
1243     }
1244   }
1245 
1246   /* setup draw positions */
1247   if (seen) {
1248     if (!pDlg->pBeginActiveWidgetList) {
1249       /* first element ( active list empty ) */
1250       fc_assert_msg(FALSE == dir, "Forbided List Operation");
1251       pNew_Widget->size.x = start_x;
1252       pNew_Widget->size.y = start_y;
1253       pDlg->pBeginActiveWidgetList = pNew_Widget;
1254       pDlg->pEndActiveWidgetList = pNew_Widget;
1255       if (!pDlg->pBeginWidgetList) {
1256         pDlg->pBeginWidgetList = pNew_Widget;
1257         pDlg->pEndWidgetList = pNew_Widget;
1258       }
1259     } else { /* there are some elements on local active list */
1260       if (last) {
1261         /* We add to last seen position */
1262         if (dir) {
1263           /* only swap pAdd_Dock with pNew_Widget on last seen positions */
1264           pNew_Widget->size.x = pAdd_Dock->size.x;
1265           pNew_Widget->size.y = pAdd_Dock->size.y;
1266           pNew_Widget->gfx = pAdd_Dock->gfx;
1267           pAdd_Dock->gfx = NULL;
1268           set_wflag(pAdd_Dock, WF_HIDDEN);
1269         } else {
1270           /* reposition all widgets */
1271           pBuf = pNew_Widget;
1272           do {
1273             pBuf->size.x = pBuf->next->size.x;
1274             pBuf->size.y = pBuf->next->size.y;
1275             pBuf->gfx = pBuf->next->gfx;
1276             pBuf->next->gfx = NULL;
1277             pBuf = pBuf->next;
1278           } while (pBuf != pDlg->pActiveWidgetList);
1279           pBuf->gfx = NULL;
1280           set_wflag(pBuf, WF_HIDDEN);
1281 	  pDlg->pActiveWidgetList = pDlg->pActiveWidgetList->prev;
1282         }
1283       } else { /* !last */
1284         pBuf = pNew_Widget;
1285         /* find last seen widget */
1286         if (pDlg->pActiveWidgetList) {
1287           pEnd = pDlg->pActiveWidgetList;
1288           count = pDlg->pScroll->active * pDlg->pScroll->step - 1;
1289           while (count && pEnd != pDlg->pBeginActiveWidgetList) {
1290             pEnd = pEnd->prev;
1291             count--;
1292           }
1293         }
1294         while (pBuf) {
1295           if (pBuf == pDlg->pBeginActiveWidgetList) {
1296             struct widget *pTmp = pBuf;
1297 
1298             count = pDlg->pScroll->step;
1299             while (count) {
1300               pTmp = pTmp->next;
1301               count--;
1302             }
1303             pBuf->size.x = pTmp->size.x;
1304             pBuf->size.y = pTmp->size.y + pTmp->size.h;
1305             /* break when last active widget or last seen widget */
1306             break;
1307           } else {
1308             pBuf->size.x = pBuf->prev->size.x;
1309             pBuf->size.y = pBuf->prev->size.y;
1310             pBuf->gfx = pBuf->prev->gfx;
1311             pBuf->prev->gfx = NULL;
1312             if (pBuf == pEnd) {
1313               break;
1314             }
1315           }
1316           pBuf = pBuf->prev;
1317         }
1318         if (pOld_End && pBuf->prev == pOld_End) {
1319           set_wflag(pOld_End, WF_HIDDEN);
1320         }
1321       } /* !last */
1322     } /* pDlg->pBeginActiveWidgetList */
1323   } else { /* !seen */
1324     set_wflag(pNew_Widget, WF_HIDDEN);
1325   }
1326 
1327   if (pDlg->pActiveWidgetList && pDlg->pScroll->pScrollBar) {
1328     widget_undraw(pDlg->pScroll->pScrollBar);
1329     widget_mark_dirty(pDlg->pScroll->pScrollBar);
1330 
1331     pDlg->pScroll->pScrollBar->size.h = scrollbar_size(pDlg->pScroll);
1332     if (last) {
1333       pDlg->pScroll->pScrollBar->size.y = get_position(pDlg);
1334     }
1335     if (get_wflags(pDlg->pScroll->pScrollBar) & WF_RESTORE_BACKGROUND) {
1336       refresh_widget_background(pDlg->pScroll->pScrollBar);
1337     }
1338     if (!seen) {
1339       redraw_vert(pDlg->pScroll->pScrollBar);
1340     }
1341   }
1342 
1343   return last;
1344 }
1345 
1346 /**************************************************************************
1347   Delete widget from scrolled list and set draw position of all changed
1348   widgets.
1349   Don't free pDlg and pDlg->pScroll (if exist)
1350   It is full secure for multi widget list case.
1351 **************************************************************************/
del_widget_from_vertical_scroll_widget_list(struct ADVANCED_DLG * pDlg,struct widget * pWidget)1352 bool del_widget_from_vertical_scroll_widget_list(struct ADVANCED_DLG *pDlg,
1353                                                  struct widget *pWidget)
1354 {
1355   int count = 0;
1356   struct widget *pBuf = pWidget;
1357 
1358   fc_assert_ret_val(pWidget != NULL, FALSE);
1359   fc_assert_ret_val(pDlg != NULL, FALSE);
1360 
1361   /* if begin == end -> size = 1 */
1362   if (pDlg->pBeginActiveWidgetList == pDlg->pEndActiveWidgetList) {
1363 
1364     if (pDlg->pScroll) {
1365       pDlg->pScroll->count = 0;
1366     }
1367 
1368     if (pDlg->pBeginActiveWidgetList == pDlg->pBeginWidgetList) {
1369       pDlg->pBeginWidgetList = pDlg->pBeginWidgetList->next;
1370     }
1371 
1372     if (pDlg->pEndActiveWidgetList == pDlg->pEndWidgetList) {
1373       pDlg->pEndWidgetList = pDlg->pEndWidgetList->prev;
1374     }
1375 
1376     pDlg->pBeginActiveWidgetList = NULL;
1377     pDlg->pActiveWidgetList = NULL;
1378     pDlg->pEndActiveWidgetList = NULL;
1379 
1380     widget_undraw(pWidget);
1381     widget_mark_dirty(pWidget);
1382     del_widget_from_gui_list(pWidget);
1383     return FALSE;
1384   }
1385 
1386   if (pDlg->pScroll && pDlg->pActiveWidgetList) {
1387     /* scrollbar exist and active, start mod. from last seen label */
1388     struct widget *pLast;
1389     bool widget_found = FALSE;
1390 
1391     /* this is always true because no-scrolbar case (active*step < count)
1392        will be supported in other part of code (see "else" part) */
1393     count = pDlg->pScroll->active * pDlg->pScroll->step;
1394 
1395     /* find last */
1396     pBuf = pDlg->pActiveWidgetList;
1397     while (count > 0) {
1398       pBuf = pBuf->prev;
1399       count--;
1400     }
1401     if (!pBuf) {
1402       pLast = pDlg->pBeginActiveWidgetList;
1403     } else {
1404       pLast = pBuf->next;
1405     }
1406 
1407     if (pLast == pDlg->pBeginActiveWidgetList) {
1408       if (pDlg->pScroll->step == 1) {
1409         pDlg->pActiveWidgetList = pDlg->pActiveWidgetList->next;
1410         clear_wflag(pDlg->pActiveWidgetList, WF_HIDDEN);
1411 
1412         /* look for the widget in the non-visible part */
1413         pBuf = pDlg->pEndActiveWidgetList;
1414         while (pBuf != pDlg->pActiveWidgetList) {
1415           if (pBuf == pWidget) {
1416             widget_found = TRUE;
1417             pBuf = pDlg->pActiveWidgetList;
1418             break;
1419           }
1420           pBuf = pBuf->prev;
1421         }
1422 
1423         /* if we haven't found it yet, look in the visible part and update the
1424          * positions of the other widgets */
1425         if (!widget_found) {
1426           while (pBuf != pWidget) {
1427             pBuf->gfx = pBuf->prev->gfx;
1428             pBuf->prev->gfx = NULL;
1429             pBuf->size.x = pBuf->prev->size.x;
1430             pBuf->size.y = pBuf->prev->size.y;
1431             pBuf = pBuf->prev;
1432           }
1433         }
1434       } else {
1435 	pBuf = pLast;
1436 	/* undraw last widget */
1437         widget_undraw(pBuf);
1438         widget_mark_dirty(pBuf);
1439         FREESURFACE(pBuf->gfx);
1440 	goto STD;
1441       }
1442     } else {
1443       clear_wflag(pBuf, WF_HIDDEN);
1444 STD:  while (pBuf != pWidget) {
1445         pBuf->gfx = pBuf->next->gfx;
1446         pBuf->next->gfx = NULL;
1447         pBuf->size.x = pBuf->next->size.x;
1448         pBuf->size.y = pBuf->next->size.y;
1449         pBuf = pBuf->next;
1450       }
1451     }
1452 
1453     if ((pDlg->pScroll->count - 1) <= (pDlg->pScroll->active * pDlg->pScroll->step)) {
1454       /* scrollbar not needed anymore */
1455       hide_scrollbar(pDlg->pScroll);
1456       pDlg->pActiveWidgetList = NULL;
1457     }
1458     pDlg->pScroll->count--;
1459 
1460     if (pDlg->pActiveWidgetList) {
1461       if (pDlg->pScroll->pScrollBar) {
1462         widget_undraw(pDlg->pScroll->pScrollBar);
1463         pDlg->pScroll->pScrollBar->size.h = scrollbar_size(pDlg->pScroll);
1464         refresh_widget_background(pDlg->pScroll->pScrollBar);
1465       }
1466     }
1467 
1468   } else { /* no scrollbar */
1469     pBuf = pDlg->pBeginActiveWidgetList;
1470 
1471     /* undraw last widget */
1472     widget_undraw(pBuf);
1473     widget_mark_dirty(pBuf);
1474     FREESURFACE(pBuf->gfx);
1475 
1476     while (pBuf != pWidget) {
1477       pBuf->gfx = pBuf->next->gfx;
1478       pBuf->next->gfx = NULL;
1479       pBuf->size.x = pBuf->next->size.x;
1480       pBuf->size.y = pBuf->next->size.y;
1481       pBuf = pBuf->next;
1482     }
1483 
1484     if (pDlg->pScroll) {
1485       pDlg->pScroll->count--;
1486     }
1487   }
1488 
1489   if (pWidget == pDlg->pBeginWidgetList) {
1490     pDlg->pBeginWidgetList = pWidget->next;
1491   }
1492 
1493   if (pWidget == pDlg->pBeginActiveWidgetList) {
1494     pDlg->pBeginActiveWidgetList = pWidget->next;
1495   }
1496 
1497   if (pWidget == pDlg->pEndActiveWidgetList) {
1498     if (pWidget == pDlg->pEndWidgetList) {
1499       pDlg->pEndWidgetList = pWidget->prev;
1500     }
1501 
1502     if (pWidget == pDlg->pActiveWidgetList) {
1503       pDlg->pActiveWidgetList = pWidget->prev;
1504     }
1505 
1506     pDlg->pEndActiveWidgetList = pWidget->prev;
1507 
1508   }
1509 
1510   if (pDlg->pActiveWidgetList && (pDlg->pActiveWidgetList == pWidget)) {
1511     pDlg->pActiveWidgetList = pWidget->prev;
1512   }
1513 
1514   del_widget_from_gui_list(pWidget);
1515 
1516   if (pDlg->pScroll && pDlg->pScroll->pScrollBar && pDlg->pActiveWidgetList) {
1517     widget_undraw(pDlg->pScroll->pScrollBar);
1518     pDlg->pScroll->pScrollBar->size.y = get_position(pDlg);
1519     refresh_widget_background(pDlg->pScroll->pScrollBar);
1520   }
1521 
1522   return TRUE;
1523 }
1524 
1525 /**************************************************************************
1526   Set default vertical scrollbar handling for scrollbar.
1527 **************************************************************************/
setup_vertical_scrollbar_default_callbacks(struct ScrollBar * pScroll)1528 void setup_vertical_scrollbar_default_callbacks(struct ScrollBar *pScroll)
1529 {
1530   fc_assert_ret(pScroll != NULL);
1531 
1532   if (pScroll->pUp_Left_Button) {
1533     pScroll->pUp_Left_Button->action = std_up_advanced_dlg_callback;
1534   }
1535   if (pScroll->pDown_Right_Button) {
1536     pScroll->pDown_Right_Button->action = std_down_advanced_dlg_callback;
1537   }
1538   if (pScroll->pScrollBar) {
1539     pScroll->pScrollBar->action = std_vscroll_advanced_dlg_callback;
1540   }
1541 }
1542 
1543 /**************************************************************************
1544   			Horizontal Scrollbar
1545 **************************************************************************/
1546 
1547 
1548 /**************************************************************************
1549   Create a new horizontal scrollbar to active widgets list.
1550 **************************************************************************/
create_horizontal_scrollbar(struct ADVANCED_DLG * pDlg,Sint16 start_x,Sint16 start_y,Uint16 width,Uint16 active,bool create_scrollbar,bool create_buttons,bool swap_start_y)1551 Uint32 create_horizontal_scrollbar(struct ADVANCED_DLG *pDlg,
1552                                    Sint16 start_x, Sint16 start_y,
1553                                    Uint16 width, Uint16 active,
1554                                    bool create_scrollbar, bool create_buttons,
1555                                    bool swap_start_y)
1556 {
1557   Uint16 count = 0;
1558   struct widget *pBuf = NULL, *pWindow = NULL;
1559 
1560   fc_assert_ret_val(pDlg != NULL, 0);
1561 
1562   pWindow = pDlg->pEndWidgetList;
1563 
1564   if (!pDlg->pScroll) {
1565     pDlg->pScroll = fc_calloc(1, sizeof(struct ScrollBar));
1566 
1567     pDlg->pScroll->active = active;
1568 
1569     pBuf = pDlg->pEndActiveWidgetList;
1570     while (pBuf && pBuf != pDlg->pBeginActiveWidgetList->prev) {
1571       pBuf = pBuf->prev;
1572       count++;
1573     }
1574 
1575     pDlg->pScroll->count = count;
1576   }
1577 
1578   if (create_buttons) {
1579     /* create up button */
1580     pBuf = create_themeicon_button(current_theme->LEFT_Icon, pWindow->dst, NULL, 0);
1581 
1582     pBuf->ID = ID_BUTTON;
1583     pBuf->data.ptr = (void *)pDlg;
1584     set_wstate(pBuf, FC_WS_NORMAL);
1585 
1586     pBuf->size.x = start_x;
1587     if (swap_start_y) {
1588       pBuf->size.y = start_y - pBuf->size.h;
1589     } else {
1590       pBuf->size.y = start_y;
1591     }
1592 
1593     pDlg->pScroll->min = start_x + pBuf->size.w;
1594     pDlg->pScroll->pUp_Left_Button = pBuf;
1595     DownAdd(pBuf, pDlg->pBeginWidgetList);
1596     pDlg->pBeginWidgetList = pBuf;
1597 
1598     count = pBuf->size.h;
1599 
1600     /* create down button */
1601     pBuf = create_themeicon_button(current_theme->RIGHT_Icon, pWindow->dst, NULL, 0);
1602 
1603     pBuf->ID = ID_BUTTON;
1604     pBuf->data.ptr = (void *)pDlg;
1605     set_wstate(pBuf, FC_WS_NORMAL);
1606 
1607     pBuf->size.x = start_x + width - pBuf->size.w;
1608     if (swap_start_y) {
1609       pBuf->size.y = start_y - pBuf->size.h;
1610     } else {
1611       pBuf->size.y = start_y;
1612     }
1613 
1614     pDlg->pScroll->max = pBuf->size.x;
1615     pDlg->pScroll->pDown_Right_Button = pBuf;
1616     DownAdd(pBuf, pDlg->pBeginWidgetList);
1617     pDlg->pBeginWidgetList = pBuf;
1618   }
1619 
1620   if (create_scrollbar) {
1621     /* create vscrollbar */
1622     pBuf = create_horizontal(current_theme->Horiz, pWindow->dst,
1623                              width, WF_RESTORE_BACKGROUND);
1624 
1625     pBuf->ID = ID_SCROLLBAR;
1626     pBuf->data.ptr = (void *)pDlg;
1627     set_wstate(pBuf, FC_WS_NORMAL);
1628 
1629     if (swap_start_y) {
1630       pBuf->size.y = start_y - pBuf->size.h;
1631     } else {
1632       pBuf->size.y = start_y;
1633     }
1634 
1635     if (create_buttons) {
1636       pBuf->size.x = start_x + pDlg->pScroll->pUp_Left_Button->size.w;
1637       if (pDlg->pScroll->count > pDlg->pScroll->active) {
1638         pBuf->size.w = scrollbar_size(pDlg->pScroll);
1639       } else {
1640         pBuf->size.w = pDlg->pScroll->max - pDlg->pScroll->min;
1641       }
1642     } else {
1643       pBuf->size.x = start_x;
1644       pDlg->pScroll->min = start_x;
1645       pDlg->pScroll->max = start_x + width;
1646     }
1647 
1648     pDlg->pScroll->pScrollBar = pBuf;
1649     DownAdd(pBuf, pDlg->pBeginWidgetList);
1650     pDlg->pBeginWidgetList = pBuf;
1651 
1652     if (!count) {
1653       count = pBuf->size.h;
1654     }
1655   }
1656 
1657   return count;
1658 }
1659