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 #include "common/array.h"
23 #include "common/list.h"
24 #include "common/system.h"
25 #include "common/timer.h"
26 
27 #include "graphics/cursorman.h"
28 #include "graphics/managed_surface.h"
29 #include "graphics/palette.h"
30 #include "graphics/primitives.h"
31 #include "graphics/macgui/macwindowmanager.h"
32 #include "graphics/macgui/macfontmanager.h"
33 #include "graphics/macgui/macwindow.h"
34 #include "graphics/macgui/mactextwindow.h"
35 #include "graphics/macgui/macmenu.h"
36 
37 #include "image/bmp.h"
38 
39 namespace Graphics {
40 
41 static const byte palette[] = {
42 	0, 0, 0,           // Black
43 	0x80, 0x80, 0x80,  // Gray80
44 	0x88, 0x88, 0x88,  // Gray88
45 	0xee, 0xee, 0xee,  // GrayEE
46 	0xff, 0xff, 0xff,  // White
47 	0x00, 0xff, 0x00,  // Green
48 	0x00, 0xcf, 0x00   // Green2
49 };
50 
51 static byte fillPatterns[][8] = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, // kPatternSolid
52 								  { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, // kPatternStripes
53 								  { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }, // kPatternCheckers
54 								  { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }, // kPatternCheckers2
55 								  { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 }, // kPatternLightGray
56 								  { 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd }  // kPatternDarkGray
57 };
58 
59 static const byte cursorPalette[] = {
60 	0, 0, 0,
61 	0xff, 0xff, 0xff
62 };
63 
64 static const byte macCursorArrow[] = {
65 	1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
66 	1, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3,
67 	1, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3,
68 	1, 0, 0, 0, 1, 3, 3, 3, 3, 3, 3,
69 	1, 0, 0, 0, 0, 1, 3, 3, 3, 3, 3,
70 	1, 0, 0, 0, 0, 0, 1, 3, 3, 3, 3,
71 	1, 0, 0, 0, 0, 0, 0, 1, 3, 3, 3,
72 	1, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3,
73 	1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3,
74 	1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
75 	1, 0, 0, 1, 0, 0, 1, 3, 3, 3, 3,
76 	1, 0, 1, 3, 1, 0, 0, 1, 3, 3, 3,
77 	1, 1, 3, 3, 1, 0, 0, 1, 3, 3, 3,
78 	1, 3, 3, 3, 3, 1, 0, 0, 1, 3, 3,
79 	3, 3, 3, 3, 3, 1, 0, 0, 1, 3, 3,
80 	3, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3
81 };
82 
83 static const byte macCursorBeam[] = {
84 	0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3,
85 	3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3,
86 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
87 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
88 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
89 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
90 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
91 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
92 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
93 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
94 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
95 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
96 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
97 	3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
98 	3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3,
99 	0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3,
100 };
101 static const byte macCursorCrossHair[] = {
102 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
103 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
104 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
105 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
106 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
107 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
109 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
110 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
111 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
112 	3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
113 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
114 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
115 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
116 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
117 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
118 };
119 static const byte macCursorWatch[] = {
120 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
121 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
122 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
123 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
124 	3, 0, 1, 1, 1, 1, 1, 1, 0, 1, 3,
125 	0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3,
126 	0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3,
127 	0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
128 	0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
129 	0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 3,
130 	0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 3,
131 	3, 0, 1, 1, 1, 1, 1, 1, 0, 1, 3,
132 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
133 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
134 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
135 	3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3,
136 };
137 static const byte macCursorCrossBar[] = {
138 	3, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3,
139 	3, 3, 3, 0, 1, 1, 0, 0, 3, 3, 3,
140 	3, 3, 3, 0, 1, 1, 0, 0, 3, 3, 3,
141 	3, 3, 3, 0, 1, 1, 0, 0, 3, 3, 3,
142 	0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 3,
143 	0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
144 	0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
145 	0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
146 	3, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
147 	3, 3, 3, 0, 1, 1, 0, 0, 3, 3, 3,
148 	3, 3, 3, 0, 1, 1, 0, 0, 3, 3, 3,
149 	3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3,
150 	3, 3, 3, 3, 0, 0, 0, 0, 3, 3, 3,
151 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
152 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
153 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
154 };
155 
156 static void menuTimerHandler(void *refCon);
157 
MacWindowManager(uint32 mode,MacPatterns * patterns,Common::Language language)158 MacWindowManager::MacWindowManager(uint32 mode, MacPatterns *patterns, Common::Language language) {
159 	_screen = nullptr;
160 	_screenCopy = nullptr;
161 	_desktopBmp = nullptr;
162 	_desktop = nullptr;
163 	_lastId = 0;
164 	_activeWindow = -1;
165 	_needsRemoval = false;
166 
167 	_activeWidget = nullptr;
168 	_mouseDown = false;
169 	_hoveredWidget = nullptr;
170 
171 	_mode = mode;
172 	_language = language;
173 
174 	_menu = 0;
175 	_menuDelay = 0;
176 	_menuTimerActive = false;
177 
178 	_engineP = nullptr;
179 	_engineR = nullptr;
180 	_redrawEngineCallback = nullptr;
181 	_screenCopyPauseToken = nullptr;
182 
183 	_colorBlack = kColorBlack;
184 	_colorGray80 = kColorGray80;
185 	_colorGray88 = kColorGray88;
186 	_colorGrayEE = kColorGrayEE;
187 	_colorWhite = kColorWhite;
188 	_colorGreen = kColorGreen;
189 	_colorGreen2 = kColorGreen2;
190 
191 	_fullRefresh = true;
192 	_inEditableArea = false;
193 
194 	_hilitingWidget = false;
195 
196 	if (mode & kWMMode32bpp)
197 		_pixelformat = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
198 	else
199 		_pixelformat = PixelFormat::createFormatCLUT8();
200 
201 	if (patterns) {
202 		_patterns = *patterns;
203 	} else {
204 		for (int i = 0; i < ARRAYSIZE(fillPatterns); i++)
205 			_patterns.push_back(fillPatterns[i]);
206 	}
207 
208 	// builtin pattern
209 	for (int i = 0; i < ARRAYSIZE(fillPatterns); i++)
210 		_builtinPatterns.push_back(fillPatterns[i]);
211 
212 	g_system->getPaletteManager()->setPalette(palette, 0, ARRAYSIZE(palette) / 3);
213 
214 	_paletteSize = ARRAYSIZE(palette) / 3;
215 	if (_paletteSize) {
216 		_palette = (byte *)malloc(_paletteSize * 3);
217 		memcpy(_palette, palette, _paletteSize * 3);
218 	}
219 
220 	_fontMan = new MacFontManager(mode, language);
221 
222 	_cursor = nullptr;
223 	_tempType = kMacCursorArrow;
224 	replaceCursor(kMacCursorArrow);
225 	CursorMan.showMouse(true);
226 
227 	loadDataBundle();
228 	if (!(_mode & Graphics::kWMNoScummVMWallpaper)) {
229 		loadDesktop();
230 	}
231 }
232 
~MacWindowManager()233 MacWindowManager::~MacWindowManager() {
234 	for (Common::HashMap<uint, BaseMacWindow *>::iterator it = _windows.begin(); it != _windows.end(); it++)
235 		delete it->_value;
236 
237 	if (_palette)
238 		free(_palette);
239 
240 	delete _fontMan;
241 	delete _screenCopy;
242 
243 	if (_desktopBmp) {
244 		_desktopBmp->free();
245 		delete _desktopBmp;
246 	}
247 	delete _desktop;
248 
249 	cleanupDataBundle();
250 
251 	g_system->getTimerManager()->removeTimerProc(&menuTimerHandler);
252 }
253 
setScreen(ManagedSurface * screen)254 void MacWindowManager::setScreen(ManagedSurface *screen) {
255 	_screen = screen;
256 	delete _screenCopy;
257 	_screenCopy = nullptr;
258 
259 	if (_desktop)
260 		_desktop->free();
261 	else
262 		_desktop = new ManagedSurface();
263 
264 	_desktop->create(_screen->w, _screen->h, _pixelformat);
265 	drawDesktop();
266 }
267 
setScreen(int w,int h)268 void MacWindowManager::setScreen(int w, int h) {
269 	if (_desktop)
270 		_desktop->free();
271 	else
272 		_desktop = new ManagedSurface();
273 
274 	_screenDims = Common::Rect(w, h);
275 	_desktop->create(w, h, _pixelformat);
276 	drawDesktop();
277 }
278 
resizeScreen(int w,int h)279 void MacWindowManager::resizeScreen(int w, int h) {
280 	if (!_screen)
281 		error("MacWindowManager::resizeScreen(): Trying to creating surface on non-existing screen");
282 	_screenDims = Common::Rect(w, h);
283 	_screen->free();
284 	_screen->create(w, h, _pixelformat);
285 }
286 
setMode(uint32 mode)287 void MacWindowManager::setMode(uint32 mode) {
288 	_mode = mode;
289 
290 	if (mode & kWMModeForceBuiltinFonts)
291 		_fontMan->forceBuiltinFonts();
292 }
293 
clearHandlingWidgets()294 void MacWindowManager::clearHandlingWidgets() {
295 	// pass a LBUTTONUP event to those widgets should clear those state
296 	Common::Event event;
297 	event.type = Common::EVENT_LBUTTONUP;
298 	event.mouse = _lastClickPos;
299 	processEvent(event);
300 
301 	setActiveWidget(nullptr);
302 	_hoveredWidget = nullptr;
303 }
304 
setActiveWidget(MacWidget * widget)305 void MacWindowManager::setActiveWidget(MacWidget *widget) {
306 	if (_activeWidget == widget)
307 		return;
308 
309 	if (_activeWidget)
310 		_activeWidget->setActive(false);
311 
312 	_activeWidget = widget;
313 
314 	if (_activeWidget)
315 		_activeWidget->setActive(true);
316 }
317 
clearWidgetRefs(MacWidget * widget)318 void MacWindowManager::clearWidgetRefs(MacWidget *widget) {
319 	if (widget == _hoveredWidget)
320 		_hoveredWidget = nullptr;
321 
322 	if (widget == _activeWidget)
323 		_activeWidget = nullptr;
324 }
325 
addWindow(bool scrollable,bool resizable,bool editable)326 MacWindow *MacWindowManager::addWindow(bool scrollable, bool resizable, bool editable) {
327 	MacWindow *w = new MacWindow(_lastId, scrollable, resizable, editable, this);
328 
329 	addWindowInitialized(w);
330 
331 	setActiveWindow(getNextId());
332 
333 	return w;
334 }
335 
addTextWindow(const MacFont * font,int fgcolor,int bgcolor,int maxWidth,TextAlign textAlignment,MacMenu * menu,bool cursorHandler)336 MacTextWindow *MacWindowManager::addTextWindow(const MacFont *font, int fgcolor, int bgcolor, int maxWidth, TextAlign textAlignment, MacMenu *menu, bool cursorHandler) {
337 	MacTextWindow *w = new MacTextWindow(this, font, fgcolor, bgcolor, maxWidth, textAlignment, menu, cursorHandler);
338 
339 	addWindowInitialized(w);
340 
341 	setActiveWindow(getNextId());
342 
343 	return w;
344 }
345 
addTextWindow(const Font * font,int fgcolor,int bgcolor,int maxWidth,TextAlign textAlignment,MacMenu * menu,bool cursorHandler)346 MacTextWindow *MacWindowManager::addTextWindow(const Font *font, int fgcolor, int bgcolor, int maxWidth, TextAlign textAlignment, MacMenu *menu, bool cursorHandler) {
347 	MacTextWindow *w = new MacTextWindow(this, font, fgcolor, bgcolor, maxWidth, textAlignment, menu, cursorHandler);
348 
349 	addWindowInitialized(w);
350 
351 	setActiveWindow(getNextId());
352 
353 	return w;
354 }
355 
356 
addWindowInitialized(MacWindow * macwindow)357 void MacWindowManager::addWindowInitialized(MacWindow *macwindow) {
358 	_windows[macwindow->getId()] = macwindow;
359 	_windowStack.push_back(macwindow);
360 }
361 
addMenu()362 MacMenu *MacWindowManager::addMenu() {
363 	if (_menu) {
364 		_windows[_menu->getId()] = nullptr;
365 		delete _menu;
366 	}
367 
368 	_menu = new MacMenu(getNextId(), getScreenBounds(), this);
369 
370 	_windows[_menu->getId()] = _menu;
371 
372 	return _menu;
373 }
374 
removeMenu()375 void MacWindowManager::removeMenu() {
376 	if (_menu) {
377 		_windows[_menu->getId()] = nullptr;
378 		delete _menu;
379 		_menu = nullptr;
380 	}
381 }
382 
activateMenu()383 void MacWindowManager::activateMenu() {
384 	if (!_menu || ((_mode & kWMModeAutohideMenu) && _menu->isVisible()))
385 		return;
386 
387 	if (_mode & kWMModalMenuMode) {
388 		activateScreenCopy();
389 	}
390 
391 	_menu->setVisible(true);
392 }
393 
activateScreenCopy()394 void MacWindowManager::activateScreenCopy() {
395 	if (_screen) {
396 		if (!_screenCopy)
397 			_screenCopy = new ManagedSurface(*_screen);	// Create a copy
398 		else
399 			*_screenCopy = *_screen;
400 	} else {
401 		Surface *surface = g_system->lockScreen();
402 
403 		if (!_screenCopy) {
404 			_screenCopy = new ManagedSurface(_screenDims.width(), _screenDims.height());
405 		}
406 
407 		_screenCopy->blitFrom(*surface);
408 		g_system->unlockScreen();
409 	}
410 
411 	_screenCopyPauseToken = new PauseToken(pauseEngine());
412 }
413 
disableScreenCopy()414 void MacWindowManager::disableScreenCopy() {
415 	if (_screenCopyPauseToken) {
416 		_screenCopyPauseToken->clear();
417 		delete _screenCopyPauseToken;
418 		_screenCopyPauseToken = nullptr;
419 	}
420 
421 	// add a check, we may not get the _screenCopy because we may not activate the menu
422 	if (!_screenCopy)
423 		return;
424 
425 	if (_screen)
426 		*_screen = *_screenCopy; // restore screen
427 
428 	g_system->copyRectToScreen(_screenCopy->getBasePtr(0, 0), _screenCopy->pitch, 0, 0, _screenCopy->w, _screenCopy->h);
429 }
430 
setMenuItemCheckMark(const Common::String & menuId,const Common::String & itemId,bool checkMark)431 void MacWindowManager::setMenuItemCheckMark(const Common::String &menuId, const Common::String &itemId, bool checkMark) {
432 	if (_menu) {
433 		_menu->setCheckMark(menuId, itemId, checkMark);
434 	} else {
435 		warning("MacWindowManager::setMenuItemCheckMark: wm doesn't have menu");
436 	}
437 }
438 
setMenuItemEnabled(const Common::String & menuId,const Common::String & itemId,bool enabled)439 void MacWindowManager::setMenuItemEnabled(const Common::String &menuId, const Common::String &itemId, bool enabled) {
440 	if (_menu) {
441 		_menu->setEnabled(menuId, itemId, enabled);
442 	} else {
443 		warning("MacWindowManager::setMenuItemEnabled: wm doesn't have menu");
444 	}
445 }
446 
setMenuItemName(const Common::String & menuId,const Common::String & itemId,const Common::String & name)447 void MacWindowManager::setMenuItemName(const Common::String &menuId, const Common::String &itemId, const Common::String &name) {
448 	if (_menu) {
449 		_menu->setName(menuId, itemId, name);
450 	} else {
451 		warning("MacWindowManager::setMenuItemName: wm doesn't have menu");
452 	}
453 }
454 
setMenuItemCheckMark(int menuId,int itemId,bool checkMark)455 void MacWindowManager::setMenuItemCheckMark(int menuId, int itemId, bool checkMark) {
456 	if (_menu) {
457 		_menu->setCheckMark(menuId, itemId, checkMark);
458 	} else {
459 		warning("MacWindowManager::setMenuItemCheckMark: wm doesn't have menu");
460 	}
461 }
462 
setMenuItemEnabled(int menuId,int itemId,bool enabled)463 void MacWindowManager::setMenuItemEnabled(int menuId, int itemId, bool enabled) {
464 	if (_menu) {
465 		_menu->setEnabled(menuId, itemId, enabled);
466 	} else {
467 		warning("MacWindowManager::setMenuItemEnabled: wm doesn't have menu");
468 	}
469 }
470 
setMenuItemName(int menuId,int itemId,const Common::String & name)471 void MacWindowManager::setMenuItemName(int menuId, int itemId, const Common::String &name) {
472 	if (_menu) {
473 		_menu->setName(menuId, itemId, name);
474 	} else {
475 		warning("MacWindowManager::setMenuItemName: wm doesn't have menu");
476 	}
477 }
478 
setMenuItemAction(const Common::String & menuId,const Common::String & itemId,int actionId)479 void MacWindowManager::setMenuItemAction(const Common::String &menuId, const Common::String &itemId, int actionId) {
480 	if (_menu) {
481 		_menu->setAction(menuId, itemId, actionId);
482 	} else {
483 		warning("MacWindowManager::setMenuItemAction: wm doesn't have menu");
484 	}
485 }
486 
setMenuItemAction(int menuId,int itemId,int actionId)487 void MacWindowManager::setMenuItemAction(int menuId, int itemId, int actionId) {
488 	if (_menu) {
489 		_menu->setAction(menuId, itemId, actionId);
490 	} else {
491 		warning("MacWindowManager::setMenuItemAction: wm doesn't have menu");
492 	}
493 }
494 
getMenuItemCheckMark(const Common::String & menuId,const Common::String & itemId)495 bool MacWindowManager::getMenuItemCheckMark(const Common::String &menuId, const Common::String &itemId) {
496 	if (_menu) {
497 		return _menu->getCheckMark(menuId, itemId);
498 	} else {
499 		warning("MacWindowManager::getMenuItemCheckMark: wm doesn't have menu");
500 		return false;
501 	}
502 }
503 
getMenuItemCheckMark(int menuId,int itemId)504 bool MacWindowManager::getMenuItemCheckMark(int menuId, int itemId) {
505 	if (_menu) {
506 		return _menu->getCheckMark(menuId, itemId);
507 	} else {
508 		warning("MacWindowManager::getMenuItemCheckMark: wm doesn't have menu");
509 		return false;
510 	}
511 }
512 
getMenuItemEnabled(const Common::String & menuId,const Common::String & itemId)513 bool MacWindowManager::getMenuItemEnabled(const Common::String &menuId, const Common::String &itemId) {
514 	if (_menu) {
515 		return _menu->getEnabled(menuId, itemId);
516 	} else {
517 		warning("MacWindowManager::getMenuItemEnabled: wm doesn't have menu");
518 		return false;
519 	}
520 }
521 
getMenuItemEnabled(int menuId,int itemId)522 bool MacWindowManager::getMenuItemEnabled(int menuId, int itemId) {
523 	if (_menu) {
524 		return _menu->getEnabled(menuId, itemId);
525 	} else {
526 		warning("MacWindowManager::getMenuItemEnabled: wm doesn't have menu");
527 		return false;
528 	}
529 }
530 
getMenuItemName(const Common::String & menuId,const Common::String & itemId)531 Common::String MacWindowManager::getMenuItemName(const Common::String &menuId, const Common::String &itemId) {
532 	if (_menu) {
533 		return _menu->getName(menuId, itemId);
534 	} else {
535 		warning("MacWindowManager::getMenuItemName: wm doesn't have menu");
536 		return Common::String();
537 	}
538 }
539 
getMenuItemName(int menuId,int itemId)540 Common::String MacWindowManager::getMenuItemName(int menuId, int itemId) {
541 	if (_menu) {
542 		return _menu->getName(menuId, itemId);
543 	} else {
544 		warning("MacWindowManager::getMenuItemName: wm doesn't have menu");
545 		return Common::String();
546 	}
547 }
548 
getMenuItemAction(const Common::String & menuId,const Common::String & itemId)549 int MacWindowManager::getMenuItemAction(const Common::String &menuId, const Common::String &itemId) {
550 	if (_menu) {
551 		return _menu->getAction(menuId, itemId);
552 	} else {
553 		warning("MacWindowManager::getMenuItemAction: wm doesn't have menu");
554 		return 0;
555 	}
556 }
557 
getMenuItemAction(int menuId,int itemId)558 int MacWindowManager::getMenuItemAction(int menuId, int itemId) {
559 	if (_menu) {
560 		return _menu->getAction(menuId, itemId);
561 	} else {
562 		warning("MacWindowManager::getMenuItemAction: wm doesn't have menu");
563 		return 0;
564 	}
565 }
566 
567 // this is refer to how we deal U32String in splitString in mactext
568 // maybe we can optimize this specifically
stripFormat(const Common::U32String & str)569 Common::U32String stripFormat(const Common::U32String &str) {
570 	Common::U32String res, paragraph, tmp;
571 	// calc the size of str
572 	const Common::U32String::value_type *l = str.c_str();
573 	while (*l) {
574 		// split paragraph first
575 		paragraph.clear();
576 		while (*l) {
577 			if (*l == '\r') {
578 				l++;
579 				if (*l == '\n')
580 					l++;
581 				break;
582 			}
583 			if (*l == '\n') {
584 				l++;
585 				break;
586 			}
587 			paragraph += *l++;
588 		}
589 		const Common::U32String::value_type *s = paragraph.c_str();
590 		tmp.clear();
591 		while (*s) {
592 			if (*s == '\001') {
593 				s++;
594 				// if there are two \001, then we regard it as one character
595 				if (*s == '\001') {
596 					tmp += *s++;
597 				}
598 			} else if (*s == '\015') {	// binary format
599 				// we are skipping the formatting stuffs
600 				// this number 12, and the number 23, is the size of our format
601 				s += 12;
602 			} else if (*s == '\016') {	// human-readable format
603 				s += 23;
604 			} else {
605 				tmp += *s++;
606 			}
607 		}
608 		res += tmp;
609 		if (*l)
610 			res += '\n';
611 	}
612 	return res;
613 }
614 
setTextInClipboard(const Common::U32String & str)615 void MacWindowManager::setTextInClipboard(const Common::U32String &str) {
616 	_clipboard = str;
617 	g_system->setTextInClipboard(stripFormat(str));
618 }
619 
620 // get the text size ignoring \n
getPureTextSize(const Common::U32String & str,bool global)621 int getPureTextSize(const Common::U32String &str, bool global) {
622 	const Common::U32String::value_type *l = str.c_str();
623 	int res = 0;
624 	if (global) {
625 		// if we are in global, then we have no format in str. thus, we ignore all \r \n
626 		while (*l) {
627 			if (*l != '\n' && *l != '\r')
628 				res++;
629 			l++;
630 		}
631 	} else {
632 		// if we are not in global, then we are using the wm clipboard, which use \n for new line
633 		// i think that if statement can be optimized to, like if (*l != '\n' && (!global || *l != '\r'))
634 		// but for the sake of readability, we keep codes here
635 		while (*l) {
636 			if (*l != '\n')
637 				res++;
638 			l++;
639 		}
640 	}
641 	return res;
642 }
643 
getTextFromClipboard(const Common::U32String & format,int * size)644 Common::U32String MacWindowManager::getTextFromClipboard(const Common::U32String &format, int *size) {
645 	Common::U32String global_str = g_system->getTextFromClipboard();
646 	// str is what we need
647 	Common::U32String str;
648 	if (_clipboard.empty()) {
649 		// if wm clipboard is empty, then we use the global clipboard, which won't contain the format
650 		str = format + global_str;
651 		if (size)
652 			*size = getPureTextSize(global_str, true);
653 	} else {
654 		Common::U32String tmp = stripFormat(_clipboard);
655 		if (tmp == global_str) {
656 			// if the text is equal, then we use wm one which contains the format
657 			str = _clipboard;
658 			if (size)
659 				*size = getPureTextSize(tmp, false);
660 		} else {
661 			// otherwise, we prefer the global one
662 			str = format + global_str;
663 			if (size)
664 				*size = getPureTextSize(global_str, true);
665 		}
666 	}
667 	return str;
668 }
669 
isMenuActive()670 bool MacWindowManager::isMenuActive() {
671 	if (!_menu)
672 		return false;
673 
674 	return _menu->isVisible();
675 }
676 
setActiveWindow(int id)677 void MacWindowManager::setActiveWindow(int id) {
678 	if (_activeWindow == id)
679 		return;
680 
681 	if (_activeWindow != -1)
682 		_windows[_activeWindow]->setActive(false);
683 
684 	_activeWindow = id;
685 
686 	_windows[id]->setActive(true);
687 
688 	_windowStack.remove(_windows[id]);
689 	_windowStack.push_back(_windows[id]);
690 
691 	_fullRefresh = true;
692 }
693 
removeWindow(MacWindow * target)694 void MacWindowManager::removeWindow(MacWindow *target) {
695 	_windowsToRemove.push_back(target);
696 	_needsRemoval = true;
697 	_hoveredWidget = nullptr;
698 
699 	if (target->getId() == _activeWindow)
700 		_activeWindow = -1;
701 }
702 
703 template<typename T>
macDrawPixel(int x,int y,int color,void * data)704 void macDrawPixel(int x, int y, int color, void *data) {
705 	MacPlotData *p = (MacPlotData *)data;
706 
707 	if (p->fillType > p->patterns->size() || !p->fillType)
708 		return;
709 
710 	byte *pat = p->patterns->operator[](p->fillType - 1);
711 
712 	if (p->thickness == 1) {
713 		if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
714 			uint xu = (uint)x; // for letting compiler optimize it
715 			uint yu = (uint)y;
716 
717 			*((T)p->surface->getBasePtr(xu, yu)) = p->invert ? ~(*((T)p->surface->getBasePtr(xu, yu))) :
718 				(pat[(yu - p->fillOriginY) % 8] & (1 << (7 - (xu - p->fillOriginX) % 8))) ? color : p->bgColor;
719 
720 			if (p->mask)
721 				*((T)p->mask->getBasePtr(xu, yu)) = 0xff;
722 		}
723 	} else {
724 		int x1 = x;
725 		int x2 = x1 + p->thickness;
726 		int y1 = y;
727 		int y2 = y1 + p->thickness;
728 
729 		for (y = y1; y < y2; y++)
730 			for (x = x1; x < x2; x++)
731 				if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
732 					uint xu = (uint)x; // for letting compiler optimize it
733 					uint yu = (uint)y;
734 					*((T)p->surface->getBasePtr(xu, yu)) = p->invert ? ~(*((T)p->surface->getBasePtr(xu, yu))) :
735 						(pat[(yu - p->fillOriginY) % 8] & (1 << (7 - (xu - p->fillOriginX) % 8))) ? color : p->bgColor;
736 
737 					if (p->mask)
738 						*((T)p->mask->getBasePtr(xu, yu)) = 0xff;
739 				}
740 	}
741 }
742 
macDrawInvertPixel(int x,int y,int color,void * data)743 void macDrawInvertPixel(int x, int y, int color, void *data) {
744 	MacPlotData *p = (MacPlotData *)data;
745 
746 	if (p->fillType > p->patterns->size() || !p->fillType)
747 		return;
748 
749 	if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
750 		uint xu = (uint)x; // for letting compiler optimize it
751 		uint yu = (uint)y;
752 
753 		byte cur_color = *((byte *)p->surface->getBasePtr(xu, yu));
754 		// 0 represent black in default palette, and 4 represent white
755 		// if color is black, we invert it to white, otherwise, we invert it to black
756 		byte invert_color = 0;
757 		if (cur_color == 0) {
758 			invert_color = 4;
759 		}
760 		*((byte *)p->surface->getBasePtr(xu, yu)) = invert_color;
761 
762 		if (p->mask)
763 			*((byte *)p->mask->getBasePtr(xu, yu)) = 0xff;
764 	}
765 }
766 
getDrawPixel()767 MacDrawPixPtr MacWindowManager::getDrawPixel() {
768 	if (_pixelformat.bytesPerPixel == 1)
769 		return &macDrawPixel<byte *>;
770 	else
771 		return &macDrawPixel<uint32 *>;
772 }
773 
774 // get the function of drawing invert pixel for default palette
getDrawInvertPixel()775 MacDrawPixPtr MacWindowManager::getDrawInvertPixel() {
776 	if (_pixelformat.bytesPerPixel == 1)
777 		return &macDrawInvertPixel;
778 	warning("function of drawing invert pixel for default palette has not implemented yet");
779 	return nullptr;
780 }
781 
loadDesktop()782 void MacWindowManager::loadDesktop() {
783 	Common::SeekableReadStream *file = getFile("scummvm_background.bmp");
784 	if (!file)
785 		return;
786 
787 	Image::BitmapDecoder bmpDecoder;
788 	Graphics::Surface *source;
789 	_desktopBmp = new Graphics::TransparentSurface();
790 
791 	bmpDecoder.loadStream(*file);
792 	source = bmpDecoder.getSurface()->convertTo(_desktopBmp->getSupportedPixelFormat(), bmpDecoder.getPalette());
793 
794 	_desktopBmp->copyFrom(*source);
795 
796 	delete file;
797 	source->free();
798 	delete source;
799 }
800 
drawDesktop()801 void MacWindowManager::drawDesktop() {
802 	if (_desktopBmp) {
803 		for (int i = 0; i < _desktop->w; ++i) {
804 			for (int j = 0; j < _desktop->h; ++j) {
805 				uint32 color = *(uint32 *)_desktopBmp->getBasePtr(i % _desktopBmp->w, j % _desktopBmp->h);
806 				if (_pixelformat.bytesPerPixel == 1) {
807 					byte r, g, b;
808 					_desktopBmp->format.colorToRGB(color, r, g, b);
809 					if (color > 0) {
810 						*((byte *)_desktop->getBasePtr(i, j)) = findBestColor(r, g, b);
811 					}
812 				} else {
813 					*((uint32 *)_desktop->getBasePtr(i, j)) = color;
814 				}
815 			}
816 		}
817 	} else {
818 		Common::Rect r(_desktop->getBounds());
819 
820 		MacPlotData pd(_desktop, nullptr, &_patterns, kPatternCheckers, 0, 0, 1, _colorWhite);
821 
822 		Graphics::drawRoundRect(r, kDesktopArc, _colorBlack, true, getDrawPixel(), &pd);
823 	}
824 }
825 
draw()826 void MacWindowManager::draw() {
827 	removeMarked();
828 
829 	Common::Rect bounds = getScreenBounds();
830 
831 	if (_fullRefresh) {
832 		if (!(_mode & kWMModeNoDesktop)) {
833 			Common::Rect screen = getScreenBounds();
834 			if (_desktop->w != screen.width() || _desktop->h != screen.height()) {
835 				_desktop->free();
836 				_desktop->create(screen.width(), screen.height(), _pixelformat);
837 				drawDesktop();
838 			}
839 
840 			if (_screen) {
841 				_screen->blitFrom(*_desktop, Common::Point(0, 0));
842 				g_system->copyRectToScreen(_screen->getPixels(), _screen->pitch, 0, 0, _screen->w, _screen->h);
843 			} else {
844 				_screenCopyPauseToken = new PauseToken(pauseEngine());
845 				g_system->copyRectToScreen(_desktop->getPixels(), _desktop->pitch, 0, 0, _desktop->w, _desktop->h);
846 			}
847 		}
848 		if (_redrawEngineCallback != nullptr)
849 			_redrawEngineCallback(_engineR);
850 	}
851 
852 	Common::Array<Common::Rect> dirtyRects;
853 	for (Common::List<BaseMacWindow *>::const_iterator it = _windowStack.begin(); it != _windowStack.end(); it++) {
854 		BaseMacWindow *w = *it;
855 		if (!w->isVisible())
856 			continue;
857 
858 		Common::Rect clip = w->getInnerDimensions();
859 		clip.clip(bounds);
860 
861 		if (clip.isEmpty())
862 			continue;
863 
864 		clip = w->getDimensions();
865 		clip.clip(bounds);
866 
867 		if (clip.isEmpty())
868 			continue;
869 
870 		bool forceRedraw = _fullRefresh;
871 		if (!forceRedraw && dirtyRects.size()) {
872 			for (Common::Array<Common::Rect>::iterator dirty = dirtyRects.begin(); dirty != dirtyRects.end(); dirty++) {
873 				if (clip.intersects(*dirty)) {
874 					forceRedraw = true;
875 					break;
876 				}
877 			}
878 		}
879 
880 		if (!_screen) {
881 			if (w->isDirty() || forceRedraw) {
882 				w->draw(forceRedraw);
883 
884 				Common::Rect outerDims = w->getDimensions();
885 				Common::Rect innerDims = w->getInnerDimensions();
886 				int adjWidth, adjHeight;
887 
888 				if (w->isDirty() || forceRedraw) {
889 					w->draw(forceRedraw);
890 
891 					adjustDimensions(clip, outerDims, adjWidth, adjHeight);
892 
893 					if (_pixelformat.bytesPerPixel == 1) {
894 						Surface *surface = g_system->lockScreen();
895 						ManagedSurface *border = w->getBorderSurface();
896 
897 						for (int y = 0; y < adjHeight; y++) {
898 							const byte *src = (const byte *)border->getBasePtr(clip.left - outerDims.left, y);
899 							byte *dst = (byte *)surface->getBasePtr(clip.left, y + clip.top);
900 							for (int x = 0; x < adjWidth; x++, src++, dst++)
901 									if (*src != _colorGreen2 && *src != _colorGreen)
902 										*dst = *src;
903 						}
904 
905 						g_system->unlockScreen();
906 					} else {
907 						g_system->copyRectToScreen(w->getBorderSurface()->getBasePtr(MAX(clip.left - outerDims.left, 0), MAX(clip.top - outerDims.top, 0)), w->getBorderSurface()->pitch, clip.left, clip.top, adjWidth, adjHeight);
908 					}
909 				}
910 
911 				adjustDimensions(clip, innerDims, adjWidth, adjHeight);
912 				g_system->copyRectToScreen(w->getWindowSurface()->getBasePtr(MAX(clip.left - innerDims.left, 0), MAX(clip.top - innerDims.top, 0)), w->getWindowSurface()->pitch,MAX(innerDims.left, (int16)0), MAX(innerDims.top, (int16)0), adjWidth, adjHeight);
913 
914 				dirtyRects.push_back(clip);
915 			}
916 
917 			if (_screenCopyPauseToken) {
918 				_screenCopyPauseToken->clear();
919 				delete _screenCopyPauseToken;
920 				_screenCopyPauseToken = nullptr;
921 			}
922 		} else if (w->draw(_screen, forceRedraw)) {
923 			w->setDirty(false);
924 			g_system->copyRectToScreen(_screen->getBasePtr(clip.left, clip.top), _screen->pitch, clip.left, clip.top, clip.width(), clip.height());
925 			dirtyRects.push_back(clip);
926 		}
927 	}
928 
929 	// Menu is drawn on top of everything and always
930 	if (_menu && !(_mode & kWMModeFullscreen)) {
931 		if (_fullRefresh)
932 			_menu->draw(_screen, _fullRefresh);
933 		else {
934 			// add intersection check with menu
935 			bool menuRedraw = false;
936 			for (Common::Array<Common::Rect>::iterator dirty = dirtyRects.begin(); dirty != dirtyRects.end(); dirty++) {
937 				if (_menu->checkIntersects(*dirty)) {
938 					menuRedraw = true;
939 					break;
940 				}
941 			}
942 			_menu->draw(_screen, menuRedraw);
943 		}
944 	}
945 
946 	_fullRefresh = false;
947 }
948 
menuTimerHandler(void * refCon)949 static void menuTimerHandler(void *refCon) {
950 	MacWindowManager *wm = (MacWindowManager *)refCon;
951 
952 	if (wm->_menuHotzone.contains(wm->_lastMousePos)) {
953 		wm->activateMenu();
954 	}
955 
956 	wm->_menuTimerActive = false;
957 
958 	g_system->getTimerManager()->removeTimerProc(&menuTimerHandler);
959 }
960 
processEvent(Common::Event & event)961 bool MacWindowManager::processEvent(Common::Event &event) {
962 	switch (event.type) {
963 	case Common::EVENT_MOUSEMOVE:
964 		_lastMousePos = event.mouse;
965 		break;
966 	case Common::EVENT_LBUTTONDOWN:
967 		_mouseDown = true;
968 		_lastClickPos = event.mouse;
969 		break;
970 	case Common::EVENT_LBUTTONUP:
971 		_mouseDown = false;
972 		break;
973 	default:
974 		break;
975 	}
976 
977 	if (_menu && !_menu->isVisible()) {
978 		if ((_mode & kWMModeAutohideMenu) && event.type == Common::EVENT_MOUSEMOVE) {
979 			if (!_menuTimerActive && _menuHotzone.contains(event.mouse)) {
980 				_menuTimerActive = true;
981 
982 				g_system->getTimerManager()->installTimerProc(&menuTimerHandler, _menuDelay, this, "menuWindowCursor");
983 			}
984 		}
985 	}
986 
987 	// Menu gets events first for shortcuts and menu bar
988 	if (_menu && _menu->processEvent(event))
989 		return true;
990 
991 	if (_activeWindow != -1) {
992 		if ((_windows[_activeWindow]->isEditable() && _windows[_activeWindow]->getType() == kWindowWindow &&
993 				 ((MacWindow *)_windows[_activeWindow])->getInnerDimensions().contains(event.mouse.x, event.mouse.y)) ||
994 				(_activeWidget && _activeWidget->isEditable() &&
995 				 _activeWidget->getDimensions().contains(event.mouse.x, event.mouse.y))) {
996 			if (getCursorType() != kMacCursorBeam) {
997 				_tempType = getCursorType();
998 				_inEditableArea = true;
999 				replaceCursor(kMacCursorBeam);
1000 			}
1001 		} else {
1002 			// here, we use _inEditableArea is distinguish whether the current Beam cursor is set by director or ourself
1003 			// if we are not in the editable area but we are drawing the Beam cursor, then the cursor is set by director, thus we don't replace it
1004 			if (getCursorType() == kMacCursorBeam && _inEditableArea) {
1005 				replaceCursor(_tempType, _cursor);
1006 				_inEditableArea = false;
1007 			}
1008 		}
1009 	}
1010 
1011 	for (Common::List<BaseMacWindow *>::const_iterator it = _windowStack.end(); it != _windowStack.begin();) {
1012 		it--;
1013 		BaseMacWindow *w = *it;
1014 
1015 		if (w->hasAllFocus() || (w->isEditable() && event.type == Common::EVENT_KEYDOWN) ||
1016 				w->getDimensions().contains(event.mouse.x, event.mouse.y)) {
1017 			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_LBUTTONUP)
1018 				setActiveWindow(w->getId());
1019 
1020 			return w->processEvent(event);
1021 		}
1022 	}
1023 
1024 	return false;
1025 }
1026 
adjustDimensions(const Common::Rect & clip,const Common::Rect & dims,int & adjWidth,int & adjHeight)1027 void MacWindowManager::adjustDimensions(const Common::Rect &clip, const Common::Rect &dims, int &adjWidth, int &adjHeight) {
1028 	int wOffset, hOffset;
1029 
1030 	wOffset = clip.left - dims.left;
1031 	adjWidth = dims.width();
1032 	if (wOffset > 0) {
1033 		adjWidth -= wOffset;
1034 	} else if (dims.right > getScreenBounds().right) {
1035 		adjWidth -= (dims.right - getScreenBounds().right);
1036 	}
1037 
1038 	hOffset = clip.top - dims.top;
1039 	adjHeight = dims.height();
1040 	if (hOffset > 0) {
1041 		adjHeight -= hOffset;
1042 	} else if (dims.bottom > getScreenBounds().bottom) {
1043 		adjHeight -= (dims.bottom - getScreenBounds().bottom);
1044 	}
1045 }
1046 
removeMarked()1047 void MacWindowManager::removeMarked() {
1048 	if (!_needsRemoval) return;
1049 
1050 	Common::List<BaseMacWindow *>::const_iterator it;
1051 	for (it = _windowsToRemove.begin(); it != _windowsToRemove.end(); it++) {
1052 		removeFromStack(*it);
1053 		removeFromWindowList(*it);
1054 		delete *it;
1055 		_activeWindow = -1;
1056 		_fullRefresh = true;
1057 	}
1058 	_windowsToRemove.clear();
1059 	_needsRemoval = false;
1060 
1061 	// Do we need compact lastid?
1062 	_lastId = 0;
1063 	for (Common::HashMap<uint, BaseMacWindow *>::iterator lit = _windows.begin(); lit != _windows.end(); lit++) {
1064 		if (lit->_key >= (uint)_lastId)
1065 			_lastId = lit->_key + 1;
1066 	}
1067 }
1068 
removeFromStack(BaseMacWindow * target)1069 void MacWindowManager::removeFromStack(BaseMacWindow *target) {
1070 	Common::List<BaseMacWindow *>::iterator stackIt;
1071 	for (stackIt = _windowStack.begin(); stackIt != _windowStack.end(); stackIt++) {
1072 		if (*stackIt == target) {
1073 			stackIt = _windowStack.erase(stackIt);
1074 			stackIt--;
1075 		}
1076 	}
1077 }
1078 
removeFromWindowList(BaseMacWindow * target)1079 void MacWindowManager::removeFromWindowList(BaseMacWindow *target) {
1080 	// _windows.erase(target->getId()); // Is applicable?
1081 	for (Common::HashMap<uint, BaseMacWindow *>::iterator it = _windows.begin(); it != _windows.end(); it++) {
1082 		if (it->_value == target) {
1083 			_windows.erase(it);
1084 			break;
1085 		}
1086 	}
1087 }
1088 
1089 
addZoomBox(ZoomBox * box)1090 void MacWindowManager::addZoomBox(ZoomBox *box) {
1091 	_zoomBoxes.push_back(box);
1092 }
1093 
renderZoomBox(bool redraw)1094 void MacWindowManager::renderZoomBox(bool redraw) {
1095 	if (!_zoomBoxes.size())
1096 		return;
1097 
1098 	ZoomBox *box = _zoomBoxes.front();
1099 	uint32 t = g_system->getMillis();
1100 
1101 	MacPlotData pd(_screen, nullptr, &getPatterns(), Graphics::kPatternCheckers, 0, 0, 1, 0, true);
1102 
1103 	// Undraw the previous boxes
1104 	if (box->last.size() != 0) {
1105 		for (uint i = 0; i < box->last.size(); i++) {
1106 			Common::Rect r = box->last.remove_at(i);
1107 			zoomBoxInner(r, pd);
1108 		}
1109 	}
1110 
1111 	if (box->nextTime > t)
1112 		return;
1113 
1114 	const int numSteps = 14;
1115 	// We have 15 steps in total, and we have flying rectange
1116 	// from switching 3/4 frames
1117 
1118 	int start, end;
1119 	// Determine, how many rectangles and what are their numbers
1120 	if (box->step <= 5) {
1121 		start = 1;
1122 		end = box->step - 1;
1123 	} else {
1124 		start = box->step - 4;
1125 		end = MIN(start + 3 - box->step % 2, 7);
1126 	}
1127 
1128 	for (int i = start; i <= end; i++) {
1129 		Common::Rect r(box->start.left   + (box->end.left   - box->start.left)   * i / 8,
1130 					   box->start.top    + (box->end.top    - box->start.top)    * i / 8,
1131 					   box->start.right  + (box->end.right  - box->start.right)  * i / 8,
1132 					   box->start.bottom + (box->end.bottom - box->start.bottom) * i / 8);
1133 
1134 		zoomBoxInner(r, pd);
1135 		box->last.push_back(r);
1136 	}
1137 
1138 	box->step++;
1139 	box->nextTime = box->startTime + 1000 * box->step * box->delay / 60;
1140 
1141 	if (redraw) {
1142 		g_system->copyRectToScreen(_screen->getPixels(), _screen->pitch, 0, 0, _screen->getBounds().width(), _screen->getBounds().height()); // zoomBox
1143 	}
1144 
1145 	if (box->step >= numSteps) {
1146 		delete _zoomBoxes[0];
1147 		_zoomBoxes.remove_at(0);
1148 	}
1149 }
1150 
zoomBoxInner(Common::Rect & r,Graphics::MacPlotData & pd)1151 void MacWindowManager::zoomBoxInner(Common::Rect &r, Graphics::MacPlotData &pd) {
1152 	Graphics::drawLine(r.left,  r.top,    r.right, r.top,    0xff, getDrawPixel(), &pd);
1153 	Graphics::drawLine(r.right, r.top,    r.right, r.bottom, 0xff, getDrawPixel(), &pd);
1154 	Graphics::drawLine(r.left,  r.bottom, r.right, r.bottom, 0xff, getDrawPixel(), &pd);
1155 	Graphics::drawLine(r.left,  r.top,    r.left,  r.bottom, 0xff, getDrawPixel(), &pd);
1156 }
1157 
1158 /////////////////
1159 // Cursor stuff
1160 /////////////////
replaceCursorType(MacCursorType type)1161 void MacWindowManager::replaceCursorType(MacCursorType type) {
1162 	if (_cursorTypeStack.empty())
1163 		_cursorTypeStack.push(type);
1164 	else
1165 		_cursorTypeStack.top() = type;
1166 }
1167 
getCursorType() const1168 MacCursorType MacWindowManager::getCursorType() const {
1169 	if (_cursorTypeStack.empty())
1170 		return kMacCursorOff;
1171 
1172 	return _cursorTypeStack.top();
1173 }
1174 
pushCursor(MacCursorType type,Cursor * cursor)1175 void MacWindowManager::pushCursor(MacCursorType type, Cursor *cursor) {
1176 	switch (type) {
1177 	case kMacCursorOff:
1178 		CursorMan.pushCursor(nullptr, 0, 0, 0, 0, 0);
1179 		CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1180 		break;
1181 	case kMacCursorArrow:
1182 		CursorMan.pushCursor(macCursorArrow, 11, 16, 1, 1, 3);
1183 		CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1184 		break;
1185 	case kMacCursorBeam:
1186 		CursorMan.pushCursor(macCursorBeam, 11, 16, 1, 1, 3);
1187 		CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1188 		break;
1189 	case kMacCursorCrossHair:
1190 		CursorMan.pushCursor(macCursorCrossHair, 11, 16, 1, 1, 3);
1191 		CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1192 		break;
1193 	case kMacCursorCrossBar:
1194 		CursorMan.pushCursor(macCursorCrossBar, 11, 16, 1, 1, 3);
1195 		CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1196 		break;
1197 	case kMacCursorWatch:
1198 		CursorMan.pushCursor(macCursorWatch, 11, 16, 1, 1, 3);
1199 		CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1200 		break;
1201 	case kMacCursorCustom:
1202 		if (!cursor) {
1203 			warning("MacWindowManager::pushCursor(): Custom cursor signified but not provided");
1204 			return;
1205 		}
1206 
1207 		pushCustomCursor(cursor);
1208 	}
1209 
1210 	_cursorTypeStack.push(type);
1211 }
1212 
replaceCursor(MacCursorType type,Cursor * cursor)1213 void MacWindowManager::replaceCursor(MacCursorType type, Cursor *cursor) {
1214 	switch (type) {
1215 	case kMacCursorOff:
1216 		CursorMan.replaceCursor(nullptr, 0, 0, 0, 0, 0);
1217 		CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
1218 		break;
1219 	case kMacCursorArrow:
1220 		CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3);
1221 		CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
1222 		break;
1223 	case kMacCursorBeam:
1224 		CursorMan.replaceCursor(macCursorBeam, 11, 16, 1, 1, 3);
1225 		CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
1226 		break;
1227 	case kMacCursorCrossHair:
1228 		CursorMan.replaceCursor(macCursorCrossHair, 11, 16, 1, 1, 3);
1229 		CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
1230 		break;
1231 	case kMacCursorCrossBar:
1232 		CursorMan.replaceCursor(macCursorCrossBar, 11, 16, 1, 1, 3);
1233 		CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
1234 		break;
1235 	case kMacCursorWatch:
1236 		CursorMan.replaceCursor(macCursorWatch, 11, 16, 1, 1, 3);
1237 		CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
1238 		break;
1239 	case kMacCursorCustom:
1240 		if (!cursor) {
1241 			warning("MacWindowManager::replaceCursor(): Custom cursor signified but not provided");
1242 			return;
1243 		}
1244 
1245 		CursorMan.replaceCursor(cursor);
1246 		break;
1247 	}
1248 
1249 	replaceCursorType(type);
1250 }
1251 
pushCustomCursor(const byte * data,int w,int h,int hx,int hy,int transcolor)1252 void MacWindowManager::pushCustomCursor(const byte *data, int w, int h, int hx, int hy, int transcolor) {
1253 	CursorMan.pushCursor(data, w, h, hx, hy, transcolor);
1254 	CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1255 	_cursorTypeStack.push(kMacCursorCustom);
1256 }
1257 
replaceCustomCursor(const byte * data,int w,int h,int hx,int hy,int transcolor)1258 void MacWindowManager::replaceCustomCursor(const byte *data, int w, int h, int hx, int hy, int transcolor) {
1259 	CursorMan.replaceCursor(data, w, h, hx, hy, transcolor);
1260 	CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
1261 	replaceCursorType(kMacCursorCustom);
1262 }
1263 
pushCustomCursor(const Graphics::Cursor * cursor)1264 void MacWindowManager::pushCustomCursor(const Graphics::Cursor *cursor) {
1265 	CursorMan.pushCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(),
1266 	                     cursor->getHotspotY(), cursor->getKeyColor());
1267 
1268 	if (cursor->getPalette())
1269 		CursorMan.pushCursorPalette(cursor->getPalette(), cursor->getPaletteStartIndex(), cursor->getPaletteCount());
1270 	else
1271 		CursorMan.pushCursorPalette(cursorPalette, 0, 2);
1272 
1273 	_cursorTypeStack.push(kMacCursorCustom);
1274 }
1275 
popCursor()1276 void MacWindowManager::popCursor() {
1277 	CursorMan.popCursor();
1278 	CursorMan.popCursorPalette();
1279 	_cursorTypeStack.pop();
1280 }
1281 
1282 ///////////////////
1283 // Palette stuff
1284 ///////////////////
1285 #define LOOKUPCOLOR(x) _color ## x = findBestColor(palette[kColor ## x * 3], palette[kColor ## x  * 3 + 1], palette[kColor ## x * 3 + 2]);
1286 
passPalette(const byte * pal,uint size)1287 void MacWindowManager::passPalette(const byte *pal, uint size) {
1288 	if (_palette)
1289 		free(_palette);
1290 
1291 	if (size) {
1292 		_palette = (byte *)malloc(size * 3);
1293 		memcpy(_palette, pal, size * 3);
1294 	}
1295 	_paletteSize = size;
1296 
1297 	_colorHash.clear();
1298 	_invertColorHash.clear();
1299 
1300 	LOOKUPCOLOR(White);
1301 	LOOKUPCOLOR(Gray80);
1302 	LOOKUPCOLOR(Gray88);
1303 	LOOKUPCOLOR(GrayEE);
1304 	LOOKUPCOLOR(Black);
1305 	LOOKUPCOLOR(Green);
1306 	LOOKUPCOLOR(Green2);
1307 
1308 	drawDesktop();
1309 	setFullRefresh(true);
1310 }
1311 
findBestColor(uint32 color)1312 uint MacWindowManager::findBestColor(uint32 color) {
1313 	byte r, g, b;
1314 	decomposeColor(color, r, g, b);
1315 	return findBestColor(r, g, b);
1316 }
1317 
findBestColor(byte cr,byte cg,byte cb)1318 uint MacWindowManager::findBestColor(byte cr, byte cg, byte cb) {
1319 	if (_pixelformat.bytesPerPixel == 4)
1320 		return _pixelformat.RGBToColor(cr, cg, cb);
1321 
1322 	uint bestColor = 0;
1323 	double min = 0xFFFFFFFF;
1324 
1325 	uint32 color = cr << 16 | cg << 8 | cb;
1326 
1327 	if (_colorHash.contains(color))
1328 		return _colorHash[color];
1329 
1330 	for (uint i = 0; i < _paletteSize; ++i) {
1331 		int rmean = (*(_palette + 3 * i + 0) + cr) / 2;
1332 		int r = *(_palette + 3 * i + 0) - cr;
1333 		int g = *(_palette + 3 * i + 1) - cg;
1334 		int b = *(_palette + 3 * i + 2) - cb;
1335 
1336 		double dist = sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8));
1337 		if (min > dist) {
1338 			bestColor = i;
1339 			min = dist;
1340 		}
1341 	}
1342 
1343 	_colorHash[color] = bestColor;
1344 
1345 	return bestColor;
1346 }
1347 
decomposeColor(uint32 color,byte & r,byte & g,byte & b)1348 void MacWindowManager::decomposeColor(uint32 color, byte &r, byte &g, byte &b) {
1349 	if (_pixelformat.bytesPerPixel == 1 || color <= 0xff) {
1350 		r = *(_palette + 3 * color + 0);
1351 		g = *(_palette + 3 * color + 1);
1352 		b = *(_palette + 3 * color + 2);
1353 	} else {
1354 		_pixelformat.colorToRGB(color, r, g, b);
1355 	}
1356 }
1357 
inverter(uint src)1358 uint MacWindowManager::inverter(uint src) {
1359 	if (_invertColorHash.contains(src))
1360 		return _invertColorHash[src];
1361 
1362 	if (_pixelformat.bytesPerPixel == 1) {
1363 		byte r, g, b;
1364 		decomposeColor(src, r, g, b);
1365 		r = ~r;
1366 		g = ~g;
1367 		b = ~b;
1368 		_invertColorHash[src] = findBestColor(r, g, b);
1369 	} else {
1370 		uint32 alpha = _pixelformat.ARGBToColor(255, 0, 0, 0);
1371 		_invertColorHash[src] = ~(src & ~alpha) | alpha;
1372 	}
1373 	return _invertColorHash[src];
1374 }
1375 
pauseEngine()1376 PauseToken MacWindowManager::pauseEngine() {
1377 	return _engineP->pauseEngine();
1378 }
1379 
setEngine(Engine * engine)1380 void MacWindowManager::setEngine(Engine *engine) {
1381 	_engineP = engine;
1382 }
1383 
setEngineRedrawCallback(void * engine,void (* redrawCallback)(void *))1384 void MacWindowManager::setEngineRedrawCallback(void *engine, void (*redrawCallback)(void *)) {
1385 	_engineR = engine;
1386 	_redrawEngineCallback = redrawCallback;
1387 }
1388 
1389 } // End of namespace Graphics
1390