1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "glk/window_pair.h"
24 #include "glk/conf.h"
25 #include "glk/glk.h"
26 #include "glk/screen.h"
27 
28 namespace Glk {
29 
PairWindow(Windows * windows,uint method,Window * key,uint size)30 PairWindow::PairWindow(Windows *windows, uint method, Window *key, uint size) :
31 	Window(windows, 0),
32 	_dir(method & winmethod_DirMask),
33 	_division(method & winmethod_DivisionMask),
34 	_wBorder((method & winmethod_BorderMask) == winmethod_Border),
35 	_vertical(_dir == winmethod_Left || _dir == winmethod_Right),
36 	_backward(_dir == winmethod_Left || _dir == winmethod_Above),
37 	_key(key), _size(size), _keyDamage(0) {
38 	_type = wintype_Pair;
39 }
40 
~PairWindow()41 PairWindow::~PairWindow() {
42 	for (uint idx = 0; idx < _children.size(); ++idx) {
43 		_children[idx]->_parent = nullptr;
44 		delete _children[idx];
45 	}
46 }
47 
rearrange(const Rect & box)48 void PairWindow::rearrange(const Rect &box) {
49 	Rect box1, box2;
50 	int min, diff, split, splitwid, max;
51 	Window *ch1, *ch2;
52 
53 	_bbox = box;
54 
55 	if (_dir == winmethod_Arbitrary) {
56 		// When a pair window is in "arbitrary" mode, each child window has it's own independant positioning,
57 		// so thre's no need to be readjusting it
58 		return;
59 	}
60 
61 	if (!_backward) {
62 		ch1 = _children[0];
63 		ch2 = _children[1];
64 	} else {
65 		ch1 = _children[1];
66 		ch2 = _children[0];
67 	}
68 
69 	if (_vertical) {
70 		min = _bbox.left;
71 		max = _bbox.right;
72 	} else {
73 		min = _bbox.top;
74 		max = _bbox.bottom;
75 	}
76 	diff = max - min;
77 
78 	// We now figure split.
79 	if (_vertical)
80 		splitwid = g_conf->_wPaddingX; // want border?
81 	else
82 		splitwid = g_conf->_wPaddingY; // want border?
83 
84 	switch (_division) {
85 	case winmethod_Proportional:
86 		split = (diff * _size) / 100;
87 		break;
88 
89 	case winmethod_Fixed:
90 		split = !_key ? 0 : _key->getSplit(_size, _vertical);
91 		break;
92 
93 	default:
94 		split = diff / 2;
95 		break;
96 	}
97 
98 	if (!_backward)
99 		split = max - split - splitwid;
100 	else
101 		split = min + split;
102 
103 	if (min >= max) {
104 		split = min;
105 	} else {
106 		if (split < min)
107 			split = min;
108 		else if (split > max - splitwid)
109 			split = max - splitwid;
110 	}
111 
112 	if (_vertical) {
113 		box1.left = _bbox.left;
114 		box1.right = split;
115 		box2.left = split + splitwid;
116 		box2.right = _bbox.right;
117 		box1.top = _bbox.top;
118 		box1.bottom = _bbox.bottom;
119 		box2.top = _bbox.top;
120 		box2.bottom = _bbox.bottom;
121 	} else {
122 		box1.top = _bbox.top;
123 		box1.bottom = split;
124 		box2.top = split + splitwid;
125 		box2.bottom = _bbox.bottom;
126 		box1.left = _bbox.left;
127 		box1.right = _bbox.right;
128 		box2.left = _bbox.left;
129 		box2.right = _bbox.right;
130 	}
131 
132 	ch1->rearrange(box1);
133 	ch2->rearrange(box2);
134 }
135 
redraw()136 void PairWindow::redraw() {
137 	// When the windows can be in arbitrary positions, some of them may be transparent, so we always
138 	// need to force a full screen redraw in such cases
139 	if (_dir == winmethod_Arbitrary)
140 		Windows::_forceRedraw = true;
141 
142 	Window::redraw();
143 
144 	for (int ctr = 0, idx = (_backward ? (int)_children.size() - 1 : 0); ctr < (int)_children.size();
145 		++ctr, idx += (_backward ? -1 : 1)) {
146 		_children[idx]->redraw();
147 	}
148 
149 	Window *child = !_backward ? _children.front() : _children.back();
150 	Rect box(child->_bbox.left, child->_yAdj ? child->_bbox.top - child->_yAdj : child->_bbox.top,
151 			 child->_bbox.right, child->_bbox.bottom);
152 
153 	if (_vertical) {
154 		int xBord = _wBorder ? g_conf->_wBorderX : 0;
155 		int xPad = (g_conf->_wPaddingX - xBord) / 2;
156 
157 		g_vm->_screen->fillRect(Rect(box.right + xPad, box.top, box.right + xPad + xBord, box.bottom),
158 								g_conf->_borderColor);
159 	} else {
160 		int yBord = _wBorder ? g_conf->_wBorderY : 0;
161 		int yPad = (g_conf->_wPaddingY - yBord) / 2;
162 		g_vm->_screen->fillRect(Rect(box.left, box.bottom + yPad, box.right, box.bottom + yPad + yBord),
163 								g_conf->_borderColor);
164 	}
165 }
166 
getArrangement(uint * method,uint * size,Window ** keyWin)167 void PairWindow::getArrangement(uint *method, uint *size, Window **keyWin) {
168 	uint val = _dir | _division;
169 	if (!_wBorder)
170 		val |= winmethod_NoBorder;
171 
172 	if (size)
173 		*size = _size;
174 	if (keyWin) {
175 		if (_key)
176 			*keyWin = _key;
177 		else
178 			*keyWin = nullptr;
179 	}
180 
181 	if (method)
182 		*method = val;
183 }
184 
setArrangement(uint method,uint size,Window * keyWin)185 void PairWindow::setArrangement(uint method, uint size, Window *keyWin) {
186 	uint newDir;
187 	bool newVertical, newBackward;
188 	assert((method & winmethod_DirMask) != winmethod_Arbitrary && _dir != winmethod_Arbitrary);
189 
190 	if (_key) {
191 		Window *wx;
192 		PairWindow *pairWin = dynamic_cast<PairWindow *>(_key);
193 
194 		if (pairWin) {
195 			warning("setArrangement: keywin cannot be a Pair");
196 			return;
197 		}
198 
199 		for (wx = _key; wx; wx = wx->_parent) {
200 			if (wx == this)
201 				break;
202 		}
203 		if (wx == nullptr) {
204 			warning("setArrangement: keywin must be a descendant");
205 			return;
206 		}
207 	}
208 
209 	newDir = method & winmethod_DirMask;
210 	newVertical = (newDir == winmethod_Left || newDir == winmethod_Right);
211 	newBackward = (newDir == winmethod_Left || newDir == winmethod_Above);
212 	if (!keyWin)
213 		keyWin = _key;
214 
215 	if ((newVertical && !_vertical) || (!newVertical && _vertical)) {
216 		if (!_vertical)
217 			warning("setArrangement: split must stay horizontal");
218 		else
219 			warning("setArrangement: split must stay vertical");
220 		return;
221 	}
222 
223 	if (keyWin && dynamic_cast<BlankWindow *>(keyWin)
224 			&& (method & winmethod_DivisionMask) == winmethod_Fixed) {
225 		warning("setArrangement: a Blank window cannot have a fixed size");
226 		return;
227 	}
228 
229 	if ((newBackward && !_backward) || (!newBackward && _backward)) {
230 		// switch the children
231 		SWAP(_children[0], _children[1]);
232 	}
233 
234 	// set up everything else
235 	_dir = newDir;
236 	_division = method & winmethod_DivisionMask;
237 	_key = keyWin;
238 	_size = size;
239 	_wBorder = ((method & winmethod_BorderMask) == winmethod_Border);
240 
241 	_vertical = (_dir == winmethod_Left || _dir == winmethod_Right);
242 	_backward = (_dir == winmethod_Left || _dir == winmethod_Above);
243 
244 	_windows->rearrange();
245 }
246 
click(const Point & newPos)247 void PairWindow::click(const Point &newPos) {
248 	// Note in case windows are partially overlapping, we want the top-most window to get the click.
249 	// WHich is why we recurse in the opposite of the rendering direction (as the _backward flag) indicates
250 	for (int ctr = 0, idx = (!_backward ? (int)_children.size() - 1 : 0); ctr < (int)_children.size();
251 		++ctr, idx += (!_backward ? -1 : 1)) {
252 		Window *w = _children[idx];
253 		if (w->_bbox.contains(newPos))
254 			w->click(newPos);
255 	}
256 }
257 
258 } // End of namespace Glk
259