palette_window.cpp

main 530 lines · 16.2 KB Raw
Alex Alex Commit Map Editor 18/10/2026 23:40
1//////////////////////////////////////////////////////////////////////
2// This file is part of Remere's Map Editor
3//////////////////////////////////////////////////////////////////////
4// Remere's Map Editor is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Remere's Map Editor is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//////////////////////////////////////////////////////////////////////
17
18#include "main.h"
19
20#include "settings.h"
21#include "gui.h"
22#include "brush.h"
23#include "map_display.h"
24
25#include "palette_window.h"
26#include "palette_brushlist.h"
27#include "palette_house.h"
28#include "palette_creature.h"
29#include "palette_waypoints.h"
30
31#include "house_brush.h"
32#include "map.h"
33
34// ============================================================================
35// Palette window
36
37BEGIN_EVENT_TABLE(PaletteWindow, wxPanel)
38EVT_CHOICEBOOK_PAGE_CHANGING(PALETTE_CHOICEBOOK, PaletteWindow::OnSwitchingPage)
39EVT_CHOICEBOOK_PAGE_CHANGED(PALETTE_CHOICEBOOK, PaletteWindow::OnPageChanged)
40EVT_CLOSE(PaletteWindow::OnClose)
41
42EVT_KEY_DOWN(PaletteWindow::OnKey)
43EVT_TEXT(wxID_ANY, PaletteWindow::OnActionIDChange)
44EVT_CHECKBOX(wxID_ANY, PaletteWindow::OnActionIDToggle)
45END_EVENT_TABLE()
46
47PaletteWindow::PaletteWindow(wxWindow* parent, const TilesetContainer& tilesets) :
48 wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(230, 250)),
49 choicebook(nullptr),
50 terrain_palette(nullptr),
51 doodad_palette(nullptr),
52 item_palette(nullptr),
53 collection_palette(nullptr),
54 creature_palette(nullptr),
55 house_palette(nullptr),
56 waypoint_palette(nullptr),
57 raw_palette(nullptr),
58 action_id_input(nullptr),
59 action_id_checkbox(nullptr),
60 action_id(0),
61 action_id_enabled(false) {
62
63 // Allow resizing but maintain minimum size
64 SetMinSize(wxSize(225, 250));
65
66 // Create main sizer
67 wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
68
69 // Create action ID controls
70 wxSizer* action_id_sizer = new wxBoxSizer(wxHORIZONTAL);
71 action_id_input = new wxTextCtrl(this, wxID_ANY, "0", wxDefaultPosition, wxSize(60, -1));
72 action_id_input->SetToolTip("Enter action ID (0-65535)");
73 action_id_checkbox = new wxCheckBox(this, wxID_ANY, "Enable Action ID");
74 action_id_checkbox->SetToolTip("When enabled, placed items will have this action ID");
75
76 action_id_sizer->Add(action_id_input, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
77 action_id_sizer->Add(action_id_checkbox, 0, wxALIGN_CENTER_VERTICAL);
78
79 main_sizer->Add(action_id_sizer, 0, wxEXPAND | wxALL, 5);
80
81 // Create choicebook with EXPAND flag to fill available space
82 choicebook = new wxChoicebook(this, PALETTE_CHOICEBOOK, wxDefaultPosition, wxDefaultSize);
83
84 terrain_palette = static_cast<BrushPalettePanel*>(CreateTerrainPalette(choicebook, tilesets));
85 choicebook->AddPage(terrain_palette, terrain_palette->GetName());
86
87 doodad_palette = static_cast<BrushPalettePanel*>(CreateDoodadPalette(choicebook, tilesets));
88 choicebook->AddPage(doodad_palette, doodad_palette->GetName());
89
90 collection_palette = static_cast<BrushPalettePanel*>(CreateCollectionPalette(choicebook, tilesets));
91 choicebook->AddPage(collection_palette, collection_palette->GetName());
92
93 item_palette = static_cast<BrushPalettePanel*>(CreateItemPalette(choicebook, tilesets));
94 choicebook->AddPage(item_palette, item_palette->GetName());
95
96 house_palette = static_cast<HousePalettePanel*>(CreateHousePalette(choicebook, tilesets));
97 choicebook->AddPage(house_palette, house_palette->GetName());
98
99 waypoint_palette = static_cast<WaypointPalettePanel*>(CreateWaypointPalette(choicebook, tilesets));
100 choicebook->AddPage(waypoint_palette, waypoint_palette->GetName());
101
102 creature_palette = static_cast<CreaturePalettePanel*>(CreateCreaturePalette(choicebook, tilesets));
103 choicebook->AddPage(creature_palette, creature_palette->GetName());
104
105 raw_palette = static_cast<BrushPalettePanel*>(CreateRAWPalette(choicebook, tilesets));
106 choicebook->AddPage(raw_palette, raw_palette->GetName());
107
108 // Add choicebook to main sizer
109 main_sizer->Add(choicebook, 1, wxEXPAND | wxALL, 2);
110 SetSizer(main_sizer);
111
112 // Load first page
113 LoadCurrentContents();
114}
115
116PaletteWindow::~PaletteWindow() {
117 // Clean up all brushes and caches in each palette panel
118 if (choicebook) {
119 // Ensure each panel type is properly cleaned up
120 if (terrain_palette) {
121 terrain_palette->DestroyAllCaches();
122 }
123 if (doodad_palette) {
124 doodad_palette->DestroyAllCaches();
125 }
126 if (item_palette) {
127 item_palette->DestroyAllCaches();
128 }
129 if (collection_palette) {
130 collection_palette->DestroyAllCaches();
131 }
132 if (raw_palette) {
133 raw_palette->DestroyAllCaches();
134 }
135
136 // Other palette types may need specific cleanup
137 for (size_t iz = 0; iz < choicebook->GetPageCount(); ++iz) {
138 PalettePanel* panel = dynamic_cast<PalettePanel*>(choicebook->GetPage(iz));
139 if (panel) {
140 panel->InvalidateContents();
141 }
142 }
143 }
144}
145
146PalettePanel* PaletteWindow::CreateTerrainPalette(wxWindow* parent, const TilesetContainer& tilesets) {
147 BrushPalettePanel* panel = newd BrushPalettePanel(parent, tilesets, TILESET_TERRAIN);
148 panel->SetListType(wxstr(g_settings.getString(Config::PALETTE_TERRAIN_STYLE)));
149
150 BrushToolPanel* tool_panel = newd BrushToolPanel(panel);
151 tool_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_TERRAIN_TOOLBAR));
152 panel->AddToolPanel(tool_panel);
153
154 BrushSizePanel* size_panel = newd BrushSizePanel(panel);
155 size_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_TERRAIN_TOOLBAR));
156 panel->AddToolPanel(size_panel);
157
158 ZoneBrushPanel* zone_brush_panel = newd ZoneBrushPanel(panel);
159 zone_brush_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_TERRAIN_TOOLBAR));
160 panel->AddToolPanel(zone_brush_panel);
161
162 return panel;
163}
164
165PalettePanel* PaletteWindow::CreateCollectionPalette(wxWindow* parent, const TilesetContainer& tilesets) {
166 BrushPalettePanel* panel = newd BrushPalettePanel(parent, tilesets, TILESET_COLLECTION);
167 panel->SetListType(wxstr(g_settings.getString(Config::PALETTE_COLLECTION_STYLE)));
168
169 // terrain palette tools
170 BrushToolPanel* tool_panel = newd BrushToolPanel(panel);
171 tool_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_COLLECTION_TOOLBAR));
172 panel->AddToolPanel(tool_panel);
173
174 // brush thickness panel
175 panel->AddToolPanel(newd BrushThicknessPanel(panel));
176
177 // brush size tools
178 BrushSizePanel* size_panel = newd BrushSizePanel(panel);
179 size_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_COLLECTION_TOOLBAR));
180 panel->AddToolPanel(size_panel);
181
182 return panel;
183}
184
185PalettePanel* PaletteWindow::CreateDoodadPalette(wxWindow* parent, const TilesetContainer& tilesets) {
186 BrushPalettePanel* panel = newd BrushPalettePanel(parent, tilesets, TILESET_DOODAD);
187 panel->SetListType(wxstr(g_settings.getString(Config::PALETTE_DOODAD_STYLE)));
188
189 panel->AddToolPanel(newd BrushThicknessPanel(panel));
190
191 BrushSizePanel* size_panel = newd BrushSizePanel(panel);
192 size_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_DOODAD_SIZEBAR));
193 panel->AddToolPanel(size_panel);
194
195 return panel;
196}
197
198PalettePanel* PaletteWindow::CreateItemPalette(wxWindow* parent, const TilesetContainer& tilesets) {
199 BrushPalettePanel* panel = newd BrushPalettePanel(parent, tilesets, TILESET_ITEM);
200 panel->SetListType(wxstr(g_settings.getString(Config::PALETTE_ITEM_STYLE)));
201
202 BrushSizePanel* size_panel = newd BrushSizePanel(panel);
203 size_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_ITEM_SIZEBAR));
204 panel->AddToolPanel(size_panel);
205 return panel;
206}
207
208PalettePanel* PaletteWindow::CreateHousePalette(wxWindow* parent, const TilesetContainer& tilesets) {
209 HousePalettePanel* panel = newd HousePalettePanel(parent);
210
211 BrushSizePanel* size_panel = newd BrushSizePanel(panel);
212 size_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_HOUSE_SIZEBAR));
213 panel->AddToolPanel(size_panel);
214 return panel;
215}
216
217PalettePanel* PaletteWindow::CreateWaypointPalette(wxWindow* parent, const TilesetContainer& tilesets) {
218 WaypointPalettePanel* panel = newd WaypointPalettePanel(parent);
219 return panel;
220}
221
222PalettePanel* PaletteWindow::CreateCreaturePalette(wxWindow* parent, const TilesetContainer& tilesets) {
223 CreaturePalettePanel* panel = newd CreaturePalettePanel(parent);
224 return panel;
225}
226
227PalettePanel* PaletteWindow::CreateRAWPalette(wxWindow* parent, const TilesetContainer& tilesets) {
228 BrushPalettePanel* panel = newd BrushPalettePanel(parent, tilesets, TILESET_RAW);
229 panel->SetListType(wxstr(g_settings.getString(Config::PALETTE_RAW_STYLE)));
230
231 BrushSizePanel* size_panel = newd BrushSizePanel(panel);
232 size_panel->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_RAW_SIZEBAR));
233 panel->AddToolPanel(size_panel);
234
235 return panel;
236}
237
238void PaletteWindow::ReloadSettings(Map* map) {
239 if (terrain_palette) {
240 terrain_palette->SetListType(wxstr(g_settings.getString(Config::PALETTE_TERRAIN_STYLE)));
241 terrain_palette->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_TERRAIN_TOOLBAR));
242 }
243 if (doodad_palette) {
244 doodad_palette->SetListType(wxstr(g_settings.getString(Config::PALETTE_DOODAD_STYLE)));
245 doodad_palette->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_DOODAD_SIZEBAR));
246 }
247 if (house_palette) {
248 house_palette->SetMap(map);
249 house_palette->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_HOUSE_SIZEBAR));
250 }
251 if (waypoint_palette) {
252 waypoint_palette->SetMap(map);
253 }
254 if (item_palette) {
255 item_palette->SetListType(wxstr(g_settings.getString(Config::PALETTE_ITEM_STYLE)));
256 item_palette->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_ITEM_SIZEBAR));
257 }
258 if (collection_palette) {
259 collection_palette->SetListType(wxstr(g_settings.getString(Config::PALETTE_COLLECTION_STYLE)));
260 collection_palette->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_COLLECTION_TOOLBAR));
261 }
262 if (raw_palette) {
263 raw_palette->SetListType(wxstr(g_settings.getString(Config::PALETTE_RAW_STYLE)));
264 raw_palette->SetToolbarIconSize(g_settings.getBoolean(Config::USE_LARGE_RAW_SIZEBAR));
265 }
266 InvalidateContents();
267}
268
269void PaletteWindow::LoadCurrentContents() {
270 if (!choicebook) {
271 return;
272 }
273 PalettePanel* panel = dynamic_cast<PalettePanel*>(choicebook->GetCurrentPage());
274 panel->LoadCurrentContents();
275 Fit();
276 Refresh();
277 Update();
278}
279
280void PaletteWindow::InvalidateContents() {
281 if (!choicebook) {
282 return;
283 }
284 for (size_t iz = 0; iz < choicebook->GetPageCount(); ++iz) {
285 PalettePanel* panel = dynamic_cast<PalettePanel*>(choicebook->GetPage(iz));
286 panel->InvalidateContents();
287 }
288 LoadCurrentContents();
289 if (creature_palette) {
290 creature_palette->OnUpdate();
291 }
292 if (house_palette) {
293 house_palette->OnUpdate();
294 }
295 if (waypoint_palette) {
296 waypoint_palette->OnUpdate();
297 }
298}
299
300void PaletteWindow::SelectPage(PaletteType id) {
301 if (!choicebook) {
302 return;
303 }
304 if (id == GetSelectedPage()) {
305 return;
306 }
307
308 // First notify the current page that it's being switched out
309 PalettePanel* currentPanel = dynamic_cast<PalettePanel*>(choicebook->GetCurrentPage());
310 if (currentPanel) {
311 currentPanel->OnSwitchOut();
312 }
313
314 // Find and select the new page
315 for (size_t iz = 0; iz < choicebook->GetPageCount(); ++iz) {
316 PalettePanel* panel = dynamic_cast<PalettePanel*>(choicebook->GetPage(iz));
317 if (panel->GetType() == id) {
318 choicebook->SetSelection(iz);
319 panel->OnSwitchIn();
320 break;
321 }
322 }
323}
324
325Brush* PaletteWindow::GetSelectedBrush() const {
326 if (!choicebook) {
327 return nullptr;
328 }
329 PalettePanel* panel = dynamic_cast<PalettePanel*>(choicebook->GetCurrentPage());
330 return panel->GetSelectedBrush();
331}
332
333int PaletteWindow::GetSelectedBrushSize() const {
334 if (!choicebook) {
335 return 0;
336 }
337 PalettePanel* panel = dynamic_cast<PalettePanel*>(choicebook->GetCurrentPage());
338 return panel->GetSelectedBrushSize();
339}
340
341PaletteType PaletteWindow::GetSelectedPage() const {
342 if (!choicebook) {
343 return TILESET_UNKNOWN;
344 }
345 PalettePanel* panel = dynamic_cast<PalettePanel*>(choicebook->GetCurrentPage());
346 ASSERT(panel);
347 return panel->GetType();
348}
349
350bool PaletteWindow::OnSelectBrush(const Brush* whatbrush, PaletteType primary) {
351 if (!choicebook || !whatbrush) {
352 return false;
353 }
354
355 if (whatbrush->isHouse() && house_palette) {
356 house_palette->SelectBrush(whatbrush);
357 SelectPage(TILESET_HOUSE);
358 return true;
359 }
360
361 switch (primary) {
362 case TILESET_TERRAIN: {
363 // This is already searched first
364 break;
365 }
366 case TILESET_DOODAD: {
367 // Ok, search doodad before terrain
368 if (doodad_palette && doodad_palette->SelectBrush(whatbrush)) {
369 SelectPage(TILESET_DOODAD);
370 return true;
371 }
372 break;
373 }
374 case TILESET_COLLECTION: {
375 if (collection_palette && collection_palette->SelectBrush(whatbrush)) {
376 SelectPage(TILESET_COLLECTION);
377 return true;
378 }
379 }
380 case TILESET_ITEM: {
381 if (item_palette && item_palette->SelectBrush(whatbrush)) {
382 SelectPage(TILESET_ITEM);
383 return true;
384 }
385 break;
386 }
387 case TILESET_CREATURE: {
388 if (creature_palette && creature_palette->SelectBrush(whatbrush)) {
389 SelectPage(TILESET_CREATURE);
390 return true;
391 }
392 break;
393 }
394 case TILESET_RAW: {
395 if (raw_palette && raw_palette->SelectBrush(whatbrush)) {
396 SelectPage(TILESET_RAW);
397 return true;
398 }
399 break;
400 }
401 default:
402 break;
403 }
404
405 // Test if it's a terrain brush
406 if (terrain_palette && terrain_palette->SelectBrush(whatbrush)) {
407 SelectPage(TILESET_TERRAIN);
408 return true;
409 }
410
411 // Test if it's a doodad brush
412 if (primary != TILESET_DOODAD) {
413 if (doodad_palette && doodad_palette->SelectBrush(whatbrush)) {
414 SelectPage(TILESET_DOODAD);
415 return true;
416 }
417 }
418
419 // Test if it's an item brush
420 if (primary != TILESET_ITEM) {
421 if (item_palette && item_palette->SelectBrush(whatbrush)) {
422 SelectPage(TILESET_ITEM);
423 return true;
424 }
425 }
426
427 // Test if it's a creature brush
428 if (primary != TILESET_CREATURE) {
429 if (creature_palette && creature_palette->SelectBrush(whatbrush)) {
430 SelectPage(TILESET_CREATURE);
431 return true;
432 }
433 }
434
435 // Test if it's a raw brush
436 if (primary != TILESET_RAW) {
437 if (raw_palette && raw_palette->SelectBrush(whatbrush)) {
438 SelectPage(TILESET_RAW);
439 return true;
440 }
441 }
442
443 return false;
444}
445
446void PaletteWindow::OnSwitchingPage(wxChoicebookEvent& event) {
447 event.Skip();
448 if (!choicebook) {
449 return;
450 }
451
452 wxWindow* old_page = choicebook->GetPage(choicebook->GetSelection());
453 PalettePanel* old_panel = dynamic_cast<PalettePanel*>(old_page);
454 if (old_panel) {
455 old_panel->OnSwitchOut();
456 }
457
458 wxWindow* page = choicebook->GetPage(event.GetSelection());
459 PalettePanel* panel = dynamic_cast<PalettePanel*>(page);
460 if (panel) {
461 panel->OnSwitchIn();
462 }
463}
464
465void PaletteWindow::OnPageChanged(wxChoicebookEvent& event) {
466 if (!choicebook) {
467 return;
468 }
469 g_gui.SelectBrush();
470}
471
472void PaletteWindow::OnUpdateBrushSize(BrushShape shape, int size) {
473 if (!choicebook) {
474 return;
475 }
476 PalettePanel* page = dynamic_cast<PalettePanel*>(choicebook->GetCurrentPage());
477 ASSERT(page);
478 page->OnUpdateBrushSize(shape, size);
479}
480
481void PaletteWindow::OnUpdate(Map* map) {
482 if (creature_palette) {
483 creature_palette->OnUpdate();
484 }
485 if (house_palette) {
486 house_palette->SetMap(map);
487 }
488 if (waypoint_palette) {
489 waypoint_palette->SetMap(map);
490 waypoint_palette->OnUpdate();
491 }
492}
493
494void PaletteWindow::OnKey(wxKeyEvent& event) {
495 if (g_gui.GetCurrentTab() != nullptr) {
496 g_gui.GetCurrentMapTab()->GetEventHandler()->AddPendingEvent(event);
497 }
498}
499
500void PaletteWindow::OnClose(wxCloseEvent& event) {
501 if (!event.CanVeto()) {
502 // We can't do anything! This sucks!
503 // (application is closed, we have to destroy ourselves)
504 Destroy();
505 } else {
506 Show(false);
507 event.Veto(true);
508 }
509}
510
511void PaletteWindow::OnActionIDChange(wxCommandEvent& event) {
512 wxString value = action_id_input->GetValue();
513 long val;
514 if (value.ToLong(&val)) {
515 if (val >= 0 && val <= 65535) {
516 action_id = static_cast<uint16_t>(val);
517 } else {
518 // Reset to previous valid value
519 action_id_input->SetValue(wxString::Format("%d", action_id));
520 }
521 } else {
522 // Reset to previous valid value
523 action_id_input->SetValue(wxString::Format("%d", action_id));
524 }
525}
526
527void PaletteWindow::OnActionIDToggle(wxCommandEvent& event) {
528 action_id_enabled = action_id_checkbox->GetValue();
529}
530
Top