diff options
Diffstat (limited to 'app/bin/toolbar.c')
| -rw-r--r-- | app/bin/toolbar.c | 613 | 
1 files changed, 613 insertions, 0 deletions
diff --git a/app/bin/toolbar.c b/app/bin/toolbar.c new file mode 100644 index 0000000..57c3659 --- /dev/null +++ b/app/bin/toolbar.c @@ -0,0 +1,613 @@ +/*****************************************************************//** + * \file   toolbar.c + * \brief  Toolbar specific functions and data + *********************************************************************/ + +/*  XTrackCad - Model Railroad CAD + *  Copyright (C) 2005,2023 Dave Bullis, Martin Fischer + * + *  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 2 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, write to the Free Software + *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "common.h" +#include "custom.h" +#include "fileio.h" +#include "param.h" +#include "track.h" +#include "include/toolbar.h" + +EXPORT void ToolbarLayout(void* unused); + +struct sToolbarState { +	int previousGroup;          // control variable for change control ops +	int layerButton;            // number of layer controls shown +	wWinPix_t nextX;            // drawing position for next control +	wWinPix_t rowHeight;        // height of row +}; + +// local function prototypes +static void InitializeToolbarDialog(void); +static void ToolbarChange(long changes); +static void ToolbarOk(void* unused); +static void ToolbarButtonPlace(struct sToolbarState* tbState, wIndex_t inx); +static void SaveToolbarConfig(void); + +// toolbar properties +static long toolbarSet; +static wWinPix_t toolbarHeight = 0; + +#define TOOLBARSET_INIT				(0xFFFF) +#define TOOLBAR_SECTION "misc" +#define TOOLBAR_VARIABLE "toolbarset" + +#define GROUP_DISTANCE (5)     // default distance between button groups +#define GROUP_BIG_DISTANCE (GROUP_DISTANCE * 3) // big gap +#define TOOLBAR_MARGIN (20)     // left and right margins of toolbar +#define FIXEDLAYERCONTROLS (2)  // the layer groups has two controls that are +// always visible (list and background) + +/* +* Bit handling macros +* these macros do not change the passed values but return the result. +* so if you want to change the value it has to be assigned eg. +* bits = SETBIT(bits, 2); +* in order to set bit 2 of the variable "bits" +* +*/ +#define GETBIT(value, bitpos )     ((value) & (1UL << (bitpos))) +#define ISBITSET(value, bitpos )   (((value)&(1UL <<bitpos))!=0) +#define CLEARBIT(value, bitpos )   ((value) & ~(1UL <<(bitpos))) +#define SETBIT(value, bitpos)      ((value) | (1UL <<(bitpos))) + +#define ISGROUPVISIBLE(group)   ISBITSET(toolbarSet, group) + +// toolbar button list +#define BUTTON_MAX (250) +static struct { +	wControl_p control; +	wBool_t enabled; +	wWinPix_t x, y; +	long options; +	int group; +	wIndex_t cmdInx; +} buttonList[BUTTON_MAX]; +EXPORT int buttonCnt = 0; // TODO-misc-refactor + + +// control the order of the button groups inside the toolbar + +struct buttonGroups { +	char*   label;          // display label +	int     group;          // id of group +	bool    biggap;         // control distance to previous group +}; + +static struct buttonGroups allToolbarGroups[] = { +	{N_("File Buttons"),            BG_FILE, false}, +	{N_("Print Buttons"),           BG_PRINT, false}, +	{N_("Import/Export Buttons"),   BG_EXPORTIMPORT, false}, +	{N_("Zoom Buttons"),            BG_ZOOM, false}, +	{N_("Undo Buttons"),            BG_UNDO, false}, +	{N_("Easement Button"),         BG_EASE, false}, +	{N_("SnapGrid Buttons"),        BG_SNAP, false}, +	{N_("Create Track Buttons"),    BG_TRKCRT, true}, +	{N_("Layout Control Elements"), BG_CONTROL, true}, +	{N_("Modify Track Buttons"),    BG_TRKMOD, false}, +	{N_("Properties/Select"),       BG_SELECT, false}, +	{N_("Track Group Buttons"),     BG_TRKGRP, false}, +	{N_("Train Group Buttons"),     BG_TRAIN, true}, +	{N_("Create Misc Buttons"),     BG_MISCCRT, false}, +	{N_("Ruler Button"),            BG_RULER, false}, +	{N_("Layer Buttons"),           BG_LAYER, true}, +	{N_("Hot Bar"),                 BG_HOTBAR}, +	{NULL, 0L} +}; + +#define COUNTTOOLBARGROUPS (BG_LAST) + +// toolbar options dialog +static wWin_p toolbarW; +static unsigned long toggleSet; + +// callbacks for button presses +static void SelectAllGroups(void* unused); +static void InvertSelection(void* unused); + +static paramData_t toolbarPLs[] = { +	{ PD_TOGGLE, &toggleSet, "toolbarset", 0, NULL}, +#define I_SELECTALL     (1) +	{ PD_BUTTON, SelectAllGroups, "selectall", PDO_DLGBOXEND, NULL, N_("Select All") }, +#define I_INVERT        (2) +	{ PD_BUTTON, InvertSelection, "invert", PDO_DLGHORZ, NULL, N_("Invert Selection")} +}; + +static paramGroup_t toolbarPG = { "toolbar", PGO_RECORD, toolbarPLs, +                                  COUNT(toolbarPLs) +                                }; + +/** + * Initialize the list of available options. The list of labels is created + * from the allToolbarGroups array. Memory allocated here + * is never freed as it might be used when opening the dialog + * + * \param unused + */ + +static void +InitializeToolbarDialog(void) +{ +	char** labels = MyMalloc((COUNT(allToolbarGroups)) * sizeof(char*)); + +	for (int i = 0; i < COUNT(allToolbarGroups); i++) { +		labels[i] = allToolbarGroups[i].label; +	} +	toolbarPLs[0].winData = labels; + +	ParamRegister(&toolbarPG); +} + +static void ToolbarChange(long changes) +{ +	if ((changes & CHANGE_TOOLBAR)) { +		MainProc(mainW, wResize_e, NULL, NULL); +	} +} + +/** + * Handle button press to select all groups. Set all bits to 1, unused bits + * will be ignored + * + * \param unused + */ +static void SelectAllGroups(void* unused) +{ +	toggleSet = ~(0UL); + +	ParamLoadControls(&toolbarPG); +} + +/** + * Handle button press to invert the current selection. Invert all bits by, + * XOR with 1s, unused bits will be ignored + * + * \param unused + */ +static void InvertSelection(void* unused) +{ +	toggleSet ^= ~(0UL); + +	ParamLoadControls(&toolbarPG); +} + +/** + * Handle the ok press. The bit pattern set up from the dialog is converted + * to the pattern used by the toolbar. Then the toolbar is refreshed. + * + * \param unused + */ + +static void ToolbarOk(void* unused) +{ +	toolbarSet = 0; + +	for (int i = 0; i < COUNTTOOLBARGROUPS; i++) { +		if (toggleSet & (1UL << i)) { +			toolbarSet = SETBIT(toolbarSet, allToolbarGroups[i].group); +		} +	} +	SaveToolbarConfig(); +	ToolbarLayout(unused); +	MainProc(mainW, wResize_e, NULL, NULL); +	wHide(toolbarW); +} + +/** + * When selected from the menu the toolbar config dialog is opened. First lazy + * initialization is done on first call. Then the toggle states are set from + * the toolbar configuration bit pattern and the dialog is shown. + * + * \param unused + */ + +EXPORT void DoToolbar(void* unused) +{ +	if (!toolbarW) { +		InitializeToolbarDialog(); +		toolbarW = ParamCreateDialog(&toolbarPG, +		                             MakeWindowTitle(_("Toolbar Options")), _("OK"), ToolbarOk, ParamCancel_Restore, +		                             TRUE, NULL, 0, NULL); +	} + +	toggleSet = 0; +	for (int i = 0; i < COUNTTOOLBARGROUPS; i++) { +		if (ISBITSET(toolbarSet, allToolbarGroups[i].group)) { +			toggleSet = SETBIT(toggleSet, i); +		} +	} +	ParamLoadControls(&toolbarPG); +	wShow(toolbarW); +} + +/** + * Check whether button group is configured to be visible. + * + * \param   group   single group to check + * \return  true if visible + */ + +EXPORT bool +ToolbarIsGroupVisible(int group) +{ +	CHECK(group > 0); +	CHECK(group <= COUNTTOOLBARGROUPS); + +	return(ISGROUPVISIBLE(group)); +} + +/** + * Get the current height of the toolbar. + * + * \return + */ + +EXPORT wWinPix_t +ToolbarGetHeight(void) +{ +	return(toolbarHeight); +} + +/** + * . + */ + +EXPORT void +ToolbarSetHeight(wWinPix_t newHeight) +{ +	toolbarHeight = newHeight; +} + +/** + * Buttons are visible when the command is enabled or when additional + * layer buttons need to be shown. + * + * \param inx + */ + +bool +IsButtonVisible(int group, long mode, long options, long layerButtons) +{ +	if (group == BG_LAYER) { +		if (layerButtons < layerCount+FIXEDLAYERCONTROLS) { +			return true; +		} else { +			return false; +		} +	} +	return(IsCommandEnabled(mode, options)); +} + +/** + * Calculate the position and visibility of a button and display it. + * + * \param inx   index into button list + */ + +static void ToolbarButtonPlace(struct sToolbarState *tbState, wIndex_t inx) +{ +	wWinPix_t w, h, offset; +	wWinPix_t width; +	wWinPix_t gap = GROUP_DISTANCE; +	int currentGroup = buttonList[inx].group; + +	wWinGetSize(mainW, &width, &h); + +	if (buttonList[inx].control) { +		if (tbState->rowHeight <= 0) { +			tbState->rowHeight = wControlGetHeight(buttonList[inx].control); +			toolbarHeight = tbState->rowHeight + 5; +		} + +		if (currentGroup != tbState->previousGroup) { +			for (int i = 0; i < COUNTTOOLBARGROUPS; i++) { +				if (allToolbarGroups[i].group == currentGroup && +				    allToolbarGroups[i].biggap) { +					gap = GROUP_BIG_DISTANCE; +				} +			} +		} + +		if ((ISGROUPVISIBLE(currentGroup)) && +		    IsButtonVisible(currentGroup, programMode, +		                    buttonList[inx].options, tbState->layerButton )) { +			if (currentGroup != tbState->previousGroup) { +				tbState->nextX += gap; +				tbState->previousGroup = currentGroup; +			} +			w = wControlGetWidth(buttonList[inx].control); +			h = wControlGetHeight(buttonList[inx].control); +			if (h < tbState->rowHeight) { +				offset = (h - tbState->rowHeight) / 2; +				h = tbState->rowHeight;  //Uniform +			} else { +				offset = 0; +			} +			if (inx < buttonCnt - 1 && +			    (buttonList[inx + 1].options & IC_ABUT)) { +				w += wControlGetWidth(buttonList[inx + 1].control); +			} +			if (tbState->nextX + w > width - TOOLBAR_MARGIN) { +				tbState->nextX = 5; +				toolbarHeight += h + 5; +			} +			if ((currentGroup == BG_LAYER) && +			    tbState->layerButton >= FIXEDLAYERCONTROLS && +			    GetLayerHidden(tbState->layerButton - FIXEDLAYERCONTROLS)) { +				wControlShow(buttonList[inx].control, FALSE); +				tbState->layerButton++; +			} else { +				wWinPix_t newX = tbState->nextX; +				wWinPix_t newY = toolbarHeight - (h + 5 + offset); + +				// count number of shown layer buttons +				if (currentGroup == BG_LAYER) { +					tbState->layerButton++; +				} +				if ((newX != buttonList[inx].x) || (newY != buttonList[inx].y)) { +					wControlShow(buttonList[inx].control, FALSE); + +					wControlSetPos(buttonList[inx].control, newX, +					               newY); +				} +				buttonList[inx].x = newX; +				buttonList[inx].y = newY; +				tbState->nextX += wControlGetWidth(buttonList[inx].control); +				wControlShow(buttonList[inx].control, TRUE); +			} +		} else { +			wControlShow(buttonList[inx].control, FALSE); +		} +	} +} + +EXPORT void ToolbarLayout(void* data) +{ +	int inx; +	struct sToolbarState state = { +		.previousGroup = 0, +		.nextX = 0, +		.layerButton = 0, +		.rowHeight = 0, +	}; + +	for (inx = 0; inx < buttonCnt; inx++) { +		ToolbarButtonPlace(&state, inx); +	} + +	if (ISBITSET(toolbarSet, BG_HOTBAR)) { +		LayoutHotBar(data); +	} else { +		HideHotBar(); +	} +} + +/** + *  Set the 'pressed' state of a toolbar button. + * + * \param button    index into button list + * \param busy      desired button state + */ + +EXPORT void ToolbarButtonBusy(wIndex_t button, wBool_t busy) +{ +	wButtonSetBusy((wButton_p)buttonList[button].control, +	               busy); +} + +/** + * Set state of a toolbar button . + * + * \param button	index into button list + * \param enable	desired state, FALSE if disabled, TRUE if enabled + */ + +EXPORT void ToolbarButtonEnable(wIndex_t button, wBool_t enable) +{ +	wControlActive(buttonList[button].control, +	               enable); +} + +/** + * Enable toolbar buttons that depend on selected track. + * + * \param selected true if any track is selected + */ + +EXPORT void ToolbarButtonEnableIfSelect(bool selected) +{ +	for (int inx = 0; inx < buttonCnt; inx++) { +		if (buttonList[inx].cmdInx < 0 +		    && (buttonList[inx].options & IC_SELECTED)) { +			ToolbarButtonEnable(inx, selected ); +		} +	} +} + +/** + * Place a control onto the toolbar. The control is added to the toolbar + * control list and initially hidden. Placement and visibility is controlled + * by ToolbarButtonPlace() + * + * \param control   the control to add + * \param options   control options + */ + +EXPORT void ToolbarControlAdd(wControl_p control, long options, int cmdGroup) +{ +	CHECK(buttonCnt < BUTTON_MAX - 1); +	buttonList[buttonCnt].enabled = TRUE; +	buttonList[buttonCnt].options = options; +	buttonList[buttonCnt].group = cmdGroup; +	buttonList[buttonCnt].x = 0; +	buttonList[buttonCnt].y = 0; +	buttonList[buttonCnt].control = control; +	buttonList[buttonCnt].cmdInx = -1; +	wControlShow(control, FALSE); +	buttonCnt++; +} + +/** + * Link a command to a specific toolbar button. + * + * \param button	the button + * \param command	command to activate when button is pressed + * \return + */ + +EXPORT void ToolbarButtonCommandLink(wIndex_t button, int command) +{ +	if (button >= 0 && buttonList[button].cmdInx == -1) { +		// set button back-link +		buttonList[button].cmdInx = commandCnt; +	} +} + +/** + * Update the toolbar button for selected command eg. circle vs. filled + * circle. + * + * \param button	toolbar button + * \param command	current command + * \param icon		new icon + * \param helpKey	new help key + * \param context	new command context + */ + +EXPORT void ToolbarUpdateButton(wIndex_t button, wIndex_t command, +                                char * icon, +                                const char * helpKey, +                                void * context) +{ +	if (buttonList[button].cmdInx != command) { +		wButtonSetLabel((wButton_p) buttonList[button].control,icon); +		wControlSetHelp(buttonList[button].control, +		                GetBalloonHelpStr(helpKey)); +		wControlSetContext(buttonList[button].control, +		                   context); +		buttonList[button].cmdInx = command; +	} +} + +/*--------------------------------------------------------------------*/ + +/** + * Handle simulated button press during playbook. + * + * \param buttInx   selected button + */ +EXPORT void PlaybackButtonMouse(wIndex_t buttInx) +{ +	wWinPix_t cmdX, cmdY; +	coOrd pos; + +	if (buttInx < 0 || buttInx >= buttonCnt) { +		return; +	} +	if (buttonList[buttInx].control == NULL) { +		return; +	} +	cmdX = buttonList[buttInx].x + 17; +	cmdY = toolbarHeight - (buttonList[buttInx].y + 17) +	       + (wWinPix_t)(mainD.size.y / mainD.scale * mainD.dpi) + 30; + +	mainD.Pix2CoOrd(&mainD, cmdX, cmdY, &pos); +	MovePlaybackCursor(&mainD, pos, TRUE, buttonList[buttInx].control); +	if (playbackTimer == 0) { +		wButtonSetBusy((wButton_p)buttonList[buttInx].control, TRUE); +		wFlush(); +		wPause(500); +		wButtonSetBusy((wButton_p)buttonList[buttInx].control, FALSE); +		wFlush(); +	} +} + +/** + * Handle cursor positioning for toolbar buttons during playback . + * + * \param buttonInx + */ + +EXPORT void ToolbarButtonPlayback(wIndex_t buttonInx) +{ +	wWinPix_t cmdX, cmdY; +	coOrd pos; + +	cmdX = buttonList[buttonInx].x + 17; +	cmdY = toolbarHeight - (buttonList[buttonInx].y + 17) +	       + (wWinPix_t)(mainD.size.y / mainD.scale * mainD.dpi) + 30; +	mainD.Pix2CoOrd(&mainD, cmdX, cmdY, &pos); +	MovePlaybackCursor(&mainD, pos, TRUE, buttonList[buttonInx].control); +} + +/** + * Save the toolbar setting to the configuration file. + * + */ + +static void +SaveToolbarConfig(void) +{ +	wPrefSetInteger(TOOLBAR_SECTION, TOOLBAR_VARIABLE, toolbarSet); +	if (recordF) +		fprintf(recordF, "PARAMETER %s %s %ld", TOOLBAR_SECTION, +		        TOOLBAR_VARIABLE, toolbarSet); + +} + +/** + * Get the preferences for the toolbar from the configuration file. + * Bits unused are cleared just to be sure; + * + */ + +EXPORT void +ToolbarLoadConfig(void) +{ +	unsigned long maxToolbarSet = (1 << COUNTTOOLBARGROUPS) - 1; +	long toolbarSetIni; + +	wPrefGetInteger(TOOLBAR_SECTION, TOOLBAR_VARIABLE, &toolbarSetIni, +	                TOOLBARSET_INIT); + +	toolbarSet = (unsigned long)toolbarSetIni & maxToolbarSet; + +	// unused but saved to stay compatible +	wPrefSetInteger(TOOLBAR_SECTION, "max-toolbarset", maxToolbarSet); + +	if (recordF) +		fprintf(recordF, "PARAMETER %s %s %lX -> %lX", TOOLBAR_SECTION, +		        TOOLBAR_VARIABLE, toolbarSetIni, toolbarSet); +} + +/** + * Initialize toolbar functions. + * + */ + +EXPORT void InitToolbar(void) +{ +	RegisterChangeNotification(ToolbarChange); + +	ToolbarLoadConfig(); +}  | 
