//----------------------------------------------------------------------------- // Copyright 2007 Jonathan Westhues // // This file is part of LDmicro. // // LDmicro 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. // // LDmicro 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 LDmicro. If not, see . //------ // // Common controls in the main window. The main window consists of the drawing // area, where the ladder diagram is displayed, plus various controls for // scrolling, I/O list, menus. // Jonathan Westhues, Nov 2004 //----------------------------------------------------------------------------- #include #include #include #include #include #include "ldmicro.h" // scrollbars for the ladder logic area static HWND HorizScrollBar; static HWND VertScrollBar; int ScrollWidth; int ScrollHeight; BOOL NeedHoriz; // status bar at the bottom of the screen, to display settings static HWND StatusBar; // have to get back to the menus to gray/ungray, check/uncheck things static HMENU FileMenu; static HMENU EditMenu; static HMENU InstructionMenu; static HMENU ProcessorMenu; static HMENU SimulateMenu; static HMENU TopMenu; // listview used to maintain the list of I/O pins with symbolic names, plus // the internal relay too HWND IoList; static int IoListSelectionPoint; static BOOL IoListOutOfSync; int IoListHeight; int IoListTop; // whether the simulation is running in real time static BOOL RealTimeSimulationRunning; //----------------------------------------------------------------------------- // Create the standard Windows controls used in the main window: a Listview // for the I/O list, and a status bar for settings. //----------------------------------------------------------------------------- void MakeMainWindowControls(void) { LVCOLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvc.fmt = LVCFMT_LEFT; #define LV_ADD_COLUMN(hWnd, i, w, s) do { \ lvc.iSubItem = i; \ lvc.pszText = s; \ lvc.iOrder = 0; \ lvc.cx = w; \ ListView_InsertColumn(hWnd, i, &lvc); \ } while(0) IoList = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, "", WS_CHILD | LVS_REPORT | LVS_NOSORTHEADER | LVS_SHOWSELALWAYS | WS_TABSTOP | LVS_SINGLESEL | WS_CLIPSIBLINGS, 12, 25, 300, 300, MainWindow, NULL, Instance, NULL); ListView_SetExtendedListViewStyle(IoList, LVS_EX_FULLROWSELECT); int typeWidth = 85; int pinWidth = 100; int portWidth = 90; LV_ADD_COLUMN(IoList, LV_IO_NAME, 250, _("Name")); LV_ADD_COLUMN(IoList, LV_IO_TYPE, typeWidth, _("Type")); LV_ADD_COLUMN(IoList, LV_IO_STATE, 100, _("State")); //LV_ADD_COLUMN(IoList, LV_IO_PIN, pinWidth, _("Pin on Processor")); //LV_ADD_COLUMN(IoList, LV_IO_PORT, portWidth, _("MCU Port")); HorizScrollBar = CreateWindowEx(0, WC_SCROLLBAR, "", WS_CHILD | SBS_HORZ | SBS_BOTTOMALIGN | WS_VISIBLE | WS_CLIPSIBLINGS, 100, 100, 100, 100, MainWindow, NULL, Instance, NULL); VertScrollBar = CreateWindowEx(0, WC_SCROLLBAR, "", WS_CHILD | SBS_VERT | SBS_LEFTALIGN | WS_VISIBLE | WS_CLIPSIBLINGS, 200, 100, 100, 100, MainWindow, NULL, Instance, NULL); RECT scroll; GetWindowRect(HorizScrollBar, &scroll); ScrollHeight = scroll.bottom - scroll.top; GetWindowRect(VertScrollBar, &scroll); ScrollWidth = scroll.right - scroll.left; StatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, "LDmicro started", MainWindow, 0); int edges[] = { 250, 370, -1 }; SendMessage(StatusBar, SB_SETPARTS, 3, (LPARAM)edges); ShowWindow(IoList, SW_SHOW); } //----------------------------------------------------------------------------- // Set up the title bar text for the main window; indicate whether we are in // simulation or editing mode, and indicate the filename. //----------------------------------------------------------------------------- void UpdateMainWindowTitleBar(void) { char line[MAX_PATH+100]; if(InSimulationMode) { if(RealTimeSimulationRunning) { strcpy(line, _("OpenPLC Ladder - Simulation (Running)")); } else { strcpy(line, _("OpenPLC Ladder - Simulation (Stopped)")); } } else { strcpy(line, _("OpenPLC Ladder - Program Editor")); } if(strlen(CurrentSaveFile) > 0) { sprintf(line+strlen(line), " - %s", CurrentSaveFile); } else { strcat(line, _(" - (not yet saved)")); } SetWindowText(MainWindow, line); } //----------------------------------------------------------------------------- // Set the enabled state of the logic menu items to reflect where we are on // the schematic (e.g. can't insert two coils in series). //----------------------------------------------------------------------------- void SetMenusEnabled(BOOL canNegate, BOOL canNormal, BOOL canResetOnly, BOOL canSetOnly, BOOL canDelete, BOOL canInsertEnd, BOOL canInsertOther, BOOL canPushDown, BOOL canPushUp, BOOL canInsertComment) { EnableMenuItem(EditMenu, MNU_PUSH_RUNG_UP, canPushUp ? MF_ENABLED : MF_GRAYED); EnableMenuItem(EditMenu, MNU_PUSH_RUNG_DOWN, canPushDown ? MF_ENABLED : MF_GRAYED); EnableMenuItem(EditMenu, MNU_DELETE_RUNG, (Prog.numRungs > 1) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(InstructionMenu, MNU_NEGATE, canNegate ? MF_ENABLED : MF_GRAYED); EnableMenuItem(InstructionMenu, MNU_MAKE_NORMAL, canNormal ? MF_ENABLED : MF_GRAYED); EnableMenuItem(InstructionMenu, MNU_MAKE_RESET_ONLY, canResetOnly ? MF_ENABLED : MF_GRAYED); EnableMenuItem(InstructionMenu, MNU_MAKE_SET_ONLY, canSetOnly ? MF_ENABLED : MF_GRAYED); EnableMenuItem(InstructionMenu, MNU_INSERT_COMMENT, canInsertComment ? MF_ENABLED : MF_GRAYED); EnableMenuItem(EditMenu, MNU_DELETE_ELEMENT, canDelete ? MF_ENABLED : MF_GRAYED); int t; t = canInsertEnd ? MF_ENABLED : MF_GRAYED; EnableMenuItem(InstructionMenu, MNU_INSERT_COIL, t); EnableMenuItem(InstructionMenu, MNU_INSERT_RES, t); EnableMenuItem(InstructionMenu, MNU_INSERT_MOV, t); EnableMenuItem(InstructionMenu, MNU_INSERT_ADD, t); EnableMenuItem(InstructionMenu, MNU_INSERT_SUB, t); EnableMenuItem(InstructionMenu, MNU_INSERT_MUL, t); EnableMenuItem(InstructionMenu, MNU_INSERT_DIV, t); EnableMenuItem(InstructionMenu, MNU_INSERT_CTC, t); EnableMenuItem(InstructionMenu, MNU_INSERT_PERSIST, t); EnableMenuItem(InstructionMenu, MNU_INSERT_READ_ADC, t); EnableMenuItem(InstructionMenu, MNU_INSERT_SET_PWM, t); EnableMenuItem(InstructionMenu, MNU_INSERT_MASTER_RLY, t); EnableMenuItem(InstructionMenu, MNU_INSERT_SHIFT_REG, t); EnableMenuItem(InstructionMenu, MNU_INSERT_LUT, t); EnableMenuItem(InstructionMenu, MNU_INSERT_PWL, t); t = canInsertOther ? MF_ENABLED : MF_GRAYED; EnableMenuItem(InstructionMenu, MNU_INSERT_TON, t); EnableMenuItem(InstructionMenu, MNU_INSERT_TOF, t); EnableMenuItem(InstructionMenu, MNU_INSERT_OSR, t); EnableMenuItem(InstructionMenu, MNU_INSERT_OSF, t); EnableMenuItem(InstructionMenu, MNU_INSERT_RTO, t); EnableMenuItem(InstructionMenu, MNU_INSERT_CONTACTS, t); EnableMenuItem(InstructionMenu, MNU_INSERT_CTU, t); EnableMenuItem(InstructionMenu, MNU_INSERT_CTD, t); EnableMenuItem(InstructionMenu, MNU_INSERT_EQU, t); EnableMenuItem(InstructionMenu, MNU_INSERT_NEQ, t); EnableMenuItem(InstructionMenu, MNU_INSERT_GRT, t); EnableMenuItem(InstructionMenu, MNU_INSERT_GEQ, t); EnableMenuItem(InstructionMenu, MNU_INSERT_LES, t); EnableMenuItem(InstructionMenu, MNU_INSERT_LEQ, t); EnableMenuItem(InstructionMenu, MNU_INSERT_SHORT, t); EnableMenuItem(InstructionMenu, MNU_INSERT_OPEN, t); EnableMenuItem(InstructionMenu, MNU_INSERT_UART_SEND, t); EnableMenuItem(InstructionMenu, MNU_INSERT_UART_RECV, t); EnableMenuItem(InstructionMenu, MNU_INSERT_FMTD_STR, t); } //----------------------------------------------------------------------------- // Set the enabled state of the undo/redo menus. //----------------------------------------------------------------------------- void SetUndoEnabled(BOOL undoEnabled, BOOL redoEnabled) { EnableMenuItem(EditMenu, MNU_UNDO, undoEnabled ? MF_ENABLED : MF_GRAYED); EnableMenuItem(EditMenu, MNU_REDO, redoEnabled ? MF_ENABLED : MF_GRAYED); } //----------------------------------------------------------------------------- // Create the top-level menu bar for the main window. Mostly static, but we // create the "select processor" menu from the list in mcutable.h dynamically. //----------------------------------------------------------------------------- HMENU MakeMainWindowMenus(void) { HMENU compile, help; //settings, compile, help; int i; FileMenu = CreatePopupMenu(); AppendMenu(FileMenu, MF_STRING, MNU_NEW, _("&New\tCtrl+N")); AppendMenu(FileMenu, MF_STRING, MNU_OPEN, _("&Open...\tCtrl+O")); AppendMenu(FileMenu, MF_STRING, MNU_SAVE, _("&Save\tCtrl+S")); AppendMenu(FileMenu, MF_STRING, MNU_SAVE_AS,_("Save &As...")); AppendMenu(FileMenu, MF_SEPARATOR,0, ""); AppendMenu(FileMenu, MF_STRING, MNU_EXPORT, _("&Export As Text...\tCtrl+E")); AppendMenu(FileMenu, MF_SEPARATOR,0, ""); AppendMenu(FileMenu, MF_STRING, MNU_EXIT, _("E&xit")); EditMenu = CreatePopupMenu(); AppendMenu(EditMenu, MF_STRING, MNU_UNDO, _("&Undo\tCtrl+Z")); AppendMenu(EditMenu, MF_STRING, MNU_REDO, _("&Redo\tCtrl+Y")); AppendMenu(EditMenu, MF_SEPARATOR, 0, NULL); AppendMenu(EditMenu, MF_STRING, MNU_INSERT_RUNG_BEFORE, _("Insert Rung &Before\tShift+6")); AppendMenu(EditMenu, MF_STRING, MNU_INSERT_RUNG_AFTER, _("Insert Rung &After\tShift+V")); AppendMenu(EditMenu, MF_STRING, MNU_PUSH_RUNG_UP, _("Move Selected Rung &Up\tShift+Up")); AppendMenu(EditMenu, MF_STRING, MNU_PUSH_RUNG_DOWN, _("Move Selected Rung &Down\tShift+Down")); AppendMenu(EditMenu, MF_SEPARATOR, 0, NULL); AppendMenu(EditMenu, MF_STRING, MNU_DELETE_ELEMENT, _("&Delete Selected Element\tDel")); AppendMenu(EditMenu, MF_STRING, MNU_DELETE_RUNG, _("D&elete Rung\tShift+Del")); InstructionMenu = CreatePopupMenu(); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_COMMENT, _("Insert Co&mment\t;")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CONTACTS, _("Insert &Contacts\tC")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_OSR, _("Insert OSR (One Shot Rising)\t&/")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_OSF, _("Insert OSF (One Shot Falling)\t&\\")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_TON, _("Insert T&ON (Delayed Turn On)\tO")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_TOF, _("Insert TO&F (Delayed Turn Off)\tF")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_RTO, _("Insert R&TO (Retentive Delayed Turn On)\tT")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CTU, _("Insert CT&U (Count Up)\tU")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CTD, _("Insert CT&D (Count Down)\tI")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CTC, _("Insert CT&C (Count Circular)\tJ")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_EQU, _("Insert EQU (Compare for Equals)\t=")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_NEQ, _("Insert NEQ (Compare for Not Equals)")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_GRT, _("Insert GRT (Compare for Greater Than)\t>")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_GEQ, _("Insert GEQ (Compare for Greater Than or Equal)\t.")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_LES, _("Insert LES (Compare for Less Than)\t<")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_LEQ, _("Insert LEQ (Compare for Less Than or Equal)\t,")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_OPEN, _("Insert Open-Circuit")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SHORT, _("Insert Short-Circuit")); //AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_MASTER_RLY, // _("Insert Master Control Relay")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_COIL, _("Insert Coi&l\tL")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_RES, _("Insert R&ES (Counter/RTO Reset)\tE")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_MOV, _("Insert MOV (Move)\tM")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_ADD, _("Insert ADD (16-bit Integer Add)\t+")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SUB, _("Insert SUB (16-bit Integer Subtract)\t-")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_MUL, _("Insert MUL (16-bit Integer Multiply)\t*")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_DIV, _("Insert DIV (16-bit Integer Divide)\tD")); /*AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SHIFT_REG, _("Insert Shift Register")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_LUT, _("Insert Look-Up Table")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_PWL, _("Insert Piecewise Linear")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_FMTD_STR, _("Insert Formatted String Over UART")); AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_UART_SEND, _("Insert &UART Send")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_UART_RECV, _("Insert &UART Receive")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SET_PWM, _("Insert Set PWM Output")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_READ_ADC, _("Insert A/D Converter Read\tP")); AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_PERSIST, _("Insert Make Persistent")); */ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL); AppendMenu(InstructionMenu, MF_STRING, MNU_MAKE_NORMAL, _("Make Norm&al\tA")); AppendMenu(InstructionMenu, MF_STRING, MNU_NEGATE, _("Make &Negated\tN")); AppendMenu(InstructionMenu, MF_STRING, MNU_MAKE_SET_ONLY, _("Make &Set-Only\tS")); AppendMenu(InstructionMenu, MF_STRING, MNU_MAKE_RESET_ONLY, _("Make &Reset-Only\tR")); /* settings = CreatePopupMenu(); AppendMenu(settings, MF_STRING, MNU_MCU_SETTINGS, _("&MCU Parameters...")); ProcessorMenu = CreatePopupMenu(); for(i = 0; i < NUM_SUPPORTED_MCUS; i++) { AppendMenu(ProcessorMenu, MF_STRING, MNU_PROCESSOR_0+i, SupportedMcus[i].mcuName); } AppendMenu(ProcessorMenu, MF_STRING, MNU_PROCESSOR_0+i, _("(no microcontroller)")); AppendMenu(settings, MF_STRING | MF_POPUP, (UINT_PTR)ProcessorMenu, _("&Microcontroller")); */ SimulateMenu = CreatePopupMenu(); AppendMenu(SimulateMenu, MF_STRING, MNU_SIMULATION_MODE, _("Si&mulation Mode\tCtrl+M")); AppendMenu(SimulateMenu, MF_STRING | MF_GRAYED, MNU_START_SIMULATION, _("Start &Real-Time Simulation\tCtrl+R")); AppendMenu(SimulateMenu, MF_STRING | MF_GRAYED, MNU_STOP_SIMULATION, _("&Halt Simulation\tCtrl+H")); AppendMenu(SimulateMenu, MF_STRING | MF_GRAYED, MNU_SINGLE_CYCLE, _("Single &Cycle\tSpace")); compile = CreatePopupMenu(); AppendMenu(compile, MF_STRING, MNU_COMPILE, _("&Compile\tF5")); AppendMenu(compile, MF_STRING, MNU_COMPILE_AS, _("Compile &As...")); help = CreatePopupMenu(); AppendMenu(help, MF_STRING, MNU_MANUAL, _("&Manual...\tF1")); AppendMenu(help, MF_STRING, MNU_ABOUT, _("&About...")); TopMenu = CreateMenu(); AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)FileMenu, _("&File")); AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)EditMenu, _("&Edit")); /* AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)settings, _("&Settings")); */ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)InstructionMenu, _("&Instruction")); AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)SimulateMenu, _("Si&mulate")); AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)compile, _("&Compile")); AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)help, _("&Help")); return TopMenu; } //----------------------------------------------------------------------------- // Adjust the size and visibility of the scrollbars as necessary, either due // to a change in the size of the program or a change in the size of the // window. //----------------------------------------------------------------------------- void RefreshScrollbars(void) { SCROLLINFO vert, horiz; SetUpScrollbars(&NeedHoriz, &horiz, &vert); SetScrollInfo(HorizScrollBar, SB_CTL, &horiz, TRUE); SetScrollInfo(VertScrollBar, SB_CTL, &vert, TRUE); RECT main; GetClientRect(MainWindow, &main); if(NeedHoriz) { MoveWindow(HorizScrollBar, 0, IoListTop - ScrollHeight - 2, main.right - ScrollWidth - 2, ScrollHeight, TRUE); ShowWindow(HorizScrollBar, SW_SHOW); EnableWindow(HorizScrollBar, TRUE); } else { ShowWindow(HorizScrollBar, SW_HIDE); } MoveWindow(VertScrollBar, main.right - ScrollWidth - 2, 1, ScrollWidth, NeedHoriz ? (IoListTop - ScrollHeight - 4) : (IoListTop - 3), TRUE); MoveWindow(VertScrollBar, main.right - ScrollWidth - 2, 1, ScrollWidth, NeedHoriz ? (IoListTop - ScrollHeight - 4) : (IoListTop - 3), TRUE); InvalidateRect(MainWindow, NULL, FALSE); } //----------------------------------------------------------------------------- // Respond to a WM_VSCROLL sent to the main window, presumably by the one and // only vertical scrollbar that it has as a child. //----------------------------------------------------------------------------- void VscrollProc(WPARAM wParam) { int prevY = ScrollYOffset; switch(LOWORD(wParam)) { case SB_LINEUP: case SB_PAGEUP: if(ScrollYOffset > 0) { ScrollYOffset--; } break; case SB_LINEDOWN: case SB_PAGEDOWN: if(ScrollYOffset < ScrollYOffsetMax) { ScrollYOffset++; } break; case SB_TOP: ScrollYOffset = 0; break; case SB_BOTTOM: ScrollYOffset = ScrollYOffsetMax; break; case SB_THUMBTRACK: case SB_THUMBPOSITION: ScrollYOffset = HIWORD(wParam); break; } if(prevY != ScrollYOffset) { SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_POS; si.nPos = ScrollYOffset; SetScrollInfo(VertScrollBar, SB_CTL, &si, TRUE); InvalidateRect(MainWindow, NULL, FALSE); } } //----------------------------------------------------------------------------- // Respond to a WM_HSCROLL sent to the main window, presumably by the one and // only horizontal scrollbar that it has as a child. //----------------------------------------------------------------------------- void HscrollProc(WPARAM wParam) { int prevX = ScrollXOffset; switch(LOWORD(wParam)) { case SB_LINEUP: ScrollXOffset -= FONT_WIDTH; break; case SB_PAGEUP: ScrollXOffset -= POS_WIDTH*FONT_WIDTH; break; case SB_LINEDOWN: ScrollXOffset += FONT_WIDTH; break; case SB_PAGEDOWN: ScrollXOffset += POS_WIDTH*FONT_WIDTH; break; case SB_TOP: ScrollXOffset = 0; break; case SB_BOTTOM: ScrollXOffset = ScrollXOffsetMax; break; case SB_THUMBTRACK: case SB_THUMBPOSITION: ScrollXOffset = HIWORD(wParam); break; } if(ScrollXOffset > ScrollXOffsetMax) ScrollXOffset = ScrollXOffsetMax; if(ScrollXOffset < 0) ScrollXOffset = 0; if(prevX != ScrollXOffset) { SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_POS; si.nPos = ScrollXOffset; SetScrollInfo(HorizScrollBar, SB_CTL, &si, TRUE); InvalidateRect(MainWindow, NULL, FALSE); } } //----------------------------------------------------------------------------- // Cause the status bar and the list view to be in sync with the actual data // structures describing the settings and the I/O configuration. Listview // does callbacks to get the strings it displays, so it just needs to know // how many elements to populate. //----------------------------------------------------------------------------- void RefreshControlsToSettings(void) { int i; if(!IoListOutOfSync) { IoListSelectionPoint = -1; for(i = 0; i < Prog.io.count; i++) { if(ListView_GetItemState(IoList, i, LVIS_SELECTED)) { IoListSelectionPoint = i; break; } } } ListView_DeleteAllItems(IoList); for(i = 0; i < Prog.io.count; i++) { LVITEM lvi; lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; lvi.state = lvi.stateMask = 0; lvi.iItem = i; lvi.iSubItem = 0; lvi.pszText = LPSTR_TEXTCALLBACK; lvi.lParam = i; if(ListView_InsertItem(IoList, &lvi) < 0) oops(); } if(IoListSelectionPoint >= 0) { for(i = 0; i < Prog.io.count; i++) { ListView_SetItemState(IoList, i, 0, LVIS_SELECTED); } ListView_SetItemState(IoList, IoListSelectionPoint, LVIS_SELECTED, LVIS_SELECTED); ListView_EnsureVisible(IoList, IoListSelectionPoint, FALSE); } IoListOutOfSync = FALSE; if(Prog.mcu) { SendMessage(StatusBar, SB_SETTEXT, 0, (LPARAM)Prog.mcu->mcuName); } else { SendMessage(StatusBar, SB_SETTEXT, 0, (LPARAM)_("no MCU selected")); } char buf[256]; sprintf(buf, _("cycle time %.2f ms"), (double)Prog.cycleTime/1000.0); SendMessage(StatusBar, SB_SETTEXT, 1, (LPARAM)buf); if(Prog.mcu && (Prog.mcu->whichIsa == ISA_ANSIC || Prog.mcu->whichIsa == ISA_INTERPRETED)) { strcpy(buf, ""); } else { sprintf(buf, _("processor clock %.4f MHz"), (double)Prog.mcuClock/1000000.0); } SendMessage(StatusBar, SB_SETTEXT, 2, (LPARAM)buf); for(i = 0; i < NUM_SUPPORTED_MCUS; i++) { if(&SupportedMcus[i] == Prog.mcu) { CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_CHECKED); } else { CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_UNCHECKED); } } // `(no microcontroller)' setting if(!Prog.mcu) { CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_CHECKED); } else { CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_UNCHECKED); } } //----------------------------------------------------------------------------- // Regenerate the I/O list, keeping the selection in the same place if // possible. //----------------------------------------------------------------------------- void GenerateIoListDontLoseSelection(void) { int i; IoListSelectionPoint = -1; for(i = 0; i < Prog.io.count; i++) { if(ListView_GetItemState(IoList, i, LVIS_SELECTED)) { IoListSelectionPoint = i; break; } } IoListSelectionPoint = GenerateIoList(IoListSelectionPoint); // can't just update the listview index; if I/O has been added then the // new selection point might be out of range till we refill it IoListOutOfSync = TRUE; RefreshControlsToSettings(); } //----------------------------------------------------------------------------- // Called when the main window has been resized. Adjust the size of the // status bar and the listview to reflect the new window size. //----------------------------------------------------------------------------- void MainWindowResized(void) { RECT main; GetClientRect(MainWindow, &main); RECT status; GetWindowRect(StatusBar, &status); int statusHeight = status.bottom - status.top; MoveWindow(StatusBar, 0, main.bottom - statusHeight, main.right, statusHeight, TRUE); // Make sure that the I/O list can't disappear entirely. if(IoListHeight < 30) { IoListHeight = 30; } IoListTop = main.bottom - IoListHeight - statusHeight; // Make sure that we can't drag the top of the I/O list above the // bottom of the menu bar, because it then becomes inaccessible. if(IoListTop < 5) { IoListHeight = main.bottom - statusHeight - 5; IoListTop = main.bottom - IoListHeight - statusHeight; } MoveWindow(IoList, 0, IoListTop, main.right, IoListHeight, TRUE); RefreshScrollbars(); InvalidateRect(MainWindow, NULL, FALSE); } //----------------------------------------------------------------------------- // Toggle whether we are in simulation mode. A lot of options are only // available in one mode or the other. //----------------------------------------------------------------------------- void ToggleSimulationMode(void) { InSimulationMode = !InSimulationMode; if(InSimulationMode) { EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_ENABLED); EnableMenuItem(SimulateMenu, MNU_SINGLE_CYCLE, MF_ENABLED); EnableMenuItem(FileMenu, MNU_OPEN, MF_GRAYED); EnableMenuItem(FileMenu, MNU_SAVE, MF_GRAYED); EnableMenuItem(FileMenu, MNU_SAVE_AS, MF_GRAYED); EnableMenuItem(FileMenu, MNU_NEW, MF_GRAYED); EnableMenuItem(FileMenu, MNU_EXPORT, MF_GRAYED); EnableMenuItem(TopMenu, 1, MF_GRAYED | MF_BYPOSITION); EnableMenuItem(TopMenu, 2, MF_GRAYED | MF_BYPOSITION); //EnableMenuItem(TopMenu, 3, MF_GRAYED | MF_BYPOSITION); EnableMenuItem(TopMenu, 4, MF_GRAYED | MF_BYPOSITION); CheckMenuItem(SimulateMenu, MNU_SIMULATION_MODE, MF_CHECKED); ClearSimulationData(); // Recheck InSimulationMode, because there could have been a compile // error, which would have kicked us out of simulation mode. if(UartFunctionUsed() && InSimulationMode) { ShowUartSimulationWindow(); } } else { RealTimeSimulationRunning = FALSE; KillTimer(MainWindow, TIMER_SIMULATE); EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_GRAYED); EnableMenuItem(SimulateMenu, MNU_STOP_SIMULATION, MF_GRAYED); EnableMenuItem(SimulateMenu, MNU_SINGLE_CYCLE, MF_GRAYED); EnableMenuItem(FileMenu, MNU_OPEN, MF_ENABLED); EnableMenuItem(FileMenu, MNU_SAVE, MF_ENABLED); EnableMenuItem(FileMenu, MNU_SAVE_AS, MF_ENABLED); EnableMenuItem(FileMenu, MNU_NEW, MF_ENABLED); EnableMenuItem(FileMenu, MNU_EXPORT, MF_ENABLED); EnableMenuItem(TopMenu, 1, MF_ENABLED | MF_BYPOSITION); EnableMenuItem(TopMenu, 2, MF_ENABLED | MF_BYPOSITION); //EnableMenuItem(TopMenu, 3, MF_ENABLED | MF_BYPOSITION); EnableMenuItem(TopMenu, 4, MF_ENABLED | MF_BYPOSITION); CheckMenuItem(SimulateMenu, MNU_SIMULATION_MODE, MF_UNCHECKED); if(UartFunctionUsed()) { DestroyUartSimulationWindow(); } } UpdateMainWindowTitleBar(); DrawMenuBar(MainWindow); InvalidateRect(MainWindow, NULL, FALSE); ListView_RedrawItems(IoList, 0, Prog.io.count - 1); } //----------------------------------------------------------------------------- // Start real-time simulation. Have to update the controls grayed status // to reflect this. //----------------------------------------------------------------------------- void StartSimulation(void) { RealTimeSimulationRunning = TRUE; EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_GRAYED); EnableMenuItem(SimulateMenu, MNU_STOP_SIMULATION, MF_ENABLED); StartSimulationTimer(); UpdateMainWindowTitleBar(); } //----------------------------------------------------------------------------- // Stop real-time simulation. Have to update the controls grayed status // to reflect this. //----------------------------------------------------------------------------- void StopSimulation(void) { RealTimeSimulationRunning = FALSE; EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_ENABLED); EnableMenuItem(SimulateMenu, MNU_STOP_SIMULATION, MF_GRAYED); KillTimer(MainWindow, TIMER_SIMULATE); UpdateMainWindowTitleBar(); }