1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name ui.cpp - The user interface globals. */
12 //
13 //      (c) Copyright 1999-2019 by Lutz Sammer, Andreas Arens,
14 //                                 Jimmy Salmon, Pali Rohár and Andrettin
15 //
16 //      This program is free software; you can redistribute it and/or modify
17 //      it under the terms of the GNU General Public License as published by
18 //      the Free Software Foundation; only version 2 of the License.
19 //
20 //      This program is distributed in the hope that it will be useful,
21 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
22 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 //      GNU General Public License for more details.
24 //
25 //      You should have received a copy of the GNU General Public License
26 //      along with this program; if not, write to the Free Software
27 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 //      02111-1307, USA.
29 //
30 
31 //@{
32 
33 /*----------------------------------------------------------------------------
34 --  Includes
35 ----------------------------------------------------------------------------*/
36 
37 #include "stratagus.h"
38 
39 #include "ui/ui.h"
40 
41 #include "font.h"
42 //Wyrmgus start
43 #include "game.h"
44 #include "grand_strategy.h"
45 //Wyrmgus end
46 #include "iolib.h"
47 #include "map/map.h"
48 #include "map/map_layer.h"
49 #include "menus.h"
50 #include "title.h"
51 #include "translate.h"
52 #include "ui/contenttype.h"
53 #include "ui/interface.h"
54 #include "ui/popup.h"
55 #include "unit/unit.h"
56 #include "video.h"
57 #include "world.h"
58 
59 #include <stdarg.h>
60 
61 /*----------------------------------------------------------------------------
62 -- Variables
63 ----------------------------------------------------------------------------*/
64 
65 bool RightButtonAttacks;                   /// right button attacks
66 
67 static ViewportModeType NewViewportMode = VIEWPORT_SINGLE;
68 
69 bool FancyBuildings;                       /// Mirror buildings 1 yes, 0 now.
70 
71 /**
72 **  The user interface configuration
73 */
74 CUserInterface UI;
75 
76 /*----------------------------------------------------------------------------
77 -- Functions
78 ----------------------------------------------------------------------------*/
79 
80 /**
81 **  Show load progress.
82 **
83 **  @param fmt  printf format string.
84 */
ShowLoadProgress(const char * fmt,...)85 void ShowLoadProgress(const char *fmt, ...)
86 {
87 	static unsigned int lastProgressUpdate = SDL_GetTicks();
88 	if (SDL_GetTicks() < lastProgressUpdate + 16) {
89 		// Only show progress updates every c. 1/60th of a second, otherwise we're waiting for the screen too much
90 		return;
91 	}
92 	lastProgressUpdate = SDL_GetTicks();
93 
94 	UpdateLoadProgress();
95 
96 	va_list va;
97 	char temp[4096];
98 
99 	va_start(va, fmt);
100 	vsnprintf(temp, sizeof(temp) - 1, fmt, va);
101 	temp[sizeof(temp) - 1] = '\0';
102 	va_end(va);
103 
104 	if (Video.Depth && IsGameFontReady() && GetGameFont().IsLoaded()) {
105 		// Remove non printable chars
106 		for (unsigned char *s = (unsigned char *)temp; *s; ++s) {
107 			if (*s < 32) {
108 				*s = ' ';
109 			}
110 		}
111 		//Wyrmgus start
112 //		Video.FillRectangle(ColorBlack, 5, Video.Height - 18, Video.Width - 10, 18);
113 		if (loadingBackground == nullptr) {
114 			Video.FillRectangle(ColorBlack, 0, Video.Height - 18, Video.Width, 18);
115 		}
116 		//Wyrmgus end
117 		CLabel(GetGameFont()).DrawCentered(Video.Width / 2, Video.Height - 16, temp);
118 		//Wyrmgus start
119 //		InvalidateArea(5, Video.Height - 18, Video.Width - 10, 18);
120 		if (loadingBackground == nullptr) {
121 			InvalidateArea(0, Video.Height - 18, Video.Width, 18);
122 		} else {
123 			InvalidateArea(0, 0, Video.Width, Video.Height);
124 		}
125 		//Wyrmgus end
126 
127 		RealizeVideoMemory();
128 	} else {
129 		DebugPrint("!!!!%s\n" _C_ temp);
130 	}
131 
132 	PollEvents();
133 }
134 
135 /**
136 **	@brief	Update load progress.
137 */
UpdateLoadProgress()138 void UpdateLoadProgress()
139 {
140 	if (Video.Depth && IsGameFontReady() && GetGameFont().IsLoaded()) {
141 		UpdateLoadingBar();
142 	}
143 
144 	PollEvents();
145 }
146 
~CUnitInfoPanel()147 CUnitInfoPanel::~CUnitInfoPanel()
148 {
149 	for (std::vector<CContentType *>::iterator content = Contents.begin();
150 		 content != Contents.end(); ++content) {
151 		delete *content;
152 	}
153 	delete Condition;
154 }
155 
156 
CUserInterface()157 CUserInterface::CUserInterface() :
158 	MouseScroll(false), KeyScroll(false), KeyScrollSpeed(1),
159 	MouseScrollSpeed(1), MouseScrollSpeedDefault(0), MouseScrollSpeedControl(0),
160 	NormalFontColor("yellow"), ReverseFontColor("white"),
161 	SingleSelectedButton(nullptr),
162 	MaxSelectedFont(nullptr), MaxSelectedTextX(0), MaxSelectedTextY(0),
163 	SingleTrainingButton(nullptr),
164 	SingleTrainingFont(nullptr), SingleTrainingTextX(0), SingleTrainingTextY(0),
165 	TrainingFont(nullptr), TrainingTextX(0), TrainingTextY(0),
166 	IdleWorkerButton(nullptr), LevelUpUnitButton(nullptr),
167 	CompletedBarColor(0), CompletedBarShadow(0),
168 	ViewportMode(VIEWPORT_SINGLE), MouseViewport(nullptr),
169 	SelectedViewport(nullptr), NumViewports(0),
170 	MessageFont(nullptr), MessageScrollSpeed(5),
171 	CurrentMapLayer(nullptr), PreviousMapLayer(nullptr),
172 	ViewportCursorColor(0), Offset640X(0), Offset480Y(0),
173 	VictoryBackgroundG(nullptr), DefeatBackgroundG(nullptr)
174 {
175 	MouseWarpPos.x = MouseWarpPos.y = -1;
176 
177 	Point.Name = "cursor-point";
178 	Glass.Name = "cursor-glass";
179 	Cross.Name = "cursor-cross";
180 	YellowHair.Name = "cursor-yellow-hair";
181 	GreenHair.Name = "cursor-green-hair";
182 	RedHair.Name = "cursor-red-hair";
183 	Scroll.Name = "cursor-scroll";
184 
185 	ArrowE.Name = "cursor-arrow-e";
186 	ArrowNE.Name = "cursor-arrow-ne";
187 	ArrowN.Name = "cursor-arrow-n";
188 	ArrowNW.Name = "cursor-arrow-nw";
189 	ArrowW.Name = "cursor-arrow-w";
190 	ArrowSW.Name = "cursor-arrow-sw";
191 	ArrowS.Name = "cursor-arrow-s";
192 	ArrowSE.Name = "cursor-arrow-se";
193 
194 	NormalFontColor = "light-blue";
195 	ReverseFontColor = "yellow";
196 }
197 
198 /**
199 **  Get popup class pointer by string identifier.
200 **
201 **  @param ident  Popup identifier.
202 **
203 **  @return       popup class pointer.
204 */
PopupByIdent(const std::string & ident)205 CPopup *PopupByIdent(const std::string &ident)
206 {
207 	for (std::vector<CPopup *>::iterator i = UI.ButtonPopups.begin(); i < UI.ButtonPopups.end(); ++i) {
208 		if ((*i)->Ident == ident) {
209 			return *i;
210 		}
211 	}
212 	return nullptr;
213 }
214 
215 /**
216 **  Initialize the user interface.
217 */
InitUserInterface()218 void InitUserInterface()
219 {
220 	ShowLoadProgress("%s", _("Loading User Interface"));
221 
222 	UI.Offset640X = (Video.Width - 640) / 2;
223 	UI.Offset480Y = (Video.Height - 480) / 2;
224 
225 	//
226 	// Calculations
227 	//
228 	if (Map.Info.MapWidth) {
229 		UI.MapArea.EndX = std::min<int>(UI.MapArea.EndX, UI.MapArea.X + Map.Info.MapWidth * Map.GetCurrentPixelTileSize().x - 1);
230 		UI.MapArea.EndY = std::min<int>(UI.MapArea.EndY, UI.MapArea.Y + Map.Info.MapHeight * Map.GetCurrentPixelTileSize().y - 1);
231 	}
232 
233 	UI.SelectedViewport = UI.Viewports;
234 
235 	SetViewportMode(VIEWPORT_SINGLE);
236 
237 	UI.CompletedBarColor = Video.MapRGB(TheScreen->format, UI.CompletedBarColorRGB);
238 	UI.ViewportCursorColor = ColorWhite;
239 }
240 
241 /**
242 **  Load Cursor.
243 */
Load()244 void CursorConfig::Load()
245 {
246 	Assert(!Name.empty());
247 	Cursor = CursorByIdent(Name);
248 	if (Cursor == nullptr) {
249 		return ;
250 	}
251 	Assert(Name == Cursor->Ident);
252 }
253 
254 /**
255 **  Load the user interface graphics.
256 */
Load()257 void CUserInterface::Load()
258 {
259 	//Wyrmgus start
260 	// set the correct UI
261 	CleanUserInterfaceFillers();
262 	std::vector<CFiller> new_ui_fillers;
263 	if (ThisPlayer) {
264 		if (ThisPlayer->Faction != -1) {
265 			new_ui_fillers = PlayerRaces.GetFactionUIFillers(ThisPlayer->Faction);
266 		} else {
267 			new_ui_fillers = PlayerRaces.GetCivilizationUIFillers(ThisPlayer->Race);
268 		}
269 	}
270 	for (size_t i = 0; i < new_ui_fillers.size(); ++i) {
271 		CFiller filler = CFiller();
272 		filler.G = CGraphic::New(new_ui_fillers[i].G->File);
273 		filler.X = new_ui_fillers[i].X;
274 		filler.Y = new_ui_fillers[i].Y;
275 		UI.Fillers.push_back(filler);
276 	}
277 	//Wyrmgus end
278 
279 	//  Load graphics
280 	const int size = (int)Fillers.size();
281 	for (int i = 0; i < size; ++i) {
282 		Fillers[i].Load();
283 	}
284 
285 	for (int i = 0; i <= FreeWorkersCount; ++i) {
286 		if (Resources[i].G) {
287 			Resources[i].G->Load();
288 			Resources[i].G->UseDisplayFormat();
289 		}
290 	}
291 
292 	if (InfoPanel.G) {
293 		InfoPanel.G->Load();
294 		InfoPanel.G->UseDisplayFormat();
295 	}
296 	if (ButtonPanel.G) {
297 		ButtonPanel.G->Load();
298 		ButtonPanel.G->UseDisplayFormat();
299 	}
300 	if (PieMenu.G) {
301 		PieMenu.G->Load();
302 		PieMenu.G->UseDisplayFormat();
303 	}
304 
305 	//Wyrmgus start
306 	if (Preference.IconFrameG) {
307 		Preference.IconFrameG->Load();
308 		Preference.IconFrameG->UseDisplayFormat();
309 	}
310 	if (Preference.PressedIconFrameG) {
311 		Preference.PressedIconFrameG->Load();
312 		Preference.PressedIconFrameG->UseDisplayFormat();
313 	}
314 	if (Preference.CommandButtonFrameG) {
315 		Preference.CommandButtonFrameG->Load();
316 		Preference.CommandButtonFrameG->UseDisplayFormat();
317 	}
318 	if (Preference.BarFrameG) {
319 		Preference.BarFrameG->Load();
320 		Preference.BarFrameG->UseDisplayFormat();
321 	}
322 	if (Preference.InfoPanelFrameG) {
323 		Preference.InfoPanelFrameG->Load();
324 		Preference.InfoPanelFrameG->UseDisplayFormat();
325 	}
326 	if (Preference.ProgressBarG) {
327 		Preference.ProgressBarG->Load();
328 		Preference.ProgressBarG->UseDisplayFormat();
329 	}
330 	//Wyrmgus end
331 
332 	//  Resolve cursors
333 	Point.Load();
334 	Glass.Load();
335 	Cross.Load();
336 	YellowHair.Load();
337 	GreenHair.Load();
338 	RedHair.Load();
339 	Scroll.Load();
340 
341 	ArrowE.Load();
342 	ArrowNE.Load();
343 	ArrowN.Load();
344 	ArrowNW.Load();
345 	ArrowW.Load();
346 	ArrowSW.Load();
347 	ArrowS.Load();
348 	ArrowSE.Load();
349 }
350 
351 
Contains(const PixelPos & screenPos) const352 bool CMapArea::Contains(const PixelPos &screenPos) const
353 {
354 	return this->X <= screenPos.x && screenPos.x <= this->EndX
355 		   && this->Y <= screenPos.y && screenPos.y <= this->EndY;
356 }
357 
358 /**
359 **  Save the viewports.
360 **
361 **  @param file  Save file handle
362 **  @param ui    User interface to save
363 */
SaveViewports(CFile & file,const CUserInterface & ui)364 static void SaveViewports(CFile &file, const CUserInterface &ui)
365 {
366 	// FIXME: don't save the number
367 	file.printf("DefineViewports(\"mode\", %d", ui.ViewportMode);
368 	for (int i = 0; i < ui.NumViewports; ++i) {
369 		const CViewport &vp = ui.Viewports[i];
370 		file.printf(",\n  \"viewport\", {%d, %d, %d}", vp.MapPos.x, vp.MapPos.y,
371 					vp.Unit ? UnitNumber(*vp.Unit) : -1);
372 	}
373 	file.printf(")\n\n");
374 }
375 
376 /**
377 **  Save the user interface module.
378 **
379 **  @param file  Save file handle
380 */
SaveUserInterface(CFile & file)381 void SaveUserInterface(CFile &file)
382 {
383 	SaveViewports(file, UI);
384 }
385 
386 /**
387 **  Clean up a user interface.
388 */
~CUserInterface()389 CUserInterface::~CUserInterface()
390 {
391 }
392 
393 /**
394 **  Clean up the user interface module.
395 */
CleanUserInterface()396 void CleanUserInterface()
397 {
398 	// Filler
399 	//Wyrmgus start
400 	/*
401 	for (int i = 0; i < (int)UI.Fillers.size(); ++i) {
402 		CGraphic::Free(UI.Fillers[i].G);
403 	}
404 	UI.Fillers.clear();
405 	*/
406 	CleanUserInterfaceFillers();
407 	//Wyrmgus end
408 
409 	// Resource Icons
410 	for (int i = 0; i <= FreeWorkersCount; ++i) {
411 		CGraphic::Free(UI.Resources[i].G);
412 	}
413 
414 	// Info Panel
415 	CGraphic::Free(UI.InfoPanel.G);
416 	for (std::vector<CUnitInfoPanel *>::iterator panel = UI.InfoPanelContents.begin();
417 		 panel != UI.InfoPanelContents.end(); ++panel) {
418 		delete *panel;
419 	}
420 	UI.InfoPanelContents.clear();
421 
422 	//Wyrmgus start
423 	CGraphic::Free(Preference.IconFrameG);
424 	CGraphic::Free(Preference.PressedIconFrameG);
425 	CGraphic::Free(Preference.CommandButtonFrameG);
426 	CGraphic::Free(Preference.BarFrameG);
427 	CGraphic::Free(Preference.InfoPanelFrameG);
428 	CGraphic::Free(Preference.ProgressBarG);
429 	//Wyrmgus end
430 
431 	// Button Popups
432 	for (std::vector<CPopup *>::iterator popup = UI.ButtonPopups.begin();
433 		 popup != UI.ButtonPopups.end(); ++popup) {
434 		delete *popup;
435 	}
436 	UI.ButtonPopups.clear();
437 
438 	delete UI.SingleSelectedButton;
439 	UI.SelectedButtons.clear();
440 	delete UI.SingleTrainingButton;
441 	UI.SingleTrainingText.clear();
442 	UI.TrainingButtons.clear();
443 	UI.TrainingText.clear();
444 	delete UI.UpgradingButton;
445 	delete UI.ResearchingButton;
446 	UI.TransportingButtons.clear();
447 	//Wyrmgus start
448 	delete UI.IdleWorkerButton;
449 	delete UI.LevelUpUnitButton;
450 	UI.HeroUnitButtons.clear();
451 	UI.InventoryButtons.clear();
452 	//Wyrmgus end
453 	UI.UserButtons.clear();
454 
455 	// Button Panel
456 	CGraphic::Free(UI.ButtonPanel.G);
457 
458 	// Pie Menu
459 	CGraphic::Free(UI.PieMenu.G);
460 
461 	// Backgrounds
462 	CGraphic::Free(UI.VictoryBackgroundG);
463 	CGraphic::Free(UI.DefeatBackgroundG);
464 
465 	// Title Screens
466 	if (TitleScreens) {
467 		for (int i = 0; TitleScreens[i]; ++i) {
468 			delete TitleScreens[i];
469 		}
470 		delete[] TitleScreens;
471 		TitleScreens = nullptr;
472 	}
473 }
474 
475 //Wyrmgus start
CleanUserInterfaceFillers()476 void CleanUserInterfaceFillers()
477 {
478 	for (int i = 0; i < (int)UI.Fillers.size(); ++i) {
479 		CGraphic::Free(UI.Fillers[i].G);
480 	}
481 	UI.Fillers.clear();
482 }
483 
UpdateSurfaceLayerButtons()484 void UpdateSurfaceLayerButtons()
485 {
486 	unsigned int last_surface_layer = 0;
487 	if (UI.CurrentMapLayer) {
488 		for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
489 			if (UI.CurrentMapLayer->Plane == Map.MapLayers[z]->Plane && UI.CurrentMapLayer->World == Map.MapLayers[z]->World && Map.MapLayers[z]->SurfaceLayer > (int) last_surface_layer) {
490 				last_surface_layer = Map.MapLayers[z]->SurfaceLayer;
491 			}
492 		}
493 	}
494 
495 	for (size_t i = 0; i < UI.SurfaceLayerButtons.size(); ++i) {
496 		if (i <= last_surface_layer && last_surface_layer > 0) {
497 			UI.SurfaceLayerButtons[i].X = -2;
498 			UI.SurfaceLayerButtons[i].Y = Video.Height - 157 + (19  * i);
499 		} else {
500 			UI.SurfaceLayerButtons[i].X = -1;
501 			UI.SurfaceLayerButtons[i].Y = -1;
502 		}
503 	}
504 }
505 //Wyrmgus end
506 
FreeButtonStyles()507 void FreeButtonStyles()
508 {
509 	std::map<std::string, ButtonStyle *>::iterator i;
510 	for (i = ButtonStyleHash.begin(); i != ButtonStyleHash.end(); ++i) {
511 		delete(*i).second;
512 	}
513 	ButtonStyleHash.clear();
514 }
515 
516 /**
517 **  Takes coordinates of a pixel in stratagus's window and computes
518 **  the map viewport which contains this pixel.
519 **
520 **  @param screenPos  pixel coordinate with origin at UL corner of screen
521 **
522 **  @return viewport pointer or null if this pixel is not inside
523 **  any of the viewports.
524 **
525 **  @note This functions only works with rectangular viewports, when
526 **  we support shaped map window, this must be rewritten.
527 */
GetViewport(const PixelPos & screenPos)528 CViewport *GetViewport(const PixelPos &screenPos)
529 {
530 	for (CViewport *vp = UI.Viewports; vp < UI.Viewports + UI.NumViewports; ++vp) {
531 		if (vp->Contains(screenPos)) {
532 			return vp;
533 		}
534 	}
535 	return nullptr;
536 }
537 
538 /**
539 **  Takes an array of new Viewports which are supposed to have their
540 **  pixel geometry (CViewport::[XY] and CViewport::End[XY]) already
541 **  computed. Using this information as well as old viewport's
542 **  parameters fills in new viewports' CViewport::Map* parameters.
543 **  Then it replaces the old viewports with the new ones and finishes
544 **  the set-up of the new mode.
545 **
546 **  @param new_vps  The array of the new viewports
547 **  @param num_vps  The number of elements in the new_vps[] array.
548 */
FinishViewportModeConfiguration(CViewport new_vps[],int num_vps)549 static void FinishViewportModeConfiguration(CViewport new_vps[], int num_vps)
550 {
551 	//  Compute location of the viewport using oldviewport
552 	for (int i = 0; i < num_vps; ++i) {
553 		new_vps[i].MapPos.x = 0;
554 		new_vps[i].MapPos.y = 0;
555 		const CViewport *vp = GetViewport(new_vps[i].GetTopLeftPos());
556 		if (vp) {
557 			const PixelDiff relDiff = new_vps[i].GetTopLeftPos() - vp->GetTopLeftPos();
558 
559 			new_vps[i].Offset = relDiff + Map.TilePosToMapPixelPos_TopLeft(vp->MapPos, UI.CurrentMapLayer) + vp->Offset;
560 		} else {
561 			new_vps[i].Offset.x = 0;
562 			new_vps[i].Offset.y = 0;
563 		}
564 	}
565 
566 	// Affect the old viewport.
567 	for (int i = 0; i < num_vps; ++i) {
568 		CViewport &vp = UI.Viewports[i];
569 
570 		vp.TopLeftPos = new_vps[i].TopLeftPos;
571 		vp.BottomRightPos = new_vps[i].BottomRightPos;
572 		vp.Set(new_vps[i].MapPos, new_vps[i].Offset);
573 	}
574 	UI.NumViewports = num_vps;
575 
576 	//
577 	//  Update the viewport pointers
578 	//
579 	UI.MouseViewport = GetViewport(CursorScreenPos);
580 	UI.SelectedViewport = std::min(UI.Viewports + UI.NumViewports - 1, UI.SelectedViewport);
581 }
582 
583 /**
584 **  Takes a viewport which is supposed to have its CViewport::[XY]
585 **  correctly filled-in and computes CViewport::End[XY] attributes
586 **  according to clipping information passed in other two arguments.
587 **
588 **  @param vp     The viewport.
589 **  @param ClipX  Maximum x-coordinate of the viewport's right side
590 **                as dictated by current UI's geometry and ViewportMode.
591 **  @param ClipY  Maximum y-coordinate of the viewport's bottom side
592 **                as dictated by current UI's geometry and ViewportMode.
593 **
594 **  @note It is supposed that values passed in Clip[XY] will
595 **  never be greater than UI::MapArea::End[XY].
596 **  However, they can be smaller according to the place
597 **  the viewport vp takes in context of current ViewportMode.
598 */
ClipViewport(CViewport & vp,int ClipX,int ClipY)599 static void ClipViewport(CViewport &vp, int ClipX, int ClipY)
600 {
601 	// begin with maximum possible viewport size
602 	//Wyrmgus start
603 //	vp.BottomRightPos.x = vp.TopLeftPos.x + Map.Info.MapWidth * Map.GetCurrentPixelTileSize().x - 1;
604 //	vp.BottomRightPos.y = vp.TopLeftPos.y + Map.Info.MapHeight * Map.GetCurrentPixelTileSize().y - 1;
605 	vp.BottomRightPos.x = vp.TopLeftPos.x + (Map.Info.MapWidths.size() && UI.CurrentMapLayer ? UI.CurrentMapLayer->GetWidth() : Map.Info.MapWidth) * Map.GetCurrentPixelTileSize().x - 1;
606 	vp.BottomRightPos.y = vp.TopLeftPos.y + (Map.Info.MapHeights.size() && UI.CurrentMapLayer ? UI.CurrentMapLayer->GetHeight() : Map.Info.MapHeight) * Map.GetCurrentPixelTileSize().y - 1;
607 	//Wyrmgus end
608 
609 	// first clip it to MapArea size if necessary
610 	vp.BottomRightPos.x = std::min<int>(vp.BottomRightPos.x, ClipX);
611 	vp.BottomRightPos.y = std::min<int>(vp.BottomRightPos.y, ClipY);
612 
613 	Assert(vp.BottomRightPos.x <= UI.MapArea.EndX);
614 	Assert(vp.BottomRightPos.y <= UI.MapArea.EndY);
615 }
616 
617 /**
618 **  Compute viewport parameters for single viewport mode.
619 **
620 **  The parameters include viewport's width and height expressed
621 **  in pixels, its position with respect to Stratagus's window
622 **  origin, and the corresponding map parameters expressed in map
623 **  tiles with origin at map origin (map tile (0,0)).
624 */
SetViewportModeSingle()625 static void SetViewportModeSingle()
626 {
627 	CViewport new_vps[MAX_NUM_VIEWPORTS];
628 
629 	DebugPrint("Single viewport set\n");
630 
631 	new_vps[0].TopLeftPos.x = UI.MapArea.X;
632 	new_vps[0].TopLeftPos.y = UI.MapArea.Y;
633 	ClipViewport(new_vps[0], UI.MapArea.EndX, UI.MapArea.EndY);
634 
635 	FinishViewportModeConfiguration(new_vps, 1);
636 }
637 
638 /**
639 **  Compute viewport parameters for horizontally split viewport mode.
640 **  This mode splits the UI::MapArea with a horizontal line to
641 **  2 (approximately) equal parts.
642 **
643 **  The parameters include viewport's width and height expressed
644 **  in pixels, its position with respect to Stratagus's window
645 **  origin, and the corresponding map parameters expressed in map
646 **  tiles with origin at map origin (map tile (0,0)).
647 */
SetViewportModeSplitHoriz()648 static void SetViewportModeSplitHoriz()
649 {
650 	CViewport new_vps[MAX_NUM_VIEWPORTS];
651 
652 	DebugPrint("Two horizontal viewports set\n");
653 
654 	new_vps[0].TopLeftPos.x = UI.MapArea.X;
655 	new_vps[0].TopLeftPos.y = UI.MapArea.Y;
656 	ClipViewport(new_vps[0], UI.MapArea.EndX,
657 				 UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);
658 
659 	new_vps[1].TopLeftPos.x = UI.MapArea.X;
660 	new_vps[1].TopLeftPos.y = new_vps[0].BottomRightPos.y + 1;
661 	ClipViewport(new_vps[1], UI.MapArea.EndX, UI.MapArea.EndY);
662 
663 	FinishViewportModeConfiguration(new_vps, 2);
664 }
665 
666 /**
667 **  Compute viewport parameters for horizontal 3-way split viewport mode.
668 **  This mode splits the UI::MapArea with a horizontal line to
669 **  2 (approximately) equal parts, then splits the bottom part vertically
670 **  to another 2 parts.
671 **
672 **  The parameters include viewport's width and height expressed
673 **  in pixels, its position with respect to Stratagus's window
674 **  origin, and the corresponding map parameters expressed in map
675 **  tiles with origin at map origin (map tile (0,0)).
676 */
SetViewportModeSplitHoriz3()677 static void SetViewportModeSplitHoriz3()
678 {
679 	CViewport new_vps[MAX_NUM_VIEWPORTS];
680 
681 	DebugPrint("Horizontal 3-way viewport division set\n");
682 
683 	new_vps[0].TopLeftPos.x = UI.MapArea.X;
684 	new_vps[0].TopLeftPos.y = UI.MapArea.Y;
685 	ClipViewport(new_vps[0], UI.MapArea.EndX,
686 				 UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);
687 
688 	new_vps[1].TopLeftPos.x = UI.MapArea.X;
689 	new_vps[1].TopLeftPos.y = new_vps[0].BottomRightPos.y + 1;
690 	ClipViewport(new_vps[1],
691 				 UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
692 				 UI.MapArea.EndY);
693 
694 	new_vps[2].TopLeftPos.x = new_vps[1].BottomRightPos.x + 1;
695 	new_vps[2].TopLeftPos.y = new_vps[0].BottomRightPos.y + 1;
696 	ClipViewport(new_vps[2], UI.MapArea.EndX, UI.MapArea.EndY);
697 
698 	FinishViewportModeConfiguration(new_vps, 3);
699 }
700 
701 /**
702 **  Compute viewport parameters for vertically split viewport mode.
703 **  This mode splits the UI::MapArea with a vertical line to
704 **  2 (approximately) equal parts.
705 **
706 **  The parameters  include viewport's width and height expressed
707 **  in pixels, its position with respect to Stratagus's window
708 **  origin, and the corresponding map parameters expressed in map
709 **  tiles with origin at map origin (map tile (0,0)).
710 */
SetViewportModeSplitVert()711 static void SetViewportModeSplitVert()
712 {
713 	CViewport new_vps[MAX_NUM_VIEWPORTS];
714 
715 	DebugPrint("Two vertical viewports set\n");
716 
717 	new_vps[0].TopLeftPos.x = UI.MapArea.X;
718 	new_vps[0].TopLeftPos.y = UI.MapArea.Y;
719 	ClipViewport(new_vps[0],
720 				 UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
721 				 UI.MapArea.EndY);
722 
723 	new_vps[1].TopLeftPos.x = new_vps[0].BottomRightPos.x + 1;
724 	new_vps[1].TopLeftPos.y = UI.MapArea.Y;
725 	ClipViewport(new_vps[1], UI.MapArea.EndX, UI.MapArea.EndY);
726 
727 	FinishViewportModeConfiguration(new_vps, 2);
728 }
729 
730 /**
731 **  Compute viewport parameters for 4-way split viewport mode.
732 **  This mode splits the UI::MapArea vertically *and* horizontally
733 **  to 4 (approximately) equal parts.
734 **
735 **  The parameters  include viewport's width and height expressed
736 **  in pixels, its position with respect to Stratagus's window
737 **  origin, and the corresponding map parameters expressed in map
738 **  tiles with origin at map origin (map tile (0,0)).
739 */
SetViewportModeQuad()740 static void SetViewportModeQuad()
741 {
742 	CViewport new_vps[MAX_NUM_VIEWPORTS];
743 
744 	DebugPrint("Four viewports set\n");
745 
746 	new_vps[0].TopLeftPos.x = UI.MapArea.X;
747 	new_vps[0].TopLeftPos.y = UI.MapArea.Y;
748 	ClipViewport(new_vps[0],
749 				 UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
750 				 UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);
751 
752 	new_vps[1].TopLeftPos.x = new_vps[0].BottomRightPos.x + 1;
753 	new_vps[1].TopLeftPos.y = UI.MapArea.Y;
754 	ClipViewport(new_vps[1],
755 				 UI.MapArea.EndX,
756 				 UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);
757 
758 	new_vps[2].TopLeftPos.x = UI.MapArea.X;
759 	new_vps[2].TopLeftPos.y = new_vps[0].BottomRightPos.y + 1;
760 	ClipViewport(new_vps[2],
761 				 UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
762 				 UI.MapArea.EndY);
763 
764 	new_vps[3].TopLeftPos.x = new_vps[1].TopLeftPos.x;
765 	new_vps[3].TopLeftPos.y = new_vps[2].TopLeftPos.y;
766 	ClipViewport(new_vps[3], UI.MapArea.EndX, UI.MapArea.EndY);
767 
768 	FinishViewportModeConfiguration(new_vps, 4);
769 }
770 
771 /**
772 **  Sets up (calls geometry setup routines for) a new viewport mode.
773 **
774 **  @param new_mode  New mode's number.
775 */
SetViewportMode(ViewportModeType new_mode)776 void SetViewportMode(ViewportModeType new_mode)
777 {
778 	switch (UI.ViewportMode = new_mode) {
779 		case VIEWPORT_SINGLE:
780 			SetViewportModeSingle();
781 			break;
782 		case VIEWPORT_SPLIT_HORIZ:
783 			SetViewportModeSplitHoriz();
784 			break;
785 		case VIEWPORT_SPLIT_HORIZ3:
786 			SetViewportModeSplitHoriz3();
787 			break;
788 		case VIEWPORT_SPLIT_VERT:
789 			SetViewportModeSplitVert();
790 			break;
791 		case VIEWPORT_QUAD:
792 			SetViewportModeQuad();
793 			break;
794 		default:
795 			DebugPrint("trying to set an unknown mode!!\n");
796 			break;
797 	}
798 }
799 
800 /**
801 **  Sets up a new viewport mode.
802 **
803 **  @param new_mode  New mode's number.
804 */
SetNewViewportMode(ViewportModeType new_mode)805 void SetNewViewportMode(ViewportModeType new_mode)
806 {
807 	NewViewportMode = new_mode;
808 	if (NewViewportMode >= NUM_VIEWPORT_MODES) {
809 		NewViewportMode = VIEWPORT_SINGLE;
810 	}
811 	if (NewViewportMode < 0) {
812 		NewViewportMode = (ViewportModeType)(NUM_VIEWPORT_MODES - 1);
813 	}
814 }
815 
816 /**
817 **  Cycles through predefined viewport modes (geometry configurations)
818 **  in order defined by the ViewportMode enumerated type.
819 **
820 **  @param step   The size of step used for cycling. Values that
821 **               make sense are mostly 1 (next viewport mode) and
822 **               -1 (previous viewport mode).
823 */
CycleViewportMode(int step)824 void CycleViewportMode(int step)
825 {
826 	NewViewportMode = (ViewportModeType)(UI.ViewportMode + step);
827 	if (NewViewportMode >= NUM_VIEWPORT_MODES) {
828 		NewViewportMode = VIEWPORT_SINGLE;
829 	}
830 	if (NewViewportMode < 0) {
831 		NewViewportMode = (ViewportModeType)(NUM_VIEWPORT_MODES - 1);
832 	}
833 }
834 
CheckViewportMode()835 void CheckViewportMode()
836 {
837 	if (NewViewportMode != UI.ViewportMode) {
838 		SetViewportMode(NewViewportMode);
839 	}
840 }
841 
842 /**
843 **  Check if mouse scrolling is enabled
844 */
GetMouseScroll()845 bool GetMouseScroll()
846 {
847 	return UI.MouseScroll;
848 }
849 
850 /**
851 **  Enable/disable scrolling with the mouse
852 **
853 **  @param enabled  True to enable mouse scrolling, false to disable
854 */
SetMouseScroll(bool enabled)855 void SetMouseScroll(bool enabled)
856 {
857 	UI.MouseScroll = enabled;
858 }
859 
860 /**
861 **  Check if keyboard scrolling is enabled
862 */
GetKeyScroll()863 bool GetKeyScroll()
864 {
865 	return UI.KeyScroll;
866 }
867 
868 /**
869 **  Enable/disable scrolling with the keyboard
870 **
871 **  @param enabled  True to enable keyboard scrolling, false to disable
872 */
SetKeyScroll(bool enabled)873 void SetKeyScroll(bool enabled)
874 {
875 	UI.KeyScroll = enabled;
876 }
877 
878 /**
879 **  Check if mouse grabbing is enabled
880 */
GetGrabMouse()881 bool GetGrabMouse()
882 {
883 	return SdlGetGrabMouse();
884 }
885 
886 /**
887 **  Enable/disable grabbing the mouse
888 **
889 **  @param enabled  True to enable mouse grabbing, false to disable
890 */
SetGrabMouse(bool enabled)891 void SetGrabMouse(bool enabled)
892 {
893 	ToggleGrabMouse(enabled ? 1 : -1);
894 }
895 
896 /**
897 **  Check if scrolling stops when leaving the window
898 */
GetLeaveStops()899 bool GetLeaveStops()
900 {
901 	return LeaveStops;
902 }
903 
904 /**
905 **  Enable/disable leaving the window stops scrolling
906 **
907 **  @param enabled  True to stop scrolling, false to disable
908 */
SetLeaveStops(bool enabled)909 void SetLeaveStops(bool enabled)
910 {
911 	LeaveStops = enabled;
912 }
913 
914 //@}
915