summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2009-01-07 16:11:27 +0000
committerrubidium <rubidium@openttd.org>2009-01-07 16:11:27 +0000
commit567b2629019678269cf017d5fab9ec3b24b8052e (patch)
tree6e7a2129d3a3830aef49e3d4654b27dfd3f2d9c5 /src
parent32d65845d4d81bd9e908ee9e1de2e1467f0349c3 (diff)
downloadopenttd-567b2629019678269cf017d5fab9ec3b24b8052e.tar.xz
(svn r14899) -Feature: remove the window limit, but leave a configurable limit on the number of non-sticky non-vital windows.
-Fix [FS#2499]: crashes/misbehaviours when (almost) all windows are stickied.
Diffstat (limited to 'src')
-rw-r--r--src/lang/english.txt2
-rw-r--r--src/settings.cpp1
-rw-r--r--src/settings_gui.cpp1
-rw-r--r--src/settings_type.h1
-rw-r--r--src/viewport.cpp24
-rw-r--r--src/window.cpp232
-rw-r--r--src/window_gui.h11
7 files changed, 135 insertions, 137 deletions
diff --git a/src/lang/english.txt b/src/lang/english.txt
index bb2f83520..77b1c2493 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1197,6 +1197,8 @@ STR_CONFIG_PATCHES_TOOLBAR_POS_CENTER :Centre
STR_CONFIG_PATCHES_TOOLBAR_POS_RIGHT :Right
STR_CONFIG_PATCHES_SNAP_RADIUS :{LTBLUE}Window snap radius: {ORANGE}{STRING1} px
STR_CONFIG_PATCHES_SNAP_RADIUS_DISABLED :{LTBLUE}Window snap radius: {ORANGE}disabled
+STR_CONFIG_PATCHES_SOFT_LIMIT :{LTBLUE}Window soft limit (non-sticky): {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_SOFT_LIMIT_DISABLED :{LTBLUE}Window soft limit (non-sticky): {ORANGE}disabled
STR_CONFIG_PATCHES_TOWN_GROWTH :{LTBLUE}Town growth speed: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_TOWN_GROWTH_NONE :None
STR_CONFIG_PATCHES_TOWN_GROWTH_SLOW :Slow
diff --git a/src/settings.cpp b/src/settings.cpp
index 7fae6bfcc..846e3312a 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1429,6 +1429,7 @@ const SettingDesc _patch_settings[] = {
SDTC_VAR(gui.errmsg_duration, SLE_UINT8, S, 0, 5, 0, 20, 0, STR_CONFIG_PATCHES_ERRMSG_DURATION, NULL),
SDTC_VAR(gui.toolbar_pos, SLE_UINT8, S, MS, 0, 0, 2, 0, STR_CONFIG_PATCHES_TOOLBAR_POS, v_PositionMainToolbar),
SDTC_VAR(gui.window_snap_radius, SLE_UINT8, S, D0, 10, 1, 32, 0, STR_CONFIG_PATCHES_SNAP_RADIUS, NULL),
+ SDTC_VAR(gui.window_soft_limit, SLE_UINT8, S, D0, 20, 5, 255, 1, STR_CONFIG_PATCHES_SOFT_LIMIT, NULL),
SDTC_BOOL(gui.population_in_label, S, 0, true, STR_CONFIG_PATCHES_POPULATION_IN_LABEL, PopulationInLabelActive),
SDTC_BOOL(gui.link_terraform_toolbar, S, 0, false, STR_CONFIG_PATCHES_LINK_TERRAFORM_TOOLBAR, NULL),
SDTC_VAR(gui.liveries, SLE_UINT8, S, MS, 2, 0, 2, 0, STR_CONFIG_PATCHES_LIVERIES, RedrawScreen),
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 30bad8889..9e3a3f391 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -618,6 +618,7 @@ static const char *_patches_ui[] = {
"gui.toolbar_pos",
"gui.measure_tooltip",
"gui.window_snap_radius",
+ "gui.window_soft_limit",
"gui.population_in_label",
"gui.link_terraform_toolbar",
"gui.liveries",
diff --git a/src/settings_type.h b/src/settings_type.h
index f75fb2d30..1adf9c504 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -57,6 +57,7 @@ struct GUISettings {
uint8 default_rail_type; ///< the default rail type for the rail GUI
uint8 toolbar_pos; ///< position of toolbars, 0=left, 1=center, 2=right
uint8 window_snap_radius; ///< windows snap at each other if closer than this
+ uint8 window_soft_limit; ///< soft limit of maximum number of non-stickied non-vital windows (0 = no limit)
bool always_build_infrastructure; ///< always allow building of infrastructure, even when you do not have the vehicles for it
byte autosave; ///< how often should we do autosaves?
bool keep_all_autosave; ///< name the autosave in a different way
diff --git a/src/viewport.cpp b/src/viewport.cpp
index 60d87b207..64f4898a1 100644
--- a/src/viewport.cpp
+++ b/src/viewport.cpp
@@ -217,38 +217,36 @@ void InitializeWindowViewport(Window *w, int x, int y,
static Point _vp_move_offs;
-static void DoSetViewportPosition(Window* const *wz, int left, int top, int width, int height)
+static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height)
{
- for (; wz != _last_z_window; wz++) {
- const Window *w = *wz;
-
+ for (; w != NULL; w = w->z_front) {
if (left + width > w->left &&
w->left + w->width > left &&
top + height > w->top &&
w->top + w->height > top) {
if (left < w->left) {
- DoSetViewportPosition(wz, left, top, w->left - left, height);
- DoSetViewportPosition(wz, left + (w->left - left), top, width - (w->left - left), height);
+ DoSetViewportPosition(w, left, top, w->left - left, height);
+ DoSetViewportPosition(w, left + (w->left - left), top, width - (w->left - left), height);
return;
}
if (left + width > w->left + w->width) {
- DoSetViewportPosition(wz, left, top, (w->left + w->width - left), height);
- DoSetViewportPosition(wz, left + (w->left + w->width - left), top, width - (w->left + w->width - left) , height);
+ DoSetViewportPosition(w, left, top, (w->left + w->width - left), height);
+ DoSetViewportPosition(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left) , height);
return;
}
if (top < w->top) {
- DoSetViewportPosition(wz, left, top, width, (w->top - top));
- DoSetViewportPosition(wz, left, top + (w->top - top), width, height - (w->top - top));
+ DoSetViewportPosition(w, left, top, width, (w->top - top));
+ DoSetViewportPosition(w, left, top + (w->top - top), width, height - (w->top - top));
return;
}
if (top + height > w->top + w->height) {
- DoSetViewportPosition(wz, left, top, width, (w->top + w->height - top));
- DoSetViewportPosition(wz, left, top + (w->top + w->height - top), width , height - (w->top + w->height - top));
+ DoSetViewportPosition(w, left, top, width, (w->top + w->height - top));
+ DoSetViewportPosition(w, left, top + (w->top + w->height - top), width , height - (w->top + w->height - top));
return;
}
@@ -334,7 +332,7 @@ static void SetViewportPosition(Window *w, int x, int y)
i = top + height - _screen.height;
if (i >= 0) height -= i;
- if (height > 0) DoSetViewportPosition(FindWindowZPosition(w) + 1, left, top, width, height);
+ if (height > 0) DoSetViewportPosition(w->z_front, left, top, width, height);
}
}
diff --git a/src/window.cpp b/src/window.cpp
index 6c1d4b11c..334533253 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -31,13 +31,10 @@
static Point _drag_delta; ///< delta between mouse cursor and upper left corner of dragged window
static Window *_mouseover_last_w = NULL; ///< Window of the last MOUSEOVER event
-/**
- * List of windows opened at the screen.
- * Uppermost window is at _z_windows[_last_z_window - 1],
- * bottom window is at _z_windows[0]
- */
-Window *_z_windows[MAX_NUMBER_OF_WINDOWS];
-Window **_last_z_window; ///< always points to the next free space in the z-array
+/** 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;
byte _no_scroll;
Point _cursorpos_drag_start;
@@ -286,24 +283,20 @@ static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
}
/**
- * Generate repaint events for the visible part of window *wz within the rectangle.
+ * Generate repaint events for the visible part of window w within the rectangle.
*
* 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.
*
- * @param wz Pointer into window stack, pointing at the window that needs to be repainted
+ * @param w Window that needs to be repainted
* @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
*/
-static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom)
+static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
{
- Window* const *vz = wz;
-
- while (++vz != _last_z_window) {
- const Window *v = *vz;
-
+ for (const Window *v = w->z_front; v != NULL; v = v->z_front) {
if (right > v->left &&
bottom > v->top &&
left < v->left + v->width &&
@@ -312,26 +305,26 @@ static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right
int x;
if (left < (x = v->left)) {
- DrawOverlappedWindow(wz, left, top, x, bottom);
- DrawOverlappedWindow(wz, x, top, right, bottom);
+ DrawOverlappedWindow(w, left, top, x, bottom);
+ DrawOverlappedWindow(w, x, top, right, bottom);
return;
}
if (right > (x = v->left + v->width)) {
- DrawOverlappedWindow(wz, left, top, x, bottom);
- DrawOverlappedWindow(wz, x, top, right, bottom);
+ DrawOverlappedWindow(w, left, top, x, bottom);
+ DrawOverlappedWindow(w, x, top, right, bottom);
return;
}
if (top < (x = v->top)) {
- DrawOverlappedWindow(wz, left, top, right, x);
- DrawOverlappedWindow(wz, left, x, right, bottom);
+ DrawOverlappedWindow(w, left, top, right, x);
+ DrawOverlappedWindow(w, left, x, right, bottom);
return;
}
if (bottom > (x = v->top + v->height)) {
- DrawOverlappedWindow(wz, left, top, right, x);
- DrawOverlappedWindow(wz, left, x, right, bottom);
+ DrawOverlappedWindow(w, left, top, right, x);
+ DrawOverlappedWindow(w, left, x, right, bottom);
return;
}
@@ -343,12 +336,12 @@ static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right
DrawPixelInfo *dp = _cur_dpi;
dp->width = right - left;
dp->height = bottom - top;
- dp->left = left - (*wz)->left;
- dp->top = top - (*wz)->top;
+ dp->left = left - w->left;
+ dp->top = top - w->top;
dp->pitch = _screen.pitch;
dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
dp->zoom = ZOOM_LVL_NORMAL;
- (*wz)->OnPaint();
+ w->OnPaint();
}
/**
@@ -361,7 +354,7 @@ static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right
*/
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
{
- const Window *w;
+ Window *w;
DrawPixelInfo bk;
_cur_dpi = &bk;
@@ -371,7 +364,7 @@ void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
left < w->left + w->width &&
top < w->top + w->height) {
/* Window w intersects with the rectangle => needs repaint */
- DrawOverlappedWindow(wz, left, top, right, bottom);
+ DrawOverlappedWindow(w, left, top, right, bottom);
}
}
}
@@ -408,24 +401,6 @@ static Window *FindChildWindow(const Window *w)
return NULL;
}
-/** Find the z-value of a window. A window must already be open
- * or the behaviour is undefined but function should never fail
- * @param w window to query Z Position
- * @return Pointer into the window-list at the position of \a w
- */
-Window **FindWindowZPosition(const Window *w)
-{
- const Window *v;
-
- FOR_ALL_WINDOWS_FROM_BACK(v) {
- if (v == w) return wz;
- }
-
- DEBUG(misc, 3, "Window (cls %d, number %d) is not open, probably removed by recursive calls",
- w->window_class, w->window_number);
- return NULL;
-}
-
/**
* Delete all children a window might have in a head-recursive manner
*/
@@ -456,10 +431,16 @@ Window::~Window()
* by moving all windows after it one to the left. This must be
* done before removing the child so we cannot cause recursion
* between the deletion of the parent and the child. */
- Window **wz = FindWindowZPosition(this);
- if (wz == NULL) return;
- memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz);
- _last_z_window--;
+ if (this->z_front == NULL) {
+ _z_front_window = this->z_back;
+ } else {
+ this->z_front->z_back = this->z_back;
+ }
+ if (this->z_back == NULL) {
+ _z_back_window = this->z_front;
+ } else {
+ this->z_back->z_front = this->z_front;
+ }
this->DeleteChildWindows();
@@ -580,7 +561,7 @@ void ChangeWindowOwner(Owner old_owner, Owner new_owner)
}
}
-static void BringWindowToFront(const Window *w);
+static void BringWindowToFront(Window *w);
/** Find a window and make it the top-window on the screen. The window
* gets a white border for a brief period of time to visualize its "activation"
@@ -622,58 +603,36 @@ static inline bool IsVitalWindow(const Window *w)
* @param w window that is put into the foreground
* @return pointer to the window, the same as the input pointer
*/
-static void BringWindowToFront(const Window *w)
+static void BringWindowToFront(Window *w)
{
- Window **wz = FindWindowZPosition(w);
- Window **vz = _last_z_window;
+ Window *v = _z_front_window;
/* Bring the window just below the vital windows */
- do {
- if (--vz < _z_windows) return;
- } while (IsVitalWindow(*vz));
-
- if (wz == vz) return; // window is already in the right position
- assert(wz < vz);
+ for (; v != NULL && v != w && IsVitalWindow(v); v = v->z_back) { }
- Window *tempz = *wz;
- memmove(wz, wz + 1, (byte*)vz - (byte*)wz);
- *vz = tempz;
+ if (v == NULL || w == v) return; // window is already in the right position
- w->SetDirty();
-}
+ /* w cannot be at the top already! */
+ assert(w != _z_front_window);
-/** We have run out of windows, so find a suitable candidate for replacement.
- * Keep all important windows intact. These are
- * - Main window (gamefield), Toolbar, Statusbar (always on)
- * - News window, Chatbar (when on)
- * - Any sticked windows since we wanted to keep these
- * @return w pointer to the window that is going to be deleted
- */
-static Window *FindDeletableWindow()
-{
- Window *w;
- FOR_ALL_WINDOWS_FROM_BACK(w) {
- if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY)) {
- return w;
- }
+ if (w->z_back == NULL) {
+ _z_back_window = w->z_front;
+ } else {
+ w->z_back->z_front = w->z_front;
}
- return NULL;
-}
+ w->z_front->z_back = w->z_back;
-/** A window must be freed, and all are marked as important windows. Ease the
- * restriction a bit by allowing to delete sticky windows. Keep important/vital
- * windows intact (Main window, Toolbar, Statusbar, News Window, Chatbar)
- * Start finding an appropiate candidate from the lowest z-values (bottom)
- * @see FindDeletableWindow()
- * @return w Pointer to the window that is being deleted
- */
-static Window *ForceFindDeletableWindow()
-{
- Window *w;
- FOR_ALL_WINDOWS_FROM_BACK(w) {
- if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
+ w->z_front = v->z_front;
+ w->z_back = v;
+
+ if (v->z_front == NULL) {
+ _z_front_window = w;
+ } else {
+ v->z_front->z_back = w;
}
- NOT_REACHED();
+ v->z_front = w;
+
+ w->SetDirty();
}
/**
@@ -723,13 +682,6 @@ static void AssignWidgetToWindow(Window *w, const Widget *widget)
void Window::Initialize(int x, int y, int min_width, int min_height,
WindowClass cls, const Widget *widget, int window_number)
{
- /* We have run out of windows, close one and use that as the place for our new one */
- if (_last_z_window == endof(_z_windows)) {
- Window *w = FindDeletableWindow();
- if (w == NULL) w = ForceFindDeletableWindow();
- delete w;
- }
-
/* Set up window properties */
this->window_class = cls;
this->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
@@ -753,19 +705,37 @@ void Window::Initialize(int x, int y, int min_width, int min_height,
* XXX - Yes, ugly, probably needs something like w->always_on_top flag
* to implement correctly, but even then you need some kind of distinction
* between on-top of chat/news and status windows, because these conflict */
- Window **wz = _last_z_window;
- if (wz != _z_windows && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
- if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) wz--;
- if (FindWindowById(WC_STATUS_BAR, 0) != NULL) wz--;
- if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) wz--;
- if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
+ Window *w = _z_front_window;
+ if (w != NULL && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
+ if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) w = w->z_back;
+ if (FindWindowById(WC_STATUS_BAR, 0) != NULL) w = w->z_back;
+ if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) w = w->z_back;
+ if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) w = w->z_back;
+
+ if (w == NULL) {
+ _z_back_window->z_front = this;
+ this->z_back = _z_back_window;
+ _z_back_window = this;
+ } else {
+ if (w->z_front == NULL) {
+ _z_front_window = this;
+ } else {
+ this->z_front = w->z_front;
+ w->z_front->z_back = this;
+ }
- assert(wz >= _z_windows);
- if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
+ this->z_back = w;
+ w->z_front = this;
+ }
+ } else {
+ this->z_back = _z_front_window;
+ if (_z_front_window != NULL) {
+ _z_front_window->z_front = this;
+ } else {
+ _z_back_window = this;
+ }
+ _z_front_window = this;
}
-
- *wz = this;
- _last_z_window++;
}
/**
@@ -1109,7 +1079,8 @@ void InitWindowSystem()
{
IConsoleClose();
- _last_z_window = _z_windows;
+ _z_back_window = NULL;
+ _z_front_window = NULL;
_mouseover_last_w = NULL;
_no_scroll = 0;
_scrolling_viewport = 0;
@@ -1120,7 +1091,7 @@ void InitWindowSystem()
*/
void UnInitWindowSystem()
{
- while (_last_z_window != _z_windows) delete _z_windows[0];
+ while (_z_front_window != NULL) delete _z_front_window;
}
/**
@@ -1601,7 +1572,7 @@ static bool HandleViewportScroll()
* modal-popup; function returns a false and child window gets a white border
* @param w Window to bring on-top
* @return false if the window has an active modal child, true otherwise */
-static bool MaybeBringWindowToFront(const Window *w)
+static bool MaybeBringWindowToFront(Window *w)
{
bool bring_to_front = false;
@@ -1612,10 +1583,7 @@ static bool MaybeBringWindowToFront(const Window *w)
return true;
}
- Window * const *wz = FindWindowZPosition(w);
- for (Window * const *uz = wz; ++uz != _last_z_window;) {
- Window *u = *uz;
-
+ for (Window *u = w->z_front; u != NULL; u = u->z_front) {
/* A modal child will prevent the activation of the parent window */
if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
u->flags4 |= WF_WHITE_BORDER_MASK;
@@ -1973,10 +1941,36 @@ void HandleMouseEvents()
}
/**
+ * 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) {
+ if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags4 & WF_STICKY)) continue;
+
+ last_deletable = w;
+ deletable_count++;
+ }
+
+ /* We've ot reached the soft limit yet */
+ if (deletable_count <= _settings_client.gui.window_soft_limit) break;
+
+ assert(last_deletable != NULL);
+ delete last_deletable;
+ }
+}
+
+/**
* Regular call from the global game loop
*/
void InputLoop()
{
+ CheckSoftLimit();
HandleKeyScrolling();
if (_input_events_this_tick != 0) {
diff --git a/src/window_gui.h b/src/window_gui.h
index 81d3f5062..ea2a5f076 100644
--- a/src/window_gui.h
+++ b/src/window_gui.h
@@ -226,6 +226,8 @@ public:
uint32 desc_flags; ///< Window/widgets default flags setting, @see WindowDefaultFlag
Window *parent; ///< Parent window
+ Window *z_front; ///< The window in front of us in z-order
+ Window *z_back; ///< The window behind us in z-order
void HandleButtonClick(byte widget);
@@ -537,12 +539,12 @@ void GuiShowTooltips(StringID str, uint paramcount = 0, const uint64 params[] =
int GetWidgetFromPos(const Window *w, int x, int y);
/* window.cpp */
-extern Window *_z_windows[];
-extern Window **_last_z_window;
+extern Window *_z_front_window;
+extern Window *_z_back_window;
/** Iterate over all windows */
-#define FOR_ALL_WINDOWS_FROM_BACK(w) for (Window **wz = _z_windows; wz != _last_z_window && (w = *wz) != NULL; wz++)
-#define FOR_ALL_WINDOWS_FROM_FRONT(w) for (Window **wz = _last_z_window; wz != _z_windows && (w = *--wz) != NULL;)
+#define FOR_ALL_WINDOWS_FROM_BACK(w) for (w = _z_back_window; w != NULL; w = w->z_front)
+#define FOR_ALL_WINDOWS_FROM_FRONT(w) for (w = _z_front_window; w != NULL; w = w->z_back)
/**
* Disable scrolling of the main viewport when an input-window is active.
@@ -568,7 +570,6 @@ enum SpecialMouseMode {
};
Window *GetCallbackWnd();
-Window **FindWindowZPosition(const Window *w);
void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y);