branch | NewGRF_ports |
changeset 10724 | 68a692eacf22 |
parent 10274 | b3c58f3df92b |
child 10731 | 67db0d431d5e |
10349:ff900a23e102 | 10724:68a692eacf22 |
---|---|
1 /* $Id$ */ |
1 /* $Id$ */ |
2 |
2 |
3 /** @file window.cpp windowing system, widgets and events */ |
3 /** @file window.cpp Windowing system, widgets and events */ |
4 |
4 |
5 #include "stdafx.h" |
5 #include "stdafx.h" |
6 #include <stdarg.h> |
6 #include <stdarg.h> |
7 #include "openttd.h" |
7 #include "openttd.h" |
8 #include "debug.h" |
8 #include "debug.h" |
18 #include "core/alloc_func.hpp" |
18 #include "core/alloc_func.hpp" |
19 #include "map_func.h" |
19 #include "map_func.h" |
20 #include "vehicle_base.h" |
20 #include "vehicle_base.h" |
21 #include "settings_type.h" |
21 #include "settings_type.h" |
22 #include "cheat_func.h" |
22 #include "cheat_func.h" |
23 #include "window_func.h" |
|
24 #include "tilehighlight_func.h" |
|
23 |
25 |
24 #include "table/sprites.h" |
26 #include "table/sprites.h" |
25 |
27 |
26 static Point _drag_delta; ///< delta between mouse cursor and upper left corner of dragged window |
28 static Point _drag_delta; ///< delta between mouse cursor and upper left corner of dragged window |
27 static Window *_mouseover_last_w = NULL; ///< Window of the last MOUSEOVER event |
29 static Window *_mouseover_last_w = NULL; ///< Window of the last MOUSEOVER event |
40 int _scrollbar_size; |
42 int _scrollbar_size; |
41 byte _scroller_click_timeout; |
43 byte _scroller_click_timeout; |
42 |
44 |
43 bool _scrolling_scrollbar; |
45 bool _scrolling_scrollbar; |
44 bool _scrolling_viewport; |
46 bool _scrolling_viewport; |
45 bool _popup_menu_active; |
|
46 |
47 |
47 byte _special_mouse_mode; |
48 byte _special_mouse_mode; |
49 |
|
50 |
|
51 /** |
|
52 * Call the window event handler for handling event \a e. |
|
53 * This is a temporary helper functions that will be removed |
|
54 * once all windows that still rely on WindowEvent and |
|
55 * WindowEventCodes have been rewritten to use the 'OnXXX' |
|
56 * event handlers. |
|
57 * @param e Window event to handle |
|
58 */ |
|
59 void Window::HandleWindowEvent(WindowEvent *e) |
|
60 { |
|
61 if (wndproc != NULL) wndproc(this, e); |
|
62 } |
|
63 |
|
64 void Window::OnPaint() |
|
65 { |
|
66 WindowEvent e; |
|
67 e.event = WE_PAINT; |
|
68 this->HandleWindowEvent(&e); |
|
69 } |
|
70 |
|
71 bool Window::OnKeyPress(uint16 key, uint16 keycode) |
|
72 { |
|
73 WindowEvent e; |
|
74 e.event = WE_KEYPRESS; |
|
75 e.we.keypress.key = key; |
|
76 e.we.keypress.keycode = keycode; |
|
77 e.we.keypress.cont = true; |
|
78 this->HandleWindowEvent(&e); |
|
79 |
|
80 return e.we.keypress.cont; |
|
81 } |
|
82 |
|
83 bool Window::OnCTRLStateChange() |
|
84 { |
|
85 WindowEvent e; |
|
86 e.event = WE_CTRL_CHANGED; |
|
87 e.we.ctrl.cont = true; |
|
88 this->HandleWindowEvent(&e); |
|
89 |
|
90 return e.we.ctrl.cont; |
|
91 } |
|
92 |
|
93 void Window::OnClick(Point pt, int widget) |
|
94 { |
|
95 WindowEvent e; |
|
96 e.event = WE_CLICK; |
|
97 e.we.click.pt = pt; |
|
98 e.we.click.widget = widget; |
|
99 this->HandleWindowEvent(&e); |
|
100 } |
|
101 |
|
102 void Window::OnMouseLoop() |
|
103 { |
|
104 WindowEvent e; |
|
105 e.event = WE_MOUSELOOP; |
|
106 this->HandleWindowEvent(&e); |
|
107 } |
|
108 |
|
109 void Window::OnTick() |
|
110 { |
|
111 WindowEvent e; |
|
112 e.event = WE_TICK; |
|
113 this->HandleWindowEvent(&e); |
|
114 } |
|
115 |
|
116 void Window::OnHundredthTick() |
|
117 { |
|
118 WindowEvent e; |
|
119 e.event = WE_100_TICKS; |
|
120 this->HandleWindowEvent(&e); |
|
121 } |
|
122 |
|
123 void Window::OnTimeout() |
|
124 { |
|
125 WindowEvent e; |
|
126 e.event = WE_TIMEOUT; |
|
127 this->HandleWindowEvent(&e); |
|
128 } |
|
129 |
|
130 void Window::OnResize(Point new_size, Point delta) |
|
131 { |
|
132 WindowEvent e; |
|
133 e.event = WE_RESIZE; |
|
134 e.we.sizing.size = new_size; |
|
135 e.we.sizing.diff = delta; |
|
136 this->HandleWindowEvent(&e); |
|
137 } |
|
138 |
|
139 void Window::OnDropdownSelect(int widget, int index) |
|
140 { |
|
141 WindowEvent e; |
|
142 e.event = WE_DROPDOWN_SELECT; |
|
143 e.we.dropdown.button = widget; |
|
144 e.we.dropdown.index = index; |
|
145 this->HandleWindowEvent(&e); |
|
146 } |
|
147 |
|
148 void Window::OnQueryTextFinished(char *str) |
|
149 { |
|
150 WindowEvent e; |
|
151 e.event = WE_ON_EDIT_TEXT; |
|
152 e.we.edittext.str = str; |
|
153 this->HandleWindowEvent(&e); |
|
154 } |
|
155 |
|
156 void Window::OnInvalidateData(int data) |
|
157 { |
|
158 WindowEvent e; |
|
159 e.event = WE_INVALIDATE_DATA; |
|
160 e.we.invalidate.data = data; |
|
161 this->HandleWindowEvent(&e); |
|
162 } |
|
163 |
|
164 void Window::OnPlaceObject(Point pt, TileIndex tile) |
|
165 { |
|
166 WindowEvent e; |
|
167 e.event = WE_PLACE_OBJ; |
|
168 e.we.place.pt = pt; |
|
169 e.we.place.tile = tile; |
|
170 this->HandleWindowEvent(&e); |
|
171 } |
|
172 |
|
173 void Window::OnPlaceObjectAbort() |
|
174 { |
|
175 WindowEvent e; |
|
176 e.event = WE_ABORT_PLACE_OBJ; |
|
177 this->HandleWindowEvent(&e); |
|
178 } |
|
179 |
|
180 |
|
181 void Window::OnPlaceDrag(ViewportPlaceMethod select_method, byte select_proc, Point pt) |
|
182 { |
|
183 WindowEvent e; |
|
184 e.event = WE_PLACE_DRAG; |
|
185 e.we.place.select_method = select_method; |
|
186 e.we.place.select_proc = select_proc; |
|
187 e.we.place.pt = pt; |
|
188 this->HandleWindowEvent(&e); |
|
189 } |
|
190 |
|
191 void Window::OnPlaceMouseUp(ViewportPlaceMethod select_method, byte select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) |
|
192 { |
|
193 WindowEvent e; |
|
194 e.event = WE_PLACE_MOUSEUP; |
|
195 e.we.place.select_method = select_method; |
|
196 e.we.place.select_proc = select_proc; |
|
197 e.we.place.pt = pt; |
|
198 e.we.place.tile = end_tile; |
|
199 e.we.place.starttile = start_tile; |
|
200 this->HandleWindowEvent(&e); |
|
201 } |
|
202 |
|
203 void Window::OnPlacePresize(Point pt, TileIndex tile) |
|
204 { |
|
205 WindowEvent e; |
|
206 e.event = WE_PLACE_PRESIZE; |
|
207 e.we.place.pt = pt; |
|
208 e.we.place.tile = tile; |
|
209 this->HandleWindowEvent(&e); |
|
210 } |
|
211 |
|
48 |
212 |
49 |
213 |
50 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...) |
214 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...) |
51 { |
215 { |
52 va_list wdg_list; |
216 va_list wdg_list; |
89 va_end(wdg_list); |
253 va_end(wdg_list); |
90 } |
254 } |
91 |
255 |
92 void Window::RaiseButtons() |
256 void Window::RaiseButtons() |
93 { |
257 { |
94 uint i; |
258 for (uint i = 0; i < this->widget_count; i++) { |
95 |
|
96 for (i = 0; i < this->widget_count; i++) { |
|
97 if (this->IsWidgetLowered(i)) { |
259 if (this->IsWidgetLowered(i)) { |
98 this->RaiseWidget(i); |
260 this->RaiseWidget(i); |
99 this->InvalidateWidget(i); |
261 this->InvalidateWidget(i); |
100 } |
262 } |
101 } |
263 } |
128 * @param y Y coordinate of the click |
290 * @param y Y coordinate of the click |
129 * @param double_click Was it a double click? |
291 * @param double_click Was it a double click? |
130 */ |
292 */ |
131 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) |
293 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) |
132 { |
294 { |
133 WindowEvent e; |
295 int widget = 0; |
134 const Widget *wi; |
|
135 |
|
136 e.we.click.pt.x = x; |
|
137 e.we.click.pt.y = y; |
|
138 e.event = double_click ? WE_DOUBLE_CLICK : WE_CLICK; |
|
139 |
|
140 if (w->desc_flags & WDF_DEF_WIDGET) { |
296 if (w->desc_flags & WDF_DEF_WIDGET) { |
141 e.we.click.widget = GetWidgetFromPos(w, x, y); |
297 widget = GetWidgetFromPos(w, x, y); |
142 if (e.we.click.widget < 0) return; // exit if clicked outside of widgets |
298 if (widget < 0) return; // exit if clicked outside of widgets |
143 |
299 |
144 /* don't allow any interaction if the button has been disabled */ |
300 /* don't allow any interaction if the button has been disabled */ |
145 if (w->IsWidgetDisabled(e.we.click.widget)) return; |
301 if (w->IsWidgetDisabled(widget)) return; |
146 |
302 |
147 wi = &w->widget[e.we.click.widget]; |
303 const Widget *wi = &w->widget[widget]; |
148 |
304 |
149 if (wi->type & WWB_MASK) { |
305 if (wi->type & WWB_MASK) { |
150 /* special widget handling for buttons*/ |
306 /* special widget handling for buttons*/ |
151 switch (wi->type) { |
307 switch (wi->type) { |
152 case WWT_PANEL | WWB_PUSHBUTTON: /* WWT_PUSHBTN */ |
308 case WWT_PANEL | WWB_PUSHBUTTON: /* WWT_PUSHBTN */ |
153 case WWT_IMGBTN | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */ |
309 case WWT_IMGBTN | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */ |
154 case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */ |
310 case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */ |
155 w->HandleButtonClick(e.we.click.widget); |
311 w->HandleButtonClick(widget); |
156 break; |
312 break; |
157 } |
313 } |
158 } else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) { |
314 } else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) { |
159 ScrollbarClickHandler(w, wi, e.we.click.pt.x, e.we.click.pt.y); |
315 ScrollbarClickHandler(w, wi, x, y); |
160 } |
316 } |
161 |
317 |
162 if (w->desc_flags & WDF_STD_BTN) { |
318 if (w->desc_flags & WDF_STD_BTN) { |
163 if (e.we.click.widget == 0) { /* 'X' */ |
319 if (widget == 0) { /* 'X' */ |
164 DeleteWindow(w); |
320 delete w; |
165 return; |
321 return; |
166 } |
322 } |
167 |
323 |
168 if (e.we.click.widget == 1) { /* 'Title bar' */ |
324 if (widget == 1) { /* 'Title bar' */ |
169 StartWindowDrag(w); |
325 StartWindowDrag(w); |
170 return; |
326 return; |
171 } |
327 } |
172 } |
328 } |
173 |
329 |
174 if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) { |
330 if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) { |
175 StartWindowSizing(w); |
331 StartWindowSizing(w); |
176 w->InvalidateWidget(e.we.click.widget); |
332 w->InvalidateWidget(widget); |
177 return; |
333 return; |
178 } |
334 } |
179 |
335 |
180 if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) { |
336 if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) { |
181 w->flags4 ^= WF_STICKY; |
337 w->flags4 ^= WF_STICKY; |
182 w->InvalidateWidget(e.we.click.widget); |
338 w->InvalidateWidget(widget); |
183 return; |
339 return; |
184 } |
340 } |
185 } |
341 } |
186 |
342 |
187 w->wndproc(w, &e); |
343 Point pt = { x, y }; |
344 |
|
345 if (double_click) { |
|
346 w->OnDoubleClick(pt, widget); |
|
347 } else { |
|
348 w->OnClick(pt, widget); |
|
349 } |
|
188 } |
350 } |
189 |
351 |
190 /** |
352 /** |
191 * Dispatch right mouse-button click in window. |
353 * Dispatch right mouse-button click in window. |
192 * @param w Window to dispatch event in |
354 * @param w Window to dispatch event in |
193 * @param x X coordinate of the click |
355 * @param x X coordinate of the click |
194 * @param y Y coordinate of the click |
356 * @param y Y coordinate of the click |
195 */ |
357 */ |
196 static void DispatchRightClickEvent(Window *w, int x, int y) |
358 static void DispatchRightClickEvent(Window *w, int x, int y) |
197 { |
359 { |
198 WindowEvent e; |
360 int widget = 0; |
199 |
361 |
200 /* default tooltips handler? */ |
362 /* default tooltips handler? */ |
201 if (w->desc_flags & WDF_STD_TOOLTIPS) { |
363 if (w->desc_flags & WDF_STD_TOOLTIPS) { |
202 e.we.click.widget = GetWidgetFromPos(w, x, y); |
364 widget = GetWidgetFromPos(w, x, y); |
203 if (e.we.click.widget < 0) |
365 if (widget < 0) return; // exit if clicked outside of widgets |
204 return; // exit if clicked outside of widgets |
366 |
205 |
367 if (w->widget[widget].tooltips != 0) { |
206 if (w->widget[e.we.click.widget].tooltips != 0) { |
368 GuiShowTooltips(w->widget[widget].tooltips); |
207 GuiShowTooltips(w->widget[e.we.click.widget].tooltips); |
|
208 return; |
369 return; |
209 } |
370 } |
210 } |
371 } |
211 |
372 |
212 e.event = WE_RCLICK; |
373 Point pt = { x, y }; |
213 e.we.click.pt.x = x; |
374 w->OnRightClick(pt, widget); |
214 e.we.click.pt.y = y; |
|
215 w->wndproc(w, &e); |
|
216 } |
375 } |
217 |
376 |
218 /** |
377 /** |
219 * Dispatch the mousewheel-action to the window. |
378 * Dispatch the mousewheel-action to the window. |
220 * The window will scroll any compatible scrollbars if the mouse is pointed over the bar or its contents |
379 * The window will scroll any compatible scrollbars if the mouse is pointed over the bar or its contents |
222 * @param widget the widget where the scrollwheel was used |
381 * @param widget the widget where the scrollwheel was used |
223 * @param wheel scroll up or down |
382 * @param wheel scroll up or down |
224 */ |
383 */ |
225 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel) |
384 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel) |
226 { |
385 { |
227 const Widget *wi1, *wi2; |
|
228 Scrollbar *sb; |
|
229 |
|
230 if (widget < 0) return; |
386 if (widget < 0) return; |
231 |
387 |
232 wi1 = &w->widget[widget]; |
388 const Widget *wi1 = &w->widget[widget]; |
233 wi2 = &w->widget[widget + 1]; |
389 const Widget *wi2 = &w->widget[widget + 1]; |
234 |
390 |
235 /* The listbox can only scroll if scrolling was done on the scrollbar itself, |
391 /* The listbox can only scroll if scrolling was done on the scrollbar itself, |
236 * or on the listbox (and the next item is (must be) the scrollbar) |
392 * or on the listbox (and the next item is (must be) the scrollbar) |
237 * XXX - should be rewritten as a widget-dependent scroller but that's |
393 * XXX - should be rewritten as a widget-dependent scroller but that's |
238 * not happening until someone rewrites the whole widget-code */ |
394 * not happening until someone rewrites the whole widget-code */ |
395 Scrollbar *sb; |
|
239 if ((sb = &w->vscroll, wi1->type == WWT_SCROLLBAR) || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR) || |
396 if ((sb = &w->vscroll, wi1->type == WWT_SCROLLBAR) || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR) || |
240 (sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) { |
397 (sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) { |
241 |
398 |
242 if (sb->count > sb->cap) { |
399 if (sb->count > sb->cap) { |
243 int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap); |
400 int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap); |
244 if (pos != sb->pos) { |
401 if (pos != sb->pos) { |
245 sb->pos = pos; |
402 sb->pos = pos; |
246 SetWindowDirty(w); |
403 w->SetDirty(); |
247 } |
404 } |
248 } |
405 } |
249 } |
406 } |
250 } |
407 } |
251 |
408 |
310 dp->left = left - (*wz)->left; |
467 dp->left = left - (*wz)->left; |
311 dp->top = top - (*wz)->top; |
468 dp->top = top - (*wz)->top; |
312 dp->pitch = _screen.pitch; |
469 dp->pitch = _screen.pitch; |
313 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top); |
470 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top); |
314 dp->zoom = ZOOM_LVL_NORMAL; |
471 dp->zoom = ZOOM_LVL_NORMAL; |
315 CallWindowEventNP(*wz, WE_PAINT); |
472 (*wz)->OnPaint(); |
316 } |
473 } |
317 |
474 |
318 /** |
475 /** |
319 * From a rectangle that needs redrawing, find the windows that intersect with the rectangle. |
476 * From a rectangle that needs redrawing, find the windows that intersect with the rectangle. |
320 * These windows should be re-painted. |
477 * These windows should be re-painted. |
340 } |
497 } |
341 } |
498 } |
342 } |
499 } |
343 |
500 |
344 /** |
501 /** |
345 * Dispatch an event to a possibly non-existing window. |
|
346 * If the window pointer w is \c NULL, the event is not dispatched |
|
347 * @param w Window to dispatch the event to, may be \c NULL |
|
348 * @param event Event to dispatch |
|
349 */ |
|
350 void CallWindowEventNP(Window *w, int event) |
|
351 { |
|
352 WindowEvent e; |
|
353 |
|
354 e.event = event; |
|
355 w->wndproc(w, &e); |
|
356 } |
|
357 |
|
358 /** |
|
359 * Mark entire window as dirty (in need of re-paint) |
502 * Mark entire window as dirty (in need of re-paint) |
360 * @param w Window to redraw |
503 * @param w Window to redraw |
361 * @ingroup dirty |
504 * @ingroup dirty |
362 */ |
505 */ |
506 void Window::SetDirty() const |
|
507 { |
|
508 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height); |
|
509 } |
|
510 |
|
511 /** |
|
512 * Mark entire window as dirty (in need of re-paint) |
|
513 * @param w Window to redraw |
|
514 * @ingroup dirty |
|
515 */ |
|
363 void SetWindowDirty(const Window *w) |
516 void SetWindowDirty(const Window *w) |
364 { |
517 { |
365 if (w == NULL) return; |
518 if (w != NULL) w->SetDirty(); |
366 SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height); |
|
367 } |
519 } |
368 |
520 |
369 /** Find the Window whose parent pointer points to this window |
521 /** Find the Window whose parent pointer points to this window |
370 * @param w parent Window to find child of |
522 * @param w parent Window to find child of |
371 * @return a Window pointer that is the child of w, or NULL otherwise */ |
523 * @return a Window pointer that is the child of w, or NULL otherwise */ |
399 return NULL; |
551 return NULL; |
400 } |
552 } |
401 |
553 |
402 /** |
554 /** |
403 * Remove window and all its child windows from the window stack. |
555 * Remove window and all its child windows from the window stack. |
404 * @param w Window to delete |
556 */ |
405 */ |
557 Window::~Window() |
406 void DeleteWindow(Window *w) |
558 { |
407 { |
|
408 if (w == NULL) return; |
|
409 |
|
410 /* Delete any children a window might have in a head-recursive manner */ |
|
411 Window *v = FindChildWindow(w); |
|
412 if (v != NULL) DeleteWindow(v); |
|
413 |
|
414 if (_thd.place_mode != VHM_NONE && |
559 if (_thd.place_mode != VHM_NONE && |
415 _thd.window_class == w->window_class && |
560 _thd.window_class == this->window_class && |
416 _thd.window_number == w->window_number) { |
561 _thd.window_number == this->window_number) { |
417 ResetObjectToPlace(); |
562 ResetObjectToPlace(); |
418 } |
563 } |
419 |
564 |
420 CallWindowEventNP(w, WE_DESTROY); |
|
421 if (w->viewport != NULL) DeleteWindowViewport(w); |
|
422 |
|
423 SetWindowDirty(w); |
|
424 free(w->widget); |
|
425 w->widget = NULL; |
|
426 w->widget_count = 0; |
|
427 w->parent = NULL; |
|
428 |
|
429 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */ |
565 /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */ |
430 if (_mouseover_last_w == w) _mouseover_last_w = NULL; |
566 if (_mouseover_last_w == this) _mouseover_last_w = NULL; |
431 |
567 |
432 /* Find the window in the z-array, and effectively remove it |
568 /* Find the window in the z-array, and effectively remove it |
433 * by moving all windows after it one to the left */ |
569 * by moving all windows after it one to the left. This must be |
434 Window **wz = FindWindowZPosition(w); |
570 * done before removing the child so we cannot cause recursion |
571 * between the deletion of the parent and the child. */ |
|
572 Window **wz = FindWindowZPosition(this); |
|
435 if (wz == NULL) return; |
573 if (wz == NULL) return; |
436 memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz); |
574 memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz); |
437 _last_z_window--; |
575 _last_z_window--; |
438 |
576 |
439 delete w; |
577 /* Delete all children a window might have in a head-recursive manner */ |
578 Window *child = FindChildWindow(this); |
|
579 while (child != NULL) { |
|
580 delete child; |
|
581 child = FindChildWindow(this); |
|
582 } |
|
583 |
|
584 WindowEvent e; |
|
585 e.event = WE_DESTROY; |
|
586 this->HandleWindowEvent(&e); |
|
587 if (this->viewport != NULL) DeleteWindowViewport(this); |
|
588 |
|
589 this->SetDirty(); |
|
590 free(this->widget); |
|
440 } |
591 } |
441 |
592 |
442 /** |
593 /** |
443 * Find a window by its class and window number |
594 * Find a window by its class and window number |
444 * @param cls Window class |
595 * @param cls Window class |
462 * @param cls Window class |
613 * @param cls Window class |
463 * @param number Number of the window within the window class |
614 * @param number Number of the window within the window class |
464 */ |
615 */ |
465 void DeleteWindowById(WindowClass cls, WindowNumber number) |
616 void DeleteWindowById(WindowClass cls, WindowNumber number) |
466 { |
617 { |
467 DeleteWindow(FindWindowById(cls, number)); |
618 delete FindWindowById(cls, number); |
468 } |
619 } |
469 |
620 |
470 /** |
621 /** |
471 * Delete all windows of a given class |
622 * Delete all windows of a given class |
472 * @param cls Window class of windows to delete |
623 * @param cls Window class of windows to delete |
480 * as deleting this window could cascade in deleting (many) others |
631 * as deleting this window could cascade in deleting (many) others |
481 * anywhere in the z-array */ |
632 * anywhere in the z-array */ |
482 FOR_ALL_WINDOWS(wz) { |
633 FOR_ALL_WINDOWS(wz) { |
483 Window *w = *wz; |
634 Window *w = *wz; |
484 if (w->window_class == cls) { |
635 if (w->window_class == cls) { |
485 DeleteWindow(w); |
636 delete w; |
486 goto restart_search; |
637 goto restart_search; |
487 } |
638 } |
488 } |
639 } |
489 } |
640 } |
490 |
641 |
501 * as deleting this window could cascade in deleting (many) others |
652 * as deleting this window could cascade in deleting (many) others |
502 * anywhere in the z-array */ |
653 * anywhere in the z-array */ |
503 FOR_ALL_WINDOWS(wz) { |
654 FOR_ALL_WINDOWS(wz) { |
504 Window *w = *wz; |
655 Window *w = *wz; |
505 if (w->caption_color == id) { |
656 if (w->caption_color == id) { |
506 DeleteWindow(w); |
657 delete w; |
507 goto restart_search; |
658 goto restart_search; |
508 } |
659 } |
509 } |
660 } |
510 |
661 |
511 /* Also delete the player specific windows, that don't have a player-colour */ |
662 /* Also delete the player specific windows, that don't have a player-colour */ |
522 Window* const *wz; |
673 Window* const *wz; |
523 |
674 |
524 FOR_ALL_WINDOWS(wz) { |
675 FOR_ALL_WINDOWS(wz) { |
525 Window *w = *wz; |
676 Window *w = *wz; |
526 |
677 |
527 if (w->caption_color != old_player) continue; |
678 if (w->caption_color != old_player) continue; |
528 if (w->window_class == WC_PLAYER_COLOR) continue; |
679 |
529 if (w->window_class == WC_FINANCES) continue; |
680 switch (w->window_class) { |
530 if (w->window_class == WC_STATION_LIST) continue; |
681 case WC_PLAYER_COLOR: |
531 if (w->window_class == WC_TRAINS_LIST) continue; |
682 case WC_FINANCES: |
532 if (w->window_class == WC_ROADVEH_LIST) continue; |
683 case WC_STATION_LIST: |
533 if (w->window_class == WC_SHIPS_LIST) continue; |
684 case WC_TRAINS_LIST: |
534 if (w->window_class == WC_AIRCRAFT_LIST) continue; |
685 case WC_ROADVEH_LIST: |
535 if (w->window_class == WC_BUY_COMPANY) continue; |
686 case WC_SHIPS_LIST: |
536 if (w->window_class == WC_COMPANY) continue; |
687 case WC_AIRCRAFT_LIST: |
537 |
688 case WC_BUY_COMPANY: |
538 w->caption_color = new_player; |
689 case WC_COMPANY: |
690 continue; |
|
691 |
|
692 default: |
|
693 w->caption_color = new_player; |
|
694 break; |
|
695 } |
|
539 } |
696 } |
540 } |
697 } |
541 |
698 |
542 static void BringWindowToFront(const Window *w); |
699 static void BringWindowToFront(const Window *w); |
543 |
700 |
551 Window *w = FindWindowById(cls, number); |
708 Window *w = FindWindowById(cls, number); |
552 |
709 |
553 if (w != NULL) { |
710 if (w != NULL) { |
554 w->flags4 |= WF_WHITE_BORDER_MASK; |
711 w->flags4 |= WF_WHITE_BORDER_MASK; |
555 BringWindowToFront(w); |
712 BringWindowToFront(w); |
556 SetWindowDirty(w); |
713 w->SetDirty(); |
557 } |
714 } |
558 |
715 |
559 return w; |
716 return w; |
560 } |
717 } |
561 |
718 |
562 static inline bool IsVitalWindow(const Window *w) |
719 static inline bool IsVitalWindow(const Window *w) |
563 { |
720 { |
564 WindowClass wc = w->window_class; |
721 switch (w->window_class) { |
565 return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG); |
722 case WC_MAIN_TOOLBAR: |
723 case WC_STATUS_BAR: |
|
724 case WC_NEWS_WINDOW: |
|
725 case WC_SEND_NETWORK_MSG: |
|
726 return true; |
|
727 |
|
728 default: |
|
729 return false; |
|
730 } |
|
566 } |
731 } |
567 |
732 |
568 /** On clicking on a window, make it the frontmost window of all. However |
733 /** On clicking on a window, make it the frontmost window of all. However |
569 * there are certain windows that always need to be on-top; these include |
734 * there are certain windows that always need to be on-top; these include |
570 * - Toolbar, Statusbar (always on) |
735 * - Toolbar, Statusbar (always on) |
573 * @param w window that is put into the foreground |
738 * @param w window that is put into the foreground |
574 * @return pointer to the window, the same as the input pointer |
739 * @return pointer to the window, the same as the input pointer |
575 */ |
740 */ |
576 static void BringWindowToFront(const Window *w) |
741 static void BringWindowToFront(const Window *w) |
577 { |
742 { |
578 Window *tempz; |
|
579 Window **wz = FindWindowZPosition(w); |
743 Window **wz = FindWindowZPosition(w); |
580 Window **vz = _last_z_window; |
744 Window **vz = _last_z_window; |
581 |
745 |
582 /* Bring the window just below the vital windows */ |
746 /* Bring the window just below the vital windows */ |
583 do { |
747 do { |
585 } while (IsVitalWindow(*vz)); |
749 } while (IsVitalWindow(*vz)); |
586 |
750 |
587 if (wz == vz) return; // window is already in the right position |
751 if (wz == vz) return; // window is already in the right position |
588 assert(wz < vz); |
752 assert(wz < vz); |
589 |
753 |
590 tempz = *wz; |
754 Window *tempz = *wz; |
591 memmove(wz, wz + 1, (byte*)vz - (byte*)wz); |
755 memmove(wz, wz + 1, (byte*)vz - (byte*)wz); |
592 *vz = tempz; |
756 *vz = tempz; |
593 |
757 |
594 SetWindowDirty(w); |
758 w->SetDirty(); |
595 } |
759 } |
596 |
760 |
597 /** We have run out of windows, so find a suitable candidate for replacement. |
761 /** We have run out of windows, so find a suitable candidate for replacement. |
598 * Keep all important windows intact. These are |
762 * Keep all important windows intact. These are |
599 * - Main window (gamefield), Toolbar, Statusbar (always on) |
763 * - Main window (gamefield), Toolbar, Statusbar (always on) |
630 if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w; |
794 if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w; |
631 } |
795 } |
632 NOT_REACHED(); |
796 NOT_REACHED(); |
633 } |
797 } |
634 |
798 |
635 bool IsWindowOfPrototype(const Window *w, const Widget *widget) |
799 /** |
636 { |
800 * Assign widgets to a new window by initialising its widget pointers, and by |
637 return (w->original_widget == widget); |
801 * copying the widget array \a widget to \c w->widget to allow for resizable |
638 } |
802 * windows. |
639 |
|
640 /** Copies 'widget' to 'w->widget' to allow for resizable windows |
|
641 * @param w Window on which to attach the widget array |
803 * @param w Window on which to attach the widget array |
642 * @param widget pointer of widget array to fill the window with */ |
804 * @param widget pointer of widget array to fill the window with |
643 void AssignWidgetToWindow(Window *w, const Widget *widget) |
805 * |
644 { |
806 * @post \c w->widget points to allocated memory and contains the copied widget array except for the terminating widget, |
645 w->original_widget = widget; |
807 * \c w->widget_count contains number of widgets in the allocated memory. |
646 |
808 */ |
809 static void AssignWidgetToWindow(Window *w, const Widget *widget) |
|
810 { |
|
647 if (widget != NULL) { |
811 if (widget != NULL) { |
648 uint index = 1; |
812 uint index = 1; |
649 const Widget *wi; |
813 |
650 |
814 for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++; |
651 for (wi = widget; wi->type != WWT_LAST; wi++) index++; |
815 |
652 |
816 w->widget = MallocT<Widget>(index); |
653 w->widget = ReallocT(w->widget, index); |
|
654 memcpy(w->widget, widget, sizeof(*w->widget) * index); |
817 memcpy(w->widget, widget, sizeof(*w->widget) * index); |
655 w->widget_count = index - 1; |
818 w->widget_count = index - 1; |
656 } else { |
819 } else { |
657 w->widget = NULL; |
820 w->widget = NULL; |
658 w->widget_count = 0; |
821 w->widget_count = 0; |
659 } |
822 } |
660 } |
823 } |
661 |
824 |
662 /** Open a new window. |
825 /** |
663 * This function is called from AllocateWindow() or AllocateWindowDesc() |
826 * Initializes a new Window. |
827 * This function is called the constructors. |
|
664 * See descriptions for those functions for usage |
828 * See descriptions for those functions for usage |
665 * See AllocateWindow() for description of arguments. |
|
666 * Only addition here is window_number, which is the window_number being assigned to the new window |
829 * Only addition here is window_number, which is the window_number being assigned to the new window |
667 * @param x offset in pixels from the left of the screen |
830 * @param x offset in pixels from the left of the screen |
668 * @param y offset in pixels from the top of the screen |
831 * @param y offset in pixels from the top of the screen |
669 * @param min_width minimum width in pixels of the window |
832 * @param min_width minimum width in pixels of the window |
670 * @param min_height minimum height in pixels of the window |
833 * @param min_height minimum height in pixels of the window |
671 * @param def_width default width in pixels of the window |
|
672 * @param def_height default height in pixels of the window |
|
673 * @param *proc see WindowProc function to call when any messages/updates happen to the window |
834 * @param *proc see WindowProc function to call when any messages/updates happen to the window |
674 * @param cls see WindowClass class of the window, used for identification and grouping |
835 * @param cls see WindowClass class of the window, used for identification and grouping |
675 * @param *widget see Widget pointer to the window layout and various elements |
836 * @param *widget see Widget pointer to the window layout and various elements |
676 * @param window_number number being assigned to the new window |
837 * @param window_number number being assigned to the new window |
677 * @param data the data to be given during the WE_CREATE message |
838 * @return Window pointer of the newly created window |
678 * @return Window pointer of the newly created window */ |
839 */ |
679 static Window *LocalAllocateWindow(int x, int y, int min_width, int min_height, int def_width, int def_height, |
840 void Window::Initialize(int x, int y, int min_width, int min_height, |
680 WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data) |
841 WindowProc *proc, WindowClass cls, const Widget *widget, int window_number) |
681 { |
842 { |
682 Window *w; |
|
683 |
|
684 /* We have run out of windows, close one and use that as the place for our new one */ |
843 /* We have run out of windows, close one and use that as the place for our new one */ |
685 if (_last_z_window == endof(_z_windows)) { |
844 if (_last_z_window == endof(_z_windows)) { |
686 w = FindDeletableWindow(); |
845 Window *w = FindDeletableWindow(); |
687 if (w == NULL) w = ForceFindDeletableWindow(); |
846 if (w == NULL) w = ForceFindDeletableWindow(); |
688 DeleteWindow(w); |
847 delete w; |
689 } |
848 } |
690 |
|
691 w = new Window; |
|
692 |
849 |
693 /* Set up window properties */ |
850 /* Set up window properties */ |
694 w->window_class = cls; |
851 this->window_class = cls; |
695 w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border |
852 this->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border |
696 w->caption_color = 0xFF; |
853 this->caption_color = 0xFF; |
697 w->left = x; |
854 this->left = x; |
698 w->top = y; |
855 this->top = y; |
699 w->width = min_width; |
856 this->width = min_width; |
700 w->height = min_height; |
857 this->height = min_height; |
701 w->wndproc = proc; |
858 this->wndproc = proc; |
702 AssignWidgetToWindow(w, widget); |
859 AssignWidgetToWindow(this, widget); |
703 w->resize.width = min_width; |
860 this->resize.width = min_width; |
704 w->resize.height = min_height; |
861 this->resize.height = min_height; |
705 w->resize.step_width = 1; |
862 this->resize.step_width = 1; |
706 w->resize.step_height = 1; |
863 this->resize.step_height = 1; |
707 w->window_number = window_number; |
864 this->window_number = window_number; |
708 |
865 |
709 { |
866 /* Hacky way of specifying always-on-top windows. These windows are |
710 Window **wz = _last_z_window; |
867 * always above other windows because they are moved below them. |
711 |
868 * status-bar is above news-window because it has been created earlier. |
712 /* Hacky way of specifying always-on-top windows. These windows are |
869 * Also, as the chat-window is excluded from this, it will always be |
713 * always above other windows because they are moved below them. |
870 * the last window, thus always on top. |
714 * status-bar is above news-window because it has been created earlier. |
871 * XXX - Yes, ugly, probably needs something like w->always_on_top flag |
715 * Also, as the chat-window is excluded from this, it will always be |
872 * to implement correctly, but even then you need some kind of distinction |
716 * the last window, thus always on top. |
873 * between on-top of chat/news and status windows, because these conflict */ |
717 * XXX - Yes, ugly, probably needs something like w->always_on_top flag |
874 Window **wz = _last_z_window; |
718 * to implement correctly, but even then you need some kind of distinction |
875 if (wz != _z_windows && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) { |
719 * between on-top of chat/news and status windows, because these conflict */ |
876 if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) wz--; |
720 if (wz != _z_windows && w->window_class != WC_SEND_NETWORK_MSG && w->window_class != WC_HIGHSCORE && w->window_class != WC_ENDSCREEN) { |
877 if (FindWindowById(WC_STATUS_BAR, 0) != NULL) wz--; |
721 if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) wz--; |
878 if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) wz--; |
722 if (FindWindowById(WC_STATUS_BAR, 0) != NULL) wz--; |
879 if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--; |
723 if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) wz--; |
880 |
724 if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--; |
881 assert(wz >= _z_windows); |
725 |
882 if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz); |
726 assert(wz >= _z_windows); |
883 } |
727 if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz); |
884 |
728 } |
885 *wz = this; |
729 |
886 _last_z_window++; |
730 *wz = w; |
|
731 _last_z_window++; |
|
732 } |
|
733 |
887 |
734 WindowEvent e; |
888 WindowEvent e; |
735 e.event = WE_CREATE; |
889 e.event = WE_CREATE; |
736 e.we.create.data = data; |
890 this->HandleWindowEvent(&e); |
737 w->wndproc(w, &e); |
891 } |
738 |
892 |
893 /** |
|
894 * Find a nice spot for this window and resize it towards the default size. |
|
895 * @param def_width default width in pixels of the window |
|
896 * @param def_height default height in pixels of the window |
|
897 */ |
|
898 void Window::FindWindowPlacementAndResize(int def_width, int def_height) |
|
899 { |
|
739 /* Try to make windows smaller when our window is too small. |
900 /* Try to make windows smaller when our window is too small. |
740 * w->(width|height) is normally the same as min_(width|height), |
901 * w->(width|height) is normally the same as min_(width|height), |
741 * but this way the GUIs can be made a little more dynamic; |
902 * but this way the GUIs can be made a little more dynamic; |
742 * one can use the same spec for multiple windows and those |
903 * one can use the same spec for multiple windows and those |
743 * can then determine the real minimum size of the window. */ |
904 * can then determine the real minimum size of the window. */ |
744 if (w->width != def_width || w->height != def_height) { |
905 if (this->width != def_width || this->height != def_height) { |
745 /* Think about the overlapping toolbars when determining the minimum window size */ |
906 /* Think about the overlapping toolbars when determining the minimum window size */ |
746 int free_height = _screen.height; |
907 int free_height = _screen.height; |
747 const Window *wt = FindWindowById(WC_STATUS_BAR, 0); |
908 const Window *wt = FindWindowById(WC_STATUS_BAR, 0); |
748 if (wt != NULL) free_height -= wt->height; |
909 if (wt != NULL) free_height -= wt->height; |
749 wt = FindWindowById(WC_MAIN_TOOLBAR, 0); |
910 wt = FindWindowById(WC_MAIN_TOOLBAR, 0); |
750 if (wt != NULL) free_height -= wt->height; |
911 if (wt != NULL) free_height -= wt->height; |
751 |
912 |
752 int enlarge_x = max(min(def_width - w->width, _screen.width - w->width), 0); |
913 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0); |
753 int enlarge_y = max(min(def_height - w->height, free_height - w->height), 0); |
914 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0); |
754 |
915 |
755 /* X and Y has to go by step.. calculate it. |
916 /* X and Y has to go by step.. calculate it. |
756 * The cast to int is necessary else x/y are implicitly casted to |
917 * The cast to int is necessary else x/y are implicitly casted to |
757 * unsigned int, which won't work. */ |
918 * unsigned int, which won't work. */ |
758 if (w->resize.step_width > 1) enlarge_x -= enlarge_x % (int)w->resize.step_width; |
919 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width; |
759 if (w->resize.step_height > 1) enlarge_y -= enlarge_y % (int)w->resize.step_height; |
920 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height; |
760 |
921 |
761 ResizeWindow(w, enlarge_x, enlarge_y); |
922 ResizeWindow(this, enlarge_x, enlarge_y); |
762 |
923 |
763 WindowEvent e; |
924 Point size; |
764 e.event = WE_RESIZE; |
925 Point diff; |
765 e.we.sizing.size.x = w->width; |
926 size.x = this->width; |
766 e.we.sizing.size.y = w->height; |
927 size.y = this->height; |
767 e.we.sizing.diff.x = enlarge_x; |
928 diff.x = enlarge_x; |
768 e.we.sizing.diff.y = enlarge_y; |
929 diff.y = enlarge_y; |
769 w->wndproc(w, &e); |
930 this->OnResize(size, diff); |
770 } |
931 } |
771 |
932 |
772 int nx = w->left; |
933 int nx = this->left; |
773 int ny = w->top; |
934 int ny = this->top; |
774 |
935 |
775 if (nx + w->width > _screen.width) nx -= (nx + w->width - _screen.width); |
936 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width); |
776 |
937 |
777 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); |
938 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); |
778 ny = max(ny, (wt == NULL || w == wt || y == 0) ? 0 : wt->height); |
939 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height); |
779 nx = max(nx, 0); |
940 nx = max(nx, 0); |
780 |
941 |
781 if (w->viewport != NULL) { |
942 if (this->viewport != NULL) { |
782 w->viewport->left += nx - w->left; |
943 this->viewport->left += nx - this->left; |
783 w->viewport->top += ny - w->top; |
944 this->viewport->top += ny - this->top; |
784 } |
945 } |
785 w->left = nx; |
946 this->left = nx; |
786 w->top = ny; |
947 this->top = ny; |
787 |
948 |
788 SetWindowDirty(w); |
949 this->SetDirty(); |
789 |
950 } |
790 return w; |
951 |
952 void Window::FindWindowPlacementAndResize(const WindowDesc *desc) |
|
953 { |
|
954 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height); |
|
791 } |
955 } |
792 |
956 |
793 /** |
957 /** |
794 * Open a new window. If there is no space for a new window, close an open |
958 * Open a new window. If there is no space for a new window, close an open |
795 * window. Try to avoid stickied windows, but if there is no else, close one of |
959 * window. Try to avoid stickied windows, but if there is no else, close one of |
802 * @param *proc see WindowProc function to call when any messages/updates happen to the window |
966 * @param *proc see WindowProc function to call when any messages/updates happen to the window |
803 * @param cls see WindowClass class of the window, used for identification and grouping |
967 * @param cls see WindowClass class of the window, used for identification and grouping |
804 * @param *widget see Widget pointer to the window layout and various elements |
968 * @param *widget see Widget pointer to the window layout and various elements |
805 * @return Window pointer of the newly created window |
969 * @return Window pointer of the newly created window |
806 */ |
970 */ |
807 Window *AllocateWindow(int x, int y, int width, int height, |
971 Window::Window(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const Widget *widget) |
808 WindowProc *proc, WindowClass cls, const Widget *widget, void *data) |
972 { |
809 { |
973 this->Initialize(x, y, width, height, proc, cls, widget, 0); |
810 return LocalAllocateWindow(x, y, width, height, width, height, proc, cls, widget, 0, data); |
974 |
975 if (proc != NULL) this->FindWindowPlacementAndResize(width, height); |
|
811 } |
976 } |
812 |
977 |
813 |
978 |
814 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos) |
979 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos) |
815 { |
980 { |
816 Window* const *wz; |
981 Window* const *wz; |
817 |
982 |
818 int right = width + left; |
983 int right = width + left; |
819 int bottom = height + top; |
984 int bottom = height + top; |
820 |
985 |
821 if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) |
986 if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) return false; |
822 return false; |
|
823 |
987 |
824 /* Make sure it is not obscured by any window. */ |
988 /* Make sure it is not obscured by any window. */ |
825 FOR_ALL_WINDOWS(wz) { |
989 FOR_ALL_WINDOWS(wz) { |
826 const Window *w = *wz; |
990 const Window *w = *wz; |
827 if (w->window_class == WC_MAIN_WINDOW) continue; |
991 if (w->window_class == WC_MAIN_WINDOW) continue; |
914 return pt; |
1078 return pt; |
915 } |
1079 } |
916 } |
1080 } |
917 |
1081 |
918 /** |
1082 /** |
919 * Set the x and y coordinates of a new window. |
1083 * Compute the position of the top-left corner of a new window that is opened. |
1084 * |
|
1085 * By default position a child window at an offset of 10/10 of its parent. |
|
1086 * With the exception of WC_BUILD_TOOLBAR (build railway/roads/ship docks/airports) |
|
1087 * and WC_SCEN_LAND_GEN (landscaping). Whose child window has an offset of 0/36 of |
|
1088 * its parent. So it's exactly under the parent toolbar and no buttons will be covered. |
|
1089 * However if it falls too extremely outside window positions, reposition |
|
1090 * it to an automatic place. |
|
920 * |
1091 * |
921 * @param *desc The pointer to the WindowDesc to be created |
1092 * @param *desc The pointer to the WindowDesc to be created |
922 * @param window_number the window number of the new window |
1093 * @param window_number the window number of the new window |
923 * @param data arbitrary data that is send with the WE_CREATE message |
|
924 * |
1094 * |
925 * @return see Window pointer of the newly created window |
1095 * @return Coordinate of the top-left corner of the new window |
926 */ |
1096 */ |
927 static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number, void *data) |
1097 static Point LocalGetWindowPlacement(const WindowDesc *desc, int window_number) |
928 { |
1098 { |
929 Point pt; |
1099 Point pt; |
930 Window *w; |
1100 Window *w; |
931 |
1101 |
932 /* By default position a child window at an offset of 10/10 of its parent. |
|
933 * With the exception of WC_BUILD_TOOLBAR (build railway/roads/ship docks/airports) |
|
934 * and WC_SCEN_LAND_GEN (landscaping). Whose child window has an offset of 0/36 of |
|
935 * its parent. So it's exactly under the parent toolbar and no buttons will be covered. |
|
936 * However if it falls too extremely outside window positions, reposition |
|
937 * it to an automatic place */ |
|
938 if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ && |
1102 if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ && |
939 (w = FindWindowById(desc->parent_cls, window_number)) != NULL && |
1103 (w = FindWindowById(desc->parent_cls, window_number)) != NULL && |
940 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) { |
1104 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) { |
941 |
1105 |
942 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10); |
1106 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10); |
944 pt.x = (_screen.width + 10 - desc->default_width) - 20; |
1108 pt.x = (_screen.width + 10 - desc->default_width) - 20; |
945 } |
1109 } |
946 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10); |
1110 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10); |
947 } else { |
1111 } else { |
948 switch (desc->left) { |
1112 switch (desc->left) { |
949 case WDP_ALIGN_TBR: { /* Align the right side with the top toolbar */ |
1113 case WDP_ALIGN_TBR: // Align the right side with the top toolbar |
950 w = FindWindowById(WC_MAIN_TOOLBAR, 0); |
1114 w = FindWindowById(WC_MAIN_TOOLBAR, 0); |
951 pt.x = (w->left + w->width) - desc->default_width; |
1115 pt.x = (w->left + w->width) - desc->default_width; |
952 } break; |
1116 break; |
953 case WDP_ALIGN_TBL: /* Align the left side with the top toolbar */ |
1117 |
1118 case WDP_ALIGN_TBL: // Align the left side with the top toolbar |
|
954 pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left; |
1119 pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left; |
955 break; |
1120 break; |
956 case WDP_AUTO: /* Find a good automatic position for the window */ |
1121 |
957 pt = GetAutoPlacePosition(desc->default_width, desc->default_height); |
1122 case WDP_AUTO: // Find a good automatic position for the window |
958 goto allocate_window; |
1123 return GetAutoPlacePosition(desc->default_width, desc->default_height); |
959 case WDP_CENTER: /* Centre the window horizontally */ |
1124 |
1125 case WDP_CENTER: // Centre the window horizontally |
|
960 pt.x = (_screen.width - desc->default_width) / 2; |
1126 pt.x = (_screen.width - desc->default_width) / 2; |
961 break; |
1127 break; |
1128 |
|
962 default: |
1129 default: |
963 pt.x = desc->left; |
1130 pt.x = desc->left; |
964 if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen |
1131 if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen |
965 } |
1132 } |
966 |
1133 |
967 switch (desc->top) { |
1134 switch (desc->top) { |
968 case WDP_CENTER: /* Centre the window vertically */ |
1135 case WDP_CENTER: // Centre the window vertically |
969 pt.y = (_screen.height - desc->default_height) / 2; |
1136 pt.y = (_screen.height - desc->default_height) / 2; |
970 break; |
1137 break; |
1138 |
|
971 /* WDP_AUTO sets the position at once and is controlled by desc->left. |
1139 /* WDP_AUTO sets the position at once and is controlled by desc->left. |
972 * Both left and top must be set to WDP_AUTO */ |
1140 * Both left and top must be set to WDP_AUTO */ |
973 case WDP_AUTO: |
1141 case WDP_AUTO: |
974 NOT_REACHED(); |
1142 NOT_REACHED(); |
975 assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO); |
1143 assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO); |
976 /* fallthrough */ |
1144 /* fallthrough */ |
1145 |
|
977 default: |
1146 default: |
978 pt.y = desc->top; |
1147 pt.y = desc->top; |
979 if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen |
1148 if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen |
980 break; |
1149 break; |
981 } |
1150 } |
982 } |
1151 } |
983 |
1152 |
984 allocate_window: |
1153 return pt; |
985 w = LocalAllocateWindow(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->default_width, desc->default_height, desc->proc, desc->cls, desc->widgets, window_number, data); |
1154 } |
986 w->desc_flags = desc->flags; |
1155 |
987 return w; |
1156 /** |
988 } |
1157 * Set the positions of a new window from a WindowDesc and open it. |
989 |
1158 * |
990 /** |
1159 * @param *desc The pointer to the WindowDesc to be created |
991 * Open a new window. |
1160 * @param window_number the window number of the new window |
992 * @param *desc The pointer to the WindowDesc to be created |
1161 * |
993 * @param data arbitrary data that is send with the WE_CREATE message |
|
994 * @return Window pointer of the newly created window |
1162 * @return Window pointer of the newly created window |
995 */ |
1163 */ |
996 Window *AllocateWindowDesc(const WindowDesc *desc, void *data) |
1164 Window::Window(const WindowDesc *desc, WindowNumber window_number) |
997 { |
1165 { |
998 return LocalAllocateWindowDesc(desc, 0, data); |
1166 Point pt = LocalGetWindowPlacement(desc, window_number); |
999 } |
1167 this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->proc, desc->cls, desc->widgets, window_number); |
1000 |
1168 this->desc_flags = desc->flags; |
1001 /** |
1169 |
1002 * Open a new window. |
1170 if (desc->proc != NULL) this->FindWindowPlacementAndResize(desc->default_width, desc->default_height); |
1003 * @param *desc The pointer to the WindowDesc to be created |
|
1004 * @param window_number the window number of the new window |
|
1005 * @param data arbitrary data that is send with the WE_CREATE message |
|
1006 * @return see Window pointer of the newly created window |
|
1007 */ |
|
1008 Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data) |
|
1009 { |
|
1010 Window *w; |
|
1011 |
|
1012 if (BringWindowToFrontById(desc->cls, window_number)) return NULL; |
|
1013 w = LocalAllocateWindowDesc(desc, window_number, data); |
|
1014 return w; |
|
1015 } |
1171 } |
1016 |
1172 |
1017 /** Do a search for a window at specific coordinates. For this we start |
1173 /** Do a search for a window at specific coordinates. For this we start |
1018 * at the topmost window, obviously and work our way down to the bottom |
1174 * at the topmost window, obviously and work our way down to the bottom |
1019 * @param x position x to query |
1175 * @param x position x to query |
1046 /** |
1202 /** |
1047 * Close down the windowing system |
1203 * Close down the windowing system |
1048 */ |
1204 */ |
1049 void UnInitWindowSystem() |
1205 void UnInitWindowSystem() |
1050 { |
1206 { |
1051 Window **wz; |
1207 while (_last_z_window != _z_windows) delete _z_windows[0]; |
1052 |
|
1053 restart_search: |
|
1054 /* Delete all windows, reset z-array. |
|
1055 * When we find the window to delete, we need to restart the search |
|
1056 * as deleting this window could cascade in deleting (many) others |
|
1057 * anywhere in the z-array. We call DeleteWindow() so that it can properly |
|
1058 * release own alloc'd memory, which otherwise could result in memleaks */ |
|
1059 FOR_ALL_WINDOWS(wz) { |
|
1060 DeleteWindow(*wz); |
|
1061 goto restart_search; |
|
1062 } |
|
1063 |
|
1064 assert(_last_z_window == _z_windows); |
|
1065 } |
1208 } |
1066 |
1209 |
1067 /** |
1210 /** |
1068 * Reset the windowing system, by means of shutting it down followed by re-initialization |
1211 * Reset the windowing system, by means of shutting it down followed by re-initialization |
1069 */ |
1212 */ |
1077 _thd.new_pos.y = 0; |
1220 _thd.new_pos.y = 0; |
1078 } |
1221 } |
1079 |
1222 |
1080 static void DecreaseWindowCounters() |
1223 static void DecreaseWindowCounters() |
1081 { |
1224 { |
1082 Window *w; |
|
1083 Window* const *wz; |
1225 Window* const *wz; |
1084 |
1226 |
1085 for (wz = _last_z_window; wz != _z_windows;) { |
1227 for (wz = _last_z_window; wz != _z_windows;) { |
1086 w = *--wz; |
1228 Window *w = *--wz; |
1087 /* Unclick scrollbar buttons if they are pressed. */ |
1229 /* Unclick scrollbar buttons if they are pressed. */ |
1088 if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) { |
1230 if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) { |
1089 w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP); |
1231 w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP); |
1090 SetWindowDirty(w); |
1232 w->SetDirty(); |
1091 } |
1233 } |
1092 CallWindowEventNP(w, WE_MOUSELOOP); |
1234 w->OnMouseLoop(); |
1093 } |
1235 } |
1094 |
1236 |
1095 for (wz = _last_z_window; wz != _z_windows;) { |
1237 for (wz = _last_z_window; wz != _z_windows;) { |
1096 w = *--wz; |
1238 Window *w = *--wz; |
1097 |
1239 |
1098 if (w->flags4&WF_TIMEOUT_MASK && !(--w->flags4&WF_TIMEOUT_MASK)) { |
1240 if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) { |
1099 CallWindowEventNP(w, WE_TIMEOUT); |
1241 w->OnTimeout(); |
1100 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(); |
1242 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(); |
1101 } |
1243 } |
1102 } |
1244 } |
1103 } |
1245 } |
1104 |
1246 |
1107 return FindWindowById(_thd.window_class, _thd.window_number); |
1249 return FindWindowById(_thd.window_class, _thd.window_number); |
1108 } |
1250 } |
1109 |
1251 |
1110 static void HandlePlacePresize() |
1252 static void HandlePlacePresize() |
1111 { |
1253 { |
1112 Window *w; |
|
1113 WindowEvent e; |
|
1114 |
|
1115 if (_special_mouse_mode != WSM_PRESIZE) return; |
1254 if (_special_mouse_mode != WSM_PRESIZE) return; |
1116 |
1255 |
1117 w = GetCallbackWnd(); |
1256 Window *w = GetCallbackWnd(); |
1118 if (w == NULL) return; |
1257 if (w == NULL) return; |
1119 |
1258 |
1120 e.we.place.pt = GetTileBelowCursor(); |
1259 Point pt = GetTileBelowCursor(); |
1121 if (e.we.place.pt.x == -1) { |
1260 if (pt.x == -1) { |
1122 _thd.selend.x = -1; |
1261 _thd.selend.x = -1; |
1123 return; |
1262 return; |
1124 } |
1263 } |
1125 e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y); |
1264 |
1126 e.event = WE_PLACE_PRESIZE; |
1265 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y)); |
1127 w->wndproc(w, &e); |
|
1128 } |
1266 } |
1129 |
1267 |
1130 static bool HandleDragDrop() |
1268 static bool HandleDragDrop() |
1131 { |
1269 { |
1132 Window *w; |
|
1133 WindowEvent e; |
|
1134 |
|
1135 if (_special_mouse_mode != WSM_DRAGDROP) return true; |
1270 if (_special_mouse_mode != WSM_DRAGDROP) return true; |
1136 |
|
1137 if (_left_button_down) return false; |
1271 if (_left_button_down) return false; |
1138 |
1272 |
1139 w = GetCallbackWnd(); |
1273 Window *w = GetCallbackWnd(); |
1140 |
1274 |
1141 if (w != NULL) { |
1275 if (w != NULL) { |
1142 /* send an event in client coordinates. */ |
1276 /* send an event in client coordinates. */ |
1143 e.event = WE_DRAGDROP; |
1277 Point pt; |
1144 e.we.dragdrop.pt.x = _cursor.pos.x - w->left; |
1278 pt.x = _cursor.pos.x - w->left; |
1145 e.we.dragdrop.pt.y = _cursor.pos.y - w->top; |
1279 pt.y = _cursor.pos.y - w->top; |
1146 e.we.dragdrop.widget = GetWidgetFromPos(w, e.we.dragdrop.pt.x, e.we.dragdrop.pt.y); |
1280 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y)); |
1147 w->wndproc(w, &e); |
|
1148 } |
1281 } |
1149 |
1282 |
1150 ResetObjectToPlace(); |
1283 ResetObjectToPlace(); |
1151 |
1284 |
1152 return false; |
1285 return false; |
1153 } |
1286 } |
1154 |
1287 |
1155 static bool HandlePopupMenu() |
|
1156 { |
|
1157 Window *w; |
|
1158 WindowEvent e; |
|
1159 |
|
1160 if (!_popup_menu_active) return true; |
|
1161 |
|
1162 w = FindWindowById(WC_TOOLBAR_MENU, 0); |
|
1163 if (w == NULL) { |
|
1164 _popup_menu_active = false; |
|
1165 return false; |
|
1166 } |
|
1167 |
|
1168 if (_left_button_down) { |
|
1169 e.event = WE_POPUPMENU_OVER; |
|
1170 e.we.popupmenu.pt = _cursor.pos; |
|
1171 } else { |
|
1172 _popup_menu_active = false; |
|
1173 e.event = WE_POPUPMENU_SELECT; |
|
1174 e.we.popupmenu.pt = _cursor.pos; |
|
1175 } |
|
1176 |
|
1177 w->wndproc(w, &e); |
|
1178 |
|
1179 return false; |
|
1180 } |
|
1181 |
|
1182 static bool HandleMouseOver() |
1288 static bool HandleMouseOver() |
1183 { |
1289 { |
1184 WindowEvent e; |
|
1185 |
|
1186 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); |
1290 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); |
1187 |
1291 |
1188 /* We changed window, put a MOUSEOVER event to the last window */ |
1292 /* We changed window, put a MOUSEOVER event to the last window */ |
1189 if (_mouseover_last_w != NULL && _mouseover_last_w != w) { |
1293 if (_mouseover_last_w != NULL && _mouseover_last_w != w) { |
1190 /* Reset mouse-over coordinates of previous window */ |
1294 /* Reset mouse-over coordinates of previous window */ |
1191 e.event = WE_MOUSEOVER; |
1295 Point pt = { -1, -1 }; |
1192 e.we.mouseover.pt.x = -1; |
1296 _mouseover_last_w->OnMouseOver(pt, 0); |
1193 e.we.mouseover.pt.y = -1; |
|
1194 if (_mouseover_last_w->wndproc != NULL) _mouseover_last_w->wndproc(_mouseover_last_w, &e); |
|
1195 } |
1297 } |
1196 |
1298 |
1197 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */ |
1299 /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */ |
1198 _mouseover_last_w = w; |
1300 _mouseover_last_w = w; |
1199 |
1301 |
1200 if (w != NULL) { |
1302 if (w != NULL) { |
1201 /* send an event in client coordinates. */ |
1303 /* send an event in client coordinates. */ |
1202 e.event = WE_MOUSEOVER; |
1304 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top }; |
1203 e.we.mouseover.pt.x = _cursor.pos.x - w->left; |
1305 int widget = 0; |
1204 e.we.mouseover.pt.y = _cursor.pos.y - w->top; |
|
1205 if (w->widget != NULL) { |
1306 if (w->widget != NULL) { |
1206 e.we.mouseover.widget = GetWidgetFromPos(w, e.we.mouseover.pt.x, e.we.mouseover.pt.y); |
1307 widget = GetWidgetFromPos(w, pt.x, pt.y); |
1207 } |
1308 } |
1208 w->wndproc(w, &e); |
1309 w->OnMouseOver(pt, widget); |
1209 } |
1310 } |
1210 |
1311 |
1211 /* Mouseover never stops execution */ |
1312 /* Mouseover never stops execution */ |
1212 return true; |
1313 return true; |
1213 } |
1314 } |
1221 * @param x delta x-size of changed window (positive if larger, etc.) |
1322 * @param x delta x-size of changed window (positive if larger, etc.) |
1222 * @param y delta y-size of changed window |
1323 * @param y delta y-size of changed window |
1223 */ |
1324 */ |
1224 void ResizeWindow(Window *w, int x, int y) |
1325 void ResizeWindow(Window *w, int x, int y) |
1225 { |
1326 { |
1226 Widget *wi; |
|
1227 bool resize_height = false; |
1327 bool resize_height = false; |
1228 bool resize_width = false; |
1328 bool resize_width = false; |
1229 |
1329 |
1230 if (x == 0 && y == 0) return; |
1330 if (x == 0 && y == 0) return; |
1231 |
1331 |
1232 SetWindowDirty(w); |
1332 w->SetDirty(); |
1233 for (wi = w->widget; wi->type != WWT_LAST; wi++) { |
1333 for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) { |
1234 /* Isolate the resizing flags */ |
1334 /* Isolate the resizing flags */ |
1235 byte rsizeflag = GB(wi->display_flags, 0, 4); |
1335 byte rsizeflag = GB(wi->display_flags, 0, 4); |
1236 |
1336 |
1237 if (rsizeflag == RESIZE_NONE) continue; |
1337 if (rsizeflag == RESIZE_NONE) continue; |
1238 |
1338 |
1260 |
1360 |
1261 /* We resized at least 1 widget, so let's resize the window totally */ |
1361 /* We resized at least 1 widget, so let's resize the window totally */ |
1262 if (resize_width) w->width += x; |
1362 if (resize_width) w->width += x; |
1263 if (resize_height) w->height += y; |
1363 if (resize_height) w->height += y; |
1264 |
1364 |
1265 SetWindowDirty(w); |
1365 w->SetDirty(); |
1266 } |
1366 } |
1267 |
1367 |
1268 static bool _dragging_window; |
1368 static bool _dragging_window; |
1269 |
1369 |
1270 static bool HandleWindowDragging() |
1370 static bool HandleWindowDragging() |
1277 FOR_ALL_WINDOWS(wz) { |
1377 FOR_ALL_WINDOWS(wz) { |
1278 Window *w = *wz; |
1378 Window *w = *wz; |
1279 |
1379 |
1280 if (w->flags4 & WF_DRAGGING) { |
1380 if (w->flags4 & WF_DRAGGING) { |
1281 const Widget *t = &w->widget[1]; // the title bar ... ugh |
1381 const Widget *t = &w->widget[1]; // the title bar ... ugh |
1282 const Window *v; |
|
1283 int x; |
|
1284 int y; |
|
1285 int nx; |
|
1286 int ny; |
|
1287 |
1382 |
1288 /* Stop the dragging if the left mouse button was released */ |
1383 /* Stop the dragging if the left mouse button was released */ |
1289 if (!_left_button_down) { |
1384 if (!_left_button_down) { |
1290 w->flags4 &= ~WF_DRAGGING; |
1385 w->flags4 &= ~WF_DRAGGING; |
1291 break; |
1386 break; |
1292 } |
1387 } |
1293 |
1388 |
1294 SetWindowDirty(w); |
1389 w->SetDirty(); |
1295 |
1390 |
1296 x = _cursor.pos.x + _drag_delta.x; |
1391 int x = _cursor.pos.x + _drag_delta.x; |
1297 y = _cursor.pos.y + _drag_delta.y; |
1392 int y = _cursor.pos.y + _drag_delta.y; |
1298 nx = x; |
1393 int nx = x; |
1299 ny = y; |
1394 int ny = y; |
1300 |
1395 |
1301 if (_patches.window_snap_radius != 0) { |
1396 if (_patches.window_snap_radius != 0) { |
1302 Window* const *vz; |
1397 Window* const *vz; |
1303 |
1398 |
1304 int hsnap = _patches.window_snap_radius; |
1399 int hsnap = _patches.window_snap_radius; |
1380 * 13 is the height of the title bar */ |
1475 * 13 is the height of the title bar */ |
1381 nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left); |
1476 nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left); |
1382 ny = Clamp(ny, 0, _screen.height - 13); |
1477 ny = Clamp(ny, 0, _screen.height - 13); |
1383 |
1478 |
1384 /* Make sure the title bar isn't hidden by behind the main tool bar */ |
1479 /* Make sure the title bar isn't hidden by behind the main tool bar */ |
1385 v = FindWindowById(WC_MAIN_TOOLBAR, 0); |
1480 Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0); |
1386 if (v != NULL) { |
1481 if (v != NULL) { |
1387 int v_bottom = v->top + v->height; |
1482 int v_bottom = v->top + v->height; |
1388 int v_right = v->left + v->width; |
1483 int v_right = v->left + v->width; |
1389 if (ny + t->top >= v->top && ny + t->top < v_bottom) { |
1484 if (ny + t->top >= v->top && ny + t->top < v_bottom) { |
1390 if ((v->left < 13 && nx + t->left < v->left) || |
1485 if ((v->left < 13 && nx + t->left < v->left) || |
1410 w->viewport->top += ny - w->top; |
1505 w->viewport->top += ny - w->top; |
1411 } |
1506 } |
1412 w->left = nx; |
1507 w->left = nx; |
1413 w->top = ny; |
1508 w->top = ny; |
1414 |
1509 |
1415 SetWindowDirty(w); |
1510 w->SetDirty(); |
1416 return false; |
1511 return false; |
1417 } else if (w->flags4 & WF_SIZING) { |
1512 } else if (w->flags4 & WF_SIZING) { |
1418 WindowEvent e; |
|
1419 int x, y; |
1513 int x, y; |
1420 |
1514 |
1421 /* Stop the sizing if the left mouse button was released */ |
1515 /* Stop the sizing if the left mouse button was released */ |
1422 if (!_left_button_down) { |
1516 if (!_left_button_down) { |
1423 w->flags4 &= ~WF_SIZING; |
1517 w->flags4 &= ~WF_SIZING; |
1424 SetWindowDirty(w); |
1518 w->SetDirty(); |
1425 break; |
1519 break; |
1426 } |
1520 } |
1427 |
1521 |
1428 x = _cursor.pos.x - _drag_delta.x; |
1522 x = _cursor.pos.x - _drag_delta.x; |
1429 y = _cursor.pos.y - _drag_delta.y; |
1523 y = _cursor.pos.y - _drag_delta.y; |
1450 _drag_delta.y += y; |
1544 _drag_delta.y += y; |
1451 |
1545 |
1452 /* ResizeWindow sets both pre- and after-size to dirty for redrawal */ |
1546 /* ResizeWindow sets both pre- and after-size to dirty for redrawal */ |
1453 ResizeWindow(w, x, y); |
1547 ResizeWindow(w, x, y); |
1454 |
1548 |
1455 e.event = WE_RESIZE; |
1549 Point size; |
1456 e.we.sizing.size.x = x + w->width; |
1550 Point diff; |
1457 e.we.sizing.size.y = y + w->height; |
1551 size.x = x + w->width; |
1458 e.we.sizing.diff.x = x; |
1552 size.y = y + w->height; |
1459 e.we.sizing.diff.y = y; |
1553 diff.x = x; |
1460 w->wndproc(w, &e); |
1554 diff.y = y; |
1555 w->OnResize(size, diff); |
|
1461 return false; |
1556 return false; |
1462 } |
1557 } |
1463 } |
1558 } |
1464 |
1559 |
1465 _dragging_window = false; |
1560 _dragging_window = false; |
1500 |
1595 |
1501 |
1596 |
1502 static bool HandleScrollbarScrolling() |
1597 static bool HandleScrollbarScrolling() |
1503 { |
1598 { |
1504 Window* const *wz; |
1599 Window* const *wz; |
1505 int i; |
|
1506 int pos; |
|
1507 Scrollbar *sb; |
|
1508 |
1600 |
1509 /* Get out quickly if no item is being scrolled */ |
1601 /* Get out quickly if no item is being scrolled */ |
1510 if (!_scrolling_scrollbar) return true; |
1602 if (!_scrolling_scrollbar) return true; |
1511 |
1603 |
1512 /* Find the scrolling window */ |
1604 /* Find the scrolling window */ |
1515 |
1607 |
1516 if (w->flags4 & WF_SCROLL_MIDDLE) { |
1608 if (w->flags4 & WF_SCROLL_MIDDLE) { |
1517 /* Abort if no button is clicked any more. */ |
1609 /* Abort if no button is clicked any more. */ |
1518 if (!_left_button_down) { |
1610 if (!_left_button_down) { |
1519 w->flags4 &= ~WF_SCROLL_MIDDLE; |
1611 w->flags4 &= ~WF_SCROLL_MIDDLE; |
1520 SetWindowDirty(w); |
1612 w->SetDirty(); |
1521 break; |
1613 break; |
1522 } |
1614 } |
1615 |
|
1616 int i; |
|
1617 Scrollbar *sb; |
|
1523 |
1618 |
1524 if (w->flags4 & WF_HSCROLL) { |
1619 if (w->flags4 & WF_HSCROLL) { |
1525 sb = &w->hscroll; |
1620 sb = &w->hscroll; |
1526 i = _cursor.pos.x - _cursorpos_drag_start.x; |
1621 i = _cursor.pos.x - _cursorpos_drag_start.x; |
1527 } else if (w->flags4 & WF_SCROLL2){ |
1622 } else if (w->flags4 & WF_SCROLL2){ |
1531 sb = &w->vscroll; |
1626 sb = &w->vscroll; |
1532 i = _cursor.pos.y - _cursorpos_drag_start.y; |
1627 i = _cursor.pos.y - _cursorpos_drag_start.y; |
1533 } |
1628 } |
1534 |
1629 |
1535 /* Find the item we want to move to and make sure it's inside bounds. */ |
1630 /* Find the item we want to move to and make sure it's inside bounds. */ |
1536 pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap)); |
1631 int pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap)); |
1537 if (pos != sb->pos) { |
1632 if (pos != sb->pos) { |
1538 sb->pos = pos; |
1633 sb->pos = pos; |
1539 SetWindowDirty(w); |
1634 w->SetDirty(); |
1540 } |
1635 } |
1541 return false; |
1636 return false; |
1542 } |
1637 } |
1543 } |
1638 } |
1544 |
1639 |
1546 return false; |
1641 return false; |
1547 } |
1642 } |
1548 |
1643 |
1549 static bool HandleViewportScroll() |
1644 static bool HandleViewportScroll() |
1550 { |
1645 { |
1551 WindowEvent e; |
|
1552 Window *w; |
|
1553 |
|
1554 bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); |
1646 bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); |
1555 |
1647 |
1556 if (!_scrolling_viewport) return true; |
1648 if (!_scrolling_viewport) return true; |
1557 |
1649 |
1558 w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); |
1650 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); |
1559 |
1651 |
1560 if (!(_right_button_down || scrollwheel_scrolling) || w == NULL) { |
1652 if (!(_right_button_down || scrollwheel_scrolling) || w == NULL) { |
1561 _cursor.fix_at = false; |
1653 _cursor.fix_at = false; |
1562 _scrolling_viewport = false; |
1654 _scrolling_viewport = false; |
1563 return true; |
1655 return true; |
1564 } |
1656 } |
1565 |
1657 |
1566 if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE && w == FindWindowById(WC_MAIN_WINDOW, 0)) { |
1658 if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) { |
1567 /* If the main window is following a vehicle, then first let go of it! */ |
1659 /* If the main window is following a vehicle, then first let go of it! */ |
1568 const Vehicle *veh = GetVehicle(WP(w, vp_d).follow_vehicle); |
1660 const Vehicle *veh = GetVehicle(w->viewport->follow_vehicle); |
1569 ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */ |
1661 ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */ |
1570 return true; |
1662 return true; |
1571 } |
1663 } |
1572 |
1664 |
1665 Point delta; |
|
1573 if (_patches.reverse_scroll) { |
1666 if (_patches.reverse_scroll) { |
1574 e.we.scroll.delta.x = -_cursor.delta.x; |
1667 delta.x = -_cursor.delta.x; |
1575 e.we.scroll.delta.y = -_cursor.delta.y; |
1668 delta.y = -_cursor.delta.y; |
1576 } else { |
1669 } else { |
1577 e.we.scroll.delta.x = _cursor.delta.x; |
1670 delta.x = _cursor.delta.x; |
1578 e.we.scroll.delta.y = _cursor.delta.y; |
1671 delta.y = _cursor.delta.y; |
1579 } |
1672 } |
1580 |
1673 |
1581 if (scrollwheel_scrolling) { |
1674 if (scrollwheel_scrolling) { |
1582 /* We are using scrollwheels for scrolling */ |
1675 /* We are using scrollwheels for scrolling */ |
1583 e.we.scroll.delta.x = _cursor.h_wheel; |
1676 delta.x = _cursor.h_wheel; |
1584 e.we.scroll.delta.y = _cursor.v_wheel; |
1677 delta.y = _cursor.v_wheel; |
1585 _cursor.v_wheel = 0; |
1678 _cursor.v_wheel = 0; |
1586 _cursor.h_wheel = 0; |
1679 _cursor.h_wheel = 0; |
1587 } |
1680 } |
1588 |
1681 |
1589 /* Create a scroll-event and send it to the window */ |
1682 /* Create a scroll-event and send it to the window */ |
1590 e.event = WE_SCROLL; |
1683 w->OnScroll(delta); |
1591 w->wndproc(w, &e); |
|
1592 |
1684 |
1593 _cursor.delta.x = 0; |
1685 _cursor.delta.x = 0; |
1594 _cursor.delta.y = 0; |
1686 _cursor.delta.y = 0; |
1595 return false; |
1687 return false; |
1596 } |
1688 } |
1604 * @param w Window to bring on-top |
1696 * @param w Window to bring on-top |
1605 * @return false if the window has an active modal child, true otherwise */ |
1697 * @return false if the window has an active modal child, true otherwise */ |
1606 static bool MaybeBringWindowToFront(const Window *w) |
1698 static bool MaybeBringWindowToFront(const Window *w) |
1607 { |
1699 { |
1608 bool bring_to_front = false; |
1700 bool bring_to_front = false; |
1609 Window * const *wz; |
|
1610 |
1701 |
1611 if (w->window_class == WC_MAIN_WINDOW || |
1702 if (w->window_class == WC_MAIN_WINDOW || |
1612 IsVitalWindow(w) || |
1703 IsVitalWindow(w) || |
1613 w->window_class == WC_TOOLTIPS || |
1704 w->window_class == WC_TOOLTIPS || |
1614 w->window_class == WC_DROPDOWN_MENU) { |
1705 w->window_class == WC_DROPDOWN_MENU) { |
1615 return true; |
1706 return true; |
1616 } |
1707 } |
1617 |
1708 |
1618 wz = FindWindowZPosition(w); |
1709 Window * const *wz = FindWindowZPosition(w); |
1619 for (Window * const *uz = wz; ++uz != _last_z_window;) { |
1710 for (Window * const *uz = wz; ++uz != _last_z_window;) { |
1620 Window *u = *uz; |
1711 Window *u = *uz; |
1621 |
1712 |
1622 /* A modal child will prevent the activation of the parent window */ |
1713 /* A modal child will prevent the activation of the parent window */ |
1623 if (u->parent == w && (u->desc_flags & WDF_MODAL)) { |
1714 if (u->parent == w && (u->desc_flags & WDF_MODAL)) { |
1624 u->flags4 |= WF_WHITE_BORDER_MASK; |
1715 u->flags4 |= WF_WHITE_BORDER_MASK; |
1625 SetWindowDirty(u); |
1716 u->SetDirty(); |
1626 return false; |
1717 return false; |
1627 } |
1718 } |
1628 |
1719 |
1629 if (u->window_class == WC_MAIN_WINDOW || |
1720 if (u->window_class == WC_MAIN_WINDOW || |
1630 IsVitalWindow(u) || |
1721 IsVitalWindow(u) || |
1646 |
1737 |
1647 if (bring_to_front) BringWindowToFront(w); |
1738 if (bring_to_front) BringWindowToFront(w); |
1648 return true; |
1739 return true; |
1649 } |
1740 } |
1650 |
1741 |
1651 /** Send a message from one window to another. The receiving window is found by |
|
1652 * @param w Window pointer pointing to the other window |
|
1653 * @param msg Specifies the message to be sent |
|
1654 * @param wparam Specifies additional message-specific information |
|
1655 * @param lparam Specifies additional message-specific information |
|
1656 */ |
|
1657 static void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam) |
|
1658 { |
|
1659 WindowEvent e; |
|
1660 |
|
1661 e.event = WE_MESSAGE; |
|
1662 e.we.message.msg = msg; |
|
1663 e.we.message.wparam = wparam; |
|
1664 e.we.message.lparam = lparam; |
|
1665 |
|
1666 w->wndproc(w, &e); |
|
1667 } |
|
1668 |
|
1669 /** Send a message from one window to another. The receiving window is found by |
|
1670 * @param wnd_class see WindowClass class AND |
|
1671 * @param wnd_num see WindowNumber number, mostly 0 |
|
1672 * @param msg Specifies the message to be sent |
|
1673 * @param wparam Specifies additional message-specific information |
|
1674 * @param lparam Specifies additional message-specific information |
|
1675 */ |
|
1676 void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, int msg, int wparam, int lparam) |
|
1677 { |
|
1678 Window *w = FindWindowById(wnd_class, wnd_num); |
|
1679 if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam); |
|
1680 } |
|
1681 |
|
1682 /** Send a message from one window to another. The message will be sent |
|
1683 * to ALL windows of the windowclass specified in the first parameter |
|
1684 * @param wnd_class see WindowClass class |
|
1685 * @param msg Specifies the message to be sent |
|
1686 * @param wparam Specifies additional message-specific information |
|
1687 * @param lparam Specifies additional message-specific information |
|
1688 */ |
|
1689 void SendWindowMessageClass(WindowClass wnd_class, int msg, int wparam, int lparam) |
|
1690 { |
|
1691 Window* const *wz; |
|
1692 |
|
1693 FOR_ALL_WINDOWS(wz) { |
|
1694 if ((*wz)->window_class == wnd_class) SendWindowMessageW(*wz, msg, wparam, lparam); |
|
1695 } |
|
1696 } |
|
1697 |
|
1698 /** Handle keyboard input. |
1742 /** Handle keyboard input. |
1699 * @param key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode |
1743 * @param raw_key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode |
1700 */ |
1744 */ |
1701 void HandleKeypress(uint32 key) |
1745 void HandleKeypress(uint32 raw_key) |
1702 { |
1746 { |
1703 WindowEvent e; |
|
1704 /* Stores if a window with a textfield for typing is open |
1747 /* Stores if a window with a textfield for typing is open |
1705 * If this is the case, keypress events are only passed to windows with text fields and |
1748 * If this is the case, keypress events are only passed to windows with text fields and |
1706 * to thein this main toolbar. */ |
1749 * to thein this main toolbar. */ |
1707 bool query_open = false; |
1750 bool query_open = false; |
1708 |
1751 |
1716 * can be handled are the 'close application' events |
1759 * can be handled are the 'close application' events |
1717 */ |
1760 */ |
1718 if (!IsGeneratingWorld()) _current_player = _local_player; |
1761 if (!IsGeneratingWorld()) _current_player = _local_player; |
1719 |
1762 |
1720 /* Setup event */ |
1763 /* Setup event */ |
1721 e.event = WE_KEYPRESS; |
1764 uint16 key = GB(raw_key, 0, 16); |
1722 e.we.keypress.key = GB(key, 0, 16); |
1765 uint16 keycode = GB(raw_key, 16, 16); |
1723 e.we.keypress.keycode = GB(key, 16, 16); |
|
1724 e.we.keypress.cont = true; |
|
1725 |
1766 |
1726 /* |
1767 /* |
1727 * The Unicode standard defines an area called the private use area. Code points in this |
1768 * The Unicode standard defines an area called the private use area. Code points in this |
1728 * area are reserved for private use and thus not portable between systems. For instance, |
1769 * area are reserved for private use and thus not portable between systems. For instance, |
1729 * Apple defines code points for the arrow keys in this area, but these are only printable |
1770 * Apple defines code points for the arrow keys in this area, but these are only printable |
1730 * on a system running OS X. We don't want these keys to show up in text fields and such, |
1771 * on a system running OS X. We don't want these keys to show up in text fields and such, |
1731 * and thus we have to clear the unicode character when we encounter such a key. |
1772 * and thus we have to clear the unicode character when we encounter such a key. |
1732 */ |
1773 */ |
1733 if (e.we.keypress.key >= 0xE000 && e.we.keypress.key <= 0xF8FF) e.we.keypress.key = 0; |
1774 if (key >= 0xE000 && key <= 0xF8FF) key = 0; |
1734 |
1775 |
1735 /* |
1776 /* |
1736 * If both key and keycode is zero, we don't bother to process the event. |
1777 * If both key and keycode is zero, we don't bother to process the event. |
1737 */ |
1778 */ |
1738 if (e.we.keypress.key == 0 && e.we.keypress.keycode == 0) return; |
1779 if (key == 0 && keycode == 0) return; |
1739 |
1780 |
1740 /* check if we have a query string window open before allowing hotkeys */ |
1781 /* check if we have a query string window open before allowing hotkeys */ |
1741 if (FindWindowById(WC_QUERY_STRING, 0) != NULL || |
1782 if (FindWindowById(WC_QUERY_STRING, 0) != NULL || |
1742 FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL || |
1783 FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL || |
1743 FindWindowById(WC_GENERATE_LANDSCAPE, 0) != NULL || |
1784 FindWindowById(WC_GENERATE_LANDSCAPE, 0) != NULL || |
1759 w->window_class != WC_CONSOLE && |
1800 w->window_class != WC_CONSOLE && |
1760 w->window_class != WC_SAVELOAD && |
1801 w->window_class != WC_SAVELOAD && |
1761 w->window_class != WC_COMPANY_PASSWORD_WINDOW) { |
1802 w->window_class != WC_COMPANY_PASSWORD_WINDOW) { |
1762 continue; |
1803 continue; |
1763 } |
1804 } |
1764 w->wndproc(w, &e); |
1805 ; |
1765 if (!e.we.keypress.cont) break; |
1806 if (!w->OnKeyPress(key, keycode)) return; |
1766 } |
1807 } |
1767 |
1808 |
1768 if (e.we.keypress.cont) { |
1809 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); |
1769 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); |
1810 /* When there is no toolbar w is null, check for that */ |
1770 /* When there is no toolbar w is null, check for that */ |
1811 if (w != NULL) w->OnKeyPress(key, keycode); |
1771 if (w != NULL) w->wndproc(w, &e); |
|
1772 } |
|
1773 } |
1812 } |
1774 |
1813 |
1775 /** |
1814 /** |
1776 * State of CONTROL key has changed |
1815 * State of CONTROL key has changed |
1777 */ |
1816 */ |
1778 void HandleCtrlChanged() |
1817 void HandleCtrlChanged() |
1779 { |
1818 { |
1780 WindowEvent e; |
|
1781 |
|
1782 e.event = WE_CTRL_CHANGED; |
|
1783 e.we.ctrl.cont = true; |
|
1784 |
|
1785 /* Call the event, start with the uppermost window. */ |
1819 /* Call the event, start with the uppermost window. */ |
1786 for (Window* const *wz = _last_z_window; wz != _z_windows;) { |
1820 for (Window* const *wz = _last_z_window; wz != _z_windows;) { |
1787 Window *w = *--wz; |
1821 Window *w = *--wz; |
1788 w->wndproc(w, &e); |
1822 if (!w->OnCTRLStateChange()) break; |
1789 if (!e.we.ctrl.cont) break; |
|
1790 } |
1823 } |
1791 } |
1824 } |
1792 |
1825 |
1793 /** |
1826 /** |
1794 * Local counter that is incremented each time an mouse input event is detected. |
1827 * Local counter that is incremented each time an mouse input event is detected. |
1802 * If needed and switched on, perform auto scrolling (automatically |
1835 * If needed and switched on, perform auto scrolling (automatically |
1803 * moving window contents when mouse is near edge of the window). |
1836 * moving window contents when mouse is near edge of the window). |
1804 */ |
1837 */ |
1805 static void HandleAutoscroll() |
1838 static void HandleAutoscroll() |
1806 { |
1839 { |
1807 Window *w; |
|
1808 ViewPort *vp; |
|
1809 int x = _cursor.pos.x; |
|
1810 int y = _cursor.pos.y; |
|
1811 |
|
1812 if (_input_events_this_tick != 0) { |
1840 if (_input_events_this_tick != 0) { |
1813 /* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */ |
1841 /* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */ |
1814 _input_events_this_tick = 0; |
1842 _input_events_this_tick = 0; |
1815 /* there were some inputs this tick, don't scroll ??? */ |
1843 /* there were some inputs this tick, don't scroll ??? */ |
1816 return; |
1844 return; |
1817 } |
1845 } |
1818 |
1846 |
1819 if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) { |
1847 if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) { |
1820 w = FindWindowFromPt(x, y); |
1848 int x = _cursor.pos.x; |
1849 int y = _cursor.pos.y; |
|
1850 Window *w = FindWindowFromPt(x, y); |
|
1821 if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return; |
1851 if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return; |
1822 vp = IsPtInWindowViewport(w, x, y); |
1852 ViewPort *vp = IsPtInWindowViewport(w, x, y); |
1823 if (vp != NULL) { |
1853 if (vp != NULL) { |
1824 x -= vp->left; |
1854 x -= vp->left; |
1825 y -= vp->top; |
1855 y -= vp->top; |
1856 |
|
1826 /* here allows scrolling in both x and y axis */ |
1857 /* here allows scrolling in both x and y axis */ |
1827 #define scrollspeed 3 |
1858 #define scrollspeed 3 |
1828 if (x - 15 < 0) { |
1859 if (x - 15 < 0) { |
1829 WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom); |
1860 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom); |
1830 } else if (15 - (vp->width - x) > 0) { |
1861 } else if (15 - (vp->width - x) > 0) { |
1831 WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom); |
1862 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom); |
1832 } |
1863 } |
1833 if (y - 15 < 0) { |
1864 if (y - 15 < 0) { |
1834 WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom); |
1865 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom); |
1835 } else if (15 - (vp->height - y) > 0) { |
1866 } else if (15 - (vp->height - y) > 0) { |
1836 WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom); |
1867 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom); |
1837 } |
1868 } |
1838 #undef scrollspeed |
1869 #undef scrollspeed |
1839 } |
1870 } |
1840 } |
1871 } |
1841 } |
1872 } |
1853 extern void UpdateTileSelection(); |
1884 extern void UpdateTileSelection(); |
1854 extern bool VpHandlePlaceSizingDrag(); |
1885 extern bool VpHandlePlaceSizingDrag(); |
1855 |
1886 |
1856 void MouseLoop(MouseClick click, int mousewheel) |
1887 void MouseLoop(MouseClick click, int mousewheel) |
1857 { |
1888 { |
1858 int x,y; |
|
1859 Window *w; |
|
1860 ViewPort *vp; |
|
1861 bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); |
|
1862 |
|
1863 DecreaseWindowCounters(); |
1889 DecreaseWindowCounters(); |
1864 HandlePlacePresize(); |
1890 HandlePlacePresize(); |
1865 UpdateTileSelection(); |
1891 UpdateTileSelection(); |
1866 if (!VpHandlePlaceSizingDrag()) return; |
1892 if (!VpHandlePlaceSizingDrag()) return; |
1867 if (!HandleDragDrop()) return; |
1893 if (!HandleDragDrop()) return; |
1868 if (!HandlePopupMenu()) return; |
|
1869 if (!HandleWindowDragging()) return; |
1894 if (!HandleWindowDragging()) return; |
1870 if (!HandleScrollbarScrolling()) return; |
1895 if (!HandleScrollbarScrolling()) return; |
1871 if (!HandleViewportScroll()) return; |
1896 if (!HandleViewportScroll()) return; |
1872 if (!HandleMouseOver()) return; |
1897 if (!HandleMouseOver()) return; |
1873 |
1898 |
1874 x = _cursor.pos.x; |
1899 bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); |
1875 y = _cursor.pos.y; |
|
1876 |
|
1877 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return; |
1900 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return; |
1878 |
1901 |
1879 w = FindWindowFromPt(x, y); |
1902 int x = _cursor.pos.x; |
1903 int y = _cursor.pos.y; |
|
1904 Window *w = FindWindowFromPt(x, y); |
|
1880 if (w == NULL) return; |
1905 if (w == NULL) return; |
1906 |
|
1881 if (!MaybeBringWindowToFront(w)) return; |
1907 if (!MaybeBringWindowToFront(w)) return; |
1882 vp = IsPtInWindowViewport(w, x, y); |
1908 ViewPort *vp = IsPtInWindowViewport(w, x, y); |
1883 |
1909 |
1884 /* Don't allow any action in a viewport if either in menu of in generating world */ |
1910 /* Don't allow any action in a viewport if either in menu of in generating world */ |
1885 if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return; |
1911 if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return; |
1886 |
1912 |
1887 if (mousewheel != 0) { |
1913 if (mousewheel != 0) { |
1888 if (_patches.scrollwheel_scrolling == 0) { |
1914 if (_patches.scrollwheel_scrolling == 0) { |
1889 /* Scrollwheel is in zoom mode. Make the zoom event. */ |
1915 /* Send mousewheel event to window */ |
1890 WindowEvent e; |
1916 w->OnMouseWheel(mousewheel); |
1891 |
|
1892 /* Send WE_MOUSEWHEEL event to window */ |
|
1893 e.event = WE_MOUSEWHEEL; |
|
1894 e.we.wheel.wheel = mousewheel; |
|
1895 w->wndproc(w, &e); |
|
1896 } |
1917 } |
1897 |
1918 |
1898 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */ |
1919 /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */ |
1899 if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel); |
1920 if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel); |
1900 } |
1921 } |
1931 default: |
1952 default: |
1932 break; |
1953 break; |
1933 } |
1954 } |
1934 } else { |
1955 } else { |
1935 switch (click) { |
1956 switch (click) { |
1936 case MC_DOUBLE_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, true); |
1957 case MC_DOUBLE_LEFT: |
1937 /* fallthough, and also give a single-click for backwards compatible */ |
1958 DispatchLeftClickEvent(w, x - w->left, y - w->top, true); |
1938 case MC_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, false); break; |
1959 if (_mouseover_last_w == NULL) break; // The window got removed. |
1960 /* fallthough, and also give a single-click for backwards compatibility */ |
|
1961 case MC_LEFT: |
|
1962 DispatchLeftClickEvent(w, x - w->left, y - w->top, false); |
|
1963 break; |
|
1964 |
|
1939 default: |
1965 default: |
1940 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break; |
1966 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break; |
1941 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons. |
1967 /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons. |
1942 * Simulate a right button click so we can get started. */ |
1968 * Simulate a right button click so we can get started. */ |
1943 /* fallthough */ |
1969 /* fallthough */ |
1952 void HandleMouseEvents() |
1978 void HandleMouseEvents() |
1953 { |
1979 { |
1954 static int double_click_time = 0; |
1980 static int double_click_time = 0; |
1955 static int double_click_x = 0; |
1981 static int double_click_x = 0; |
1956 static int double_click_y = 0; |
1982 static int double_click_y = 0; |
1957 MouseClick click; |
|
1958 int mousewheel; |
|
1959 |
1983 |
1960 /* |
1984 /* |
1961 * During the generation of the world, there might be |
1985 * During the generation of the world, there might be |
1962 * another thread that is currently building for example |
1986 * another thread that is currently building for example |
1963 * a road. To not interfere with those tasks, we should |
1987 * a road. To not interfere with those tasks, we should |
1967 * can be handled are the 'close application' events |
1991 * can be handled are the 'close application' events |
1968 */ |
1992 */ |
1969 if (!IsGeneratingWorld()) _current_player = _local_player; |
1993 if (!IsGeneratingWorld()) _current_player = _local_player; |
1970 |
1994 |
1971 /* Mouse event? */ |
1995 /* Mouse event? */ |
1972 click = MC_NONE; |
1996 MouseClick click = MC_NONE; |
1973 if (_left_button_down && !_left_button_clicked) { |
1997 if (_left_button_down && !_left_button_clicked) { |
1974 click = MC_LEFT; |
1998 click = MC_LEFT; |
1975 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK && |
1999 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK && |
1976 double_click_x != 0 && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK && |
2000 double_click_x != 0 && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK && |
1977 double_click_y != 0 && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) { |
2001 double_click_y != 0 && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) { |
1986 _right_button_clicked = false; |
2010 _right_button_clicked = false; |
1987 click = MC_RIGHT; |
2011 click = MC_RIGHT; |
1988 _input_events_this_tick++; |
2012 _input_events_this_tick++; |
1989 } |
2013 } |
1990 |
2014 |
1991 mousewheel = 0; |
2015 int mousewheel = 0; |
1992 if (_cursor.wheel) { |
2016 if (_cursor.wheel) { |
1993 mousewheel = _cursor.wheel; |
2017 mousewheel = _cursor.wheel; |
1994 _cursor.wheel = 0; |
2018 _cursor.wheel = 0; |
1995 _input_events_this_tick++; |
2019 _input_events_this_tick++; |
1996 } |
2020 } |
2016 static int we4_timer = 0; |
2040 static int we4_timer = 0; |
2017 int t = we4_timer + 1; |
2041 int t = we4_timer + 1; |
2018 |
2042 |
2019 if (t >= 100) { |
2043 if (t >= 100) { |
2020 for (wz = _last_z_window; wz != _z_windows;) { |
2044 for (wz = _last_z_window; wz != _z_windows;) { |
2021 CallWindowEventNP(*--wz, WE_4); |
2045 (*--wz)->OnHundredthTick(); |
2022 } |
2046 } |
2023 t = 0; |
2047 t = 0; |
2024 } |
2048 } |
2025 we4_timer = t; |
2049 we4_timer = t; |
2026 |
2050 |
2027 for (wz = _last_z_window; wz != _z_windows;) { |
2051 for (wz = _last_z_window; wz != _z_windows;) { |
2028 Window *w = *--wz; |
2052 Window *w = *--wz; |
2029 if (w->flags4 & WF_WHITE_BORDER_MASK) { |
2053 if (w->flags4 & WF_WHITE_BORDER_MASK) { |
2030 w->flags4 -= WF_WHITE_BORDER_ONE; |
2054 w->flags4 -= WF_WHITE_BORDER_ONE; |
2031 |
2055 |
2032 if (!(w->flags4 & WF_WHITE_BORDER_MASK)) SetWindowDirty(w); |
2056 if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty(); |
2033 } |
2057 } |
2034 } |
2058 } |
2035 |
2059 |
2036 DrawDirtyBlocks(); |
2060 DrawDirtyBlocks(); |
2037 |
2061 |
2041 DrawChatMessage(); |
2065 DrawChatMessage(); |
2042 /* Redraw mouse cursor in case it was hidden */ |
2066 /* Redraw mouse cursor in case it was hidden */ |
2043 DrawMouseCursor(); |
2067 DrawMouseCursor(); |
2044 } |
2068 } |
2045 |
2069 |
2046 |
|
2047 /** |
|
2048 * In a window with menu_d custom extension, retrieve the menu item number from a position |
|
2049 * @param w Window holding the menu items |
|
2050 * @param x X coordinate of the position |
|
2051 * @param y Y coordinate of the position |
|
2052 * @return Index number of the menu item, or \c -1 if no valid selection under position |
|
2053 */ |
|
2054 int GetMenuItemIndex(const Window *w, int x, int y) |
|
2055 { |
|
2056 if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) { |
|
2057 y /= 10; |
|
2058 |
|
2059 if (y < WP(w, const menu_d).item_count && |
|
2060 !HasBit(WP(w, const menu_d).disabled_items, y)) { |
|
2061 return y; |
|
2062 } |
|
2063 } |
|
2064 return -1; |
|
2065 } |
|
2066 |
|
2067 /** |
2070 /** |
2068 * Mark window as dirty (in need of repainting) |
2071 * Mark window as dirty (in need of repainting) |
2069 * @param cls Window class |
2072 * @param cls Window class |
2070 * @param number Window number in that class |
2073 * @param number Window number in that class |
2071 */ |
2074 */ |
2073 { |
2076 { |
2074 Window* const *wz; |
2077 Window* const *wz; |
2075 |
2078 |
2076 FOR_ALL_WINDOWS(wz) { |
2079 FOR_ALL_WINDOWS(wz) { |
2077 const Window *w = *wz; |
2080 const Window *w = *wz; |
2078 if (w->window_class == cls && w->window_number == number) SetWindowDirty(w); |
2081 if (w->window_class == cls && w->window_number == number) w->SetDirty(); |
2079 } |
2082 } |
2080 } |
2083 } |
2081 |
2084 |
2082 /** |
2085 /** |
2083 * Mark a particular widget in a particular window as dirty (in need of repainting) |
2086 * Mark a particular widget in a particular window as dirty (in need of repainting) |
2104 void InvalidateWindowClasses(WindowClass cls) |
2107 void InvalidateWindowClasses(WindowClass cls) |
2105 { |
2108 { |
2106 Window* const *wz; |
2109 Window* const *wz; |
2107 |
2110 |
2108 FOR_ALL_WINDOWS(wz) { |
2111 FOR_ALL_WINDOWS(wz) { |
2109 if ((*wz)->window_class == cls) SetWindowDirty(*wz); |
2112 if ((*wz)->window_class == cls) (*wz)->SetDirty(); |
2110 } |
2113 } |
2111 } |
2114 } |
2112 |
2115 |
2113 /** |
2116 /** |
2114 * Mark window data as invalid (in need of re-computing) |
2117 * Mark window data as invalid (in need of re-computing) |
2115 * @param w Window with invalid data |
2118 * @param w Window with invalid data |
2116 */ |
2119 */ |
2117 void InvalidateThisWindowData(Window *w) |
2120 void InvalidateThisWindowData(Window *w, int data) |
2118 { |
2121 { |
2119 CallWindowEventNP(w, WE_INVALIDATE_DATA); |
2122 w->OnInvalidateData(data); |
2120 SetWindowDirty(w); |
2123 w->SetDirty(); |
2121 } |
2124 } |
2122 |
2125 |
2123 /** |
2126 /** |
2124 * Mark window data of the window of a given class and specific window number as invalid (in need of re-computing) |
2127 * Mark window data of the window of a given class and specific window number as invalid (in need of re-computing) |
2125 * @param cls Window class |
2128 * @param cls Window class |
2126 * @param number Window number within the class |
2129 * @param number Window number within the class |
2127 */ |
2130 */ |
2128 void InvalidateWindowData(WindowClass cls, WindowNumber number) |
2131 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data) |
2129 { |
2132 { |
2130 Window* const *wz; |
2133 Window* const *wz; |
2131 |
2134 |
2132 FOR_ALL_WINDOWS(wz) { |
2135 FOR_ALL_WINDOWS(wz) { |
2133 Window *w = *wz; |
2136 Window *w = *wz; |
2134 if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w); |
2137 if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w, data); |
2135 } |
2138 } |
2136 } |
2139 } |
2137 |
2140 |
2138 /** |
2141 /** |
2139 * Mark window data of all windows of a given class as invalid (in need of re-computing) |
2142 * Mark window data of all windows of a given class as invalid (in need of re-computing) |
2140 * @param cls Window class |
2143 * @param cls Window class |
2141 */ |
2144 */ |
2142 void InvalidateWindowClassesData(WindowClass cls) |
2145 void InvalidateWindowClassesData(WindowClass cls, int data) |
2143 { |
2146 { |
2144 Window* const *wz; |
2147 Window* const *wz; |
2145 |
2148 |
2146 FOR_ALL_WINDOWS(wz) { |
2149 FOR_ALL_WINDOWS(wz) { |
2147 if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz); |
2150 if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz, data); |
2148 } |
2151 } |
2149 } |
2152 } |
2150 |
2153 |
2151 /** |
2154 /** |
2152 * Dispatch WE_TICK event over all windows |
2155 * Dispatch WE_TICK event over all windows |
2153 */ |
2156 */ |
2154 void CallWindowTickEvent() |
2157 void CallWindowTickEvent() |
2155 { |
2158 { |
2156 for (Window * const *wz = _last_z_window; wz != _z_windows;) { |
2159 for (Window * const *wz = _last_z_window; wz != _z_windows;) { |
2157 CallWindowEventNP(*--wz, WE_TICK); |
2160 (*--wz)->OnTick(); |
2158 } |
2161 } |
2159 } |
2162 } |
2160 |
2163 |
2161 /** |
2164 /** |
2162 * Try to delete a non-vital window. |
2165 * Try to delete a non-vital window. |
2180 w->window_class != WC_STATUS_BAR && |
2183 w->window_class != WC_STATUS_BAR && |
2181 w->window_class != WC_TOOLBAR_MENU && |
2184 w->window_class != WC_TOOLBAR_MENU && |
2182 w->window_class != WC_TOOLTIPS && |
2185 w->window_class != WC_TOOLTIPS && |
2183 (w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned' |
2186 (w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned' |
2184 |
2187 |
2185 DeleteWindow(w); |
2188 delete w; |
2186 goto restart_search; |
2189 goto restart_search; |
2187 } |
2190 } |
2188 } |
2191 } |
2189 } |
2192 } |
2190 |
2193 |
2204 /* When we find the window to delete, we need to restart the search |
2207 /* When we find the window to delete, we need to restart the search |
2205 * as deleting this window could cascade in deleting (many) others |
2208 * as deleting this window could cascade in deleting (many) others |
2206 * anywhere in the z-array */ |
2209 * anywhere in the z-array */ |
2207 FOR_ALL_WINDOWS(wz) { |
2210 FOR_ALL_WINDOWS(wz) { |
2208 if ((*wz)->flags4 & WF_STICKY) { |
2211 if ((*wz)->flags4 & WF_STICKY) { |
2209 DeleteWindow(*wz); |
2212 delete *wz; |
2210 goto restart_search; |
2213 goto restart_search; |
2211 } |
2214 } |
2212 } |
2215 } |
2213 } |
2216 } |
2214 |
2217 |
2269 switch (w->window_class) { |
2272 switch (w->window_class) { |
2270 case WC_MAIN_TOOLBAR: |
2273 case WC_MAIN_TOOLBAR: |
2271 if (neww - w->width != 0) { |
2274 if (neww - w->width != 0) { |
2272 ResizeWindow(w, min(neww, 640) - w->width, 0); |
2275 ResizeWindow(w, min(neww, 640) - w->width, 0); |
2273 |
2276 |
2274 WindowEvent e; |
2277 Point size; |
2275 e.event = WE_RESIZE; |
2278 Point delta; |
2276 e.we.sizing.size.x = w->width; |
2279 size.x = w->width; |
2277 e.we.sizing.size.y = w->height; |
2280 size.y = w->height; |
2278 e.we.sizing.diff.x = neww - w->width; |
2281 delta.x = neww - w->width; |
2279 e.we.sizing.diff.y = 0; |
2282 delta.y = 0; |
2280 w->wndproc(w, &e); |
2283 w->OnResize(size, delta); |
2281 } |
2284 } |
2282 |
2285 |
2283 top = w->top; |
2286 top = w->top; |
2284 left = PositionMainToolbar(w); // changes toolbar orientation |
2287 left = PositionMainToolbar(w); // changes toolbar orientation |
2285 break; |
2288 break; |
2337 |
2340 |
2338 w->left = left; |
2341 w->left = left; |
2339 w->top = top; |
2342 w->top = top; |
2340 } |
2343 } |
2341 } |
2344 } |
2345 |
|
2346 /** Destructor of the base class PickerWindowBase |
|
2347 * Main utility is to stop the base Window destructor from triggering |
|
2348 * a free while the child will already be free, in this case by the ResetObjectToPlace(). |
|
2349 */ |
|
2350 PickerWindowBase::~PickerWindowBase() |
|
2351 { |
|
2352 this->window_class = WC_INVALID; // stop the ancestor from freeing the already (to be) child |
|
2353 ResetObjectToPlace(); |
|
2354 } |