/*
 * @(#)xdial.c
 *
 * Copyright 1994 - 2025  David A. Bagley, bagleyd AT verizon.net
 *
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of the author not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * 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.
 */

/* Driver file for Dial */

#include "version.h"
#include "xwin.h"
#include "xgui.h"
static char aboutHelp[] = {
"Dial Version " VERSION "\n"
"Send bugs (reports or fixes) to the author: "
"David Bagley <bagleyd AT verizon.net>\n"
"The latest version is at: "
"https://www.sillycycle.com/puzzles.html"
};

#include "Dial.h"
#include "DialSq.h"
#ifdef HAVE_XPM
#include <X11/xpm.h>
#ifdef CONSTPIXMAPS
#include "icons/16x16/dial.xpm"
#include "icons/22x22/dial.xpm"
#include "icons/24x24/dial.xpm"
#include "icons/32x32/dial.xpm"
#include "icons/48x48/dial.xpm"
#include "icons/64x64/dial.xpm"
#else
#include "pixmaps/16x16/dial.xpm"
#include "pixmaps/22x22/dial.xpm"
#include "pixmaps/24x24/dial.xpm"
#include "pixmaps/32x32/dial.xpm"
#include "pixmaps/48x48/dial.xpm"
#include "pixmaps/64x64/dial.xpm"
#endif
#define RESIZE_XPM(s) ((char **) (((s)<=32)?\
(((s)<=22)?(((s)<=16)?dial_16x16:dial_22x22):\
(((s)<=24)?dial_24x24:dial_32x32)):\
(((s)<=48)?dial_48x48:dial_64x64)))
#endif
#include "pixmaps/64x64/dial.xbm"
#define DEFINE_XBM (char *) dial_64x64_bits, dial_64x64_width, dial_64x64_height
#ifdef HAVE_EDITRES
#ifdef __VMS
#include <Xmu/Editres.h>
#else
#include <X11/Xmu/Editres.h>
#endif
#endif

#ifdef HAVE_ATHENA
static const char *fileTypes[] =
{
	"Exit"
};
#define numFileTypes (sizeof(fileTypes)/sizeof(fileTypes[0]))
static const char *moveTypes[] =
{
	"Increment",
	"Decrement"
};
#define numMoveTypes (sizeof(moveTypes)/sizeof(moveTypes[0]))
static const char *helpTypes[] =
{
	"About..."
};
#define numHelpTypes (sizeof(helpTypes)/sizeof(helpTypes[0]))
#endif
#ifndef HAVE_MOTIF
static char prog[] = "  TAC";
#endif

static Widget dial, dialsq;
#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
static Widget aboutDialog;
static Widget tracker;
#else
static Widget shell;
#endif
static Pixmap pixmap = None;
static Widget topLevel;

static void printState(Widget w, int n)
{
	char titleDsp[TITLE_LENGTH] = "";
	int factor;
	float m = n;

	XtVaGetValues(dial,
		XtNfactor, &factor, NULL);
#ifdef HAVE_MOTIF
	{
		XmString xmstr;

		if (!XtIsSubclass(tracker, xmLabelWidgetClass))
			XtError("printState() requires a Label Widget");
#ifdef HAVE_SNPRINTF
		(void) snprintf(titleDsp, TITLE_LENGTH,
			"%g      ", m / factor);
#else
		(void) sprintf(titleDsp,
			"%g      ", m / factor);
#endif
		xmstr = XmStringCreateLtoR(titleDsp, XmSTRING_DEFAULT_CHARSET);
		XtVaSetValues(tracker,
			XmNlabelString, xmstr, NULL);
		XmStringFree(xmstr);
	}
#else
#ifdef HAVE_SNPRINTF
	(void) snprintf(titleDsp, TITLE_LENGTH,
		"%6.2f%s", m / factor, prog);
#else
	(void) sprintf(titleDsp,
		"%6.2f%s", m / factor, prog);
#endif
#ifdef HAVE_ATHENA
	XtVaSetValues(tracker,
		XtNlabel, titleDsp, NULL);
#else
	XtVaSetValues(w,
		XtNtitle, titleDsp, NULL);
#endif
#endif
}

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
static void
fileMenuCallback(Widget w, XtPointer clientData, XtPointer callData)
{
	int value = (int) ((size_t) clientData);

	if (value == 0)
		exit(0);
}

static void
moveMenuCallback(Widget w, XtPointer clientData, XtPointer callData)
{
	int value = (int) ((size_t) clientData) + ACTION_INCREMENT;

	XtVaSetValues(dial,
		XtNmenu, value, NULL);
}

static void
helpMenuCallback(Widget w, XtPointer clientData, XtPointer callData)
{
	int value = (int) ((size_t) clientData);
	Widget dialog;

#ifdef HAVE_ATHENA
	if (wmDeleteWindow == None)
		wmDeleteWindow = XInternAtom(XtDisplay(topLevel),
			"WM_DELETE_WINDOW", FALSE);
#endif
	switch (value) {
	case 0:
		dialog = aboutDialog;
		break;
	default:
		{
			char *buf;

			intCat(&buf, "helpMenuCallback: %d", value);
			XtWarning(buf);
			free(buf);
			return;
		}
	}
#ifdef HAVE_MOTIF
	XtManageChild(dialog);
#elif defined(HAVE_ATHENA)
	XtPopup(dialog, XtGrabNone);
	XSetWMProtocols(XtDisplay(topLevel),
		XtWindow(dialog), &wmDeleteWindow, 1);
#endif
}
#endif

static void initialize(void)
{
	int n;
	Boolean versionOnly;

	XtVaGetValues(dial,
		XtNval, &n,
		XtNversionOnly, &versionOnly, NULL);
	if (versionOnly) {
		(void) printf("%s\n", aboutHelp);
		exit(0);
	}
	printState(XtParent(dial), n);
	XtVaGetValues(dialsq,
		XtNval, &n, NULL);
	printState(XtParent(dialsq), n);
}

static void callbackDial(Widget w, caddr_t clientData,
		dialCallbackStruct *callData)
{
	int reason = callData->reason;
	Widget otherw;

	if (reason == ACTION_HIDE) {
		(void) XIconifyWindow(XtDisplay(topLevel),
			XtWindow(topLevel),
			XScreenNumberOfScreen(XtScreen(topLevel)));
#if !defined(HAVE_MOTIF) && !defined(HAVE_ATHENA)
		(void) XIconifyWindow(XtDisplay(shell),
			XtWindow(shell),
			XScreenNumberOfScreen(XtScreen(shell)));
#endif
		return;
	}
	if (w == dial)
		otherw = dialsq;
	else /* (w == dialsq) */
		otherw = dial;
	XtVaSetValues(w,
		XtNval, callData->val, NULL);
	XtVaSetValues(otherw,
		XtNval, callData->val, NULL);
#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
	printState(tracker, callData->val);
#else
	printState(XtParent(w), callData->val);
	printState(XtParent(otherw), callData->val);
#endif
}

int
main(int argc, char **argv)
{
	int pixmapSize;
	XtAppContext appCon;
#if defined (HAVE_MOTIF) || defined(HAVE_ATHENA)
	Widget mainPanel, menuBar;
#endif
#ifdef HAVE_MOTIF
	Widget pullDownMenu, widget;
	Widget menuBarPanel, controlPanel;
	Widget controlRowCol;
	XmString fileString, moveString;
	XmString quitString;
	XmString incrementString, decrementString;

#elif defined(HAVE_ATHENA)
	Widget fileLabel, moveLabel, helpLabel;
	XtActionsRec actions[] = {
		{(char *) "DeleteWindowProc", deleteWindowProc},
		{(char *) "ClosePopupPanel", (XtActionProc) closePopupPanel}
	};
	String fallbackResources[] = {
		(char *) "?.translations: #override <Message>WM_PROTOCOLS: DeleteWindowProc()",
		(char *) "?.TransientShell.translations: #override <Message>WM_PROTOCOLS: ClosePopupPanel()",
		NULL
	};
#endif

#if defined (HAVE_MOTIF) || defined(HAVE_ATHENA)
#ifdef __VMS
	int n;
	progDsp = strrchr(argv[0], ']');
	for (n = 0; progDsp[n] != '\0' && progDsp[n] != '.'; n++);
	progDsp[n] = '\0';
#else
	progDsp = strrchr(argv[0], '/');
#endif
	if (progDsp != NULL)
		progDsp++;
	if (progDsp == NULL)
		progDsp = argv[0];
#endif
	topLevel = XtVaAppInitialize(NULL, "XDial",
		NULL, 0, &argc, argv,
#ifdef HAVE_ATHENA
		fallbackResources,
#else
		NULL,
#endif
		NULL);
	appCon = XtWidgetToApplicationContext(topLevel);
#ifdef HAVE_MOTIF
	menuBarPanel = XtVaCreateManagedWidget("menuBarPanel",
		xmPanedWindowWidgetClass, topLevel,
		XmNseparatorOn, False,
		XmNsashWidth, 1,
		XmNsashHeight, 1, NULL);
	fileString = XmStringCreateSimple((char *) "File");
	moveString = XmStringCreateSimple((char *) "Move");
	menuBar = XmVaCreateSimpleMenuBar(menuBarPanel, (char *) "menuBar",
		XmVaCASCADEBUTTON, fileString, 'F',
		XmVaCASCADEBUTTON, moveString, 'M', NULL);
	XmStringFree(fileString);
	XmStringFree(moveString);
	XtManageChild(menuBar);
	quitString = XmStringCreateSimple((char *) "Exit");
	(void) XmVaCreateSimplePulldownMenu(menuBar, (char *) "fileMenu",
		0, fileMenuCallback,
		XmVaPUSHBUTTON, quitString, 'x', NULL, NULL, NULL);
	XmStringFree(quitString);
	incrementString = XmStringCreateSimple((char *) "Increment");
	decrementString = XmStringCreateSimple((char *) "Decrement");
	(void) XmVaCreateSimplePulldownMenu(menuBar, (char *) "moveMenu",
		1, moveMenuCallback,
		XmVaPUSHBUTTON, incrementString, 'I', NULL, NULL,
		XmVaPUSHBUTTON, decrementString, 'D', NULL, NULL, NULL);
	XmStringFree(incrementString);
	XmStringFree(decrementString);
	pullDownMenu = XmCreatePulldownMenu(menuBar,
		(char *) "helpPullDown", NULL, 0);
	widget = XtVaCreateManagedWidget("Help",
		xmCascadeButtonWidgetClass, menuBar,
		XmNsubMenuId, pullDownMenu,
		XmNmnemonic, 'H', NULL); /* mnemonic warning on Cygwin */
	XtVaSetValues(menuBar,
		XmNmenuHelpWidget, widget, NULL);
	widget = XtVaCreateManagedWidget("About...",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'A', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuCallback, (char *) 0);
	XtManageChild(menuBar);
	aboutDialog = createHelp(menuBar, (char *) "About",
		(char *) aboutHelp);
	mainPanel = XtVaCreateManagedWidget("mainPanel",
		xmPanedWindowWidgetClass, menuBarPanel, NULL);
	controlPanel = XtVaCreateManagedWidget("controlPanel",
		xmPanedWindowWidgetClass, mainPanel,
		XmNseparatorOn, False,
		XmNsashWidth, 1,
		XmNsashHeight, 1, NULL);
	controlRowCol = XtVaCreateManagedWidget("controlRowCol",
		xmRowColumnWidgetClass, controlPanel,
		XmNnumColumns, 1,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	dial = XtVaCreateManagedWidget("dial",
		dialWidgetClass, mainPanel, NULL);
	XtVaSetValues(dial,
		XtNheight, 100, NULL);
	dialsq = XtVaCreateManagedWidget("dialsq",
		squareDialWidgetClass, mainPanel, NULL);
	XtVaSetValues(dialsq,
		XtNheight, 100, NULL);
	(void) XtVaCreateManagedWidget("tacText",
		xmLabelGadgetClass, controlRowCol,
		XtVaTypedArg, XmNlabelString, XmRString, "TAC", 4, NULL);
	tracker = XtVaCreateManagedWidget("0     ",
		xmLabelWidgetClass, controlRowCol, NULL);
#elif defined(HAVE_ATHENA)
	XtAppAddActions(appCon, actions, XtNumber(actions));
	createHelp(topLevel, &aboutDialog, (char *) "About",
		(char *) aboutHelp, (XtCallbackProc) closePopupPanel2);
	mainPanel = XtVaCreateManagedWidget("form",
		panedWidgetClass, topLevel, NULL);
		menuBar = XtVaCreateManagedWidget("MenuBar",
		formWidgetClass, mainPanel,
		XtNborderWidth, 1, NULL);
	createMenu(menuBar, &fileLabel, NULL,
		fileTypes, "File", numFileTypes,
		0, False, fileMenuCallback);
	createMenu(menuBar, &moveLabel, fileLabel,
		moveTypes, "Controls", numMoveTypes,
		0, False, moveMenuCallback);
	createMenu(menuBar, &helpLabel, moveLabel,
		helpTypes, "Help", numHelpTypes,
		0, False, helpMenuCallback);
	tracker = XtVaCreateManagedWidget("  0.00   ",
		labelWidgetClass, mainPanel,
		XtNfromVert, menuBar, NULL);
	dial = XtVaCreateManagedWidget("dial",
		dialWidgetClass, mainPanel,
		XtNfromVert, tracker, NULL);
	dialsq = XtVaCreateManagedWidget("dialsq",
		squareDialWidgetClass, mainPanel,
		XtNfromVert, dial, NULL);
	XtRealizeWidget(topLevel);
	if (wmDeleteWindow == None)
		wmDeleteWindow = XInternAtom(XtDisplay(topLevel),
			"WM_DELETE_WINDOW", FALSE);
	XSetWMProtocols(XtDisplay(topLevel), XtWindow(topLevel),
		&wmDeleteWindow, 1);
#else
	dial = XtVaCreateManagedWidget("dial",
		dialWidgetClass, topLevel, NULL);
	shell = XtAppCreateShell(argv[0], "XDialSq",
		topLevelShellWidgetClass, XtDisplay(topLevel), NULL, 0);
	dialsq = XtVaCreateManagedWidget("dialsq",
		squareDialWidgetClass, shell, NULL);
#endif
	XtVaGetValues(dial,
		XtNpixmapSize, &pixmapSize, NULL);
#ifdef HAVE_XPM
	{
		XpmAttributes xpmAttributes;
		XpmColorSymbol transparentColor[1] = {{NULL,
			(char *) "none", 0 }};
		Pixel bg;

		xpmAttributes.valuemask = XpmColorSymbols | XpmCloseness;
		xpmAttributes.colorsymbols = transparentColor;
		xpmAttributes.numsymbols = 1;
		xpmAttributes.closeness = 40000;
		XtVaGetValues(topLevel, XtNbackground, &bg, NULL);
		transparentColor[0].pixel = bg;
		(void) XpmCreatePixmapFromData(XtDisplay(topLevel),
			RootWindowOfScreen(XtScreen(topLevel)),
			RESIZE_XPM(pixmapSize), &pixmap, NULL,
			&xpmAttributes);
	}
	if (pixmap == (Pixmap) NULL)
#endif
		pixmap = XCreateBitmapFromData(XtDisplay(topLevel),
			RootWindowOfScreen(XtScreen(topLevel)),
			DEFINE_XBM);
	XtVaSetValues(topLevel,
#ifdef HAVE_MOTIF
		XmNkeyboardFocusPolicy, XmPOINTER, /* not XmEXPLICIT */
#else
		XtNinput, True,
#endif
		XtNiconPixmap, pixmap, NULL);
	XtAddCallback(dial, XtNselectCallback,
		(XtCallbackProc) callbackDial, (XtPointer) NULL);
	XtAddCallback(dialsq, XtNselectCallback,
		(XtCallbackProc) callbackDial, (XtPointer) NULL);
#if !defined(HAVE_MOTIF) && !defined(HAVE_ATHENA)
	XtVaSetValues(shell,
		XtNinput, True,
		XtNiconPixmap, pixmap, NULL);
#endif
	initialize();
	XtRealizeWidget(topLevel);
#if !defined(HAVE_MOTIF) && !defined(HAVE_ATHENA)
	XtRealizeWidget(shell);
#endif
	XGrabButton(XtDisplay(dial), (unsigned int) AnyButton, AnyModifier,
		XtWindow(dial), TRUE,
		(unsigned int) (ButtonPressMask | ButtonMotionMask |
		ButtonReleaseMask),
		GrabModeAsync, GrabModeAsync, XtWindow(dial),
		XCreateFontCursor(XtDisplay(dial), XC_hand2));
	XGrabButton(XtDisplay(dialsq), (unsigned int) AnyButton, AnyModifier,
		XtWindow(dialsq), TRUE,
		(unsigned int) (ButtonPressMask | ButtonMotionMask |
		ButtonReleaseMask),
		GrabModeAsync, GrabModeAsync, XtWindow(dialsq),
		XCreateFontCursor(XtDisplay(dialsq), XC_hand2));
#ifdef HAVE_EDITRES
	XtAddEventHandler(topLevel, (EventMask) 0, TRUE,
		(XtEventHandler) _XEditResCheckMessages, NULL);
#endif
	XtAppMainLoop(appCon);

#ifdef __VMS
	return 1;
#else
	return 0;
#endif
}
