1 ///////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2010 by The Allacrost Project
3 //                         All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 /*!****************************************************************************
11  * \file    editor.cpp
12  * \author  Philip Vorsilak, gorzuate@allacrost.org
13  * \brief   Source file for editor's main window and user interface.
14  *****************************************************************************/
15 
16 #include "editor.h"
17 
18 using namespace hoa_script;
19 using namespace hoa_utils;
20 using namespace hoa_editor;
21 using namespace hoa_video;
22 using namespace std;
23 
Editor()24 Editor::Editor() : QMainWindow(),
25 	_skill_editor(NULL)
26 {
27 	// create the undo stack
28 	_undo_stack = new QUndoStack();
29 
30 	// set scollview to NULL because it's being checked inside _TilesEnableActions
31 	_ed_scrollview = NULL;
32 
33 	// create actions, menus, and toolbars
34 	_CreateActions();
35 	_CreateMenus();
36 	_CreateToolbars();
37 	_TilesEnableActions();
38 
39 	connect(_undo_stack, SIGNAL(canRedoChanged(bool)), _redo_action, SLOT(setEnabled(bool)));
40 	connect(_undo_stack, SIGNAL(canUndoChanged(bool)), _undo_action, SLOT(setEnabled(bool)));
41 
42 	// initialize viewing items
43 	_grid_on = false;
44 	_select_on = false;
45 	_ll_on = false;
46 	_ml_on = false;
47 	_ul_on = false;
48 	_ol_on = false;
49 	_coord_type = 0;
50 
51 	// create the main widget and layout
52 	_ed_splitter = new QSplitter(this);
53 	_ed_splitter->setOrientation(Qt::Vertical);
54 	_ed_tabs = NULL;
55 	setCentralWidget(_ed_splitter);
56 	resize(600, 400);
57 
58 	// set the window icon
59 	setWindowIcon(QIcon("img/logos/program_icon.bmp"));
60 
61 	// create error message for exceeding maximum number of contexts
62 	_error_max_contexts = new QErrorMessage(this);
63 
64 	// create the video engine's singleton
65 	// VideoManager = VideoEngine::SingletonCreate();
66 	// Commented out grid and tileset editor create and destroy VideoManager
67 
68 } // Editor constructor
69 
~Editor()70 Editor::~Editor()
71 {
72 	if (_ed_scrollview != NULL)
73 		delete _ed_scrollview;
74 	if (_ed_tabs != NULL)
75 		delete _ed_tabs;
76 	delete _ed_splitter;
77 	if (_skill_editor != NULL)
78 		delete _skill_editor;
79 	delete _undo_stack;
80 
81 	ScriptEngine::SingletonDestroy();
82 	VideoEngine::SingletonDestroy();
83 } // Editor destructor
84 
85 
86 
87 // ********** Protected function **********
88 
closeEvent(QCloseEvent *)89 void Editor::closeEvent(QCloseEvent*)
90 {
91     _FileQuit();
92 } // closeEvent(...)
93 
94 // ********** Private slots **********
95 
_FileMenuSetup()96 void Editor::_FileMenuSetup()
97 {
98 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
99 	{
100 		_save_as_action->setEnabled(true);
101 		_save_action->setEnabled(_ed_scrollview->_map->GetChanged());
102 		_close_action->setEnabled(true);
103 	} // map must exist in order to save or close it
104 	else
105 	{
106 		_save_as_action->setEnabled(false);
107 		_save_action->setEnabled(false);
108 		_close_action->setEnabled(false);
109 	} // map does not exist, can't save or close it
110 } // _FileMenuSetup()
111 
_ViewMenuSetup()112 void Editor::_ViewMenuSetup()
113 {
114 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
115 	{
116 		_toggle_grid_action->setEnabled(true);
117 		_toggle_ll_action->setEnabled(true);
118 		_toggle_ml_action->setEnabled(true);
119 		_toggle_ul_action->setEnabled(true);
120 		_toggle_ol_action->setEnabled(true);
121 		_coord_tile_action->setEnabled(true);
122 		_coord_collision_action->setEnabled(true);
123 		_view_textures_action->setEnabled(true);
124 	} // map must exist in order to set view options
125 	else
126 	{
127 		_toggle_grid_action->setEnabled(false);
128 		_toggle_ll_action->setEnabled(false);
129 		_toggle_ml_action->setEnabled(false);
130 		_toggle_ul_action->setEnabled(false);
131 		_toggle_ol_action->setEnabled(false);
132 		_coord_tile_action->setEnabled(false);
133 		_coord_collision_action->setEnabled(false);
134 		_view_textures_action->setEnabled(false);
135 	} // map does not exist, can't view it*/
136 } // _ViewMenuSetup()
137 
_TilesEnableActions()138 void Editor::_TilesEnableActions()
139 {
140 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
141 	{
142 		_undo_action->setText("Undo " + _undo_stack->undoText());
143 		_redo_action->setText("Redo " + _undo_stack->redoText());
144 		_layer_fill_action->setEnabled(true);
145 		_layer_clear_action->setEnabled(true);
146 		_toggle_select_action->setEnabled(true);
147 		_mode_paint_action->setEnabled(true);
148 		_mode_move_action->setEnabled(true);
149 		_mode_delete_action->setEnabled(true);
150 		_edit_ll_action->setEnabled(true);
151 		_edit_ml_action->setEnabled(true);
152 		_edit_ul_action->setEnabled(true);
153 		_edit_ol_action->setEnabled(true);
154 		_context_cbox->setEnabled(true);
155 	} // map must exist in order to paint it
156 	else
157 	{
158 		_undo_action->setEnabled(false);
159 		_redo_action->setEnabled(false);
160 		_layer_fill_action->setEnabled(false);
161 		_layer_clear_action->setEnabled(false);
162 		_toggle_select_action->setEnabled(false);
163 		_mode_paint_action->setEnabled(false);
164 		_mode_move_action->setEnabled(false);
165 		_mode_delete_action->setEnabled(false);
166 		_edit_ll_action->setEnabled(false);
167 		_edit_ml_action->setEnabled(false);
168 		_edit_ul_action->setEnabled(false);
169 		_edit_ol_action->setEnabled(false);
170 		_context_cbox->setEnabled(false);
171 	} // map does not exist, can't paint it*/
172 } // _TilesEnableActions()
173 
_TilesetMenuSetup()174 void Editor::_TilesetMenuSetup()
175 {
176 	// TODO: temp fix for bug 161: don't edit tilesets if a map is open
177 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
178 		_edit_tileset_action->setEnabled(false);
179 	else
180 		_edit_tileset_action->setEnabled(true);
181 } // _TilesetMenuSetup
182 
_MapMenuSetup()183 void Editor::_MapMenuSetup()
184 {
185 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
186 	{
187 		_select_music_action->setEnabled(true);
188 		_map_properties_action->setEnabled(true);
189 		_context_properties_action->setEnabled(true);
190 	} // map must exist in order to set properties
191 	else
192 	{
193 		_select_music_action->setEnabled(false);
194 		_map_properties_action->setEnabled(false);
195 		_context_properties_action->setEnabled(false);
196 	} // map does not exist, can't modify it
197 } // _MapMenuSetup()
198 
_ScriptMenuSetup()199 void Editor::_ScriptMenuSetup()
200 {
201 	_edit_skill_action->setEnabled(false);
202 } // _ScriptMenuSetup
203 
_FileNew()204 void Editor::_FileNew()
205 {
206 	if (_EraseOK())
207 	{
208 		MapPropertiesDialog* new_map = new MapPropertiesDialog(this, "new_map", false);
209 
210 		if (new_map->exec() == QDialog::Accepted)
211 		{
212 			if (_ed_scrollview != NULL)
213 				delete _ed_scrollview;
214 			_ed_scrollview = new EditorScrollView(NULL, "map", new_map->GetWidth(), new_map->GetHeight());
215 
216 			if (_ed_tabs != NULL)
217 				delete _ed_tabs;
218 			_ed_tabs = new QTabWidget();
219 			_ed_tabs->setTabPosition(QTabWidget::South);
220 
221 			_ed_splitter->addWidget(_ed_scrollview);
222 			_ed_splitter->addWidget(_ed_tabs);
223 
224 			QTreeWidget* tilesets = new_map->GetTilesetTree();
225 			int num_items     = tilesets->topLevelItemCount();
226 			int checked_items = 0;
227 			for (int i = 0; i < num_items; i++)
228 				if (tilesets->topLevelItem(i)->checkState(0) == Qt::Checked)
229 					checked_items++;
230 
231 			// Used to show the progress of tilesets that have been loaded.
232 			QProgressDialog* new_map_progress =
233 				new QProgressDialog(tr("Loading tilesets..."), NULL, 0, checked_items, this,
234 				        Qt::Widget | Qt::FramelessWindowHint | Qt::WindowTitleHint);
235 			new_map_progress->setWindowTitle(tr("Creating Map..."));
236 
237 			// Set location of and show the progress dialog
238 			new_map_progress->move(this->pos().x() + this->width()/2  - new_map_progress->width()/2,
239 			                       this->pos().y() + this->height()/2 - new_map_progress->height()/2);
240 			new_map_progress->show();
241 
242 			checked_items = 0;
243 			for (int i = 0; i < num_items; i++)
244 			{
245 				if (tilesets->topLevelItem(i)->checkState(0) == Qt::Checked)
246 				{
247 					new_map_progress->setValue(checked_items++);
248 
249 					TilesetTable* a_tileset = new TilesetTable();
250 					if (a_tileset->Load(tilesets->topLevelItem(i)->text(0)) == false)
251 						QMessageBox::critical(this, tr("HoA Level Editor"),
252 							tr("Failed to load tileset image: " +
253 							   tilesets->topLevelItem(i)->text(0)));
254 
255 					_ed_tabs->addTab(a_tileset->table, tilesets->topLevelItem(i)->text(0));
256 					_ed_scrollview->_map->tilesets.push_back(a_tileset);
257 					_ed_scrollview->_map->tileset_names.push_back(a_tileset->tileset_name);
258 				} // tileset must be checked
259 			} // iterate through all possible tilesets
260 			new_map_progress->setValue(checked_items);
261 
262 			_ed_scrollview->_map->SetInitialized(true);
263 			_ed_scrollview->resize(new_map->GetWidth() * TILE_WIDTH, new_map->GetHeight() * TILE_HEIGHT);
264 			_ed_splitter->show();
265 
266 			_grid_on = false;
267 			_textures_on = true;
268 			_ll_on   = false;
269 			_ml_on   = false;
270 			_ul_on   = false;
271 			_ol_on	 = false;
272 			_coord_type = 0;
273 			if (_select_on)
274 				_TileToggleSelect();
275 			_ViewToggleGrid();
276 			_ViewToggleLL();
277 			_ViewToggleML();
278 			_ViewToggleUL();
279 			_ViewToggleOL();
280 			_ViewCoordTile();
281 			_ViewTextures();
282 
283 			// Populate the context combobox
284 			// _context_cbox->clear() doesn't work, it seg faults.
285 			// I guess it can't have an empty combobox?
286 			int count = _context_cbox->count();
287 			_context_cbox->addItems(_ed_scrollview->_map->context_names);
288 			for (int i = 0; i < count; i++)
289 				_context_cbox->removeItem(0);
290 
291 			// Enable appropriate actions
292 			_TilesEnableActions();
293 
294 			// Set default edit mode
295 			_ed_scrollview->_layer_edit = LOWER_LAYER;
296 			_ed_scrollview->_tile_mode  = PAINT_TILE;
297 
298 			_undo_stack->setClean();
299 
300 			// Hide and delete progress bar
301 			new_map_progress->hide();
302 			delete new_map_progress;
303 
304 			statusBar()->showMessage("New map created", 5000);
305 		} // only if the user pressed OK
306 		else
307 			statusBar()->showMessage("No map created!", 5000);
308 
309 		delete new_map;
310 	} // make sure an unsaved map is not lost
311 } // _FileNew()
312 
_FileOpen()313 void Editor::_FileOpen()
314 {
315 	if (_EraseOK())
316 	{
317 		// file to open
318 		QString file_name = QFileDialog::getOpenFileName(this, "HoA Level Editor -- File Open",
319 			"dat/maps", "Maps (*.lua)");
320 
321 		if (!file_name.isEmpty())
322 		{
323 			if (_ed_scrollview != NULL)
324 				delete _ed_scrollview;
325 			_ed_scrollview = new EditorScrollView(NULL, "map", 0, 0);
326 
327 			if (_ed_tabs != NULL)
328 				delete _ed_tabs;
329 			_ed_tabs = new QTabWidget();
330 			_ed_tabs->setTabPosition(QTabWidget::South);
331 
332 			_ed_splitter->addWidget(_ed_scrollview);
333 			_ed_splitter->addWidget(_ed_tabs);
334 
335 			_ed_scrollview->_map->SetFileName(file_name);
336 			_ed_scrollview->_map->LoadMap();
337 
338 			// Count for the tileset names
339 			int num_items = _ed_scrollview->_map->tileset_names.count();
340 			int progress_steps = 0;
341 
342 			// Used to show the progress of tilesets has been loaded.
343 			QProgressDialog* new_map_progress =
344 				new QProgressDialog(tr("Loading tilesets..."), NULL, 0, num_items, this,
345 				        Qt::Widget | Qt::FramelessWindowHint | Qt::WindowTitleHint);
346 			new_map_progress->setWindowTitle(tr("Creating Map..."));
347 
348 			// Set the progress bar
349 			new_map_progress->move(this->pos().x() + this->width()/2  - new_map_progress->width()/2,
350 			                       this->pos().y() + this->height()/2 - new_map_progress->height()/2);
351 			new_map_progress->show();
352 
353 			for (QStringList::ConstIterator it = _ed_scrollview->_map->tileset_names.begin();
354 				it != _ed_scrollview->_map->tileset_names.end(); it++)
355 			{
356 				new_map_progress->setValue(progress_steps++);
357 
358 				TilesetTable* a_tileset = new TilesetTable();
359 				if (a_tileset->Load(*it) == false)
360 					QMessageBox::critical(this, tr("HoA Level Editor"),
361 						tr("Failed to load tileset image: " + *it));
362 
363 				_ed_tabs->addTab(a_tileset->table, *it);
364 				_ed_scrollview->_map->tilesets.push_back(a_tileset);
365 			} // iterate through all tilesets in the map
366 			new_map_progress->setValue(progress_steps);
367 
368 			_ed_scrollview->_map->SetInitialized(true);
369 			_ed_scrollview->resize(_ed_scrollview->_map->GetWidth(),
370 				_ed_scrollview->_map->GetHeight());
371 			_ed_splitter->show();
372 
373 			_grid_on = false;
374 			_textures_on = true;
375 			_ll_on   = false;
376 			_ml_on   = false;
377 			_ul_on   = false;
378 			_ol_on	 = false;
379 			_coord_type = 0;
380 			if (_select_on)
381 				_TileToggleSelect();
382 			_ViewToggleGrid();
383 			_ViewToggleLL();
384 			_ViewToggleML();
385 			_ViewToggleUL();
386 			_ViewToggleOL();
387 			_ViewCoordTile();
388 			_ViewTextures();
389 
390 			// Populate the context combobox
391 			// _context_cbox->clear() doesn't work, it seg faults.
392 			// I guess it can't have an empty combobox?
393 			int count = _context_cbox->count();
394 			_context_cbox->addItems(_ed_scrollview->_map->context_names);
395 			for (int i = 0; i < count; i++)
396 				_context_cbox->removeItem(0);
397 
398 			// Enable appropriate actions
399 			_TilesEnableActions();
400 
401 			// Set default edit mode
402 			_ed_scrollview->_layer_edit = LOWER_LAYER;
403 			_ed_scrollview->_tile_mode  = PAINT_TILE;
404 
405 			// Hide and delete progress bar
406 			new_map_progress->hide();
407 			delete new_map_progress;
408 
409 			_undo_stack->setClean();
410 			statusBar()->showMessage(QString("Opened \'%1\'").
411 				arg(_ed_scrollview->_map->GetFileName()), 5000);
412 		} // file must exist in order to open it
413 		else
414 			statusBar()->showMessage("No map created!", 5000);
415 	} // make sure an unsaved map is not lost
416 } // _FileOpen()
417 
_FileSaveAs()418 void Editor::_FileSaveAs()
419 {
420 	// get the file name from the user
421 	QString file_name = QFileDialog::getSaveFileName(this,
422 		"HoA Level Editor -- File Save", "dat/maps", "Maps (*.lua)");
423 
424 	if (!file_name.isEmpty())
425 	{
426 		_ed_scrollview->_map->SetFileName(file_name);
427 		_FileSave();
428 		return;
429     } // make sure the file name is not blank
430 
431 	statusBar()->showMessage("Save abandoned.", 5000);
432 } // _FileSaveAs()
433 
_FileSave()434 void Editor::_FileSave()
435 {
436 	if (_ed_scrollview->_map->GetFileName().isEmpty() ||
437 		_ed_scrollview->_map->GetFileName() == "Untitled")
438 	{
439 		_FileSaveAs();
440 		return;
441 	} // gets a file name if it is blank
442 
443 	_ed_scrollview->_map->SaveMap();      // actually saves the map
444 	_undo_stack->setClean();
445 	setCaption(QString("%1").arg(_ed_scrollview->_map->GetFileName()));
446 	statusBar()->showMessage(QString("Saved \'%1\' successfully!").
447 		arg(_ed_scrollview->_map->GetFileName()), 5000);
448 } // _FileSave()
449 
_FileClose()450 void Editor::_FileClose()
451 {
452 	// Checks to see if the map is unsaved.
453 	if (_EraseOK())
454 	{
455 		if (_ed_scrollview != NULL)
456 		{
457 			delete _ed_scrollview;
458 			_ed_scrollview = NULL;
459 			_undo_stack->clear();
460 
461 			// Clear the context combobox
462 			// _context_cbox->clear() doesn't work, it seg faults.
463 			// I guess it can't have an empty combobox?
464 			int count = _context_cbox->count();
465 			for (int i = 0; i < count; i++)
466 				_context_cbox->removeItem(0);
467 
468 			// Enable appropriate actions
469 			_TilesEnableActions();
470 		} // scrollview must exist first
471 
472 		if (_ed_tabs != NULL)
473 		{
474 			delete _ed_tabs;
475 			_ed_tabs = NULL;
476 		} // tabs must exist first
477 
478 		setCaption("Hero of Allacrost Level Editor");
479 	} // make sure an unsaved map is not lost
480 } // _FileClose()
481 
_FileQuit()482 void Editor::_FileQuit()
483 {
484 	// Checks to see if the map is unsaved.
485 	if (_EraseOK())
486 		qApp->exit(0);
487 } // _FileQuit()
488 
_ViewToggleGrid()489 void Editor::_ViewToggleGrid()
490 {
491 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
492 	{
493 		_grid_on = !_grid_on;
494 		_toggle_grid_action->setChecked(_grid_on);
495 		_ed_scrollview->_map->SetGridOn(_grid_on);
496 	} // map must exist in order to view things on it
497 } // _ViewToggleGrid()
498 
_ViewToggleLL()499 void Editor::_ViewToggleLL()
500 {
501 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
502 	{
503 		_ll_on = !_ll_on;
504 		_toggle_ll_action->setChecked(_ll_on);
505 		_ed_scrollview->_map->SetLLOn(_ll_on);
506 	} // map must exist in order to view things on it
507 } // _ViewToggleLL()
508 
_ViewToggleML()509 void Editor::_ViewToggleML()
510 {
511 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
512 	{
513 		_ml_on = !_ml_on;
514 		_toggle_ml_action->setChecked(_ml_on);
515 		_ed_scrollview->_map->SetMLOn(_ml_on);
516 	} // map must exist in order to view things on it
517 } // _ViewToggleML()
518 
_ViewToggleUL()519 void Editor::_ViewToggleUL()
520 {
521 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
522 	{
523 		_ul_on = !_ul_on;
524 		_toggle_ul_action->setChecked(_ul_on);
525 		_ed_scrollview->_map->SetULOn(_ul_on);
526 	} // map must exist in order to view things on it
527 } // _ViewToggleUL()
528 
_ViewToggleOL()529 void Editor::_ViewToggleOL()
530 {
531 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
532 	{
533 		_ol_on = !_ol_on;
534 		_toggle_ol_action->setChecked(_ol_on);
535 		_ed_scrollview->_map->SetOLOn(_ol_on);
536 	} // map must exist in order to view things on it
537 } // _ViewToggleOL()
538 
_ViewCoordTile()539 void Editor::_ViewCoordTile()
540 {
541 	_coord_type = 0;
542 	_coord_tile_action->setChecked(true);
543 	_coord_collision_action->setChecked(false);
544 }
545 
_ViewCoordCollision()546 void Editor::_ViewCoordCollision()
547 {
548 	_coord_type = 1;
549 	_coord_collision_action->setChecked(true);
550 	_coord_tile_action->setChecked(false);
551 }
552 
_ViewTextures()553 void Editor::_ViewTextures()
554 {
555 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
556 	{
557 		_textures_on = !_textures_on;
558 		if(_textures_on)
559 		{
560 			VideoManager->Textures()->DEBUG_NextTexSheet();
561 		}
562 		_ed_scrollview->_map->SetTexturesOn(_textures_on);
563 	} // map must exist in order to view things on it
564 } // _ViewTextures()
565 
_TileLayerFill()566 void Editor::_TileLayerFill()
567 {
568 	// get reference to current tileset
569 	Q3Table* table = static_cast<Q3Table*> (_ed_tabs->currentPage());
570 
571 	// put selected tile from tileset into tile array at correct position
572 	int32 tileset_index = table->currentRow() * 16 + table->currentColumn();
573 	int32 multiplier = _ed_scrollview->_map->tileset_names.findIndex(_ed_tabs->tabText(_ed_tabs->currentIndex()));
574 	if (multiplier == -1)
575 	{
576 		_ed_scrollview->_map->tileset_names.append(_ed_tabs->tabText(_ed_tabs->currentIndex()));
577 		multiplier = _ed_scrollview->_map->tileset_names.findIndex(_ed_tabs->tabText(_ed_tabs->currentIndex()));
578 	} // calculate index of current tileset
579 
580 	vector<int32>& current_layer = _ed_scrollview->GetCurrentLayer();
581 
582 	// Record the information for undo/redo operations.
583 	vector<int32> previous = current_layer;
584 	vector<int32> modified;
585 	vector<int32> indeces(current_layer.size());
586 	for (int32 i = 0; i < static_cast<int32>(current_layer.size()); i++)
587 		indeces[i] = i;
588 
589 	// Fill the layer.
590 	for (vector<int32>::iterator iter = current_layer.begin(); iter != current_layer.end(); iter++)
591 	{
592 		_ed_scrollview->_AutotileRandomize(multiplier, tileset_index);
593 		*iter = tileset_index + multiplier * 256;
594 		modified.push_back(tileset_index + multiplier * 256);
595 	} // iterate through the entire layer
596 
597 	LayerCommand* fill_command = new LayerCommand(indeces, previous, modified,
598 		_ed_scrollview->_layer_edit, _ed_scrollview->_map->GetContext(), this,
599 		"Fill Layer");
600 	_undo_stack->push(fill_command);
601 	indeces.clear();
602 	previous.clear();
603 	modified.clear();
604 
605 	// Draw the changes.
606 	_ed_scrollview->_map->SetChanged(true);
607 	_ed_scrollview->_map->updateGL();
608 } // _TileLayerFill()
609 
_TileLayerClear()610 void Editor::_TileLayerClear()
611 {
612 	vector<int32>::iterator it;    // used to iterate over an entire layer
613 	vector<int32>& current_layer = _ed_scrollview->GetCurrentLayer();
614 
615 	// Record the information for undo/redo operations.
616 	vector<int32> previous = current_layer;
617 	vector<int32> modified(current_layer.size(), -1);
618 	vector<int32> indeces(current_layer.size());
619 	for (int32 i = 0; i < static_cast<int32>(current_layer.size()); i++)
620 		indeces[i] = i;
621 
622 	// Clear the layer.
623 	for (it = current_layer.begin(); it != current_layer.end(); it++)
624 		*it = -1;
625 
626 	LayerCommand* clear_command = new LayerCommand(indeces, previous, modified,
627 		_ed_scrollview->_layer_edit, _ed_scrollview->_map->GetContext(), this,
628 		"Clear Layer");
629 	_undo_stack->push(clear_command);
630 	indeces.clear();
631 	previous.clear();
632 	modified.clear();
633 
634 	// Draw the changes.
635 	_ed_scrollview->_map->SetChanged(true);
636 	_ed_scrollview->_map->updateGL();
637 } // _TileLayerClear()
638 
_TileToggleSelect()639 void Editor::_TileToggleSelect()
640 {
641 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
642 	{
643 		_select_on = !_select_on;
644 		_toggle_select_action->setChecked(_select_on);
645 		_ed_scrollview->_map->SetSelectOn(_select_on);
646 	} // map must exist in order to view things on it
647 } // _TileToggleSelect()
648 
_TileModePaint()649 void Editor::_TileModePaint()
650 {
651 	if (_ed_scrollview != NULL)
652 	{
653 		// Clear the selection layer.
654 		if (_ed_scrollview->_moving == true && _select_on == true)
655 		{
656 			vector<int32>::iterator it;    // used to iterate over an entire layer
657 			vector<int32>& select_layer = _ed_scrollview->_map->GetLayer(SELECT_LAYER, 0);
658 			for (it = select_layer.begin(); it != select_layer.end(); it++)
659 				*it = -1;
660 		} // clears when selected tiles were going to be moved but
661 		  // user changed their mind in the midst of the move operation
662 
663 		_ed_scrollview->_tile_mode = PAINT_TILE;
664 		_ed_scrollview->_moving = false;
665 	} // scrollview must exist in order to switch modes
666 } // _TileModePaint()
667 
_TileModeMove()668 void Editor::_TileModeMove()
669 {
670 	if (_ed_scrollview != NULL)
671 	{
672 		// Clear the selection layer.
673 		if (_ed_scrollview->_moving == true && _select_on == true)
674 		{
675 			vector<int32>::iterator it;    // used to iterate over an entire layer
676 			vector<int32>& select_layer = _ed_scrollview->_map->GetLayer(SELECT_LAYER, 0);
677 			for (it = select_layer.begin(); it != select_layer.end(); it++)
678 				*it = -1;
679 		} // clears when selected tiles were going to be moved but
680 		  // user changed their mind in the midst of the move operation
681 
682 		_ed_scrollview->_tile_mode = MOVE_TILE;
683 		_ed_scrollview->_moving = false;
684 	} // scrollview must exist in order to switch modes
685 } // _TileModeMove()
686 
_TileModeDelete()687 void Editor::_TileModeDelete()
688 {
689 	if (_ed_scrollview != NULL)
690 	{
691 		// Clear the selection layer.
692 		if (_ed_scrollview->_moving == true && _select_on == true)
693 		{
694 			vector<int32>::iterator it;    // used to iterate over an entire layer
695 			vector<int32>& select_layer = _ed_scrollview->_map->GetLayer(SELECT_LAYER, 0);
696 			for (it = select_layer.begin(); it != select_layer.end(); it++)
697 				*it = -1;
698 		} // clears when selected tiles were going to be moved but
699 		  // user changed their mind in the midst of the move operation
700 
701 		_ed_scrollview->_tile_mode = DELETE_TILE;
702 		_ed_scrollview->_moving = false;
703 	} // scrollview must exist in order to switch modes
704 } // _TileModeDelete()
705 
_TileEditLL()706 void Editor::_TileEditLL()
707 {
708 	if (_ed_scrollview != NULL)
709 		_ed_scrollview->_layer_edit = LOWER_LAYER;
710 } // _TileEditLL()
711 
_TileEditML()712 void Editor::_TileEditML()
713 {
714 	if (_ed_scrollview != NULL)
715 		_ed_scrollview->_layer_edit = MIDDLE_LAYER;
716 } // _TileEditML()
717 
_TileEditUL()718 void Editor::_TileEditUL()
719 {
720 	if (_ed_scrollview != NULL)
721 		_ed_scrollview->_layer_edit = UPPER_LAYER;
722 } // _TileEditUL()
723 
_TileEditOL()724 void Editor::_TileEditOL()
725 {
726 	if (_ed_scrollview != NULL)
727 		_ed_scrollview->_layer_edit = OBJECT_LAYER;
728 } // _TileEditOL()
729 
730 
_TilesetEdit()731 void Editor::_TilesetEdit()
732 {
733 	TilesetEditor* tileset_editor = new TilesetEditor(this, "tileset_editor", true);
734 
735 	if (tileset_editor->exec() == QDialog::Accepted)
736 	{
737 	} // only process results if user selected okay
738 	else
739 		statusBar()->showMessage("Properties not modified!", 5000);
740 
741 	delete tileset_editor;
742 } // _TilesetEdit
743 
_MapSelectMusic()744 void Editor::_MapSelectMusic()
745 {
746 	if (_ed_scrollview == NULL)
747 		return;
748 
749 	MusicDialog* music = new MusicDialog(this, "music_dialog");
750 
751 	if (music->exec() == QDialog::Accepted)
752 	{
753 		// Turn the items in the listwidget into a stringlist that the map
754 		// will understand
755 		QStringList  music_names;
756 		QListWidget* music_list = music->GetMusicList();
757 		for (unsigned int i = 0; i < music_list->count(); i++)
758 			music_names << music_list->item(i)->text();
759 
760 		_ed_scrollview->_map->music_files = music_names;
761 		_ed_scrollview->_map->SetChanged(true);
762 	} // only process results if user selected okay
763 
764 	delete music;
765 } // _MapSelectMusic()
766 
_MapProperties()767 void Editor::_MapProperties()
768 {
769 	MapPropertiesDialog* props = new
770 		MapPropertiesDialog(this, "map_properties", true);
771 
772 	if (props->exec() == QDialog::Accepted)
773 	{
774 #if !defined(WIN32)
775 		if (_ed_scrollview->_map->GetWidth() < props->GetWidth())
776 		{
777 			// User wants to make map wider so we must insert columns of tiles at the edge of the map.
778 
779 			int map_width     = _ed_scrollview->_map->GetWidth();
780 			int map_height    = _ed_scrollview->_map->GetHeight();
781 			int extra_columns = props->GetWidth() - map_width;
782 
783 			// Add in the extra columns one by one.
784 			for (int col = extra_columns; col > 0; col--)
785 			{
786 				vector<int32>& lower_layer = _ed_scrollview->_map->GetLayer(LOWER_LAYER, _ed_scrollview->_map->GetContext());
787 				vector<int32>::iterator it = lower_layer.begin() + map_width;
788 				for (int row = 0; row < map_height; row++)
789 				{
790 					lower_layer.insert(it, -1);
791 					it += map_width + 1;
792 				} // iterate through the rows of the lower layer
793 
794 				vector<int32>& middle_layer = _ed_scrollview->_map->GetLayer(MIDDLE_LAYER, _ed_scrollview->_map->GetContext());
795 				it = middle_layer.begin() + map_width;
796 				for (int row = 0; row < map_height; row++)
797 				{
798 					middle_layer.insert(it, -1);
799 					it += map_width + 1;
800 				} // iterate through the rows of the middle layer
801 
802 				vector<int32>& upper_layer = _ed_scrollview->_map->GetLayer(UPPER_LAYER, _ed_scrollview->_map->GetContext());
803 				it = upper_layer.begin() + map_width;
804 				for (int row = 0; row < map_height; row++)
805 				{
806 					upper_layer.insert(it, -1);
807 					it += map_width + 1;
808 				} // iterate through the rows of the upper layer
809 
810 				map_width++;
811 				_ed_scrollview->_map->SetWidth(map_width);
812 			} // add in all the extra columns
813 		} // insert columns
814 		else if (_ed_scrollview->_map->GetWidth() > props->GetWidth())
815 		{
816 			// User wants to make map less wide so we must delete columns of tiles from the edge of the map.
817 
818 			int map_width     = _ed_scrollview->_map->GetWidth();
819 			int map_height    = _ed_scrollview->_map->GetHeight();
820 			int extra_columns = map_width - props->GetWidth();
821 
822 			// Delete all the extra columns one by one.
823 			for (int col = extra_columns; col > 0; col--)
824 			{
825 				vector<int32>& lower_layer = _ed_scrollview->_map->GetLayer(LOWER_LAYER, _ed_scrollview->_map->GetContext());
826 				vector<int32>::iterator it = lower_layer.begin() + map_width - 1;
827 				for (int row = 0; row < map_height; row++)
828 				{
829 					lower_layer.erase(it);
830 					it += map_width - 1;
831 				} // iterate through the rows of the lower layer
832 
833 				vector<int32>& middle_layer = _ed_scrollview->_map->GetLayer(MIDDLE_LAYER, _ed_scrollview->_map->GetContext());
834 				it = middle_layer.begin() + map_width - 1;
835 				for (int row = 0; row < map_height; row++)
836 				{
837 					middle_layer.erase(it);
838 					it += map_width - 1;
839 				} // iterate through the rows of the middle layer
840 
841 				vector<int32>& upper_layer = _ed_scrollview->_map->GetLayer(UPPER_LAYER, _ed_scrollview->_map->GetContext());
842 				it = upper_layer.begin() + map_width - 1;
843 				for (int row = 0; row < map_height; row++)
844 				{
845 					upper_layer.erase(it);
846 					it += map_width - 1;
847 				} // iterate through the rows of the upper layer
848 
849 				map_width--;
850 				_ed_scrollview->_map->SetWidth(map_width);
851 			} // delete all the extra columns
852 		} // delete columns
853 
854 		if (_ed_scrollview->_map->GetHeight() < props->GetHeight())
855 		{
856 			// User wants to make map taller so we must insert rows of tiles at the edge of the map.
857 
858 			int map_width = _ed_scrollview->_map->GetWidth();
859 			int extra_rows = props->GetHeight() - _ed_scrollview->_map->GetHeight();
860 
861 			vector<int32>& lower_layer  = _ed_scrollview->_map->GetLayer(LOWER_LAYER, _ed_scrollview->_map->GetContext());
862 			vector<int32>& middle_layer = _ed_scrollview->_map->GetLayer(MIDDLE_LAYER, _ed_scrollview->_map->GetContext());
863 			vector<int32>& upper_layer  = _ed_scrollview->_map->GetLayer(UPPER_LAYER, _ed_scrollview->_map->GetContext());
864 			lower_layer.insert( lower_layer.end(),  extra_rows * map_width, -1);
865 			middle_layer.insert(middle_layer.end(), extra_rows * map_width, -1);
866 			upper_layer.insert( upper_layer.end(),  extra_rows * map_width, -1);
867 		} // add rows
868 		else if (_ed_scrollview->_map->GetHeight() > props->GetHeight())
869 		{
870 			// User wants to make map less tall so we must delete rows of tiles from the edge of the map.
871 
872 			int map_width  = _ed_scrollview->_map->GetWidth();
873 			int extra_rows = _ed_scrollview->_map->GetHeight() - props->GetHeight();
874 
875 			vector<int32>& lower_layer  = _ed_scrollview->_map->GetLayer(LOWER_LAYER, _ed_scrollview->_map->GetContext());
876 			vector<int32>& middle_layer = _ed_scrollview->_map->GetLayer(MIDDLE_LAYER, _ed_scrollview->_map->GetContext());
877 			vector<int32>& upper_layer  = _ed_scrollview->_map->GetLayer(UPPER_LAYER, _ed_scrollview->_map->GetContext());
878 			lower_layer.erase( lower_layer.end()  - extra_rows * map_width, lower_layer.end());
879 			middle_layer.erase(middle_layer.end() - extra_rows * map_width, middle_layer.end());
880 			upper_layer.erase( upper_layer.end()  - extra_rows * map_width, upper_layer.end());
881 		} // delete rows
882 
883 		// Resize the map, QOpenGL and QScrollView widgets.
884 		_ed_scrollview->_map->SetHeight(props->GetHeight());
885 		_ed_scrollview->_map->resize(props->GetWidth() * TILE_WIDTH, props->GetHeight() * TILE_HEIGHT);
886 		_ed_scrollview->resize(props->GetWidth() * TILE_WIDTH, props->GetHeight() * TILE_HEIGHT);
887 #endif
888 
889 
890 
891 		// User has the ability to add or remove tilesets being used. We don't want
892 		// to reload tilesets that have already been loaded before.
893 
894 		QTreeWidget* tilesets = props->GetTilesetTree();
895 
896 		// Put the names of the tabs into a nice list that can be easily searched
897 		// with one command instead of a loop.
898 		QStringList tab_names;
899 		for (int i = 0; i < _ed_tabs->count(); i++)
900 			tab_names << _ed_tabs->tabText(i);
901 
902 		// Go through the list of tilesets, adding selected tilesets and removing
903 		// any unwanted tilesets.
904 		int num_items = tilesets->topLevelItemCount();
905 		for (int i = 0; i < num_items; i++)
906 		{
907 			if (tilesets->topLevelItem(i)->checkState(0) == Qt::Checked)
908 			{
909 				if (tab_names.contains(tilesets->topLevelItem(i)->text(0)) == false)
910 				{
911 					TilesetTable* a_tileset = new TilesetTable();
912 					a_tileset->Load(tilesets->topLevelItem(i)->text(0));
913 					_ed_tabs->addTab(a_tileset->table, tilesets->topLevelItem(i)->text(0));
914 					_ed_scrollview->_map->tilesets.push_back(a_tileset);
915 				} // only add a tileset if it isn't already loaded
916 			} // tileset must be checked in order to add it
917 			else if (tilesets->topLevelItem(i)->checkState(0) == Qt::Unchecked &&
918 			         tab_names.contains(tilesets->topLevelItem(i)->text(0)))
919 				_ed_tabs->removeTab(tab_names.indexOf(tilesets->topLevelItem(i)->text(0)));
920 				// FIXME:
921 				// Where to add and remove tileset name from the tilesets list
922 				// in the _map? Do it here or when actually painting and deleting
923 				// tiles? Here the assumption is made that if the user is adding a
924 				// tileset, then s/he expects to use tiles from that tileset and we
925 				// can safely add the tileset name to the _map. Otherwise we would
926 				// have to constantly check every time a paint operation occurs
927 				// whether or not the tileset name of the selected tile was present
928 				// in the tileset name list in _map. That's cumbersome.
929 				//
930 				// When removing a tileset however, there might still be tiles in
931 				// the map from that tileset, and the user is only removing the
932 				// tileset from the view in the bottom of the map to unclutter
933 				// things. In this case we wouldn't want to remove the tileset name
934 				// from the list in _map.
935 		} // iterate through all possible tilesets
936 
937 		_ed_splitter->addWidget(_ed_tabs);
938 	} // only if the user pressed OK
939 	else
940 		statusBar()->showMessage("Properties not modified!", 5000);
941 
942 	delete props;
943 } // _MapProperties()
944 
_MapAddContext()945 void Editor::_MapAddContext()
946 {
947 	if (_ed_scrollview->_map->context_names.size() >= MAX_CONTEXTS)
948 	{
949 		_error_max_contexts->move(this->pos().x() + this->width()/2  - _error_max_contexts->width()/2,
950 		                          this->pos().y() + this->height()/2 - _error_max_contexts->height()/2);
951 		_error_max_contexts->showMessage(
952 			QString("Maximum number of contexts (%1) reached. No new context will be created.").
953 				arg(MAX_CONTEXTS));
954 		statusBar()->showMessage("Maximum number of contexts reached. No new context created!", 5000);
955 		return;
956 	} // don't want more than the max allowable contexts
957 
958 	ContextPropertiesDialog* props = new
959 		ContextPropertiesDialog(this, "context_properties");
960 
961 	if (props->exec() == QDialog::Accepted)
962 	{
963 		_ed_scrollview->_map->context_names << props->GetName();
964 		QStringList context_names = _ed_scrollview->_map->context_names;
965 
966 		// Gets the index of the context to inherit from. Default is the
967 		// base context, which is index 0. If no context is selected in the
968 		// dialog, use the default, since a new context cannot be created without
969 		// inheriting from another.
970 		int inherit_context;
971 		if (props->GetContextTree()->currentItem() == NULL)
972 			inherit_context = 0;
973 		else
974 			inherit_context = context_names.indexOf(
975 				props->GetContextTree()->currentItem()->text(0));
976 
977 		// Perform the copy from one context to another.
978 		_ed_scrollview->_map->CreateNewContext(inherit_context);
979 
980 		// Add new context to context combobox.
981 		_context_cbox->addItem(props->GetName());
982 
983 		// Switch to newly created context.
984 		_context_cbox->setCurrentIndex(context_names.size() - 1);
985 		_ed_scrollview->_map->SetContext(context_names.size() - 1);
986 	} // only if the user pressed OK
987 	else
988 		statusBar()->showMessage("No new context created!", 5000);
989 
990 	delete props;
991 } // _MapAddContext()
992 
_ScriptEditSkills()993 void Editor::_ScriptEditSkills()
994 {
995 	if (_skill_editor == NULL)
996 	{
997 		// create the skill editor window
998 		_skill_editor = new SkillEditor(NULL, "skill_editor");
999 		_skill_editor->resize(600,400);
1000 	}
1001 	_skill_editor->show();
1002 	//SkillEditor *skill_editor = new SkillEditor(this, "skill_editor");
1003 	//skill_editor->exec();
1004 	//delete skill_editor;
1005 } // _ScriptEditSkills()
1006 
_HelpHelp()1007 void Editor::_HelpHelp()
1008 {
1009 	statusBar()->showMessage(tr("See http://allacrost.sourceforge.net/wiki/index.php/Code_Documentation#Map_Editor_Documentation for more details"), 10000);
1010 } // _HelpHelp()
1011 
_HelpAbout()1012 void Editor::_HelpAbout()
1013 {
1014     QMessageBox::about(this, "HoA Level Editor -- About",
1015 		"<center><h1><font color=blue>Hero of Allacrost Level Editor<font>"
1016 		"</h1></center>"
1017 		"<center><h2><font color=blue>Copyright (c) 2004-2010<font></h2></center>"
1018 		"<p>A level editor created for the Hero of Allacrost project."
1019 		" See 'http://www.allacrost.org/' for more details</p>");
1020 } // _HelpAbout()
1021 
_HelpAboutQt()1022 void Editor::_HelpAboutQt()
1023 {
1024     QMessageBox::aboutQt(this, "HoA Level Editor -- About Qt");
1025 } // _HelpAboutQt()
1026 
_SwitchMapContext(int context)1027 void Editor::_SwitchMapContext(int context)
1028 {
1029 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
1030 	{
1031 		_ed_scrollview->_map->SetContext(context);
1032 		_ed_scrollview->_map->updateGL();
1033 	} // map must exist in order to change the context
1034 } // _SwitchMapContext()
1035 
1036 
1037 
1038 // ********** Private functions **********
1039 
_CreateActions()1040 void Editor::_CreateActions()
1041 {
1042 	// Create menu actions related to the File menu
1043 
1044 	_new_action = new QAction("&New...", this);
1045 	_new_action->setShortcut(tr("Ctrl+N"));
1046 	_new_action->setStatusTip("Create a new map");
1047 	connect(_new_action, SIGNAL(triggered()), this, SLOT(_FileNew()));
1048 
1049 	_open_action = new QAction("&Open...", this);
1050 	_open_action->setShortcut(tr("Ctrl+O"));
1051 	_open_action->setStatusTip("Open an existing map");
1052 	connect(_open_action, SIGNAL(triggered()), this, SLOT(_FileOpen()));
1053 
1054 	_save_as_action = new QAction("Save &As...", this);
1055 	_save_as_action->setStatusTip("Save the map with another name");
1056 	connect(_save_as_action, SIGNAL(triggered()), this, SLOT(_FileSaveAs()));
1057 
1058 	_save_action = new QAction("&Save", this);
1059 	_save_action->setShortcut(tr("Ctrl+S"));
1060 	_save_action->setStatusTip("Save the map");
1061 	connect(_save_action, SIGNAL(triggered()), this, SLOT(_FileSave()));
1062 
1063 	_close_action = new QAction("&Close", this);
1064 	_close_action->setShortcut(tr("Ctrl+W"));
1065 	_close_action->setStatusTip("Close the map");
1066 	connect(_close_action, SIGNAL(triggered()), this, SLOT(_FileClose()));
1067 
1068 	_quit_action = new QAction("&Quit", this);
1069 	_quit_action->setShortcut(tr("Ctrl+Q"));
1070 	_quit_action->setStatusTip("Quits from the editor");
1071 	connect(_quit_action, SIGNAL(triggered()), this, SLOT(_FileQuit()));
1072 
1073 
1074 
1075 	// Create menu actions related to the View menu
1076 
1077 	_toggle_grid_action = new QAction("&Grid", this);
1078 	_toggle_grid_action->setStatusTip("Switches the grid on and off");
1079 	_toggle_grid_action->setShortcut(tr("G"));
1080 	_toggle_grid_action->setCheckable(true);
1081 	connect(_toggle_grid_action, SIGNAL(triggered()), this, SLOT(_ViewToggleGrid()));
1082 
1083 	_toggle_ll_action = new QAction("&Lower Layer", this);
1084 	_toggle_ll_action->setStatusTip("Switches the lower layer on and off");
1085 	_toggle_ll_action->setShortcut(tr("L"));
1086 	_toggle_ll_action->setCheckable(true);
1087 	connect(_toggle_ll_action, SIGNAL(triggered()), this, SLOT(_ViewToggleLL()));
1088 
1089 	_toggle_ml_action = new QAction("&Middle Layer", this);
1090 	_toggle_ml_action->setStatusTip("Switches the middle layer on and off");
1091 	_toggle_ml_action->setShortcut(tr("M"));
1092 	_toggle_ml_action->setCheckable(true);
1093 	connect(_toggle_ml_action, SIGNAL(triggered()), this, SLOT(_ViewToggleML()));
1094 
1095 	_toggle_ul_action = new QAction("&Upper Layer", this);
1096 	_toggle_ul_action->setStatusTip("Switches the upper layer on and off");
1097 	_toggle_ul_action->setShortcut(tr("U"));
1098 	_toggle_ul_action->setCheckable(true);
1099 	connect(_toggle_ul_action, SIGNAL(triggered()), this, SLOT(_ViewToggleUL()));
1100 
1101 	_toggle_ol_action = new QAction("&Object Layer", this);
1102 	_toggle_ol_action->setStatusTip("Switches the object layer on and off");
1103 	_toggle_ol_action->setShortcut(tr("O"));
1104 	_toggle_ol_action->setCheckable(true);
1105 	connect(_toggle_ol_action, SIGNAL(triggered()), this, SLOT(_ViewToggleOL()));
1106 
1107 	_coord_tile_action = new QAction("Tile Coordinates", this);
1108 	_coord_tile_action->setStatusTip("Switch the coordinate display to tile coordinates");
1109 	_coord_tile_action->setCheckable(true);
1110 	connect(_coord_tile_action, SIGNAL(triggered()), this, SLOT(_ViewCoordTile()));
1111 
1112 	_coord_collision_action = new QAction("Collision Coordinates", this);
1113 	_coord_collision_action->setStatusTip("Switch the coordinate display to collision coordinates");
1114 	_coord_collision_action->setCheckable(true);
1115 	connect(_coord_collision_action, SIGNAL(triggered()), this, SLOT(_ViewCoordCollision()));
1116 
1117 	_view_textures_action = new QAction("&Texture sheets", this);
1118 	_view_textures_action->setShortcut(tr("Ctrl+T"));
1119 	_view_textures_action->setStatusTip("Cycles through the video engine's texture sheets");
1120 	connect(_view_textures_action, SIGNAL(triggered()), this, SLOT(_ViewTextures()));
1121 
1122 
1123 
1124 	// Create menu actions related to the Tiles menu
1125 
1126 	_undo_action = new QAction(
1127 		QIcon("img/misc/editor-tools/arrow-left.png"),
1128 		"&Undo", this);
1129 	_undo_action->setShortcut(tr("Ctrl+Z"));
1130 	_undo_action->setStatusTip("Undoes the previous command");
1131 	connect(_undo_action, SIGNAL(triggered()), _undo_stack, SLOT(undo()));
1132 
1133 	_redo_action = new QAction(
1134 		QIcon("img/misc/editor-tools/arrow-right.png"),
1135 		"&Redo", this);
1136 	_redo_action->setShortcut(tr("Ctrl+Y"));
1137 	_redo_action->setStatusTip("Redoes the next command");
1138 	connect(_redo_action, SIGNAL(triggered()), _undo_stack, SLOT(redo()));
1139 
1140 	_layer_fill_action = new QAction(
1141 		QIcon("img/misc/editor-tools/stock-tool-bucket-fill-22.png"),
1142 		"&Fill layer", this);
1143 	_layer_fill_action->setStatusTip("Fills current layer with selected tile");
1144 	connect(_layer_fill_action, SIGNAL(triggered()), this, SLOT(_TileLayerFill()));
1145 
1146 	_layer_clear_action = new QAction("&Clear layer", this);
1147 	_layer_clear_action->setStatusTip("Clears current layer from any tiles");
1148 	connect(_layer_clear_action, SIGNAL(triggered()), this, SLOT(_TileLayerClear()));
1149 
1150 	_toggle_select_action = new QAction(
1151 		QIcon("img/misc/editor-tools/stock-tool-rect-select-22.png"),
1152 		"Marquee &Select", this);
1153 	_toggle_select_action->setShortcut(tr("Shift+S"));
1154 	_toggle_select_action->setStatusTip("Rectangularly select tiles on the map");
1155 	_toggle_select_action->setCheckable(true);
1156 	connect(_toggle_select_action, SIGNAL(triggered()), this, SLOT(_TileToggleSelect()));
1157 
1158 	_mode_paint_action = new QAction(
1159 		QIcon("img/misc/editor-tools/stock-tool-pencil-22.png"),
1160 		"&Paint mode", this);
1161 	_mode_paint_action->setShortcut(tr("Shift+P"));
1162 	_mode_paint_action->setStatusTip("Switches to paint mode to draw tiles on the map");
1163 	_mode_paint_action->setCheckable(true);
1164 	connect(_mode_paint_action, SIGNAL(triggered()), this, SLOT(_TileModePaint()));
1165 
1166 	_mode_move_action = new QAction(
1167 		QIcon("img/misc/editor-tools/stock-tool-arrow.png"),
1168 		"Mo&ve mode", this);
1169 	_mode_move_action->setShortcut(tr("Shift+V"));
1170 	_mode_move_action->setStatusTip("Switches to move mode to move tiles around on the map");
1171 	_mode_move_action->setCheckable(true);
1172 	connect(_mode_move_action, SIGNAL(triggered()), this, SLOT(_TileModeMove()));
1173 
1174 	_mode_delete_action = new QAction(
1175 		QIcon("img/misc/editor-tools/stock-tool-eraser-22.png"),
1176 		"&Delete mode", this);
1177 	_mode_delete_action->setShortcut(tr("Shift+D"));
1178 	_mode_delete_action->setStatusTip("Switches to delete mode to erase tiles from the map");
1179 	_mode_delete_action->setCheckable(true);
1180 	connect(_mode_delete_action, SIGNAL(triggered()), this, SLOT(_TileModeDelete()));
1181 
1182 	_mode_group = new QActionGroup(this);
1183 	_mode_group->addAction(_mode_paint_action);
1184 	_mode_group->addAction(_mode_move_action);
1185 	_mode_group->addAction(_mode_delete_action);
1186 	_mode_paint_action->setChecked(true);
1187 
1188 	_edit_ll_action = new QAction("Edit &lower layer", this);
1189 	_edit_ll_action->setShortcut(tr("Shift+L"));
1190 	_edit_ll_action->setStatusTip("Makes lower layer of the map current");
1191 	_edit_ll_action->setCheckable(true);
1192 	connect(_edit_ll_action, SIGNAL(triggered()), this, SLOT(_TileEditLL()));
1193 
1194 	_edit_ml_action = new QAction("Edit &middle layer", this);
1195 	_edit_ml_action->setShortcut(tr("Shift+M"));
1196 	_edit_ml_action->setStatusTip("Makes middle layer of the map current");
1197 	_edit_ml_action->setCheckable(true);
1198 	connect(_edit_ml_action, SIGNAL(triggered()), this, SLOT(_TileEditML()));
1199 
1200 	_edit_ul_action = new QAction("Edit &upper layer", this);
1201 	_edit_ul_action->setShortcut(tr("Shift+U"));
1202 	_edit_ul_action->setStatusTip("Makes upper layer of the map current");
1203 	_edit_ul_action->setCheckable(true);
1204 	connect(_edit_ul_action, SIGNAL(triggered()), this, SLOT(_TileEditUL()));
1205 
1206 	_edit_ol_action = new QAction("Edit &object layer", this);
1207 	_edit_ol_action->setShortcut(tr("Shift+O"));
1208 	_edit_ol_action->setStatusTip("Makes object layer of the map current");
1209 	_edit_ol_action->setCheckable(true);
1210 	connect(_edit_ol_action, SIGNAL(triggered()), this, SLOT(_TileEditOL()));
1211 
1212 	_edit_group = new QActionGroup(this);
1213 	_edit_group->addAction(_edit_ll_action);
1214 	_edit_group->addAction(_edit_ml_action);
1215 	_edit_group->addAction(_edit_ul_action);
1216 	_edit_group->addAction(_edit_ol_action);
1217 	_edit_ll_action->setChecked(true);
1218 
1219 
1220 
1221 	// Create tileset actions related to the Tileset Menu
1222 
1223 	_edit_tileset_action = new QAction("Edit &Tileset", this);
1224 	_edit_tileset_action->setStatusTip("Lets the user paint walkability on the tileset");
1225 	//_edit_walkability_action->setCheckable(true);
1226 	connect(_edit_tileset_action, SIGNAL(triggered()), this, SLOT(_TilesetEdit()));
1227 
1228 
1229 
1230 	// Create menu actions related to the Map menu
1231 
1232 	_select_music_action = new QAction("&Select map music...", this);
1233 	_select_music_action->setStatusTip("Choose background music for the map");
1234 	connect(_select_music_action, SIGNAL(triggered()), this, SLOT(_MapSelectMusic()));
1235 
1236 	_context_properties_action = new QAction("&Add Context...", this);
1237 	_context_properties_action->setStatusTip("Create a new context on the map");
1238 	connect(_context_properties_action, SIGNAL(triggered()), this, SLOT(_MapAddContext()));
1239 
1240 	_map_properties_action = new QAction("&Properties...", this);
1241 	_map_properties_action->setStatusTip("Modify the properties of the map");
1242 	connect(_map_properties_action, SIGNAL(triggered()), this, SLOT(_MapProperties()));
1243 
1244 
1245 
1246 	// Create menu actions related to the Script menu
1247 	_edit_skill_action = new QAction("Edit S&kills", this);
1248 	_edit_skill_action->setStatusTip("Add/Edit skills");
1249 	connect(_edit_skill_action, SIGNAL(triggered()), this, SLOT(_ScriptEditSkills()));
1250 
1251 
1252 
1253 	// Create menu actions related to the Help menu
1254 
1255 	_help_action = new QAction("&Help", this);
1256 	_help_action->setShortcut(Qt::Key_F1);
1257 	_help_action->setStatusTip("Brings up help documentation for the editor");
1258 	connect(_help_action, SIGNAL(triggered()), this, SLOT(_HelpHelp()));
1259 
1260 	_about_action = new QAction("&About", this);
1261 	_about_action->setStatusTip("Brings up information about the editor");
1262 	connect(_about_action, SIGNAL(triggered()), this, SLOT(_HelpAbout()));
1263 
1264 	_about_qt_action = new QAction("About &Qt", this);
1265 	_about_qt_action->setStatusTip("Brings up information about Qt");
1266 	connect(_about_qt_action, SIGNAL(triggered()), this, SLOT(_HelpAboutQt()));
1267 } // _CreateActions()
1268 
_CreateMenus()1269 void Editor::_CreateMenus()
1270 {
1271 	// file menu creation
1272 	_file_menu = menuBar()->addMenu("&File");
1273 	_file_menu->addAction(_new_action);
1274 	_file_menu->addAction(_open_action);
1275 	_file_menu->addSeparator();
1276 	_file_menu->addAction(_save_action);
1277 	_file_menu->addAction(_save_as_action);
1278 	_file_menu->addSeparator();
1279 	_file_menu->addAction(_close_action);
1280 	_file_menu->addAction(_quit_action);
1281 	connect(_file_menu, SIGNAL(aboutToShow()), this, SLOT(_FileMenuSetup()));
1282 
1283 	// view menu creation
1284 	_view_menu = menuBar()->addMenu("&View");
1285 	_view_menu->addAction(_toggle_grid_action);
1286 	_view_menu->addSeparator();
1287 	_view_menu->addAction(_toggle_ll_action);
1288 	_view_menu->addAction(_toggle_ml_action);
1289 	_view_menu->addAction(_toggle_ul_action);
1290 	_view_menu->addAction(_toggle_ol_action);
1291 	_view_menu->addSeparator();
1292 	_view_menu->addAction(_coord_tile_action);
1293 	_view_menu->addAction(_coord_collision_action);
1294 	_view_menu->addSeparator();
1295 	_view_menu->addAction(_view_textures_action);
1296 	_view_menu->setTearOffEnabled(true);
1297 	connect(_view_menu, SIGNAL(aboutToShow()), this, SLOT(_ViewMenuSetup()));
1298 
1299 	// tile menu creation
1300 	_tiles_menu = menuBar()->addMenu("&Tiles");
1301 	_tiles_menu->addAction(_undo_action);
1302 	_tiles_menu->addAction(_redo_action);
1303 	_tiles_menu->addSeparator();
1304 	_tiles_menu->addAction(_layer_fill_action);
1305 	_tiles_menu->addAction(_layer_clear_action);
1306 	_tiles_menu->addSeparator();
1307 	_tiles_menu->addAction(_toggle_select_action);
1308 	_tiles_menu->addSeparator()->setText("Editing Mode");
1309 	_tiles_menu->addAction(_mode_paint_action);
1310 	_tiles_menu->addAction(_mode_move_action);
1311 	_tiles_menu->addAction(_mode_delete_action);
1312 	_tiles_menu->addSeparator()->setText("Current Layer");
1313 	_tiles_menu->addAction(_edit_ll_action);
1314 	_tiles_menu->addAction(_edit_ml_action);
1315 	_tiles_menu->addAction(_edit_ul_action);
1316 	_tiles_menu->addAction(_edit_ol_action);
1317 
1318 	_tiles_menu->setTearOffEnabled(true);
1319 	connect(_tiles_menu, SIGNAL(aboutToShow()), this, SLOT(_TilesEnableActions()));
1320 
1321 	// tileset menu creation
1322 	_tileset_menu = menuBar()->addMenu("Tile&set");
1323 	_tileset_menu->addAction(_edit_tileset_action);
1324 	connect(_tileset_menu, SIGNAL(aboutToShow()), this, SLOT(_TilesetMenuSetup()));
1325 
1326 	// map menu creation
1327 	_map_menu = menuBar()->addMenu("&Map");
1328 	_map_menu->addAction(_select_music_action);
1329 	_map_menu->addAction(_context_properties_action);
1330 	_map_menu->addSeparator();
1331 	_map_menu->addAction(_map_properties_action);
1332 	connect(_map_menu, SIGNAL(aboutToShow()), this, SLOT(_MapMenuSetup()));
1333 
1334 	// script menu creation
1335 	_script_menu = menuBar()->addMenu("&Script");
1336 	_script_menu->addAction(_edit_skill_action);
1337 	connect(_script_menu, SIGNAL(aboutToShow()), this, SLOT(_ScriptMenuSetup()));
1338 
1339 	// help menu creation
1340 	_help_menu = menuBar()->addMenu("&Help");
1341 	_help_menu->addAction(_help_action);
1342 	_help_menu->addAction(_about_action);
1343 	_help_menu->addAction(_about_qt_action);
1344 } // _CreateMenus()
1345 
_CreateToolbars()1346 void Editor::_CreateToolbars()
1347 {
1348 	_tiles_toolbar = addToolBar("Tiles");
1349 	_tiles_toolbar->addAction(_layer_fill_action);
1350 	_tiles_toolbar->addSeparator();
1351 	_tiles_toolbar->addAction(_mode_paint_action);
1352 	_tiles_toolbar->addAction(_mode_move_action);
1353 	_tiles_toolbar->addAction(_mode_delete_action);
1354 	_tiles_toolbar->addSeparator();
1355 	_tiles_toolbar->addAction(_undo_action);
1356 	_tiles_toolbar->addAction(_redo_action);
1357 	_tiles_toolbar->addSeparator();
1358 	_tiles_toolbar->addAction(_toggle_select_action);
1359 	_tiles_toolbar->addSeparator();
1360 
1361 	QLabel* context_label = new QLabel("Context:", this);
1362 	_tiles_toolbar->addWidget(context_label);
1363 	_context_cbox = new QComboBox(this);
1364 	_context_cbox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
1365 	_context_cbox->addItem("Base");
1366 	_tiles_toolbar->addWidget(_context_cbox);
1367 	connect(_context_cbox, SIGNAL(currentIndexChanged(int)), this,
1368 		SLOT(_SwitchMapContext(int)));
1369 } // _CreateToolbars()
1370 
_EraseOK()1371 bool Editor::_EraseOK()
1372 {
1373 	if (_ed_scrollview != NULL && _ed_scrollview->_map != NULL)
1374 	{
1375 	    if (_ed_scrollview->_map->GetChanged())
1376 		{
1377 			switch(QMessageBox::warning(this, "Unsaved File", "The document contains unsaved changes!\n"
1378 				"Do you want to save the changes before proceeding?", "&Save", "&Discard", "Cancel",
1379 				0,		// Enter == button 0
1380         		2))		// Escape == button 2
1381 			{
1382     			case 0: // Save clicked or Alt+S pressed or Enter pressed.
1383         			// save and exit
1384 					_FileSave();
1385 					break;
1386 				case 1: // Discard clicked or Alt+D pressed
1387 					// don't save but exit
1388 					break;
1389 				default: // Cancel clicked or Escape pressed
1390     	    		// don't exit
1391 					statusBar()->showMessage("Save abandoned", 5000);
1392     	    		return false;
1393 	    	} // warn the user to save
1394 	    } // map has been modified
1395 	} // map must exist first
1396 
1397     return true;
1398 } // _EraseOK()
1399 
1400 
1401 
1402 /************************
1403   EditorScrollView class functions follow
1404 ************************/
1405 
EditorScrollView(QWidget * parent,const QString & name,int width,int height)1406 EditorScrollView::EditorScrollView(QWidget* parent, const QString& name,
1407 	int width, int height)
1408 	: Q3ScrollView(parent, (const char*) name,
1409 		             Qt::WNoAutoErase|Qt::WStaticContents)
1410 {
1411 	// Set default editing modes.
1412 	_tile_mode  = PAINT_TILE;
1413 	_layer_edit = LOWER_LAYER;
1414 	_moving     = false;
1415 
1416 	// Clear the undo/redo vectors.
1417 	_tile_indeces.clear();
1418 	_previous_tiles.clear();
1419 	_modified_tiles.clear();
1420 
1421 	// set viewport
1422 	viewport()->setMouseTracking(true);
1423 
1424 	// for tracking key events
1425 	setFocusPolicy(Qt::StrongFocus);
1426 
1427 	// Create a new map.
1428 	_map = new Grid(viewport(), "Untitled", width, height);
1429 	_map->_ed_scrollview = this;
1430 	addChild(_map);
1431 
1432 	// Create menu actions related to the Context menu.
1433 	_insert_row_action = new QAction("Insert row", this);
1434 	_insert_row_action->setStatusTip("Inserts a row of empty tiles on all layers above the currently selected tile");
1435 	connect(_insert_row_action, SIGNAL(triggered()), this, SLOT(_ContextInsertRow()));
1436 	_insert_column_action = new QAction("Insert column", this);
1437 	_insert_column_action->setStatusTip("Inserts a column of empty tiles on all layers to the left of the currently selected tile");
1438 	connect(_insert_column_action, SIGNAL(triggered()), this, SLOT(_ContextInsertColumn()));
1439 	_delete_row_action = new QAction("Delete row", this);
1440 	_delete_row_action->setStatusTip("Deletes the currently selected row of tiles from all layers");
1441 	connect(_delete_row_action, SIGNAL(triggered()), this, SLOT(_ContextDeleteRow()));
1442 	_delete_column_action = new QAction("Delete column", this);
1443 	_delete_column_action->setStatusTip("Deletes the currently selected column of tiles from all layers");
1444 	connect(_delete_column_action, SIGNAL(triggered()), this, SLOT(_ContextDeleteColumn()));
1445 
1446 	// Context menu creation.
1447 	_context_menu = new QMenu(this);
1448 	_context_menu->addAction(_insert_row_action);
1449 	_context_menu->addAction(_insert_column_action);
1450 	_context_menu->addSeparator();
1451 	_context_menu->addAction(_delete_row_action);
1452 	_context_menu->addAction(_delete_column_action);
1453 } // EditorScrollView constructor
1454 
~EditorScrollView()1455 EditorScrollView::~EditorScrollView()
1456 {
1457 	delete _map;
1458 	_map = NULL;
1459 	delete _context_menu;
1460 	_context_menu = NULL;
1461 } // EditorScrollView destructor
1462 
Resize(int width,int height)1463 void EditorScrollView::Resize(int width, int height)
1464 {
1465 	_map->resize(width * TILE_WIDTH, height * TILE_HEIGHT);
1466 	_map->SetHeight(height);
1467 	_map->SetWidth(width);
1468 } // Resize(...)
1469 
GetCurrentLayer()1470 vector<int32>& EditorScrollView::GetCurrentLayer()
1471 {
1472 	return _map->GetLayer(_layer_edit, _map->GetContext());
1473 } // GetCurrentLayer()
1474 
1475 
1476 
1477 // ********** Protected functions **********
1478 
contentsMousePressEvent(QMouseEvent * evt)1479 void EditorScrollView::contentsMousePressEvent(QMouseEvent* evt)
1480 {
1481 	// don't draw outside the map
1482 	if ((evt->y() / TILE_HEIGHT) >= static_cast<uint32>(_map->GetHeight()) ||
1483 		(evt->x() / TILE_WIDTH)  >= static_cast<uint32>(_map->GetWidth()) ||
1484 		evt->x() < 0 || evt->y() < 0)
1485 		return;
1486 
1487 	// get reference to Editor
1488 	Editor* editor = static_cast<Editor*> (topLevelWidget());
1489 
1490 	_map->SetChanged(true);
1491 
1492 	int16 selection_count = 0;
1493 	if(_layer_edit != OBJECT_LAYER) {
1494 		// record location of pressed tile
1495 		_tile_index = static_cast<int32>
1496 			(evt->y() / TILE_HEIGHT * _map->GetWidth() + evt->x() / TILE_WIDTH);
1497 
1498 		// record the location of the beginning of the selection rectangle
1499 		if (evt->button() == Qt::LeftButton && editor->_select_on == true &&
1500 				_moving == false)
1501 		{
1502 			_first_corner_index = _tile_index;
1503 			_map->GetLayer(SELECT_LAYER, 0)[_tile_index] = 1;
1504 		} // selection mode is on
1505 	} else {
1506 		// select sprites
1507 
1508 		// check for selection amounts
1509 		for( std::list<MapSprite*>::iterator it=_map->sprites.begin(); it!=_map->sprites.end(); it++ )
1510 			if( (*it)->is_selected == true )
1511 				selection_count++;
1512 
1513 		for( std::list<MapSprite*>::iterator it=_map->sprites.begin(); it!=_map->sprites.end(); it++ )
1514 		{
1515 			if( (*it)->IsInHoverArea(static_cast<float>(evt->x())/TILE_WIDTH, static_cast<float>(evt->y())/ TILE_HEIGHT) )
1516 			{	// in the hovering area
1517 
1518 				(*it)->is_selected = true;
1519 				if(selection_count <= 0)	// the last one selection, if there's 2 more selections, substract it...
1520 					break;
1521 				else						// deselect it unless we got the last one selection
1522 				{
1523 					selection_count--;
1524 					(*it)->is_selected = false;
1525 				}
1526 			}
1527 			else	// if not in the hovering area, deselect it
1528 				(*it)->is_selected = false;
1529 		}
1530 	}
1531 
1532 	if (_layer_edit != OBJECT_LAYER)
1533 	{
1534 		switch (_tile_mode)
1535 		{
1536 			case PAINT_TILE: // start painting tiles
1537 			{
1538 				if (evt->button() == Qt::LeftButton && editor->_select_on == false)
1539 					_PaintTile(_tile_index);
1540 
1541 				break;
1542 			} // edit mode PAINT_TILE
1543 
1544 			case MOVE_TILE: // start moving a tile
1545 			{
1546 				// select tiles
1547 				if(_layer_edit != OBJECT_LAYER) {
1548 					_move_source_index = _tile_index;
1549 					if (editor->_select_on == false)
1550 						_moving = true;
1551 				}
1552 				break;
1553 			} // edit mode MOVE_TILE
1554 
1555 			case DELETE_TILE: // start deleting tiles
1556 			{
1557 				if (evt->button() == Qt::LeftButton && editor->_select_on == false)
1558 					_DeleteTile(_tile_index);
1559 
1560 				break;
1561 			} // edit mode DELETE_TILE
1562 
1563 			default:
1564 				QMessageBox::warning(this, "Tile editing mode",
1565 					"ERROR: Invalid tile editing mode!");
1566 		} // switch on tile editing mode
1567 	} // don't manipulate tiles on the object layer
1568 
1569 	// Draw the changes.
1570 	_map->updateGL();
1571 } // contentsMousePressEvent(...)
1572 
contentsMouseMoveEvent(QMouseEvent * evt)1573 void EditorScrollView::contentsMouseMoveEvent(QMouseEvent *evt)
1574 {
1575 	// get reference to Editor
1576 	Editor* editor = static_cast<Editor*> (topLevelWidget());
1577 
1578 	// don't draw outside the map
1579 	if ((evt->y() / TILE_HEIGHT) >= static_cast<uint32>(_map->GetHeight()) ||
1580 		(evt->x() / TILE_WIDTH)  >= static_cast<uint32>(_map->GetWidth()) ||
1581 		evt->x() < 0 || evt->y() < 0 )
1582 	{
1583 		editor->statusBar()->clear();
1584 		return;
1585 	}
1586 
1587 	// Move sprites
1588 	bool is_object_layer = (_layer_edit == OBJECT_LAYER);
1589 	if(is_object_layer && evt->buttons() == Qt::LeftButton) {
1590 		for( std::list<MapSprite*>::iterator it=_map->sprites.begin(); it!=_map->sprites.end(); it++ )
1591 			if( (*it)->is_selected ) {
1592 				float x = evt->x();
1593 				float y = evt->y();
1594 				float x_position = x*2/TILE_WIDTH + (*it)->img_half_width/2;
1595 				float y_position = y*2/TILE_HEIGHT + (*it)->img_height/2;
1596 				(*it)->SetXPosition( x_position, 0 );
1597 				(*it)->SetYPosition( y_position, 0 );
1598 			}
1599 	}
1600 		int32 index = static_cast<int32>
1601 			(evt->y() / TILE_HEIGHT * _map->GetWidth() + evt->x() / TILE_WIDTH);
1602 
1603 	if (index != _tile_index && !is_object_layer)  // user has moved onto another tile
1604 	                                               // ignore the object layer
1605 	{
1606 		_tile_index = index;
1607 
1608 		if (evt->state() == Qt::LeftButton && editor->_select_on == true &&
1609 		    _moving == false)
1610 		{
1611 			// Calculate the actual selection rectangle here, otherwise it's just
1612 			// like selecting individual tiles...
1613 			int x_old = _first_corner_index % _map->GetWidth();
1614 			int y_old = _first_corner_index / _map->GetWidth();
1615 			int x_new = _tile_index % _map->GetWidth();
1616 			int y_new = _tile_index / _map->GetWidth();
1617 
1618 			// Swap the coordinates around so *_old is always smaller than *_new.
1619 			int temp;
1620 			if (x_old > x_new)
1621 			{
1622 				temp = x_old;
1623 				x_old = x_new;
1624 				x_new = temp;
1625 			}
1626 			if (y_old > y_new)
1627 			{
1628 				temp = y_old;
1629 				y_old = y_new;
1630 				y_new = temp;
1631 			}
1632 
1633 			for (int y = y_old; y <= y_new; y++)
1634 				for (int x = x_old; x <= x_new; x++)
1635 					_map->GetLayer(SELECT_LAYER, 0)[y * _map->GetWidth() + x] = 1;
1636 		} // left mouse button was pressed and selection mode is on
1637 
1638 		switch (_tile_mode)
1639 		{
1640 			case PAINT_TILE: // continue painting tiles
1641 			{
1642 				if (evt->state() == Qt::LeftButton && editor->_select_on == false)
1643 					_PaintTile(_tile_index);
1644 
1645 				break;
1646 			} // edit mode PAINT_TILE
1647 
1648 			case MOVE_TILE: // continue moving a tile
1649 			{
1650 				break;
1651 			} // edit mode MOVE_TILE
1652 
1653 			case DELETE_TILE: // continue deleting tiles
1654 			{
1655 				if (evt->state() == Qt::LeftButton && editor->_select_on == false)
1656 					_DeleteTile(_tile_index);
1657 
1658 				break;
1659 			} // edit mode DELETE_TILE
1660 
1661 			default:
1662 				QMessageBox::warning(this, "Tile editing mode",
1663 					"ERROR: Invalid tile editing mode!");
1664 		} // switch on tile editing mode
1665 	} // mouse has moved to a new tile position
1666 
1667 	// Display mouse position in the format specified by _coord_type
1668 	QString position;
1669 	if (editor->_coord_type == 0)
1670 		position = QString("x: %1  y: %2").arg(static_cast<double>(evt->x() / TILE_WIDTH), 0, 'f', 0).arg(
1671 			static_cast<double>(evt->y() / TILE_HEIGHT), 0, 'f', 0);
1672 	else if (editor->_coord_type == 1)
1673 		position = QString("x: %1  y: %2").arg(static_cast<double>(evt->x() * 2 / TILE_WIDTH), 0, 'f', 0).arg(
1674 			static_cast<double>(evt->y() * 2 / TILE_HEIGHT), 0, 'f', 0);
1675 	else
1676 		position = QString("x: %1  y: %2").arg(evt->x() / static_cast<float>(TILE_WIDTH), 0, 'f', 1).arg(
1677 			evt->y() / static_cast<float>(TILE_HEIGHT), 0, 'f', 1);
1678 	editor->statusBar()->showMessage(position);
1679 
1680 	// Draw the changes.
1681 	_map->updateGL();
1682 } // contentsMouseMoveEvent(...)
1683 
contentsMouseReleaseEvent(QMouseEvent * evt)1684 void EditorScrollView::contentsMouseReleaseEvent(QMouseEvent *evt)
1685 {
1686 	vector<int32>::iterator it;    // used to iterate over an entire layer
1687 
1688 	// get reference to Editor so we can access the undo stack
1689 	Editor* editor = static_cast<Editor*> (topLevelWidget());
1690 
1691 	bool is_object_layer = (_layer_edit == OBJECT_LAYER);
1692 	if( !is_object_layer )
1693 	{
1694 		switch (_tile_mode)
1695 		{
1696 			case PAINT_TILE: // wrap up painting tiles
1697 			{
1698 				if (editor->_select_on == true)
1699 				{
1700 					vector<int32> select_layer = _map->GetLayer(SELECT_LAYER, 0);
1701 					for (int32 i = 0; i < static_cast<int32>(select_layer.size()); i++)
1702 					{
1703 						// Works because the selection layer and the current layer
1704 						// are the same size.
1705 						if (select_layer[i] != -1)
1706 							_PaintTile(i);
1707 					} // iterate over selection layer
1708 				} // only if painting a bunch of tiles
1709 
1710 				// Push command onto the undo stack.
1711 				LayerCommand* paint_command = new LayerCommand(_tile_indeces,
1712 					_previous_tiles, _modified_tiles, _layer_edit,
1713 					_map->GetContext(), editor, "Paint");
1714 				editor->_undo_stack->push(paint_command);
1715 				_tile_indeces.clear();
1716 				_previous_tiles.clear();
1717 				_modified_tiles.clear();
1718 				break;
1719 			} // edit mode PAINT_TILE
1720 
1721 			case MOVE_TILE: // wrap up moving tiles
1722 			{
1723 				if (_moving == true)
1724 				{
1725 					// record location of released tile
1726 					_tile_index = static_cast<int32>
1727 						(evt->y() / TILE_HEIGHT * _map->GetWidth() + evt->x() / TILE_WIDTH);
1728 					vector<int32>& layer = GetCurrentLayer();
1729 
1730 					if (editor->_select_on == false)
1731 					{
1732 						// Record information for undo/redo action.
1733 						_tile_indeces.push_back(_move_source_index);
1734 						_previous_tiles.push_back(layer[_move_source_index]);
1735 						_modified_tiles.push_back(-1);
1736 						_tile_indeces.push_back(_tile_index);
1737 						_previous_tiles.push_back(layer[_tile_index]);
1738 						_modified_tiles.push_back(layer[_move_source_index]);
1739 
1740 						// Perform the move.
1741 						layer[_tile_index] = layer[_move_source_index];
1742 						layer[_move_source_index] = -1;
1743 					} // only moving one tile at a time
1744 					else
1745 					{
1746 						vector<int32> select_layer = _map->GetLayer(SELECT_LAYER, 0);
1747 						for (int32 i = 0; i < static_cast<int32>(select_layer.size()); i++)
1748 						{
1749 							// Works because the selection layer and the current layer
1750 							// are the same size.
1751 							if (select_layer[i] != -1)
1752 							{
1753 								// Record information for undo/redo action.
1754 								_tile_indeces.push_back(i);
1755 								_previous_tiles.push_back(layer[i]);
1756 								_modified_tiles.push_back(-1);
1757 								_tile_indeces.push_back(i + _tile_index - _move_source_index);
1758 								_previous_tiles.push_back(layer[i + _tile_index - _move_source_index]);
1759 								_modified_tiles.push_back(layer[i]);
1760 
1761 								// Perform the move.
1762 								layer[i + _tile_index - _move_source_index] = layer[i];
1763 								layer[i] = -1;
1764 							} // only if current tile is selected
1765 						} // iterate over selection layer
1766 					} // moving a bunch of tiles at once
1767 
1768 					// Push command onto the undo stack.
1769 					LayerCommand* move_command = new LayerCommand(_tile_indeces,
1770 						_previous_tiles, _modified_tiles, _layer_edit,
1771 						_map->GetContext(), editor, "Move");
1772 					editor->_undo_stack->push(move_command);
1773 					_tile_indeces.clear();
1774 					_previous_tiles.clear();
1775 					_modified_tiles.clear();
1776 				} // moving tiles and not selecting them
1777 
1778 				break;
1779 			} // edit mode MOVE_TILE
1780 
1781 			case DELETE_TILE: // wrap up deleting tiles
1782 			{
1783 				if (editor->_select_on == true)
1784 				{
1785 					vector<int32> select_layer = _map->GetLayer(SELECT_LAYER, 0);
1786 					for (int32 i = 0; i < static_cast<int32>(select_layer.size()); i++)
1787 					{
1788 						// Works because the selection layer and the current layer
1789 						// are the same size.
1790 						if (select_layer[i] != -1)
1791 							_DeleteTile(i);
1792 					} // iterate over selection layer
1793 				} // only if deleting a bunch of tiles
1794 
1795 				// Push command onto undo stack.
1796 				LayerCommand* delete_command = new LayerCommand(_tile_indeces,
1797 					_previous_tiles, _modified_tiles, _layer_edit,
1798 					_map->GetContext(), editor, "Delete");
1799 				editor->_undo_stack->push(delete_command);
1800 				_tile_indeces.clear();
1801 				_previous_tiles.clear();
1802 				_modified_tiles.clear();
1803 				break;
1804 			} // edit mode DELETE_TILE
1805 
1806 			default:
1807 				QMessageBox::warning(this, "Tile editing mode",
1808 					"ERROR: Invalid tile editing mode!");
1809 		} // switch on tile editing mode
1810 	} // don't manipulate tiles on the object layer
1811 
1812 	// Clear the selection layer.
1813 	if ((_tile_mode != MOVE_TILE || _moving == true) && editor->_select_on == true && !is_object_layer )
1814 	{
1815 		vector<int32>& select_layer = _map->GetLayer(SELECT_LAYER, 0);
1816 		for (it = select_layer.begin(); it != select_layer.end(); it++)
1817 			*it = -1;
1818 	} // clears when not moving tiles or when moving tiles and not selecting them
1819 
1820 	if (editor->_select_on == true && _moving == false && _tile_mode == MOVE_TILE && !is_object_layer)
1821 		_moving = true;
1822 	else
1823 		_moving = false;
1824 
1825 	// Draw the changes.
1826 	_map->updateGL();
1827 } // contentsMouseReleaseEvent(...)
1828 
contentsContextMenuEvent(QContextMenuEvent * evt)1829 void EditorScrollView::contentsContextMenuEvent(QContextMenuEvent *evt)
1830 {
1831 	// Don't popup a menu outside the map.
1832 	if ((evt->y() / TILE_HEIGHT) >= static_cast<uint32>(_map->GetHeight()) ||
1833 		(evt->x() / TILE_WIDTH)  >= static_cast<uint32>(_map->GetWidth()) ||
1834 		evt->x() < 0 || evt->y() < 0)
1835 		return;
1836 
1837 	_tile_index = evt->y() / TILE_HEIGHT * _map->GetWidth() + evt->x() / TILE_WIDTH;
1838 	_context_menu->exec(QCursor::pos());
1839 	(static_cast<Editor*> (topLevelWidget()))->statusBar()->clear();
1840 } // contentsContextMenuEvent(...)
1841 
keyPressEvent(QKeyEvent * evt)1842 void EditorScrollView::keyPressEvent(QKeyEvent *evt)
1843 {
1844 	if(evt->key() == Qt::Key_Delete && _layer_edit == OBJECT_LAYER)
1845 		for( std::list<MapSprite*>::iterator it=_map->sprites.begin(); it!=_map->sprites.end(); it++ )
1846 		if( (*it)->is_selected ) {
1847 			_map->sprites.remove(*it);
1848 			break;	// break is needed for preventing iterator error
1849 	}
1850 
1851 } // keyPressEvent(...)
1852 
1853 
1854 // ********** Private slots **********
1855 
_ContextInsertRow()1856 void EditorScrollView::_ContextInsertRow()
1857 {
1858 	_map->InsertRow(_tile_index);
1859 } // _ContextInsertRow()
1860 
_ContextInsertColumn()1861 void EditorScrollView::_ContextInsertColumn()
1862 {
1863 	_map->InsertCol(_tile_index);
1864 } // _ContextInsertColumn()
1865 
_ContextDeleteRow()1866 void EditorScrollView::_ContextDeleteRow()
1867 {
1868 	_map->DeleteRow(_tile_index);
1869 } // _ContextDeleteRow()
1870 
_ContextDeleteColumn()1871 void EditorScrollView::_ContextDeleteColumn()
1872 {
1873 	_map->DeleteCol(_tile_index);
1874 } // _ContextDeleteColumn()
1875 
1876 
1877 
1878 // ********** Private functions **********
1879 
_PaintTile(int32 index)1880 void EditorScrollView::_PaintTile(int32 index)
1881 {
1882 	// get reference to current tileset
1883 	Editor* editor = static_cast<Editor*> (topLevelWidget());
1884 	Q3Table* table = static_cast<Q3Table*> (editor->_ed_tabs->currentPage());
1885 	QString tileset_name = editor->_ed_tabs->tabText(editor->_ed_tabs->currentIndex());
1886 	Q3TableSelection selection = table->selection(0);
1887 
1888 	int32 multiplier = _map->tileset_names.findIndex(tileset_name);
1889 	if (multiplier == -1)
1890 	{
1891 		_map->tileset_names.append(tileset_name);
1892 		multiplier = _map->tileset_names.findIndex(tileset_name);
1893 	} // calculate index of current tileset
1894 
1895 	if (selection.isActive() && (selection.numCols() * selection.numRows() > 1))
1896 	{
1897 		int32 map_row = index / _map->GetWidth();
1898 		int32 map_col = index % _map->GetWidth();
1899 
1900 		// Draw tiles from tileset selection onto map, one tile at a time.
1901 		for (int32 i = 0; i < selection.numRows() && map_row + i < _map->GetHeight(); i++)
1902 		{
1903 			for (int32 j = 0; j < selection.numCols() && map_col + j < _map->GetWidth(); j++)
1904 			{
1905 				int32 tileset_index = (selection.topRow() + i) * 16 + (selection.leftCol() + j);
1906 				int32 tile = (map_row + i) * _map->GetWidth() + map_col + j;
1907 
1908 				// perform randomization for autotiles
1909 				_AutotileRandomize(multiplier, tileset_index);
1910 
1911 				// Record information for undo/redo action.
1912 				_tile_indeces.push_back(tile);
1913 				_previous_tiles.push_back(GetCurrentLayer()[tile]);
1914 				_modified_tiles.push_back(tileset_index + multiplier * 256);
1915 
1916 				GetCurrentLayer()[tile] = tileset_index + multiplier * 256;
1917 			} // iterate through columns of selection
1918 		} // iterate through rows of selection
1919 	} // multiple tiles are selected
1920 	else
1921 	{
1922 		// put selected tile from tileset into tile array at correct position
1923 		int32 tileset_index = table->currentRow() * 16 + table->currentColumn();
1924 
1925 		// perform randomization for autotiles
1926 		_AutotileRandomize(multiplier, tileset_index);
1927 
1928 		// Record information for undo/redo action.
1929 		_tile_indeces.push_back(index);
1930 		_previous_tiles.push_back(GetCurrentLayer()[index]);
1931 		_modified_tiles.push_back(tileset_index + multiplier * 256);
1932 
1933 		GetCurrentLayer()[index] = tileset_index + multiplier * 256;
1934 	} // a single tile is selected
1935 
1936 } // _PaintTile(...)
1937 
_DeleteTile(int32 index)1938 void EditorScrollView::_DeleteTile(int32 index)
1939 {
1940 	// Record information for undo/redo action.
1941 	_tile_indeces.push_back(index);
1942 	_previous_tiles.push_back(GetCurrentLayer()[index]);
1943 	_modified_tiles.push_back(-1);
1944 
1945 	// Delete the tile.
1946 	GetCurrentLayer()[index] = -1;
1947 } // _DeleteTile(...)
1948 
_AutotileRandomize(int32 & tileset_num,int32 & tile_index)1949 void EditorScrollView::_AutotileRandomize(int32& tileset_num, int32& tile_index)
1950 {
1951 	map<int, string>::iterator it = _map->tilesets[tileset_num]->
1952 		autotileability.find(tile_index);
1953 
1954 	if (it != _map->tilesets[tileset_num]->autotileability.end())
1955 	{
1956 		// Set up for opening autotiling.lua.
1957 		ReadScriptDescriptor read_data;
1958 		if (read_data.OpenFile("dat/tilesets/autotiling.lua", true) == false)
1959 			QMessageBox::warning(this, "Loading File...",
1960 				QString("ERROR: could not open dat/tilesets/autotiling.lua for reading!"));
1961 
1962 		read_data.OpenTable(it->second);
1963 		int32 random_index = RandomBoundedInteger(1, static_cast<int32>(read_data.GetTableSize()));
1964 		read_data.OpenTable(random_index);
1965 		string tileset_name = read_data.ReadString(1);
1966 		tile_index = read_data.ReadInt(2);
1967 		read_data.CloseTable();
1968 		tileset_num = _map->tileset_names.indexOf(
1969 			QString(tileset_name.c_str()));
1970 		read_data.CloseTable();
1971 
1972 		read_data.CloseFile();
1973 
1974 		_AutotileTransitions(tileset_num, tile_index, it->second);
1975 	} // must have an autotileable tile
1976 } // _AutotileRandomize(...)
1977 
_AutotileTransitions(int32 & tileset_num,int32 & tile_index,const string tile_group)1978 void EditorScrollView::_AutotileTransitions(int32& tileset_num, int32& tile_index, const string tile_group)
1979 {
1980 	// These 2 vectors have a one-to-one correspondence. They should always
1981 	// contain 8 entries.
1982 	vector<int32>  existing_tiles;   // This vector will contain all the tiles around the current painted tile that need to be examined.
1983 	vector<string> existing_groups;  // This vector will contain the autotileable groups of the existing tiles.
1984 
1985 	// These booleans are used to know whether the current tile being painted is on the edge of the map.
1986 	// This will affect the transition/border algorithm.
1987 	bool top_edge    = (_tile_index - _map->GetWidth()) < 0;
1988 	bool bottom_edge = (_tile_index + _map->GetWidth()) >= (_map->GetWidth() * _map->GetHeight());
1989 	bool left_edge   = (_tile_index % _map->GetWidth()) == 0;
1990 	bool right_edge  = (_tile_index & _map->GetWidth()) == (_map->GetWidth() - 1);
1991 
1992 
1993 	// Now figure out which tiles surround the current painted one and put them into the existing_tiles vector.
1994 	if (!top_edge)
1995 	{
1996 		if (!left_edge)
1997 			existing_tiles.push_back(GetCurrentLayer()[_tile_index - _map->GetWidth() - 1]);
1998 		else
1999 			existing_tiles.push_back(-1);
2000 		existing_tiles.push_back(GetCurrentLayer()[_tile_index - _map->GetWidth()]);
2001 		if (!right_edge)
2002 			existing_tiles.push_back(GetCurrentLayer()[_tile_index - _map->GetWidth() + 1]);
2003 		else
2004 			existing_tiles.push_back(-1);
2005 	} // make sure there is a row of tiles above the painted one
2006 	else
2007 	{
2008 		existing_tiles.push_back(-1);
2009 		existing_tiles.push_back(-1);
2010 		existing_tiles.push_back(-1);
2011 	} // these tiles don't exist
2012 
2013 	if (!left_edge)
2014 		existing_tiles.push_back(GetCurrentLayer()[_tile_index - 1]);
2015 	else
2016 		existing_tiles.push_back(-1);
2017 
2018 	if (!right_edge)
2019 		existing_tiles.push_back(GetCurrentLayer()[_tile_index + 1]);
2020 	else
2021 		existing_tiles.push_back(-1);
2022 
2023 	if (!bottom_edge)
2024 	{
2025 		if (!left_edge)
2026 			existing_tiles.push_back(GetCurrentLayer()[_tile_index + _map->GetWidth() - 1]);
2027 		else
2028 			existing_tiles.push_back(-1);
2029 		existing_tiles.push_back(GetCurrentLayer()[_tile_index + _map->GetWidth()]);
2030 		if (!right_edge)
2031 			existing_tiles.push_back(GetCurrentLayer()[_tile_index + _map->GetWidth() + 1]);
2032 		else
2033 			existing_tiles.push_back(-1);
2034 	} // make sure there is a row of tiles below the painted one
2035 	else
2036 	{
2037 		existing_tiles.push_back(-1);
2038 		existing_tiles.push_back(-1);
2039 		existing_tiles.push_back(-1);
2040 	} // these tiles don't exist
2041 
2042 
2043 	// Now figure out what groups the existing tiles belong to.
2044 	for (unsigned int i = 0; i < existing_tiles.size(); i++)
2045 	{
2046 		int32 multiplier    = existing_tiles[i] / 256;
2047 		int32 tileset_index = existing_tiles[i] % 256;
2048 		map<int, string>::iterator it = _map->tilesets[multiplier]->
2049 			autotileability.find(tileset_index);
2050 
2051 		// Here we check to make sure the tile exists in the autotileability
2052 		// table. But if the tile in question is a transition tile with multiple
2053 		// variations, we want to assign it a group name of "none", otherwise
2054 		// the pattern detection algorithm won't work properly. Transition tiles
2055 		// with multiple variations are still handled correctly.
2056 		if (it != _map->tilesets[multiplier]->autotileability.end() &&
2057 			it->second.find("east", 0)      == string::npos &&
2058 			it->second.find("north", 0)     == string::npos &&
2059 			it->second.find("_ne", 0)       == string::npos &&
2060 			it->second.find("ne_corner", 0) == string::npos &&
2061 			it->second.find("_nw", 0)       == string::npos &&
2062 			it->second.find("nw_corner", 0) == string::npos &&
2063 			it->second.find("_se", 0)       == string::npos &&
2064 			it->second.find("se_corner", 0) == string::npos &&
2065 			it->second.find("south", 0)     == string::npos &&
2066 			it->second.find("_sw", 0)       == string::npos &&
2067 			it->second.find("sw_corner", 0) == string::npos &&
2068 			it->second.find("west", 0)      == string::npos)
2069 			existing_groups.push_back(it->second);
2070 		else
2071 			existing_groups.push_back("none");
2072 	} // iterate through the existing_tiles vector
2073 
2074 
2075 	// Transition tiles exist only for certain patterns of tiles surrounding the painted tile.
2076 	// Check for any of these patterns, and if one exists, transition magic begins!
2077 
2078 	string transition_group = "none";  // autotileable grouping for the border tile if it exists
2079 	TRANSITION_PATTERN_TYPE pattern = _CheckForTransitionPattern(tile_group, existing_groups,
2080 		transition_group);
2081 
2082 	if (pattern != INVALID_PATTERN)
2083 	{
2084 		transition_group = tile_group + "_" + transition_group;
2085 
2086 		// Set up for opening autotiling.lua.
2087 		ReadScriptDescriptor read_data;
2088 		if (read_data.OpenFile("dat/tilesets/autotiling.lua", true) == false)
2089 			QMessageBox::warning(this, "Loading File...",
2090 				QString("ERROR: could not open dat/tilesets/autotiling.lua for reading!"));
2091 
2092 		// Extract the correct transition tile from autotiling.lua as determined by
2093 		// _CheckForTransitionPattern(...).
2094 		if (read_data.DoesTableExist(transition_group) == true)
2095 		{
2096 			read_data.OpenTable(transition_group);
2097 
2098 			switch (pattern)
2099 			{
2100 				case NW_BORDER_PATTERN:
2101 					//cerr << "nw_border" << endl;
2102 					read_data.OpenTable(1);
2103 					break;
2104 				case N_BORDER_PATTERN:
2105 					//cerr << "n_border" << endl;
2106 					read_data.OpenTable(2);
2107 					break;
2108 				case NE_BORDER_PATTERN:
2109 					//cerr << "ne_border" << endl;
2110 					read_data.OpenTable(3);
2111 					break;
2112 				case E_BORDER_PATTERN:
2113 					//cerr << "e_border" << endl;
2114 					read_data.OpenTable(4);
2115 					break;
2116 				case SE_BORDER_PATTERN:
2117 					//cerr << "se_border" << endl;
2118 					read_data.OpenTable(5);
2119 					break;
2120 				case S_BORDER_PATTERN:
2121 					//cerr << "s_border" << endl;
2122 					read_data.OpenTable(6);
2123 					break;
2124 				case SW_BORDER_PATTERN:
2125 					//cerr << "sw_border" << endl;
2126 					read_data.OpenTable(7);
2127 					break;
2128 				case W_BORDER_PATTERN:
2129 					//cerr << "w_border" << endl;
2130 					read_data.OpenTable(8);
2131 					break;
2132 				case NW_CORNER_PATTERN:
2133 					//cerr << "nw_corner" << endl;
2134 					read_data.OpenTable(9);
2135 					break;
2136 				case NE_CORNER_PATTERN:
2137 					//cerr << "ne_corner" << endl;
2138 					read_data.OpenTable(10);
2139 					break;
2140 				case SE_CORNER_PATTERN:
2141 					//cerr << "se_corner" << endl;
2142 					read_data.OpenTable(11);
2143 					break;
2144 				case SW_CORNER_PATTERN:
2145 					//cerr << "sw_corner" << endl;
2146 					read_data.OpenTable(12);
2147 					break;
2148 				default: // should never get here
2149 					read_data.CloseTable();
2150 					read_data.CloseFile();
2151 					QMessageBox::warning(this, "Transition detection...",
2152 						QString("ERROR: Invalid pattern detected! No autotiling will occur for this tile!"));
2153 					return;
2154 			} // switch on transition pattern
2155 
2156 			string tileset_name = read_data.ReadString(1);
2157 			tile_index = read_data.ReadInt(2);
2158 			read_data.CloseTable();
2159 			tileset_num = _map->tileset_names.indexOf(
2160 				QString(tileset_name.c_str()));
2161 
2162 			read_data.CloseTable();
2163 
2164 			// Border/transition tiles may also have variations, so randomize them.
2165 			//assert(tileset_num != -1);
2166 			_AutotileRandomize(tileset_num, tile_index);
2167 		} // make sure the selected transition tiles exist
2168 
2169 		read_data.CloseFile();
2170 	} // make sure a transition pattern exists
2171 } // _AutotileTransitions(...)
2172 
_CheckForTransitionPattern(const string current_group,const vector<string> & surrounding_groups,string & border_group)2173 TRANSITION_PATTERN_TYPE EditorScrollView::_CheckForTransitionPattern(const string current_group,
2174 	const vector<string>& surrounding_groups, string& border_group)
2175 {
2176 	// Assumes that surrounding_groups always has 8 entries. Well, it's an error if it doesn't,
2177 	// and technically should never happen.
2178 
2179 	if (
2180 	    (surrounding_groups[0] == surrounding_groups[1] || surrounding_groups[0] == "none") &&
2181 	    (surrounding_groups[2] == surrounding_groups[1] || surrounding_groups[2] == "none") &&
2182 	    (surrounding_groups[1] != current_group && surrounding_groups[1] != "none" &&
2183 	     current_group != "none") &&
2184 	    (surrounding_groups[3] == current_group ||
2185 		 surrounding_groups[3] == "none" ||
2186 		 surrounding_groups[3] == surrounding_groups[1]) &&
2187 	    (surrounding_groups[4] == current_group ||
2188 		 surrounding_groups[4] == "none" ||
2189 		 surrounding_groups[4] == surrounding_groups[1]) &&
2190 	    (surrounding_groups[5] != surrounding_groups[1]) &&
2191 	    (surrounding_groups[7] != surrounding_groups[1]) &&
2192 	    (surrounding_groups[6] != surrounding_groups[1]))
2193 	{
2194 		border_group = surrounding_groups[1];
2195 		return N_BORDER_PATTERN;
2196 	} // check for the northern border pattern
2197 
2198 	else if (
2199 	    (surrounding_groups[2] == surrounding_groups[4] || surrounding_groups[2] == "none") &&
2200 	    (surrounding_groups[7] == surrounding_groups[4] || surrounding_groups[7] == "none") &&
2201 	    (surrounding_groups[4] != current_group && surrounding_groups[4] != "none" &&
2202 	     current_group != "none") &&
2203 	    (surrounding_groups[1] == current_group ||
2204 		 surrounding_groups[1] == "none" ||
2205 		 surrounding_groups[1] == surrounding_groups[4]) &&
2206 	    (surrounding_groups[6] == current_group ||
2207 		 surrounding_groups[6] == "none" ||
2208 		 surrounding_groups[6] == surrounding_groups[4]) &&
2209 	    (surrounding_groups[0] != surrounding_groups[4]) &&
2210 	    (surrounding_groups[5] != surrounding_groups[4]) &&
2211 	    (surrounding_groups[3] != surrounding_groups[4]))
2212 	{
2213 		border_group = surrounding_groups[4];
2214 		return E_BORDER_PATTERN;
2215 	} // check for the eastern border pattern
2216 
2217 	else if (
2218 	    (surrounding_groups[7] == surrounding_groups[6] || surrounding_groups[7] == "none") &&
2219 	    (surrounding_groups[5] == surrounding_groups[6] || surrounding_groups[5] == "none") &&
2220 	    (surrounding_groups[6] != current_group && surrounding_groups[6] != "none" &&
2221 	     current_group != "none") &&
2222 	    (surrounding_groups[3] == current_group ||
2223 		 surrounding_groups[3] == "none" ||
2224 		 surrounding_groups[3] == surrounding_groups[6]) &&
2225 	    (surrounding_groups[4] == current_group ||
2226 		 surrounding_groups[4] == "none" ||
2227 		 surrounding_groups[4] == surrounding_groups[6]) &&
2228 	    (surrounding_groups[2] != surrounding_groups[6]) &&
2229 	    (surrounding_groups[0] != surrounding_groups[6]) &&
2230 	    (surrounding_groups[1] != surrounding_groups[6]))
2231 	{
2232 		border_group = surrounding_groups[6];
2233 		return S_BORDER_PATTERN;
2234 	} // check for the southern border pattern
2235 
2236 	else if (
2237 	    (surrounding_groups[0] == surrounding_groups[3] || surrounding_groups[0] == "none") &&
2238 	    (surrounding_groups[5] == surrounding_groups[3] || surrounding_groups[5] == "none") &&
2239 	    (surrounding_groups[3] != current_group && surrounding_groups[3] != "none" &&
2240 	     current_group != "none") &&
2241 	    (surrounding_groups[1] == current_group ||
2242 		 surrounding_groups[1] == "none" ||
2243 		 surrounding_groups[1] == surrounding_groups[3]) &&
2244 	    (surrounding_groups[6] == current_group ||
2245 		 surrounding_groups[6] == "none" ||
2246 		 surrounding_groups[6] == surrounding_groups[3]) &&
2247 	    (surrounding_groups[2] != surrounding_groups[3]) &&
2248 	    (surrounding_groups[7] != surrounding_groups[3]) &&
2249 	    (surrounding_groups[4] != surrounding_groups[3]))
2250 	{
2251 		border_group = surrounding_groups[3];
2252 		return W_BORDER_PATTERN;
2253 	} // check for the western border pattern
2254 
2255 	else if (
2256 	    (surrounding_groups[1] == surrounding_groups[0]) &&
2257 	    (surrounding_groups[3] == surrounding_groups[0]) &&
2258 	    (surrounding_groups[0] != current_group && surrounding_groups[0] != "none" &&
2259 	     current_group != "none") &&
2260 	    (surrounding_groups[4] == current_group || surrounding_groups[4] == "none") &&
2261 	    (surrounding_groups[6] == current_group || surrounding_groups[6] == "none") &&
2262 	    (surrounding_groups[7] != surrounding_groups[0]))
2263 	{
2264 		border_group = surrounding_groups[0];
2265 		return NW_BORDER_PATTERN;
2266 	} // check for the northwestern border pattern
2267 
2268 	else if (
2269 	    (surrounding_groups[1] == surrounding_groups[2]) &&
2270 	    (surrounding_groups[4] == surrounding_groups[2]) &&
2271 	    (surrounding_groups[2] != current_group && surrounding_groups[2] != "none" &&
2272 	     current_group != "none") &&
2273 	    (surrounding_groups[3] == current_group || surrounding_groups[3] == "none") &&
2274 	    (surrounding_groups[6] == current_group || surrounding_groups[6] == "none") &&
2275 	    (surrounding_groups[5] != surrounding_groups[2]))
2276 	{
2277 		border_group = surrounding_groups[2];
2278 		return NE_BORDER_PATTERN;
2279 	} // check for the northeastern border pattern
2280 
2281 	else if (
2282 	    (surrounding_groups[4] == surrounding_groups[7]) &&
2283 	    (surrounding_groups[6] == surrounding_groups[7]) &&
2284 	    (surrounding_groups[7] != current_group && surrounding_groups[7] != "none" &&
2285 	     current_group != "none") &&
2286 	    (surrounding_groups[1] == current_group || surrounding_groups[1] == "none") &&
2287 	    (surrounding_groups[3] == current_group || surrounding_groups[3] == "none") &&
2288 	    (surrounding_groups[0] != surrounding_groups[7]))
2289 	{
2290 		border_group = surrounding_groups[7];
2291 		return SE_BORDER_PATTERN;
2292 	} // check for the southeastern border pattern
2293 
2294 	else if (
2295 	    (surrounding_groups[3] == surrounding_groups[5]) &&
2296 	    (surrounding_groups[6] == surrounding_groups[5]) &&
2297 	    (surrounding_groups[5] != current_group && surrounding_groups[5] != "none" &&
2298 	     current_group != "none") &&
2299 	    (surrounding_groups[1] == current_group || surrounding_groups[1] == "none") &&
2300 	    (surrounding_groups[4] == current_group || surrounding_groups[4] == "none") &&
2301 	    (surrounding_groups[2] != surrounding_groups[5]))
2302 	{
2303 		border_group = surrounding_groups[5];
2304 		return SW_BORDER_PATTERN;
2305 	} // check for the southwestern border pattern
2306 
2307 	else if (
2308 	    (surrounding_groups[0] != current_group && surrounding_groups[0] != "none" &&
2309 	     current_group != "none") &&
2310 	    (surrounding_groups[1] == current_group || surrounding_groups[1] == "none") &&
2311 	    (surrounding_groups[3] == current_group || surrounding_groups[3] == "none") &&
2312 	    (surrounding_groups[2] != surrounding_groups[0]) &&
2313 	    (surrounding_groups[4] != surrounding_groups[0]) &&
2314 	    (surrounding_groups[5] != surrounding_groups[0]) &&
2315 	    (surrounding_groups[6] != surrounding_groups[0]) &&
2316 	    (surrounding_groups[7] != surrounding_groups[0]))
2317 	{
2318 		border_group = surrounding_groups[0];
2319 		return NW_CORNER_PATTERN;
2320 	} // check for the northwestern corner pattern
2321 
2322 	else if (
2323 	    (surrounding_groups[2] != current_group && surrounding_groups[2] != "none" &&
2324 	     current_group != "none") &&
2325 	    (surrounding_groups[1] == current_group || surrounding_groups[1] == "none") &&
2326 	    (surrounding_groups[4] == current_group || surrounding_groups[4] == "none") &&
2327 	    (surrounding_groups[0] != surrounding_groups[2]) &&
2328 	    (surrounding_groups[3] != surrounding_groups[2]) &&
2329 	    (surrounding_groups[5] != surrounding_groups[2]) &&
2330 	    (surrounding_groups[6] != surrounding_groups[2]) &&
2331 	    (surrounding_groups[7] != surrounding_groups[2]))
2332 	{
2333 		border_group = surrounding_groups[2];
2334 		return NE_CORNER_PATTERN;
2335 	} // check for the northeastern corner pattern
2336 
2337 	else if (
2338 	    (surrounding_groups[7] != current_group && surrounding_groups[7] != "none" &&
2339 	     current_group != "none") &&
2340 	    (surrounding_groups[4] == current_group || surrounding_groups[4] == "none") &&
2341 	    (surrounding_groups[6] == current_group || surrounding_groups[6] == "none") &&
2342 	    (surrounding_groups[0] != surrounding_groups[7]) &&
2343 	    (surrounding_groups[1] != surrounding_groups[7]) &&
2344 	    (surrounding_groups[2] != surrounding_groups[7]) &&
2345 	    (surrounding_groups[3] != surrounding_groups[7]) &&
2346 	    (surrounding_groups[5] != surrounding_groups[7]))
2347 	{
2348 		border_group = surrounding_groups[7];
2349 		return SE_CORNER_PATTERN;
2350 	} // check for the southeastern corner pattern
2351 
2352 	else if (
2353 	    (surrounding_groups[5] != current_group && surrounding_groups[5] != "none" &&
2354 	     current_group != "none") &&
2355 	    (surrounding_groups[3] == current_group || surrounding_groups[3] == "none") &&
2356 	    (surrounding_groups[6] == current_group || surrounding_groups[6] == "none") &&
2357 	    (surrounding_groups[0] != surrounding_groups[5]) &&
2358 	    (surrounding_groups[1] != surrounding_groups[5]) &&
2359 	    (surrounding_groups[2] != surrounding_groups[5]) &&
2360 	    (surrounding_groups[4] != surrounding_groups[5]) &&
2361 	    (surrounding_groups[7] != surrounding_groups[5]))
2362 	{
2363 		border_group = surrounding_groups[5];
2364 		return SW_CORNER_PATTERN;
2365 	} // check for the southwestern corner pattern
2366 
2367 	return INVALID_PATTERN;
2368 } // _CheckForTransitionPattern(...)
2369 
2370 
2371 /************************
2372   LayerCommand class functions follow
2373 ************************/
2374 
LayerCommand(vector<int32> indeces,vector<int32> previous,vector<int32> modified,LAYER_TYPE layer,int context,Editor * editor,const QString & text,QUndoCommand * parent)2375 LayerCommand::LayerCommand(vector<int32> indeces, vector<int32> previous,
2376 	vector<int32> modified, LAYER_TYPE layer, int context, Editor* editor,
2377 	const QString& text, QUndoCommand* parent)
2378 	: QUndoCommand(text, parent)
2379 {
2380 	_tile_indeces = indeces;
2381 	_previous_tiles = previous;
2382 	_modified_tiles = modified;
2383 	_edited_layer = layer;
2384 	_context = context;
2385 	_editor = editor;
2386 } // constructor
2387 
undo()2388 void LayerCommand::undo()
2389 {
2390 	for (int32 i = 0; i < static_cast<int32>(_tile_indeces.size()); i++)
2391 		_editor->_ed_scrollview->_map->GetLayer(_edited_layer, _context)
2392 			[_tile_indeces[i]] = _previous_tiles[i];
2393 	_editor->_ed_scrollview->_map->updateGL();
2394 } // undo()
2395 
redo()2396 void LayerCommand::redo()
2397 {
2398 	for (int32 i = 0; i < static_cast<int32>(_tile_indeces.size()); i++)
2399 		_editor->_ed_scrollview->_map->GetLayer(_edited_layer, _context)
2400 			[_tile_indeces[i]] = _modified_tiles[i];
2401 	_editor->_ed_scrollview->_map->updateGL();
2402 } // redo()
2403 
2404