1 /* editWindows.cc
2    Implements all the windows in the editMode
3 
4    Copyright (C) 2003-2004  Mathias Broxvall
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include "editWindows.h"
22 #include "editMode.h"
23 #include "map.h"
24 #include "menuMode.h"
25 #include "menusystem.h"
26 #include "settings.h"
27 
28 #include <SDL2/SDL_keyboard.h>
29 #include <SDL2/SDL_keycode.h>
30 #include <SDL2/SDL_mouse.h>
31 #include <SDL2/SDL_timer.h>
32 #include <dirent.h>
33 
EMenuWindow()34 EMenuWindow::EMenuWindow() : MyWindow(0, 0, screenWidth, 30) {
35   activeSubID = -1;
36   activeSubWindow = NULL;
37   spacing = screenWidth / N_SUBMENUS;
38   for (int i = 0; i < N_SUBMENUS; i++) subWindows[i] = new ESubWindow(i, spacing * i, 30);
39 }
~EMenuWindow()40 EMenuWindow::~EMenuWindow() {
41   for (int i = 0; i < N_SUBMENUS; i++) delete subWindows[i];
42 }
refreshChildPositions()43 void EMenuWindow::refreshChildPositions() {
44   this->resize(screenWidth, 30);
45   spacing = screenWidth / N_SUBMENUS;
46   for (int i = 0; i < N_SUBMENUS; i++) { subWindows[i]->moveTo(spacing * i, 30); }
47 }
48 
draw()49 void EMenuWindow::draw() {
50   int fontSize = 20;  // 16;
51   char str[256];
52 
53   this->MyWindow::draw();
54   for (int i = 0; i < N_SUBMENUS; i++) {
55     snprintf(str, sizeof(str), "F%d %s", i + 1, cMenuNames[i]);
56     addText_Center(CODE_FROM_MENU(i), fontSize / 2, height / 2, str,
57                    spacing * i + spacing / 2);
58   }
59 }
mouseDown(int,int,int)60 void EMenuWindow::mouseDown(int /*state*/, int /*x*/, int /*y*/) {
61   int code = getSelectedArea();
62   int menu = CODE_TO_MENU(code);
63   if (menu >= 0 && menu < N_SUBMENUS) openSubMenu(menu);
64 }
openSubMenu(int id)65 void EMenuWindow::openSubMenu(int id) {
66   if (activeSubWindow) activeSubWindow->remove();
67   if (activeSubID == id) { /* Close window instead if already opened */
68     activeSubID = -1;
69     return;
70   }
71   activeSubID = id;
72   activeSubWindow = subWindows[id];
73   activeSubWindow->attach();
74 }
keyToMenuEntry(int key,int shift) const75 int EMenuWindow::keyToMenuEntry(int key, int shift) const {
76   /* Convert keypad numbers to normal numbers */
77   if (key >= SDLK_KP_0 && key <= SDLK_KP_9) key = '0' + key - SDLK_KP_0;
78   if (key == SDLK_KP_PLUS) key = '+';
79   if (key == SDLK_KP_MINUS) key = '-';
80 
81   /* Make all keys lower case since SHIFT and uppercase keys have
82          a special meaning */
83   if (shift) key = key - 'A' + 'a';
84   if (key == SDLK_UP) key = 'U';
85   if (key == SDLK_LEFT) key = 'L';
86   if (key == SDLK_RIGHT) key = 'R';
87   if (key == SDLK_DOWN) key = 'D';
88 
89   /* Search current menu first */
90   int i = activeSubID;
91   if (i != -1)
92     for (int j = 0; cKeyShortcuts[i][j]; j++)
93       if (cKeyShortcuts[i][j] == key) return i * MAX_MENU_ENTRIES + j;
94   for (i = 0; i < N_SUBMENUS; i++)
95     for (int j = 0; cKeyShortcuts[i][j]; j++)
96       if (cKeyShortcuts[i][j] == key) return i * MAX_MENU_ENTRIES + j;
97   return -1;
98 }
key(int key,int shift,int,int)99 void EMenuWindow::key(int key, int shift, int /*x*/, int /*y*/) {
100   if (key >= SDLK_F1 && key <= SDLK_F12) {
101     int menu = key - SDLK_F1;
102     if (menu >= 0 && menu < N_SUBMENUS) openSubMenu(menu);
103   } else {
104     int command = keyToMenuEntry(key, shift);
105     if (command >= 0) EditMode::editMode->doCommand(command);
106   }
107 }
108 
ESubWindow(int id,int x,int y)109 ESubWindow::ESubWindow(int id, int x, int y) : MyWindow(x, y, 100, 0) {
110   fontSize = 16;
111 
112   this->id = id;
113   rows = countRows();
114   resize(180, fontSize * rows + 2);
115 }
116 
draw()117 void ESubWindow::draw() {
118   char str[256];
119 
120   this->MyWindow::draw();
121   for (int i = 0; i < rows; i++) {
122     if (cMenuEntries[id][i][0] == '*') {
123       snprintf(str, 255, "%s", cMenuEntries[id][i] + 1);
124       addText_Left(0, fontSize / 2, y + 2 + fontSize * i + fontSize / 2, str,
125                    x + 2 + fontSize / 2);
126     } else {
127       if (cMenuEntries[id][i][0] == '/')
128         snprintf(str, 255, "%s", cMenuEntries[id][i] + 1); /* Shortcut already part of name */
129       else
130         snprintf(str, 255, "%c %s", cKeyShortcuts[id][i], cMenuEntries[id][i]);
131 
132       addText_Left(CODE_FROM_MENUENTRY(id * MAX_MENU_ENTRIES + i), fontSize / 2,
133                    y + 2 + fontSize * i + fontSize / 2, str, x + 2 + fontSize / 2);
134       if (id == FLAGS_MENU && i < 9) {
135         /* The flags current status can only be added if we have a map loaded */
136         if (EditMode::editMode->map) {
137           Cell& cell =
138               EditMode::editMode->map->cell(EditMode::editMode->x, EditMode::editMode->y);
139           if (cell.flags & (1 << i) || (cell.flags & (1 << 11) && i == 9))
140             addText_Left(CODE_FROM_MENUENTRY(id * MAX_MENU_ENTRIES + i), fontSize / 2,
141                          y + 2 + fontSize * i + fontSize / 2, _("y"),
142                          x + 2 + fontSize / 2 + 160);
143           else
144             addText_Left(CODE_FROM_MENUENTRY(id * MAX_MENU_ENTRIES + i), fontSize / 2,
145                          y + 2 + fontSize * i + fontSize / 2, _("n"),
146                          x + 2 + fontSize / 2 + 160);
147         }
148       } else if (id == FLAGS_MENU && i == 10) {
149         /* The current texture can only be added if we have a map loaded */
150         if (EditMode::editMode->map) {
151           Cell& cell =
152               EditMode::editMode->map->cell(EditMode::editMode->x, EditMode::editMode->y);
153           snprintf(str, sizeof(str), "%d", cell.texture);
154           addText_Left(CODE_FROM_MENUENTRY(id * MAX_MENU_ENTRIES + i), fontSize / 2,
155                        y + 2 + fontSize * i + fontSize / 2, str, x + 2 + fontSize / 2 + 160);
156         }
157       }
158     }
159   }
160 }
161 
162 /** Count how many menu entries exists for a given menu. Uses the length of the shortcut key
163  * string for this */
countRows() const164 int ESubWindow::countRows() const {
165   for (int i = 0; i < MAX_MENU_ENTRIES; i++)
166     if (!cMenuEntries[id][i]) return i;
167   return MAX_MENU_ENTRIES;
168 }
169 
mouseDown(int,int,int)170 void ESubWindow::mouseDown(int /*state*/, int /*x*/, int /*y*/) {
171   int code = getSelectedArea();
172   int menuentry = CODE_TO_MENUENTRY(code);
173   if (menuentry >= id * MAX_MENU_ENTRIES && menuentry < (id + 1) * MAX_MENU_ENTRIES)
174     EditMode::editMode->doCommand(menuentry);
175 }
176 
EStatusWindow()177 EStatusWindow::EStatusWindow() : MyWindow(0, screenHeight - 110, screenWidth, 110) {}
178 
draw()179 void EStatusWindow::draw() {
180   int fontSize = 24;
181 
182   this->MyWindow::draw();
183   if (!EditMode::editMode->map) return;
184   int row1 = y + fontSize / 2 + 2;
185   int row2 = row1 + fontSize + 2;
186   int row3 = row2 + fontSize + 2;
187   int row4 = row3 + fontSize + 2;
188   int col0 = x + 2 + fontSize / 2;
189   int col1 = x + 2 + fontSize / 2 + fontSize * 10;
190   int area3x = x + 520;
191   char str[512];
192 
193   const char* editModeNames[6] = {_("height"),   _("color"), _("water"),
194                                   _("velocity"), _("lines"), _("feature")};
195 
196   snprintf(str, 255, _("Pos: %d,%d"), EditMode::editMode->x, EditMode::editMode->y);
197   addText_Left(0, fontSize / 2, row2, str, col0);
198   snprintf(str, 255, _("Increment: %3.2f"), EditMode::editMode->scale);
199   addText_Left(CODE_INCREMENT, fontSize / 2, row3, str, col0);
200   snprintf(str, 255, _("Edit: %s"), editModeNames[EditMode::editMode->currentEditMode]);
201   addText_Left(CODE_EDITMODE, fontSize / 2, row4, str, col0);
202 
203   /* Small separator between area 1 - 2 */
204   draw2DRectangle(col1 - fontSize - 1, y, 2, height, 0., 0., 1., 1., 0.5, 0.5, 0.5, 1.0);
205   /* Small separator between area 2 - 3 */
206   draw2DRectangle(area3x - 1, y, 2, height, 0., 0., 1., 1., 0.5, 0.5, 0.5, 1.0);
207 
208   /* TODO. Make the height/colour etc. text selectable areas
209          with the same effect as corresponding menu choice */
210 
211   Cell& cell = EditMode::editMode->map->cell(EditMode::editMode->x, EditMode::editMode->y);
212 
213   if (EditMode::editMode->currentEditMode == EDITMODE_HEIGHT) {
214     addText_Left(0, fontSize / 2, row1, _("Heights"), col1 + fontSize * 1);
215     snprintf(str, sizeof(str), "%2.1f", cell.heights[3]);
216     addText_Left(CODE_FROM_MENUENTRY(EDIT_UPPER), fontSize / 2, row2, str,
217                  col1 + fontSize * 2);
218     snprintf(str, sizeof(str), "%2.1f", cell.heights[1]);
219     addText_Left(CODE_FROM_MENUENTRY(EDIT_LEFT), fontSize / 2, row3, str, col1 + fontSize * 0);
220     snprintf(str, sizeof(str), "%2.1f", cell.heights[Cell::CENTER]);
221     addText_Left(CODE_FROM_MENUENTRY(EDIT_CENTER), fontSize / 2, row3, str,
222                  col1 + fontSize * 2);
223     snprintf(str, sizeof(str), "%2.1f", cell.heights[2]);
224     addText_Left(CODE_FROM_MENUENTRY(EDIT_RIGHT), fontSize / 2, row3, str,
225                  col1 + fontSize * 4);
226     snprintf(str, sizeof(str), "%2.1f", cell.heights[0]);
227     addText_Left(CODE_FROM_MENUENTRY(EDIT_BOTTOM), fontSize / 2, row4, str,
228                  col1 + fontSize * 2);
229   } else if (EditMode::editMode->currentEditMode == EDITMODE_WATER) {
230     addText_Left(0, fontSize / 2, row1, _("Water heights"), col1 + fontSize * 1);
231     snprintf(str, sizeof(str), "%2.1f", cell.waterHeights[3]);
232     addText_Left(CODE_FROM_MENUENTRY(EDIT_UPPER), fontSize / 2, row2, str,
233                  col1 + fontSize * 2);
234     snprintf(str, sizeof(str), "%2.1f", cell.waterHeights[1]);
235     addText_Left(CODE_FROM_MENUENTRY(EDIT_LEFT), fontSize / 2, row3, str, col1 + fontSize * 0);
236     snprintf(str, sizeof(str), "%2.1f", cell.waterHeights[Cell::CENTER]);
237     addText_Left(CODE_FROM_MENUENTRY(EDIT_CENTER), fontSize / 2, row3, str,
238                  col1 + fontSize * 2);
239     snprintf(str, sizeof(str), "%2.1f", cell.waterHeights[2]);
240     addText_Left(CODE_FROM_MENUENTRY(EDIT_RIGHT), fontSize / 2, row3, str,
241                  col1 + fontSize * 4);
242     snprintf(str, sizeof(str), "%2.1f", cell.waterHeights[0]);
243     addText_Left(CODE_FROM_MENUENTRY(EDIT_BOTTOM), fontSize / 2, row4, str,
244                  col1 + fontSize * 2);
245   } else if (EditMode::editMode->currentEditMode == EDITMODE_COLOR) {
246     snprintf(str, 255, _("red: %2.2f"), EditMode::editMode->color.f0());
247     addText_Left(CODE_FROM_MENUENTRY(COLOR_RED), fontSize / 2, row2, str, col1 + fontSize * 3);
248     snprintf(str, 255, _("green: %2.2f"), EditMode::editMode->color.f1());
249     addText_Left(CODE_FROM_MENUENTRY(COLOR_GREEN), fontSize / 2, row3, str,
250                  col1 + fontSize * 3);
251     snprintf(str, 255, _("blue: %2.2f"), EditMode::editMode->color.f2());
252     addText_Left(CODE_FROM_MENUENTRY(COLOR_BLUE), fontSize / 2, row4, str,
253                  col1 + fontSize * 3);
254     snprintf(str, 255, _("alpha: %2.2f"), EditMode::editMode->color.f3());
255     addText_Left(CODE_FROM_MENUENTRY(COLOR_ALPHA), fontSize / 2, row1, str,
256                  col1 + fontSize * 3);
257 
258     draw2DRectangle(col1, row2, 2 * fontSize, row4 - row2, 0., 0., 1., 1.,
259                     EditMode::editMode->color.f0(), EditMode::editMode->color.f1(),
260                     EditMode::editMode->color.f2(), EditMode::editMode->color.f3());
261   } else if (EditMode::editMode->currentEditMode == EDITMODE_VELOCITY) {
262     Cell& cell = EditMode::editMode->map->cell(EditMode::editMode->x, EditMode::editMode->y);
263     snprintf(str, 255, _("dx: %2.2f"), cell.velocity[0]);
264     addText_Left(0, fontSize / 2, row1, str, col1 + fontSize * 1);
265     snprintf(str, 255, _("dy: %2.2f"), cell.velocity[1]);
266     addText_Left(0, fontSize / 2, row1, str, col1 + fontSize * 7);
267   } else if (EditMode::editMode->currentEditMode == EDITMODE_NOLINES) {
268     addText_Left(0, fontSize / 2, row1, _("Lines"), col1);
269 
270     GLfloat line_off[4][4] = {
271         {1., 1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1.}};
272     GLfloat line_on[4][4] = {
273         {0., 0., 0., 1.}, {0., 0., 0., 1.}, {0., 0., 0., 1.}, {0., 0., 0., 1.}};
274     GLfloat txco[4][2] = {{0., 0.}, {0., 0.}, {0., 0.}, {0., 0.}};
275 
276     GLfloat r = 1.5;
277     GLfloat lineA[4][2] = {{col1 + 0.f, row3 - r},
278                            {col1 + 0.f, row3 + r},
279                            {col1 + fontSize * 1.5f, row2 - r},
280                            {col1 + fontSize * 1.5f, row2 + r}};
281     draw2DQuad(lineA, txco, (cell.flags & CELL_NOLINENORTH) ? line_off : line_on);
282 
283     GLfloat lineB[4][2] = {{col1 + fontSize * 3.f, row3 - r},
284                            {col1 + fontSize * 3.f, row3 + r},
285                            {col1 + fontSize * 1.5f, row2 - r},
286                            {col1 + fontSize * 1.5f, row2 + r}};
287     draw2DQuad(lineB, txco, (cell.flags & CELL_NOLINEEAST) ? line_off : line_on);
288 
289     GLfloat lineC[4][2] = {{col1 + fontSize * 3.f, row3 - r},
290                            {col1 + fontSize * 3.f, row3 + r},
291                            {col1 + fontSize * 1.5f, row4 - r},
292                            {col1 + fontSize * 1.5f, row4 + r}};
293     draw2DQuad(lineC, txco, (cell.flags & CELL_NOLINESOUTH) ? line_off : line_on);
294 
295     GLfloat lineD[4][2] = {{col1 + 0.f, row3 - r},
296                            {col1 + 0.f, row3 + r},
297                            {col1 + fontSize * 1.5f, row4 - r},
298                            {col1 + fontSize * 1.5f, row4 + r}};
299     draw2DQuad(lineD, txco, (cell.flags & CELL_NOLINEWEST) ? line_off : line_on);
300   } else if (EditMode::editMode->currentEditMode == EDITMODE_FEATURES) {
301     const char* feature = "";
302     switch (EditMode::editMode->currentFeature) {
303     case FEATURE_SPIKE:
304       feature = _("spike");
305       break;
306     case FEATURE_SMALL_HILL:
307       feature = _("small hill");
308       break;
309     case FEATURE_MEDIUM_HILL:
310       feature = _("medium hill");
311       break;
312     case FEATURE_LARGE_HILL:
313       feature = _("large hill");
314       break;
315     case FEATURE_HUGE_HILL:
316       feature = _("huge hill");
317       break;
318     case FEATURE_SMALL_SMOOTH:
319       feature = _("small smooth");
320       break;
321     case FEATURE_LARGE_SMOOTH:
322       feature = _("large smooth");
323       break;
324     }
325     addText_Center(0, fontSize / 2, row1, feature, (col1 + area3x) / 2);
326   }
327 
328   /* Draw area 3, eg. some extra info */
329   snprintf(str, sizeof(str), "%s/%s", EditMode::editMode->pathname,
330            EditMode::editMode->levelname);
331   addText_Left(0, fontSize / 3, row1, str, area3x + 10);
332 }
mouseDown(int button,int,int)333 void EStatusWindow::mouseDown(int button, int /*x*/, int /*y*/) {
334   int code = getSelectedArea();
335   if (code == CODE_INCREMENT) {
336     if (button == SDL_BUTTON_LEFT)
337       EditMode::editMode->doCommand(EDIT_RAISE_INCREMENT);
338     else
339       EditMode::editMode->doCommand(EDIT_LOWER_INCREMENT);
340   }
341   if (code == CODE_EDITMODE)
342     EditMode::editMode->currentEditMode =
343         (EditMode::editMode->currentEditMode +
344          ((button == SDL_BUTTON_LEFT) ? 1 : N_EDITMODES - 1)) %
345         N_EDITMODES;
346 
347   if (CODE_TO_MENUENTRY(code) >= 0 && CODE_TO_MENUENTRY(code) <= 170)
348     EditMode::editMode->doCommand(CODE_TO_MENUENTRY(code));
349 }
350 
351 /***********************************
352             QuitWindow
353 ***********************************/
354 
EQuitWindow()355 EQuitWindow::EQuitWindow() : MyWindow(0, 0, 300, 80) {}
draw()356 void EQuitWindow::draw() {
357   int fontSize = 24;
358   this->MyWindow::draw();
359   int row1 = y + fontSize + 2;
360   int row2 = row1 + fontSize + 2;
361 
362   addText_Center(0, fontSize / 2, row1, _("Quit without saving?"), x + width / 2);
363   addText_Center(CODE_YES, fontSize / 2, row2, _("Yes"), x + fontSize * 5);
364   addText_Center(CODE_NO, fontSize / 2, row2, _("No"), x + width - fontSize * 5);
365 }
mouseDown(int,int,int)366 void EQuitWindow::mouseDown(int /*state*/, int /*x*/, int /*y*/) {
367   int code = getSelectedArea();
368   if (code == CODE_YES)
369     yes();
370   else if (code == CODE_NO)
371     no();
372 }
373 
yes()374 void EQuitWindow::yes() {
375   remove();
376   EditMode::editMode->closeMap();
377   Settings::settings->doSpecialLevel = 0;
378   GameMode::activate(MenuMode::init());
379 }
380 
no()381 void EQuitWindow::no() { remove(); }
382 
383 /***********************************
384             SaveWindow
385 ***********************************/
386 
387 /* The private variable saveCnt is used to delay the save action
388    until we have drawing the text "saving..." on the screen. The actual
389    saving will be done during the drawing cycle when saveCnt reaches 1 */
ESaveWindow()390 ESaveWindow::ESaveWindow() : MyWindow(0, 0, 400, 80) { saveCnt = 0; }
draw()391 void ESaveWindow::draw() {
392   int fontSize = 24;
393   this->MyWindow::draw();
394   int row1 = y + fontSize + 2;
395   int row2 = row1 + fontSize + 2;
396   char str[512];
397 
398   if (!saveCnt) {
399     addText_Center(0, fontSize / 2, row1, _("Save map?"), x + width / 2);
400     addText_Center(CODE_YES, fontSize / 2, row2, _("Yes"), x + fontSize * 5);
401     addText_Center(CODE_NO, fontSize / 2, row2, _("No"), x + width - fontSize * 5);
402   } else if (saveCnt == 2) {
403     addText_Center(0, fontSize / 2, row1, _("Saving"), x + width / 2);
404     snprintf(str, sizeof(str), "%s/levels/%s", effectiveLocalDir,
405              EditMode::editMode->levelname);
406     // addText_Center(0,fontSize/2,row2,str,x+width/2);
407     addText_Center(0, fontSize / 3, row2, str, x + width / 2);
408     saveCnt = 1;
409   } else if (saveCnt == 1) {
410     struct timespec t0 = getMonotonicTime();
411     EditMode::editMode->saveMap();
412     remove();
413     saveCnt = 0;
414     /* Make sure it takes atleast three seconds to save, so user can see the message above
415      * properly */
416     struct timespec t1;
417     do {
418       t1 = getMonotonicTime();
419       SDL_Delay(10);
420     } while (getTimeDifference(t0, t1) < 3.0);
421   }
422 }
mouseDown(int,int,int)423 void ESaveWindow::mouseDown(int /*state*/, int /*x*/, int /*y*/) {
424   int code = getSelectedArea();
425   if (code == CODE_YES)
426     yes();
427   else if (code == CODE_NO)
428     no();
429 }
430 
yes()431 void ESaveWindow::yes() { saveCnt = 2; }
432 
no()433 void ESaveWindow::no() { remove(); }
434 
435 /***********************************
436             CloseWindow
437 ***********************************/
438 
ECloseWindow()439 ECloseWindow::ECloseWindow() : MyWindow(0, 0, 300, 80) {}
draw()440 void ECloseWindow::draw() {
441   int fontSize = 24;
442   this->MyWindow::draw();
443   int row1 = y + fontSize + 2;
444   int row2 = row1 + fontSize + 2;
445 
446   addText_Center(0, fontSize / 2, row1, _("Close without saving?"), x + width / 2);
447   addText_Center(CODE_YES, fontSize / 2, row2, _("Yes"), x + fontSize * 5);
448   addText_Center(CODE_NO, fontSize / 2, row2, _("No"), x + width - fontSize * 5);
449 }
mouseDown(int,int,int)450 void ECloseWindow::mouseDown(int /*state*/, int /*x*/, int /*y*/) {
451   int code = getSelectedArea();
452   if (code == CODE_YES)
453     yes();
454   else if (code == CODE_NO)
455     no();
456 }
457 
yes()458 void ECloseWindow::yes() {
459   remove();
460   EditMode::editMode->closeMap();
461 }
462 
no()463 void ECloseWindow::no() { remove(); }
464 
465 /***********************************
466             NewWindow
467 ***********************************/
468 
ENewWindow()469 ENewWindow::ENewWindow() : MyWindow(0, 0, 300, 100) { name[0] = 0; }
draw()470 void ENewWindow::draw() {
471   int fontSize = 24;
472   this->MyWindow::draw();
473   int row1 = y + fontSize + 2;
474   int row2 = row1 + fontSize + 2;
475   int row3 = row1 + fontSize * 2 + 2;
476 
477   addText_Center(0, fontSize / 2, row1, _("Name of map"), x + width / 2);
478   addText_Center(0, fontSize / 2, row2, name, x + width / 2);
479   addText_Left(CODE_CANCEL, fontSize / 2, row3, _("Cancel"), x + fontSize / 2 + 2 + 10);
480   addText_Right(CODE_OK, fontSize / 2, row3, _("Open"), x + width - fontSize / 2 - 2);
481 }
mouseDown(int,int,int)482 void ENewWindow::mouseDown(int /*state*/, int /*x*/, int /*y*/) {
483   int code = getSelectedArea();
484   if (code == CODE_CANCEL)
485     remove();
486   else if (code == CODE_OK) {
487     EditMode::editMode->loadMap(name);
488     remove();
489   }
490 }
key(int key,int shift,int,int)491 void ENewWindow::key(int key, int shift, int /*x*/, int /*y*/) {
492   if (key == SDLK_BACKSPACE) {
493     int len = strlen(name);
494     if (len > 0) name[len - 1] = 0;
495   } else if ((key < 128 && isalnum(key)) || key == ' ' || key == '_' || key == '-') {
496     if (shift && islower(key)) key = toupper(key);
497     int len = strlen(name);
498     if (len >= 255) return;
499     name[len++] = key;
500     name[len] = 0;
501   }
502 }
503 
504 /***********************************
505             OpenWindow
506 ***********************************/
507 #define PAGE_SIZE 8
508 
EOpenWindow()509 EOpenWindow::EOpenWindow() : MyWindow(0, 0, 400, 300) {
510   nNames = 0;
511   currPage = 0;
512   memset(names, 0, sizeof(names));
513 }
draw()514 void EOpenWindow::draw() {
515   int fontSize = 24;
516   this->MyWindow::draw();
517 
518   int nPages = (nNames + PAGE_SIZE - 1) / PAGE_SIZE;
519   int row1 = y + fontSize + 2;
520   int rowN = y + 300 - fontSize;
521   int col0 = x + 2 + fontSize / 2;
522   int colN = x + 400 - fontSize / 2;
523   char str[256];
524 
525   addText_Center(0, fontSize / 2, row1, _("Open map"), x + width / 2);
526   for (int i = 0; i < PAGE_SIZE; i++) {
527     int n = i + currPage * PAGE_SIZE;
528     if (n < nNames)
529       addText_Left(CODE_MAP0 + n, fontSize / 2, row1 + fontSize * (i + 1), names[n], col0);
530   }
531 
532   snprintf(str, sizeof(str), _("page %d / %d"), currPage + 1, nPages);
533   addText_Left(CODE_PAGE, fontSize / 2, rowN, str, col0);
534   addText_Right(CODE_CANCEL, fontSize / 2, rowN, _("Cancel"), colN);
535 }
mouseDown(int button,int,int)536 void EOpenWindow::mouseDown(int button, int /*x*/, int /*y*/) {
537   int code = getSelectedArea();
538   int nPages = (nNames + PAGE_SIZE - 1) / PAGE_SIZE;
539 
540   if (code >= CODE_MAP0 && code <= CODE_MAP0 + nNames) {
541     /* A map has been selected */
542     int mapNumber = code - CODE_MAP0;
543     EditMode::editMode->loadMap(names[mapNumber]);
544     remove();
545   } else
546     switch (code) {
547     case CODE_CANCEL:
548       remove();
549       break;
550     case CODE_PAGE:
551       if (button == SDL_BUTTON_LEFT)
552         currPage = (currPage + 1) % nPages;
553       else
554         currPage = (currPage + nPages - 1) % nPages;
555       break;
556     }
557 }
558 
sortstrcmp(const void * n1,const void * n2)559 int sortstrcmp(const void* n1, const void* n2) { return strcmp((char*)n1, (char*)n2); }
560 
refreshMapList()561 void EOpenWindow::refreshMapList() {
562   char str[512];
563   struct dirent* dirent;
564   DIR* dir;
565 
566   nNames = 0;
567   currPage = 0;
568 
569   /* Add all maps from the home directory */
570   snprintf(str, sizeof(str), "%s/levels", effectiveLocalDir);
571   dir = opendir(str);
572   if (dir) {
573     /* This iteratives over all files there */
574     while ((dirent = readdir(dir))) {
575       /* And adds the ones ending with .map */
576       if (strlen(dirent->d_name) > 4 &&
577           (strcmp(&dirent->d_name[strlen(dirent->d_name) - 4], ".map") == 0)) {
578         strncpy(str, dirent->d_name, sizeof(str));
579         str[strlen(str) - 4] = 0; /* Remove .map ending */
580         strncpy(names[nNames++], str, 256);
581       }
582     }
583     closedir(dir);
584   }
585   qsort(names, nNames, sizeof(char[256]), sortstrcmp);
586   int divNames = nNames;
587 
588   /* Add all maps from the share directory */
589   snprintf(str, sizeof(str), "%s/levels", effectiveShareDir);
590   dir = opendir(str);
591   if (dir) {
592     /* This iteratives over all files there */
593     while ((dirent = readdir(dir))) {
594       /* And adds the ones ending with .map *but* not if they already where added from the home
595        * directory */
596       if (strlen(dirent->d_name) > 4 &&
597           (strcmp(&dirent->d_name[strlen(dirent->d_name) - 4], ".map") == 0)) {
598         strncpy(str, dirent->d_name, sizeof(str));
599         str[strlen(str) - 4] = 0; /* Remove .map ending */
600         int i;
601         for (i = 0; i < nNames; i++)
602           if (strncasecmp(str, names[i], 256) == 0) break;
603         if (i == nNames) strncpy(names[nNames++], str, 256);
604       }
605     }
606     closedir(dir);
607   }
608   qsort(&names[divNames][0], nNames - divNames, sizeof(char[256]), sortstrcmp);
609 }
610