]> git.draconx.ca Git - rrace.git/commitdiff
Implement the about dialog.
authorNick Bowler <nbowler@draconx.ca>
Thu, 17 Mar 2022 00:07:21 +0000 (20:07 -0400)
committerNick Bowler <nbowler@draconx.ca>
Thu, 17 Mar 2022 00:08:51 +0000 (20:08 -0400)
Makefile.am
conf_post.h [new file with mode: 0644]
configure.ac
m4/.gitignore
m4/gnulib-cache.m4
src/motif.c
src/motif.h
src/motif_ui.c
src/motifgui.dat
src/version.c [new file with mode: 0644]
src/version.h [new file with mode: 0644]

index 15f3a5d3901ca14ba2c84acc7cfeff93872d307b..887411a72c17f733024ff44edae6f59fed9a30af 100644 (file)
@@ -22,9 +22,12 @@ bin_PROGRAMS = rrace-motif
 dist_man_MANS = doc/rrace-motif.1
 endif
 
+noinst_HEADERS = conf_post.h
+
 rrace_motif_SOURCES = src/game.c src/x11.c src/game.h src/motif.h \
                       src/colour.h src/ewmhicon.c src/ewmhicon.h \
-                      src/xcounter.c src/xcounter.h
+                      src/version.c src/version.h src/xcounter.c \
+                      src/xcounter.h
 rrace_motif_LDADD = $(libmotifmain_a_OBJECTS) $(libmotifui_a_OBJECTS) \
                     $(libglohelp_a_OBJECTS) libgnu.a $(MOTIF_LIBS) \
                     $(LIB_CLOCK_GETTIME) $(LIB_GETHRXTIME)
diff --git a/conf_post.h b/conf_post.h
new file mode 100644 (file)
index 0000000..6368d76
--- /dev/null
@@ -0,0 +1,4 @@
+/* Allow ENABLE_NLS to be used in C conditionals. */
+#ifndef ENABLE_NLS
+#      define ENABLE_NLS 0
+#endif
index 2378c8e8770c94f1330604530b1ab25ceb1794e5..755d911fba6d30315bb4b73a2af2a611173769fe 100644 (file)
@@ -22,6 +22,13 @@ gl_INIT
 AC_CACHE_SAVE
 m4_include([lib/gnulib.mk])
 
+dnl We will provide our own makefile rules for gettext.  Disable tracing of
+dnl AM_GNU_GETTEXT to prevent autoreconf from running autopoint, and to
+dnl prevent automake from growing gratuitous error conditions.
+m4_traceoff([AM_GNU_GETTEXT])
+AM_GNU_GETTEXT([external])
+AH_BOTTOM([#include <conf_post.h>])
+
 AC_PATH_XTRA
 AS_IF([test x"$no_x" != x"yes"],
 [AC_CACHE_CHECK([for Motif], [dx_cv_have_motif],
index 5ee6340f89d6b5f192fef2c749735834521236f5..90eb9293bf694e4bbc9d169207f00cff40dc5810 100644 (file)
@@ -1,9 +1,11 @@
 /00gnulib.m4
 /absolute-header.m4
 /clock_time.m4
+/codeset.m4
 /extensions.m4
 /extern-inline.m4
 /flexmember.m4
+/free.m4
 /gethrxtime.m4
 /getopt.m4
 /gettime.m4
 /gnulib-common.m4
 /gnulib-comp.m4
 /gnulib-tool.m4
+/host-cpu-c-abi.m4
+/iconv.m4
+/iconv_h.m4
+/iconv_open.m4
 /include_next.m4
 /inline.m4
+/inttypes.m4
+/lib-ld.m4
+/lib-link.m4
+/lib-prefix.m4
 /libtool.m4
+/libunistring-base.m4
+/limits-h.m4
+/localcharset.m4
+/locale-fr.m4
+/locale-ja.m4
+/locale-zh.m4
+/locale_h.m4
 /ltoptions.m4
 /ltsugar.m4
 /ltversion.m4
 /lt~obsolete.m4
+/mbrtowc.m4
+/mbsinit.m4
+/mbstate_t.m4
+/mbswidth.m4
+/multiarch.m4
 /nocrash.m4
 /off_t.m4
 /pid_t.m4
+/setlocale_null.m4
 /ssize_t.m4
+/std-gnu11.m4
+/stdbool.m4
 /stddef_h.m4
+/stdint.m4
+/stdlib_h.m4
+/string_h.m4
 /sys_socket_h.m4
 /sys_time_h.m4
 /sys_types_h.m4
+/threadlib.m4
 /time_h.m4
 /timespec.m4
 /unistd_h.m4
 /vararrays.m4
+/visibility.m4
 /warn-on-use.m4
+/wchar_h.m4
 /wchar_t.m4
+/wctype_h.m4
+/wcwidth.m4
+/wint_t.m4
 /zzgnulib.m4
index 6b6352e434cf95a840807aadac99f12606f40ab8..e7a0fcf1cd8e0576db9f6cc63ba2c26a5b5fbc4d 100644 (file)
 #  flexmember \
 #  gethrxtime \
 #  getopt-gnu \
-#  inline
+#  gettext-h \
+#  inline \
+#  localcharset \
+#  mbswidth \
+#  striconv
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([])
@@ -50,7 +54,11 @@ gl_MODULES([
   flexmember
   gethrxtime
   getopt-gnu
+  gettext-h
   inline
+  localcharset
+  mbswidth
+  striconv
 ])
 gl_AVOID([])
 gl_SOURCE_BASE([lib])
index f9da2922ef11d2a1ba54c3b305a3698953b492f6..ef06c5269f9d4a1adb3a7960957f386a563c130f 100644 (file)
@@ -28,6 +28,7 @@
 #include "motif.h"
 #include "motifopt.h"
 #include "game.h"
+#include "version.h"
 
 #define TIMER_UPDATE_MS 33
 
@@ -49,13 +50,14 @@ static char * const default_resources[] = {
        "*gameExit.accelerator: Ctrl<Key>Q",
        "*gameExit.acceleratorText: Ctrl+Q",
 
+       "*aboutDialog*pixmapTextPadding: 10",
+
        NULL
 };
 
 static void print_version(void)
 {
-       printf("%s %s\n", PROGNAME, PACKAGE_VERSION);
-       printf("Copyright (C) 2022 Nick Bowler\n");
+       version_print_head("rrace-motif", stdout);
        puts("License GPLv3+: GNU GPL version 3 or any later version");
        puts("This is free software: you are free to change and redistribute it.");
        puts("There is NO WARRANTY, to the extent permitted by law.");
@@ -211,6 +213,16 @@ static void game_input(Widget w, void *data, void *cb_data)
 
 static struct app_state state;
 
+static Widget get_shell(Widget w)
+{
+       Widget shell;
+
+       for (shell = w; !XtIsWMShell(shell);)
+               shell = XtParent(shell);
+
+       return shell;
+}
+
 static void proc_exit(Widget w, XEvent *e, String *argv, Cardinal *argc)
 {
        XtAppSetExitFlag(XtWidgetToApplicationContext(w));
@@ -218,16 +230,11 @@ static void proc_exit(Widget w, XEvent *e, String *argv, Cardinal *argc)
 
 static void proc_new_game(Widget w, XEvent *e, String *argv, Cardinal *argc)
 {
-       Widget shell;
-
-       for (shell = w; !XtIsWMShell(shell);)
-               shell = XtParent(shell);
-
        game_reset(&state.board);
 
        x11_redraw_goal(&state, -1);
        x11_redraw_game(&state, -1);
-       x11_redraw_icon(&state, shell);
+       x11_redraw_icon(&state, get_shell(w));
 
        if (!state.timer_tick) {
                XtAppContext app = XtWidgetToApplicationContext(w);
@@ -238,9 +245,15 @@ static void proc_new_game(Widget w, XEvent *e, String *argv, Cardinal *argc)
        game_begin(&state.board);
 }
 
+static void proc_about(Widget w, XEvent *e, String *argv, Cardinal *argc)
+{
+       ui_show_about(&state, get_shell(w));
+}
+
 static const XtActionsRec menu_actions[] = {
        { "gameNew", proc_new_game },
-       { "gameExit", proc_exit }
+       { "gameExit", proc_exit },
+       { "helpAbout", proc_about }
 };
 
 static XtAppContext app_initialize(int argc, char **argv)
index cdbbe487434cd85afe4ed227e549432405edcf28..9064788691c61185150bd13a411af56ae4a5fa5c 100644 (file)
@@ -46,6 +46,7 @@ struct app_state {
 };
 
 void ui_initialize(struct app_state *state, Widget shell);
+void ui_show_about(struct app_state *state, Widget shell);
 void ui_timer_update(struct app_state *state, int_fast32_t elapsed);
 
 void x11_initialize(struct app_state *state, Widget shell);
index a49dfec0717bd58ca985bcebcb48ae8b4128b00b..05721e09a95b1515dcf79feb25b7f54320f43c04 100644 (file)
@@ -25,6 +25,7 @@
 #include "motifstr.h"
 #include "motifgui.h"
 #include "xcounter.h"
+#include "version.h"
 
 #define SPLIT_NUMERATOR    75
 #define SPLIT_DENOMINATOR 100
@@ -42,7 +43,11 @@ enum {
        widgetMax,
 
        /* Pseudo-widgets */
-       widgetMenuBar = widgetMax
+       widgetMenuBar = widgetMax,
+       widgetMessageDialog,
+       widgetScrolledText,
+
+       widgetUnmanaged = 128
 };
 
 static WidgetClass * const widgets[widgetMax] = {
@@ -171,14 +176,19 @@ static Widget create_widget(const struct ui_widget *item, Widget parent,
                             ArgList args, Cardinal num_args)
 {
        String name = (void *)&tree_strtab[item->name];
-       WidgetClass class;
+       unsigned type = item->widget_type & widgetUnmanaged-1;
 
-       if (item->widget_type == widgetMenuBar)
+       switch (type) {
+       case widgetMenuBar:
                return XmCreateMenuBar(parent, name, args, num_args);
+       case widgetMessageDialog:
+               return XmCreateMessageDialog(parent, name, args, num_args);
+       case widgetScrolledText:
+               return XmCreateScrolledText(parent, name, args, num_args);
+       }
 
-       assert(item->widget_type < widgetMax);
-       class = *widgets[item->widget_type];
-       return XtCreateWidget(name, class, parent, args, num_args);
+       assert(type < widgetMax);
+       return XtCreateWidget(name, *widgets[type], parent, args, num_args);
 }
 
 static void
@@ -192,7 +202,8 @@ construct_widgets(const struct ui_widget *root, Widget parent, unsigned i)
                if (item->subtree)
                        construct_widgets(root, w, item->subtree);
 
-               XtManageChild(w);
+               if (!(item->widget_type & widgetUnmanaged))
+                       XtManageChild(w);
        }
 }
 
@@ -365,3 +376,63 @@ void ui_initialize(struct app_state *state, Widget shell)
        XtAddCallback(state->goal, XmNresizeCallback, goal_resize, state);
        XtAddCallback(state->goal, XmNexposeCallback, goal_expose, state);
 }
+
+static void dialog_close(Widget w, void *data, void *cb_data)
+{
+       XtDestroyWidget(w);
+}
+
+/*
+ * Expands to an XtVaSetValues argument list to set the given resource with
+ * a typed string value, which is internally converted to the appropriate
+ * resource type.
+ *
+ * Note that str is expanded twice and thus should not have side effects.
+ */
+#define STRING_ARG(resource, str) \
+       XtVaTypedArg, (resource), XtRString, (str), strlen((str))+1
+
+void ui_show_about(struct app_state *state, Widget shell)
+{
+       static const struct ui_widget dialog[] = { ABOUTDIALOG_INITIALIZER };
+       Widget w, l;
+       char *msg;
+
+       construct_widgets(dialog, shell, 0);
+
+       w = XtNameToWidget(shell, "*aboutDialog");
+       XtUnmanageChild(XmMessageBoxGetChild(w, XmDIALOG_CANCEL_BUTTON));
+
+       XtAddCallback(w, XmNunmapCallback, dialog_close, NULL);
+
+       XtVaSetValues(w, STRING_ARG(XmNokLabelString, "Close"), (char *)NULL);
+
+       msg = version_format_head("rrace-motif");
+       l = XmMessageBoxGetChild(w, XmDIALOG_MESSAGE_LABEL);
+       XtVaSetValues(l, XmNlabelType, XmPIXMAP_AND_STRING,
+                        XmNlabelPixmap, state->icon_pixmap,
+                        STRING_ARG(XmNlabelString, msg),
+                        (char *)NULL);
+       free(msg);
+
+       l = XtNameToWidget(w, "*licenseBlurb");
+       XmTextSetString(l,
+"This program is free software: you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License as published by\n"
+"the Free Software Foundation, either version 3 of the License, or\n"
+"(at your option) any later version.\n\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+"GNU General Public License for more details.\n\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this program.  If not, see <https://www.gnu.org/licenses/>.");
+
+       XtVaSetValues(l, XmNeditMode, XmMULTI_LINE_EDIT,
+                        XmNeditable, FALSE,
+                        XmNresizeWidth, TRUE,
+                        XmNrows, 5,
+                        (char *)NULL);
+
+       XtManageChild(w);
+}
index 2e6a67545c006bc3f369a81b30df0f15dc884e36..159a8152bca0ffa1a01a3584e6eaad03f22d3bc1 100644 (file)
@@ -8,6 +8,10 @@ MAINWIN
     goalCanvas, 0, widgetDrawingArea
    timeDisplay, 0, widgetDrawingArea
 
+ABOUTDIALOG
+ aboutDialog, aboutDialog_OFFSET, widgetUnmanaged|widgetMessageDialog
+  licenseBlurb, 0, widgetScrolledText
+
 MAINMENU
  gameMenu, gameMenu_OFFSET, widgetCascadeButton, gameMenuLabel
   gameNew, 0, widgetPushButton, gameNewLabel
diff --git a/src/version.c b/src/version.c
new file mode 100644 (file)
index 0000000..6a5ce9a
--- /dev/null
@@ -0,0 +1,51 @@
+#include <config.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <striconv.h>
+#include <localcharset.h>
+
+#include "version.h"
+
+const char *init_copysign(char **alloc)
+{
+       *alloc = NULL;
+
+       if (ENABLE_NLS) {
+               char *buf = str_iconv("\xc2\xa9", "UTF-8", locale_charset());
+               if (buf)
+                       return *alloc = buf;
+       }
+
+       return "(C)";
+}
+
+#define VERSION_HEAD_FMT "%s (RRace) %s\nCopyright %s 2022 Nick Bowler"
+#define VERSION_HEAD_ARGS progname, PACKAGE_VERSION, copysign
+
+void version_print_head(const char *progname, FILE *f)
+{
+       const char *copysign;
+       char *copybuf;
+
+       copysign = init_copysign(&copybuf);
+
+       printf(VERSION_HEAD_FMT, VERSION_HEAD_ARGS);
+       putc('\n', f);
+
+       free(copybuf);
+}
+
+char *version_format_head(const char *progname)
+{
+       const char *copysign;
+       char *copybuf, *ret;
+
+       copysign = init_copysign(&copybuf);
+
+       ret = malloc(sizeof VERSION_HEAD_FMT + 100);
+       if (ret)
+               sprintf(ret, VERSION_HEAD_FMT, VERSION_HEAD_ARGS);
+
+       free(copybuf);
+       return ret;
+}
diff --git a/src/version.h b/src/version.h
new file mode 100644 (file)
index 0000000..5c3850f
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef RRACE_VERSION_H_
+#define RRACE_VERSION_H_
+
+#include <stdio.h>
+
+/*
+ * Print a line containing the name of the program and the version of the
+ * package, followed by a line for each copyright notice to the specified file.
+ */
+void version_print_head(const char *progname, FILE *f);
+
+/*
+ * As with the above, but the string is put in a newly-allocated string which
+ * must be freed by the caller.  The resulting string has newline characters
+ * between each line but there is no newline after the final line.
+ */
+char *version_format_head(const char *progname);
+
+#endif