added options for alignment of selection lists on setup sub-screens
[rocksndiamonds.git] / src / screens.c
index 06732b87cbd5c27a1d136d822151801cfed8d568..5195cacd33776452acc609b371898fa2ad5b7e3c 100644 (file)
 // screen gadget identifiers
 #define SCREEN_CTRL_ID_PREV_LEVEL      0
 #define SCREEN_CTRL_ID_NEXT_LEVEL      1
-#define SCREEN_CTRL_ID_PREV_PLAYER     2
-#define SCREEN_CTRL_ID_NEXT_PLAYER     3
-#define SCREEN_CTRL_ID_INSERT_SOLUTION 4
-#define SCREEN_CTRL_ID_PLAY_SOLUTION   5
-#define SCREEN_CTRL_ID_SCROLL_UP       6
-#define SCREEN_CTRL_ID_SCROLL_DOWN     7
-#define SCREEN_CTRL_ID_SCROLL_VERTICAL 8
-#define SCREEN_CTRL_ID_NETWORK_SERVER  9
-
-#define NUM_SCREEN_GADGETS             10
-
-#define NUM_SCREEN_MENUBUTTONS         6
+#define SCREEN_CTRL_ID_FIRST_LEVEL     2
+#define SCREEN_CTRL_ID_LAST_LEVEL      3
+#define SCREEN_CTRL_ID_LEVEL_NUMBER    4
+#define SCREEN_CTRL_ID_PREV_PLAYER     5
+#define SCREEN_CTRL_ID_NEXT_PLAYER     6
+#define SCREEN_CTRL_ID_INSERT_SOLUTION 7
+#define SCREEN_CTRL_ID_PLAY_SOLUTION   8
+#define SCREEN_CTRL_ID_SCROLL_UP       9
+#define SCREEN_CTRL_ID_SCROLL_DOWN     10
+#define SCREEN_CTRL_ID_SCROLL_VERTICAL 11
+#define SCREEN_CTRL_ID_NETWORK_SERVER  12
+
+#define NUM_SCREEN_GADGETS             13
+
+#define NUM_SCREEN_MENUBUTTONS         9
 #define NUM_SCREEN_SCROLLBUTTONS       2
 #define NUM_SCREEN_SCROLLBARS          1
 #define NUM_SCREEN_TEXTINPUT           1
@@ -520,6 +523,9 @@ static struct ValueTextInfo grid_sizes_list[] =
   {    -1,     NULL                            },
 };
 
+static int align_xoffset = 0;
+static int align_yoffset = 0;
+
 #define DRAW_MODE(s)           ((s) >= GAME_MODE_MAIN &&               \
                                 (s) <= GAME_MODE_SETUP ? (s) :         \
                                 (s) == GAME_MODE_PSEUDO_TYPENAME ?     \
@@ -576,6 +582,9 @@ static struct ValueTextInfo grid_sizes_list[] =
 #define mSX                    (SX + DRAW_XOFFSET(game_status))
 #define mSY                    (SY + DRAW_YOFFSET(game_status))
 
+#define amSX                   (mSX + align_xoffset)
+#define amSY                   (mSY + align_yoffset)
+
 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?  \
                                    menu.list_size[game_status] :       \
                                    MAX_MENU_ENTRIES_ON_SCREEN)
@@ -724,36 +733,33 @@ static struct MainControlInfo main_controls[] =
     &menu.main.text.quit,              &main_text_quit,
     NULL,                              NULL,
   },
-#if 0
-  // (these two buttons are real gadgets)
   {
     MAIN_CONTROL_PREV_LEVEL,
-    &menu.main.button.prev_level,      IMG_MENU_BUTTON_PREV_LEVEL,
+    NULL,                              -1,
     NULL,                              NULL,
     NULL,                              NULL,
   },
   {
     MAIN_CONTROL_NEXT_LEVEL,
-    &menu.main.button.next_level,      IMG_MENU_BUTTON_NEXT_LEVEL,
+    NULL,                              -1,
     NULL,                              NULL,
     NULL,                              NULL,
   },
-#endif
   {
     MAIN_CONTROL_FIRST_LEVEL,
-    &menu.main.button.first_level,     IMG_MENU_BUTTON_FIRST_LEVEL,
+    NULL,                              -1,
     &menu.main.text.first_level,       &main_text_first_level,
     NULL,                              NULL,
   },
   {
     MAIN_CONTROL_LAST_LEVEL,
-    &menu.main.button.last_level,      IMG_MENU_BUTTON_LAST_LEVEL,
+    NULL,                              -1,
     &menu.main.text.last_level,                &main_text_last_level,
     NULL,                              NULL,
   },
   {
     MAIN_CONTROL_LEVEL_NUMBER,
-    &menu.main.button.level_number,    IMG_MENU_BUTTON_LEVEL_NUMBER,
+    NULL,                              -1,
     &menu.main.text.level_number,      &main_text_level_number,
     NULL,                              NULL,
   },
@@ -1364,8 +1370,8 @@ static void clearMenuListArea(void)
 static void drawCursorExt(int xpos, int ypos, boolean active, int graphic)
 {
   static int cursor_array[MAX_LEV_FIELDY];
-  int x = mSX + TILEX * xpos;
-  int y = mSY + TILEY * (MENU_SCREEN_START_YPOS + ypos);
+  int x = amSX + TILEX * xpos;
+  int y = amSY + TILEY * (MENU_SCREEN_START_YPOS + ypos);
 
   if (xpos == 0)
   {
@@ -1593,7 +1599,7 @@ void DrawMainMenu(void)
   if (redraw_mask & REDRAW_ALL)
     fade_mask = REDRAW_ALL;
 
-  if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
+  if (CheckFadeAll())
     fade_mask = REDRAW_ALL;
 
   FadeOut(fade_mask);
@@ -1745,10 +1751,10 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
                                    "No title screen for this level set.");
        return;
       }
-
-      FadeMenuSoundsAndMusic();
     }
 
+    FadeMenuSoundsAndMusic();
+
     FadeOut(REDRAW_ALL);
 
     // title screens may have different window size
@@ -2035,9 +2041,23 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 
       if (pos == MAIN_CONTROL_NAME)
       {
-       SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
+       if ((mx || my) &&
+           insideTextPosRect(main_controls[i].pos_text, mx - mSX, my - mSY))
+       {
+         // special case: menu text "name/team" clicked -- toggle team mode
+         setup.team_mode = !setup.team_mode;
+
+         InitializeMainControls();
+         DrawCursorAndText_Main(choice, TRUE, FALSE);
 
-       HandleTypeName(strlen(setup.player_name), 0);
+         DrawPreviewPlayers();
+       }
+       else
+       {
+         SetGameStatus(GAME_MODE_PSEUDO_TYPENAME);
+
+         HandleTypeName(strlen(setup.player_name), 0);
+       }
       }
       else if (pos == MAIN_CONTROL_LEVELS)
       {
@@ -2324,7 +2344,7 @@ static void DrawInfoScreen_Main(void)
   if (redraw_mask & REDRAW_ALL)
     fade_mask = REDRAW_ALL;
 
-  if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
+  if (CheckFadeAll())
     fade_mask = REDRAW_ALL;
 
   UnmapAllGadgets();
@@ -3601,12 +3621,8 @@ static void DrawInfoScreen_Version(void)
   SDL_version sdl_version_compiled;
   const SDL_version *sdl_version_linked;
   int driver_name_len = 10;
-#if defined(TARGET_SDL2)
   SDL_version sdl_version_linked_ext;
   const char *driver_name = NULL;
-#else
-  char driver_name[driver_name_len];
-#endif
 
   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_VERSION);
 
@@ -3657,12 +3673,8 @@ static void DrawInfoScreen_Version(void)
   ystart += ystep_head;
 
   SDL_VERSION(&sdl_version_compiled);
-#if defined(TARGET_SDL2)
   SDL_GetVersion(&sdl_version_linked_ext);
   sdl_version_linked = &sdl_version_linked_ext;
-#else
-  sdl_version_linked = SDL_Linked_Version();
-#endif
 
   DrawTextF(xstart1, ystart, font_text, "SDL");
   DrawTextF(xstart2, ystart, font_text, "%d.%d.%d",
@@ -3722,22 +3734,14 @@ static void DrawInfoScreen_Version(void)
   DrawTextF(xstart3, ystart, font_head, "Used");
   ystart += ystep_head;
 
-#if defined(TARGET_SDL2)
   driver_name = getStringCopyNStatic(SDL_GetVideoDriver(0), driver_name_len);
-#else
-  SDL_VideoDriverName(driver_name, driver_name_len);
-#endif
 
   DrawTextF(xstart1, ystart, font_text, "SDL_VideoDriver");
   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_videodriver);
   DrawTextF(xstart3, ystart, font_text, "%s", driver_name);
   ystart += ystep_line;
 
-#if defined(TARGET_SDL2)
   driver_name = getStringCopyNStatic(SDL_GetAudioDriver(0), driver_name_len);
-#else
-  SDL_AudioDriverName(driver_name, driver_name_len);
-#endif
 
   DrawTextF(xstart1, ystart, font_text, "SDL_AudioDriver");
   DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_audiodriver);
@@ -3992,11 +3996,56 @@ void HandleTypeName(int newxpos, Key key)
 // tree menu functions
 // ============================================================================
 
+static int getAlignXOffsetFromTreeInfo(TreeInfo *ti)
+{
+  if (game_status != GAME_MODE_SETUP ||
+      DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
+    return 0;
+
+  int max_text_size = 0;
+  TreeInfo *node;
+
+  for (node = getTreeInfoFirstGroupEntry(ti); node != NULL; node = node->next)
+    max_text_size = MAX(max_text_size, strlen(node->name));
+
+  int num_entries = numTreeInfoInGroup(ti);
+  boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN);
+  int text_width = max_text_size * getFontWidth(FONT_TEXT_1);
+  int button_width = SC_MENUBUTTON_XSIZE;
+  int scrollbar_xpos = SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
+  int screen_width = (scrollbar_needed ? scrollbar_xpos : SXSIZE);
+  int align = menu.list_setup[SETUP_MODE_CHOOSE_OTHER].align;
+  int x = ALIGNED_XPOS(0, screen_width, align) * -1;
+  int align_xoffset_raw = ALIGNED_XPOS(x, button_width + text_width, align);
+  int align_xoffset = MAX(0, align_xoffset_raw);
+
+  return align_xoffset;
+}
+
+static int getAlignYOffsetFromTreeInfo(TreeInfo *ti)
+{
+  if (game_status != GAME_MODE_SETUP ||
+      DRAW_MODE_SETUP(setup_mode) != SETUP_MODE_CHOOSE_OTHER)
+    return 0;
+
+  int num_entries = numTreeInfoInGroup(ti);
+  int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
+  int font_height = getFontHeight(FONT_TEXT_1);
+  int text_height = font_height * num_page_entries;
+  int page_height = font_height * NUM_MENU_ENTRIES_ON_SCREEN;
+  int align = menu.list_setup[SETUP_MODE_CHOOSE_OTHER].valign;
+  int y = ALIGNED_YPOS(0, page_height, align) * -1;
+  int align_yoffset_raw = ALIGNED_YPOS(y, text_height, align);
+  int align_yoffset = MAX(0, align_yoffset_raw);
+
+  return align_yoffset;
+}
+
 static void DrawChooseTree(TreeInfo **ti_ptr)
 {
   int fade_mask = REDRAW_FIELD;
 
-  if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
+  if (CheckFadeAll())
     fade_mask = REDRAW_ALL;
 
   if (strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY))
@@ -4038,6 +4087,12 @@ static void DrawChooseTree(TreeInfo **ti_ptr)
 static void drawChooseTreeList(int first_entry, int num_page_entries,
                               TreeInfo *ti)
 {
+  int num_entries = numTreeInfoInGroup(ti);
+  boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN);
+  int scrollbar_xpos = SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
+  int screen_width = (scrollbar_needed ? scrollbar_xpos : SXSIZE);
+  int font_nr = FONT_TEXT_1;
+  int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
   int i;
   char *title_string = NULL;
   int yoffset_sets = MENU_TITLE1_YPOS;
@@ -4057,14 +4112,12 @@ static void drawChooseTreeList(int first_entry, int num_page_entries,
     int entry_pos = first_entry + i;
     int xpos = MENU_SCREEN_START_XPOS;
     int ypos = MENU_SCREEN_START_YPOS + i;
-    int startx = mSX + xpos * 32;
-    int starty = mSY + ypos * 32;
-    int font_nr = FONT_TEXT_1;
-    int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
+    int startx = amSX + xpos * 32;
+    int starty = amSY + ypos * 32;
     int startx_text = startx + font_xoffset;
-    int startx_scrollbar = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
-    int text_size = startx_scrollbar - startx_text;
-    int max_buffer_len = text_size / getFontWidth(font_nr);
+    int endx_text = amSX + screen_width;
+    int max_text_size = endx_text - startx_text;
+    int max_buffer_len = max_text_size / getFontWidth(font_nr);
     char buffer[max_buffer_len + 1];
 
     node_first = getTreeInfoFirstGroupEntry(ti);
@@ -4142,6 +4195,9 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
     int num_entries = numTreeInfoInGroup(ti);
     int entry_pos = posTreeInfo(ti);
 
+    align_xoffset = getAlignXOffsetFromTreeInfo(ti);
+    align_yoffset = getAlignYOffsetFromTreeInfo(ti);
+
     if (ti->cl_first == -1)
     {
       // only on initialization
@@ -4226,8 +4282,8 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
 
   if (mx || my)                // mouse input
   {
-    x = (mx - mSX) / 32;
-    y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
+    x = (mx - amSX) / 32;
+    y = (my - amSY) / 32 - MENU_SCREEN_START_YPOS;
   }
   else if (dx || dy)   // keyboard or scrollbar/scrollbutton input
   {
@@ -4533,7 +4589,7 @@ void DrawHallOfFame(int level_nr, int highlight_position)
 {
   int fade_mask = REDRAW_FIELD;
 
-  if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
+  if (CheckFadeAll())
     fade_mask = REDRAW_ALL;
 
   UnmapAllGadgets();
@@ -5216,7 +5272,6 @@ static void execSetupGraphics(void)
   if (check_game_speed)
     ModifyGameSpeedIfNeeded();
 
-#if defined(TARGET_SDL2)
   // window scaling may have changed at this point
   ToggleFullscreenOrChangeWindowScalingIfNeeded();
 
@@ -5229,7 +5284,6 @@ static void execSetupGraphics(void)
 
   // screen vsync mode may have changed at this point
   SDLSetScreenVsyncMode(setup.vsync_mode);
-#endif
 }
 
 static void execSetupChooseWindowSize(void)
@@ -6218,7 +6272,7 @@ static struct TokenInfo setup_info_editor[] =
 
 static struct TokenInfo setup_info_graphics[] =
 {
-#if defined(TARGET_SDL2) && !defined(PLATFORM_ANDROID)
+#if !defined(PLATFORM_ANDROID)
   { TYPE_SWITCH,       &setup.fullscreen,      "Fullscreen:"           },
   { TYPE_ENTER_LIST,   execSetupChooseWindowSize, "Window Scaling:"    },
   { TYPE_STRING,       &window_size_text,      ""                      },
@@ -6777,7 +6831,7 @@ static void DrawSetupScreen_Generic(void)
   char *title_string = NULL;
   int i;
 
-  if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
+  if (CheckFadeAll())
     fade_mask = REDRAW_ALL;
 
   UnmapAllGadgets();
@@ -7383,7 +7437,6 @@ void CustomizeKeyboard(int player_nr)
 
 static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
 {
-#if defined(TARGET_SDL2)
   static boolean bitmaps_initialized = FALSE;
   boolean screen_initialized = FALSE;
   static Bitmap *controller, *button, *axis_x, *axis_y;
@@ -7729,9 +7782,6 @@ static boolean ConfigureJoystickMapButtonsAndAxes(SDL_Joystick *joystick)
   while (NextValidEvent(&event));
 
   return success;
-#else
-  return TRUE;
-#endif
 }
 
 static int ConfigureJoystickMain(int player_nr)
@@ -7895,19 +7945,13 @@ static boolean ConfigureVirtualButtonsMain(void)
 
            // press 'Enter' to keep the existing key binding
            if (key == KSYM_Return ||
-#if defined(TARGET_SDL2)
                key == KSYM_Menu ||
-#endif
                key == KSYM_space)
            {
              step_nr++;
            }
-           else if (key == KSYM_BackSpace
-#if defined(TARGET_SDL2)
-                    ||
-                    key == KSYM_Back
-#endif
-                    )
+           else if (key == KSYM_BackSpace ||
+                    key == KSYM_Back)
            {
              if (step_nr == 0)
              {
@@ -7996,7 +8040,6 @@ static boolean ConfigureVirtualButtonsMain(void)
          }
          break;
 
-#if defined(TARGET_SDL2)
        case SDL_WINDOWEVENT:
          HandleWindowEvent((WindowEvent *) &event);
 
@@ -8019,7 +8062,6 @@ static boolean ConfigureVirtualButtonsMain(void)
        case SDL_APP_DIDENTERFOREGROUND:
          HandlePauseResumeEvent((PauseResumeEvent *) &event);
          break;
-#endif
 
         default:
          HandleOtherEvents(&event);
@@ -8080,6 +8122,9 @@ void ConfigureVirtualButtons(void)
 
 void DrawSetupScreen(void)
 {
+  align_xoffset = 0;
+  align_yoffset = 0;
+
   if (setup_mode == SETUP_MODE_INPUT)
     DrawSetupScreen_Input();
   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
@@ -8248,6 +8293,21 @@ static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
       *y = mSY + GDI_ACTIVE_POS(menu.main.button.next_level.y);
       break;
 
+    case SCREEN_CTRL_ID_FIRST_LEVEL:
+      *x = mSX + GDI_ACTIVE_POS(menu.main.button.first_level.x);
+      *y = mSY + GDI_ACTIVE_POS(menu.main.button.first_level.y);
+      break;
+
+    case SCREEN_CTRL_ID_LAST_LEVEL:
+      *x = mSX + GDI_ACTIVE_POS(menu.main.button.last_level.x);
+      *y = mSY + GDI_ACTIVE_POS(menu.main.button.last_level.y);
+      break;
+
+    case SCREEN_CTRL_ID_LEVEL_NUMBER:
+      *x = mSX + GDI_ACTIVE_POS(menu.main.button.level_number.x);
+      *y = mSY + GDI_ACTIVE_POS(menu.main.button.level_number.y);
+      break;
+
     case SCREEN_CTRL_ID_PREV_PLAYER:
       *x = mSX + TILEX * 10;
       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
@@ -8279,6 +8339,7 @@ static struct
   void (*get_gadget_position)(int *, int *, int);
   int gadget_id;
   int screen_mask;
+  unsigned int event_mask;
   char *infotext;
 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
 {
@@ -8287,27 +8348,55 @@ static struct
     getScreenMenuButtonPos,
     SCREEN_CTRL_ID_PREV_LEVEL,
     SCREEN_MASK_MAIN,
-    "last level"
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
+    "previous level"
   },
   {
     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
     getScreenMenuButtonPos,
     SCREEN_CTRL_ID_NEXT_LEVEL,
     SCREEN_MASK_MAIN,
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
     "next level"
   },
+  {
+    IMG_MENU_BUTTON_FIRST_LEVEL, IMG_MENU_BUTTON_FIRST_LEVEL_ACTIVE,
+    getScreenMenuButtonPos,
+    SCREEN_CTRL_ID_FIRST_LEVEL,
+    SCREEN_MASK_MAIN,
+    GD_EVENT_RELEASED,
+    "first level"
+  },
+  {
+    IMG_MENU_BUTTON_LAST_LEVEL, IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE,
+    getScreenMenuButtonPos,
+    SCREEN_CTRL_ID_LAST_LEVEL,
+    SCREEN_MASK_MAIN,
+    GD_EVENT_RELEASED,
+    "last level"
+  },
+  {
+    IMG_MENU_BUTTON_LEVEL_NUMBER, IMG_MENU_BUTTON_LEVEL_NUMBER_ACTIVE,
+    getScreenMenuButtonPos,
+    SCREEN_CTRL_ID_LEVEL_NUMBER,
+    SCREEN_MASK_MAIN,
+    GD_EVENT_RELEASED,
+    "level number"
+  },
   {
     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
     getScreenMenuButtonPos,
     SCREEN_CTRL_ID_PREV_PLAYER,
     SCREEN_MASK_INPUT,
-    "last player"
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
+    "previous player"
   },
   {
     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
     getScreenMenuButtonPos,
     SCREEN_CTRL_ID_NEXT_PLAYER,
     SCREEN_MASK_INPUT,
+    GD_EVENT_PRESSED | GD_EVENT_REPEATED,
     "next player"
   },
   {
@@ -8315,6 +8404,7 @@ static struct
     getScreenMenuButtonPos,
     SCREEN_CTRL_ID_INSERT_SOLUTION,
     SCREEN_MASK_MAIN_HAS_SOLUTION,
+    GD_EVENT_RELEASED,
     "insert solution tape"
   },
   {
@@ -8322,6 +8412,7 @@ static struct
     getScreenMenuButtonPos,
     SCREEN_CTRL_ID_PLAY_SOLUTION,
     SCREEN_MASK_MAIN_HAS_SOLUTION,
+    GD_EVENT_RELEASED,
     "play solution tape"
   },
 };
@@ -8402,10 +8493,7 @@ static void CreateScreenMenubuttons(void)
     int gd_x1, gd_x2, gd_y1, gd_y2;
     int id = menubutton_info[i].gadget_id;
 
-    if (menubutton_info[i].screen_mask == SCREEN_MASK_MAIN_HAS_SOLUTION)
-      event_mask = GD_EVENT_RELEASED;
-    else
-      event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+    event_mask = menubutton_info[i].event_mask;
 
     menubutton_info[i].get_gadget_position(&x, &y, id);
 
@@ -8739,6 +8827,20 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
       HandleMainMenu_SelectLevel(step, +1, NO_DIRECT_LEVEL_SELECT);
       break;
 
+    case SCREEN_CTRL_ID_FIRST_LEVEL:
+      HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT);
+      break;
+
+    case SCREEN_CTRL_ID_LAST_LEVEL:
+      HandleMainMenu_SelectLevel(MAX_LEVELS, +1, NO_DIRECT_LEVEL_SELECT);
+      break;
+
+    case SCREEN_CTRL_ID_LEVEL_NUMBER:
+      CloseDoor(DOOR_CLOSE_2);
+      SetGameStatus(GAME_MODE_LEVELNR);
+      DrawChooseLevelNr();
+      break;
+
     case SCREEN_CTRL_ID_PREV_PLAYER:
       HandleSetupScreen_Input_Player(step, -1);
       break;
@@ -8864,3 +8966,71 @@ boolean DoScreenAction(int image_id)
 
   return FALSE;
 }
+
+void DrawScreenAfterAddingSet(char *tree_subdir_new, int tree_type)
+{
+  // get tree info node of newly added level or artwork set
+  TreeInfo *tree_node_first = TREE_FIRST_NODE(tree_type);
+  TreeInfo *tree_node_new = getTreeInfoFromIdentifier(tree_node_first,
+                                                     tree_subdir_new);
+  if (tree_node_new == NULL)   // should not happen
+    return;
+
+  // if request dialog is active, do nothing
+  if (game.request_active)
+    return;
+
+  if (game_status == GAME_MODE_MAIN &&
+      tree_type == TREE_TYPE_LEVEL_DIR)
+  {
+    // when adding new level set in main menu, select it as current level set
+
+    // change current level set to newly added level set from zip file
+    leveldir_current = tree_node_new;
+
+    // change current level number to first level of newly added level set
+    level_nr = leveldir_current->first_level;
+
+    // redraw screen to reflect changed level set
+    DrawMainMenu();
+
+    // save this level set and level number as last selected level set
+    SaveLevelSetup_LastSeries();
+    SaveLevelSetup_SeriesInfo();
+  }
+  else if (game_status == GAME_MODE_LEVELS &&
+          tree_type == TREE_TYPE_LEVEL_DIR)
+  {
+    // when adding new level set in level set menu, set cursor and update screen
+
+    leveldir_current = tree_node_new;
+
+    DrawChooseTree(&leveldir_current);
+  }
+  else if (game_status == GAME_MODE_SETUP)
+  {
+    // when adding new artwork set in setup menu, set cursor and update screen
+
+    if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS &&
+       tree_type == TREE_TYPE_GRAPHICS_DIR)
+    {
+      artwork.gfx_current = tree_node_new;
+
+      DrawChooseTree(&artwork.gfx_current);
+    }
+    else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS &&
+            tree_type == TREE_TYPE_SOUNDS_DIR)
+    {
+      artwork.snd_current = tree_node_new;
+
+      DrawChooseTree(&artwork.snd_current);
+    }
+    else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC &&
+            tree_type == TREE_TYPE_MUSIC_DIR)
+    {
+      artwork.mus_current = tree_node_new;
+
+      DrawChooseTree(&artwork.mus_current);
+    }
+  }
+}