2005-07-24 16:12:37 +02:00
/* $Id$ */
2009-08-21 22:21:05 +02:00
/*
* This file is part of OpenTTD .
* OpenTTD is free software ; you can redistribute it and / or modify it under the terms of the GNU General Public License as published by the Free Software Foundation , version 2.
* OpenTTD is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with OpenTTD . If not , see < http : //www.gnu.org/licenses/>.
*/
2008-05-06 17:11:33 +02:00
/** @file window.cpp Windowing system, widgets and events */
2007-02-23 02:48:53 +01:00
2004-08-09 19:04:08 +02:00
# include "stdafx.h"
2006-10-06 23:10:14 +02:00
# include <stdarg.h>
2008-09-30 22:51:04 +02:00
# include "company_func.h"
2008-01-09 10:45:45 +01:00
# include "gfx_func.h"
2008-05-24 12:15:06 +02:00
# include "console_func.h"
# include "console_gui.h"
2008-01-09 10:45:45 +01:00
# include "viewport_func.h"
2011-08-21 14:48:46 +02:00
# include "progress.h"
2007-06-17 22:30:28 +02:00
# include "blitter/factory.hpp"
2007-12-23 11:56:02 +01:00
# include "zoom_func.h"
2007-12-27 14:35:39 +01:00
# include "vehicle_base.h"
2008-05-07 00:17:12 +02:00
# include "window_func.h"
2008-05-07 15:10:15 +02:00
# include "tilehighlight_func.h"
2008-08-12 00:45:11 +02:00
# include "network/network.h"
2009-01-03 14:59:05 +01:00
# include "querystring_gui.h"
2009-02-01 23:32:07 +01:00
# include "widgets/dropdown_func.h"
2009-11-16 22:28:12 +01:00
# include "strings_func.h"
2010-01-15 17:41:15 +01:00
# include "settings_type.h"
2013-05-26 21:27:22 +02:00
# include "settings_func.h"
# include "ini_type.h"
2010-04-26 22:35:27 +02:00
# include "newgrf_debug.h"
2010-07-03 23:43:44 +02:00
# include "hotkeys.h"
2010-09-15 23:10:14 +02:00
# include "toolbar_gui.h"
2010-11-14 00:40:36 +01:00
# include "statusbar_gui.h"
2011-12-10 16:16:58 +01:00
# include "error.h"
2011-12-19 22:05:14 +01:00
# include "game/game.hpp"
2013-08-05 22:37:02 +02:00
# include "video/video_driver.hpp"
2018-07-19 21:17:07 +02:00
# include "framerate_type.h"
2018-05-10 22:49:52 +02:00
# include "network/network_func.h"
2018-05-20 10:58:36 +02:00
# include "guitimer_func.h"
2019-02-17 00:15:58 +01:00
# include "news_func.h"
2004-08-09 19:04:08 +02:00
2014-04-23 22:13:33 +02:00
# include "safeguards.h"
2012-10-13 11:16:20 +02:00
/** Values for _settings_client.gui.auto_scrolling */
enum ViewportAutoscrolling {
VA_DISABLED , //!< Do not autoscroll when mouse is at edge of viewport.
VA_MAIN_VIEWPORT_FULLSCREEN , //!< Scroll main viewport at edge when using fullscreen.
VA_MAIN_VIEWPORT , //!< Scroll main viewport at edge.
VA_EVERY_VIEWPORT , //!< Scroll all viewports at their edges.
} ;
2008-01-13 02:21:35 +01:00
2008-04-13 21:06:30 +02:00
static Point _drag_delta ; ///< delta between mouse cursor and upper left corner of dragged window
2018-10-28 03:17:36 +01:00
static Window * _mouseover_last_w = NULL ; ///< Window of the last OnMouseOver event.
2011-01-18 22:08:19 +01:00
static Window * _last_scroll_window = NULL ; ///< Window of the last scroll event.
2004-11-10 22:14:16 +01:00
2009-01-07 17:11:27 +01:00
/** List of windows opened at the screen sorted from the front. */
Window * _z_front_window = NULL ;
/** List of windows opened at the screen sorted from the back. */
Window * _z_back_window = NULL ;
2006-11-18 17:47:02 +01:00
2011-12-19 22:05:14 +01:00
/** If false, highlight is white, otherwise the by the widget defined colour. */
bool _window_highlight_colour = false ;
2009-02-09 02:22:29 +01:00
/*
2010-05-30 15:05:36 +02:00
* Window that currently has focus . - The main purpose is to generate
2010-07-31 23:02:56 +02:00
* # FocusLost events , not to give next window in z - order focus when a
2009-02-09 02:22:29 +01:00
* window is closed .
*/
Window * _focused_window ;
2008-01-13 14:36:01 +01:00
Point _cursorpos_drag_start ;
int _scrollbar_start_pos ;
int _scrollbar_size ;
2010-08-20 13:55:10 +02:00
byte _scroller_click_timeout = 0 ;
2008-01-13 14:36:01 +01:00
2010-05-30 15:16:51 +02:00
bool _scrolling_viewport ; ///< A viewport is being scrolled with the mouse.
2010-07-11 12:53:07 +02:00
bool _mouse_hovering ; ///< The mouse is hovering over the same point.
2008-01-13 14:36:01 +01:00
2010-05-30 12:36:32 +02:00
SpecialMouseMode _special_mouse_mode ; ///< Mode of the mouse.
2008-01-13 14:36:01 +01:00
2013-05-26 21:27:22 +02:00
/**
* List of all WindowDescs .
* This is a pointer to ensure initialisation order with the various static WindowDesc instances .
*/
static SmallVector < WindowDesc * , 16 > * _window_descs = NULL ;
/** Config file to store WindowDesc */
char * _windows_file ;
2009-03-15 16:12:06 +01:00
/** Window description constructor. */
2015-02-13 22:25:48 +01:00
WindowDesc : : WindowDesc ( WindowPosition def_pos , const char * ini_key , int16 def_width_trad , int16 def_height_trad ,
2009-11-15 11:26:01 +01:00
WindowClass window_class , WindowClass parent_class , uint32 flags ,
2013-06-15 17:30:16 +02:00
const NWidgetPart * nwid_parts , int16 nwid_length , HotkeyList * hotkeys ) :
2009-11-28 15:42:35 +01:00
default_pos ( def_pos ) ,
2009-11-17 20:16:48 +01:00
cls ( window_class ) ,
parent_cls ( parent_class ) ,
2013-05-26 21:25:01 +02:00
ini_key ( ini_key ) ,
2009-11-17 20:16:48 +01:00
flags ( flags ) ,
nwid_parts ( nwid_parts ) ,
2013-05-26 21:27:44 +02:00
nwid_length ( nwid_length ) ,
2013-06-15 17:30:16 +02:00
hotkeys ( hotkeys ) ,
2013-05-26 21:30:31 +02:00
pref_sticky ( false ) ,
pref_width ( 0 ) ,
2015-02-13 22:25:48 +01:00
pref_height ( 0 ) ,
default_width_trad ( def_width_trad ) ,
default_height_trad ( def_height_trad )
2009-03-15 16:12:06 +01:00
{
2013-05-26 21:27:22 +02:00
if ( _window_descs = = NULL ) _window_descs = new SmallVector < WindowDesc * , 16 > ( ) ;
* _window_descs - > Append ( ) = this ;
2009-03-22 22:15:45 +01:00
}
WindowDesc : : ~ WindowDesc ( )
{
2013-06-15 17:06:22 +02:00
_window_descs - > Erase ( _window_descs - > Find ( this ) ) ;
2009-03-22 22:15:45 +01:00
}
2009-03-15 16:12:06 +01:00
2015-02-13 22:25:48 +01:00
/**
* Determine default width of window .
* This is either a stored user preferred size , or the build - in default .
* @ return Width in pixels .
*/
int16 WindowDesc : : GetDefaultWidth ( ) const
{
return this - > pref_width ! = 0 ? this - > pref_width : ScaleGUITrad ( this - > default_width_trad ) ;
}
/**
* Determine default height of window .
* This is either a stored user preferred size , or the build - in default .
* @ return Height in pixels .
*/
int16 WindowDesc : : GetDefaultHeight ( ) const
{
return this - > pref_height ! = 0 ? this - > pref_height : ScaleGUITrad ( this - > default_height_trad ) ;
}
2013-05-26 21:27:22 +02:00
/**
* Load all WindowDesc settings from _windows_file .
*/
void WindowDesc : : LoadFromConfig ( )
{
IniFile * ini = new IniFile ( ) ;
2017-03-20 20:30:49 +01:00
ini - > LoadFromDisk ( _windows_file , NO_DIRECTORY ) ;
2013-05-26 21:27:22 +02:00
for ( WindowDesc * * it = _window_descs - > Begin ( ) ; it ! = _window_descs - > End ( ) ; + + it ) {
if ( ( * it ) - > ini_key = = NULL ) continue ;
IniLoadWindowSettings ( ini , ( * it ) - > ini_key , * it ) ;
}
delete ini ;
}
/**
* Sort WindowDesc by ini_key .
*/
2013-05-26 22:17:07 +02:00
static int CDECL DescSorter ( WindowDesc * const * a , WindowDesc * const * b )
2013-05-26 21:27:22 +02:00
{
if ( ( * a ) - > ini_key ! = NULL & & ( * b ) - > ini_key ! = NULL ) return strcmp ( ( * a ) - > ini_key , ( * b ) - > ini_key ) ;
return ( ( * b ) - > ini_key ! = NULL ? 1 : 0 ) - ( ( * a ) - > ini_key ! = NULL ? 1 : 0 ) ;
}
/**
* Save all WindowDesc settings to _windows_file .
*/
void WindowDesc : : SaveToConfig ( )
{
/* Sort the stuff to get a nice ini file on first write */
QSortT ( _window_descs - > Begin ( ) , _window_descs - > Length ( ) , DescSorter ) ;
IniFile * ini = new IniFile ( ) ;
2017-03-20 20:30:49 +01:00
ini - > LoadFromDisk ( _windows_file , NO_DIRECTORY ) ;
2013-05-26 21:27:22 +02:00
for ( WindowDesc * * it = _window_descs - > Begin ( ) ; it ! = _window_descs - > End ( ) ; + + it ) {
if ( ( * it ) - > ini_key = = NULL ) continue ;
IniSaveWindowSettings ( ini , ( * it ) - > ini_key , * it ) ;
}
ini - > SaveToDisk ( _windows_file ) ;
delete ini ;
}
2013-05-26 21:27:44 +02:00
/**
* Read default values from WindowDesc configuration an apply them to the window .
*/
void Window : : ApplyDefaults ( )
{
if ( this - > nested_root ! = NULL & & this - > nested_root - > GetWidgetOfType ( WWT_STICKYBOX ) ! = NULL ) {
if ( this - > window_desc - > pref_sticky ) this - > flags | = WF_STICKY ;
} else {
/* There is no stickybox; clear the preference in case someone tried to be funny */
this - > window_desc - > pref_sticky = false ;
}
}
2010-07-26 15:02:28 +02:00
/**
* Compute the row of a widget that a user clicked in .
* @ param clickpos Vertical position of the mouse click .
* @ param widget Widget number of the widget clicked in .
* @ param padding Amount of empty space between the widget edge and the top of the first row .
2010-07-26 15:03:40 +02:00
* @ param line_height Height of a single row . A negative value means using the vertical resize step of the widget .
2010-07-26 15:02:28 +02:00
* @ return Row number clicked at . If clicked at a wrong position , # INT_MAX is returned .
* @ note The widget does not know where a list printed at the widget ends , so below a list is not a wrong position .
*/
int Window : : GetRowFromWidget ( int clickpos , int widget , int padding , int line_height ) const
{
const NWidgetBase * wid = this - > GetWidget < NWidgetBase > ( widget ) ;
2010-07-26 15:03:40 +02:00
if ( line_height < 0 ) line_height = wid - > resize_y ;
2010-07-26 15:02:28 +02:00
if ( clickpos < ( int ) wid - > pos_y + padding ) return INT_MAX ;
return ( clickpos - ( int ) wid - > pos_y - padding ) / line_height ;
}
2011-12-19 22:05:14 +01:00
/**
* Disable the highlighted status of all widgets .
*/
void Window : : DisableAllWidgetHighlight ( )
{
for ( uint i = 0 ; i < this - > nested_array_size ; i + + ) {
NWidgetBase * nwid = this - > GetWidget < NWidgetBase > ( i ) ;
if ( nwid = = NULL ) continue ;
if ( nwid - > IsHighlighted ( ) ) {
nwid - > SetHighlighted ( TC_INVALID ) ;
this - > SetWidgetDirty ( i ) ;
}
}
CLRBITS ( this - > flags , WF_HIGHLIGHTED ) ;
}
/**
* Sets the highlighted status of a widget .
* @ param widget_index index of this widget in the window
* @ param highlighted_colour Colour of highlight , or TC_INVALID to disable .
*/
void Window : : SetWidgetHighlight ( byte widget_index , TextColour highlighted_colour )
{
assert ( widget_index < this - > nested_array_size ) ;
NWidgetBase * nwid = this - > GetWidget < NWidgetBase > ( widget_index ) ;
if ( nwid = = NULL ) return ;
nwid - > SetHighlighted ( highlighted_colour ) ;
this - > SetWidgetDirty ( widget_index ) ;
if ( highlighted_colour ! = TC_INVALID ) {
/* If we set a highlight, the window has a highlight */
this - > flags | = WF_HIGHLIGHTED ;
} else {
/* If we disable a highlight, check all widgets if anyone still has a highlight */
bool valid = false ;
for ( uint i = 0 ; i < this - > nested_array_size ; i + + ) {
NWidgetBase * nwid = this - > GetWidget < NWidgetBase > ( i ) ;
if ( nwid = = NULL ) continue ;
if ( ! nwid - > IsHighlighted ( ) ) continue ;
valid = true ;
}
/* If nobody has a highlight, disable the flag on the window */
if ( ! valid ) CLRBITS ( this - > flags , WF_HIGHLIGHTED ) ;
}
}
/**
* Gets the highlighted status of a widget .
* @ param widget_index index of this widget in the window
* @ return status of the widget ie : highlighted = true , not highlighted = false
*/
bool Window : : IsWidgetHighlighted ( byte widget_index ) const
{
assert ( widget_index < this - > nested_array_size ) ;
const NWidgetBase * nwid = this - > GetWidget < NWidgetBase > ( widget_index ) ;
if ( nwid = = NULL ) return false ;
return nwid - > IsHighlighted ( ) ;
}
2012-06-01 12:42:46 +02:00
/**
* A dropdown window associated to this window has been closed .
* @ param pt the point inside the window the mouse resides on after closure .
* @ param widget the widget ( button ) that the dropdown is associated with .
* @ param index the element in the dropdown that is selected .
* @ param instant_close whether the dropdown was configured to close on mouse up .
*/
void Window : : OnDropdownClose ( Point pt , int widget , int index , bool instant_close )
{
if ( widget < 0 ) return ;
if ( instant_close ) {
/* Send event for selected option if we're still
* on the parent button of the dropdown ( behaviour of the dropdowns in the main toolbar ) . */
if ( GetWidgetFromPos ( this , pt . x , pt . y ) = = widget ) {
this - > OnDropdownSelect ( widget , index ) ;
}
}
/* Raise the dropdown button */
2013-11-26 14:23:31 +01:00
NWidgetCore * nwi2 = this - > GetWidget < NWidgetCore > ( widget ) ;
if ( ( nwi2 - > type & WWT_MASK ) = = NWID_BUTTON_DROPDOWN ) {
nwi2 - > disp_flags & = ~ ND_DROPDOWN_ACTIVE ;
2012-06-01 12:42:46 +02:00
} else {
this - > RaiseWidget ( widget ) ;
}
this - > SetWidgetDirty ( widget ) ;
}
2010-08-12 08:40:28 +02:00
/**
* Return the Scrollbar to a widget index .
* @ param widnum Scrollbar widget index
* @ return Scrollbar to the widget
*/
const Scrollbar * Window : : GetScrollbar ( uint widnum ) const
{
2010-08-12 11:13:04 +02:00
return this - > GetWidget < NWidgetScrollbar > ( widnum ) ;
2010-08-12 08:40:28 +02:00
}
/**
* Return the Scrollbar to a widget index .
* @ param widnum Scrollbar widget index
* @ return Scrollbar to the widget
*/
Scrollbar * Window : : GetScrollbar ( uint widnum )
{
2010-08-12 11:13:04 +02:00
return this - > GetWidget < NWidgetScrollbar > ( widnum ) ;
2010-08-12 08:40:28 +02:00
}
2012-11-14 23:50:35 +01:00
/**
* Return the querystring associated to a editbox .
* @ param widnum Editbox widget index
* @ return QueryString or NULL .
*/
const QueryString * Window : : GetQueryString ( uint widnum ) const
{
const SmallMap < int , QueryString * > : : Pair * query = this - > querystrings . Find ( widnum ) ;
return query ! = this - > querystrings . End ( ) ? query - > second : NULL ;
}
/**
* Return the querystring associated to a editbox .
* @ param widnum Editbox widget index
* @ return QueryString or NULL .
*/
QueryString * Window : : GetQueryString ( uint widnum )
{
SmallMap < int , QueryString * > : : Pair * query = this - > querystrings . Find ( widnum ) ;
return query ! = this - > querystrings . End ( ) ? query - > second : NULL ;
}
2013-08-05 22:37:44 +02:00
/**
* Get the current input text if an edit box has the focus .
* @ return The currently focused input text or NULL if no input focused .
*/
/* virtual */ const char * Window : : GetFocusedText ( ) const
{
if ( this - > nested_focus ! = NULL & & this - > nested_focus - > type = = WWT_EDITBOX ) {
return this - > GetQueryString ( this - > nested_focus - > index ) - > GetText ( ) ;
}
return NULL ;
}
/**
* Get the string at the caret if an edit box has the focus .
* @ return The text at the caret or NULL if no edit box is focused .
*/
/* virtual */ const char * Window : : GetCaret ( ) const
{
if ( this - > nested_focus ! = NULL & & this - > nested_focus - > type = = WWT_EDITBOX ) {
return this - > GetQueryString ( this - > nested_focus - > index ) - > GetCaret ( ) ;
}
return NULL ;
}
/**
* Get the range of the currently marked input text .
* @ param [ out ] length Length of the marked text .
* @ return Pointer to the start of the marked text or NULL if no text is marked .
*/
/* virtual */ const char * Window : : GetMarkedText ( size_t * length ) const
{
if ( this - > nested_focus ! = NULL & & this - > nested_focus - > type = = WWT_EDITBOX ) {
return this - > GetQueryString ( this - > nested_focus - > index ) - > GetMarkedText ( length ) ;
}
return NULL ;
}
2013-08-05 22:37:14 +02:00
/**
* Get the current caret position if an edit box has the focus .
* @ return Top - left location of the caret , relative to the window .
*/
/* virtual */ Point Window : : GetCaretPosition ( ) const
{
if ( this - > nested_focus ! = NULL & & this - > nested_focus - > type = = WWT_EDITBOX ) {
return this - > GetQueryString ( this - > nested_focus - > index ) - > GetCaretPosition ( this , this - > nested_focus - > index ) ;
}
Point pt = { 0 , 0 } ;
return pt ;
}
2013-08-05 22:37:48 +02:00
/**
* Get the bounding rectangle for a text range if an edit box has the focus .
* @ param from Start of the string range .
* @ param to End of the string range .
* @ return Rectangle encompassing the string range , relative to the window .
*/
/* virtual */ Rect Window : : GetTextBoundingRect ( const char * from , const char * to ) const
{
if ( this - > nested_focus ! = NULL & & this - > nested_focus - > type = = WWT_EDITBOX ) {
return this - > GetQueryString ( this - > nested_focus - > index ) - > GetBoundingRect ( this , this - > nested_focus - > index , from , to ) ;
}
Rect r = { 0 , 0 , 0 , 0 } ;
return r ;
}
2013-08-05 22:37:53 +02:00
/**
* Get the character that is rendered at a position by the focused edit box .
* @ param pt The position to test .
* @ return Pointer to the character at the position or NULL if no character is at the position .
*/
/* virtual */ const char * Window : : GetTextCharacterAtPosition ( const Point & pt ) const
{
if ( this - > nested_focus ! = NULL & & this - > nested_focus - > type = = WWT_EDITBOX ) {
return this - > GetQueryString ( this - > nested_focus - > index ) - > GetCharAtPosition ( this , this - > nested_focus - > index , pt ) ;
}
return NULL ;
}
2010-08-12 08:40:28 +02:00
2009-02-09 02:22:29 +01:00
/**
* Set the window that has the focus
* @ param w The window to set the focus on
*/
void SetFocusedWindow ( Window * w )
{
if ( _focused_window = = w ) return ;
/* Invalidate focused widget */
2009-06-04 16:07:05 +02:00
if ( _focused_window ! = NULL ) {
2009-09-13 21:15:59 +02:00
if ( _focused_window - > nested_focus ! = NULL ) _focused_window - > nested_focus - > SetDirty ( _focused_window ) ;
2009-02-09 02:22:29 +01:00
}
/* Remember which window was previously focused */
Window * old_focused = _focused_window ;
_focused_window = w ;
/* So we can inform it that it lost focus */
if ( old_focused ! = NULL ) old_focused - > OnFocusLost ( ) ;
if ( _focused_window ! = NULL ) _focused_window - > OnFocus ( ) ;
}
/**
* Check if an edit box is in global focus . That is if focused window
* has a edit box as focused widget , or if a console is focused .
* @ return returns true if an edit box is in global focus or if the focused window is a console , else false
*/
2013-08-05 22:36:20 +02:00
bool EditBoxInGlobalFocus ( )
2009-02-09 02:22:29 +01:00
{
2009-06-04 16:07:05 +02:00
if ( _focused_window = = NULL ) return false ;
2009-02-09 02:22:29 +01:00
/* The console does not have an edit box so a special case is needed. */
2009-06-04 16:07:05 +02:00
if ( _focused_window - > window_class = = WC_CONSOLE ) return true ;
2009-11-15 14:36:30 +01:00
return _focused_window - > nested_focus ! = NULL & & _focused_window - > nested_focus - > type = = WWT_EDITBOX ;
2009-02-09 02:22:29 +01:00
}
2008-01-13 14:36:01 +01:00
2009-12-31 19:11:03 +01:00
/**
* Makes no widget on this window have focus . The function however doesn ' t change which window has focus .
*/
void Window : : UnfocusFocusedWidget ( )
{
if ( this - > nested_focus ! = NULL ) {
2014-04-28 23:06:51 +02:00
if ( this - > nested_focus - > type = = WWT_EDITBOX ) VideoDriver : : GetInstance ( ) - > EditBoxLostFocus ( ) ;
2013-08-05 22:37:02 +02:00
2009-12-31 19:11:03 +01:00
/* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
this - > nested_focus - > SetDirty ( this ) ;
this - > nested_focus = NULL ;
}
}
2009-09-11 21:12:05 +02:00
/**
* Set focus within this window to the given widget . The function however doesn ' t change which window has focus .
* @ param widget_index Index of the widget in the window to set the focus to .
* @ return Focus has changed .
*/
2012-12-03 22:03:13 +01:00
bool Window : : SetFocusedWidget ( int widget_index )
2009-09-11 21:12:05 +02:00
{
2009-11-15 14:36:30 +01:00
/* Do nothing if widget_index is already focused, or if it wasn't a valid widget. */
2012-12-03 22:03:13 +01:00
if ( ( uint ) widget_index > = this - > nested_array_size ) return false ;
2009-09-11 21:12:05 +02:00
2009-11-15 14:36:30 +01:00
assert ( this - > nested_array [ widget_index ] ! = NULL ) ; // Setting focus to a non-existing widget is a bad idea.
if ( this - > nested_focus ! = NULL ) {
if ( this - > GetWidget < NWidgetCore > ( widget_index ) = = this - > nested_focus ) return false ;
2009-09-11 21:12:05 +02:00
2009-11-15 14:36:30 +01:00
/* Repaint the widget that lost focus. A focused edit box may else leave the caret on the screen. */
this - > nested_focus - > SetDirty ( this ) ;
2014-04-28 23:06:51 +02:00
if ( this - > nested_focus - > type = = WWT_EDITBOX ) VideoDriver : : GetInstance ( ) - > EditBoxLostFocus ( ) ;
2009-09-11 21:12:05 +02:00
}
2009-11-15 14:36:30 +01:00
this - > nested_focus = this - > GetWidget < NWidgetCore > ( widget_index ) ;
return true ;
2009-09-11 21:12:05 +02:00
}
2013-08-05 22:37:02 +02:00
/**
* Called when window looses focus
*/
void Window : : OnFocusLost ( )
{
2014-04-28 23:06:51 +02:00
if ( this - > nested_focus ! = NULL & & this - > nested_focus - > type = = WWT_EDITBOX ) VideoDriver : : GetInstance ( ) - > EditBoxLostFocus ( ) ;
2013-08-05 22:37:02 +02:00
}
2008-10-14 21:27:08 +02:00
/**
* Sets the enabled / disabled status of a list of widgets .
* By default , widgets are enabled .
* On certain conditions , they have to be disabled .
* @ param disab_stat status to use ie : disabled = true , enabled = false
* @ param widgets list of widgets ended by WIDGET_LIST_END
*/
2007-12-02 01:59:48 +01:00
void CDECL Window : : SetWidgetsDisabledState ( bool disab_stat , int widgets , . . . )
{
va_list wdg_list ;
va_start ( wdg_list , widgets ) ;
while ( widgets ! = WIDGET_LIST_END ) {
SetWidgetDisabledState ( widgets , disab_stat ) ;
widgets = va_arg ( wdg_list , int ) ;
}
va_end ( wdg_list ) ;
}
2008-10-14 21:27:08 +02:00
/**
* Sets the lowered / raised status of a list of widgets .
* @ param lowered_stat status to use ie : lowered = true , raised = false
* @ param widgets list of widgets ended by WIDGET_LIST_END
*/
2007-12-02 01:59:48 +01:00
void CDECL Window : : SetWidgetsLoweredState ( bool lowered_stat , int widgets , . . . )
{
va_list wdg_list ;
va_start ( wdg_list , widgets ) ;
while ( widgets ! = WIDGET_LIST_END ) {
SetWidgetLoweredState ( widgets , lowered_stat ) ;
widgets = va_arg ( wdg_list , int ) ;
}
va_end ( wdg_list ) ;
}
2008-10-14 21:27:08 +02:00
/**
2009-08-14 23:27:08 +02:00
* Raise the buttons of the window .
* @ param autoraise Raise only the push buttons of the window .
2008-10-14 21:27:08 +02:00
*/
2009-08-14 23:27:08 +02:00
void Window : : RaiseButtons ( bool autoraise )
2007-12-02 01:59:48 +01:00
{
2009-11-15 14:36:30 +01:00
for ( uint i = 0 ; i < this - > nested_array_size ; i + + ) {
2012-11-14 23:50:56 +01:00
if ( this - > nested_array [ i ] = = NULL ) continue ;
WidgetType type = this - > nested_array [ i ] - > type ;
if ( ( ( type & ~ WWB_PUSHBUTTON ) < WWT_LAST | | type = = NWID_PUSHBUTTON_DROPDOWN ) & &
( ! autoraise | | ( type & WWB_PUSHBUTTON ) | | type = = WWT_EDITBOX ) & & this - > IsWidgetLowered ( i ) ) {
2009-11-15 14:36:30 +01:00
this - > RaiseWidget ( i ) ;
this - > SetWidgetDirty ( i ) ;
2007-12-02 01:59:48 +01:00
}
}
2013-05-26 21:30:07 +02:00
/* Special widgets without widget index */
NWidgetCore * wid = this - > nested_root ! = NULL ? ( NWidgetCore * ) this - > nested_root - > GetWidgetOfType ( WWT_DEFSIZEBOX ) : NULL ;
if ( wid ! = NULL ) {
wid - > SetLowered ( false ) ;
wid - > SetDirty ( this ) ;
}
2007-12-02 01:59:48 +01:00
}
2008-10-14 21:27:08 +02:00
/**
* Invalidate a widget , i . e . mark it as being changed and in need of redraw .
* @ param widget_index the widget to redraw .
*/
2009-09-13 21:15:59 +02:00
void Window : : SetWidgetDirty ( byte widget_index ) const
2007-12-02 01:59:48 +01:00
{
2009-11-15 15:07:23 +01:00
/* Sometimes this function is called before the window is even fully initialized */
if ( this - > nested_array = = NULL ) return ;
2009-11-15 14:36:30 +01:00
this - > nested_array [ widget_index ] - > SetDirty ( this ) ;
2007-12-02 01:59:48 +01:00
}
2013-06-15 17:30:16 +02:00
/**
* A hotkey has been pressed .
* @ param hotkey Hotkey index , by default a widget index of a button or editbox .
* @ return # ES_HANDLED if the key press has been handled , and the hotkey is not unavailable for some reason .
*/
EventState Window : : OnHotkey ( int hotkey )
{
if ( hotkey < 0 ) return ES_NOT_HANDLED ;
NWidgetCore * nw = this - > GetWidget < NWidgetCore > ( hotkey ) ;
if ( nw = = NULL | | nw - > IsDisabled ( ) ) return ES_NOT_HANDLED ;
if ( nw - > type = = WWT_EDITBOX ) {
2013-06-15 17:31:04 +02:00
if ( this - > IsShaded ( ) ) return ES_NOT_HANDLED ;
2013-06-15 17:30:16 +02:00
/* Focus editbox */
this - > SetFocusedWidget ( hotkey ) ;
SetFocusedWindow ( this ) ;
} else {
/* Click button */
this - > OnClick ( Point ( ) , hotkey , 1 ) ;
}
return ES_HANDLED ;
}
2008-10-14 21:27:08 +02:00
/**
* Do all things to make a button look clicked and mark it to be
* unclicked in a few ticks .
* @ param widget the widget to " click "
*/
2007-12-08 03:55:47 +01:00
void Window : : HandleButtonClick ( byte widget )
{
this - > LowerWidget ( widget ) ;
2011-12-15 20:54:23 +01:00
this - > SetTimeout ( ) ;
2009-09-13 21:15:59 +02:00
this - > SetWidgetDirty ( widget ) ;
2007-12-08 03:55:47 +01:00
}
2006-11-18 17:47:02 +01:00
static void StartWindowDrag ( Window * w ) ;
2009-03-18 02:06:48 +01:00
static void StartWindowSizing ( Window * w , bool to_left ) ;
2006-01-05 13:40:50 +01:00
2008-04-07 22:28:58 +02:00
/**
* Dispatch left mouse - button ( possibly double ) click in window .
* @ param w Window to dispatch event in
* @ param x X coordinate of the click
* @ param y Y coordinate of the click
2010-01-30 19:34:48 +01:00
* @ param click_count Number of fast consecutive clicks at same position
2008-04-07 22:28:58 +02:00
*/
2010-01-30 19:34:48 +01:00
static void DispatchLeftClickEvent ( Window * w , int x , int y , int click_count )
2005-11-04 15:01:44 +01:00
{
2010-08-12 11:09:24 +02:00
NWidgetCore * nw = w - > nested_root - > GetWidgetFromPos ( x , y ) ;
2009-11-24 18:13:24 +01:00
WidgetType widget_type = ( nw ! = NULL ) ? nw - > type : WWT_EMPTY ;
bool focused_widget_changed = false ;
/* If clicked on a window that previously did dot have focus */
if ( _focused_window ! = w & & // We already have focus, right?
2013-05-26 21:24:11 +02:00
( w - > window_desc - > flags & WDF_NO_FOCUS ) = = 0 & & // Don't lose focus to toolbars
2009-11-24 18:13:24 +01:00
widget_type ! = WWT_CLOSEBOX ) { // Don't change focused window if 'X' (close button) was clicked
focused_widget_changed = true ;
SetFocusedWindow ( w ) ;
}
2009-02-09 02:22:29 +01:00
2009-11-24 18:13:24 +01:00
if ( nw = = NULL ) return ; // exit if clicked outside of widgets
2004-09-05 16:20:36 +02:00
2009-11-24 18:13:24 +01:00
/* don't allow any interaction if the button has been disabled */
if ( nw - > IsDisabled ( ) ) return ;
2006-12-04 14:57:04 +01:00
2009-11-24 18:13:24 +01:00
int widget_index = nw - > index ; ///< Index of the widget
2009-02-09 02:22:29 +01:00
2010-01-30 19:34:48 +01:00
/* Clicked on a widget that is not disabled.
2012-11-28 22:14:28 +01:00
* So unless the clicked widget is the caption bar , change focus to this widget .
2019-01-17 23:01:07 +01:00
* Exception : In the OSK we always want the editbox to stay focused . */
2012-11-28 22:14:28 +01:00
if ( widget_type ! = WWT_CAPTION & & w - > window_class ! = WC_OSK ) {
2010-01-30 19:34:48 +01:00
/* focused_widget_changed is 'now' only true if the window this widget
* is in gained focus . In that case it must remain true , also if the
* local widget focus did not change . As such it ' s the logical - or of
* both changed states .
*
* If this is not preserved , then the OSK window would be opened when
* a user has the edit box focused and then click on another window and
* then back again on the edit box ( to type some text ) .
*/
focused_widget_changed | = w - > SetFocusedWidget ( widget_index ) ;
}
/* Close any child drop down menus. If the button pressed was the drop down
* list ' s own button , then we should not process the click any further . */
if ( HideDropDownMenu ( w ) = = widget_index & & widget_index > = 0 ) return ;
2010-08-12 18:44:51 +02:00
if ( ( widget_type & ~ WWB_PUSHBUTTON ) < WWT_LAST & & ( widget_type & WWB_PUSHBUTTON ) ) w - > HandleButtonClick ( widget_index ) ;
2010-01-06 21:17:46 +01:00
2012-11-14 23:50:42 +01:00
Point pt = { x , y } ;
2010-08-12 18:44:51 +02:00
switch ( widget_type ) {
2010-08-12 11:16:43 +02:00
case NWID_VSCROLLBAR :
case NWID_HSCROLLBAR :
2010-01-30 19:34:48 +01:00
ScrollbarClickHandler ( w , nw , x , y ) ;
break ;
2009-11-24 18:13:24 +01:00
2012-11-14 23:50:42 +01:00
case WWT_EDITBOX : {
QueryString * query = w - > GetQueryString ( widget_index ) ;
if ( query ! = NULL ) query - > ClickEditBox ( w , pt , widget_index , click_count , focused_widget_changed ) ;
2010-01-30 19:34:48 +01:00
break ;
2012-11-14 23:50:42 +01:00
}
2004-08-09 19:04:08 +02:00
2010-01-30 19:34:48 +01:00
case WWT_CLOSEBOX : // 'X'
delete w ;
return ;
2005-01-23 14:09:35 +01:00
2010-01-30 19:34:48 +01:00
case WWT_CAPTION : // 'Title bar'
StartWindowDrag ( w ) ;
return ;
2005-01-03 20:45:18 +01:00
2010-01-30 19:34:48 +01:00
case WWT_RESIZEBOX :
/* When the resize widget is on the left size of the window
* we assume that that button is used to resize to the left . */
StartWindowSizing ( w , ( int ) nw - > pos_x < ( w - > width / 2 ) ) ;
nw - > SetDirty ( w ) ;
return ;
2005-01-03 20:45:18 +01:00
2013-05-26 21:30:07 +02:00
case WWT_DEFSIZEBOX : {
2013-05-26 21:30:31 +02:00
if ( _ctrl_pressed ) {
w - > window_desc - > pref_width = w - > width ;
w - > window_desc - > pref_height = w - > height ;
} else {
int16 def_width = max < int16 > ( min ( w - > window_desc - > GetDefaultWidth ( ) , _screen . width ) , w - > nested_root - > smallest_x ) ;
int16 def_height = max < int16 > ( min ( w - > window_desc - > GetDefaultHeight ( ) , _screen . height - 50 ) , w - > nested_root - > smallest_y ) ;
int dx = ( w - > resize . step_width = = 0 ) ? 0 : def_width - w - > width ;
int dy = ( w - > resize . step_height = = 0 ) ? 0 : def_height - w - > height ;
/* dx and dy has to go by step.. calculate it.
* The cast to int is necessary else dx / dy are implicitly casted to unsigned int , which won ' t work . */
if ( w - > resize . step_width > 1 ) dx - = dx % ( int ) w - > resize . step_width ;
if ( w - > resize . step_height > 1 ) dy - = dy % ( int ) w - > resize . step_height ;
ResizeWindow ( w , dx , dy , false ) ;
}
2013-05-26 21:30:07 +02:00
nw - > SetLowered ( true ) ;
nw - > SetDirty ( w ) ;
w - > SetTimeout ( ) ;
break ;
}
2010-04-24 15:27:22 +02:00
case WWT_DEBUGBOX :
w - > ShowNewGRFInspectWindow ( ) ;
break ;
2010-01-30 19:34:48 +01:00
case WWT_SHADEBOX :
nw - > SetDirty ( w ) ;
w - > SetShaded ( ! w - > IsShaded ( ) ) ;
return ;
2009-12-21 17:06:20 +01:00
2010-01-30 19:34:48 +01:00
case WWT_STICKYBOX :
2011-12-15 20:54:23 +01:00
w - > flags ^ = WF_STICKY ;
2010-01-30 19:34:48 +01:00
nw - > SetDirty ( w ) ;
2013-05-26 22:17:07 +02:00
if ( _ctrl_pressed ) w - > window_desc - > pref_sticky = ( w - > flags & WF_STICKY ) ! = 0 ;
2010-01-30 19:34:48 +01:00
return ;
2009-11-24 18:13:24 +01:00
2010-01-30 19:34:48 +01:00
default :
break ;
2004-08-09 19:04:08 +02:00
}
2005-01-16 13:30:52 +01:00
2009-11-24 18:13:24 +01:00
/* Widget has no index, so the window is not interested in it. */
if ( widget_index < 0 ) return ;
2011-12-19 22:05:14 +01:00
/* Check if the widget is highlighted; if so, disable highlight and dispatch an event to the GameScript */
if ( w - > IsWidgetHighlighted ( widget_index ) ) {
w - > SetWidgetHighlight ( widget_index , TC_INVALID ) ;
Game : : NewEvent ( new ScriptEventWindowWidgetClick ( ( ScriptWindow : : WindowClass ) w - > window_class , w - > window_number , widget_index ) ) ;
}
2010-01-30 19:34:48 +01:00
w - > OnClick ( pt , widget_index , click_count ) ;
2004-08-09 19:04:08 +02:00
}
2008-04-07 22:28:58 +02:00
/**
* Dispatch right mouse - button click in window .
* @ param w Window to dispatch event in
* @ param x X coordinate of the click
* @ param y Y coordinate of the click
*/
2006-07-26 05:33:12 +02:00
static void DispatchRightClickEvent ( Window * w , int x , int y )
2005-11-04 15:01:44 +01:00
{
2009-11-24 18:13:24 +01:00
NWidgetCore * wid = w - > nested_root - > GetWidgetFromPos ( x , y ) ;
2010-07-14 19:36:27 +02:00
if ( wid = = NULL ) return ;
2004-08-09 19:04:08 +02:00
2010-07-11 12:58:55 +02:00
/* No widget to handle, or the window is not interested in it. */
2010-07-14 19:36:27 +02:00
if ( wid - > index > = 0 ) {
Point pt = { x , y } ;
if ( w - > OnRightClick ( pt , wid - > index ) ) return ;
}
2010-07-11 12:58:55 +02:00
2017-03-24 19:55:16 +01:00
/* Right-click close is enabled and there is a closebox */
2017-03-24 20:25:01 +01:00
if ( _settings_client . gui . right_mouse_wnd_close & & w - > nested_root - > GetWidgetOfType ( WWT_CLOSEBOX ) ) {
2017-03-24 19:55:16 +01:00
delete w ;
2017-03-24 20:25:01 +01:00
} else if ( _settings_client . gui . hover_delay_ms = = 0 & & wid - > tool_tip ! = 0 ) {
2017-03-24 19:55:16 +01:00
GuiShowTooltips ( w , wid - > tool_tip , 0 , NULL , TCC_RIGHT_CLICK ) ;
}
2010-07-11 12:58:55 +02:00
}
/**
* Dispatch hover of the mouse over a window .
* @ param w Window to dispatch event in .
* @ param x X coordinate of the click .
* @ param y Y coordinate of the click .
*/
static void DispatchHoverEvent ( Window * w , int x , int y )
{
NWidgetCore * wid = w - > nested_root - > GetWidgetFromPos ( x , y ) ;
2009-11-24 18:13:24 +01:00
/* No widget to handle */
if ( wid = = NULL ) return ;
2004-08-09 19:04:08 +02:00
2009-11-24 18:13:24 +01:00
/* Show the tooltip if there is any */
if ( wid - > tool_tip ! = 0 ) {
2010-12-12 15:14:26 +01:00
GuiShowTooltips ( w , wid - > tool_tip ) ;
2009-11-24 18:13:24 +01:00
return ;
2004-08-09 19:04:08 +02:00
}
2010-07-11 13:00:09 +02:00
/* Widget has no index, so the window is not interested in it. */
if ( wid - > index < 0 ) return ;
Point pt = { x , y } ;
w - > OnHover ( pt , wid - > index ) ;
2004-08-09 19:04:08 +02:00
}
2008-04-07 22:28:58 +02:00
/**
* Dispatch the mousewheel - action to the window .
* The window will scroll any compatible scrollbars if the mouse is pointed over the bar or its contents
* @ param w Window
2009-11-24 18:13:24 +01:00
* @ param nwid the widget where the scrollwheel was used
2005-07-08 02:14:19 +02:00
* @ param wheel scroll up or down
*/
2011-01-09 16:36:02 +01:00
static void DispatchMouseWheelEvent ( Window * w , NWidgetCore * nwid , int wheel )
2004-08-09 19:04:08 +02:00
{
2009-11-24 18:13:24 +01:00
if ( nwid = = NULL ) return ;
2009-06-04 14:46:37 +02:00
2009-12-21 17:06:20 +01:00
/* Using wheel on caption/shade-box shades or unshades the window. */
if ( nwid - > type = = WWT_CAPTION | | nwid - > type = = WWT_SHADEBOX ) {
2010-08-16 01:42:36 +02:00
w - > SetShaded ( wheel < 0 ) ;
2009-12-21 17:06:20 +01:00
return ;
}
2011-01-09 16:36:02 +01:00
/* Wheeling a vertical scrollbar. */
if ( nwid - > type = = NWID_VSCROLLBAR ) {
NWidgetScrollbar * sb = static_cast < NWidgetScrollbar * > ( nwid ) ;
if ( sb - > GetCount ( ) > sb - > GetCapacity ( ) ) {
sb - > UpdatePosition ( wheel ) ;
w - > SetDirty ( ) ;
}
return ;
}
2009-12-21 17:06:20 +01:00
/* Scroll the widget attached to the scrollbar. */
2010-08-12 08:44:45 +02:00
Scrollbar * sb = ( nwid - > scrollbar_index > = 0 ? w - > GetScrollbar ( nwid - > scrollbar_index ) : NULL ) ;
2009-09-02 10:58:20 +02:00
if ( sb ! = NULL & & sb - > GetCount ( ) > sb - > GetCapacity ( ) ) {
sb - > UpdatePosition ( wheel ) ;
w - > SetDirty ( ) ;
2004-08-09 19:04:08 +02:00
}
}
2011-08-21 14:51:52 +02:00
/**
* Returns whether a window may be shown or not .
* @ param w The window to consider .
* @ return True iff it may be shown , otherwise false .
*/
static bool MayBeShown ( const Window * w )
{
/* If we're not modal, everything is okay. */
if ( ! HasModalProgress ( ) ) return true ;
switch ( w - > window_class ) {
case WC_MAIN_WINDOW : ///< The background, i.e. the game.
case WC_MODAL_PROGRESS : ///< The actual progress window.
2012-05-07 21:34:51 +02:00
case WC_CONFIRM_POPUP_QUERY : ///< The abort window.
2011-08-21 14:51:52 +02:00
return true ;
default :
return false ;
}
}
2008-04-07 22:28:58 +02:00
/**
2009-01-07 17:11:27 +01:00
* Generate repaint events for the visible part of window w within the rectangle .
2008-04-07 22:28:58 +02:00
*
* The function goes recursively upwards in the window stack , and splits the rectangle
* into multiple pieces at the window edges , so obscured parts are not redrawn .
*
2009-01-07 17:11:27 +01:00
* @ param w Window that needs to be repainted
2008-04-07 22:28:58 +02:00
* @ param left Left edge of the rectangle that should be repainted
* @ param top Top edge of the rectangle that should be repainted
* @ param right Right edge of the rectangle that should be repainted
* @ param bottom Bottom edge of the rectangle that should be repainted
*/
2009-01-07 17:11:27 +01:00
static void DrawOverlappedWindow ( Window * w , int left , int top , int right , int bottom )
2004-08-09 19:04:08 +02:00
{
2009-01-07 19:59:46 +01:00
const Window * v ;
FOR_ALL_WINDOWS_FROM_BACK_FROM ( v , w - > z_front ) {
2011-08-21 14:51:52 +02:00
if ( MayBeShown ( v ) & &
right > v - > left & &
2005-07-09 00:25:24 +02:00
bottom > v - > top & &
2004-08-09 19:04:08 +02:00
left < v - > left + v - > width & &
top < v - > top + v - > height ) {
2011-08-21 14:51:52 +02:00
/* v and rectangle intersect with each other */
2008-04-07 22:28:58 +02:00
int x ;
2007-04-19 00:10:36 +02:00
if ( left < ( x = v - > left ) ) {
2009-01-07 17:11:27 +01:00
DrawOverlappedWindow ( w , left , top , x , bottom ) ;
DrawOverlappedWindow ( w , x , top , right , bottom ) ;
2004-08-09 19:04:08 +02:00
return ;
}
2007-04-19 00:10:36 +02:00
if ( right > ( x = v - > left + v - > width ) ) {
2009-01-07 17:11:27 +01:00
DrawOverlappedWindow ( w , left , top , x , bottom ) ;
DrawOverlappedWindow ( w , x , top , right , bottom ) ;
2004-08-09 19:04:08 +02:00
return ;
}
2007-04-19 00:10:36 +02:00
if ( top < ( x = v - > top ) ) {
2009-01-07 17:11:27 +01:00
DrawOverlappedWindow ( w , left , top , right , x ) ;
DrawOverlappedWindow ( w , left , x , right , bottom ) ;
2004-08-09 19:04:08 +02:00
return ;
}
2007-04-19 00:10:36 +02:00
if ( bottom > ( x = v - > top + v - > height ) ) {
2009-01-07 17:11:27 +01:00
DrawOverlappedWindow ( w , left , top , right , x ) ;
DrawOverlappedWindow ( w , left , x , right , bottom ) ;
2004-08-09 19:04:08 +02:00
return ;
}
return ;
}
}
2008-04-19 15:05:05 +02:00
/* Setup blitter, and dispatch a repaint event to window *wz */
2008-04-13 20:59:32 +02:00
DrawPixelInfo * dp = _cur_dpi ;
dp - > width = right - left ;
dp - > height = bottom - top ;
2009-01-07 17:11:27 +01:00
dp - > left = left - w - > left ;
dp - > top = top - w - > top ;
2008-04-13 20:59:32 +02:00
dp - > pitch = _screen . pitch ;
2014-01-02 23:41:58 +01:00
dp - > dst_ptr = BlitterFactory : : GetCurrentBlitter ( ) - > MoveTo ( _screen . dst_ptr , left , top ) ;
2008-04-13 20:59:32 +02:00
dp - > zoom = ZOOM_LVL_NORMAL ;
2009-01-07 17:11:27 +01:00
w - > OnPaint ( ) ;
2008-04-13 20:59:32 +02:00
}
/**
* From a rectangle that needs redrawing , find the windows that intersect with the rectangle .
* These windows should be re - painted .
* @ param left Left edge of the rectangle that should be repainted
* @ param top Top edge of the rectangle that should be repainted
* @ param right Right edge of the rectangle that should be repainted
* @ param bottom Bottom edge of the rectangle that should be repainted
*/
void DrawOverlappedWindowForAll ( int left , int top , int right , int bottom )
{
2009-01-07 17:11:27 +01:00
Window * w ;
2018-10-15 00:36:14 +02:00
DrawPixelInfo * old_dpi = _cur_dpi ;
2008-04-13 20:59:32 +02:00
DrawPixelInfo bk ;
_cur_dpi = & bk ;
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2011-08-21 14:51:52 +02:00
if ( MayBeShown ( w ) & &
right > w - > left & &
2008-04-13 20:59:32 +02:00
bottom > w - > top & &
left < w - > left + w - > width & &
top < w - > top + w - > height ) {
2008-04-19 15:05:05 +02:00
/* Window w intersects with the rectangle => needs repaint */
2016-12-25 18:56:57 +01:00
DrawOverlappedWindow ( w , max ( left , w - > left ) , max ( top , w - > top ) , min ( right , w - > left + w - > width ) , min ( bottom , w - > top + w - > height ) ) ;
2008-04-13 20:59:32 +02:00
}
2004-08-09 19:04:08 +02:00
}
2018-10-15 00:36:14 +02:00
_cur_dpi = old_dpi ;
2004-08-09 19:04:08 +02:00
}
2008-05-06 23:28:30 +02:00
/**
* Mark entire window as dirty ( in need of re - paint )
* @ ingroup dirty
*/
void Window : : SetDirty ( ) const
{
SetDirtyBlocks ( this - > left , this - > top , this - > left + this - > width , this - > top + this - > height ) ;
}
2010-08-01 21:22:34 +02:00
/**
* Re - initialize a window , and optionally change its size .
2009-11-08 16:22:04 +01:00
* @ param rx Horizontal resize of the window .
* @ param ry Vertical resize of the window .
* @ note For just resizing the window , use # ResizeWindow instead .
*/
void Window : : ReInit ( int rx , int ry )
2009-06-28 22:09:40 +02:00
{
this - > SetDirty ( ) ; // Mark whole current window as dirty.
/* Save current size. */
int window_width = this - > width ;
int window_height = this - > height ;
2009-11-29 22:14:34 +01:00
this - > OnInit ( ) ;
2009-06-28 22:09:40 +02:00
/* Re-initialize the window from the ground up. No need to change the nested_array, as all widgets stay where they are. */
2009-07-16 00:51:37 +02:00
this - > nested_root - > SetupSmallestSize ( this , false ) ;
2010-11-13 10:56:25 +01:00
this - > nested_root - > AssignSizePosition ( ST_SMALLEST , 0 , 0 , this - > nested_root - > smallest_x , this - > nested_root - > smallest_y , _current_text_dir = = TD_RTL ) ;
2009-06-28 22:09:40 +02:00
this - > width = this - > nested_root - > smallest_x ;
this - > height = this - > nested_root - > smallest_y ;
this - > resize . step_width = this - > nested_root - > resize_x ;
this - > resize . step_height = this - > nested_root - > resize_y ;
2009-11-08 16:22:04 +01:00
/* Resize as close to the original size + requested resize as possible. */
window_width = max ( window_width + rx , this - > width ) ;
window_height = max ( window_height + ry , this - > height ) ;
2009-06-28 22:09:40 +02:00
int dx = ( this - > resize . step_width = = 0 ) ? 0 : window_width - this - > width ;
int dy = ( this - > resize . step_height = = 0 ) ? 0 : window_height - this - > height ;
/* dx and dy has to go by step.. calculate it.
* The cast to int is necessary else dx / dy are implicitly casted to unsigned int , which won ' t work . */
if ( this - > resize . step_width > 1 ) dx - = dx % ( int ) this - > resize . step_width ;
if ( this - > resize . step_height > 1 ) dy - = dy % ( int ) this - > resize . step_height ;
2009-11-16 17:42:04 +01:00
ResizeWindow ( this , dx , dy ) ;
2010-04-04 13:21:56 +02:00
/* ResizeWindow() does this->SetDirty() already, no need to do it again here. */
2009-06-28 22:09:40 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Set the shaded state of the window to \ a make_shaded .
2009-12-21 17:06:20 +01:00
* @ param make_shaded If \ c true , shade the window ( roll up until just the title bar is visible ) , else unshade / unroll the window to its original size .
* @ note The method uses # Window : : ReInit ( ) , thus after the call , the whole window should be considered changed .
*/
void Window : : SetShaded ( bool make_shaded )
{
if ( this - > shade_select = = NULL ) return ;
2009-12-22 21:53:28 +01:00
int desired = make_shaded ? SZSP_HORIZONTAL : 0 ;
2009-12-21 17:06:20 +01:00
if ( this - > shade_select - > shown_plane ! = desired ) {
if ( make_shaded ) {
2013-06-15 17:31:04 +02:00
if ( this - > nested_focus ! = NULL ) this - > UnfocusFocusedWidget ( ) ;
2009-12-21 17:06:20 +01:00
this - > unshaded_size . width = this - > width ;
this - > unshaded_size . height = this - > height ;
this - > shade_select - > SetDisplayedPlane ( desired ) ;
2009-12-22 21:53:28 +01:00
this - > ReInit ( 0 , - this - > height ) ;
2009-12-21 17:06:20 +01:00
} else {
this - > shade_select - > SetDisplayedPlane ( desired ) ;
int dx = ( ( int ) this - > unshaded_size . width > this - > width ) ? ( int ) this - > unshaded_size . width - this - > width : 0 ;
int dy = ( ( int ) this - > unshaded_size . height > this - > height ) ? ( int ) this - > unshaded_size . height - this - > height : 0 ;
this - > ReInit ( dx , dy ) ;
}
}
}
2010-08-01 21:22:34 +02:00
/**
* Find the Window whose parent pointer points to this window
2007-04-09 17:06:24 +02:00
* @ param w parent Window to find child of
2012-01-01 18:22:32 +01:00
* @ param wc Window class of the window to remove ; # WC_INVALID if class does not matter
* @ return a Window pointer that is the child of \ a w , or \ c NULL otherwise
2009-10-31 15:53:19 +01:00
*/
static Window * FindChildWindow ( const Window * w , WindowClass wc )
2006-12-29 18:07:41 +01:00
{
2009-01-06 23:37:42 +01:00
Window * v ;
FOR_ALL_WINDOWS_FROM_BACK ( v ) {
2009-10-31 15:53:19 +01:00
if ( ( wc = = WC_INVALID | | wc = = v - > window_class ) & & v - > parent = = w ) return v ;
2006-12-29 18:07:41 +01:00
}
return NULL ;
}
2008-09-24 18:40:06 +02:00
/**
* Delete all children a window might have in a head - recursive manner
2012-01-01 18:22:32 +01:00
* @ param wc Window class of the window to remove ; # WC_INVALID if class does not matter
2008-09-24 18:40:06 +02:00
*/
2009-10-31 15:53:19 +01:00
void Window : : DeleteChildWindows ( WindowClass wc ) const
2008-09-24 18:40:06 +02:00
{
2009-10-31 15:53:19 +01:00
Window * child = FindChildWindow ( this , wc ) ;
2008-09-24 18:40:06 +02:00
while ( child ! = NULL ) {
delete child ;
2009-10-31 15:53:19 +01:00
child = FindChildWindow ( this , wc ) ;
2008-09-24 18:40:06 +02:00
}
}
2008-04-07 22:28:58 +02:00
/**
2008-04-19 15:05:05 +02:00
* Remove window and all its child windows from the window stack .
2008-04-07 22:28:58 +02:00
*/
2008-05-06 23:28:30 +02:00
Window : : ~ Window ( )
2006-11-18 17:47:02 +01:00
{
2010-12-25 13:47:05 +01:00
if ( _thd . window_class = = this - > window_class & &
2008-05-06 23:28:30 +02:00
_thd . window_number = = this - > window_number ) {
2004-08-09 19:04:08 +02:00
ResetObjectToPlace ( ) ;
}
2008-04-13 21:06:30 +02:00
/* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
2008-05-06 23:28:30 +02:00
if ( _mouseover_last_w = = this ) _mouseover_last_w = NULL ;
2008-04-13 21:06:30 +02:00
2011-01-18 22:08:19 +01:00
/* We can't scroll the window when it's closed. */
if ( _last_scroll_window = = this ) _last_scroll_window = NULL ;
2009-06-01 15:28:05 +02:00
/* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */
2013-08-05 22:37:02 +02:00
if ( _focused_window = = this ) {
this - > OnFocusLost ( ) ;
_focused_window = NULL ;
}
2009-02-09 02:22:29 +01:00
2008-09-24 18:40:06 +02:00
this - > DeleteChildWindows ( ) ;
2008-05-04 23:36:37 +02:00
2008-05-06 23:28:30 +02:00
if ( this - > viewport ! = NULL ) DeleteWindowViewport ( this ) ;
2008-05-04 23:36:37 +02:00
2008-05-06 23:28:30 +02:00
this - > SetDirty ( ) ;
2008-12-16 18:58:27 +01:00
2009-06-03 22:00:33 +02:00
free ( this - > nested_array ) ; // Contents is released through deletion of #nested_root.
delete this - > nested_root ;
2009-01-07 19:59:46 +01:00
2015-03-13 21:54:35 +01:00
/*
* Make fairly sure that this is written , and not " optimized " away .
* The delete operator is overwritten to not delete it ; the deletion
* happens at a later moment in time after the window has been
* removed from the list of windows to prevent issues with items
* being removed during the iteration as not one but more windows
* may be removed by a single call to ~ Window by means of the
* DeleteChildWindows function .
*/
const_cast < volatile WindowClass & > ( this - > window_class ) = WC_INVALID ;
2004-08-09 19:04:08 +02:00
}
2008-04-07 22:28:58 +02:00
/**
* Find a window by its class and window number
* @ param cls Window class
* @ param number Number of the window within the window class
* @ return Pointer to the found window , or \ c NULL if not available
*/
2004-08-09 19:04:08 +02:00
Window * FindWindowById ( WindowClass cls , WindowNumber number )
{
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2005-11-14 20:48:04 +01:00
if ( w - > window_class = = cls & & w - > window_number = = number ) return w ;
2004-09-05 16:20:36 +02:00
}
2004-08-09 19:04:08 +02:00
return NULL ;
}
2009-11-29 01:41:08 +01:00
/**
* Find any window by its class . Useful when searching for a window that uses
2018-10-28 03:17:36 +01:00
* the window number as a # WindowClass , like # WC_SEND_NETWORK_MSG .
2009-11-29 01:41:08 +01:00
* @ param cls Window class
* @ return Pointer to the found window , or \ c NULL if not available
*/
Window * FindWindowByClass ( WindowClass cls )
{
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
if ( w - > window_class = = cls ) return w ;
}
return NULL ;
}
2008-04-07 22:28:58 +02:00
/**
* Delete a window by its class and window number ( if it is open ) .
* @ param cls Window class
* @ param number Number of the window within the window class
2009-01-02 21:59:04 +01:00
* @ param force force deletion ; if false don ' t delete when stickied
2008-04-07 22:28:58 +02:00
*/
2009-01-02 21:59:04 +01:00
void DeleteWindowById ( WindowClass cls , WindowNumber number , bool force )
2004-08-09 19:04:08 +02:00
{
2009-01-02 21:59:04 +01:00
Window * w = FindWindowById ( cls , number ) ;
if ( force | | w = = NULL | |
2011-12-15 20:54:23 +01:00
( w - > flags & WF_STICKY ) = = 0 ) {
2009-01-02 21:59:04 +01:00
delete w ;
}
2004-09-05 16:20:36 +02:00
}
2004-08-09 19:04:08 +02:00
2008-04-07 22:28:58 +02:00
/**
* Delete all windows of a given class
* @ param cls Window class of windows to delete
*/
2005-01-13 17:50:20 +01:00
void DeleteWindowByClass ( WindowClass cls )
{
2009-01-06 23:37:42 +01:00
Window * w ;
2005-11-14 20:48:04 +01:00
2006-11-18 01:44:09 +01:00
restart_search :
/* When we find the window to delete, we need to restart the search
2006-11-18 17:47:02 +01:00
* as deleting this window could cascade in deleting ( many ) others
* anywhere in the z - array */
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2005-01-13 17:50:20 +01:00
if ( w - > window_class = = cls ) {
2008-05-06 23:28:30 +02:00
delete w ;
2006-11-18 01:44:09 +01:00
goto restart_search ;
2005-11-14 20:48:04 +01:00
}
2005-01-13 17:50:20 +01:00
}
}
2010-08-01 21:22:34 +02:00
/**
* Delete all windows of a company . We identify windows of a company
2008-09-30 22:39:50 +02:00
* by looking at the caption colour . If it is equal to the company ID
* then we say the window belongs to the company and should be deleted
2010-08-01 21:44:49 +02:00
* @ param id company identifier
*/
2008-09-30 22:39:50 +02:00
void DeleteCompanyWindows ( CompanyID id )
2006-11-13 21:33:51 +01:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
2006-11-13 21:33:51 +01:00
2006-11-18 01:44:09 +01:00
restart_search :
/* When we find the window to delete, we need to restart the search
2006-11-18 17:47:02 +01:00
* as deleting this window could cascade in deleting ( many ) others
* anywhere in the z - array */
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2009-02-09 03:33:10 +01:00
if ( w - > owner = = id ) {
2008-05-06 23:28:30 +02:00
delete w ;
2006-11-18 01:44:09 +01:00
goto restart_search ;
2006-11-13 21:33:51 +01:00
}
}
2010-10-30 19:51:07 +02:00
/* Also delete the company specific windows that don't have a company-colour. */
2006-11-18 01:14:43 +01:00
DeleteWindowById ( WC_BUY_COMPANY , id ) ;
2006-11-13 21:33:51 +01:00
}
2010-08-01 21:22:34 +02:00
/**
* Change the owner of all the windows one company can take over from another
2008-09-30 22:39:50 +02:00
* company in the case of a company merger . Do not change ownership of windows
2006-11-18 01:14:43 +01:00
* that need to be deleted once takeover is complete
2008-09-30 22:39:50 +02:00
* @ param old_owner original owner of the window
2010-08-01 21:44:49 +02:00
* @ param new_owner the new owner of the window
*/
2008-09-30 22:39:50 +02:00
void ChangeWindowOwner ( Owner old_owner , Owner new_owner )
2006-11-13 21:33:51 +01:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2009-02-09 03:33:10 +01:00
if ( w - > owner ! = old_owner ) continue ;
2008-05-07 10:27:55 +02:00
switch ( w - > window_class ) {
2009-02-09 03:57:15 +01:00
case WC_COMPANY_COLOUR :
2008-05-07 10:27:55 +02:00
case WC_FINANCES :
case WC_STATION_LIST :
case WC_TRAINS_LIST :
case WC_ROADVEH_LIST :
case WC_SHIPS_LIST :
case WC_AIRCRAFT_LIST :
case WC_BUY_COMPANY :
case WC_COMPANY :
2011-12-04 00:40:08 +01:00
case WC_COMPANY_INFRASTRUCTURE :
2015-10-30 18:23:16 +01:00
case WC_VEHICLE_ORDERS : // Changing owner would also require changing WindowDesc, which is not possible; however keeping the old one crashes because of missing widgets etc.. See ShowOrdersWindow().
2008-05-07 10:27:55 +02:00
continue ;
default :
2009-02-09 03:33:10 +01:00
w - > owner = new_owner ;
2008-05-07 10:27:55 +02:00
break ;
}
2006-11-13 21:33:51 +01:00
}
}
2009-01-07 17:11:27 +01:00
static void BringWindowToFront ( Window * w ) ;
2006-01-05 13:40:50 +01:00
2010-08-01 21:22:34 +02:00
/**
2011-11-26 17:09:25 +01:00
* Find a window and make it the relative top - window on the screen .
2009-12-21 17:06:20 +01:00
* The window gets unshaded if it was shaded , and a white border is drawn at its edges for a brief period of time to visualize its " activation " .
2007-04-09 17:06:24 +02:00
* @ param cls WindowClass of the window to activate
* @ param number WindowNumber of the window to activate
2010-08-01 21:44:49 +02:00
* @ return a pointer to the window thus activated
*/
2004-08-09 19:04:08 +02:00
Window * BringWindowToFrontById ( WindowClass cls , WindowNumber number )
{
Window * w = FindWindowById ( cls , number ) ;
if ( w ! = NULL ) {
2009-12-21 17:06:20 +01:00
if ( w - > IsShaded ( ) ) w - > SetShaded ( false ) ; // Restore original window size if it was shaded.
2011-12-15 20:54:23 +01:00
w - > SetWhiteBorder ( ) ;
2006-11-18 17:47:02 +01:00
BringWindowToFront ( w ) ;
2008-05-07 00:08:18 +02:00
w - > SetDirty ( ) ;
2004-08-09 19:04:08 +02:00
}
return w ;
}
2005-04-05 23:03:30 +02:00
static inline bool IsVitalWindow ( const Window * w )
{
2008-05-07 10:27:55 +02:00
switch ( w - > window_class ) {
case WC_MAIN_TOOLBAR :
case WC_STATUS_BAR :
case WC_NEWS_WINDOW :
case WC_SEND_NETWORK_MSG :
return true ;
default :
return false ;
}
2005-04-05 23:03:30 +02:00
}
2010-08-01 21:22:34 +02:00
/**
2011-11-26 17:09:25 +01:00
* Get the z - priority for a given window . This is used in comparison with other z - priority values ;
* a window with a given z - priority will appear above other windows with a lower value , and below
* those with a higher one ( the ordering within z - priorities is arbitrary ) .
2017-08-27 15:14:37 +02:00
* @ param wc The window class of window to get the z - priority for
* @ pre wc ! = WC_INVALID
2011-11-26 17:09:25 +01:00
* @ return The window ' s z - priority
2005-04-05 23:03:30 +02:00
*/
2017-08-27 15:14:37 +02:00
static uint GetWindowZPriority ( WindowClass wc )
2011-11-26 17:09:25 +01:00
{
2017-08-27 15:14:37 +02:00
assert ( wc ! = WC_INVALID ) ;
2011-11-26 17:09:25 +01:00
uint z_priority = 0 ;
2017-08-27 15:14:37 +02:00
switch ( wc ) {
2011-11-26 17:09:25 +01:00
case WC_ENDSCREEN :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
case WC_HIGHSCORE :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
case WC_TOOLTIPS :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
case WC_DROPDOWN_MENU :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
case WC_MAIN_TOOLBAR :
case WC_STATUS_BAR :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
2011-12-20 20:48:33 +01:00
case WC_OSK :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-12-20 20:48:33 +01:00
2011-12-03 15:45:02 +01:00
case WC_QUERY_STRING :
2012-01-03 01:22:14 +01:00
case WC_SEND_NETWORK_MSG :
2011-12-03 15:45:02 +01:00
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-12-03 15:45:02 +01:00
2011-11-26 17:09:25 +01:00
case WC_ERRMSG :
case WC_CONFIRM_POPUP_QUERY :
2011-12-01 12:57:25 +01:00
case WC_MODAL_PROGRESS :
case WC_NETWORK_STATUS_WINDOW :
2014-05-24 21:11:20 +02:00
case WC_SAVE_PRESET :
2011-11-26 17:09:25 +01:00
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
2011-12-14 21:47:33 +01:00
case WC_GENERATE_LANDSCAPE :
2011-11-26 17:09:25 +01:00
case WC_SAVELOAD :
2011-12-04 12:58:56 +01:00
case WC_GAME_OPTIONS :
2011-12-04 13:11:27 +01:00
case WC_CUSTOM_CURRENCY :
2011-12-01 12:57:25 +01:00
case WC_NETWORK_WINDOW :
2011-12-04 12:58:56 +01:00
case WC_GRF_PARAMETERS :
case WC_AI_LIST :
case WC_AI_SETTINGS :
2012-02-12 11:32:41 +01:00
case WC_TEXTFILE :
2011-11-26 17:09:25 +01:00
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
case WC_CONSOLE :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
case WC_NEWS_WINDOW :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
default :
+ + z_priority ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2011-11-26 17:09:25 +01:00
case WC_MAIN_WINDOW :
return z_priority ;
}
}
/**
* Adds a window to the z - ordering , according to its z - priority .
* @ param w Window to add
*/
static void AddWindowToZOrdering ( Window * w )
2004-08-09 19:04:08 +02:00
{
2011-11-26 17:09:25 +01:00
assert ( w - > z_front = = NULL & & w - > z_back = = NULL ) ;
if ( _z_front_window = = NULL ) {
/* It's the only window. */
_z_front_window = _z_back_window = w ;
w - > z_front = w - > z_back = NULL ;
} else {
/* Search down the z-ordering for its location. */
Window * v = _z_front_window ;
uint last_z_priority = UINT_MAX ;
2017-08-27 15:14:37 +02:00
while ( v ! = NULL & & ( v - > window_class = = WC_INVALID | | GetWindowZPriority ( v - > window_class ) > GetWindowZPriority ( w - > window_class ) ) ) {
2011-11-26 17:09:25 +01:00
if ( v - > window_class ! = WC_INVALID ) {
/* Sanity check z-ordering, while we're at it. */
2017-08-27 15:14:37 +02:00
assert ( last_z_priority > = GetWindowZPriority ( v - > window_class ) ) ;
last_z_priority = GetWindowZPriority ( v - > window_class ) ;
2011-11-26 17:09:25 +01:00
}
v = v - > z_back ;
}
2004-08-09 19:04:08 +02:00
2011-11-26 17:09:25 +01:00
if ( v = = NULL ) {
/* It's the new back window. */
w - > z_front = _z_back_window ;
w - > z_back = NULL ;
_z_back_window - > z_back = w ;
_z_back_window = w ;
} else if ( v = = _z_front_window ) {
/* It's the new front window. */
w - > z_front = NULL ;
w - > z_back = _z_front_window ;
_z_front_window - > z_front = w ;
_z_front_window = w ;
} else {
/* It's somewhere else in the z-ordering. */
w - > z_front = v - > z_front ;
w - > z_back = v ;
v - > z_front - > z_back = w ;
v - > z_front = w ;
}
}
}
2004-08-09 19:04:08 +02:00
2011-11-26 17:09:25 +01:00
/**
* Removes a window from the z - ordering .
* @ param w Window to remove
*/
static void RemoveWindowFromZOrdering ( Window * w )
{
if ( w - > z_front = = NULL ) {
assert ( _z_front_window = = w ) ;
_z_front_window = w - > z_back ;
} else {
w - > z_front - > z_back = w - > z_back ;
}
2004-08-09 19:04:08 +02:00
2009-01-07 17:11:27 +01:00
if ( w - > z_back = = NULL ) {
2011-11-26 17:09:25 +01:00
assert ( _z_back_window = = w ) ;
2009-01-07 17:11:27 +01:00
_z_back_window = w - > z_front ;
} else {
w - > z_back - > z_front = w - > z_front ;
2004-12-22 18:37:21 +01:00
}
2011-11-26 17:09:25 +01:00
w - > z_front = w - > z_back = NULL ;
}
2009-01-07 17:11:27 +01:00
2011-11-26 17:09:25 +01:00
/**
* On clicking on a window , make it the frontmost window of all windows with an equal
* or lower z - priority . The window is marked dirty for a repaint
* @ param w window that is put into the relative foreground
*/
static void BringWindowToFront ( Window * w )
{
RemoveWindowFromZOrdering ( w ) ;
AddWindowToZOrdering ( w ) ;
2009-01-07 17:11:27 +01:00
w - > SetDirty ( ) ;
2004-12-22 18:37:21 +01:00
}
2008-05-08 13:31:41 +02:00
/**
2009-07-09 21:52:47 +02:00
* Initializes the data ( except the position and initial size ) of a new Window .
2009-06-04 16:34:38 +02:00
* @ param window_number Number being assigned to the new window
2008-05-13 16:43:33 +02:00
* @ return Window pointer of the newly created window
2009-08-01 17:49:18 +02:00
* @ pre If nested widgets are used ( \ a widget is \ c NULL ) , # nested_root and # nested_array_size must be initialized .
* In addition , # nested_array is either \ c NULL , or already initialized .
2008-05-13 16:43:33 +02:00
*/
2013-05-26 21:23:42 +02:00
void Window : : InitializeData ( WindowNumber window_number )
2004-08-09 19:04:08 +02:00
{
2009-10-20 23:25:25 +02:00
/* Set up window properties; some of them are needed to set up smallest size below */
2013-05-26 21:23:42 +02:00
this - > window_class = this - > window_desc - > cls ;
2011-12-15 20:54:23 +01:00
this - > SetWhiteBorder ( ) ;
2013-05-26 21:23:42 +02:00
if ( this - > window_desc - > default_pos = = WDP_CENTER ) this - > flags | = WF_CENTERED ;
2009-10-20 23:25:25 +02:00
this - > owner = INVALID_OWNER ;
this - > nested_focus = NULL ;
this - > window_number = window_number ;
2009-11-29 22:14:34 +01:00
this - > OnInit ( ) ;
/* Initialize nested widget tree. */
2009-11-15 14:36:30 +01:00
if ( this - > nested_array = = NULL ) {
this - > nested_array = CallocT < NWidgetBase * > ( this - > nested_array_size ) ;
this - > nested_root - > SetupSmallestSize ( this , true ) ;
} else {
this - > nested_root - > SetupSmallestSize ( this , false ) ;
2009-06-04 16:34:38 +02:00
}
2009-11-15 14:36:30 +01:00
/* Initialize to smallest size. */
2010-11-13 10:56:25 +01:00
this - > nested_root - > AssignSizePosition ( ST_SMALLEST , 0 , 0 , this - > nested_root - > smallest_x , this - > nested_root - > smallest_y , _current_text_dir = = TD_RTL ) ;
2009-06-03 22:00:33 +02:00
2009-10-20 23:25:25 +02:00
/* Further set up window properties,
2009-07-09 21:52:47 +02:00
* this - > left , this - > top , this - > width , this - > height , this - > resize . width , and this - > resize . height are initialized later . */
2009-11-29 11:56:51 +01:00
this - > resize . step_width = this - > nested_root - > resize_x ;
this - > resize . step_height = this - > nested_root - > resize_y ;
2008-05-08 13:31:41 +02:00
2012-11-28 22:14:28 +01:00
/* Give focus to the opened window unless a text box
2009-02-09 02:22:29 +01:00
* of focused window has focus ( so we don ' t interrupt typing ) . But if the new
* window has a text box , then take focus anyway . */
2012-11-28 22:14:28 +01:00
if ( ! EditBoxInGlobalFocus ( ) | | this - > nested_root - > GetWidgetOfType ( WWT_EDITBOX ) ! = NULL ) SetFocusedWindow ( this ) ;
2009-02-09 02:22:29 +01:00
2011-11-26 17:09:25 +01:00
/* Insert the window into the correct location in the z-ordering. */
AddWindowToZOrdering ( this ) ;
2008-05-11 13:41:18 +02:00
}
2007-07-27 14:49:04 +02:00
2009-07-09 21:52:47 +02:00
/**
* Set the position and smallest size of the window .
* @ param x Offset in pixels from the left of the screen of the new window .
* @ param y Offset in pixels from the top of the screen of the new window .
* @ param sm_width Smallest width in pixels of the window .
* @ param sm_height Smallest height in pixels of the window .
*/
void Window : : InitializePositionSize ( int x , int y , int sm_width , int sm_height )
{
this - > left = x ;
this - > top = y ;
this - > width = sm_width ;
this - > height = sm_height ;
}
2008-05-11 13:41:18 +02:00
/**
2008-05-30 00:22:02 +02:00
* Resize window towards the default size .
* Prior to construction , a position for the new window ( for its default size )
* has been found with LocalGetWindowPlacement ( ) . Initially , the window is
* constructed with minimal size . Resizing the window to its default size is
* done here .
2008-05-11 13:41:18 +02:00
* @ param def_width default width in pixels of the window
* @ param def_height default height in pixels of the window
2009-07-09 21:52:47 +02:00
* @ see Window : : Window ( ) , Window : : InitializeData ( ) , Window : : InitializePositionSize ( )
2008-05-11 13:41:18 +02:00
*/
void Window : : FindWindowPlacementAndResize ( int def_width , int def_height )
{
2009-06-04 16:34:38 +02:00
def_width = max ( def_width , this - > width ) ; // Don't allow default size to be smaller than smallest size
def_height = max ( def_height , this - > height ) ;
2007-07-28 22:59:30 +02:00
/* Try to make windows smaller when our window is too small.
* w - > ( width | height ) is normally the same as min_ ( width | height ) ,
* but this way the GUIs can be made a little more dynamic ;
* one can use the same spec for multiple windows and those
* can then determine the real minimum size of the window . */
2008-05-08 13:31:41 +02:00
if ( this - > width ! = def_width | | this - > height ! = def_height ) {
2007-07-28 00:08:59 +02:00
/* Think about the overlapping toolbars when determining the minimum window size */
int free_height = _screen . height ;
const Window * wt = FindWindowById ( WC_STATUS_BAR , 0 ) ;
if ( wt ! = NULL ) free_height - = wt - > height ;
wt = FindWindowById ( WC_MAIN_TOOLBAR , 0 ) ;
if ( wt ! = NULL ) free_height - = wt - > height ;
2008-05-08 13:31:41 +02:00
int enlarge_x = max ( min ( def_width - this - > width , _screen . width - this - > width ) , 0 ) ;
int enlarge_y = max ( min ( def_height - this - > height , free_height - this - > height ) , 0 ) ;
2007-07-27 14:49:04 +02:00
/* X and Y has to go by step.. calculate it.
* The cast to int is necessary else x / y are implicitly casted to
* unsigned int , which won ' t work . */
2008-05-08 13:31:41 +02:00
if ( this - > resize . step_width > 1 ) enlarge_x - = enlarge_x % ( int ) this - > resize . step_width ;
if ( this - > resize . step_height > 1 ) enlarge_y - = enlarge_y % ( int ) this - > resize . step_height ;
2007-07-27 14:49:04 +02:00
2008-05-08 13:31:41 +02:00
ResizeWindow ( this , enlarge_x , enlarge_y ) ;
2010-04-04 13:21:56 +02:00
/* ResizeWindow() calls this->OnResize(). */
} else {
/* Always call OnResize; that way the scrollbars and matrices get initialized. */
this - > OnResize ( ) ;
2007-07-27 14:49:04 +02:00
}
2008-05-08 13:31:41 +02:00
int nx = this - > left ;
int ny = this - > top ;
2007-08-23 13:08:59 +02:00
2008-05-08 13:31:41 +02:00
if ( nx + this - > width > _screen . width ) nx - = ( nx + this - > width - _screen . width ) ;
2007-07-29 22:54:37 +02:00
2007-07-28 00:08:59 +02:00
const Window * wt = FindWindowById ( WC_MAIN_TOOLBAR , 0 ) ;
2008-05-11 13:41:18 +02:00
ny = max ( ny , ( wt = = NULL | | this = = wt | | this - > top = = 0 ) ? 0 : wt - > height ) ;
2007-08-23 13:08:59 +02:00
nx = max ( nx , 0 ) ;
2008-05-08 13:31:41 +02:00
if ( this - > viewport ! = NULL ) {
this - > viewport - > left + = nx - this - > left ;
this - > viewport - > top + = ny - this - > top ;
2007-08-23 13:08:59 +02:00
}
2008-05-08 13:31:41 +02:00
this - > left = nx ;
this - > top = ny ;
2007-07-28 00:08:59 +02:00
2008-05-08 13:31:41 +02:00
this - > SetDirty ( ) ;
2004-08-09 19:04:08 +02:00
}
2008-05-30 00:22:02 +02:00
/**
* Decide whether a given rectangle is a good place to open a completely visible new window .
* The new window should be within screen borders , and not overlap with another already
* existing window ( except for the main window in the background ) .
* @ param left Left edge of the rectangle
* @ param top Top edge of the rectangle
* @ param width Width of the rectangle
* @ param height Height of the rectangle
2017-08-27 13:48:38 +02:00
* @ param toolbar_y Height of main toolbar
2008-05-30 00:22:02 +02:00
* @ param pos If rectangle is good , use this parameter to return the top - left corner of the new window
* @ return Boolean indication that the rectangle is a good place for the new window
*/
2017-08-27 13:48:38 +02:00
static bool IsGoodAutoPlace1 ( int left , int top , int width , int height , int toolbar_y , Point & pos )
2004-08-09 19:04:08 +02:00
{
2007-04-26 19:32:54 +02:00
int right = width + left ;
int bottom = height + top ;
2004-08-09 19:04:08 +02:00
2017-08-27 13:48:38 +02:00
if ( left < 0 | | top < toolbar_y | | right > _screen . width | | bottom > _screen . height ) return false ;
2004-08-09 19:04:08 +02:00
2007-04-06 06:10:19 +02:00
/* Make sure it is not obscured by any window. */
2009-01-06 23:37:42 +01:00
const Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2005-11-14 20:48:04 +01:00
if ( w - > window_class = = WC_MAIN_WINDOW ) continue ;
2004-08-09 19:04:08 +02:00
2004-09-05 16:20:36 +02:00
if ( right > w - > left & &
2005-11-14 20:48:04 +01:00
w - > left + w - > width > left & &
2004-08-09 19:04:08 +02:00
bottom > w - > top & &
2005-11-14 20:48:04 +01:00
w - > top + w - > height > top ) {
return false ;
}
2004-09-05 16:20:36 +02:00
}
2004-08-09 19:04:08 +02:00
2007-04-26 19:32:54 +02:00
pos . x = left ;
pos . y = top ;
2004-08-09 19:04:08 +02:00
return true ;
}
2008-05-30 00:22:02 +02:00
/**
* Decide whether a given rectangle is a good place to open a mostly visible new window .
* The new window should be mostly within screen borders , and not overlap with another already
* existing window ( except for the main window in the background ) .
* @ param left Left edge of the rectangle
* @ param top Top edge of the rectangle
* @ param width Width of the rectangle
* @ param height Height of the rectangle
2017-08-27 13:48:38 +02:00
* @ param toolbar_y Height of main toolbar
2008-05-30 00:22:02 +02:00
* @ param pos If rectangle is good , use this parameter to return the top - left corner of the new window
* @ return Boolean indication that the rectangle is a good place for the new window
*/
2017-08-27 13:48:38 +02:00
static bool IsGoodAutoPlace2 ( int left , int top , int width , int height , int toolbar_y , Point & pos )
2004-08-09 19:04:08 +02:00
{
2017-08-27 13:48:38 +02:00
bool rtl = _current_text_dir = = TD_RTL ;
2008-05-30 00:22:02 +02:00
/* Left part of the rectangle may be at most 1/4 off-screen,
* right part of the rectangle may be at most 1 / 2 off - screen
*/
2017-08-27 13:48:38 +02:00
if ( rtl ) {
if ( left < - ( width > > 1 ) | | left > _screen . width - ( width > > 2 ) ) return false ;
} else {
if ( left < - ( width > > 2 ) | | left > _screen . width - ( width > > 1 ) ) return false ;
}
2008-05-30 00:22:02 +02:00
/* Bottom part of the rectangle may be at most 1/4 off-screen */
2017-08-27 13:48:38 +02:00
if ( top < toolbar_y | | top > _screen . height - ( height > > 2 ) ) return false ;
2004-08-09 19:04:08 +02:00
2007-04-06 06:10:19 +02:00
/* Make sure it is not obscured by any window. */
2009-01-06 23:37:42 +01:00
const Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2005-11-14 20:48:04 +01:00
if ( w - > window_class = = WC_MAIN_WINDOW ) continue ;
2004-08-09 19:04:08 +02:00
2004-09-05 16:20:36 +02:00
if ( left + width > w - > left & &
2005-11-14 20:48:04 +01:00
w - > left + w - > width > left & &
2004-08-09 19:04:08 +02:00
top + height > w - > top & &
2005-11-14 20:48:04 +01:00
w - > top + w - > height > top ) {
return false ;
}
2004-09-05 16:20:36 +02:00
}
2004-08-09 19:04:08 +02:00
2007-04-26 19:32:54 +02:00
pos . x = left ;
pos . y = top ;
2004-08-09 19:04:08 +02:00
return true ;
}
2008-05-30 00:22:02 +02:00
/**
* Find a good place for opening a new window of a given width and height .
* @ param width Width of the new window
* @ param height Height of the new window
* @ return Top - left coordinate of the new window
*/
2005-01-22 23:47:58 +01:00
static Point GetAutoPlacePosition ( int width , int height )
{
2004-08-09 19:04:08 +02:00
Point pt ;
2017-08-27 13:48:38 +02:00
bool rtl = _current_text_dir = = TD_RTL ;
2008-05-30 00:22:02 +02:00
/* First attempt, try top-left of the screen */
2010-09-17 08:12:53 +02:00
const Window * main_toolbar = FindWindowByClass ( WC_MAIN_TOOLBAR ) ;
2017-08-27 13:48:38 +02:00
const int toolbar_y = main_toolbar ! = NULL ? main_toolbar - > height : 0 ;
if ( IsGoodAutoPlace1 ( rtl ? _screen . width - width : 0 , toolbar_y , width , height , toolbar_y , pt ) ) return pt ;
2004-08-09 19:04:08 +02:00
2017-08-27 13:48:38 +02:00
/* Second attempt, try around all existing windows.
2008-05-30 00:22:02 +02:00
* The new window must be entirely on - screen , and not overlap with an existing window .
* Eight starting points are tried , two at each corner .
*/
2009-01-06 23:37:42 +01:00
const Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2005-11-14 20:48:04 +01:00
if ( w - > window_class = = WC_MAIN_WINDOW ) continue ;
2004-08-09 19:04:08 +02:00
2017-08-27 13:48:38 +02:00
if ( IsGoodAutoPlace1 ( w - > left + w - > width , w - > top , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace1 ( w - > left - width , w - > top , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace1 ( w - > left , w - > top + w - > height , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace1 ( w - > left , w - > top - height , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace1 ( w - > left + w - > width , w - > top + w - > height - height , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace1 ( w - > left - width , w - > top + w - > height - height , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace1 ( w - > left + w - > width - width , w - > top + w - > height , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace1 ( w - > left + w - > width - width , w - > top - height , width , height , toolbar_y , pt ) ) return pt ;
2004-09-05 16:20:36 +02:00
}
2017-08-27 13:48:38 +02:00
/* Third attempt, try around all existing windows.
2008-05-30 00:22:02 +02:00
* The new window may be partly off - screen , and must not overlap with an existing window .
* Only four starting points are tried .
*/
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2005-11-14 20:48:04 +01:00
if ( w - > window_class = = WC_MAIN_WINDOW ) continue ;
2004-08-09 19:04:08 +02:00
2017-08-27 13:48:38 +02:00
if ( IsGoodAutoPlace2 ( w - > left + w - > width , w - > top , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace2 ( w - > left - width , w - > top , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace2 ( w - > left , w - > top + w - > height , width , height , toolbar_y , pt ) ) return pt ;
if ( IsGoodAutoPlace2 ( w - > left , w - > top - height , width , height , toolbar_y , pt ) ) return pt ;
2004-08-09 19:04:08 +02:00
}
2017-08-27 13:48:38 +02:00
/* Fourth and final attempt, put window at diagonal starting from (0, toolbar_y), try multiples
* of the closebox
2008-05-30 00:22:02 +02:00
*/
2017-08-27 13:48:38 +02:00
int left = rtl ? _screen . width - width : 0 , top = toolbar_y ;
2017-12-09 20:21:41 +01:00
int offset_x = rtl ? - ( int ) NWidgetLeaf : : closebox_dimension . width : ( int ) NWidgetLeaf : : closebox_dimension . width ;
2017-08-27 13:48:38 +02:00
int offset_y = max < int > ( NWidgetLeaf : : closebox_dimension . height , FONT_HEIGHT_NORMAL + WD_CAPTIONTEXT_TOP + WD_CAPTIONTEXT_BOTTOM ) ;
2004-09-05 16:20:36 +02:00
2007-04-26 19:32:54 +02:00
restart :
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
if ( w - > left = = left & & w - > top = = top ) {
2017-08-27 13:48:38 +02:00
left + = offset_x ;
top + = offset_y ;
2009-01-06 23:37:42 +01:00
goto restart ;
2004-08-09 19:04:08 +02:00
}
}
2009-01-06 23:37:42 +01:00
pt . x = left ;
pt . y = top ;
return pt ;
2004-08-09 19:04:08 +02:00
}
2009-11-28 15:30:00 +01:00
/**
* Computer the position of the top - left corner of a window to be opened right
* under the toolbar .
* @ param window_width the width of the window to get the position for
* @ return Coordinate of the top - left corner of the new window .
*/
Point GetToolbarAlignedWindowPosition ( int window_width )
{
const Window * w = FindWindowById ( WC_MAIN_TOOLBAR , 0 ) ;
assert ( w ! = NULL ) ;
2010-11-13 10:56:25 +01:00
Point pt = { _current_text_dir = = TD_RTL ? w - > left : ( w - > left + w - > width ) - window_width , w - > top + w - > height } ;
2009-11-28 15:30:00 +01:00
return pt ;
}
2007-10-14 02:26:24 +02:00
/**
2008-05-04 10:49:57 +02:00
* Compute the position of the top - left corner of a new window that is opened .
2007-10-14 02:26:24 +02:00
*
2008-05-04 10:56:59 +02:00
* By default position a child window at an offset of 10 / 10 of its parent .
* With the exception of WC_BUILD_TOOLBAR ( build railway / roads / ship docks / airports )
2009-07-22 00:25:21 +02:00
* and WC_SCEN_LAND_GEN ( landscaping ) . Whose child window has an offset of 0 / toolbar - height of
2008-05-04 10:56:59 +02:00
* its parent . So it ' s exactly under the parent toolbar and no buttons will be covered .
* However if it falls too extremely outside window positions , reposition
* it to an automatic place .
*
2009-07-09 21:57:33 +02:00
* @ param * desc The pointer to the WindowDesc to be created .
* @ param sm_width Smallest width of the window .
* @ param sm_height Smallest height of the window .
* @ param window_number The window number of the new window .
2007-10-14 02:26:24 +02:00
*
2009-07-09 21:57:33 +02:00
* @ return Coordinate of the top - left corner of the new window .
2007-10-14 02:26:24 +02:00
*/
2009-07-09 21:57:33 +02:00
static Point LocalGetWindowPlacement ( const WindowDesc * desc , int16 sm_width , int16 sm_height , int window_number )
2004-08-09 19:04:08 +02:00
{
Point pt ;
2009-11-28 15:30:00 +01:00
const Window * w ;
2004-08-09 19:04:08 +02:00
2013-05-26 21:30:31 +02:00
int16 default_width = max ( desc - > GetDefaultWidth ( ) , sm_width ) ;
int16 default_height = max ( desc - > GetDefaultHeight ( ) , sm_height ) ;
2009-07-09 21:57:33 +02:00
2013-10-18 20:52:01 +02:00
if ( desc - > parent_cls ! = WC_NONE & & ( w = FindWindowById ( desc - > parent_cls , window_number ) ) ! = NULL ) {
2017-08-27 13:48:38 +02:00
bool rtl = _current_text_dir = = TD_RTL ;
if ( desc - > parent_cls = = WC_BUILD_TOOLBAR | | desc - > parent_cls = = WC_SCEN_LAND_GEN ) {
pt . x = w - > left + ( rtl ? w - > width - default_width : 0 ) ;
pt . y = w - > top + w - > height ;
return pt ;
} else {
/* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
* - Y position : closebox of parent + closebox of child + statusbar
* - X position : closebox on left / right , resizebox on right / left ( depending on ltr / rtl )
*/
int indent_y = max < int > ( NWidgetLeaf : : closebox_dimension . height , FONT_HEIGHT_NORMAL + WD_CAPTIONTEXT_TOP + WD_CAPTIONTEXT_BOTTOM ) ;
if ( w - > top + 3 * indent_y < _screen . height ) {
pt . y = w - > top + indent_y ;
int indent_close = NWidgetLeaf : : closebox_dimension . width ;
int indent_resize = NWidgetLeaf : : resizebox_dimension . width ;
if ( _current_text_dir = = TD_RTL ) {
pt . x = max ( w - > left + w - > width - default_width - indent_close , 0 ) ;
if ( pt . x + default_width > = indent_close & & pt . x + indent_resize < = _screen . width ) return pt ;
} else {
pt . x = min ( w - > left + indent_close , _screen . width - default_width ) ;
if ( pt . x + default_width > = indent_resize & & pt . x + indent_close < = _screen . width ) return pt ;
}
}
2006-11-11 10:47:44 +01:00
}
2009-11-28 15:42:35 +01:00
}
2008-05-04 10:56:59 +02:00
2009-11-28 15:42:35 +01:00
switch ( desc - > default_pos ) {
case WDP_ALIGN_TOOLBAR : // Align to the toolbar
return GetToolbarAlignedWindowPosition ( default_width ) ;
2008-05-04 10:56:59 +02:00
2009-11-28 15:42:35 +01:00
case WDP_AUTO : // Find a good automatic position for the window
return GetAutoPlacePosition ( default_width , default_height ) ;
2008-05-04 10:56:59 +02:00
2009-11-28 15:42:35 +01:00
case WDP_CENTER : // Centre the window horizontally
pt . x = ( _screen . width - default_width ) / 2 ;
pt . y = ( _screen . height - default_height ) / 2 ;
break ;
2008-05-04 10:56:59 +02:00
2009-11-28 15:42:35 +01:00
case WDP_MANUAL :
pt . x = 0 ;
pt . y = 0 ;
break ;
2008-05-04 10:56:59 +02:00
2009-11-28 15:42:35 +01:00
default :
NOT_REACHED ( ) ;
2004-08-09 19:04:08 +02:00
}
2008-05-04 10:49:57 +02:00
return pt ;
}
2013-05-26 21:23:42 +02:00
/* virtual */ Point Window : : OnInitialPosition ( int16 sm_width , int16 sm_height , int window_number )
2009-10-31 12:34:43 +01:00
{
2013-05-26 21:23:42 +02:00
return LocalGetWindowPlacement ( this - > window_desc , sm_width , sm_height , window_number ) ;
2009-10-31 12:34:43 +01:00
}
2009-06-04 16:34:38 +02:00
/**
2009-08-01 17:49:18 +02:00
* Perform the first part of the initialization of a nested widget tree .
* Construct a nested widget tree in # nested_root , and optionally fill the # nested_array array to provide quick access to the uninitialized widgets .
* This is mainly useful for setting very basic properties .
* @ param fill_nested Fill the # nested_array ( enabling is expensive ! ) .
* @ note Filling the nested array requires an additional traversal through the nested widget tree , and is best performed by # FinishInitNested rather than here .
2009-06-04 16:34:38 +02:00
*/
2013-05-26 21:23:42 +02:00
void Window : : CreateNestedTree ( bool fill_nested )
2009-06-04 16:34:38 +02:00
{
2009-07-16 00:51:37 +02:00
int biggest_index = - 1 ;
2013-05-26 21:23:42 +02:00
this - > nested_root = MakeWindowNWidgetTree ( this - > window_desc - > nwid_parts , this - > window_desc - > nwid_length , & biggest_index , & this - > shade_select ) ;
2009-08-01 17:49:18 +02:00
this - > nested_array_size = ( uint ) ( biggest_index + 1 ) ;
if ( fill_nested ) {
2009-09-19 13:31:12 +02:00
this - > nested_array = CallocT < NWidgetBase * > ( this - > nested_array_size ) ;
2009-08-01 17:49:18 +02:00
this - > nested_root - > FillNestedArray ( this - > nested_array , this - > nested_array_size ) ;
}
}
/**
* Perform the second part of the initialization of a nested widget tree .
* @ param window_number Number of the new window .
*/
2013-05-26 21:23:42 +02:00
void Window : : FinishInitNested ( WindowNumber window_number )
2009-08-01 17:49:18 +02:00
{
2013-05-26 21:23:42 +02:00
this - > InitializeData ( window_number ) ;
2013-05-26 21:27:44 +02:00
this - > ApplyDefaults ( ) ;
2013-05-26 21:23:42 +02:00
Point pt = this - > OnInitialPosition ( this - > nested_root - > smallest_x , this - > nested_root - > smallest_y , window_number ) ;
2009-07-09 21:52:47 +02:00
this - > InitializePositionSize ( pt . x , pt . y , this - > nested_root - > smallest_x , this - > nested_root - > smallest_y ) ;
2013-05-26 21:30:31 +02:00
this - > FindWindowPlacementAndResize ( this - > window_desc - > GetDefaultWidth ( ) , this - > window_desc - > GetDefaultHeight ( ) ) ;
2009-06-04 16:34:38 +02:00
}
2009-08-01 17:49:18 +02:00
/**
* Perform complete initialization of the # Window with nested widgets , to allow use .
* @ param window_number Number of the new window .
*/
2013-05-26 21:23:42 +02:00
void Window : : InitNested ( WindowNumber window_number )
2009-08-01 17:49:18 +02:00
{
2013-05-26 21:23:42 +02:00
this - > CreateNestedTree ( false ) ;
this - > FinishInitNested ( window_number ) ;
2009-08-01 17:49:18 +02:00
}
2013-05-26 21:23:42 +02:00
/**
* Empty constructor , initialization has been moved to # InitNested ( ) called from the constructor of the derived class .
* @ param desc The description of the window .
*/
2019-02-14 07:25:17 +01:00
Window : : Window ( WindowDesc * desc ) : window_desc ( desc ) , mouse_capture_widget ( - 1 )
2009-06-04 16:34:38 +02:00
{
2006-09-02 22:09:16 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Do a search for a window at specific coordinates . For this we start
2006-11-18 01:14:43 +01:00
* at the topmost window , obviously and work our way down to the bottom
2007-04-09 17:06:24 +02:00
* @ param x position x to query
* @ param y position y to query
2010-08-01 21:44:49 +02:00
* @ return a pointer to the found window if any , NULL otherwise
*/
2004-08-09 19:04:08 +02:00
Window * FindWindowFromPt ( int x , int y )
{
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
2011-08-21 18:48:31 +02:00
if ( MayBeShown ( w ) & & IsInsideBS ( x , w - > left , w - > width ) & & IsInsideBS ( y , w - > top , w - > height ) ) {
2005-11-14 20:48:04 +01:00
return w ;
}
2004-08-09 19:04:08 +02:00
}
return NULL ;
}
2008-04-13 21:01:26 +02:00
/**
* ( re ) initialize the windowing system
*/
2007-03-07 12:47:46 +01:00
void InitWindowSystem ( )
2004-09-03 21:59:05 +02:00
{
2004-08-25 10:55:53 +02:00
IConsoleClose ( ) ;
2005-03-09 20:48:20 +01:00
2009-01-07 17:11:27 +01:00
_z_back_window = NULL ;
_z_front_window = NULL ;
2009-02-09 02:22:29 +01:00
_focused_window = NULL ;
2008-04-19 15:28:48 +02:00
_mouseover_last_w = NULL ;
2011-01-18 22:08:19 +01:00
_last_scroll_window = NULL ;
2010-05-30 14:40:40 +02:00
_scrolling_viewport = false ;
2010-07-11 12:53:07 +02:00
_mouse_hovering = false ;
2009-07-12 14:19:41 +02:00
NWidgetLeaf : : InvalidateDimensionCache ( ) ; // Reset cached sizes of several widgets.
2011-10-11 10:07:47 +02:00
NWidgetScrollbar : : InvalidateDimensionCache ( ) ;
2011-12-10 16:16:58 +01:00
ShowFirstError ( ) ;
2004-08-09 19:04:08 +02:00
}
2008-04-13 21:01:26 +02:00
/**
* Close down the windowing system
*/
2007-03-07 12:47:46 +01:00
void UnInitWindowSystem ( )
2005-03-09 20:48:20 +01:00
{
2011-12-10 16:16:58 +01:00
UnshowCriticalError ( ) ;
2009-01-07 19:59:46 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) delete w ;
for ( w = _z_front_window ; w ! = NULL ; /* nothing */ ) {
Window * to_del = w ;
w = w - > z_back ;
free ( to_del ) ;
}
_z_front_window = NULL ;
_z_back_window = NULL ;
2005-03-09 20:48:20 +01:00
}
2008-04-13 21:01:26 +02:00
/**
* Reset the windowing system , by means of shutting it down followed by re - initialization
*/
2007-03-07 12:47:46 +01:00
void ResetWindowSystem ( )
2005-03-09 20:48:20 +01:00
{
UnInitWindowSystem ( ) ;
InitWindowSystem ( ) ;
2010-12-30 16:32:31 +01:00
_thd . Reset ( ) ;
2005-03-09 20:48:20 +01:00
}
2007-03-07 12:47:46 +01:00
static void DecreaseWindowCounters ( )
2004-08-09 19:04:08 +02:00
{
2018-05-10 22:49:52 +02:00
if ( _scroller_click_timeout ! = 0 ) _scroller_click_timeout - - ;
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
2010-08-20 13:55:10 +02:00
if ( _scroller_click_timeout = = 0 ) {
/* Unclick scrollbar buttons if they are pressed. */
for ( uint i = 0 ; i < w - > nested_array_size ; i + + ) {
NWidgetBase * nwid = w - > nested_array [ i ] ;
if ( nwid ! = NULL & & ( nwid - > type = = NWID_HSCROLLBAR | | nwid - > type = = NWID_VSCROLLBAR ) ) {
NWidgetScrollbar * sb = static_cast < NWidgetScrollbar * > ( nwid ) ;
if ( sb - > disp_flags & ( ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN ) ) {
sb - > disp_flags & = ~ ( ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN ) ;
2019-02-14 07:25:17 +01:00
w - > mouse_capture_widget = - 1 ;
2010-08-20 13:55:10 +02:00
sb - > SetDirty ( w ) ;
}
2010-08-12 11:14:34 +02:00
}
}
2004-08-09 19:04:08 +02:00
}
2012-11-13 22:46:33 +01:00
/* Handle editboxes */
2012-11-14 23:50:35 +01:00
for ( SmallMap < int , QueryString * > : : Pair * it = w - > querystrings . Begin ( ) ; it ! = w - > querystrings . End ( ) ; + + it ) {
it - > second - > HandleEditBox ( w , it - > first ) ;
2012-11-13 22:46:33 +01:00
}
2008-05-10 15:46:36 +02:00
w - > OnMouseLoop ( ) ;
2004-08-09 19:04:08 +02:00
}
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
2011-12-15 20:54:23 +01:00
if ( ( w - > flags & WF_TIMEOUT ) & & - - w - > timeout_timer = = 0 ) {
CLRBITS ( w - > flags , WF_TIMEOUT ) ;
2008-05-10 15:46:36 +02:00
w - > OnTimeout ( ) ;
2012-11-11 17:10:43 +01:00
w - > RaiseButtons ( true ) ;
2004-08-09 19:04:08 +02:00
}
}
}
2007-03-07 12:47:46 +01:00
static void HandlePlacePresize ( )
2004-08-09 19:04:08 +02:00
{
2005-11-14 20:48:04 +01:00
if ( _special_mouse_mode ! = WSM_PRESIZE ) return ;
2004-08-09 19:04:08 +02:00
2010-12-30 14:16:31 +01:00
Window * w = _thd . GetCallbackWnd ( ) ;
2005-11-14 20:48:04 +01:00
if ( w = = NULL ) return ;
2004-08-09 19:04:08 +02:00
2008-05-10 15:46:36 +02:00
Point pt = GetTileBelowCursor ( ) ;
if ( pt . x = = - 1 ) {
2004-08-09 19:04:08 +02:00
_thd . selend . x = - 1 ;
return ;
}
2008-05-10 15:46:36 +02:00
w - > OnPlacePresize ( pt , TileVirtXY ( pt . x , pt . y ) ) ;
2004-08-09 19:04:08 +02:00
}
2010-08-01 21:22:34 +02:00
/**
2011-01-16 12:39:09 +01:00
* Handle dragging and dropping in mouse dragging mode ( # WSM_DRAGDROP ) .
2010-05-30 14:15:28 +02:00
* @ return State of handling the event .
*/
2011-01-16 12:39:09 +01:00
static EventState HandleMouseDragDrop ( )
2004-08-09 19:04:08 +02:00
{
2011-01-16 12:39:57 +01:00
if ( _special_mouse_mode ! = WSM_DRAGDROP ) return ES_NOT_HANDLED ;
if ( _left_button_down & & _cursor . delta . x = = 0 & & _cursor . delta . y = = 0 ) return ES_HANDLED ; // Dragging, but the mouse did not move.
2004-08-09 19:04:08 +02:00
2011-01-16 12:40:36 +01:00
Window * w = _thd . GetCallbackWnd ( ) ;
2005-11-14 20:48:04 +01:00
if ( w ! = NULL ) {
2011-01-16 12:37:54 +01:00
/* Send an event in client coordinates. */
2008-05-10 15:46:36 +02:00
Point pt ;
pt . x = _cursor . pos . x - w - > left ;
pt . y = _cursor . pos . y - w - > top ;
2011-01-16 12:40:36 +01:00
if ( _left_button_down ) {
w - > OnMouseDrag ( pt , GetWidgetFromPos ( w , pt . x , pt . y ) ) ;
} else {
w - > OnDragDrop ( pt , GetWidgetFromPos ( w , pt . x , pt . y ) ) ;
}
2010-05-23 16:53:39 +02:00
}
2011-01-16 12:40:36 +01:00
if ( ! _left_button_down ) ResetObjectToPlace ( ) ; // Button released, finished dragging.
2010-05-30 14:15:28 +02:00
return ES_HANDLED ;
2010-05-23 16:53:39 +02:00
}
2010-05-30 14:18:49 +02:00
/** Report position of the mouse to the underlying window. */
static void HandleMouseOver ( )
2004-12-04 18:54:56 +01:00
{
2008-04-13 21:06:30 +02:00
Window * w = FindWindowFromPt ( _cursor . pos . x , _cursor . pos . y ) ;
2004-12-04 18:54:56 +01:00
2018-10-28 03:17:36 +01:00
/* We changed window, put an OnMouseOver event to the last window */
2008-04-13 21:06:30 +02:00
if ( _mouseover_last_w ! = NULL & & _mouseover_last_w ! = w ) {
/* Reset mouse-over coordinates of previous window */
2008-05-10 15:46:36 +02:00
Point pt = { - 1 , - 1 } ;
_mouseover_last_w - > OnMouseOver ( pt , 0 ) ;
2004-12-04 18:54:56 +01:00
}
2008-04-13 21:06:30 +02:00
/* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
_mouseover_last_w = w ;
2004-12-04 18:54:56 +01:00
2005-11-14 20:48:04 +01:00
if ( w ! = NULL ) {
2007-04-06 06:10:19 +02:00
/* send an event in client coordinates. */
2008-05-10 15:46:36 +02:00
Point pt = { _cursor . pos . x - w - > left , _cursor . pos . y - w - > top } ;
2009-11-15 14:50:49 +01:00
const NWidgetCore * widget = w - > nested_root - > GetWidgetFromPos ( pt . x , pt . y ) ;
if ( widget ! = NULL ) w - > OnMouseOver ( pt , widget - > index ) ;
2004-12-04 18:54:56 +01:00
}
}
2010-08-30 16:50:42 +02:00
/** The minimum number of pixels of the title bar must be visible in both the X or Y direction */
static const int MIN_VISIBLE_TITLE_BAR = 13 ;
/** Direction for moving the window. */
enum PreventHideDirection {
PHD_UP , ///< Above v is a safe position.
PHD_DOWN , ///< Below v is a safe position.
} ;
/**
* Do not allow hiding of the rectangle with base coordinates \ a nx and \ a ny behind window \ a v .
* If needed , move the window base coordinates to keep it visible .
* @ param nx Base horizontal coordinate of the rectangle .
* @ param ny Base vertical coordinate of the rectangle .
* @ param rect Rectangle that must stay visible for # MIN_VISIBLE_TITLE_BAR pixels ( horizontally , vertically , or both )
* @ param v Window lying in front of the rectangle .
* @ param px Previous horizontal base coordinate .
* @ param dir If no room horizontally , move the rectangle to the indicated position .
*/
static void PreventHiding ( int * nx , int * ny , const Rect & rect , const Window * v , int px , PreventHideDirection dir )
{
if ( v = = NULL ) return ;
int v_bottom = v - > top + v - > height ;
int v_right = v - > left + v - > width ;
int safe_y = ( dir = = PHD_UP ) ? ( v - > top - MIN_VISIBLE_TITLE_BAR - rect . top ) : ( v_bottom + MIN_VISIBLE_TITLE_BAR - rect . bottom ) ; // Compute safe vertical position.
if ( * ny + rect . top < = v - > top - MIN_VISIBLE_TITLE_BAR ) return ; // Above v is enough space
if ( * ny + rect . bottom > = v_bottom + MIN_VISIBLE_TITLE_BAR ) return ; // Below v is enough space
/* Vertically, the rectangle is hidden behind v. */
if ( * nx + rect . left + MIN_VISIBLE_TITLE_BAR < v - > left ) { // At left of v.
if ( v - > left < MIN_VISIBLE_TITLE_BAR ) * ny = safe_y ; // But enough room, force it to a safe position.
return ;
}
if ( * nx + rect . right - MIN_VISIBLE_TITLE_BAR > v_right ) { // At right of v.
if ( v_right > _screen . width - MIN_VISIBLE_TITLE_BAR ) * ny = safe_y ; // Not enough room, force it to a safe position.
return ;
}
/* Horizontally also hidden, force movement to a safe area. */
if ( px + rect . left < v - > left & & v - > left > = MIN_VISIBLE_TITLE_BAR ) { // Coming from the left, and enough room there.
* nx = v - > left - MIN_VISIBLE_TITLE_BAR - rect . left ;
} else if ( px + rect . right > v_right & & v_right < = _screen . width - MIN_VISIBLE_TITLE_BAR ) { // Coming from the right, and enough room there.
* nx = v_right + MIN_VISIBLE_TITLE_BAR - rect . right ;
} else {
* ny = safe_y ;
}
}
2010-08-30 16:51:11 +02:00
/**
* Make sure at least a part of the caption bar is still visible by moving
* the window if necessary .
* @ param w The window to check .
* @ param nx The proposed new x - location of the window .
* @ param ny The proposed new y - location of the window .
*/
static void EnsureVisibleCaption ( Window * w , int nx , int ny )
{
/* Search for the title bar rectangle. */
Rect caption_rect ;
const NWidgetBase * caption = w - > nested_root - > GetWidgetOfType ( WWT_CAPTION ) ;
2010-08-30 16:52:28 +02:00
if ( caption ! = NULL ) {
caption_rect . left = caption - > pos_x ;
caption_rect . right = caption - > pos_x + caption - > current_x ;
caption_rect . top = caption - > pos_y ;
caption_rect . bottom = caption - > pos_y + caption - > current_y ;
/* Make sure the window doesn't leave the screen */
nx = Clamp ( nx , MIN_VISIBLE_TITLE_BAR - caption_rect . right , _screen . width - MIN_VISIBLE_TITLE_BAR - caption_rect . left ) ;
ny = Clamp ( ny , 0 , _screen . height - MIN_VISIBLE_TITLE_BAR ) ;
/* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
PreventHiding ( & nx , & ny , caption_rect , FindWindowById ( WC_MAIN_TOOLBAR , 0 ) , w - > left , PHD_DOWN ) ;
PreventHiding ( & nx , & ny , caption_rect , FindWindowById ( WC_STATUS_BAR , 0 ) , w - > left , PHD_UP ) ;
2011-05-22 00:23:13 +02:00
}
2010-08-30 16:52:28 +02:00
2011-05-22 00:23:13 +02:00
if ( w - > viewport ! = NULL ) {
w - > viewport - > left + = nx - w - > left ;
w - > viewport - > top + = ny - w - > top ;
2010-08-30 16:51:11 +02:00
}
2011-05-22 00:23:13 +02:00
2010-08-30 16:51:11 +02:00
w - > left = nx ;
w - > top = ny ;
}
2008-04-07 22:28:58 +02:00
/**
* Resize the window .
* Update all the widgets of a window based on their resize flags
2006-12-07 01:47:35 +01:00
* Both the areas of the old window and the new sized window are set dirty
* ensuring proper redrawal .
2009-06-01 15:28:05 +02:00
* @ param w Window to resize
* @ param delta_x Delta x - size of changed window ( positive if larger , etc . )
* @ param delta_y Delta y - size of changed window
2012-04-03 22:09:41 +02:00
* @ param clamp_to_screen Whether to make sure the whole window stays visible
2008-04-07 22:28:58 +02:00
*/
2012-04-03 22:09:41 +02:00
void ResizeWindow ( Window * w , int delta_x , int delta_y , bool clamp_to_screen )
2006-12-07 01:47:35 +01:00
{
2010-04-04 22:47:51 +02:00
if ( delta_x ! = 0 | | delta_y ! = 0 ) {
2012-04-03 22:09:41 +02:00
if ( clamp_to_screen ) {
/* Determine the new right/bottom position. If that is outside of the bounds of
* the resolution clamp it in such a manner that it stays within the bounds . */
int new_right = w - > left + w - > width + delta_x ;
int new_bottom = w - > top + w - > height + delta_y ;
if ( new_right > = ( int ) _cur_resolution . width ) delta_x - = Ceil ( new_right - _cur_resolution . width , max ( 1U , w - > nested_root - > resize_x ) ) ;
if ( new_bottom > = ( int ) _cur_resolution . height ) delta_y - = Ceil ( new_bottom - _cur_resolution . height , max ( 1U , w - > nested_root - > resize_y ) ) ;
}
2011-12-04 15:00:23 +01:00
2010-04-04 22:47:51 +02:00
w - > SetDirty ( ) ;
2006-12-07 01:47:35 +01:00
2010-04-04 22:47:51 +02:00
uint new_xinc = max ( 0 , ( w - > nested_root - > resize_x = = 0 ) ? 0 : ( int ) ( w - > nested_root - > current_x - w - > nested_root - > smallest_x ) + delta_x ) ;
uint new_yinc = max ( 0 , ( w - > nested_root - > resize_y = = 0 ) ? 0 : ( int ) ( w - > nested_root - > current_y - w - > nested_root - > smallest_y ) + delta_y ) ;
assert ( w - > nested_root - > resize_x = = 0 | | new_xinc % w - > nested_root - > resize_x = = 0 ) ;
assert ( w - > nested_root - > resize_y = = 0 | | new_yinc % w - > nested_root - > resize_y = = 0 ) ;
2006-12-07 01:47:35 +01:00
2010-11-13 10:56:25 +01:00
w - > nested_root - > AssignSizePosition ( ST_RESIZE , 0 , 0 , w - > nested_root - > smallest_x + new_xinc , w - > nested_root - > smallest_y + new_yinc , _current_text_dir = = TD_RTL ) ;
2010-04-04 22:47:51 +02:00
w - > width = w - > nested_root - > current_x ;
w - > height = w - > nested_root - > current_y ;
}
2006-12-07 01:47:35 +01:00
2010-08-30 16:52:28 +02:00
EnsureVisibleCaption ( w , w - > left , w - > top ) ;
2010-04-04 22:47:51 +02:00
/* Always call OnResize to make sure everything is initialised correctly if it needs to be. */
2010-04-04 13:21:56 +02:00
w - > OnResize ( ) ;
2008-05-07 00:08:18 +02:00
w - > SetDirty ( ) ;
2006-12-07 01:47:35 +01:00
}
2005-11-04 15:01:44 +01:00
2010-08-01 21:22:34 +02:00
/**
* Return the top of the main view available for general use .
2009-11-07 18:24:04 +01:00
* @ return Uppermost vertical coordinate available .
* @ note Above the upper y coordinate is often the main toolbar .
*/
int GetMainViewTop ( )
{
Window * w = FindWindowById ( WC_MAIN_TOOLBAR , 0 ) ;
return ( w = = NULL ) ? 0 : w - > top + w - > height ;
}
2010-08-01 21:22:34 +02:00
/**
* Return the bottom of the main view available for general use .
2009-11-07 18:24:04 +01:00
* @ return The vertical coordinate of the first unusable row , so ' top + height < = bottom ' gives the correct result .
* @ note At and below the bottom y coordinate is often the status bar .
*/
int GetMainViewBottom ( )
{
Window * w = FindWindowById ( WC_STATUS_BAR , 0 ) ;
return ( w = = NULL ) ? _screen . height : w - > top ;
}
2009-03-06 04:01:35 +01:00
static bool _dragging_window ; ///< A window is being dragged or resized.
2005-11-04 15:01:44 +01:00
2010-08-01 21:22:34 +02:00
/**
* Handle dragging / resizing of a window .
2010-05-30 14:15:28 +02:00
* @ return State of handling the event .
*/
static EventState HandleWindowDragging ( )
2004-08-09 19:04:08 +02:00
{
2007-04-06 06:10:19 +02:00
/* Get out immediately if no window is being dragged at all. */
2010-05-30 14:15:28 +02:00
if ( ! _dragging_window ) return ES_NOT_HANDLED ;
2004-08-09 19:04:08 +02:00
2010-01-28 03:31:37 +01:00
/* If button still down, but cursor hasn't moved, there is nothing to do. */
2010-05-30 14:15:28 +02:00
if ( _left_button_down & & _cursor . delta . x = = 0 & & _cursor . delta . y = = 0 ) return ES_HANDLED ;
2010-01-28 03:31:37 +01:00
2007-04-06 06:10:19 +02:00
/* Otherwise find the window... */
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2011-12-15 20:54:23 +01:00
if ( w - > flags & WF_DRAGGING ) {
2007-04-06 06:10:19 +02:00
/* Stop the dragging if the left mouse button was released */
2004-08-09 19:04:08 +02:00
if ( ! _left_button_down ) {
2011-12-15 20:54:23 +01:00
w - > flags & = ~ WF_DRAGGING ;
2004-08-09 19:04:08 +02:00
break ;
}
2008-05-07 00:08:18 +02:00
w - > SetDirty ( ) ;
2004-08-09 19:04:08 +02:00
2008-05-07 10:27:55 +02:00
int x = _cursor . pos . x + _drag_delta . x ;
int y = _cursor . pos . y + _drag_delta . y ;
int nx = x ;
int ny = y ;
2004-11-10 22:14:16 +01:00
2008-05-29 17:13:28 +02:00
if ( _settings_client . gui . window_snap_radius ! = 0 ) {
2009-01-06 23:37:42 +01:00
const Window * v ;
2006-11-18 17:47:02 +01:00
2008-05-29 17:13:28 +02:00
int hsnap = _settings_client . gui . window_snap_radius ;
int vsnap = _settings_client . gui . window_snap_radius ;
2004-11-11 22:20:15 +01:00
int delta ;
2004-11-10 22:14:16 +01:00
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( v ) {
2004-11-10 22:14:16 +01:00
if ( v = = w ) continue ; // Don't snap at yourself
if ( y + w - > height > v - > top & & y < v - > top + v - > height ) {
2007-04-06 06:10:19 +02:00
/* Your left border <-> other right border */
2004-11-10 22:14:16 +01:00
delta = abs ( v - > left + v - > width - x ) ;
if ( delta < = hsnap ) {
nx = v - > left + v - > width ;
hsnap = delta ;
}
2007-04-06 06:10:19 +02:00
/* Your right border <-> other left border */
2004-11-10 22:14:16 +01:00
delta = abs ( v - > left - x - w - > width ) ;
if ( delta < = hsnap ) {
nx = v - > left - w - > width ;
hsnap = delta ;
}
}
2004-11-11 22:20:15 +01:00
if ( w - > top + w - > height > = v - > top & & w - > top < = v - > top + v - > height ) {
2007-04-06 06:10:19 +02:00
/* Your left border <-> other left border */
2004-11-11 22:20:15 +01:00
delta = abs ( v - > left - x ) ;
if ( delta < = hsnap ) {
nx = v - > left ;
hsnap = delta ;
}
2007-04-06 06:10:19 +02:00
/* Your right border <-> other right border */
2004-11-11 22:20:15 +01:00
delta = abs ( v - > left + v - > width - x - w - > width ) ;
if ( delta < = hsnap ) {
nx = v - > left + v - > width - w - > width ;
hsnap = delta ;
}
}
2004-11-10 22:14:16 +01:00
if ( x + w - > width > v - > left & & x < v - > left + v - > width ) {
2007-04-06 06:10:19 +02:00
/* Your top border <-> other bottom border */
2004-11-10 22:14:16 +01:00
delta = abs ( v - > top + v - > height - y ) ;
if ( delta < = vsnap ) {
ny = v - > top + v - > height ;
vsnap = delta ;
}
2007-04-06 06:10:19 +02:00
/* Your bottom border <-> other top border */
2004-11-10 22:14:16 +01:00
delta = abs ( v - > top - y - w - > height ) ;
if ( delta < = vsnap ) {
ny = v - > top - w - > height ;
vsnap = delta ;
}
}
2004-11-11 22:20:15 +01:00
if ( w - > left + w - > width > = v - > left & & w - > left < = v - > left + v - > width ) {
2007-04-06 06:10:19 +02:00
/* Your top border <-> other top border */
2004-11-11 22:20:15 +01:00
delta = abs ( v - > top - y ) ;
if ( delta < = vsnap ) {
ny = v - > top ;
vsnap = delta ;
}
2007-04-06 06:10:19 +02:00
/* Your bottom border <-> other bottom border */
2004-11-11 22:20:15 +01:00
delta = abs ( v - > top + v - > height - y - w - > height ) ;
if ( delta < = vsnap ) {
ny = v - > top + v - > height - w - > height ;
vsnap = delta ;
}
}
2004-08-09 19:04:08 +02:00
}
}
2004-09-05 16:20:36 +02:00
2010-08-30 16:51:11 +02:00
EnsureVisibleCaption ( w , nx , ny ) ;
2004-11-10 22:14:16 +01:00
2008-05-07 00:08:18 +02:00
w - > SetDirty ( ) ;
2010-05-30 14:15:28 +02:00
return ES_HANDLED ;
2011-12-15 20:54:23 +01:00
} else if ( w - > flags & WF_SIZING ) {
2005-01-03 20:45:18 +01:00
/* Stop the sizing if the left mouse button was released */
if ( ! _left_button_down ) {
2011-12-15 20:54:23 +01:00
w - > flags & = ~ WF_SIZING ;
2008-05-07 00:08:18 +02:00
w - > SetDirty ( ) ;
2005-01-03 20:45:18 +01:00
break ;
}
2009-03-18 02:06:48 +01:00
/* Compute difference in pixels between cursor position and reference point in the window.
* If resizing the left edge of the window , moving to the left makes the window bigger not smaller .
*/
int x , y = _cursor . pos . y - _drag_delta . y ;
2011-12-15 20:54:23 +01:00
if ( w - > flags & WF_SIZING_LEFT ) {
2009-03-18 02:06:48 +01:00
x = _drag_delta . x - _cursor . pos . x ;
} else {
x = _cursor . pos . x - _drag_delta . x ;
}
2005-01-03 20:45:18 +01:00
2009-11-29 11:56:51 +01:00
/* resize.step_width and/or resize.step_height may be 0, which means no resize is possible. */
if ( w - > resize . step_width = = 0 ) x = 0 ;
if ( w - > resize . step_height = = 0 ) y = 0 ;
2010-10-23 19:37:54 +02:00
/* Check the resize button won't go past the bottom of the screen */
if ( w - > top + w - > height + y > _screen . height ) {
y = _screen . height - w - > height - w - > top ;
}
2005-11-18 21:28:55 +01:00
/* X and Y has to go by step.. calculate it.
* The cast to int is necessary else x / y are implicitly casted to
* unsigned int , which won ' t work . */
2009-06-04 16:07:05 +02:00
if ( w - > resize . step_width > 1 ) x - = x % ( int ) w - > resize . step_width ;
2005-11-18 21:28:55 +01:00
if ( w - > resize . step_height > 1 ) y - = y % ( int ) w - > resize . step_height ;
2005-01-03 20:45:18 +01:00
2009-12-20 21:49:47 +01:00
/* Check that we don't go below the minimum set size */
if ( ( int ) w - > width + x < ( int ) w - > nested_root - > smallest_x ) {
x = w - > nested_root - > smallest_x - w - > width ;
2009-03-18 02:06:48 +01:00
}
2009-12-20 21:49:47 +01:00
if ( ( int ) w - > height + y < ( int ) w - > nested_root - > smallest_y ) {
y = w - > nested_root - > smallest_y - w - > height ;
2009-03-18 02:06:48 +01:00
}
2005-01-03 20:45:18 +01:00
/* Window already on size */
2010-05-30 14:15:28 +02:00
if ( x = = 0 & & y = = 0 ) return ES_HANDLED ;
2005-01-03 20:45:18 +01:00
2009-03-18 02:06:48 +01:00
/* Now find the new cursor pos.. this is NOT _cursor, because we move in steps. */
2005-01-03 20:45:18 +01:00
_drag_delta . y + = y ;
2011-12-15 20:54:23 +01:00
if ( ( w - > flags & WF_SIZING_LEFT ) & & x ! = 0 ) {
2009-03-18 02:06:48 +01:00
_drag_delta . x - = x ; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
w - > SetDirty ( ) ;
w - > left - = x ; // If dragging left edge, move left window edge in opposite direction by the same amount.
/* ResizeWindow() below ensures marking new position as dirty. */
} else {
_drag_delta . x + = x ;
}
2005-01-03 20:45:18 +01:00
2006-12-07 01:47:35 +01:00
/* ResizeWindow sets both pre- and after-size to dirty for redrawal */
ResizeWindow ( w , x , y ) ;
2010-05-30 14:15:28 +02:00
return ES_HANDLED ;
2004-08-09 19:04:08 +02:00
}
}
_dragging_window = false ;
2010-05-30 14:15:28 +02:00
return ES_HANDLED ;
2004-08-09 19:04:08 +02:00
}
2008-04-07 22:28:58 +02:00
/**
* Start window dragging
* @ param w Window to start dragging
*/
2006-11-18 17:47:02 +01:00
static void StartWindowDrag ( Window * w )
2004-08-09 19:04:08 +02:00
{
2011-12-15 20:54:23 +01:00
w - > flags | = WF_DRAGGING ;
w - > flags & = ~ WF_CENTERED ;
2004-08-09 19:04:08 +02:00
_dragging_window = true ;
2005-01-03 20:45:18 +01:00
2004-11-10 22:14:16 +01:00
_drag_delta . x = w - > left - _cursor . pos . x ;
_drag_delta . y = w - > top - _cursor . pos . y ;
2005-01-03 20:45:18 +01:00
2006-11-18 17:47:02 +01:00
BringWindowToFront ( w ) ;
2004-08-09 19:04:08 +02:00
DeleteWindowById ( WC_DROPDOWN_MENU , 0 ) ;
}
2008-04-07 22:28:58 +02:00
/**
2009-03-18 02:06:48 +01:00
* Start resizing a window .
* @ param w Window to start resizing .
* @ param to_left Whether to drag towards the left or not
2008-04-07 22:28:58 +02:00
*/
2009-03-18 02:06:48 +01:00
static void StartWindowSizing ( Window * w , bool to_left )
2004-08-09 19:04:08 +02:00
{
2011-12-15 20:54:23 +01:00
w - > flags | = to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT ;
w - > flags & = ~ WF_CENTERED ;
2004-08-09 19:04:08 +02:00
_dragging_window = true ;
2005-01-03 20:45:18 +01:00
_drag_delta . x = _cursor . pos . x ;
_drag_delta . y = _cursor . pos . y ;
2006-11-18 17:47:02 +01:00
BringWindowToFront ( w ) ;
2004-08-09 19:04:08 +02:00
DeleteWindowById ( WC_DROPDOWN_MENU , 0 ) ;
}
2010-08-01 21:22:34 +02:00
/**
2019-02-14 07:25:17 +01:00
* Handle scrollbar scrolling with the mouse .
* @ param w window with active scrollbar .
*/
static void HandleScrollbarScrolling ( Window * w )
{
int i ;
NWidgetScrollbar * sb = w - > GetWidget < NWidgetScrollbar > ( w - > mouse_capture_widget ) ;
bool rtl = false ;
if ( sb - > type = = NWID_HSCROLLBAR ) {
i = _cursor . pos . x - _cursorpos_drag_start . x ;
rtl = _current_text_dir = = TD_RTL ;
} else {
i = _cursor . pos . y - _cursorpos_drag_start . y ;
}
if ( sb - > disp_flags & ND_SCROLLBAR_BTN ) {
if ( _scroller_click_timeout = = 1 ) {
_scroller_click_timeout = 3 ;
sb - > UpdatePosition ( rtl = = HasBit ( sb - > disp_flags , NDB_SCROLLBAR_UP ) ? 1 : - 1 ) ;
w - > SetDirty ( ) ;
}
return ;
}
/* Find the item we want to move to and make sure it's inside bounds. */
int pos = min ( max ( 0 , i + _scrollbar_start_pos ) * sb - > GetCount ( ) / _scrollbar_size , max ( 0 , sb - > GetCount ( ) - sb - > GetCapacity ( ) ) ) ;
if ( rtl ) pos = max ( 0 , sb - > GetCount ( ) - sb - > GetCapacity ( ) - pos ) ;
if ( pos ! = sb - > GetPosition ( ) ) {
sb - > SetPosition ( pos ) ;
w - > SetDirty ( ) ;
}
}
/**
* Handle active widget ( mouse draggin on widget ) with the mouse .
2010-05-30 14:15:28 +02:00
* @ return State of handling the event .
*/
2019-02-14 07:25:17 +01:00
static EventState HandleActiveWidget ( )
2004-08-09 19:04:08 +02:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2019-02-14 07:25:17 +01:00
if ( w - > mouse_capture_widget > = 0 ) {
2007-04-06 06:10:19 +02:00
/* Abort if no button is clicked any more. */
2004-08-09 19:04:08 +02:00
if ( ! _left_button_down ) {
2019-02-14 07:25:17 +01:00
w - > mouse_capture_widget = - 1 ;
2008-05-07 00:08:18 +02:00
w - > SetDirty ( ) ;
2010-08-12 11:09:24 +02:00
return ES_HANDLED ;
2004-09-05 16:20:36 +02:00
}
2004-08-09 19:04:08 +02:00
2019-02-14 07:25:17 +01:00
/* Handle scrollbar internally, or dispatch click event */
WidgetType type = w - > GetWidget < NWidgetBase > ( w - > mouse_capture_widget ) - > type ;
if ( type = = NWID_VSCROLLBAR | | type = = NWID_HSCROLLBAR ) {
HandleScrollbarScrolling ( w ) ;
2004-08-09 19:04:08 +02:00
} else {
2019-02-22 00:53:16 +01:00
/* If cursor hasn't moved, there is nothing to do. */
if ( _cursor . delta . x = = 0 & & _cursor . delta . y = = 0 ) return ES_HANDLED ;
2019-02-14 07:25:17 +01:00
Point pt = { _cursor . pos . x - w - > left , _cursor . pos . y - w - > top } ;
w - > OnClick ( pt , w - > mouse_capture_widget , 0 ) ;
2004-08-09 19:04:08 +02:00
}
2010-05-30 14:15:28 +02:00
return ES_HANDLED ;
2004-09-05 16:20:36 +02:00
}
2004-08-09 19:04:08 +02:00
}
2004-09-05 16:20:36 +02:00
2010-08-12 11:09:24 +02:00
return ES_NOT_HANDLED ;
2004-08-09 19:04:08 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Handle viewport scrolling with the mouse .
2010-05-30 14:15:28 +02:00
* @ return State of handling the event .
*/
static EventState HandleViewportScroll ( )
2004-08-09 19:04:08 +02:00
{
2008-05-29 17:13:28 +02:00
bool scrollwheel_scrolling = _settings_client . gui . scrollwheel_scrolling = = 1 & & ( _cursor . v_wheel ! = 0 | | _cursor . h_wheel ! = 0 ) ;
2007-03-11 11:55:35 +01:00
2010-05-30 14:15:28 +02:00
if ( ! _scrolling_viewport ) return ES_NOT_HANDLED ;
2004-08-09 19:04:08 +02:00
2011-01-18 22:08:19 +01:00
/* When we don't have a last scroll window we are starting to scroll.
* When the last scroll window and this are not the same we went
* outside of the window and should not left - mouse scroll anymore . */
if ( _last_scroll_window = = NULL ) _last_scroll_window = FindWindowFromPt ( _cursor . pos . x , _cursor . pos . y ) ;
2006-08-21 16:34:59 +02:00
2018-04-28 23:27:14 +02:00
if ( _last_scroll_window = = NULL | | ! ( ( _settings_client . gui . scroll_mode ! = VSM_MAP_LMB & & _right_button_down ) | | scrollwheel_scrolling | | ( _settings_client . gui . scroll_mode = = VSM_MAP_LMB & & _left_button_down ) ) ) {
2007-11-11 13:26:44 +01:00
_cursor . fix_at = false ;
_scrolling_viewport = false ;
2011-01-18 22:08:19 +01:00
_last_scroll_window = NULL ;
2010-05-30 14:15:28 +02:00
return ES_NOT_HANDLED ;
2007-11-11 13:26:44 +01:00
}
2011-01-18 22:08:19 +01:00
if ( _last_scroll_window = = FindWindowById ( WC_MAIN_WINDOW , 0 ) & & _last_scroll_window - > viewport - > follow_vehicle ! = INVALID_VEHICLE ) {
2007-10-20 02:09:39 +02:00
/* If the main window is following a vehicle, then first let go of it! */
2011-01-18 22:08:19 +01:00
const Vehicle * veh = Vehicle : : Get ( _last_scroll_window - > viewport - > follow_vehicle ) ;
2009-03-15 16:25:18 +01:00
ScrollMainWindowTo ( veh - > x_pos , veh - > y_pos , veh - > z_pos , true ) ; // This also resets follow_vehicle
2010-05-30 14:15:28 +02:00
return ES_NOT_HANDLED ;
2007-10-20 02:09:39 +02:00
}
2008-05-10 15:46:36 +02:00
Point delta ;
2007-03-11 11:55:35 +01:00
if ( scrollwheel_scrolling ) {
/* We are using scrollwheels for scrolling */
2008-05-10 15:46:36 +02:00
delta . x = _cursor . h_wheel ;
delta . y = _cursor . v_wheel ;
2007-03-11 11:55:35 +01:00
_cursor . v_wheel = 0 ;
_cursor . h_wheel = 0 ;
2018-05-29 10:44:33 +02:00
} else {
if ( _settings_client . gui . scroll_mode ! = VSM_VIEWPORT_RMB_FIXED ) {
delta . x = - _cursor . delta . x ;
delta . y = - _cursor . delta . y ;
} else {
delta . x = _cursor . delta . x ;
delta . y = _cursor . delta . y ;
}
2007-03-11 11:55:35 +01:00
}
2006-08-21 16:34:59 +02:00
/* Create a scroll-event and send it to the window */
2011-01-18 22:08:19 +01:00
if ( delta . x ! = 0 | | delta . y ! = 0 ) _last_scroll_window - > OnScroll ( delta ) ;
2006-02-06 10:18:04 +01:00
_cursor . delta . x = 0 ;
_cursor . delta . y = 0 ;
2010-05-30 14:15:28 +02:00
return ES_HANDLED ;
2004-08-09 19:04:08 +02:00
}
2010-08-01 21:22:34 +02:00
/**
2011-11-26 17:09:25 +01:00
* Check if a window can be made relative top - most window , and if so do
2006-12-29 18:16:12 +01:00
* it . If a window does not obscure any other windows , it will not
* be brought to the foreground . Also if the only obscuring windows
* are so - called system - windows , the window will not be moved .
* The function will return false when a child window of this window is a
* modal - popup ; function returns a false and child window gets a white border
2011-11-26 17:09:25 +01:00
* @ param w Window to bring relatively on - top
2010-08-01 21:44:49 +02:00
* @ return false if the window has an active modal child , true otherwise
*/
2009-01-07 17:11:27 +01:00
static bool MaybeBringWindowToFront ( Window * w )
2004-08-09 19:04:08 +02:00
{
2006-12-29 18:16:12 +01:00
bool bring_to_front = false ;
2004-08-09 19:04:08 +02:00
2005-11-14 20:48:04 +01:00
if ( w - > window_class = = WC_MAIN_WINDOW | |
IsVitalWindow ( w ) | |
w - > window_class = = WC_TOOLTIPS | |
w - > window_class = = WC_DROPDOWN_MENU ) {
2006-12-29 18:16:12 +01:00
return true ;
2005-11-14 20:48:04 +01:00
}
2004-08-09 19:04:08 +02:00
2009-12-21 17:06:20 +01:00
/* Use unshaded window size rather than current size for shaded windows. */
int w_width = w - > width ;
int w_height = w - > height ;
if ( w - > IsShaded ( ) ) {
w_width = w - > unshaded_size . width ;
w_height = w - > unshaded_size . height ;
}
2009-01-07 19:59:46 +01:00
Window * u ;
FOR_ALL_WINDOWS_FROM_BACK_FROM ( u , w - > z_front ) {
2006-12-29 18:16:12 +01:00
/* A modal child will prevent the activation of the parent window */
2013-05-26 21:24:11 +02:00
if ( u - > parent = = w & & ( u - > window_desc - > flags & WDF_MODAL ) ) {
2011-12-15 20:54:23 +01:00
u - > SetWhiteBorder ( ) ;
2008-05-07 00:08:18 +02:00
u - > SetDirty ( ) ;
2006-12-29 18:16:12 +01:00
return false ;
}
2006-11-18 17:47:02 +01:00
2005-11-14 20:48:04 +01:00
if ( u - > window_class = = WC_MAIN_WINDOW | |
IsVitalWindow ( u ) | |
u - > window_class = = WC_TOOLTIPS | |
u - > window_class = = WC_DROPDOWN_MENU ) {
continue ;
}
2004-08-09 19:04:08 +02:00
2006-12-29 18:16:12 +01:00
/* Window sizes don't interfere, leave z-order alone */
2009-12-21 17:06:20 +01:00
if ( w - > left + w_width < = u - > left | |
2004-08-09 19:04:08 +02:00
u - > left + u - > width < = w - > left | |
2009-12-21 17:06:20 +01:00
w - > top + w_height < = u - > top | |
2005-11-14 20:48:04 +01:00
u - > top + u - > height < = w - > top ) {
continue ;
}
2004-09-05 16:20:36 +02:00
2006-12-29 18:16:12 +01:00
bring_to_front = true ;
2004-08-09 19:04:08 +02:00
}
2006-12-29 18:16:12 +01:00
if ( bring_to_front ) BringWindowToFront ( w ) ;
return true ;
2004-08-09 19:04:08 +02:00
}
2012-11-13 22:47:13 +01:00
/**
* Process keypress for editbox widget .
* @ param wid Editbox widget .
* @ param key the Unicode value of the key .
* @ param keycode the untranslated key code including shift state .
* @ return # ES_HANDLED if the key press has been handled and no other
* window should receive the event .
*/
2013-08-05 22:36:32 +02:00
EventState Window : : HandleEditBoxKey ( int wid , WChar key , uint16 keycode )
2012-11-13 22:47:13 +01:00
{
2012-11-14 23:50:35 +01:00
QueryString * query = this - > GetQueryString ( wid ) ;
2013-03-17 14:05:45 +01:00
if ( query = = NULL ) return ES_NOT_HANDLED ;
2012-11-13 22:47:13 +01:00
2012-11-14 23:50:39 +01:00
int action = QueryString : : ACTION_NOTHING ;
2013-03-17 14:05:45 +01:00
switch ( query - > text . HandleKeyPress ( key , keycode ) ) {
case HKPR_EDITING :
2012-11-28 21:54:56 +01:00
this - > SetWidgetDirty ( wid ) ;
2012-11-13 22:47:13 +01:00
this - > OnEditboxChanged ( wid ) ;
break ;
2013-03-17 14:05:45 +01:00
case HKPR_CURSOR :
2012-11-28 21:54:56 +01:00
this - > SetWidgetDirty ( wid ) ;
2012-11-28 22:14:28 +01:00
/* For the OSK also invalidate the parent window */
if ( this - > window_class = = WC_OSK ) this - > InvalidateData ( ) ;
2012-11-28 21:54:56 +01:00
break ;
2013-03-17 14:05:45 +01:00
case HKPR_CONFIRM :
2012-11-28 22:14:28 +01:00
if ( this - > window_class = = WC_OSK ) {
this - > OnClick ( Point ( ) , WID_OSK_OK , 1 ) ;
} else if ( query - > ok_button > = 0 ) {
2012-11-13 22:47:13 +01:00
this - > OnClick ( Point ( ) , query - > ok_button , 1 ) ;
2012-11-14 23:50:39 +01:00
} else {
action = query - > ok_button ;
2012-11-13 22:47:13 +01:00
}
break ;
2013-03-17 14:05:45 +01:00
case HKPR_CANCEL :
2012-11-28 22:14:28 +01:00
if ( this - > window_class = = WC_OSK ) {
this - > OnClick ( Point ( ) , WID_OSK_CANCEL , 1 ) ;
} else if ( query - > cancel_button > = 0 ) {
2012-11-13 22:47:13 +01:00
this - > OnClick ( Point ( ) , query - > cancel_button , 1 ) ;
} else {
2012-11-14 23:50:39 +01:00
action = query - > cancel_button ;
2012-11-13 22:47:13 +01:00
}
break ;
2013-03-17 14:05:45 +01:00
case HKPR_NOT_HANDLED :
return ES_NOT_HANDLED ;
2012-11-13 22:47:13 +01:00
default : break ;
}
2012-11-14 23:50:39 +01:00
switch ( action ) {
case QueryString : : ACTION_DESELECT :
this - > UnfocusFocusedWidget ( ) ;
break ;
case QueryString : : ACTION_CLEAR :
2013-08-03 23:26:49 +02:00
if ( query - > text . bytes < = 1 ) {
/* If already empty, unfocus instead */
this - > UnfocusFocusedWidget ( ) ;
} else {
query - > text . DeleteAll ( ) ;
this - > SetWidgetDirty ( wid ) ;
this - > OnEditboxChanged ( wid ) ;
}
2012-11-14 23:50:39 +01:00
break ;
default :
break ;
}
2013-03-17 14:05:45 +01:00
return ES_HANDLED ;
2012-11-13 22:47:13 +01:00
}
2010-08-01 21:22:34 +02:00
/**
* Handle keyboard input .
2013-08-05 22:36:36 +02:00
* @ param keycode Virtual keycode of the key .
* @ param key Unicode character of the key .
2008-04-07 22:28:58 +02:00
*/
2013-08-05 22:36:36 +02:00
void HandleKeypress ( uint keycode , WChar key )
2004-08-09 19:04:08 +02:00
{
2010-06-05 15:32:42 +02:00
/* World generation is multithreaded and messes with companies.
* But there is no company related window open anyway , so _current_company is not used . */
2011-08-21 14:46:46 +02:00
assert ( HasModalProgress ( ) | | IsLocalCompany ( ) ) ;
2006-11-15 20:35:52 +01:00
2008-01-01 19:55:15 +01:00
/*
* The Unicode standard defines an area called the private use area . Code points in this
* area are reserved for private use and thus not portable between systems . For instance ,
* Apple defines code points for the arrow keys in this area , but these are only printable
* on a system running OS X . We don ' t want these keys to show up in text fields and such ,
* and thus we have to clear the unicode character when we encounter such a key .
*/
2008-05-10 15:46:36 +02:00
if ( key > = 0xE000 & & key < = 0xF8FF ) key = 0 ;
2008-01-01 19:55:15 +01:00
/*
* If both key and keycode is zero , we don ' t bother to process the event .
*/
2008-05-10 15:46:36 +02:00
if ( key = = 0 & & keycode = = 0 ) return ;
2008-01-01 19:55:15 +01:00
2009-02-09 02:22:29 +01:00
/* Check if the focused window has a focused editbox */
if ( EditBoxInGlobalFocus ( ) ) {
2012-11-13 22:47:02 +01:00
/* All input will in this case go to the focused editbox */
if ( _focused_window - > window_class = = WC_CONSOLE ) {
if ( _focused_window - > OnKeyPress ( key , keycode ) = = ES_HANDLED ) return ;
} else {
2012-11-13 22:47:13 +01:00
if ( _focused_window - > HandleEditBoxKey ( _focused_window - > nested_focus - > index , key , keycode ) = = ES_HANDLED ) return ;
2012-11-13 22:47:02 +01:00
}
2007-12-06 16:58:39 +01:00
}
2009-07-13 18:41:34 +02:00
/* Call the event, start with the uppermost window, but ignore the toolbar. */
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
2009-07-13 18:41:34 +02:00
if ( w - > window_class = = WC_MAIN_TOOLBAR ) continue ;
2013-06-15 17:30:16 +02:00
if ( w - > window_desc - > hotkeys ! = NULL ) {
int hotkey = w - > window_desc - > hotkeys - > CheckMatch ( keycode ) ;
if ( hotkey > = 0 & & w - > OnHotkey ( hotkey ) = = ES_HANDLED ) return ;
}
2010-05-30 14:06:18 +02:00
if ( w - > OnKeyPress ( key , keycode ) = = ES_HANDLED ) return ;
2004-08-09 19:04:08 +02:00
}
2005-04-03 15:35:43 +02:00
2009-01-06 23:37:42 +01:00
w = FindWindowById ( WC_MAIN_TOOLBAR , 0 ) ;
2008-05-10 15:46:36 +02:00
/* When there is no toolbar w is null, check for that */
2013-06-15 17:30:16 +02:00
if ( w ! = NULL ) {
if ( w - > window_desc - > hotkeys ! = NULL ) {
int hotkey = w - > window_desc - > hotkeys - > CheckMatch ( keycode ) ;
if ( hotkey > = 0 & & w - > OnHotkey ( hotkey ) = = ES_HANDLED ) return ;
}
if ( w - > OnKeyPress ( key , keycode ) = = ES_HANDLED ) return ;
}
2010-07-03 23:43:44 +02:00
HandleGlobalHotkeys ( key , keycode ) ;
2004-08-09 19:04:08 +02:00
}
2008-04-07 22:28:58 +02:00
/**
* State of CONTROL key has changed
*/
2008-02-17 18:00:43 +01:00
void HandleCtrlChanged ( )
{
/* Call the event, start with the uppermost window. */
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
2010-05-30 14:06:18 +02:00
if ( w - > OnCTRLStateChange ( ) = = ES_HANDLED ) return ;
2008-02-17 18:00:43 +01:00
}
}
2013-08-05 22:37:06 +02:00
/**
* Insert a text string at the cursor position into the edit box widget .
* @ param wid Edit box widget .
* @ param str Text string to insert .
*/
2013-08-05 22:37:57 +02:00
/* virtual */ void Window : : InsertTextString ( int wid , const char * str , bool marked , const char * caret , const char * insert_location , const char * replacement_end )
2013-08-05 22:37:06 +02:00
{
QueryString * query = this - > GetQueryString ( wid ) ;
if ( query = = NULL ) return ;
2013-08-05 22:37:57 +02:00
if ( query - > text . InsertString ( str , marked , caret , insert_location , replacement_end ) | | marked ) {
2013-08-05 22:37:06 +02:00
this - > SetWidgetDirty ( wid ) ;
this - > OnEditboxChanged ( wid ) ;
}
}
/**
* Handle text input .
* @ param str Text string to input .
2013-08-05 22:37:25 +02:00
* @ param marked Is the input a marked composition string from an IME ?
* @ param caret Move the caret to this point in the insertion string .
2013-08-05 22:37:06 +02:00
*/
2013-08-05 22:37:57 +02:00
void HandleTextInput ( const char * str , bool marked , const char * caret , const char * insert_location , const char * replacement_end )
2013-08-05 22:37:06 +02:00
{
if ( ! EditBoxInGlobalFocus ( ) ) return ;
2013-08-05 22:37:57 +02:00
_focused_window - > InsertTextString ( _focused_window - > window_class = = WC_CONSOLE ? 0 : _focused_window - > nested_focus - > index , str , marked , caret , insert_location , replacement_end ) ;
2013-08-05 22:37:06 +02:00
}
2008-04-07 22:28:58 +02:00
/**
* Local counter that is incremented each time an mouse input event is detected .
* The counter is used to stop auto - scrolling .
* @ see HandleAutoscroll ( )
* @ see HandleMouseEvents ( )
*/
2006-11-15 22:01:19 +01:00
static int _input_events_this_tick = 0 ;
2008-04-07 22:28:58 +02:00
/**
* If needed and switched on , perform auto scrolling ( automatically
* moving window contents when mouse is near edge of the window ) .
*/
2007-03-07 12:47:46 +01:00
static void HandleAutoscroll ( )
2006-11-15 22:01:19 +01:00
{
2012-10-13 10:34:10 +02:00
if ( _game_mode = = GM_MENU | | HasModalProgress ( ) ) return ;
2012-10-13 11:16:20 +02:00
if ( _settings_client . gui . auto_scrolling = = VA_DISABLED ) return ;
if ( _settings_client . gui . auto_scrolling = = VA_MAIN_VIEWPORT_FULLSCREEN & & ! _fullscreen ) return ;
2012-10-13 10:34:10 +02:00
int x = _cursor . pos . x ;
int y = _cursor . pos . y ;
Window * w = FindWindowFromPt ( x , y ) ;
if ( w = = NULL | | w - > flags & WF_DISABLE_VP_SCROLL ) return ;
2012-10-13 11:16:20 +02:00
if ( _settings_client . gui . auto_scrolling ! = VA_EVERY_VIEWPORT & & w - > window_class ! = WC_MAIN_WINDOW ) return ;
2012-10-13 10:34:10 +02:00
ViewPort * vp = IsPtInWindowViewport ( w , x , y ) ;
if ( vp = = NULL ) return ;
x - = vp - > left ;
y - = vp - > top ;
/* here allows scrolling in both x and y axis */
2018-10-17 21:51:47 +02:00
static const int SCROLLSPEED = 3 ;
2012-10-13 10:34:10 +02:00
if ( x - 15 < 0 ) {
2018-10-17 21:51:47 +02:00
w - > viewport - > dest_scrollpos_x + = ScaleByZoom ( ( x - 15 ) * SCROLLSPEED , vp - > zoom ) ;
2012-10-13 10:34:10 +02:00
} else if ( 15 - ( vp - > width - x ) > 0 ) {
2018-10-17 21:51:47 +02:00
w - > viewport - > dest_scrollpos_x + = ScaleByZoom ( ( 15 - ( vp - > width - x ) ) * SCROLLSPEED , vp - > zoom ) ;
2006-11-15 22:01:19 +01:00
}
2012-10-13 10:34:10 +02:00
if ( y - 15 < 0 ) {
2018-10-17 21:51:47 +02:00
w - > viewport - > dest_scrollpos_y + = ScaleByZoom ( ( y - 15 ) * SCROLLSPEED , vp - > zoom ) ;
2012-10-13 10:34:10 +02:00
} else if ( 15 - ( vp - > height - y ) > 0 ) {
2018-10-17 21:51:47 +02:00
w - > viewport - > dest_scrollpos_y + = ScaleByZoom ( ( 15 - ( vp - > height - y ) ) * SCROLLSPEED , vp - > zoom ) ;
2012-10-13 10:34:10 +02:00
}
2006-11-15 22:01:19 +01:00
}
2007-06-22 12:57:53 +02:00
enum MouseClick {
MC_NONE = 0 ,
MC_LEFT ,
MC_RIGHT ,
MC_DOUBLE_LEFT ,
2010-07-11 12:53:07 +02:00
MC_HOVER ,
2007-06-22 12:57:53 +02:00
2007-06-22 22:04:21 +02:00
MAX_OFFSET_DOUBLE_CLICK = 5 , ///< How much the mouse is allowed to move to call it a double click
TIME_BETWEEN_DOUBLE_CLICK = 500 , ///< Time between 2 left clicks before it becoming a double click, in ms
2010-07-11 12:53:07 +02:00
MAX_OFFSET_HOVER = 5 , ///< Maximum mouse movement before stopping a hover event.
2007-06-22 12:57:53 +02:00
} ;
2010-05-30 14:15:28 +02:00
extern EventState VpHandlePlaceSizingDrag ( ) ;
2008-04-19 15:28:48 +02:00
2008-05-29 12:16:59 +02:00
static void ScrollMainViewport ( int x , int y )
{
if ( _game_mode ! = GM_MENU ) {
Window * w = FindWindowById ( WC_MAIN_WINDOW , 0 ) ;
assert ( w ) ;
w - > viewport - > dest_scrollpos_x + = ScaleByZoom ( x , w - > viewport - > zoom ) ;
w - > viewport - > dest_scrollpos_y + = ScaleByZoom ( y , w - > viewport - > zoom ) ;
}
}
/**
* Describes all the different arrow key combinations the game allows
* when it is in scrolling mode .
* The real arrow keys are bitwise numbered as
* 1 = left
* 2 = up
* 4 = right
* 8 = down
*/
static const int8 scrollamt [ 16 ] [ 2 ] = {
{ 0 , 0 } , ///< no key specified
{ - 2 , 0 } , ///< 1 : left
{ 0 , - 2 } , ///< 2 : up
{ - 2 , - 1 } , ///< 3 : left + up
{ 2 , 0 } , ///< 4 : right
{ 0 , 0 } , ///< 5 : left + right = nothing
{ 2 , - 1 } , ///< 6 : right + up
{ 0 , - 2 } , ///< 7 : right + left + up = up
2009-10-02 17:13:15 +02:00
{ 0 , 2 } , ///< 8 : down
{ - 2 , 1 } , ///< 9 : down + left
2008-05-29 12:16:59 +02:00
{ 0 , 0 } , ///< 10 : down + up = nothing
{ - 2 , 0 } , ///< 11 : left + up + down = left
{ 2 , 1 } , ///< 12 : down + right
{ 0 , 2 } , ///< 13 : left + right + down = down
{ 2 , 0 } , ///< 14 : right + up + down = right
{ 0 , 0 } , ///< 15 : left + up + right + down = nothing
} ;
2008-05-30 23:06:43 +02:00
static void HandleKeyScrolling ( )
2008-05-29 12:16:59 +02:00
{
2009-02-09 02:22:29 +01:00
/*
* Check that any of the dirkeys is pressed and that the focused window
2013-01-08 23:46:42 +01:00
* doesn ' t have an edit - box as focused widget .
2009-02-09 02:22:29 +01:00
*/
if ( _dirkeys & & ! EditBoxInGlobalFocus ( ) ) {
2008-05-29 12:16:59 +02:00
int factor = _shift_pressed ? 50 : 10 ;
ScrollMainViewport ( scrollamt [ _dirkeys ] [ 0 ] * factor , scrollamt [ _dirkeys ] [ 1 ] * factor ) ;
}
}
2009-11-09 17:05:24 +01:00
static void MouseLoop ( MouseClick click , int mousewheel )
2004-08-09 19:04:08 +02:00
{
2010-06-05 15:32:42 +02:00
/* World generation is multithreaded and messes with companies.
* But there is no company related window open anyway , so _current_company is not used . */
2011-08-21 14:46:46 +02:00
assert ( HasModalProgress ( ) | | IsLocalCompany ( ) ) ;
2010-06-05 15:32:42 +02:00
2004-08-09 19:04:08 +02:00
HandlePlacePresize ( ) ;
UpdateTileSelection ( ) ;
2008-05-30 23:06:43 +02:00
2010-05-30 14:15:28 +02:00
if ( VpHandlePlaceSizingDrag ( ) = = ES_HANDLED ) return ;
2011-01-16 12:39:09 +01:00
if ( HandleMouseDragDrop ( ) = = ES_HANDLED ) return ;
2010-05-30 14:15:28 +02:00
if ( HandleWindowDragging ( ) = = ES_HANDLED ) return ;
2019-02-14 07:25:17 +01:00
if ( HandleActiveWidget ( ) = = ES_HANDLED ) return ;
2010-05-30 14:15:28 +02:00
if ( HandleViewportScroll ( ) = = ES_HANDLED ) return ;
2010-05-30 14:18:49 +02:00
HandleMouseOver ( ) ;
2004-12-04 18:54:56 +01:00
2008-05-29 17:13:28 +02:00
bool scrollwheel_scrolling = _settings_client . gui . scrollwheel_scrolling = = 1 & & ( _cursor . v_wheel ! = 0 | | _cursor . h_wheel ! = 0 ) ;
2007-06-22 12:57:53 +02:00
if ( click = = MC_NONE & & mousewheel = = 0 & & ! scrollwheel_scrolling ) return ;
2004-08-09 19:04:08 +02:00
2008-05-07 10:27:55 +02:00
int x = _cursor . pos . x ;
int y = _cursor . pos . y ;
Window * w = FindWindowFromPt ( x , y ) ;
2005-11-14 20:48:04 +01:00
if ( w = = NULL ) return ;
2008-05-07 10:27:55 +02:00
2010-07-11 16:35:53 +02:00
if ( click ! = MC_HOVER & & ! MaybeBringWindowToFront ( w ) ) return ;
2008-05-07 10:27:55 +02:00
ViewPort * vp = IsPtInWindowViewport ( w , x , y ) ;
2006-08-21 16:59:58 +02:00
2011-08-21 14:46:46 +02:00
/* Don't allow any action in a viewport if either in menu or when having a modal progress window */
if ( vp ! = NULL & & ( _game_mode = = GM_MENU | | HasModalProgress ( ) ) ) return ;
2006-08-21 16:59:58 +02:00
if ( mousewheel ! = 0 ) {
2018-04-28 00:12:49 +02:00
/* Send mousewheel event to window, unless we're scrolling a viewport or the map */
if ( ! scrollwheel_scrolling | | ( vp = = NULL & & w - > window_class ! = WC_SMALLMAP ) ) w - > OnMouseWheel ( mousewheel ) ;
2006-08-21 16:59:58 +02:00
/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
2009-11-24 18:13:24 +01:00
if ( vp = = NULL ) DispatchMouseWheelEvent ( w , w - > nested_root - > GetWidgetFromPos ( x - w - > left , y - w - > top ) , mousewheel ) ;
2006-08-21 16:59:58 +02:00
}
2004-08-09 19:04:08 +02:00
if ( vp ! = NULL ) {
2018-05-29 10:44:33 +02:00
if ( scrollwheel_scrolling & & ! ( w - > flags & WF_DISABLE_VP_SCROLL ) ) {
_scrolling_viewport = true ;
_cursor . fix_at = true ;
return ;
}
2006-08-21 16:59:58 +02:00
switch ( click ) {
2007-06-22 12:57:53 +02:00
case MC_DOUBLE_LEFT :
case MC_LEFT :
2017-03-24 19:55:16 +01:00
if ( HandleViewportClicked ( vp , x , y ) ) return ;
if ( ! ( w - > flags & WF_DISABLE_VP_SCROLL ) & &
2018-04-28 23:27:14 +02:00
_settings_client . gui . scroll_mode = = VSM_MAP_LMB ) {
2010-09-06 16:14:09 +02:00
_scrolling_viewport = true ;
_cursor . fix_at = false ;
2017-03-24 19:55:16 +01:00
return ;
2006-08-21 16:59:58 +02:00
}
break ;
2004-09-05 16:20:36 +02:00
2007-06-22 12:57:53 +02:00
case MC_RIGHT :
2018-04-28 23:27:14 +02:00
if ( ! ( w - > flags & WF_DISABLE_VP_SCROLL ) & &
_settings_client . gui . scroll_mode ! = VSM_MAP_LMB ) {
2006-08-21 16:59:58 +02:00
_scrolling_viewport = true ;
2018-04-28 23:27:14 +02:00
_cursor . fix_at = ( _settings_client . gui . scroll_mode = = VSM_VIEWPORT_RMB_FIXED | |
_settings_client . gui . scroll_mode = = VSM_MAP_RMB_FIXED ) ;
2017-03-24 19:55:16 +01:00
return ;
2006-08-21 16:59:58 +02:00
}
break ;
2007-06-22 12:57:53 +02:00
default :
break ;
2004-08-09 19:04:08 +02:00
}
2017-03-24 19:55:16 +01:00
}
if ( vp = = NULL | | ( w - > flags & WF_DISABLE_VP_SCROLL ) ) {
2005-11-13 15:54:09 +01:00
switch ( click ) {
2008-05-07 23:45:27 +02:00
case MC_LEFT :
2010-01-06 21:17:46 +01:00
case MC_DOUBLE_LEFT :
2010-01-30 19:34:48 +01:00
DispatchLeftClickEvent ( w , x - w - > left , y - w - > top , click = = MC_DOUBLE_LEFT ? 2 : 1 ) ;
2018-05-29 10:44:33 +02:00
return ;
2008-05-07 23:45:27 +02:00
2007-03-11 12:57:11 +01:00
default :
if ( ! scrollwheel_scrolling | | w = = NULL | | w - > window_class ! = WC_SMALLMAP ) break ;
/* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
2009-03-14 19:16:29 +01:00
* Simulate a right button click so we can get started . */
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2009-03-14 19:16:29 +01:00
2018-05-29 10:44:33 +02:00
case MC_RIGHT :
DispatchRightClickEvent ( w , x - w - > left , y - w - > top ) ;
return ;
2010-07-11 12:58:55 +02:00
2018-05-29 10:44:33 +02:00
case MC_HOVER :
DispatchHoverEvent ( w , x - w - > left , y - w - > top ) ;
break ;
2005-11-13 15:54:09 +01:00
}
2004-08-09 19:04:08 +02:00
}
2018-05-29 10:44:33 +02:00
/* We're not doing anything with 2D scrolling, so reset the value. */
_cursor . h_wheel = 0 ;
_cursor . v_wheel = 0 ;
2004-08-09 19:04:08 +02:00
}
2008-04-07 22:28:58 +02:00
/**
* Handle a mouse event from the video driver
*/
2007-03-07 12:47:46 +01:00
void HandleMouseEvents ( )
2005-03-26 05:16:39 +01:00
{
2010-06-05 15:32:42 +02:00
/* World generation is multithreaded and messes with companies.
* But there is no company related window open anyway , so _current_company is not used . */
2011-08-21 14:46:46 +02:00
assert ( HasModalProgress ( ) | | IsLocalCompany ( ) ) ;
2010-06-05 15:32:42 +02:00
2007-06-22 12:57:53 +02:00
static int double_click_time = 0 ;
2010-07-11 12:50:47 +02:00
static Point double_click_pos = { 0 , 0 } ;
2005-03-26 05:16:39 +01:00
2007-04-06 06:10:19 +02:00
/* Mouse event? */
2008-05-07 10:27:55 +02:00
MouseClick click = MC_NONE ;
2005-03-26 05:16:39 +01:00
if ( _left_button_down & & ! _left_button_clicked ) {
2007-06-22 12:57:53 +02:00
click = MC_LEFT ;
2007-06-22 22:04:21 +02:00
if ( double_click_time ! = 0 & & _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK & &
2010-07-11 12:50:47 +02:00
double_click_pos . x ! = 0 & & abs ( _cursor . pos . x - double_click_pos . x ) < MAX_OFFSET_DOUBLE_CLICK & &
double_click_pos . y ! = 0 & & abs ( _cursor . pos . y - double_click_pos . y ) < MAX_OFFSET_DOUBLE_CLICK ) {
2007-06-22 12:57:53 +02:00
click = MC_DOUBLE_LEFT ;
}
2007-06-22 22:04:21 +02:00
double_click_time = _realtime_tick ;
2010-07-11 12:50:47 +02:00
double_click_pos = _cursor . pos ;
2005-03-26 05:16:39 +01:00
_left_button_clicked = true ;
2006-11-15 22:01:19 +01:00
_input_events_this_tick + + ;
2005-03-26 05:16:39 +01:00
} else if ( _right_button_clicked ) {
_right_button_clicked = false ;
2007-06-22 12:57:53 +02:00
click = MC_RIGHT ;
2006-11-15 22:01:19 +01:00
_input_events_this_tick + + ;
2005-03-26 05:16:39 +01:00
}
2008-05-07 10:27:55 +02:00
int mousewheel = 0 ;
2005-03-26 05:16:39 +01:00
if ( _cursor . wheel ) {
mousewheel = _cursor . wheel ;
_cursor . wheel = 0 ;
2006-11-15 22:01:19 +01:00
_input_events_this_tick + + ;
2005-03-26 05:16:39 +01:00
}
2010-07-14 22:24:45 +02:00
static uint32 hover_time = 0 ;
2010-07-11 12:53:07 +02:00
static Point hover_pos = { 0 , 0 } ;
2014-09-13 15:30:31 +02:00
if ( _settings_client . gui . hover_delay_ms > 0 ) {
2010-08-19 10:22:08 +02:00
if ( ! _cursor . in_window | | click ! = MC_NONE | | mousewheel ! = 0 | | _left_button_down | | _right_button_down | |
2010-07-14 17:08:31 +02:00
hover_pos . x = = 0 | | abs ( _cursor . pos . x - hover_pos . x ) > = MAX_OFFSET_HOVER | |
hover_pos . y = = 0 | | abs ( _cursor . pos . y - hover_pos . y ) > = MAX_OFFSET_HOVER ) {
hover_pos = _cursor . pos ;
hover_time = _realtime_tick ;
_mouse_hovering = false ;
} else {
2014-09-13 15:30:31 +02:00
if ( hover_time ! = 0 & & _realtime_tick > hover_time + _settings_client . gui . hover_delay_ms ) {
2010-07-14 17:08:31 +02:00
click = MC_HOVER ;
_input_events_this_tick + + ;
_mouse_hovering = true ;
}
2010-07-11 12:53:07 +02:00
}
}
2010-04-26 22:35:27 +02:00
/* Handle sprite picker before any GUI interaction */
if ( _newgrf_debug_sprite_picker . mode = = SPM_REDRAW & & _newgrf_debug_sprite_picker . click_time ! = _realtime_tick ) {
/* Next realtime tick? Then redraw has finished */
_newgrf_debug_sprite_picker . mode = SPM_NONE ;
InvalidateWindowData ( WC_SPRITE_ALIGNER , 0 , 1 ) ;
}
if ( click = = MC_LEFT & & _newgrf_debug_sprite_picker . mode = = SPM_WAIT_CLICK ) {
/* Mark whole screen dirty, and wait for the next realtime tick, when drawing is finished. */
2014-01-02 23:41:58 +01:00
Blitter * blitter = BlitterFactory : : GetCurrentBlitter ( ) ;
2010-04-26 22:35:27 +02:00
_newgrf_debug_sprite_picker . clicked_pixel = blitter - > MoveTo ( _screen . dst_ptr , _cursor . pos . x , _cursor . pos . y ) ;
_newgrf_debug_sprite_picker . click_time = _realtime_tick ;
_newgrf_debug_sprite_picker . sprites . Clear ( ) ;
_newgrf_debug_sprite_picker . mode = SPM_REDRAW ;
MarkWholeScreenDirty ( ) ;
} else {
MouseLoop ( click , mousewheel ) ;
}
2009-11-09 17:05:24 +01:00
/* We have moved the mouse the required distance,
* no need to move it at any later time . */
_cursor . delta . x = 0 ;
_cursor . delta . y = 0 ;
2005-03-26 05:16:39 +01:00
}
2009-01-07 17:11:27 +01:00
/**
* Check the soft limit of deletable ( non vital , non sticky ) windows .
*/
static void CheckSoftLimit ( )
{
if ( _settings_client . gui . window_soft_limit = = 0 ) return ;
for ( ; ; ) {
uint deletable_count = 0 ;
Window * w , * last_deletable = NULL ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
2011-12-15 20:54:23 +01:00
if ( w - > window_class = = WC_MAIN_WINDOW | | IsVitalWindow ( w ) | | ( w - > flags & WF_STICKY ) ) continue ;
2009-01-07 17:11:27 +01:00
last_deletable = w ;
deletable_count + + ;
}
2012-01-01 18:22:32 +01:00
/* We've not reached the soft limit yet. */
2009-01-07 17:11:27 +01:00
if ( deletable_count < = _settings_client . gui . window_soft_limit ) break ;
assert ( last_deletable ! = NULL ) ;
delete last_deletable ;
}
}
2008-04-07 22:28:58 +02:00
/**
* Regular call from the global game loop
*/
2007-03-07 12:47:46 +01:00
void InputLoop ( )
2006-11-15 22:01:19 +01:00
{
2010-06-05 15:32:42 +02:00
/* World generation is multithreaded and messes with companies.
* But there is no company related window open anyway , so _current_company is not used . */
2011-08-21 14:46:46 +02:00
assert ( HasModalProgress ( ) | | IsLocalCompany ( ) ) ;
2010-06-05 15:32:42 +02:00
2009-01-07 17:11:27 +01:00
CheckSoftLimit ( ) ;
2009-01-04 13:09:48 +01:00
2009-01-07 19:59:46 +01:00
/* Do the actual free of the deleted windows. */
for ( Window * v = _z_front_window ; v ! = NULL ; /* nothing */ ) {
Window * w = v ;
v = v - > z_back ;
if ( w - > window_class ! = WC_INVALID ) continue ;
2011-11-26 17:09:25 +01:00
RemoveWindowFromZOrdering ( w ) ;
2009-01-07 19:59:46 +01:00
free ( w ) ;
}
2009-01-04 13:09:48 +01:00
if ( _input_events_this_tick ! = 0 ) {
/* The input loop is called only once per GameLoop() - so we can clear the counter here */
_input_events_this_tick = 0 ;
/* there were some inputs this tick, don't scroll ??? */
return ;
}
/* HandleMouseEvents was already called for this tick */
2006-11-15 22:01:19 +01:00
HandleMouseEvents ( ) ;
}
2018-05-04 22:29:22 +02:00
/**
* Dispatch OnRealtimeTick event over all windows
*/
void CallWindowRealtimeTickEvent ( uint delta_ms )
{
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
w - > OnRealtimeTick ( delta_ms ) ;
}
}
2008-04-07 22:28:58 +02:00
/**
* Update the continuously changing contents of the windows , such as the viewports
*/
2007-03-07 12:47:46 +01:00
void UpdateWindows ( )
2004-08-09 19:04:08 +02:00
{
2018-05-04 22:29:22 +02:00
static uint32 last_realtime_tick = _realtime_tick ;
uint delta_ms = _realtime_tick - last_realtime_tick ;
last_realtime_tick = _realtime_tick ;
if ( delta_ms = = 0 ) return ;
2018-07-19 21:17:07 +02:00
PerformanceMeasurer framerate ( PFE_DRAWING ) ;
PerformanceAccumulator : : Reset ( PFE_DRAWWORLD ) ;
2018-05-04 22:29:22 +02:00
CallWindowRealtimeTickEvent ( delta_ms ) ;
2019-02-02 16:39:16 +01:00
# ifdef ENABLE_NETWORK
2018-05-20 10:58:36 +02:00
static GUITimer network_message_timer = GUITimer ( 1 ) ;
if ( network_message_timer . Elapsed ( delta_ms ) ) {
network_message_timer . SetInterval ( 1000 ) ;
2018-05-10 22:49:52 +02:00
NetworkChatMessageLoop ( ) ;
}
2019-01-13 17:50:21 +01:00
# endif
2018-05-10 22:49:52 +02:00
2009-01-06 23:37:42 +01:00
Window * w ;
2011-03-08 20:43:38 +01:00
2018-05-20 10:58:36 +02:00
static GUITimer window_timer = GUITimer ( 1 ) ;
if ( window_timer . Elapsed ( delta_ms ) ) {
if ( _network_dedicated ) window_timer . SetInterval ( MILLISECONDS_PER_TICK ) ;
2018-05-10 22:49:52 +02:00
extern int _caret_timer ;
_caret_timer + = 3 ;
CursorTick ( ) ;
HandleKeyScrolling ( ) ;
HandleAutoscroll ( ) ;
DecreaseWindowCounters ( ) ;
}
2018-05-20 10:58:36 +02:00
static GUITimer highlight_timer = GUITimer ( 1 ) ;
if ( highlight_timer . Elapsed ( delta_ms ) ) {
highlight_timer . SetInterval ( 450 ) ;
2011-12-19 22:05:14 +01:00
_window_highlight_colour = ! _window_highlight_colour ;
}
2018-05-11 18:52:06 +02:00
if ( ! _pause_mode | | _game_mode = = GM_EDITOR | | _settings_game . construction . command_pause_level > CMDPL_NO_CONSTRUCTION ) MoveAllTextEffects ( delta_ms ) ;
2011-03-08 20:43:38 +01:00
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
w - > ProcessScheduledInvalidations ( ) ;
2011-12-19 22:05:14 +01:00
w - > ProcessHighlightedInvalidations ( ) ;
2011-03-08 20:43:38 +01:00
}
2016-05-22 13:30:25 +02:00
/* Skip the actual drawing on dedicated servers without screen.
* But still empty the invalidation queues above . */
if ( _network_dedicated ) return ;
2018-05-20 10:58:36 +02:00
static GUITimer hundredth_timer = GUITimer ( 1 ) ;
if ( hundredth_timer . Elapsed ( delta_ms ) ) {
hundredth_timer . SetInterval ( 3000 ) ; // Historical reason: 100 * MILLISECONDS_PER_TICK
2004-08-09 19:04:08 +02:00
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
w - > OnHundredthTick ( ) ;
2004-08-09 19:04:08 +02:00
}
}
2018-05-20 10:58:36 +02:00
if ( window_timer . HasElapsed ( ) ) {
window_timer . SetInterval ( MILLISECONDS_PER_TICK ) ;
2018-05-10 22:49:52 +02:00
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
if ( ( w - > flags & WF_WHITE_BORDER ) & & - - w - > white_border_timer = = 0 ) {
CLRBITS ( w - > flags , WF_WHITE_BORDER ) ;
w - > SetDirty ( ) ;
}
2004-08-09 19:04:08 +02:00
}
2018-05-10 22:49:52 +02:00
}
2004-08-09 19:04:08 +02:00
2018-05-20 14:03:09 +02:00
DrawDirtyBlocks ( ) ;
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2009-12-21 17:06:20 +01:00
/* Update viewport only if window is not shaded. */
if ( w - > viewport ! = NULL & & ! w - > IsShaded ( ) ) UpdateViewportPosition ( w ) ;
2004-08-09 19:04:08 +02:00
}
2008-08-12 00:45:11 +02:00
NetworkDrawChatMessage ( ) ;
2007-04-06 06:10:19 +02:00
/* Redraw mouse cursor in case it was hidden */
2004-08-09 19:04:08 +02:00
DrawMouseCursor ( ) ;
2004-09-05 16:20:36 +02:00
}
2004-08-09 19:04:08 +02:00
2008-04-07 22:28:58 +02:00
/**
2008-04-19 15:05:05 +02:00
* Mark window as dirty ( in need of repainting )
* @ param cls Window class
* @ param number Window number in that class
2008-04-07 22:28:58 +02:00
*/
2009-09-13 21:15:59 +02:00
void SetWindowDirty ( WindowClass cls , WindowNumber number )
2004-08-09 19:04:08 +02:00
{
2009-01-06 23:37:42 +01:00
const Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2008-05-07 00:08:18 +02:00
if ( w - > window_class = = cls & & w - > window_number = = number ) w - > SetDirty ( ) ;
2004-08-09 19:04:08 +02:00
}
}
2008-04-19 15:05:05 +02:00
/**
2008-04-07 22:28:58 +02:00
* Mark a particular widget in a particular window as dirty ( in need of repainting )
* @ param cls Window class
* @ param number Window number in that class
* @ param widget_index Index number of the widget that needs repainting
*/
2009-09-13 21:15:59 +02:00
void SetWindowWidgetDirty ( WindowClass cls , WindowNumber number , byte widget_index )
2004-08-09 19:04:08 +02:00
{
2009-01-06 23:37:42 +01:00
const Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2005-11-14 20:48:04 +01:00
if ( w - > window_class = = cls & & w - > window_number = = number ) {
2009-09-13 21:15:59 +02:00
w - > SetWidgetDirty ( widget_index ) ;
2004-08-09 19:04:08 +02:00
}
}
}
2008-04-19 15:05:05 +02:00
/**
2008-04-07 22:28:58 +02:00
* Mark all windows of a particular class as dirty ( in need of repainting )
* @ param cls Window class
*/
2009-09-13 21:15:59 +02:00
void SetWindowClassesDirty ( WindowClass cls )
2004-08-09 19:04:08 +02:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
if ( w - > window_class = = cls ) w - > SetDirty ( ) ;
2004-08-09 19:04:08 +02:00
}
}
2011-12-15 20:54:23 +01:00
/**
* Mark this window ' s data as invalid ( in need of re - computing )
* @ param data The data to invalidate with
2013-01-08 23:46:42 +01:00
* @ param gui_scope Whether the function is called from GUI scope .
2011-12-15 20:54:23 +01:00
*/
void Window : : InvalidateData ( int data , bool gui_scope )
{
this - > SetDirty ( ) ;
if ( ! gui_scope ) {
/* Schedule GUI-scope invalidation for next redraw. */
* this - > scheduled_invalidation_data . Append ( ) = data ;
}
this - > OnInvalidateData ( data , gui_scope ) ;
}
/**
* Process all scheduled invalidations .
*/
void Window : : ProcessScheduledInvalidations ( )
{
for ( int * data = this - > scheduled_invalidation_data . Begin ( ) ; this - > window_class ! = WC_INVALID & & data ! = this - > scheduled_invalidation_data . End ( ) ; data + + ) {
this - > OnInvalidateData ( * data , true ) ;
}
this - > scheduled_invalidation_data . Clear ( ) ;
}
2011-12-19 22:05:14 +01:00
/**
* Process all invalidation of highlighted widgets .
*/
void Window : : ProcessHighlightedInvalidations ( )
{
if ( ( this - > flags & WF_HIGHLIGHTED ) = = 0 ) return ;
for ( uint i = 0 ; i < this - > nested_array_size ; i + + ) {
if ( this - > IsWidgetHighlighted ( i ) ) this - > SetWidgetDirty ( i ) ;
}
}
2008-04-07 22:28:58 +02:00
/**
2008-04-19 15:05:05 +02:00
* Mark window data of the window of a given class and specific window number as invalid ( in need of re - computing )
2011-03-13 22:32:13 +01:00
*
* Note that by default the invalidation is not considered to be called from GUI scope .
* That means only a part of invalidation is executed immediately . The rest is scheduled for the next redraw .
2011-02-23 21:54:55 +01:00
* The asynchronous execution is important to prevent GUI code being executed from command scope .
2011-03-13 22:32:13 +01:00
* When not in GUI - scope :
* - OnInvalidateData ( ) may not do test - runs on commands , as they might affect the execution of
* the command which triggered the invalidation . ( town rating and such )
* - OnInvalidateData ( ) may not rely on _current_company = = _local_company .
* This implies that no NewGRF callbacks may be run .
*
* However , when invalidations are scheduled , then multiple calls may be scheduled before execution starts . Earlier scheduled
* invalidations may be called with invalidation - data , which is already invalid at the point of execution .
* That means some stuff requires to be executed immediately in command scope , while not everything may be executed in command
* scope . While GUI - scope calls have no restrictions on what they may do , they cannot assume the game to still be in the state
* when the invalidation was scheduled ; passed IDs may have got invalid in the mean time .
*
* Finally , note that invalidations triggered from commands or the game loop result in OnInvalidateData ( ) being called twice .
* Once in command - scope , once in GUI - scope . So make sure to not process differential - changes twice .
*
2008-04-07 22:28:58 +02:00
* @ param cls Window class
* @ param number Window number within the class
2009-09-19 11:51:14 +02:00
* @ param data The data to invalidate with
2011-03-13 22:32:13 +01:00
* @ param gui_scope Whether the call is done from GUI scope
2008-04-07 22:28:58 +02:00
*/
2011-03-13 22:32:13 +01:00
void InvalidateWindowData ( WindowClass cls , WindowNumber number , int data , bool gui_scope )
2006-10-05 14:59:28 +02:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2011-02-23 21:54:55 +01:00
if ( w - > window_class = = cls & & w - > window_number = = number ) {
2011-03-13 22:32:13 +01:00
w - > InvalidateData ( data , gui_scope ) ;
2011-02-23 21:54:55 +01:00
}
2006-10-07 16:30:13 +02:00
}
}
2008-04-07 22:28:58 +02:00
/**
* Mark window data of all windows of a given class as invalid ( in need of re - computing )
2011-03-13 22:32:13 +01:00
* Note that by default the invalidation is not considered to be called from GUI scope .
* See InvalidateWindowData ( ) for details on GUI - scope vs . command - scope .
2008-04-07 22:28:58 +02:00
* @ param cls Window class
2009-09-19 11:51:14 +02:00
* @ param data The data to invalidate with
2011-03-13 22:32:13 +01:00
* @ param gui_scope Whether the call is done from GUI scope
2008-04-07 22:28:58 +02:00
*/
2011-03-13 22:32:13 +01:00
void InvalidateWindowClassesData ( WindowClass cls , int data , bool gui_scope )
2006-10-07 16:30:13 +02:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
2006-10-07 16:30:13 +02:00
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2011-02-23 21:54:55 +01:00
if ( w - > window_class = = cls ) {
2011-03-13 22:32:13 +01:00
w - > InvalidateData ( data , gui_scope ) ;
2011-02-23 21:54:55 +01:00
}
2006-10-05 14:59:28 +02:00
}
}
2004-08-09 19:04:08 +02:00
2008-04-07 22:28:58 +02:00
/**
2019-01-13 00:23:23 +01:00
* Dispatch OnGameTick event over all windows
2008-04-07 22:28:58 +02:00
*/
2018-05-04 22:29:22 +02:00
void CallWindowGameTickEvent ( )
2004-08-09 19:04:08 +02:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
FOR_ALL_WINDOWS_FROM_FRONT ( w ) {
2018-05-04 22:29:22 +02:00
w - > OnGameTick ( ) ;
2004-08-09 19:04:08 +02:00
}
}
2008-04-19 15:28:48 +02:00
/**
* Try to delete a non - vital window .
* Non - vital windows are windows other than the game selection , main toolbar ,
* status bar , toolbar menu , and tooltip windows . Stickied windows are also
* considered vital .
*/
2007-03-07 12:47:46 +01:00
void DeleteNonVitalWindows ( )
2004-08-09 19:04:08 +02:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
2005-11-14 20:48:04 +01:00
2006-11-18 01:44:09 +01:00
restart_search :
/* When we find the window to delete, we need to restart the search
2006-11-18 17:47:02 +01:00
* as deleting this window could cascade in deleting ( many ) others
* anywhere in the z - array */
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2004-08-09 19:04:08 +02:00
if ( w - > window_class ! = WC_MAIN_WINDOW & &
w - > window_class ! = WC_SELECT_GAME & &
w - > window_class ! = WC_MAIN_TOOLBAR & &
w - > window_class ! = WC_STATUS_BAR & &
2004-12-16 00:33:04 +01:00
w - > window_class ! = WC_TOOLTIPS & &
2011-12-15 20:54:23 +01:00
( w - > flags & WF_STICKY ) = = 0 ) { // do not delete windows which are 'pinned'
2006-11-18 17:47:02 +01:00
2008-05-06 23:28:30 +02:00
delete w ;
2006-11-18 01:44:09 +01:00
goto restart_search ;
2004-08-09 19:04:08 +02:00
}
}
}
2010-08-01 21:22:34 +02:00
/**
* It is possible that a stickied window gets to a position where the
2004-12-22 18:37:21 +01:00
* ' close ' button is outside the gaming area . You cannot close it then ; except
* with this function . It closes all windows calling the standard function ,
* then , does a little hacked loop of closing all stickied windows . Note
2010-08-01 21:44:49 +02:00
* that standard windows ( status bar , etc . ) are not stickied , so these aren ' t affected
*/
2007-03-07 12:47:46 +01:00
void DeleteAllNonVitalWindows ( )
2004-12-22 18:37:21 +01:00
{
2009-01-06 23:37:42 +01:00
Window * w ;
2005-11-14 20:48:04 +01:00
2006-11-18 01:44:09 +01:00
/* Delete every window except for stickied ones, then sticky ones as well */
2004-12-22 18:37:21 +01:00
DeleteNonVitalWindows ( ) ;
2006-11-18 17:47:02 +01:00
2006-11-18 01:44:09 +01:00
restart_search :
/* When we find the window to delete, we need to restart the search
2006-11-18 17:47:02 +01:00
* as deleting this window could cascade in deleting ( many ) others
* anywhere in the z - array */
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2011-12-15 20:54:23 +01:00
if ( w - > flags & WF_STICKY ) {
2009-01-06 23:37:42 +01:00
delete w ;
2006-11-18 01:44:09 +01:00
goto restart_search ;
2006-06-27 23:25:53 +02:00
}
2004-12-22 18:37:21 +01:00
}
}
2019-02-17 00:15:58 +01:00
/**
* Delete all messages and their corresponding window ( if any ) .
*/
void DeleteAllMessages ( )
{
InitNewsItemStructs ( ) ;
InvalidateWindowData ( WC_STATUS_BAR , 0 , SBI_NEWS_DELETED ) ; // invalidate the statusbar
InvalidateWindowData ( WC_MESSAGE_HISTORY , 0 ) ; // invalidate the message history
DeleteWindowById ( WC_NEWS_WINDOW , 0 ) ; // close newspaper or general message window if shown
}
2009-02-04 17:59:41 +01:00
/**
* Delete all windows that are used for construction of vehicle etc .
* Once done with that invalidate the others to ensure they get refreshed too .
*/
void DeleteConstructionWindows ( )
{
Window * w ;
restart_search :
/* When we find the window to delete, we need to restart the search
* as deleting this window could cascade in deleting ( many ) others
* anywhere in the z - array */
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2013-05-26 21:24:11 +02:00
if ( w - > window_desc - > flags & WDF_CONSTRUCTION ) {
2009-02-04 17:59:41 +01:00
delete w ;
goto restart_search ;
}
}
FOR_ALL_WINDOWS_FROM_BACK ( w ) w - > SetDirty ( ) ;
}
2007-04-09 17:06:24 +02:00
/** Delete all always on-top windows to get an empty screen */
2007-03-07 12:47:46 +01:00
void HideVitalWindows ( )
2005-01-11 01:54:06 +01:00
{
DeleteWindowById ( WC_MAIN_TOOLBAR , 0 ) ;
DeleteWindowById ( WC_STATUS_BAR , 0 ) ;
}
2009-06-28 22:09:40 +02:00
/** Re-initialize all windows. */
void ReInitAllWindows ( )
{
2009-07-12 14:19:41 +02:00
NWidgetLeaf : : InvalidateDimensionCache ( ) ; // Reset cached sizes of several widgets.
2011-10-11 10:07:47 +02:00
NWidgetScrollbar : : InvalidateDimensionCache ( ) ;
2009-06-28 22:09:40 +02:00
2014-10-11 15:22:37 +02:00
extern void InitDepotWindowBlockSizes ( ) ;
InitDepotWindowBlockSizes ( ) ;
2009-07-12 14:19:41 +02:00
Window * w ;
2009-06-28 22:09:40 +02:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
w - > ReInit ( ) ;
}
2010-08-31 14:18:28 +02:00
# ifdef ENABLE_NETWORK
void NetworkReInitChatBoxSize ( ) ;
NetworkReInitChatBoxSize ( ) ;
# endif
2009-07-25 17:58:48 +02:00
/* Make sure essential parts of all windows are visible */
RelocateAllWindows ( _cur_resolution . width , _cur_resolution . height ) ;
MarkWholeScreenDirty ( ) ;
2009-06-28 22:09:40 +02:00
}
2008-04-07 22:28:58 +02:00
/**
2010-11-14 00:40:36 +01:00
* ( Re ) position a window at the screen .
* @ param w Window structure of the window , may also be \ c NULL .
* @ param clss The class of the window to position .
* @ param setting The actual setting used for the window ' s position .
* @ return X coordinate of left edge of the repositioned window .
2008-04-07 22:28:58 +02:00
*/
2010-11-14 00:40:36 +01:00
static int PositionWindow ( Window * w , WindowClass clss , int setting )
2004-08-16 23:02:06 +02:00
{
2010-11-14 00:40:36 +01:00
if ( w = = NULL | | w - > window_class ! = clss ) {
w = FindWindowById ( clss , 0 ) ;
2006-06-27 23:25:53 +02:00
}
2010-11-18 20:24:40 +01:00
if ( w = = NULL ) return 0 ;
2004-08-16 23:02:06 +02:00
2010-11-18 20:24:40 +01:00
int old_left = w - > left ;
2010-11-14 00:40:36 +01:00
switch ( setting ) {
2006-11-11 10:47:44 +01:00
case 1 : w - > left = ( _screen . width - w - > width ) / 2 ; break ;
2005-07-09 00:25:24 +02:00
case 2 : w - > left = _screen . width - w - > width ; break ;
2010-11-14 00:40:36 +01:00
default : w - > left = 0 ; break ;
2004-08-16 23:02:06 +02:00
}
2010-11-18 20:24:40 +01:00
if ( w - > viewport ! = NULL ) w - > viewport - > left + = w - > left - old_left ;
2010-11-14 00:40:36 +01:00
SetDirtyBlocks ( 0 , w - > top , _screen . width , w - > top + w - > height ) ; // invalidate the whole row
2004-08-16 23:02:06 +02:00
return w - > left ;
}
2010-11-14 00:40:36 +01:00
/**
* ( Re ) position main toolbar window at the screen .
* @ param w Window structure of the main toolbar window , may also be \ c NULL .
* @ return X coordinate of left edge of the repositioned toolbar window .
*/
int PositionMainToolbar ( Window * w )
{
DEBUG ( misc , 5 , " Repositioning Main Toolbar... " ) ;
return PositionWindow ( w , WC_MAIN_TOOLBAR , _settings_client . gui . toolbar_pos ) ;
}
/**
* ( Re ) position statusbar window at the screen .
* @ param w Window structure of the statusbar window , may also be \ c NULL .
* @ return X coordinate of left edge of the repositioned statusbar .
*/
int PositionStatusbar ( Window * w )
{
DEBUG ( misc , 5 , " Repositioning statusbar... " ) ;
return PositionWindow ( w , WC_STATUS_BAR , _settings_client . gui . statusbar_pos ) ;
}
2010-11-18 20:24:40 +01:00
/**
* ( Re ) position news message window at the screen .
* @ param w Window structure of the news message window , may also be \ c NULL .
* @ return X coordinate of left edge of the repositioned news message .
*/
int PositionNewsMessage ( Window * w )
{
DEBUG ( misc , 5 , " Repositioning news message... " ) ;
return PositionWindow ( w , WC_NEWS_WINDOW , _settings_client . gui . statusbar_pos ) ;
}
2011-02-11 19:24:12 +01:00
/**
* ( Re ) position network chat window at the screen .
* @ param w Window structure of the network chat window , may also be \ c NULL .
2013-01-08 23:46:42 +01:00
* @ return X coordinate of left edge of the repositioned network chat window .
2011-02-11 19:24:12 +01:00
*/
int PositionNetworkChatWindow ( Window * w )
{
DEBUG ( misc , 5 , " Repositioning network chat window... " ) ;
return PositionWindow ( w , WC_SEND_NETWORK_MSG , _settings_client . gui . statusbar_pos ) ;
}
2009-08-10 22:06:39 +02:00
/**
* Switches viewports following vehicles , which get autoreplaced
* @ param from_index the old vehicle ID
* @ param to_index the new vehicle ID
*/
void ChangeVehicleViewports ( VehicleID from_index , VehicleID to_index )
{
Window * w ;
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
if ( w - > viewport ! = NULL & & w - > viewport - > follow_vehicle = = from_index ) {
w - > viewport - > follow_vehicle = to_index ;
w - > SetDirty ( ) ;
}
}
}
2008-04-07 22:28:58 +02:00
/**
* Relocate all windows to fit the new size of the game application screen
* @ param neww New width of the game application screen
2010-07-31 23:02:56 +02:00
* @ param newh New height of the game application screen .
2008-04-07 22:28:58 +02:00
*/
2004-08-09 19:04:08 +02:00
void RelocateAllWindows ( int neww , int newh )
{
2019-01-14 21:21:35 +01:00
DeleteWindowById ( WC_DROPDOWN_MENU , 0 ) ;
2004-08-09 19:04:08 +02:00
2019-01-14 21:21:35 +01:00
Window * w ;
2009-01-06 23:37:42 +01:00
FOR_ALL_WINDOWS_FROM_BACK ( w ) {
2004-08-09 19:04:08 +02:00
int left , top ;
2010-07-31 23:02:56 +02:00
/* XXX - this probably needs something more sane. For example specifying
* in a ' backup ' - desc that the window should always be centered . */
2006-02-06 10:18:04 +01:00
switch ( w - > window_class ) {
2013-10-19 17:28:54 +02:00
case WC_MAIN_WINDOW :
2011-11-17 22:17:17 +01:00
case WC_BOOTSTRAP :
ResizeWindow ( w , neww , newh ) ;
continue ;
2006-02-06 10:18:04 +01:00
case WC_MAIN_TOOLBAR :
2015-02-13 22:13:45 +01:00
ResizeWindow ( w , min ( neww , _toolbar_width ) - w - > width , 0 , false ) ;
2007-07-29 21:18:22 +02:00
2006-02-06 10:18:04 +01:00
top = w - > top ;
left = PositionMainToolbar ( w ) ; // changes toolbar orientation
break ;
case WC_NEWS_WINDOW :
top = newh - w - > height ;
2010-11-18 20:24:40 +01:00
left = PositionNewsMessage ( w ) ;
2006-02-06 10:18:04 +01:00
break ;
case WC_STATUS_BAR :
2015-02-13 22:13:45 +01:00
ResizeWindow ( w , min ( neww , _toolbar_width ) - w - > width , 0 , false ) ;
2010-11-14 00:40:36 +01:00
2006-02-06 10:18:04 +01:00
top = newh - w - > height ;
2010-11-14 00:40:36 +01:00
left = PositionStatusbar ( w ) ;
2006-02-06 10:18:04 +01:00
break ;
case WC_SEND_NETWORK_MSG :
2015-02-13 22:13:45 +01:00
ResizeWindow ( w , min ( neww , _toolbar_width ) - w - > width , 0 , false ) ;
2009-11-02 11:15:48 +01:00
top = newh - w - > height - FindWindowById ( WC_STATUS_BAR , 0 ) - > height ;
2011-02-11 19:24:12 +01:00
left = PositionNetworkChatWindow ( w ) ;
2006-02-06 10:18:04 +01:00
break ;
2006-11-18 18:07:05 +01:00
case WC_CONSOLE :
2006-11-21 23:10:52 +01:00
IConsoleResize ( w ) ;
2006-11-19 23:24:18 +01:00
continue ;
2006-11-18 18:07:05 +01:00
2008-03-15 21:32:42 +01:00
default : {
2011-12-15 20:54:23 +01:00
if ( w - > flags & WF_CENTERED ) {
2010-05-01 17:32:17 +02:00
top = ( newh - w - > height ) > > 1 ;
left = ( neww - w - > width ) > > 1 ;
break ;
}
2006-02-06 10:18:04 +01:00
left = w - > left ;
if ( left + ( w - > width > > 1 ) > = neww ) left = neww - w - > width ;
2007-12-06 21:55:48 +01:00
if ( left < 0 ) left = 0 ;
2006-02-06 10:18:04 +01:00
top = w - > top ;
if ( top + ( w - > height > > 1 ) > = newh ) top = newh - w - > height ;
2010-08-01 20:53:30 +02:00
break ;
}
2004-08-09 19:04:08 +02:00
}
2011-05-22 00:22:02 +02:00
EnsureVisibleCaption ( w , left , top ) ;
2004-08-09 19:04:08 +02:00
}
}
2008-05-17 05:29:16 +02:00
2010-08-01 21:22:34 +02:00
/**
* Destructor of the base class PickerWindowBase
2008-05-17 05:29:16 +02:00
* Main utility is to stop the base Window destructor from triggering
* a free while the child will already be free , in this case by the ResetObjectToPlace ( ) .
*/
PickerWindowBase : : ~ PickerWindowBase ( )
{
this - > window_class = WC_INVALID ; // stop the ancestor from freeing the already (to be) child
ResetObjectToPlace ( ) ;
}