/*****************************************************************//** * \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 < 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(); }