+ EL_CUSTOM_START + 200,
+ EL_CUSTOM_START + 201,
+ EL_CUSTOM_START + 202,
+ EL_CUSTOM_START + 203,
+
+ EL_CUSTOM_START + 204,
+ EL_CUSTOM_START + 205,
+ EL_CUSTOM_START + 206,
+ EL_CUSTOM_START + 207,
+
+ EL_CUSTOM_START + 208,
+ EL_CUSTOM_START + 209,
+ EL_CUSTOM_START + 210,
+ EL_CUSTOM_START + 211,
+
+ EL_CUSTOM_START + 212,
+ EL_CUSTOM_START + 213,
+ EL_CUSTOM_START + 214,
+ EL_CUSTOM_START + 215,
+
+ EL_CUSTOM_START + 216,
+ EL_CUSTOM_START + 217,
+ EL_CUSTOM_START + 218,
+ EL_CUSTOM_START + 219,
+
+ EL_CUSTOM_START + 220,
+ EL_CUSTOM_START + 221,
+ EL_CUSTOM_START + 222,
+ EL_CUSTOM_START + 223,
+
+ EL_CUSTOM_START + 224,
+ EL_CUSTOM_START + 225,
+ EL_CUSTOM_START + 226,
+ EL_CUSTOM_START + 227,
+
+ EL_CUSTOM_START + 228,
+ EL_CUSTOM_START + 229,
+ EL_CUSTOM_START + 230,
+ EL_CUSTOM_START + 231,
+
+ EL_CUSTOM_START + 232,
+ EL_CUSTOM_START + 233,
+ EL_CUSTOM_START + 234,
+ EL_CUSTOM_START + 235,
+
+ EL_CUSTOM_START + 236,
+ EL_CUSTOM_START + 237,
+ EL_CUSTOM_START + 238,
+ EL_CUSTOM_START + 239,
+
+ EL_CUSTOM_START + 240,
+ EL_CUSTOM_START + 241,
+ EL_CUSTOM_START + 242,
+ EL_CUSTOM_START + 243,
+
+ EL_CUSTOM_START + 244,
+ EL_CUSTOM_START + 245,
+ EL_CUSTOM_START + 246,
+ EL_CUSTOM_START + 247,
+
+ EL_CUSTOM_START + 248,
+ EL_CUSTOM_START + 249,
+ EL_CUSTOM_START + 250,
+ EL_CUSTOM_START + 251,
+
+ EL_CUSTOM_START + 252,
+ EL_CUSTOM_START + 253,
+ EL_CUSTOM_START + 254,
+ EL_CUSTOM_START + 255
+};
+static int *editor_hl_custom_ptr = editor_hl_custom;
+static int *editor_el_custom_ptr = editor_el_custom;
+static int num_editor_hl_custom = ARRAY_SIZE(editor_hl_custom);
+static int num_editor_el_custom = ARRAY_SIZE(editor_el_custom);
+
+static int editor_hl_group[] =
+{
+ EL_INTERNAL_CASCADE_GE_ACTIVE,
+ EL_CHAR('G'),
+ EL_CHAR('E'),
+ EL_EMPTY,
+};
+
+static int editor_el_group[] =
+{
+ EL_GROUP_START + 0,
+ EL_GROUP_START + 1,
+ EL_GROUP_START + 2,
+ EL_GROUP_START + 3,
+
+ EL_GROUP_START + 4,
+ EL_GROUP_START + 5,
+ EL_GROUP_START + 6,
+ EL_GROUP_START + 7,
+
+ EL_GROUP_START + 8,
+ EL_GROUP_START + 9,
+ EL_GROUP_START + 10,
+ EL_GROUP_START + 11,
+
+ EL_GROUP_START + 12,
+ EL_GROUP_START + 13,
+ EL_GROUP_START + 14,
+ EL_GROUP_START + 15,
+
+ EL_GROUP_START + 16,
+ EL_GROUP_START + 17,
+ EL_GROUP_START + 18,
+ EL_GROUP_START + 19,
+
+ EL_GROUP_START + 20,
+ EL_GROUP_START + 21,
+ EL_GROUP_START + 22,
+ EL_GROUP_START + 23,
+
+ EL_GROUP_START + 24,
+ EL_GROUP_START + 25,
+ EL_GROUP_START + 26,
+ EL_GROUP_START + 27,
+
+ EL_GROUP_START + 28,
+ EL_GROUP_START + 29,
+ EL_GROUP_START + 30,
+ EL_GROUP_START + 31
+};
+static int *editor_hl_group_ptr = editor_hl_group;
+static int *editor_el_group_ptr = editor_el_group;
+static int num_editor_hl_group = ARRAY_SIZE(editor_hl_group);
+static int num_editor_el_group = ARRAY_SIZE(editor_el_group);
+
+static int editor_hl_empty_space[] =
+{
+ EL_INTERNAL_CASCADE_ES_ACTIVE,
+ EL_CHAR('E'),
+ EL_CHAR('S'),
+ EL_EMPTY,
+};
+
+static int editor_el_empty_space[] =
+{
+ EL_EMPTY_SPACE_1,
+ EL_EMPTY_SPACE_2,
+ EL_EMPTY_SPACE_3,
+ EL_EMPTY_SPACE_4,
+
+ EL_EMPTY_SPACE_5,
+ EL_EMPTY_SPACE_6,
+ EL_EMPTY_SPACE_7,
+ EL_EMPTY_SPACE_8,
+
+ EL_EMPTY_SPACE_9,
+ EL_EMPTY_SPACE_10,
+ EL_EMPTY_SPACE_11,
+ EL_EMPTY_SPACE_12,
+
+ EL_EMPTY_SPACE_13,
+ EL_EMPTY_SPACE_14,
+ EL_EMPTY_SPACE_15,
+ EL_EMPTY_SPACE_16
+};
+static int *editor_hl_empty_space_ptr = editor_hl_empty_space;
+static int *editor_el_empty_space_ptr = editor_el_empty_space;
+static int num_editor_hl_empty_space = ARRAY_SIZE(editor_hl_empty_space);
+static int num_editor_el_empty_space = ARRAY_SIZE(editor_el_empty_space);
+
+static int editor_hl_reference[] =
+{
+ EL_INTERNAL_CASCADE_REF_ACTIVE,
+ EL_CHAR('R'),
+ EL_CHAR('E'),
+ EL_CHAR('F')
+};
+
+static int editor_el_reference[] =
+{
+ EL_TRIGGER_PLAYER,
+ EL_TRIGGER_ELEMENT,
+ EL_TRIGGER_CE_VALUE,
+ EL_TRIGGER_CE_SCORE,
+
+ EL_SELF,
+ EL_ANY_ELEMENT,
+ EL_CURRENT_CE_VALUE,
+ EL_CURRENT_CE_SCORE,
+
+ EL_PREV_CE_8,
+ EL_PREV_CE_7,
+ EL_PREV_CE_6,
+ EL_PREV_CE_5,
+
+ EL_PREV_CE_4,
+ EL_PREV_CE_3,
+ EL_PREV_CE_2,
+ EL_PREV_CE_1,
+
+ EL_NEXT_CE_1,
+ EL_NEXT_CE_2,
+ EL_NEXT_CE_3,
+ EL_NEXT_CE_4,
+
+ EL_NEXT_CE_5,
+ EL_NEXT_CE_6,
+ EL_NEXT_CE_7,
+ EL_NEXT_CE_8,
+};
+static int *editor_hl_reference_ptr = editor_hl_reference;
+static int *editor_el_reference_ptr = editor_el_reference;
+static int num_editor_hl_reference = ARRAY_SIZE(editor_hl_reference);
+static int num_editor_el_reference = ARRAY_SIZE(editor_el_reference);
+
+static int editor_hl_user_defined[] =
+{
+ EL_INTERNAL_CASCADE_USER_ACTIVE,
+ EL_CHAR('M'),
+ EL_CHAR('Y'),
+ EL_EMPTY,
+};
+
+static int *editor_hl_user_defined_ptr = editor_hl_user_defined;
+static int *editor_el_user_defined_ptr = NULL;
+static int num_editor_hl_user_defined = ARRAY_SIZE(editor_hl_user_defined);
+static int num_editor_el_user_defined = 0;
+
+static int editor_hl_dynamic[] =
+{
+ EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
+ EL_CHAR('U'),
+ EL_CHAR('S'),
+ EL_CHAR('E'),
+};
+
+static int *editor_hl_dynamic_ptr = editor_hl_dynamic;
+static int *editor_el_dynamic_ptr = NULL;
+static int num_editor_hl_dynamic = ARRAY_SIZE(editor_hl_dynamic);
+static int num_editor_el_dynamic = 0;
+
+static int editor_hl_empty[] = { EL_EMPTY };
+static int *editor_el_empty = NULL; // dynamically allocated
+
+static int *editor_hl_empty_ptr = editor_hl_empty;
+static int *editor_el_empty_ptr = NULL;
+static int num_editor_hl_empty = 0;
+static int num_editor_el_empty = 0; // dynamically determined, if needed
+
+static boolean use_el_empty = FALSE;
+
+static int *editor_elements = NULL; // dynamically allocated
+static int num_editor_elements = 0; // dynamically determined
+
+static boolean setup_editor_cascade_never = FALSE;
+
+static boolean setup_editor_el_players = TRUE;
+static boolean setup_editor_el_boulderdash = TRUE;
+static boolean setup_editor_el_boulderdash_native = TRUE;
+static boolean setup_editor_el_boulderdash_effects = TRUE;
+static boolean setup_editor_el_emerald_mine = TRUE;
+static boolean setup_editor_el_emerald_mine_club = TRUE;
+static boolean setup_editor_el_more = TRUE;
+static boolean setup_editor_el_sokoban = TRUE;
+static boolean setup_editor_el_supaplex = TRUE;
+static boolean setup_editor_el_diamond_caves = TRUE;
+static boolean setup_editor_el_dx_boulderdash = TRUE;
+static boolean setup_editor_el_mirror_magic = TRUE;
+static boolean setup_editor_el_deflektor = TRUE;
+static boolean setup_editor_el_chars = TRUE;
+static boolean setup_editor_el_steel_chars = TRUE;
+static boolean setup_editor_el_custom = TRUE;
+static boolean setup_editor_el_user_defined = TRUE;
+static boolean setup_editor_el_dynamic = TRUE;
+
+static int editor_hl_unused[] = { EL_EMPTY };
+static int *editor_hl_unused_ptr = editor_hl_unused;
+static int num_editor_hl_unused = 0;
+
+static struct
+{
+ boolean *setup_value;
+ boolean *setup_cascade_value;
+
+ int **headline_list;
+ int *headline_list_size;
+
+ int **element_list;
+ int *element_list_size;
+
+ boolean last_setup_value;
+}
+editor_elements_info[] =
+{
+ {
+ &setup_editor_el_players,
+ &setup_editor_cascade_never,
+ &editor_hl_unused_ptr, &num_editor_hl_unused,
+ &editor_el_players_ptr, &num_editor_el_players
+ },
+ {
+ &setup_editor_el_boulderdash,
+ &setup.editor_cascade.el_bd,
+ &editor_hl_boulderdash_ptr, &num_editor_hl_boulderdash,
+ &editor_el_boulderdash_ptr, &num_editor_el_boulderdash
+ },
+ {
+ &setup_editor_el_boulderdash_native,
+ &setup.editor_cascade.el_bd_native,
+ &editor_hl_boulderdash_native_ptr, &num_editor_hl_boulderdash_native,
+ &editor_el_boulderdash_native_ptr, &num_editor_el_boulderdash_native
+ },
+ {
+ &setup_editor_el_boulderdash_effects,
+ &setup.editor_cascade.el_bd_effects,
+ &editor_hl_boulderdash_effects_ptr, &num_editor_hl_boulderdash_effects,
+ &editor_el_boulderdash_effects_ptr, &num_editor_el_boulderdash_effects
+ },
+ {
+ &setup_editor_el_emerald_mine,
+ &setup.editor_cascade.el_em,
+ &editor_hl_emerald_mine_ptr, &num_editor_hl_emerald_mine,
+ &editor_el_emerald_mine_ptr, &num_editor_el_emerald_mine
+ },
+ {
+ &setup_editor_el_emerald_mine_club,
+ &setup.editor_cascade.el_emc,
+ &editor_hl_emerald_mine_club_ptr, &num_editor_hl_emerald_mine_club,
+ &editor_el_emerald_mine_club_ptr, &num_editor_el_emerald_mine_club
+ },
+ {
+ &setup_editor_el_more,
+ &setup.editor_cascade.el_rnd,
+ &editor_hl_rnd_ptr, &num_editor_hl_rnd,
+ &editor_el_rnd_ptr, &num_editor_el_rnd
+ },
+ {
+ &setup_editor_el_sokoban,
+ &setup.editor_cascade.el_sb,
+ &editor_hl_sokoban_ptr, &num_editor_hl_sokoban,
+ &editor_el_sokoban_ptr, &num_editor_el_sokoban
+ },
+ {
+ &setup_editor_el_supaplex,
+ &setup.editor_cascade.el_sp,
+ &editor_hl_supaplex_ptr, &num_editor_hl_supaplex,
+ &editor_el_supaplex_ptr, &num_editor_el_supaplex
+ },
+ {
+ &setup_editor_el_diamond_caves,
+ &setup.editor_cascade.el_dc,
+ &editor_hl_diamond_caves_ptr, &num_editor_hl_diamond_caves,
+ &editor_el_diamond_caves_ptr, &num_editor_el_diamond_caves
+ },
+ {
+ &setup_editor_el_dx_boulderdash,
+ &setup.editor_cascade.el_dx,
+ &editor_hl_dx_boulderdash_ptr, &num_editor_hl_dx_boulderdash,
+ &editor_el_dx_boulderdash_ptr, &num_editor_el_dx_boulderdash
+ },
+ {
+ &setup_editor_el_mirror_magic,
+ &setup.editor_cascade.el_mm,
+ &editor_hl_mirror_magic_ptr, &num_editor_hl_mirror_magic,
+ &editor_el_mirror_magic_ptr, &num_editor_el_mirror_magic
+ },
+ {
+ &setup_editor_el_deflektor,
+ &setup.editor_cascade.el_df,
+ &editor_hl_deflektor_ptr, &num_editor_hl_deflektor,
+ &editor_el_deflektor_ptr, &num_editor_el_deflektor
+ },
+ {
+ &setup_editor_el_chars,
+ &setup.editor_cascade.el_chars,
+ &editor_hl_chars_ptr, &num_editor_hl_chars,
+ &editor_el_chars_ptr, &num_editor_el_chars
+ },
+ {
+ &setup_editor_el_steel_chars,
+ &setup.editor_cascade.el_steel_chars,
+ &editor_hl_steel_chars_ptr, &num_editor_hl_steel_chars,
+ &editor_el_steel_chars_ptr, &num_editor_el_steel_chars
+ },
+ {
+ &setup_editor_el_custom,
+ &setup.editor_cascade.el_ce,
+ &editor_hl_custom_ptr, &num_editor_hl_custom,
+ &editor_el_custom_ptr, &num_editor_el_custom
+ },
+ {
+ &setup_editor_el_custom,
+ &setup.editor_cascade.el_ge,
+ &editor_hl_group_ptr, &num_editor_hl_group,
+ &editor_el_group_ptr, &num_editor_el_group
+ },
+ {
+ &setup_editor_el_custom,
+ &setup.editor_cascade.el_es,
+ &editor_hl_empty_space_ptr, &num_editor_hl_empty_space,
+ &editor_el_empty_space_ptr, &num_editor_el_empty_space
+ },
+ {
+ &setup_editor_el_custom,
+ &setup.editor_cascade.el_ref,
+ &editor_hl_reference_ptr, &num_editor_hl_reference,
+ &editor_el_reference_ptr, &num_editor_el_reference
+ },
+ {
+ &setup_editor_el_user_defined,
+ &setup.editor_cascade.el_user,
+ &editor_hl_user_defined_ptr, &num_editor_hl_user_defined,
+ &editor_el_user_defined_ptr, &num_editor_el_user_defined
+ },
+ {
+ &setup_editor_el_dynamic,
+ &setup.editor_cascade.el_dynamic,
+ &editor_hl_dynamic_ptr, &num_editor_hl_dynamic,
+ &editor_el_dynamic_ptr, &num_editor_el_dynamic,
+ },
+ {
+ &use_el_empty,
+ &use_el_empty,
+ &editor_hl_empty_ptr, &num_editor_hl_empty,
+ &editor_el_empty_ptr, &num_editor_el_empty,
+ },
+ {
+ NULL,
+ NULL,
+ NULL, NULL,
+ NULL, NULL
+ }
+};
+
+static struct XY xy_directions[] =
+{
+ { -1, 0 },
+ { +1, 0 },
+ { 0, -1 },
+ { 0, +1 }
+};
+
+
+// ----------------------------------------------------------------------------
+// functions
+// ----------------------------------------------------------------------------
+
+static int getMaxInfoTextLength(void)
+{
+ return (SXSIZE / getFontWidth(INFOTEXT_FONT));
+}
+
+static int getTextWidthForGadget(char *text)
+{
+ if (text == NULL)
+ return 0;
+
+ return (getTextWidth(text, FONT_TEXT_1) + ED_GADGET_TEXT_DISTANCE);
+}
+
+static int getTextWidthForDrawingArea(char *text)
+{
+ if (text == NULL)
+ return 0;
+
+ return (getTextWidth(text, FONT_TEXT_1) + ED_DRAWINGAREA_BORDER_SIZE);
+}
+
+static int getRightGadgetBorder(struct GadgetInfo *gi, char *text)
+{
+ return (gi->x + gi->width + getTextWidthForGadget(text));
+}
+
+static char *getElementInfoText(int element)
+{
+ char *info_text = NULL;
+
+ if (element < MAX_NUM_ELEMENTS)
+ {
+ if (strlen(element_info[element].description) > 0)
+ info_text = element_info[element].description;
+ else if (element_info[element].custom_description != NULL)
+ info_text = element_info[element].custom_description;
+ else if (element_info[element].editor_description != NULL)
+ info_text = element_info[element].editor_description;
+ }
+
+ if (info_text == NULL)
+ info_text = INFOTEXT_UNKNOWN_ELEMENT;
+
+ return info_text;
+}
+
+static char *getElementDescriptionFilenameExt(char *basename)
+{
+ char *elements_subdir = ELEMENTS_DIRECTORY;
+ static char *elements_subdir2 = NULL;
+ static char *filename = NULL;
+
+ if (elements_subdir2 == NULL)
+ elements_subdir2 = getPath2(DOCS_DIRECTORY, elements_subdir);
+
+ checked_free(filename);
+
+ // 1st try: look for element description in current level set directory
+ filename = getPath3(getCurrentLevelDir(), elements_subdir2, basename);
+ if (fileExists(filename))
+ return filename;
+
+ free(filename);
+
+ // 2nd try: look for element description in the game's base directory
+ filename = getPath3(options.docs_directory, elements_subdir, basename);
+ if (fileExists(filename))
+ return filename;
+
+ return NULL;
+}
+
+static char *getElementDescriptionFilename(int element)
+{
+ char basename[MAX_FILENAME_LEN];
+ char *filename;
+
+ // 1st try: look for element description file for exactly this element
+ sprintf(basename, "%s.txt", element_info[element].token_name);
+ filename = getElementDescriptionFilenameExt(basename);
+ if (filename != NULL)
+ return filename;
+
+ // 2nd try: look for element description file for this element's class
+ sprintf(basename, "%s.txt", element_info[element].class_name);
+ filename = getElementDescriptionFilenameExt(basename);
+ if (filename != NULL)
+ return filename;
+
+ // 3rd try: look for generic fallback text file for any element
+ filename = getElementDescriptionFilenameExt(FALLBACK_TEXT_FILENAME);
+ if (filename != NULL)
+ return filename;
+
+ return NULL;
+}
+
+static boolean suppressBorderElement(void)
+{
+ return (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
+ lev_fieldx <= MAX_ED_FIELDX &&
+ lev_fieldy <= MAX_ED_FIELDY);
+}
+
+static void InitDynamicEditorElementList(int **elements, int *num_elements)
+{
+ boolean element_found[NUM_FILE_ELEMENTS];
+ int i, x, y;
+
+ // initialize list of used elements to "not used"
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ element_found[i] = FALSE;
+
+ // find all elements used in current level
+ for (y = 0; y < lev_fieldy; y++)
+ {
+ for (x = 0; x < lev_fieldx; x++)
+ {
+ if (Tile[x][y] >= NUM_FILE_ELEMENTS) // should never happen
+ continue;
+
+ if (IS_MM_WALL(Tile[x][y]))
+ element_found[map_mm_wall_element(Tile[x][y])] = TRUE;
+ else
+ element_found[Tile[x][y]] = TRUE;
+ }
+ }
+
+ *num_elements = 0;
+
+ // count number of elements used in current level
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ if (element_found[i])
+ (*num_elements)++;
+
+ // add space for up to 3 more elements for padding that may be needed
+ *num_elements += 3;
+
+ // free memory for old list of elements, if needed
+ checked_free(*elements);
+
+ // allocate memory for new list of elements
+ *elements = checked_malloc(*num_elements * sizeof(int));
+
+ *num_elements = 0;
+
+ // add all elements used in current level (non-custom/group/empty elements)
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ if (element_found[i] && !(IS_CUSTOM_ELEMENT(i) ||
+ IS_GROUP_ELEMENT(i) ||
+ IS_EMPTY_ELEMENT(i)))
+ (*elements)[(*num_elements)++] = i;
+
+ // add all elements used in current level (custom/group/empty elements)
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ if (element_found[i] && (IS_CUSTOM_ELEMENT(i) ||
+ IS_GROUP_ELEMENT(i) ||
+ IS_EMPTY_ELEMENT(i)))
+ (*elements)[(*num_elements)++] = i;
+
+ while (*num_elements % 4) // pad with empty elements, if needed
+ (*elements)[(*num_elements)++] = EL_EMPTY;
+}
+
+static void ReinitializeElementList_EnableSections(void)
+{
+ // default: enable all element sections
+
+ setup_editor_el_players = TRUE;
+ setup_editor_el_boulderdash = TRUE;
+ setup_editor_el_boulderdash_native = TRUE;
+ setup_editor_el_boulderdash_effects = TRUE;
+ setup_editor_el_emerald_mine = TRUE;
+ setup_editor_el_emerald_mine_club = TRUE;
+ setup_editor_el_more = TRUE;
+ setup_editor_el_sokoban = TRUE;
+ setup_editor_el_supaplex = TRUE;
+ setup_editor_el_diamond_caves = TRUE;
+ setup_editor_el_dx_boulderdash = TRUE;
+ setup_editor_el_mirror_magic = TRUE;
+ setup_editor_el_deflektor = TRUE;
+ setup_editor_el_chars = TRUE;
+ setup_editor_el_steel_chars = TRUE;
+
+ setup_editor_el_custom = TRUE;
+ setup_editor_el_user_defined = TRUE;
+ setup_editor_el_dynamic = TRUE;
+
+ // now disable all element sections not to be displayed
+
+ if (!setup.editor.el_classic)
+ {
+ setup_editor_el_players = FALSE;
+ setup_editor_el_boulderdash = FALSE;
+ setup_editor_el_boulderdash_native = FALSE;
+ setup_editor_el_boulderdash_effects = FALSE;
+ setup_editor_el_emerald_mine = FALSE;
+ setup_editor_el_emerald_mine_club = FALSE;
+ setup_editor_el_more = FALSE;
+ setup_editor_el_sokoban = FALSE;
+ setup_editor_el_supaplex = FALSE;
+ setup_editor_el_diamond_caves = FALSE;
+ setup_editor_el_dx_boulderdash = FALSE;
+ setup_editor_el_mirror_magic = FALSE;
+ setup_editor_el_deflektor = FALSE;
+ setup_editor_el_chars = FALSE;
+ setup_editor_el_steel_chars = FALSE;
+ }
+
+ if (!setup.editor.el_custom)
+ {
+ setup_editor_el_custom = FALSE;
+ }
+
+ if (!setup.editor.el_user_defined)
+ {
+ setup_editor_el_user_defined = FALSE;
+ }
+
+ if (!setup.editor.el_dynamic)
+ {
+ setup_editor_el_dynamic = FALSE;
+ }
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ {
+ setup_editor_el_boulderdash_native = FALSE;
+ setup_editor_el_boulderdash_effects = FALSE;
+ setup_editor_el_mirror_magic = FALSE;
+ setup_editor_el_deflektor = FALSE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
+ {
+ setup_editor_el_players = FALSE;
+ setup_editor_el_boulderdash = FALSE;
+ setup_editor_el_emerald_mine = FALSE;
+ setup_editor_el_emerald_mine_club = FALSE;
+ setup_editor_el_more = FALSE;
+ setup_editor_el_sokoban = FALSE;
+ setup_editor_el_supaplex = FALSE;
+ setup_editor_el_diamond_caves = FALSE;
+ setup_editor_el_dx_boulderdash = FALSE;
+ setup_editor_el_mirror_magic = FALSE;
+ setup_editor_el_deflektor = FALSE;
+ setup_editor_el_chars = FALSE;
+ setup_editor_el_steel_chars = FALSE;
+
+ setup_editor_el_custom = FALSE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ setup_editor_el_boulderdash = FALSE;
+ setup_editor_el_boulderdash_native = FALSE;
+ setup_editor_el_boulderdash_effects = FALSE;
+ setup_editor_el_more = FALSE;
+ setup_editor_el_sokoban = FALSE;
+ setup_editor_el_supaplex = FALSE;
+ setup_editor_el_diamond_caves = FALSE;
+ setup_editor_el_dx_boulderdash = FALSE;
+ setup_editor_el_mirror_magic = FALSE;
+ setup_editor_el_deflektor = FALSE;
+ setup_editor_el_steel_chars = FALSE;
+
+ setup_editor_el_custom = FALSE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ setup_editor_el_players = FALSE;
+ setup_editor_el_boulderdash = FALSE;
+ setup_editor_el_boulderdash_native = FALSE;
+ setup_editor_el_boulderdash_effects = FALSE;
+ setup_editor_el_emerald_mine = FALSE;
+ setup_editor_el_emerald_mine_club = FALSE;
+ setup_editor_el_more = FALSE;
+ setup_editor_el_sokoban = FALSE;
+ setup_editor_el_diamond_caves = FALSE;
+ setup_editor_el_dx_boulderdash = FALSE;
+ setup_editor_el_mirror_magic = FALSE;
+ setup_editor_el_deflektor = FALSE;
+ setup_editor_el_chars = FALSE;
+ setup_editor_el_steel_chars = FALSE;
+
+ setup_editor_el_custom = FALSE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+ {
+ setup_editor_el_players = FALSE;
+ setup_editor_el_boulderdash = FALSE;
+ setup_editor_el_boulderdash_native = FALSE;
+ setup_editor_el_boulderdash_effects = FALSE;
+ setup_editor_el_emerald_mine = FALSE;
+ setup_editor_el_emerald_mine_club = FALSE;
+ setup_editor_el_more = FALSE;
+ setup_editor_el_sokoban = FALSE;
+ setup_editor_el_supaplex = FALSE;
+ setup_editor_el_diamond_caves = FALSE;
+ setup_editor_el_dx_boulderdash = FALSE;
+ setup_editor_el_steel_chars = FALSE;
+
+ setup_editor_el_custom = FALSE;
+ }
+}
+
+static void ReinitializeElementList(void)
+{
+ static boolean initialization_needed = TRUE;
+ int pos = 0;
+ int i, j;
+
+ ReinitializeElementList_EnableSections();
+
+ if (initialization_needed)
+ {
+ LoadSetup_EditorCascade(); // load last editor cascade state
+
+ // initialize editor cascade element from saved cascade state
+ for (i = 0; editor_elements_info[i].setup_value != NULL; i++)
+ {
+ int *cascade_element = &(*editor_elements_info[i].headline_list)[0];
+ boolean cascade_value = *editor_elements_info[i].setup_cascade_value;
+
+ if (IS_EDITOR_CASCADE(*cascade_element))
+ *cascade_element =
+ (cascade_value ? EL_CASCADE_ACTIVE(*cascade_element) :
+ EL_CASCADE_INACTIVE(*cascade_element));
+ }
+
+ initialization_needed = FALSE;
+ }
+
+ checked_free(editor_elements);
+
+ // reload optional user defined element list for each invocation of editor
+ LoadUserDefinedEditorElementList(&editor_el_user_defined_ptr,
+ &num_editor_el_user_defined);
+
+ // initialize dynamic level element list for each invocation of editor
+ InitDynamicEditorElementList(&editor_el_dynamic_ptr,
+ &num_editor_el_dynamic);
+
+ // initialize list of empty elements (used for padding, if needed)
+ for (i = 0; i < ED_NUM_ELEMENTLIST_BUTTONS; i++)
+ editor_el_empty[i] = EL_EMPTY;
+
+ // do some sanity checks for each element from element list
+ for (i = 0; editor_elements_info[i].setup_value != NULL; i++)
+ {
+ for (j = 0; j < *editor_elements_info[i].element_list_size; j++)
+ {
+ int element = (*editor_elements_info[i].element_list)[j];
+
+ if (element >= NUM_FILE_ELEMENTS)
+ Warn("editor element %d is runtime element", element);
+
+ if (strEqual(getElementInfoText(element), INFOTEXT_UNKNOWN_ELEMENT))
+ Warn("no element description text for element %d", element);
+ }
+ }
+
+ num_editor_elements = 0;
+ use_el_empty = FALSE;
+
+ // determine size of element list
+ for (i = 0; editor_elements_info[i].setup_value != NULL; i++)
+ {
+ if (*editor_elements_info[i].setup_value)
+ {
+ boolean found_inactive_cascade = FALSE;
+
+ if (setup.editor.el_headlines)
+ {
+ // required for correct padding of palette headline buttons
+ if (*editor_elements_info[i].headline_list_size > 0)
+ num_editor_elements += ED_ELEMENTLIST_COLS;
+
+ for (j = 0; j < *editor_elements_info[i].headline_list_size; j++)
+ {
+ int element = (*editor_elements_info[i].headline_list)[j];
+
+ if (IS_EDITOR_CASCADE_INACTIVE(element))
+ found_inactive_cascade = TRUE;
+ }
+ }
+
+ if (found_inactive_cascade)
+ continue;
+
+ // required for correct padding of palette element buttons
+ int element_list_size = *editor_elements_info[i].element_list_size;
+ int element_rows =
+ (element_list_size + ED_ELEMENTLIST_COLS - 1) / ED_ELEMENTLIST_COLS;
+ int element_buttons = ED_ELEMENTLIST_COLS * element_rows;
+
+ num_editor_elements += element_buttons;
+ }
+ }
+
+ if (num_editor_elements < ED_NUM_ELEMENTLIST_BUTTONS)
+ {
+ // offer at least as many elements as element buttons exist
+ use_el_empty = TRUE;
+ num_editor_el_empty = ED_NUM_ELEMENTLIST_BUTTONS - num_editor_elements;
+
+ num_editor_elements += num_editor_el_empty;
+ }
+ else
+ {
+ num_editor_el_empty = 0;
+ }
+
+ editor_elements = checked_malloc(num_editor_elements * sizeof(int));
+
+ // fill element list
+ for (i = 0; editor_elements_info[i].setup_value != NULL; i++)
+ {
+ boolean found_inactive_cascade = FALSE;
+
+ if (*editor_elements_info[i].setup_value)
+ {
+ if (setup.editor.el_headlines)
+ {
+ // required for correct padding of palette headline buttons
+ int headline_size = (*editor_elements_info[i].headline_list_size > 0 ?
+ ED_ELEMENTLIST_COLS : 0);
+
+ for (j = 0; j < headline_size; j++)
+ {
+ // use empty elements for padding of palette headline buttons
+ int element = (j < *editor_elements_info[i].headline_list_size ?
+ (*editor_elements_info[i].headline_list)[j] :
+ editor_el_empty[0]);
+
+ editor_elements[pos++] = element;
+
+ if (IS_EDITOR_CASCADE_INACTIVE(element))
+ found_inactive_cascade = TRUE;
+ }
+ }
+
+ if (found_inactive_cascade)
+ continue;
+
+ // required for correct padding of palette element buttons
+ int element_list_size = *editor_elements_info[i].element_list_size;
+ int element_rows =
+ (element_list_size + ED_ELEMENTLIST_COLS - 1) / ED_ELEMENTLIST_COLS;
+ int element_buttons = ED_ELEMENTLIST_COLS * element_rows;
+
+ // copy all elements from element list
+ for (j = 0; j < element_list_size; j++)
+ editor_elements[pos++] = (*editor_elements_info[i].element_list)[j];
+
+ // use empty elements for padding of palette element buttons
+ for (j = 0; j < element_buttons - element_list_size; j++)
+ editor_elements[pos++] = editor_el_empty[0];
+ }
+ }
+
+ // (this function is also called before editor gadgets are initialized!)
+ AdjustElementListScrollbar();
+}
+
+void PrintEditorElementList(void)
+{
+ boolean *stop = &setup_editor_el_user_defined;
+ int i, j;
+
+ for (i = 0; editor_elements_info[i].setup_value != stop; i++)
+ {
+ int cascade_element = (*editor_elements_info[i].headline_list)[0];
+
+ if (IS_EDITOR_CASCADE(cascade_element))
+ {
+ int cascade_element_show = EL_CASCADE_INACTIVE(cascade_element);
+ char *headline = element_info[cascade_element_show].editor_description;
+
+ PrintLineWithPrefix("# ", "-", 77);
+ Print("# %s\n", headline);
+ PrintLineWithPrefix("# ", "-", 77);
+ }
+
+ for (j = 0; j < *editor_elements_info[i].headline_list_size; j++)
+ {
+ int element = (*editor_elements_info[i].headline_list)[j];
+
+ if (IS_EDITOR_CASCADE(element))
+ element = EL_CHAR_MINUS;
+
+ Print("# %s\n", element_info[element].token_name);
+ }
+
+ if (j > 0)
+ Print("#\n");
+
+ for (j = 0; j < *editor_elements_info[i].element_list_size; j++)
+ {
+ int element = (*editor_elements_info[i].element_list)[j];
+
+ Print("# %s\n", element_info[element].token_name);
+ }
+
+ if (j > 0)
+ Print("#\n");
+ }
+}
+
+static void ReinitializeElementListButtons(void)
+{
+ static boolean last_setup_value_headlines = FALSE;
+ static boolean initialization_needed = TRUE;
+ int i;
+
+ if (!initialization_needed) // check if editor element setup has changed
+ {
+ if (last_setup_value_headlines != setup.editor.el_headlines)
+ initialization_needed = TRUE;
+
+ for (i = 0; editor_elements_info[i].setup_value != NULL; i++)
+ if (editor_elements_info[i].last_setup_value !=
+ *editor_elements_info[i].setup_value)
+ initialization_needed = TRUE;
+ }
+
+ if (!initialization_needed)
+ return;
+
+ FreeLevelEditorGadgets();
+ CreateLevelEditorGadgets();
+
+ // store current setup values for next invocation of this function
+ last_setup_value_headlines = setup.editor.el_headlines;
+ for (i = 0; editor_elements_info[i].setup_value != NULL; i++)
+ editor_elements_info[i].last_setup_value =
+ *editor_elements_info[i].setup_value;
+
+ initialization_needed = FALSE;
+}
+
+static void DrawElementBorder(int dest_x, int dest_y, int width, int height,
+ boolean input)
+{
+ int border_graphic =
+ (input ? IMG_EDITOR_ELEMENT_BORDER_INPUT : IMG_EDITOR_ELEMENT_BORDER);
+ struct GraphicInfo *g = &graphic_info[border_graphic];
+ Bitmap *src_bitmap = g->bitmap;
+ int src_x = g->src_x;
+ int src_y = g->src_y;
+ int border_size = g->border_size;
+ int border_xpos = g->width - border_size;
+ int border_ypos = g->height - border_size;
+ int tilesize = ED_DRAWINGAREA_TILE_SIZE;
+ int i;
+
+ BlitBitmap(src_bitmap, drawto, src_x, src_y,
+ border_size, border_size,
+ dest_x - border_size, dest_y - border_size);
+ BlitBitmap(src_bitmap, drawto, src_x + border_xpos, src_y,
+ border_size, border_size,
+ dest_x + width, dest_y - border_size);
+ BlitBitmap(src_bitmap, drawto, src_x, src_y + border_ypos,
+ border_size, border_size,
+ dest_x - border_size, dest_y + height);
+ BlitBitmap(src_bitmap, drawto, src_x + border_xpos, src_y + border_ypos,
+ border_size, border_size,
+ dest_x + width, dest_y + height);
+
+ for (i = 0; i < width / tilesize; i++)
+ {
+ BlitBitmap(src_bitmap, drawto, src_x + border_size, src_y,
+ tilesize, border_size,
+ dest_x + i * tilesize, dest_y - border_size);
+ BlitBitmap(src_bitmap, drawto, src_x + border_size, src_y + border_ypos,
+ tilesize, border_size,
+ dest_x + i * tilesize, dest_y + height);
+ }
+
+ for (i = 0; i < height / tilesize; i++)
+ {
+ BlitBitmap(src_bitmap, drawto, src_x, src_y + border_size,
+ border_size, tilesize,
+ dest_x - border_size, dest_y + i * tilesize);
+ BlitBitmap(src_bitmap, drawto, src_x + border_xpos, src_y + border_size,
+ border_size, tilesize,
+ dest_x + width, dest_y + i * tilesize);
+ }
+
+ ClearRectangle(drawto, dest_x - 1, dest_y - 1, width + 2, height + 2);
+}
+
+static void DrawEditorLevelBorderLine(int x, int y, int xsize, int ysize)
+{
+ int xsize_tile = MAX(ed_tilesize, xsize);
+ int ysize_tile = MAX(ed_tilesize, ysize);
+ int xsize_full = xsize + 1;
+ int ysize_full = ysize + 1;
+ int xsize_thin = (xsize < ed_tilesize ? 1 : xsize);
+ int ysize_thin = (ysize < ed_tilesize ? 1 : ysize);
+ Pixel line_color = getTabulatorBarColor();
+
+ if (line_color == BLACK_PIXEL) // black => transparent
+ return;
+
+ FillRectangle(drawto, SX + x, SY + y, xsize_tile, ysize_tile, BLACK_PIXEL);
+ FillRectangle(drawto, SX + x, SY + y, xsize_full, ysize_full, line_color);
+ FillRectangle(drawto, SX + x, SY + y, xsize_thin, ysize_thin, BLACK_PIXEL);
+}
+
+static void DrawEditorLevelBorderLinesIfNeeded(void)
+{
+ int xsize = lev_fieldx * ed_tilesize;
+ int ysize = lev_fieldy * ed_tilesize;
+ int line_size = getTabulatorBarHeight();
+
+ if (!suppressBorderElement())
+ return;
+
+ // draw little border line around editable level playfield
+
+ if (xsize < SXSIZE)
+ DrawEditorLevelBorderLine(xsize, 0, line_size, ysize);
+
+ if (ysize < SYSIZE)
+ DrawEditorLevelBorderLine(0, ysize, xsize, line_size);
+
+ if (xsize < SXSIZE && ysize < SYSIZE)
+ DrawEditorLevelBorderLine(xsize, ysize, line_size, line_size);
+}
+
+static void DrawEditorElement(int x, int y, int element)
+{
+ DrawSizedElement(x, y, element, ed_tilesize);
+}
+
+static void DrawEditorElementThruMask(int x, int y, int element)
+{
+ DrawSizedElementThruMask(x, y, element, ed_tilesize);
+}
+
+static void DrawEditorElementOrWall(int x, int y, int scroll_x, int scroll_y)
+{
+ DrawSizedElementOrWall(x, y, scroll_x, scroll_y, ed_tilesize);
+}
+
+static void DrawEditorLevel(int size_x, int size_y, int scroll_x, int scroll_y)
+{
+ DrawSizedLevel(size_x, size_y, scroll_x, scroll_y, ed_tilesize);
+ DrawEditorLevelBorderLinesIfNeeded();
+}
+
+static void DrawDrawingArea(int id)
+{
+ struct GadgetInfo *gi = level_editor_gadget[drawingarea_info[id].gadget_id];
+ int x, y;
+
+ int *value = drawingarea_info[id].value;
+ int area_xsize = drawingarea_info[id].area_xsize;
+ int area_ysize = drawingarea_info[id].area_ysize;
+ int tilesize = ED_DRAWINGAREA_TILE_SIZE;
+
+ for (x = 0; x < area_xsize; x++)
+ {
+ for (y = 0; y < area_ysize; y++)
+ {
+ int element = value[x * area_ysize + y];
+ int graphic;
+ int frame;
+
+ getEditorGraphicAndFrame(element, &graphic, &frame, TRUE);
+
+ DrawSizedGraphicExt(drawto,
+ gi->x + x * tilesize,
+ gi->y + y * tilesize,
+ graphic, frame, tilesize);
+ }
+ }
+}
+
+static void ScrollEditorLevel(int from_x, int from_y, int scroll)
+{
+ int x, y;
+ int dx = (scroll == ED_SCROLL_LEFT ? -1 : scroll == ED_SCROLL_RIGHT ? 1 : 0);
+ int dy = (scroll == ED_SCROLL_UP ? -1 : scroll == ED_SCROLL_DOWN ? 1 : 0);
+
+ BlitBitmap(drawto, drawto,
+ SX + (dx == -1 ? ed_tilesize : 0),
+ SY + (dy == -1 ? ed_tilesize : 0),
+ (ed_fieldx * ed_tilesize) - (dx != 0 ? ed_tilesize : 0),
+ (ed_fieldy * ed_tilesize) - (dy != 0 ? ed_tilesize : 0),
+ SX + (dx == +1 ? ed_tilesize : 0),
+ SY + (dy == +1 ? ed_tilesize : 0));
+
+ if (dx)
+ {
+ x = (dx == 1 ? 0 : ed_fieldx - 1);
+ for (y = 0; y < ed_fieldy; y++)
+ DrawEditorElementOrWall(x, y, from_x, from_y);
+ }
+ else if (dy)
+ {
+ y = (dy == 1 ? 0 : ed_fieldy - 1);
+ for (x = 0; x < ed_fieldx; x++)
+ DrawEditorElementOrWall(x, y, from_x, from_y);
+ }
+
+ redraw_mask |= REDRAW_FIELD;
+ BackToFront();
+}
+
+static void getEditorGraphicAndFrame(int element, int *graphic, int *frame, boolean use_editor_gfx)
+{
+ if (use_editor_gfx)
+ {
+ *graphic = el2edimg(element);
+ *frame = 0;
+ }
+ else
+ {
+ *graphic = el2img(element);
+ *frame = (ANIM_MODE(*graphic) == ANIM_CE_VALUE ?
+ custom_element.ce_value_fixed_initial :
+ ANIM_MODE(*graphic) == ANIM_CE_SCORE ?
+ custom_element.collect_score_initial : FrameCounter);
+ }
+
+ if (*graphic == IMG_UNKNOWN)
+ {
+ // no graphic defined -- if BD style, try to get runtime ("effect") element graphics
+ // (normal BD style elements have graphics, but runtime ("effects") elements do not)
+ int element_bd = map_element_RND_to_BD_cave(element);
+
+ if (element_bd != O_UNKNOWN)
+ {
+ struct GraphicInfo_BD *g_bd = &graphic_info_bd_object[element_bd][0];
+
+ *graphic = g_bd->graphic;
+ *frame = g_bd->frame;
+ }
+ }
+}
+
+static void getEditorGraphicSource(int element, int tile_size, Bitmap **bitmap,
+ int *x, int *y)
+{
+ int graphic;
+ int frame;
+
+ getEditorGraphicAndFrame(element, &graphic, &frame, TRUE);
+
+ getSizedGraphicSource(graphic, frame, tile_size, bitmap, x, y);
+}
+
+static void CreateControlButtons(void)
+{
+ struct GadgetInfo *gi;
+ int i;
+
+ // create toolbox buttons
+ for (i = 0; i < ED_NUM_CTRL_BUTTONS; i++)
+ {
+ int type_id = controlbutton_info[i].gadget_id; // same as gadget ID here
+ int id = controlbutton_info[i].gadget_id;
+ int type = controlbutton_info[i].gadget_type;
+ int graphic = controlbutton_info[i].graphic;
+ struct XYTileSize *pos = controlbutton_info[i].pos;
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ Bitmap *deco_bitmap = NULL;
+ int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
+ int tile_size = 0, deco_shift = 0;
+ boolean deco_masked = FALSE;
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ int gd_x1a = gd->src_x + gd->active_xoffset;
+ int gd_y1a = gd->src_y + gd->active_yoffset;
+ int gd_x2a = gd->src_x + gd->active_xoffset + gd->pressed_xoffset;
+ int gd_y2a = gd->src_y + gd->active_yoffset + gd->pressed_yoffset;
+ int x = pos->x;
+ int y = pos->y;
+ unsigned int event_mask;
+ int radio_button_nr = RADIO_NR_NONE;
+ boolean checked = FALSE;
+
+ if (type_id != i)
+ Fail("'controlbutton_info' structure corrupted at index %d -- please fix", i);
+
+ if (type == GD_TYPE_RADIO_BUTTON)
+ {
+ event_mask = GD_EVENT_PRESSED;
+ radio_button_nr = RADIO_NR_DRAWING_TOOLBOX;
+
+ if (id == drawing_function)
+ checked = TRUE;
+ }
+ else
+ {
+ if (id == GADGET_ID_WRAP_LEFT ||
+ id == GADGET_ID_WRAP_RIGHT ||
+ id == GADGET_ID_WRAP_UP ||
+ id == GADGET_ID_WRAP_DOWN)
+ event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+ else
+ event_mask = GD_EVENT_RELEASED;
+ }
+
+ if (id == GADGET_ID_PROPERTIES ||
+ id == GADGET_ID_PALETTE)
+ {
+ x += DX;
+ y += DY;
+ }
+ else if (id == GADGET_ID_ELEMENT_LEFT ||
+ id == GADGET_ID_ELEMENT_MIDDLE ||
+ id == GADGET_ID_ELEMENT_RIGHT)
+ {
+ x += DX;
+ y += DY;
+
+ int element = (id == GADGET_ID_ELEMENT_LEFT ? new_element1 :
+ id == GADGET_ID_ELEMENT_MIDDLE ? new_element2 :
+ id == GADGET_ID_ELEMENT_RIGHT ? new_element3 : EL_EMPTY);
+
+ tile_size = BUTTON_TILE_SIZE(id == GADGET_ID_ELEMENT_LEFT ?
+ editor.button.element_left.tile_size :
+ id == GADGET_ID_ELEMENT_MIDDLE ?
+ editor.button.element_middle.tile_size :
+ id == GADGET_ID_ELEMENT_RIGHT ?
+ editor.button.element_right.tile_size : 0);
+
+ // make sure that decoration does not overlap gadget border
+ tile_size = MIN(tile_size, MIN(gd->width, gd->height));
+
+ getEditorGraphicSource(element, tile_size, &deco_bitmap, &deco_x,&deco_y);
+
+ deco_xpos = (gd->width - tile_size) / 2;
+ deco_ypos = (gd->height - tile_size) / 2;
+ deco_shift = 1;
+ deco_masked = gd->draw_masked;
+ }
+ else
+ {
+ x += EX;
+ y += EY;
+ }
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, controlbutton_info[i].infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_WIDTH, gd->width,
+ GDI_HEIGHT, gd->height,
+ GDI_TYPE, type,
+ GDI_STATE, GD_BUTTON_UNPRESSED,
+ GDI_RADIO_NR, radio_button_nr,
+ GDI_CHECKED, checked,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_ALT_DESIGN_UNPRESSED, gd->bitmap, gd_x1a, gd_y1a,
+ GDI_ALT_DESIGN_PRESSED, gd->bitmap, gd_x2a, gd_y2a,
+ GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
+ GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
+ GDI_DECORATION_SIZE, tile_size, tile_size,
+ GDI_DECORATION_SHIFTING, deco_shift, deco_shift,
+ GDI_DECORATION_MASKED, deco_masked,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleControlButtons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ }
+
+ // these values are not constant, but can change at runtime
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_UP].x = ED_SCROLL_UP_XPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_UP].y = ED_SCROLL_UP_YPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_DOWN].x = ED_SCROLL_DOWN_XPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_DOWN].y = ED_SCROLL_DOWN_YPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_LEFT].x = ED_SCROLL_LEFT_XPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_LEFT].y = ED_SCROLL_LEFT_YPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_RIGHT].x = ED_SCROLL_RIGHT_XPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_AREA_RIGHT].y = ED_SCROLL_RIGHT_YPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_LIST_UP].x = ED_SCROLL2_UP_XPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_LIST_UP].y = ED_SCROLL2_UP_YPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_LIST_DOWN].x = ED_SCROLL2_DOWN_XPOS;
+ scrollbutton_pos[ED_SCROLLBUTTON_ID_LIST_DOWN].y = ED_SCROLL2_DOWN_YPOS;
+
+ // create buttons for scrolling of drawing area and element list
+ for (i = 0; i < ED_NUM_SCROLLBUTTONS; i++)
+ {
+ int id = scrollbutton_info[i].gadget_id;
+ int type_id = scrollbutton_info[i].gadget_type_id;
+ int graphic = scrollbutton_info[i].graphic;
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ Bitmap *gd_bitmap = gd->bitmap;
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ int width = gd->width;
+ int height = gd->height;
+ int x = scrollbutton_pos[i].x;
+ int y = scrollbutton_pos[i].y;
+ unsigned int event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+
+ if (type_id != i)
+ Fail("'scrollbutton_info' structure corrupted at index %d -- please fix", i);
+
+ if (id == GADGET_ID_SCROLL_LIST_UP ||
+ id == GADGET_ID_SCROLL_LIST_DOWN)
+ {
+ x += PX;
+ y += PY;
+ }
+ else
+ {
+ x += SX;
+ y += SY;
+ }
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, scrollbutton_info[i].infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_WIDTH, width,
+ GDI_HEIGHT, height,
+ GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+ GDI_STATE, GD_BUTTON_UNPRESSED,
+ GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleControlButtons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ }
+
+ // create buttons for element list
+ for (i = 0; i < ED_NUM_ELEMENTLIST_BUTTONS; i++)
+ {
+ int type_id = i;
+ int id = GADGET_ID_ELEMENTLIST_FIRST + i;
+ int graphic = IMG_EDITOR_PALETTE_BUTTON;
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ Bitmap *gd_bitmap = gd->bitmap;
+ Bitmap *deco_bitmap;
+ int deco_x, deco_y, deco_xpos, deco_ypos;
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ int xx = i % ED_ELEMENTLIST_BUTTONS_HORIZ;
+ int yy = i / ED_ELEMENTLIST_BUTTONS_HORIZ;
+ int x = PX + ED_ELEMENTLIST_XPOS + xx * gd->width;
+ int y = PY + ED_ELEMENTLIST_YPOS + yy * gd->height;
+ int element = editor_elements[i];
+ int tile_size = BUTTON_TILE_SIZE(editor.palette.tile_size);
+ unsigned int event_mask = GD_EVENT_RELEASED;
+
+ getEditorGraphicSource(element, tile_size, &deco_bitmap, &deco_x, &deco_y);
+
+ deco_xpos = (gd->width - tile_size) / 2;
+ deco_ypos = (gd->height - tile_size) / 2;
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, getElementInfoText(element),
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_WIDTH, gd->width,
+ GDI_HEIGHT, gd->height,
+ GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+ GDI_STATE, GD_BUTTON_UNPRESSED,
+ GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+ GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
+ GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
+ GDI_DECORATION_SIZE, tile_size, tile_size,
+ GDI_DECORATION_SHIFTING, 1, 1,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleControlButtons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ }
+}
+
+static void CreateCounterButtons(void)
+{
+ int max_infotext_len = getMaxInfoTextLength();
+ int i;
+
+ for (i = 0; i < ED_NUM_COUNTERBUTTONS; i++)
+ {
+ int type_id = counterbutton_info[i].gadget_type_id;
+ int x = SX + ED_SETTINGS_X(counterbutton_info[i].x); // down count button
+ int y = SY + ED_SETTINGS_Y(counterbutton_info[i].y);
+ int j;
+
+ if (type_id != i)
+ Fail("'counterbutton_info' structure corrupted at index %d -- please fix", i);
+
+ // determine horizontal position to the right of specified gadget
+ if (counterbutton_info[i].gadget_id_align != GADGET_ID_NONE)
+ x = (right_gadget_border[counterbutton_info[i].gadget_id_align] +
+ ED_GADGET_TEXT_DISTANCE);
+
+ // determine horizontal offset for leading text
+ if (counterbutton_info[i].text_left != NULL)
+ x += getTextWidthForGadget(counterbutton_info[i].text_left);
+
+ for (j = 0; j < 2; j++)
+ {
+ struct GadgetInfo *gi;
+ int id = (j == 0 ?
+ counterbutton_info[i].gadget_id_down :
+ counterbutton_info[i].gadget_id_up);
+ int graphic;
+ struct GraphicInfo *gd;
+ int gd_x1, gd_x2, gd_y1, gd_y2;
+ unsigned int event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+ char infotext[max_infotext_len + 1];
+
+ if (i == ED_COUNTER_ID_SELECT_LEVEL)
+ {
+ graphic = (j == 0 ?
+ IMG_GFX_EDITOR_BUTTON_PREV_LEVEL :
+ IMG_GFX_EDITOR_BUTTON_NEXT_LEVEL);
+
+ event_mask |= GD_EVENT_RELEASED;
+
+ if (j == 0)
+ {
+ x = DX + editor.button.prev_level.x;
+ y = DY + editor.button.prev_level.y;
+ }
+ else
+ {
+ x = DX + editor.button.next_level.x;
+ y = DY + editor.button.next_level.y;
+ }
+ }
+ else
+ {
+ graphic = (j == 0 ?
+ IMG_EDITOR_COUNTER_DOWN :
+ IMG_EDITOR_COUNTER_UP);
+ }
+
+ gd = &graphic_info[graphic];
+
+ gd_x1 = gd->src_x;
+ gd_y1 = gd->src_y;
+ gd_x2 = gd->src_x + gd->pressed_xoffset;
+ gd_y2 = gd->src_y + gd->pressed_yoffset;
+
+ sprintf(infotext, "%s counter value by 1, 5 or 10",
+ (j == 0 ? "Decrease" : "Increase"));
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_WIDTH, gd->width,
+ GDI_HEIGHT, gd->height,
+ GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+ GDI_STATE, GD_BUTTON_UNPRESSED,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleCounterButtons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, counterbutton_info[i].text_right);
+
+ x += gi->width + ED_GADGET_SMALL_DISTANCE; // text count button
+
+ if (j == 0)
+ {
+ int font_type = FONT_INPUT_1;
+ int font_type_active = FONT_INPUT_1_ACTIVE;
+
+ id = counterbutton_info[i].gadget_id_text;
+
+ event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
+
+ if (i == ED_COUNTER_ID_SELECT_LEVEL)
+ {
+ graphic = IMG_GFX_EDITOR_INPUT_LEVEL_NUMBER;
+
+ font_type = FONT_LEVEL_NUMBER;
+ font_type_active = FONT_LEVEL_NUMBER_ACTIVE;
+
+ x = DX + editor.input.level_number.x;
+ y = DY + editor.input.level_number.y;
+ }
+ else
+ {
+ graphic = IMG_EDITOR_COUNTER_INPUT;
+ }
+
+ gd = &graphic_info[graphic];
+
+ gd_x1 = gd->src_x;
+ gd_y1 = gd->src_y;
+ gd_x2 = gd->src_x + gd->active_xoffset;
+ gd_y2 = gd->src_y + gd->active_yoffset;
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, "Enter counter value",
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_TYPE, GD_TYPE_TEXT_INPUT_NUMERIC,
+ GDI_NUMBER_VALUE, 0,
+ GDI_NUMBER_MIN, counterbutton_info[i].min_value,
+ GDI_NUMBER_MAX, counterbutton_info[i].max_value,
+ GDI_TEXT_SIZE, 3, // minimal counter text size
+ GDI_TEXT_FONT, font_type,
+ GDI_TEXT_FONT_ACTIVE, font_type_active,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_BORDER_SIZE, gd->border_size, gd->border_size,
+ GDI_DESIGN_WIDTH, gd->width,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleCounterButtons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, counterbutton_info[i].text_right);
+
+ x += gi->width + ED_GADGET_SMALL_DISTANCE; // up count button
+ }
+ }
+ }
+}
+
+static void CreateDrawingAreas(void)
+{
+ int i;
+
+ // these values are not constant, but can change at runtime
+ drawingarea_info[ED_DRAWING_ID_DRAWING_LEVEL].area_xsize = MAX_ED_FIELDX;
+ drawingarea_info[ED_DRAWING_ID_DRAWING_LEVEL].area_ysize = MAX_ED_FIELDY;
+
+ for (i = 0; i < ED_NUM_DRAWING_AREAS; i++)
+ {
+ struct GadgetInfo *gi;
+ int id = drawingarea_info[i].gadget_id;
+ int type_id = drawingarea_info[i].gadget_type_id;
+ int x = SX + ED_AREA_SETTINGS_X(drawingarea_info[i]);
+ int y = SY + ED_AREA_SETTINGS_Y(drawingarea_info[i]);
+ int area_xsize = drawingarea_info[i].area_xsize;
+ int area_ysize = drawingarea_info[i].area_ysize;
+ int item_size = (id == GADGET_ID_DRAWING_LEVEL ?
+ ed_tilesize : ED_DRAWINGAREA_TILE_SIZE);
+ unsigned int event_mask =
+ GD_EVENT_PRESSED | GD_EVENT_RELEASED | GD_EVENT_MOVING |
+ GD_EVENT_OFF_BORDERS | GD_EVENT_PIXEL_PRECISE;
+
+ if (type_id != i)
+ Fail("'drawingarea_info' structure corrupted at index %d -- please fix", i);
+
+ // determine horizontal position to the right of specified gadget
+ if (drawingarea_info[i].gadget_id_align != GADGET_ID_NONE)
+ x = (right_gadget_border[drawingarea_info[i].gadget_id_align] +
+ ED_DRAWINGAREA_TEXT_DISTANCE);
+
+ // determine horizontal offset for leading text
+ if (drawingarea_info[i].text_left != NULL)
+ x += getTextWidthForDrawingArea(drawingarea_info[i].text_left);
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_TYPE, GD_TYPE_DRAWING_AREA,
+ GDI_AREA_SIZE, area_xsize, area_ysize,
+ GDI_ITEM_SIZE, item_size, item_size,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleDrawingAreaInfo,
+ GDI_CALLBACK_ACTION, HandleDrawingAreas,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, drawingarea_info[i].text_right);
+ }
+}
+
+static void CreateTextInputGadgets(void)
+{
+ struct GraphicInfo *gd = &graphic_info[IMG_EDITOR_INPUT_TEXT];
+ int max_infotext_len = getMaxInfoTextLength();
+ int i;
+
+ for (i = 0; i < ED_NUM_TEXTINPUT; i++)
+ {
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->active_xoffset;
+ int gd_y2 = gd->src_y + gd->active_yoffset;
+ struct GadgetInfo *gi;
+ unsigned int event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
+ char infotext[MAX_OUTPUT_LINESIZE + 1];
+ int id = textinput_info[i].gadget_id;
+ int type_id = textinput_info[i].gadget_type_id;
+ int x, y;
+
+ if (type_id != i)
+ Fail("'textinput_info' structure corrupted at index %d -- please fix", i);
+
+ if (i == ED_TEXTINPUT_ID_ELEMENT_NAME)
+ {
+ int element_border = graphic_info[IMG_EDITOR_ELEMENT_BORDER].border_size;
+ int border_size = gd->border_size;
+ int font_nr = FONT_INPUT_1;
+ int font_height = getFontHeight(font_nr);
+ int xoffset = element_border + TILEX + element_border + 3 * border_size;
+ int yoffset = element_border + (TILEY - font_height) / 2;
+
+ x = (editor.settings.element_name.x != -1 ?
+ editor.settings.element_name.x :
+ editor.settings.element_graphic.x + xoffset) - border_size;
+ y = (editor.settings.element_name.y != -1 ?
+ editor.settings.element_name.y :
+ editor.settings.element_graphic.y + yoffset) - border_size;
+ }
+ else
+ {
+ x = ED_SETTINGS_X(textinput_info[i].x);
+ y = ED_SETTINGS_Y(textinput_info[i].y);
+ }
+
+ // determine horizontal offset for leading text
+ if (textinput_info[i].text_left != NULL)
+ x += getTextWidthForGadget(textinput_info[i].text_left);
+
+ sprintf(infotext, "%s", textinput_info[i].infotext);
+ infotext[max_infotext_len] = '\0';
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_INFO_TEXT, infotext,
+ GDI_X, SX + x,
+ GDI_Y, SY + y,
+ GDI_TYPE, GD_TYPE_TEXT_INPUT_ALPHANUMERIC,
+ GDI_TEXT_VALUE, textinput_info[i].value,
+ GDI_TEXT_SIZE, textinput_info[i].size,
+ GDI_TEXT_FONT, FONT_INPUT_1,
+ GDI_TEXT_FONT_ACTIVE, FONT_INPUT_1_ACTIVE,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_BORDER_SIZE, gd->border_size, gd->border_size,
+ GDI_DESIGN_WIDTH, gd->width,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleTextInputGadgets,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ }
+}
+
+static void CreateTextAreaGadgets(void)
+{
+ int max_infotext_len = getMaxInfoTextLength();
+ int i;
+
+ for (i = 0; i < ED_NUM_TEXTAREAS; i++)
+ {
+ struct GraphicInfo *gd = &graphic_info[IMG_EDITOR_INPUT_TEXTAREA];
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->active_xoffset;
+ int gd_y2 = gd->src_y + gd->active_yoffset;
+ struct GadgetInfo *gi;
+ unsigned int event_mask = GD_EVENT_TEXT_LEAVING;
+ char infotext[MAX_OUTPUT_LINESIZE + 1];
+ int id = textarea_info[i].gadget_id;
+ int type_id = textarea_info[i].gadget_type_id;
+ int area_xsize = textarea_info[i].xsize;
+ int area_ysize = textarea_info[i].ysize;
+
+ if (type_id != i)
+ Fail("'textarea_info' structure corrupted at index %d -- please fix", i);
+
+ sprintf(infotext, "Enter %s", textarea_info[i].infotext);
+ infotext[max_infotext_len] = '\0';
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_INFO_TEXT, infotext,
+ GDI_X, SX + ED_SETTINGS_X(textarea_info[i].x),
+ GDI_Y, SY + ED_SETTINGS_Y(textarea_info[i].y),
+ GDI_TYPE, GD_TYPE_TEXT_AREA,
+ GDI_AREA_SIZE, area_xsize, area_ysize,
+ GDI_TEXT_FONT, FONT_INPUT_1,
+ GDI_TEXT_FONT_ACTIVE, FONT_INPUT_1_ACTIVE,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_BORDER_SIZE, gd->border_size, gd->border_size,
+ GDI_DESIGN_WIDTH, gd->width,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleTextAreaGadgets,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ }
+}
+
+static void CreateSelectboxGadgets(void)
+{
+ int max_infotext_len = getMaxInfoTextLength();
+
+ int i, j;
+
+ for (i = 0; i < ED_NUM_SELECTBOX; i++)
+ {
+ struct GraphicInfo *gd = &graphic_info[IMG_EDITOR_SELECTBOX_INPUT];
+ struct GraphicInfo *gd2 = &graphic_info[IMG_EDITOR_SELECTBOX_BUTTON];
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->active_xoffset;
+ int gd_y2 = gd->src_y + gd->active_yoffset;
+ int selectbox_button_xsize = gd2->width;
+ struct GadgetInfo *gi;
+ char infotext[MAX_OUTPUT_LINESIZE + 1];
+ int id = selectbox_info[i].gadget_id;
+ int type_id = selectbox_info[i].gadget_type_id;
+ int x = SX + ED_SETTINGS_X(selectbox_info[i].x);
+ int y = SY + ED_SETTINGS_Y(selectbox_info[i].y);
+ unsigned int event_mask =
+ GD_EVENT_RELEASED | GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
+
+ if (type_id != i)
+ Fail("'selectbox_info' structure corrupted at index %d -- please fix", i);
+
+ if (selectbox_info[i].size == -1) // dynamically determine size
+ {
+ // (we cannot use -1 for uninitialized values if we directly compare
+ // with results from strlen(), because the '<' and '>' operation will
+ // implicitely cast -1 to an unsigned integer value!)
+ selectbox_info[i].size = 0;
+
+ for (j = 0; selectbox_info[i].options[j].text != NULL; j++)
+ if (strlen(selectbox_info[i].options[j].text) > selectbox_info[i].size)
+ selectbox_info[i].size = strlen(selectbox_info[i].options[j].text);
+
+ selectbox_info[i].size++; // add one character empty space
+ }
+
+ // determine horizontal position to the right of specified gadget
+ if (selectbox_info[i].gadget_id_align != GADGET_ID_NONE)
+ x = (right_gadget_border[selectbox_info[i].gadget_id_align] +
+ ED_GADGET_TEXT_DISTANCE);
+
+ // determine horizontal offset for leading text
+ if (selectbox_info[i].text_left != NULL)
+ x += getTextWidthForGadget(selectbox_info[i].text_left);
+
+ sprintf(infotext, "%s", selectbox_info[i].infotext);
+ infotext[max_infotext_len] = '\0';
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_INFO_TEXT, infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_TYPE, GD_TYPE_SELECTBOX,
+ GDI_SELECTBOX_OPTIONS, selectbox_info[i].options,
+ GDI_SELECTBOX_CHAR_UNSELECTABLE, '[',
+ GDI_TEXT_SIZE, selectbox_info[i].size,
+ GDI_TEXT_FONT, FONT_INPUT_1,
+ GDI_TEXT_FONT_ACTIVE, FONT_INPUT_1_ACTIVE,
+ GDI_TEXT_FONT_UNSELECTABLE, FONT_TEXT_1,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_BORDER_SIZE, gd->border_size, gd->border_size,
+ GDI_BORDER_SIZE_SELECTBUTTON, selectbox_button_xsize,
+ GDI_DESIGN_WIDTH, gd->width,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleSelectboxGadgets,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, selectbox_info[i].text_right);
+ }
+}
+
+static void CreateTextbuttonGadgets(void)
+{
+ int max_infotext_len = getMaxInfoTextLength();
+ int i;
+
+ for (i = 0; i < ED_NUM_TEXTBUTTONS; i++)
+ {
+ int id = textbutton_info[i].gadget_id;
+ int type_id = textbutton_info[i].gadget_type_id;
+ int is_tab_button =
+ ((id >= GADGET_ID_LEVELCONFIG_LEVEL && id <= GADGET_ID_LEVELCONFIG_ENGINE) ||
+ (id >= GADGET_ID_ENGINECONFIG_CONFIG && id <= GADGET_ID_ENGINECONFIG_COLORS) ||
+ (id >= GADGET_ID_PROPERTIES_INFO && id <= GADGET_ID_PROPERTIES_CHANGE));
+ int graphic =
+ (is_tab_button ? IMG_EDITOR_TABBUTTON : IMG_EDITOR_TEXTBUTTON);
+ int gadget_distance =
+ (is_tab_button ? ED_GADGET_SMALL_DISTANCE : ED_GADGET_TEXT_DISTANCE);
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ int gd_x1a = gd->src_x + gd->active_xoffset;
+ int gd_y1a = gd->src_y + gd->active_yoffset;
+ int border_xsize = gd->border_size + gd->draw_xoffset;
+ int border_ysize = gd->border_size;
+ struct GadgetInfo *gi;
+ unsigned int event_mask = GD_EVENT_RELEASED;
+ char infotext[MAX_OUTPUT_LINESIZE + 1];
+ int x = SX + ED_SETTINGS_X(textbutton_info[i].x);
+ int y = SY + ED_SETTINGS_Y(textbutton_info[i].y);
+
+ if (type_id != i)
+ Fail("'textbutton_info' structure corrupted at index %d -- please fix", i);
+
+ if (textbutton_info[i].size == -1) // dynamically determine size
+ textbutton_info[i].size = strlen(textbutton_info[i].text);
+
+ sprintf(infotext, "%s", textbutton_info[i].infotext);
+ infotext[max_infotext_len] = '\0';
+
+ // determine horizontal position to the right of specified gadget
+ if (textbutton_info[i].gadget_id_align != GADGET_ID_NONE)
+ {
+ int gadget_id_align = textbutton_info[i].gadget_id_align;
+
+ x = right_gadget_border[gadget_id_align] + gadget_distance;
+
+ if (textbutton_info[i].y == -1)
+ y = level_editor_gadget[gadget_id_align]->y;
+ }
+
+ // determine horizontal offset for leading text
+ if (textbutton_info[i].text_left != NULL)
+ x += getTextWidthForGadget(textbutton_info[i].text_left);
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_TYPE, GD_TYPE_TEXT_BUTTON,
+ GDI_TEXT_VALUE, textbutton_info[i].text,
+ GDI_TEXT_SIZE, textbutton_info[i].size,
+ GDI_TEXT_FONT, FONT_INPUT_2,
+ GDI_TEXT_FONT_ACTIVE, FONT_INPUT_2_ACTIVE,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_ALT_DESIGN_UNPRESSED, gd->bitmap, gd_x1a, gd_y1a,
+ GDI_BORDER_SIZE, border_xsize, border_ysize,
+ GDI_DESIGN_WIDTH, gd->width,
+ GDI_DECORATION_SHIFTING, 1, 1,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleTextbuttonGadgets,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, textbutton_info[i].text_right);
+ }
+}
+
+static void CreateGraphicbuttonGadgets(void)
+{
+ struct GadgetInfo *gi;
+ int i;
+
+ // create buttons for scrolling of drawing area and element list
+ for (i = 0; i < ED_NUM_GRAPHICBUTTONS; i++)
+ {
+ int id = graphicbutton_info[i].gadget_id;
+ int type_id = graphicbutton_info[i].gadget_type_id;
+ int x = SX + ED_SETTINGS_X(graphicbutton_info[i].x);
+ int y = SY + ED_SETTINGS_Y(graphicbutton_info[i].y);
+ int graphic = graphicbutton_info[i].graphic;
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ unsigned int event_mask = GD_EVENT_RELEASED;
+
+ if (type_id != i)
+ Fail("'graphicbutton_info' structure corrupted at index %d -- please fix", i);
+
+ // determine horizontal position to the right of specified gadget
+ if (graphicbutton_info[i].gadget_id_align != GADGET_ID_NONE)
+ x = (right_gadget_border[graphicbutton_info[i].gadget_id_align] +
+ ED_GADGET_TEXT_DISTANCE);
+
+ // determine horizontal offset for leading text
+ if (graphicbutton_info[i].text_left != NULL)
+ x += getTextWidthForGadget(graphicbutton_info[i].text_left);
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, graphicbutton_info[i].infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_WIDTH, gd->width,
+ GDI_HEIGHT, gd->height,
+ GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+ GDI_STATE, GD_BUTTON_UNPRESSED,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleGraphicbuttonGadgets,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, graphicbutton_info[i].text_right);
+ }
+}
+
+static void CreateScrollbarGadgets(void)
+{
+ int i;
+
+ // these values are not constant, but can change at runtime
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].x =
+ SX + ED_SCROLL_HORIZONTAL_XPOS;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].y =
+ SY + ED_SCROLL_HORIZONTAL_YPOS;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].width =
+ ED_SCROLL_HORIZONTAL_XSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].height =
+ ED_SCROLL_HORIZONTAL_YSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].wheel_x = SX;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].wheel_y = SY;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].wheel_width = SXSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_HORIZONTAL].wheel_height = SYSIZE;
+
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].x =
+ SX + ED_SCROLL_VERTICAL_XPOS;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].y =
+ SY + ED_SCROLL_VERTICAL_YPOS;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].width =
+ ED_SCROLL_VERTICAL_XSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].height =
+ ED_SCROLL_VERTICAL_YSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].wheel_x = SX;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].wheel_y = SY;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].wheel_width = SXSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_AREA_VERTICAL].wheel_height = SYSIZE;
+
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].x =
+ PX + ED_SCROLL2_VERTICAL_XPOS;
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].y =
+ PY + ED_SCROLL2_VERTICAL_YPOS;
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].width =
+ ED_SCROLL2_VERTICAL_XSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].height =
+ ED_SCROLL2_VERTICAL_YSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].wheel_x = PX;
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].wheel_y = PY;
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].wheel_width = PXSIZE;
+ scrollbar_pos[ED_SCROLLBAR_ID_LIST_VERTICAL].wheel_height = PYSIZE;
+
+ for (i = 0; i < ED_NUM_SCROLLBARS; i++)
+ {
+ int id = scrollbar_info[i].gadget_id;
+ int type_id = scrollbar_info[i].gadget_type_id;
+ int graphic = scrollbar_info[i].graphic;
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ struct GadgetInfo *gi;
+ int items_max, items_visible, item_position;
+ unsigned int event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
+
+ if (type_id != i)
+ Fail("'scrollbar_info' structure corrupted at index %d -- please fix", i);
+
+ if (i == ED_SCROLLBAR_ID_LIST_VERTICAL)
+ {
+ items_max = num_editor_elements / ED_ELEMENTLIST_BUTTONS_HORIZ;
+ items_visible = ED_ELEMENTLIST_BUTTONS_VERT;
+ item_position = element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ;
+ }
+ else // drawing area scrollbars
+ {
+ if (scrollbar_info[i].type == GD_TYPE_SCROLLBAR_HORIZONTAL)
+ {
+ items_max = MAX(lev_fieldx + 2, ed_fieldx);
+ items_visible = ed_fieldx;
+ item_position = 0;
+ }
+ else
+ {
+ items_max = MAX(lev_fieldy + 2, ed_fieldy);
+ items_visible = ed_fieldy;
+ item_position = 0;
+ }
+ }
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, scrollbar_info[i].infotext,
+ GDI_X, scrollbar_pos[i].x,
+ GDI_Y, scrollbar_pos[i].y,
+ GDI_WIDTH, scrollbar_pos[i].width,
+ GDI_HEIGHT, scrollbar_pos[i].height,
+ GDI_TYPE, scrollbar_info[i].type,
+ GDI_SCROLLBAR_ITEMS_MAX, items_max,
+ GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
+ GDI_SCROLLBAR_ITEM_POSITION, item_position,
+ GDI_WHEEL_AREA_X, scrollbar_pos[i].wheel_x,
+ GDI_WHEEL_AREA_Y, scrollbar_pos[i].wheel_y,
+ GDI_WHEEL_AREA_WIDTH, scrollbar_pos[i].wheel_width,
+ GDI_WHEEL_AREA_HEIGHT, scrollbar_pos[i].wheel_height,
+ GDI_STATE, GD_BUTTON_UNPRESSED,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_BORDER_SIZE, gd->border_size, gd->border_size,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleControlButtons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ }
+}
+
+static void CreateCheckbuttonGadgets(void)
+{
+ struct GadgetInfo *gi;
+ int i;
+
+ for (i = 0; i < ED_NUM_CHECKBUTTONS; i++)
+ {
+ int id = checkbutton_info[i].gadget_id;
+ int type_id = checkbutton_info[i].gadget_type_id;
+ int graphic = (id == GADGET_ID_STICK_ELEMENT ? IMG_EDITOR_STICKYBUTTON :
+ IMG_EDITOR_CHECKBOX);
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ int gd_x1a = gd->src_x + gd->active_xoffset;
+ int gd_y1a = gd->src_y + gd->active_yoffset;
+ int gd_x2a = gd->src_x + gd->active_xoffset + gd->pressed_xoffset;
+ int gd_y2a = gd->src_y + gd->active_yoffset + gd->pressed_yoffset;
+ int x = SX + ED_SETTINGS_X(checkbutton_info[i].x);
+ int y = SY + ED_SETTINGS_Y(checkbutton_info[i].y);
+ unsigned int event_mask = GD_EVENT_PRESSED;
+
+ if (type_id != i)
+ Fail("'checkbutton_info' structure corrupted at index %d -- please fix", i);
+
+ // determine horizontal position to the right of specified gadget
+ if (checkbutton_info[i].gadget_id_align != GADGET_ID_NONE)
+ x = (right_gadget_border[checkbutton_info[i].gadget_id_align] +
+ ED_GADGET_TEXT_DISTANCE);
+
+ // determine horizontal offset for leading text
+ if (checkbutton_info[i].text_left != NULL)
+ x += getTextWidthForGadget(checkbutton_info[i].text_left);
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, checkbutton_info[i].infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_WIDTH, gd->width,
+ GDI_HEIGHT, gd->height,
+ GDI_TYPE, GD_TYPE_CHECK_BUTTON,
+ GDI_CHECKED, *checkbutton_info[i].value,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_ALT_DESIGN_UNPRESSED, gd->bitmap, gd_x1a, gd_y1a,
+ GDI_ALT_DESIGN_PRESSED, gd->bitmap, gd_x2a, gd_y2a,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleCheckbuttons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, checkbutton_info[i].text_right);
+ }
+}
+
+static void CreateRadiobuttonGadgets(void)
+{
+ int graphic = IMG_EDITOR_RADIOBUTTON;
+ struct GraphicInfo *gd = &graphic_info[graphic];
+ int gd_x1 = gd->src_x;
+ int gd_y1 = gd->src_y;
+ int gd_x2 = gd->src_x + gd->pressed_xoffset;
+ int gd_y2 = gd->src_y + gd->pressed_yoffset;
+ int gd_x1a = gd->src_x + gd->active_xoffset;
+ int gd_y1a = gd->src_y + gd->active_yoffset;
+ int gd_x2a = gd->src_x + gd->active_xoffset + gd->pressed_xoffset;
+ int gd_y2a = gd->src_y + gd->active_yoffset + gd->pressed_yoffset;
+ struct GadgetInfo *gi;
+ int i;
+
+ for (i = 0; i < ED_NUM_RADIOBUTTONS; i++)
+ {
+ int id = radiobutton_info[i].gadget_id;
+ int type_id = radiobutton_info[i].gadget_type_id;
+ int x = SX + ED_SETTINGS_X(radiobutton_info[i].x);
+ int y = SY + ED_SETTINGS_Y(radiobutton_info[i].y);
+ unsigned int event_mask = GD_EVENT_PRESSED;
+
+ if (type_id != i)
+ Fail("'radiobutton_info' structure corrupted at index %d -- please fix", i);
+
+ int checked =
+ (*radiobutton_info[i].value == radiobutton_info[i].checked_value);
+
+ // determine horizontal position to the right of specified gadget
+ if (radiobutton_info[i].gadget_id_align != GADGET_ID_NONE)
+ x = (right_gadget_border[radiobutton_info[i].gadget_id_align] +
+ ED_GADGET_TEXT_DISTANCE);
+
+ // determine horizontal offset for leading text
+ if (radiobutton_info[i].text_left != NULL)
+ x += getTextWidthForGadget(radiobutton_info[i].text_left);
+
+ gi = CreateGadget(GDI_CUSTOM_ID, id,
+ GDI_CUSTOM_TYPE_ID, type_id,
+ GDI_IMAGE_ID, graphic,
+ GDI_INFO_TEXT, radiobutton_info[i].infotext,
+ GDI_X, x,
+ GDI_Y, y,
+ GDI_WIDTH, gd->width,
+ GDI_HEIGHT, gd->height,
+ GDI_TYPE, GD_TYPE_RADIO_BUTTON,
+ GDI_RADIO_NR, radiobutton_info[i].radio_button_nr,
+ GDI_CHECKED, checked,
+ GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+ GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+ GDI_ALT_DESIGN_UNPRESSED, gd->bitmap, gd_x1a, gd_y1a,
+ GDI_ALT_DESIGN_PRESSED, gd->bitmap, gd_x2a, gd_y2a,
+ GDI_EVENT_MASK, event_mask,
+ GDI_CALLBACK_INFO, HandleEditorGadgetInfoText,
+ GDI_CALLBACK_ACTION, HandleRadiobuttons,
+ GDI_END);
+
+ if (gi == NULL)
+ Fail("cannot create gadget");
+
+ level_editor_gadget[id] = gi;
+ right_gadget_border[id] =
+ getRightGadgetBorder(gi, radiobutton_info[i].text_right);
+ }
+}
+
+void CreateLevelEditorGadgets(void)
+{
+ // force EDITOR font inside level editor
+ SetFontStatus(GAME_MODE_EDITOR);
+
+ // these values are not constant, but can change at runtime
+ ed_fieldx = MAX_ED_FIELDX - 1;
+ ed_fieldy = MAX_ED_FIELDY - 1;
+
+ num_editor_gadgets = NUM_EDITOR_GADGETS;
+
+ // Debug("editor", "allocating %d gadgets ...\n", num_editor_gadgets);
+
+ level_editor_gadget =
+ checked_calloc(num_editor_gadgets * sizeof(struct GadgetInfo *));
+ right_gadget_border =
+ checked_calloc(num_editor_gadgets * sizeof(int));
+
+ // set number of empty (padding) element buttons to maximum number of buttons
+ num_editor_el_empty = ED_NUM_ELEMENTLIST_BUTTONS;
+
+ editor_el_empty = checked_calloc(num_editor_el_empty * sizeof(int));
+ editor_el_empty_ptr = editor_el_empty;
+
+ use_permanent_palette = !editor.palette.show_as_separate_screen;
+
+ InitGadgetScreenBorders(-1, INFOTEXT_YPOS);
+
+ ReinitializeElementList();
+
+ CreateControlButtons();
+ CreateScrollbarGadgets();
+
+ // order of function calls is important because of cross-references
+ CreateCheckbuttonGadgets();
+ CreateCounterButtons();
+ CreateRadiobuttonGadgets();
+ CreateTextInputGadgets();
+ CreateTextAreaGadgets();
+ CreateSelectboxGadgets();
+ CreateGraphicbuttonGadgets();
+ CreateTextbuttonGadgets();
+ CreateDrawingAreas();
+
+ ResetFontStatus();
+}
+
+void FreeLevelEditorGadgets(void)
+{
+ int i;
+
+ // Debug("editor", "freeing %d gadgets ...\n", num_editor_gadgets);
+
+ for (i = 0; i < num_editor_gadgets; i++)
+ {
+ FreeGadget(level_editor_gadget[i]);
+
+ level_editor_gadget[i] = NULL;
+ }
+
+ checked_free(level_editor_gadget);
+ checked_free(right_gadget_border);
+
+ checked_free(editor_el_empty);
+}
+
+static void MapCounterButtons(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ int gadget_id_down = counterbutton_info[id].gadget_id_down;
+ int gadget_id_text = counterbutton_info[id].gadget_id_text;
+ int gadget_id_up = counterbutton_info[id].gadget_id_up;
+ struct GadgetInfo *gi_down = level_editor_gadget[gadget_id_down];
+ struct GadgetInfo *gi_text = level_editor_gadget[gadget_id_text];
+ struct GadgetInfo *gi_up = level_editor_gadget[gadget_id_up];
+ int xoffset_left = getTextWidthForGadget(counterbutton_info[id].text_left);
+ int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+ int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
+ int yoffset = (gi_down->height - font_height) / 2;
+ int x_left = gi_down->x - xoffset_left;
+ int x_right; // set after gadget position was modified
+ int y_above = gi_down->y - yoffset_above;
+ int x = gi_down->x;
+ int y; // set after gadget position was modified
+
+ // counter limits must be changed first to prevent value truncation
+ ModifyEditorCounterLimits(id, counterbutton_info[id].min_value,
+ counterbutton_info[id].max_value);
+
+ // right text position might have changed after setting position above
+ x_right = gi_up->x + gi_up->width + xoffset_right;
+
+ ModifyEditorCounterValue(id, *counterbutton_info[id].value);
+
+ // set position for counter gadgets with dynamically determined position
+ if (id != ED_COUNTER_ID_SELECT_LEVEL)
+ {
+ ModifyGadget(gi_down, GDI_Y, SY + ED_SETTINGS_Y(counterbutton_info[id].y), GDI_END);
+ ModifyGadget(gi_text, GDI_Y, SY + ED_SETTINGS_Y(counterbutton_info[id].y), GDI_END);
+ ModifyGadget(gi_up, GDI_Y, SY + ED_SETTINGS_Y(counterbutton_info[id].y), GDI_END);
+ }
+
+ // vertical position might have changed after setting position above
+ y = gi_up->y + yoffset;
+
+ if (counterbutton_info[id].text_above)
+ DrawText(x, y_above, counterbutton_info[id].text_above, font_nr);
+
+ if (counterbutton_info[id].text_left)
+ DrawText(x_left, y, counterbutton_info[id].text_left, font_nr);
+
+ if (counterbutton_info[id].text_right)
+ DrawText(x_right, y, counterbutton_info[id].text_right, font_nr);
+
+ MapGadget(gi_down);
+ MapGadget(gi_text);
+ MapGadget(gi_up);
+}
+
+static void MapControlButtons(void)
+{
+ int counter_id;
+ int i;
+
+ // map toolbox buttons (excluding special CE toolbox buttons)
+ for (i = 0; i < ED_NUM_CTRL1_2_BUTTONS; i++)
+ MapGadget(level_editor_gadget[i]);
+
+ // map toolbox buttons (element properties buttons)
+ for (i = ED_NUM_CTRL1_4_BUTTONS; i < ED_NUM_CTRL1_7_BUTTONS; i++)
+ MapGadget(level_editor_gadget[i]);
+
+ if (use_permanent_palette)
+ {
+ // map buttons to select elements
+ for (i = 0; i < ED_NUM_ELEMENTLIST_BUTTONS; i++)
+ MapGadget(level_editor_gadget[GADGET_ID_ELEMENTLIST_FIRST + i]);
+ MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL]);
+ MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_UP]);
+ MapGadget(level_editor_gadget[GADGET_ID_SCROLL_LIST_DOWN]);
+ }
+
+ // map buttons to select level
+ counter_id = ED_COUNTER_ID_SELECT_LEVEL;
+ counterbutton_info[counter_id].min_value = leveldir_current->first_level;
+ counterbutton_info[counter_id].max_value = leveldir_current->last_level;
+ MapCounterButtons(counter_id);
+}
+
+static void MapDrawingArea(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[drawingarea_info[id].gadget_id];
+ int area_xsize = gi->drawing.area_xsize;
+ int area_ysize = gi->drawing.area_ysize;
+ int xoffset_left = getTextWidthForDrawingArea(drawingarea_info[id].text_left);
+ int xoffset_below = getTextWidth(drawingarea_info[id].text_below, font_nr);
+ int x_left = gi->x - xoffset_left - ED_DRAWINGAREA_BORDER_SIZE;
+ int x_right = gi->x + gi->width + ED_DRAWINGAREA_TEXT_DISTANCE;
+ int x_above = gi->x - ED_DRAWINGAREA_BORDER_SIZE;
+ int x_below = gi->x + (gi->width - xoffset_below) / 2;
+ int y_side = gi->y + (gi->height - font_height) / 2;
+ int y_above = gi->y - font_height - ED_DRAWINGAREA_TEXT_DISTANCE;
+ int y_below = gi->y + gi->height + ED_DRAWINGAREA_TEXT_DISTANCE;
+
+ if (drawingarea_info[id].text_left)
+ DrawText(x_left, y_side, drawingarea_info[id].text_left, font_nr);
+
+ if (drawingarea_info[id].text_right)
+ DrawText(x_right, y_side, drawingarea_info[id].text_right, font_nr);
+
+ if (drawingarea_info[id].text_above)
+ DrawText(x_above, y_above, drawingarea_info[id].text_above, font_nr);
+
+ if (drawingarea_info[id].text_below)
+ DrawText(x_below, y_below, drawingarea_info[id].text_below, font_nr);
+
+ if (id != ED_DRAWING_ID_DRAWING_LEVEL)
+ {
+ DrawElementBorder(gi->x, gi->y,
+ area_xsize * ED_DRAWINGAREA_TILE_SIZE,
+ area_ysize * ED_DRAWINGAREA_TILE_SIZE, TRUE);
+
+ DrawDrawingArea(id);
+ }
+
+ MapGadget(gi);
+}
+
+static void MapTextInputGadget(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[textinput_info[id].gadget_id];
+ int xoffset_left = getTextWidthForGadget(textinput_info[id].text_left);
+ int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+ int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
+ int yoffset = (gi->height - font_height) / 2;
+ int x_left = gi->x - xoffset_left;
+ int x_right = gi->x + gi->width + xoffset_right;
+ int y_above = gi->y - yoffset_above;
+ int y = gi->y + yoffset;
+
+ if (textinput_info[id].text_above)
+ DrawText(x_left, y_above, textinput_info[id].text_above, font_nr);
+
+ if (textinput_info[id].text_left)
+ DrawText(x_left, y, textinput_info[id].text_left, font_nr);
+
+ if (textinput_info[id].text_right)
+ DrawText(x_right, y, textinput_info[id].text_right, font_nr);
+
+ ModifyGadget(gi, GDI_TEXT_VALUE, textinput_info[id].value, GDI_END);
+
+ MapGadget(gi);
+}
+
+static void MapTextAreaGadget(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[textarea_info[id].gadget_id];
+ int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
+ int x_above = ED_SETTINGS_X(textarea_info[id].x);
+ int y_above = ED_SETTINGS_Y(textarea_info[id].y) - yoffset_above;
+ char *text_above = textarea_info[id].text_above;
+
+ if (gi->textarea.cropped && textarea_info[id].text_above_cropped)
+ text_above = textarea_info[id].text_above_cropped;
+
+ if (text_above)
+ DrawTextS(x_above, y_above, font_nr, text_above);
+
+ ModifyGadget(gi, GDI_TEXT_VALUE, textarea_info[id].value, GDI_END);
+
+ MapGadget(gi);
+}
+
+static void MapSelectboxGadget(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[selectbox_info[id].gadget_id];
+ int xoffset_left = getTextWidthForGadget(selectbox_info[id].text_left);
+ int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+ int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
+ int yoffset = (gi->height - font_height) / 2;
+ int x_left = gi->x - xoffset_left;
+ int x_right = gi->x + gi->width + xoffset_right;
+ int y_above = gi->y - yoffset_above;
+ int y = gi->y + yoffset;
+
+ if (selectbox_info[id].text_above)
+ DrawText(x_left, y_above, selectbox_info[id].text_above, font_nr);
+
+ if (selectbox_info[id].text_left)
+ DrawText(x_left, y, selectbox_info[id].text_left, font_nr);
+
+ if (selectbox_info[id].text_right)
+ DrawText(x_right, y, selectbox_info[id].text_right, font_nr);
+
+ ModifyEditorSelectboxValue(id, *selectbox_info[id].value);
+
+ MapGadget(gi);
+}
+
+static void MapTextbuttonGadget(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[textbutton_info[id].gadget_id];
+ int xoffset_left = getTextWidthForGadget(textbutton_info[id].text_left);
+ int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+ int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
+ int yoffset = (gi->height - font_height) / 2;
+ int x_left = gi->x - xoffset_left;
+ int x_right = gi->x + gi->width + xoffset_right;
+ int y_above = gi->y - yoffset_above;
+ int x = gi->x;
+ int y = gi->y + yoffset;
+
+ // only show button to delete change pages when more than minimum pages
+ if (id == ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE &&
+ custom_element.num_change_pages == MIN_CHANGE_PAGES)
+ return;
+
+ if (textbutton_info[id].text_above)
+ DrawText(x, y_above, textbutton_info[id].text_above, font_nr);
+
+ if (textbutton_info[id].text_left)
+ DrawText(x_left, y, textbutton_info[id].text_left, font_nr);
+
+ if (textbutton_info[id].text_right)
+ DrawText(x_right, y, textbutton_info[id].text_right, font_nr);
+
+ MapGadget(gi);
+}
+
+static void MapGraphicbuttonGadget(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[graphicbutton_info[id].gadget_id];
+ int xoffset_left = getTextWidthForGadget(graphicbutton_info[id].text_left);
+ int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+ int yoffset = (gi->height - font_height) / 2;
+ int x_left = gi->x - xoffset_left;
+ int x_right = gi->x + gi->width + xoffset_right;
+ int y = gi->y + yoffset;
+
+ if (graphicbutton_info[id].text_left)
+ DrawText(x_left, y, graphicbutton_info[id].text_left, font_nr);
+
+ if (graphicbutton_info[id].text_right)
+ DrawText(x_right, y, graphicbutton_info[id].text_right, font_nr);
+
+ MapGadget(gi);
+}
+
+static void MapRadiobuttonGadget(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[radiobutton_info[id].gadget_id];
+ int xoffset_left = getTextWidthForGadget(checkbutton_info[id].text_left);
+ int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+ int yoffset = (gi->height - font_height) / 2;
+ int x_left = gi->x - xoffset_left;
+ int x_right = gi->x + gi->width + xoffset_right;
+ int y = gi->y + yoffset;
+ boolean checked =
+ (*radiobutton_info[id].value == radiobutton_info[id].checked_value);
+
+ if (radiobutton_info[id].text_left)
+ DrawText(x_left, y, radiobutton_info[id].text_left, font_nr);
+
+ if (radiobutton_info[id].text_right)
+ DrawText(x_right, y, radiobutton_info[id].text_right, font_nr);
+
+ ModifyGadget(gi, GDI_CHECKED, checked, GDI_END);
+
+ MapGadget(gi);
+}
+
+static void MapCheckbuttonGadget(int id)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ struct GadgetInfo *gi = level_editor_gadget[checkbutton_info[id].gadget_id];
+ int xoffset_left = getTextWidthForGadget(checkbutton_info[id].text_left);
+ int xoffset_right = ED_GADGET_TEXT_DISTANCE;
+ int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
+ int yoffset = (gi->height - font_height) / 2;
+ int y_above = gi->y - yoffset_above;
+ int x = gi->x;
+ int x_left, x_right, y; // set after gadget position was modified
+
+ // set position for gadgets with dynamically determined position
+ if (checkbutton_info[id].x != -1) // do not change dynamic positions
+ ModifyGadget(gi, GDI_X, SX + ED_SETTINGS_X(checkbutton_info[id].x), GDI_END);
+ ModifyGadget(gi, GDI_Y, SY + ED_SETTINGS_Y(checkbutton_info[id].y), GDI_END);
+
+ x_left = gi->x - xoffset_left;
+ x_right = gi->x + gi->width + xoffset_right;
+ y = gi->y + yoffset;
+
+ if (checkbutton_info[id].text_above)
+ DrawText(x, y_above, checkbutton_info[id].text_above, font_nr);
+
+ if (checkbutton_info[id].text_left)
+ DrawText(x_left, y, checkbutton_info[id].text_left, font_nr);
+
+ if (checkbutton_info[id].text_right)
+ DrawText(x_right, y, checkbutton_info[id].text_right, font_nr);
+
+ ModifyGadget(gi, GDI_CHECKED, *checkbutton_info[id].value, GDI_END);
+
+ MapGadget(gi);
+}
+
+static void MapMainDrawingArea(void)
+{
+ boolean no_horizontal_scrollbar = (lev_fieldx + 2 <= ed_fieldx);
+ boolean no_vertical_scrollbar = (lev_fieldy + 2 <= ed_fieldy);
+ int i;
+
+ if (suppressBorderElement())
+ {
+ no_horizontal_scrollbar = (lev_fieldx <= ed_fieldx);
+ no_vertical_scrollbar = (lev_fieldy <= ed_fieldy);
+ }
+
+ for (i = ED_SCROLLBUTTON_ID_AREA_FIRST; i <= ED_SCROLLBUTTON_ID_AREA_LAST; i++)
+ {
+ if (((i == ED_SCROLLBUTTON_ID_AREA_LEFT ||
+ i == ED_SCROLLBUTTON_ID_AREA_RIGHT) &&
+ no_horizontal_scrollbar) ||
+ ((i == ED_SCROLLBUTTON_ID_AREA_UP ||
+ i == ED_SCROLLBUTTON_ID_AREA_DOWN) &&
+ no_vertical_scrollbar))
+ continue;
+
+ MapGadget(level_editor_gadget[scrollbutton_info[i].gadget_id]);
+ }
+
+ for (i = ED_SCROLLBAR_ID_AREA_FIRST; i <= ED_SCROLLBAR_ID_AREA_LAST; i++)
+ {
+ if ((i == ED_SCROLLBAR_ID_AREA_HORIZONTAL && no_horizontal_scrollbar) ||
+ (i == ED_SCROLLBAR_ID_AREA_VERTICAL && no_vertical_scrollbar))
+ continue;
+
+ MapGadget(level_editor_gadget[scrollbar_info[i].gadget_id]);
+ }
+
+ MapDrawingArea(ED_DRAWING_ID_DRAWING_LEVEL);
+}
+
+static void MapOrUnmapLevelEditorToolboxCustomGadgets(boolean map)
+{
+ int i;
+
+ for (i = 0; i < ED_NUM_CTRL_BUTTONS; i++)
+ {
+ if (i == GADGET_ID_CUSTOM_COPY_FROM ||
+ i == GADGET_ID_CUSTOM_COPY_TO ||
+ i == GADGET_ID_CUSTOM_EXCHANGE ||
+ i == GADGET_ID_CUSTOM_COPY ||
+ i == GADGET_ID_CUSTOM_PASTE)
+ {
+ if (map)
+ MapGadget(level_editor_gadget[i]);
+ else
+ UnmapGadget(level_editor_gadget[i]);
+ }
+ }
+}
+
+static void MapLevelEditorToolboxCustomGadgets(void)
+{
+ MapOrUnmapLevelEditorToolboxCustomGadgets(TRUE);
+}
+
+static void MapLevelEditorToolboxCustomGadgetsIfNeeded(void)
+{
+ if (IS_CUSTOM_ELEMENT(properties_element) ||
+ IS_GROUP_ELEMENT(properties_element) ||
+ IS_EMPTY_ELEMENT(properties_element))
+ MapLevelEditorToolboxCustomGadgets();
+}
+
+static void UnmapLevelEditorToolboxCustomGadgets(void)
+{
+ MapOrUnmapLevelEditorToolboxCustomGadgets(FALSE);
+}
+
+static void MapOrUnmapLevelEditorToolboxDrawingGadgets(boolean map)
+{
+ int i;
+
+ for (i = 0; i < ED_NUM_CTRL1_BUTTONS; i++)
+ {
+ if (i != GADGET_ID_SINGLE_ITEMS &&
+ i != GADGET_ID_PICK_ELEMENT)
+ {
+ struct GadgetInfo *gi = level_editor_gadget[i];
+
+ if (map)
+ {
+ MapGadget(gi);
+ }
+ else
+ {
+ int graphic = IMG_EDITOR_NO_TOOLBOX_BUTTON;
+ struct GraphicInfo *gd = &graphic_info[graphic];
+
+ UnmapGadget(gi);
+
+ BlitBitmap(gd->bitmap, drawto, gd->src_x, gd->src_y,
+ gi->width, gi->height, gi->x, gi->y);
+
+ redraw_mask |= REDRAW_DOOR_3;
+ }
+ }
+ }
+}
+
+static void MapLevelEditorToolboxDrawingGadgets(void)
+{
+ MapOrUnmapLevelEditorToolboxDrawingGadgets(TRUE);
+}
+
+static void UnmapLevelEditorToolboxDrawingGadgets(void)
+{
+ MapOrUnmapLevelEditorToolboxDrawingGadgets(FALSE);
+}
+
+static void UnmapDrawingArea(int id)
+{
+ UnmapGadget(level_editor_gadget[drawingarea_info[id].gadget_id]);
+}
+
+static void UnmapLevelEditorFieldGadgets(void)
+{
+ int i;
+
+ for (i = 0; i < num_editor_gadgets; i++)
+ if (IN_GFX_FIELD_FULL(level_editor_gadget[i]->x,
+ level_editor_gadget[i]->y))
+ UnmapGadget(level_editor_gadget[i]);
+}
+
+void UnmapLevelEditorGadgets(void)
+{
+ int i;
+
+ for (i = 0; i < num_editor_gadgets; i++)
+ UnmapGadget(level_editor_gadget[i]);
+}
+
+static void ResetUndoBuffer(void)
+{
+ undo_buffer_position = -1;
+ undo_buffer_steps = -1;
+ redo_buffer_steps = 0;
+
+ CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+
+ level.changed = FALSE;
+}
+
+static void DrawEditModeWindowExt(boolean remap_toolbox_gadgets)
+{
+ if (remap_toolbox_gadgets)
+ {
+ ModifyEditorElementList();
+ RedrawDrawingElements();
+ }
+
+ if (edit_mode == ED_MODE_LEVELCONFIG)
+ DrawLevelConfigWindow();
+ else if (edit_mode == ED_MODE_PROPERTIES)
+ DrawPropertiesWindow();
+ else if (edit_mode == ED_MODE_PALETTE)
+ DrawPaletteWindow();
+ else // edit_mode == ED_MODE_DRAWING
+ DrawDrawingWindowExt(remap_toolbox_gadgets);
+}
+
+static void DrawEditModeWindow(void)
+{
+ DrawEditModeWindowExt(TRUE);
+}
+
+static void DrawEditModeWindow_PlayfieldOnly(void)
+{
+ DrawEditModeWindowExt(FALSE);
+}
+
+static void ChangeEditModeWindow(int new_edit_mode)
+{
+ edit_mode = (new_edit_mode != edit_mode ? new_edit_mode : ED_MODE_DRAWING);
+
+ DrawEditModeWindow();
+}
+
+static boolean LevelChanged(void)
+{
+ boolean field_changed = FALSE;
+ int x, y;
+
+ for (y = 0; y < lev_fieldy; y++)
+ for (x = 0; x < lev_fieldx; x++)
+ if (Tile[x][y] != level.field[x][y])
+ field_changed = TRUE;
+
+ return (level.changed || field_changed);
+}
+
+static boolean PrepareSavingIntoPersonalLevelSet(void)
+{
+ static LevelDirTree *last_copied_leveldir = NULL;
+ static LevelDirTree *last_written_leveldir = NULL;
+ static int last_copied_level_nr = -1;
+ static int last_written_level_nr = -1;
+ LevelDirTree *leveldir_former = leveldir_current;
+ int level_nr_former = level_nr;
+ int new_level_nr;
+
+ // remember last mod/save so that for current session, we write
+ // back to the same personal copy, asking only about overwrite.
+ if (leveldir_current == last_copied_leveldir &&
+ level_nr == last_copied_level_nr)
+ {
+ // "cd" to personal level set dir (as used when writing last copy)
+ leveldir_current = last_written_leveldir;
+ level_nr = last_written_level_nr;
+
+ return TRUE;
+ }
+
+ if (!Request("This level is read-only! "
+ "Save into personal level set?", REQ_ASK))
+ return FALSE;
+
+ // "cd" to personal level set dir (for writing copy the first time)
+ leveldir_current =
+ getTreeInfoFromIdentifier(leveldir_first, getLoginName());
+
+ // this may happen if "setup.internal.create_user_levelset" is FALSE
+ // or if file "levelinfo.conf" is missing in personal user level set
+ if (leveldir_current == NULL)
+ {
+ Request("Cannot find personal level set?!", REQ_CONFIRM);
+
+ leveldir_current = leveldir_former;
+
+ return FALSE;
+ }
+
+ // find unused level number
+ for (new_level_nr = leveldir_current->first_level; ; new_level_nr++)
+ {
+ static char *level_filename = NULL;
+
+ setString(&level_filename, getDefaultLevelFilename(new_level_nr));
+
+ if (!fileExists(level_filename))
+ break;
+ }
+
+ last_copied_leveldir = leveldir_former;
+ last_copied_level_nr = level_nr_former;
+
+ last_written_leveldir = leveldir_current;
+ last_written_level_nr = level_nr = new_level_nr;
+
+ return TRUE;
+}
+
+static void ModifyLevelConfigForSavingIntoPersonalLevelSet(char *former_name)
+{
+ static char *filename_levelinfo = NULL, *mod_name = NULL;
+ FILE *file;
+
+ // annotate this copy-and-mod in personal levelinfo.conf
+ setString(&filename_levelinfo,
+ getPath2(getCurrentLevelDir(), LEVELINFO_FILENAME));
+
+ if ((file = fopen(filename_levelinfo, MODE_APPEND)))
+ {
+ fprintf(file, "\n");
+ fprintf(file, "# level %d was modified from:\n", level_nr);
+ fprintf(file, "# - previous level set name: %s\n",
+ former_name);
+ fprintf(file, "# - level within previous set: %d \"%s\"\n",
+ level.file_info.nr, level.name);
+ fprintf(file, "# - previous author: %s\n",
+ level.author);
+ fprintf(file, "# - previous save date: ");
+
+ if (level.creation_date.src == DATE_SRC_LEVELFILE)
+ {
+ fprintf(file, "%04d-%02d-%02d\n",
+ level.creation_date.year,
+ level.creation_date.month,
+ level.creation_date.day);
+ }
+ else
+ {
+ fprintf(file, "not recorded\n");
+ }
+
+ fclose(file);
+ }
+
+ if (level_nr > leveldir_current->last_level)
+ UpdateUserLevelSet(getLoginName(), NULL, NULL, level_nr + 9);
+
+ // else: allow the save even if annotation failed
+
+ // now... spray graffiti on the old level vital statistics
+ // user can change these; just trying to set a good baseline
+
+ // don't truncate names for fear of making offensive or silly:
+ // long-named original author only recorded in levelinfo.conf.
+ // try to fit "Joe after Bob", "Joe (ed.)", then just "Joe"
+ if (!strEqual(level.author, leveldir_current->author))
+ {
+ setString(&mod_name, getStringCat3(leveldir_current->author,
+ " after ", level.author));
+
+ if (strlen(mod_name) > MAX_LEVEL_AUTHOR_LEN)
+ setString(&mod_name,
+ getStringCat2(leveldir_current->author, " (ed.)"));
+
+ if (strlen(mod_name) > MAX_LEVEL_AUTHOR_LEN)
+ setString(&mod_name, leveldir_current->author);
+
+ strncpy(level.author, mod_name, MAX_LEVEL_AUTHOR_LEN);
+
+ // less worried about truncation here
+ setString(&mod_name, getStringCat2("Mod: ", level.name));
+ strncpy(level.name, mod_name, MAX_LEVEL_NAME_LEN);
+ }
+}
+
+static void CopyPlayfield(short src[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
+ short dst[MAX_LEV_FIELDX][MAX_LEV_FIELDY])
+{
+ int x, y;
+
+ for (x = 0; x < lev_fieldx; x++)
+ for (y = 0; y < lev_fieldy; y++)
+ dst[x][y] = src[x][y];
+}
+
+static int setSelectboxValue(int selectbox_id, int new_value)
+{
+ int new_index_value = 0;
+ int i;
+
+ for (i = 0; selectbox_info[selectbox_id].options[i].text != NULL; i++)
+ if (selectbox_info[selectbox_id].options[i].value == new_value)
+ new_index_value = i;
+
+ *selectbox_info[selectbox_id].value =
+ selectbox_info[selectbox_id].options[new_index_value].value;
+
+ return new_index_value;
+}
+
+static void setSelectboxSpecialActionVariablesIfNeeded(void)
+{
+ int i;
+
+ // change action mode and arg variables according to action type variable
+ for (i = 0; action_arg_options[i].value != -1; i++)
+ {
+ if (action_arg_options[i].value == custom_element_change.action_type)
+ {
+ int mode = action_arg_options[i].mode;
+
+ // only change if corresponding selectbox has changed
+ if (selectbox_info[ED_SELECTBOX_ID_ACTION_MODE].options !=
+ action_arg_modes[mode])
+ custom_element_change.action_mode = -1;
+
+ // only change if corresponding selectbox has changed
+ if (selectbox_info[ED_SELECTBOX_ID_ACTION_ARG].options !=
+ action_arg_options[i].options)
+ custom_element_change.action_arg = -1;
+
+ break;
+ }
+ }
+}
+
+static void setSelectboxSpecialActionOptions(void)
+{
+ int i;
+
+ // change action mode and arg selectbox according to action type selectbox
+ for (i = 0; action_arg_options[i].value != -1; i++)
+ {
+ if (action_arg_options[i].value == custom_element_change.action_type)
+ {
+ int mode = action_arg_options[i].mode;
+
+ ModifyEditorSelectboxOptions(ED_SELECTBOX_ID_ACTION_MODE,
+ action_arg_modes[mode]);
+ ModifyEditorSelectboxValue(ED_SELECTBOX_ID_ACTION_MODE,
+ custom_element_change.action_mode);
+
+ ModifyEditorSelectboxOptions(ED_SELECTBOX_ID_ACTION_ARG,
+ action_arg_options[i].options);
+ ModifyEditorSelectboxValue(ED_SELECTBOX_ID_ACTION_ARG,
+ custom_element_change.action_arg);
+ break;
+ }
+ }
+}
+
+static void copy_custom_element_settings(int element_from, int element_to)
+{
+ struct ElementInfo *ei_from = &element_info[element_from];
+ struct ElementInfo *ei_to = &element_info[element_to];
+
+ copyElementInfo(ei_from, ei_to);
+}
+
+static void replace_custom_element_in_settings(int element_from,
+ int element_to)
+{
+ int i, j, x, y;
+
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
+
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
+ if (ei->content.e[x][y] == element_from)
+ ei->content.e[x][y] = element_to;
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ if (change->target_element == element_from)
+ change->target_element = element_to;
+
+ if (change->initial_trigger_element == element_from)
+ change->initial_trigger_element = element_to;
+
+ if (change->action_element == element_from)
+ change->action_element = element_to;
+
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
+ if (change->target_content.e[x][y] == element_from)
+ change->target_content.e[x][y] = element_to;
+ }
+
+ if (ei->group != NULL) // group or internal
+ for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
+ if (ei->group->element[j] == element_from)
+ ei->group->element[j] = element_to;
+ }
+}
+
+static void replace_custom_element_in_playfield(int element_from,
+ int element_to)
+{
+ int x, y;
+
+ for (x = 0; x < lev_fieldx; x++)
+ for (y = 0; y < lev_fieldy; y++)
+ if (Tile[x][y] == element_from)
+ Tile[x][y] = element_to;
+}
+
+static boolean CopyCustomElement(int element_old, int element_new,
+ int copy_mode)
+{
+ int copy_mode_orig = copy_mode;
+
+ if (copy_mode == GADGET_ID_CUSTOM_COPY)
+ {
+ element_new = (IS_CUSTOM_ELEMENT(element_old) ?
+ EL_INTERNAL_CLIPBOARD_CUSTOM : EL_INTERNAL_CLIPBOARD_GROUP);
+ copy_mode = GADGET_ID_CUSTOM_COPY_TO;
+ }
+ else if (copy_mode == GADGET_ID_CUSTOM_PASTE)
+ {
+ element_old = (IS_CUSTOM_ELEMENT(element_new) ?
+ EL_INTERNAL_CLIPBOARD_CUSTOM : EL_INTERNAL_CLIPBOARD_GROUP);
+ copy_mode = GADGET_ID_CUSTOM_COPY_TO;
+
+ level.changed = TRUE;
+ }
+ else if (IS_CUSTOM_ELEMENT(element_old) && !IS_CUSTOM_ELEMENT(element_new))
+ {
+ Request("Please choose custom element!", REQ_CONFIRM);
+
+ return FALSE;
+ }
+ else if (IS_GROUP_ELEMENT(element_old) && !IS_GROUP_ELEMENT(element_new))
+ {
+ Request("Please choose group element!", REQ_CONFIRM);
+
+ return FALSE;
+ }
+ else if (IS_EMPTY_ELEMENT(element_old) && !IS_EMPTY_ELEMENT(element_new))
+ {
+ Request("Please choose empty element!", REQ_CONFIRM);
+
+ return FALSE;
+ }
+ else
+ {
+ level.changed = TRUE;
+ }
+
+ // when modifying custom/group element, ask for copying level template
+ if (copy_mode_orig != GADGET_ID_CUSTOM_COPY && level.use_custom_template)
+ {
+ if (!AskToCopyAndModifyLevelTemplate())
+ return FALSE;
+ }
+
+ if (copy_mode == GADGET_ID_CUSTOM_COPY_FROM)
+ {
+ copy_custom_element_settings(element_new, element_old);
+ }
+ else if (copy_mode == GADGET_ID_CUSTOM_COPY_TO)
+ {
+ copy_custom_element_settings(element_old, element_new);
+ }
+ else if (copy_mode == GADGET_ID_CUSTOM_EXCHANGE)
+ {
+ copy_custom_element_settings(element_old, EL_INTERNAL_DUMMY);
+ copy_custom_element_settings(element_new, element_old);
+ copy_custom_element_settings(EL_INTERNAL_DUMMY, element_new);
+
+ replace_custom_element_in_settings(element_old, EL_INTERNAL_DUMMY);
+ replace_custom_element_in_settings(element_new, element_old);
+ replace_custom_element_in_settings(EL_INTERNAL_DUMMY, element_new);
+
+ replace_custom_element_in_playfield(element_old, EL_INTERNAL_DUMMY);
+ replace_custom_element_in_playfield(element_new, element_old);
+ replace_custom_element_in_playfield(EL_INTERNAL_DUMMY, element_new);
+ }
+
+ UpdateCustomElementGraphicGadgets();
+ DrawPropertiesWindow();
+
+ return TRUE;
+}
+
+static void CopyCustomElementPropertiesToEditor(int element)
+{
+ int i;
+ int current_change_page = element_info[element].current_change_page;
+
+ // dynamically (re)build selectbox for selecting change page
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ sprintf(options_change_page_strings[i], "%d", i + 1);
+
+ options_change_page[i].value = i;
+ options_change_page[i].text = options_change_page_strings[i];
+ }
+
+ options_change_page[i].value = -1;
+ options_change_page[i].text = NULL;
+
+ // needed here to initialize combined element properties
+ InitElementPropertiesEngine(level.game_version);
+
+ element_info[element].change =
+ &element_info[element].change_page[current_change_page];
+
+ custom_element = element_info[element];
+ custom_element_change = *element_info[element].change;
+
+ // needed to initially set selectbox options for special action options
+ setSelectboxSpecialActionOptions();
+
+ // needed to initially set selectbox value variables to reliable defaults
+ for (i = 0; i < ED_NUM_SELECTBOX; i++)
+ setSelectboxValue(i, *selectbox_info[i].value);
+
+ for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
+ custom_element_properties[i] = HAS_PROPERTY(element, i);
+
+ for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+ custom_element_change_events[i] = HAS_CHANGE_EVENT(element, i);
+
+ // ---------- element settings: configure (custom elements) -----------------
+
+ // set accessible layer selectbox help value
+ custom_element.access_type =
+ (IS_WALKABLE(element) ? EP_WALKABLE :
+ IS_PASSABLE(element) ? EP_PASSABLE :
+ custom_element.access_type);
+ custom_element.access_layer =
+ (IS_ACCESSIBLE_OVER(element) ? EP_ACCESSIBLE_OVER :
+ IS_ACCESSIBLE_INSIDE(element) ? EP_ACCESSIBLE_INSIDE :
+ IS_ACCESSIBLE_UNDER(element) ? EP_ACCESSIBLE_UNDER :
+ custom_element.access_layer);
+ custom_element.access_protected =
+ (IS_PROTECTED(element) ? 1 : 0);
+ custom_element_properties[EP_ACCESSIBLE] =
+ (IS_ACCESSIBLE_OVER(element) ||
+ IS_ACCESSIBLE_INSIDE(element) ||
+ IS_ACCESSIBLE_UNDER(element));
+
+ // set walk-to-object action selectbox help value
+ custom_element.walk_to_action =
+ (IS_DIGGABLE(element) ? EP_DIGGABLE :
+ IS_COLLECTIBLE_ONLY(element) ? EP_COLLECTIBLE_ONLY :
+ IS_DROPPABLE(element) ? EP_DROPPABLE :
+ IS_THROWABLE(element) ? EP_THROWABLE :
+ IS_PUSHABLE(element) ? EP_PUSHABLE :
+ custom_element.walk_to_action);
+ custom_element_properties[EP_WALK_TO_OBJECT] =
+ (IS_DIGGABLE(element) ||
+ IS_COLLECTIBLE_ONLY(element) ||
+ IS_DROPPABLE(element) ||
+ IS_THROWABLE(element) ||
+ IS_PUSHABLE(element));
+
+ // set smash targets selectbox help value
+ custom_element.smash_targets =
+ (CAN_SMASH_EVERYTHING(element) ? EP_CAN_SMASH_EVERYTHING :
+ CAN_SMASH_ENEMIES(element) ? EP_CAN_SMASH_ENEMIES :
+ CAN_SMASH_PLAYER(element) ? EP_CAN_SMASH_PLAYER :
+ custom_element.smash_targets);
+ custom_element_properties[EP_CAN_SMASH] =
+ (CAN_SMASH_EVERYTHING(element) ||
+ CAN_SMASH_ENEMIES(element) ||
+ CAN_SMASH_PLAYER(element));
+
+ // set deadliness selectbox help value
+ custom_element.deadliness =
+ (DONT_TOUCH(element) ? EP_DONT_TOUCH :
+ DONT_GET_HIT_BY(element) ? EP_DONT_GET_HIT_BY :
+ DONT_COLLIDE_WITH(element) ? EP_DONT_COLLIDE_WITH :
+ DONT_RUN_INTO(element) ? EP_DONT_RUN_INTO :
+ custom_element.deadliness);
+ custom_element_properties[EP_DEADLY] =
+ (DONT_TOUCH(element) ||
+ DONT_GET_HIT_BY(element) ||
+ DONT_COLLIDE_WITH(element) ||
+ DONT_RUN_INTO(element));
+
+ // ---------- element settings: advanced (custom elements) ------------------
+
+ // set "change by direct action" selectbox help value
+ custom_element_change.direct_action =
+ (HAS_CHANGE_EVENT(element, CE_NEXT_TO_PLAYER) ? CE_NEXT_TO_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_TOUCHED_BY_PLAYER) ? CE_TOUCHED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_PRESSED_BY_PLAYER) ? CE_PRESSED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_SWITCHED_BY_PLAYER) ? CE_SWITCHED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_SNAPPED_BY_PLAYER) ? CE_SNAPPED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_PUSHED_BY_PLAYER) ? CE_PUSHED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_ENTERED_BY_PLAYER) ? CE_ENTERED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_LEFT_BY_PLAYER) ? CE_LEFT_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_DROPPED_BY_PLAYER) ? CE_DROPPED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_SWITCHED) ? CE_SWITCHED :
+ HAS_CHANGE_EVENT(element, CE_HITTING_SOMETHING) ? CE_HITTING_SOMETHING :
+ HAS_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING) ? CE_HIT_BY_SOMETHING :
+ HAS_CHANGE_EVENT(element, CE_BLOCKED) ? CE_BLOCKED :
+ HAS_CHANGE_EVENT(element, CE_IMPACT) ? CE_IMPACT :
+ HAS_CHANGE_EVENT(element, CE_SMASHED) ? CE_SMASHED :
+ HAS_CHANGE_EVENT(element, CE_VALUE_CHANGES) ? CE_VALUE_CHANGES :
+ HAS_CHANGE_EVENT(element, CE_SCORE_CHANGES) ? CE_SCORE_CHANGES :
+ HAS_CHANGE_EVENT(element, CE_VALUE_GETS_ZERO) ? CE_VALUE_GETS_ZERO :
+ HAS_CHANGE_EVENT(element, CE_SCORE_GETS_ZERO) ? CE_SCORE_GETS_ZERO :
+ HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ? CE_CLICKED_BY_MOUSE :
+ HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ? CE_PRESSED_BY_MOUSE :
+ custom_element_change.direct_action);