+/*
+ * Curses UI for slide puzzle game
+ * Copyright © 2022 Nick Bowler
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <curses.h>
+
+#include "cursesui.h"
+#include "cursmenu.h"
+
+enum {
+ ACTION_EXIT = 0x80,
+ ACTION_ABOUT,
+ ACTION_NEWGAME,
+ ACTION_MAX
+};
+
+static const struct menuitem {
+ uint_least8_t label, position, action;
+} menu[] = { FUNC_INITIALIZER };
+
+static void do_function(struct app_state *state, unsigned action)
+{
+ if (action < sizeof menu / sizeof menu[0]) {
+ state->toolbar_state = action;
+ curs_draw_toolbar(state);
+ doupdate();
+ }
+
+ switch (action) {
+ case ACTION_NEWGAME:
+ curs_new_game(state);
+ break;
+ case ACTION_EXIT:
+ endwin();
+ exit(0);
+ };
+}
+
+void curs_execute_function(struct app_state *state, int func)
+{
+ int i;
+
+ for (i = state->toolbar_state; menu[i].position; i++) {
+ if (func == menu[i].position) {
+ do_function(state, menu[i].action);
+ return;
+ }
+ }
+}
+
+/*
+ * Return the width of the toolbar and the target width of toolbar labels.
+ */
+static int toolbar_width(WINDOW *toolbar, int *label_width)
+{
+ int w, h;
+
+ getmaxyx(toolbar, h, w);
+ *label_width = MAX(6, w/10);
+ return (void)h, w;
+}
+
+/*
+ * Given the toolbar function number (between 1 and 10, inclusive), and the
+ * total width of the screen, return the character position for the start of
+ * its label display.
+ *
+ * The intention is to divide the width of the screen into 10 roughly
+ * equally-sized areas, spreading out the remainder so that the width of
+ * each label is a monotone increasing function of the total width.
+ *
+ * The minimum size of a label is 6 characters (2 of which are used for the
+ * number indicator)
+ */
+static int toolbar_xpos(int i, int total_width, int label_width)
+{
+ int rem = total_width - 10*label_width;
+ int pos = (i-1)*label_width;
+
+ switch (rem) {
+ case 9: pos += i > 6;
+ case 8: pos += i > 2;
+ case 7: pos += i > 7;
+ case 6: pos += i > 3;
+ case 5: pos += i > 8;
+ case 4: pos += i > 4;
+ case 3: pos += i > 9;
+ case 2: pos += i > 5;
+ }
+
+ return pos;
+}
+
+/*
+ * Redraw the current set of toolbar labels.
+ */
+void curs_draw_toolbar(struct app_state *state)
+{
+ WINDOW *toolbar = state->toolbar;
+ int i, w, lw;
+
+ werase(toolbar);
+
+ w = toolbar_width(toolbar, &lw);
+ for (i = state->toolbar_state; menu[i].position; i++) {
+ int pos = toolbar_xpos(menu[i].position, w, lw);
+ mvwprintw(toolbar, 0, pos+2, "%.*s", lw, strtab+menu[i].label);
+ }
+
+ mvwchgat(toolbar, 0, 0, -1, A_REVERSE, RR_COLOUR_TOOLBAR, 0);
+ for (i = 1; i <= 10; i++) {
+ mvwprintw(toolbar, 0, toolbar_xpos(i, w, lw), "%2d", i);
+ }
+
+ wnoutrefresh(state->toolbar);
+}
+
+#if HAVE_CURSES_MOUSE_SUPPORT
+/*
+ * Returns the toolbar function (1 through 10) under the given x, y screen
+ * coordinates, or 0 if the coordinates are outside of the toolbar.
+ */
+int curs_toolbar_mouse_func(struct app_state *state, int x, int y)
+{
+ int toolbar_x, toolbar_y, i, w, lw;
+
+ getbegyx(state->toolbar, toolbar_y, toolbar_x);
+ if ((void)toolbar_x, y != toolbar_y)
+ return 0;
+
+ /* OK, selected a button, determine which one */
+ w = toolbar_width(state->toolbar, &lw);
+ for (i = 10; i > 1; i--) {
+ if (x >= toolbar_xpos(i, w, lw))
+ break;
+ }
+
+ return i;
+}
+#endif