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 MOVE {
40   bool moved;
41   struct widget *pWindow;
42   int prev_x;
43   int prev_y;
44 };
45 
46 static int (*baseclass_redraw)(struct widget *pwidget);
47 
48 /**************************************************************************
49   Redraw Window Graphic ( without other Widgets )
50 **************************************************************************/
redraw_window(struct widget * pWindow)51 static int redraw_window(struct widget *pWindow)
52 {
53   int ret;
54   SDL_Color title_bg_color = {255, 255, 255, 200};
55   SDL_Surface *pTmp = NULL;
56   SDL_Rect dst = pWindow->size;
57 
58   ret = (*baseclass_redraw)(pWindow);
59   if (ret != 0) {
60     return ret;
61   }
62 
63   /* Draw theme */
64   clear_surface(pWindow->dst->surface, &dst);
65   alphablit(pWindow->theme, NULL, pWindow->dst->surface, &dst, 255);
66 
67   /* window has title string == has title bar */
68   if (pWindow->string_utf8 != NULL) {
69 
70     /* Draw Window's TitleBar */
71     dst = pWindow->area;
72     dst.y -= (WINDOW_TITLE_HEIGHT + 1);
73     dst.h = WINDOW_TITLE_HEIGHT;
74     fill_rect_alpha(pWindow->dst->surface, &dst, &title_bg_color);
75 
76     /* Draw Text on Window's TitleBar */
77     pTmp = create_text_surf_from_utf8(pWindow->string_utf8);
78     dst.x += adj_size(4);
79     if (pTmp) {
80       dst.y += ((WINDOW_TITLE_HEIGHT - pTmp->h) / 2);
81       alphablit(pTmp, NULL, pWindow->dst->surface, &dst, 255);
82       FREESURFACE(pTmp);
83     }
84 
85     dst = pWindow->area;
86 
87     create_line(pWindow->dst->surface,
88                 dst.x, dst.y - 1,
89                 dst.x + dst.w - 1, dst.y - 1,
90                 get_theme_color(COLOR_THEME_WINDOW_TITLEBAR_SEPARATOR));
91   }
92 
93   /* draw frame */
94   if (get_wflags(pWindow) & WF_DRAW_FRAME_AROUND_WIDGET) {
95     widget_draw_frame(pWindow);
96   }
97 
98   return 0;
99 }
100 
101 /**************************************************************************
102   Window mechanism.
103 
104   Active Window schould be first on list (All Widgets on this
105   Window that are on List must be above)
106 
107   LIST:
108 
109   *pFirst_Widget_on_Active_Window.
110 
111   *pN__Widget_on_Active_Window.
112   *pActive_Window. <------
113   *pRest_Widgets.
114 
115   This trick give us:
116   - if any Widget is under ( area of ) this Window and Mouse
117     cursor is above them, 'WidgetListScaner(...)' return
118     pointer to Active Window not to this Widget.
119 **************************************************************************/
120 
121 /**************************************************************************
122   Set position for the window.
123 **************************************************************************/
window_set_position(struct widget * pWindow,int x,int y)124 static void window_set_position(struct widget *pWindow, int x, int y)
125 {
126   struct gui_layer *gui_layer;
127 
128   pWindow->size.x = 0;
129   pWindow->size.y = 0;
130 
131   gui_layer = get_gui_layer(pWindow->dst->surface);
132   gui_layer->dest_rect.x = x;
133   gui_layer->dest_rect.y = y;
134 }
135 
136 /**************************************************************************
137   Selected callback for the window widget.
138 **************************************************************************/
window_select(struct widget * pWindow)139 static void window_select(struct widget *pWindow)
140 {
141   /* nothing */
142 }
143 
144 /**************************************************************************
145   Unselected callback for the window widget.
146 **************************************************************************/
window_unselect(struct widget * pWindow)147 static void window_unselect(struct widget *pWindow)
148 {
149   /* nothing */
150 }
151 
152 /**************************************************************************
153   Set area for the window widget.
154 **************************************************************************/
set_client_area(struct widget * pWindow)155 static void set_client_area(struct widget *pWindow)
156 {
157   SDL_Rect area;
158 
159   if (get_wflags(pWindow) & WF_DRAW_FRAME_AROUND_WIDGET) {
160     area.x = current_theme->FR_Left->w;
161     area.y = current_theme->FR_Top->h;
162     area.w = pWindow->size.w - current_theme->FR_Left->w - current_theme->FR_Right->w;
163     area.h = pWindow->size.h - current_theme->FR_Top->h - current_theme->FR_Bottom->h;
164   } else {
165     area = pWindow->size;
166   }
167 
168   if (pWindow->string_utf8 != NULL) {
169     area.y += (WINDOW_TITLE_HEIGHT + 1);
170     area.h -= (WINDOW_TITLE_HEIGHT + 1);
171   }
172 
173   widget_set_area(pWindow, area);
174 }
175 
176 /**************************************************************************
177   Allocate Window Widget Structure.
178   Text to titlebar is taken from 'title'.
179 **************************************************************************/
create_window_skeleton(struct gui_layer * pDest,utf8_str * title,Uint32 flags)180 struct widget *create_window_skeleton(struct gui_layer *pDest,
181                                       utf8_str *title, Uint32 flags)
182 {
183   int w = 0, h = 0;
184   struct widget *pWindow = widget_new();
185 
186   pWindow->set_position = window_set_position;
187 
188   baseclass_redraw = pWindow->redraw;
189   pWindow->redraw = redraw_window;
190   pWindow->select = window_select;
191   pWindow->unselect = window_unselect;
192 
193   pWindow->string_utf8 = title;
194   set_wflag(pWindow, WF_FREE_STRING | WF_FREE_GFX | WF_FREE_THEME |
195             WF_DRAW_FRAME_AROUND_WIDGET| flags);
196   set_wstate(pWindow, FC_WS_DISABLED);
197   set_wtype(pWindow, WT_WINDOW);
198   pWindow->mod = KMOD_NONE;
199 
200   if (get_wflags(pWindow) & WF_DRAW_FRAME_AROUND_WIDGET) {
201     w += current_theme->FR_Left->w + current_theme->FR_Right->w;
202     h += current_theme->FR_Top->h + current_theme->FR_Bottom->h;
203   }
204 
205   if (title != NULL) {
206     SDL_Rect size;
207 
208     utf8_str_size(title, &size);
209 
210     w += size.w + adj_size(10);
211     h += MAX(size.h, WINDOW_TITLE_HEIGHT + 1);
212   }
213 
214   pWindow->size.w = w;
215   pWindow->size.h = h;
216 
217   set_client_area(pWindow);
218 
219   if (pDest) {
220     pWindow->dst = pDest;
221   } else {
222     pWindow->dst = add_gui_layer(w, h);
223   }
224 
225   return pWindow;
226 }
227 
228 /**************************************************************************
229   Create window widget
230 **************************************************************************/
create_window(struct gui_layer * pDest,utf8_str * title,Uint16 w,Uint16 h,Uint32 flags)231 struct widget *create_window(struct gui_layer *pDest, utf8_str *title,
232                              Uint16 w, Uint16 h, Uint32 flags)
233 {
234   struct widget *pWindow = create_window_skeleton(pDest, title, flags);
235 
236   resize_window(pWindow, NULL, NULL, w, h);
237 
238   return pWindow;
239 }
240 
241 /**************************************************************************
242   Resize Window 'pWindow' to 'new_w' and 'new_h'.
243   and refresh window background ( save screen under window ).
244 
245   If pBcgd == NULL then theme is set to
246   white transparent ( ALPHA = 128 ).
247 
248   Return 1 if allocate new surface and 0 if used 'pBcgd' surface.
249 
250   Exp.
251   if ( resize_window( pWindow , pBcgd , new_w , new_h ) ) {
252     FREESURFACE( pBcgd );
253   }
254 **************************************************************************/
resize_window(struct widget * pWindow,SDL_Surface * pBcgd,SDL_Color * pColor,Uint16 new_w,Uint16 new_h)255 int resize_window(struct widget *pWindow, SDL_Surface *pBcgd,
256                   SDL_Color *pColor, Uint16 new_w, Uint16 new_h)
257 {
258   SDL_Color color;
259 
260   /* window */
261   if ((new_w != pWindow->size.w) || (new_h != pWindow->size.h)) {
262     pWindow->size.w = new_w;
263     pWindow->size.h = new_h;
264 
265     set_client_area(pWindow);
266 
267     if (get_wflags(pWindow) & WF_RESTORE_BACKGROUND) {
268       refresh_widget_background(pWindow);
269     }
270 
271     FREESURFACE(pWindow->dst->surface);
272     pWindow->dst->surface = create_surf(pWindow->size.w,
273                                         pWindow->size.h,
274                                         SDL_SWSURFACE);
275   }
276 
277   if (pBcgd != pWindow->theme) {
278     FREESURFACE(pWindow->theme);
279   }
280 
281   if (pBcgd) {
282     if (pBcgd->w != new_w || pBcgd->h != new_h) {
283       pWindow->theme = ResizeSurface(pBcgd, new_w, new_h, 2);
284       return 1;
285     } else {
286       pWindow->theme = pBcgd;
287       return 0;
288     }
289   } else {
290     pWindow->theme = create_surf(new_w, new_h, SDL_SWSURFACE);
291 
292     if (pColor == NULL) {
293       color = *get_theme_color(COLOR_THEME_BACKGROUND);
294 
295       pColor = &color;
296     }
297 
298     SDL_FillRect(pWindow->theme, NULL, map_rgba(pWindow->theme->format, *pColor));
299 
300     return 1;
301   }
302 }
303 
304 /**************************************************************************
305   Move window as event instructs.
306 **************************************************************************/
move_window_motion(SDL_MouseMotionEvent * pMotionEvent,void * pData)307 static Uint16 move_window_motion(SDL_MouseMotionEvent *pMotionEvent,
308                                  void *pData)
309 {
310   struct MOVE *pMove = (struct MOVE *)pData;
311   int xrel, yrel;
312 
313   if (!pMove->moved) {
314     pMove->moved = TRUE;
315   }
316 
317   widget_mark_dirty(pMove->pWindow);
318 
319   xrel = pMotionEvent->x - pMove->prev_x;
320   yrel = pMotionEvent->y - pMove->prev_y;
321   pMove->prev_x = pMotionEvent->x;
322   pMove->prev_y = pMotionEvent->y;
323 
324   widget_set_position(pMove->pWindow,
325                       (pMove->pWindow->dst->dest_rect.x + pMove->pWindow->size.x) + xrel,
326                       (pMove->pWindow->dst->dest_rect.y + pMove->pWindow->size.y) + yrel);
327 
328   widget_mark_dirty(pMove->pWindow);
329   flush_dirty();
330 
331   return ID_ERROR;
332 }
333 
334 /**************************************************************************
335   Button up event handler for the window moving event loop.
336 **************************************************************************/
move_window_button_up(SDL_MouseButtonEvent * pButtonEvent,void * pData)337 static Uint16 move_window_button_up(SDL_MouseButtonEvent *pButtonEvent,
338                                     void *pData)
339 {
340   struct MOVE *pMove = (struct MOVE *)pData;
341 
342   if (pMove && pMove->moved) {
343     return (Uint16)ID_MOVED_WINDOW;
344   }
345 
346   return (Uint16)ID_WINDOW;
347 }
348 
349 /**************************************************************************
350   Move window in a event loop.
351 **************************************************************************/
move_window(struct widget * pWindow)352 bool move_window(struct widget *pWindow)
353 {
354   bool ret;
355   struct MOVE pMove;
356 
357   pMove.pWindow = pWindow;
358   pMove.moved = FALSE;
359   SDL_GetMouseState(&pMove.prev_x, &pMove.prev_y);
360   /* Filter mouse motion events */
361   SDL_SetEventFilter(FilterMouseMotionEvents, NULL);
362   ret = (gui_event_loop((void *)&pMove, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
363 	  move_window_button_up, move_window_motion) == ID_MOVED_WINDOW);
364   /* Turn off Filter mouse motion events */
365   SDL_SetEventFilter(NULL, NULL);
366 
367   return ret;
368 }
369