OpenPLC-Ladder-Editor/ldmicro-rel2.2/ldmicro/ldmicro.cpp

1177 lines
36 KiB
C++

//-----------------------------------------------------------------------------
// 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 <http://www.gnu.org/licenses/>.
//------
//
// A ladder logic compiler for 8 bit micros: user draws a ladder diagram,
// with an appropriately constrained `schematic editor,' and then we can
// simulated it under Windows or generate PIC/AVR code that performs the
// requested operations. This files contains the program entry point, plus
// most of the UI logic relating to the main window.
// Jonathan Westhues, Oct 2004
//-----------------------------------------------------------------------------
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <stdio.h>
#include <stdlib.h>
#include "ldmicro.h"
#include "freeze.h"
#include "mcutable.h"
HINSTANCE Instance;
HWND MainWindow;
HDC Hdc;
// parameters used to capture the mouse when implementing our totally non-
// general splitter control
static HHOOK MouseHookHandle;
static int MouseY;
// For the open/save dialog boxes
#define LDMICRO_PATTERN "OpenPLC Ladder Logic Programs (*.ld)\0*.ld\0" \
"All files\0*\0\0"
char CurrentSaveFile[MAX_PATH];
static BOOL ProgramChangedNotSaved = FALSE;
#define HEX_PATTERN "Intel Hex Files (*.hex)\0*.hex\0All files\0*\0\0"
#define C_PATTERN "C Source Files (*.c)\0*.c\0All Files\0*\0\0"
#define INTERPRETED_PATTERN \
"Interpretable Byte Code Files (*.int)\0*.int\0All Files\0*\0\0"
char CurrentCompileFile[MAX_PATH];
#define TXT_PATTERN "Text Files (*.txt)\0*.txt\0All files\0*\0\0"
// Everything relating to the PLC's program, I/O configuration, processor
// choice, and so on--basically everything that would be saved in the
// project file.
PlcProgram Prog;
//-----------------------------------------------------------------------------
// Get a filename with a common dialog box and then save the program to that
// file and then set our default filename to that.
//-----------------------------------------------------------------------------
static BOOL SaveAsDialog(void)
{
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hInstance = Instance;
ofn.lpstrFilter = LDMICRO_PATTERN;
ofn.lpstrDefExt = "ld";
ofn.lpstrFile = CurrentSaveFile;
ofn.nMaxFile = sizeof(CurrentSaveFile);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
if(!GetSaveFileName(&ofn))
return FALSE;
if(!SaveProjectToFile(CurrentSaveFile)) {
Error(_("Couldn't write to '%s'."), CurrentSaveFile);
return FALSE;
} else {
ProgramChangedNotSaved = FALSE;
return TRUE;
}
}
//-----------------------------------------------------------------------------
// Get a filename with a common dialog box and then export the program as
// an ASCII art drawing.
//-----------------------------------------------------------------------------
static void ExportDialog(void)
{
char exportFile[MAX_PATH];
OPENFILENAME ofn;
exportFile[0] = '\0';
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hInstance = Instance;
ofn.lpstrFilter = TXT_PATTERN;
ofn.lpstrFile = exportFile;
ofn.lpstrTitle = _("Export As Text");
ofn.nMaxFile = sizeof(exportFile);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
if(!GetSaveFileName(&ofn))
return;
ExportDrawingAsText(exportFile);
}
//-----------------------------------------------------------------------------
// If we already have a filename, save the program to that. Otherwise same
// as Save As. Returns TRUE if it worked, else returns FALSE.
//-----------------------------------------------------------------------------
static BOOL SaveProgram(void)
{
if(strlen(CurrentSaveFile)) {
if(!SaveProjectToFile(CurrentSaveFile)) {
Error(_("Couldn't write to '%s'."), CurrentSaveFile);
return FALSE;
} else {
ProgramChangedNotSaved = FALSE;
return TRUE;
}
} else {
return SaveAsDialog();
}
}
//-----------------------------------------------------------------------------
// Compile the program to a hex file for the target micro. Get the output
// file name if necessary, then call the micro-specific compile routines.
//-----------------------------------------------------------------------------
static void CompileProgram(BOOL compileAs)
{
//OpenPLC changes
Prog.mcu=&SupportedMcus[ISA_ANSIC]; //define the MCU to ANSI C code
Prog.mcu->whichIsa = ISA_ANSIC;
Prog.cycleTime = 50000; //defines the cycle time to 50ms
sprintf(CurrentCompileFile, _("Core\\arduino\\libraries\\Ladder\\Ladder.cpp"));
if(compileAs || strlen(CurrentCompileFile)==0) {
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hInstance = Instance;
ofn.lpstrTitle = _("Compile To");
if(Prog.mcu && Prog.mcu->whichIsa == ISA_ANSIC) {
ofn.lpstrFilter = C_PATTERN;
ofn.lpstrDefExt = "c";
} else if(Prog.mcu && Prog.mcu->whichIsa == ISA_INTERPRETED) {
ofn.lpstrFilter = INTERPRETED_PATTERN;
ofn.lpstrDefExt = "int";
} else {
ofn.lpstrFilter = HEX_PATTERN;
ofn.lpstrDefExt = "hex";
}
ofn.lpstrFile = CurrentCompileFile;
ofn.nMaxFile = sizeof(CurrentCompileFile);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
if(!GetSaveFileName(&ofn))
return;
// hex output filename is stored in the .ld file
ProgramChangedNotSaved = TRUE;
}
if(!GenerateIntermediateCode()) return;
if(Prog.mcu == NULL) {
Error(_("Must choose a target microcontroller before compiling."));
return;
}
if(UartFunctionUsed() && Prog.mcu->uartNeeds.rxPin == 0) {
Error(_("UART function used but not supported for this micro."));
return;
}
if(PwmFunctionUsed() && Prog.mcu->pwmNeedsPin == 0) {
Error(_("PWM function used but not supported for this micro."));
return;
}
CompileAnsiC(CurrentCompileFile);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
CreateProcess( NULL, // No module name (use command line)
"Core\\Compile.exe", // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
"Core\\", // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ); // Pointer to PROCESS_INFORMATION structure
/*
switch(Prog.mcu->whichIsa) {
case ISA_AVR: CompileAvr(CurrentCompileFile); break;
case ISA_PIC16: CompilePic16(CurrentCompileFile); break;
case ISA_ANSIC: CompileAnsiC(CurrentCompileFile); break;
case ISA_INTERPRETED: CompileInterpreted(CurrentCompileFile); break;
default: oops();
}
*/
// IntDumpListing("t.pl");
}
//-----------------------------------------------------------------------------
// If the program has been modified then give the user the option to save it
// or to cancel the operation they are performing. Return TRUE if they want
// to cancel.
//-----------------------------------------------------------------------------
BOOL CheckSaveUserCancels(void)
{
if(!ProgramChangedNotSaved) {
// no problem
return FALSE;
}
int r = MessageBox(MainWindow,
_("The program has changed since it was last saved.\r\n\r\n"
"Do you want to save the changes?"), "OpenPLC Ladder",
MB_YESNOCANCEL | MB_ICONWARNING);
switch(r) {
case IDYES:
if(SaveProgram())
return FALSE;
else
return TRUE;
case IDNO:
return FALSE;
case IDCANCEL:
return TRUE;
default:
oops();
}
}
//-----------------------------------------------------------------------------
// Load a new program from a file. If it succeeds then set our default filename
// to that, else we end up with an empty file then.
//-----------------------------------------------------------------------------
static void OpenDialog(void)
{
OPENFILENAME ofn;
char tempSaveFile[MAX_PATH] = "";
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hInstance = Instance;
ofn.lpstrFilter = LDMICRO_PATTERN;
ofn.lpstrDefExt = "ld";
ofn.lpstrFile = tempSaveFile;
ofn.nMaxFile = sizeof(tempSaveFile);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
if(!GetOpenFileName(&ofn))
return;
if(!LoadProjectFromFile(tempSaveFile)) {
Error(_("Couldn't open '%s'."), tempSaveFile);
CurrentSaveFile[0] = '\0';
} else {
ProgramChangedNotSaved = FALSE;
strcpy(CurrentSaveFile, tempSaveFile);
UndoFlush();
}
GenerateIoListDontLoseSelection();
RefreshScrollbars();
UpdateMainWindowTitleBar();
}
//-----------------------------------------------------------------------------
// Housekeeping required when the program changes: mark the program as
// changed so that we ask if user wants to save before exiting, and update
// the I/O list.
//-----------------------------------------------------------------------------
void ProgramChanged(void)
{
ProgramChangedNotSaved = TRUE;
GenerateIoListDontLoseSelection();
RefreshScrollbars();
}
#define CHANGING_PROGRAM(x) { \
UndoRemember(); \
x; \
ProgramChanged(); \
}
//-----------------------------------------------------------------------------
// Hook that we install when the user starts dragging the `splitter,' in case
// they drag it out of the narrow area of the drawn splitter bar. Resize
// the listview in response to mouse move, and unhook ourselves when they
// release the mouse button.
//-----------------------------------------------------------------------------
static LRESULT CALLBACK MouseHook(int code, WPARAM wParam, LPARAM lParam)
{
switch(code) {
case HC_ACTION: {
MSLLHOOKSTRUCT *mhs = (MSLLHOOKSTRUCT *)lParam;
switch(wParam) {
case WM_MOUSEMOVE: {
int dy = MouseY - mhs->pt.y;
IoListHeight += dy;
if(IoListHeight < 50) IoListHeight = 50;
MouseY = mhs->pt.y;
MainWindowResized();
break;
}
case WM_LBUTTONUP:
UnhookWindowsHookEx(MouseHookHandle);
break;
}
break;
}
}
return CallNextHookEx(MouseHookHandle, code, wParam, lParam);
}
//-----------------------------------------------------------------------------
// Handle a selection from the menu bar of the main window.
//-----------------------------------------------------------------------------
static void ProcessMenu(int code)
{
if(code >= MNU_PROCESSOR_0 && code < MNU_PROCESSOR_0+NUM_SUPPORTED_MCUS) {
strcpy(CurrentCompileFile, "");
Prog.mcu = &SupportedMcus[code - MNU_PROCESSOR_0];
RefreshControlsToSettings();
return;
}
if(code == MNU_PROCESSOR_0+NUM_SUPPORTED_MCUS) {
Prog.mcu = NULL;
strcpy(CurrentCompileFile, "");
RefreshControlsToSettings();
return;
}
switch(code) {
case MNU_NEW:
if(CheckSaveUserCancels()) break;
NewProgram();
strcpy(CurrentSaveFile, "");
strcpy(CurrentCompileFile, "");
GenerateIoListDontLoseSelection();
RefreshScrollbars();
UpdateMainWindowTitleBar();
break;
case MNU_OPEN:
if(CheckSaveUserCancels()) break;
OpenDialog();
break;
case MNU_SAVE:
SaveProgram();
UpdateMainWindowTitleBar();
break;
case MNU_SAVE_AS:
SaveAsDialog();
UpdateMainWindowTitleBar();
break;
case MNU_EXPORT:
ExportDialog();
break;
case MNU_EXIT:
if(CheckSaveUserCancels()) break;
PostQuitMessage(0);
break;
case MNU_INSERT_COMMENT:
CHANGING_PROGRAM(AddComment(_("--add comment here--")));
break;
case MNU_INSERT_CONTACTS:
CHANGING_PROGRAM(AddContact());
break;
case MNU_INSERT_COIL:
CHANGING_PROGRAM(AddCoil());
break;
case MNU_INSERT_TON:
CHANGING_PROGRAM(AddTimer(ELEM_TON));
break;
case MNU_INSERT_TOF:
CHANGING_PROGRAM(AddTimer(ELEM_TOF));
break;
case MNU_INSERT_RTO:
CHANGING_PROGRAM(AddTimer(ELEM_RTO));
break;
case MNU_INSERT_CTU:
CHANGING_PROGRAM(AddCounter(ELEM_CTU));
break;
case MNU_INSERT_CTD:
CHANGING_PROGRAM(AddCounter(ELEM_CTD));
break;
case MNU_INSERT_CTC:
CHANGING_PROGRAM(AddCounter(ELEM_CTC));
break;
case MNU_INSERT_RES:
CHANGING_PROGRAM(AddReset());
break;
case MNU_INSERT_OPEN:
CHANGING_PROGRAM(AddEmpty(ELEM_OPEN));
break;
case MNU_INSERT_SHORT:
CHANGING_PROGRAM(AddEmpty(ELEM_SHORT));
break;
case MNU_INSERT_MASTER_RLY:
CHANGING_PROGRAM(AddMasterRelay());
break;
case MNU_INSERT_SHIFT_REG:
CHANGING_PROGRAM(AddShiftRegister());
break;
case MNU_INSERT_LUT:
CHANGING_PROGRAM(AddLookUpTable());
break;
case MNU_INSERT_PWL:
CHANGING_PROGRAM(AddPiecewiseLinear());
break;
case MNU_INSERT_FMTD_STR:
CHANGING_PROGRAM(AddFormattedString());
break;
case MNU_INSERT_OSR:
CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_RISING));
break;
case MNU_INSERT_OSF:
CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_FALLING));
break;
case MNU_INSERT_MOV:
CHANGING_PROGRAM(AddMove());
break;
case MNU_INSERT_SET_PWM:
CHANGING_PROGRAM(AddSetPwm());
break;
case MNU_INSERT_READ_ADC:
CHANGING_PROGRAM(AddReadAdc());
break;
case MNU_INSERT_UART_SEND:
CHANGING_PROGRAM(AddUart(ELEM_UART_SEND));
break;
case MNU_INSERT_UART_RECV:
CHANGING_PROGRAM(AddUart(ELEM_UART_RECV));
break;
case MNU_INSERT_PERSIST:
CHANGING_PROGRAM(AddPersist());
break;
{
int elem;
case MNU_INSERT_ADD: elem = ELEM_ADD; goto math;
case MNU_INSERT_SUB: elem = ELEM_SUB; goto math;
case MNU_INSERT_MUL: elem = ELEM_MUL; goto math;
case MNU_INSERT_DIV: elem = ELEM_DIV; goto math;
math:
CHANGING_PROGRAM(AddMath(elem));
break;
}
{
int elem;
case MNU_INSERT_EQU: elem = ELEM_EQU; goto cmp;
case MNU_INSERT_NEQ: elem = ELEM_NEQ; goto cmp;
case MNU_INSERT_GRT: elem = ELEM_GRT; goto cmp;
case MNU_INSERT_GEQ: elem = ELEM_GEQ; goto cmp;
case MNU_INSERT_LES: elem = ELEM_LES; goto cmp;
case MNU_INSERT_LEQ: elem = ELEM_LEQ; goto cmp;
cmp:
CHANGING_PROGRAM(AddCmp(elem));
break;
}
case MNU_MAKE_NORMAL:
CHANGING_PROGRAM(MakeNormalSelected());
break;
case MNU_NEGATE:
CHANGING_PROGRAM(NegateSelected());
break;
case MNU_MAKE_SET_ONLY:
CHANGING_PROGRAM(MakeSetOnlySelected());
break;
case MNU_MAKE_RESET_ONLY:
CHANGING_PROGRAM(MakeResetOnlySelected());
break;
case MNU_UNDO:
UndoUndo();
break;
case MNU_REDO:
UndoRedo();
break;
case MNU_INSERT_RUNG_BEFORE:
CHANGING_PROGRAM(InsertRung(FALSE));
break;
case MNU_INSERT_RUNG_AFTER:
CHANGING_PROGRAM(InsertRung(TRUE));
break;
case MNU_DELETE_RUNG:
CHANGING_PROGRAM(DeleteSelectedRung());
break;
case MNU_PUSH_RUNG_UP:
CHANGING_PROGRAM(PushRungUp());
break;
case MNU_PUSH_RUNG_DOWN:
CHANGING_PROGRAM(PushRungDown());
break;
case MNU_DELETE_ELEMENT:
CHANGING_PROGRAM(DeleteSelectedFromProgram());
break;
case MNU_MCU_SETTINGS:
CHANGING_PROGRAM(ShowConfDialog());
break;
case MNU_SIMULATION_MODE:
ToggleSimulationMode();
break;
case MNU_START_SIMULATION:
StartSimulation();
break;
case MNU_STOP_SIMULATION:
StopSimulation();
break;
case MNU_SINGLE_CYCLE:
SimulateOneCycle(TRUE);
break;
case MNU_COMPILE:
CompileProgram(FALSE);
break;
case MNU_COMPILE_AS:
CompileProgram(TRUE);
break;
case MNU_MANUAL:
ShowHelpDialog(FALSE);
break;
case MNU_ABOUT:
ShowHelpDialog(TRUE);
break;
}
}
//-----------------------------------------------------------------------------
// WndProc for MainWindow.
//-----------------------------------------------------------------------------
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_ERASEBKGND:
break;
case WM_SETFOCUS:
InvalidateRect(MainWindow, NULL, FALSE);
break;
case WM_PAINT: {
PAINTSTRUCT ps;
Hdc = BeginPaint(hwnd, &ps);
// This draws the schematic.
PaintWindow();
RECT r;
// Fill around the scroll bars
if(NeedHoriz) {
r.top = IoListTop - ScrollHeight - 2;
r.bottom = IoListTop - 2;
FillRect(Hdc, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
}
GetClientRect(MainWindow, &r);
r.left = r.right - ScrollWidth - 2;
FillRect(Hdc, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
// Draw the splitter thing to grab to resize the I/O listview.
GetClientRect(MainWindow, &r);
r.top = IoListTop - 2;
r.bottom = IoListTop;
FillRect(Hdc, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
r.top = IoListTop - 2;
r.bottom = IoListTop - 1;
FillRect(Hdc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
r.top = IoListTop;
r.bottom = IoListTop + 1;
FillRect(Hdc, &r, (HBRUSH)GetStockObject(DKGRAY_BRUSH));
EndPaint(hwnd, &ps);
return 1;
}
case WM_KEYDOWN: {
if(wParam == 'M') {
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
ToggleSimulationMode();
break;
}
} else if(wParam == VK_TAB) {
SetFocus(IoList);
BlinkCursor(0, 0, 0, 0);
break;
} else if(wParam == VK_F1) {
ShowHelpDialog(FALSE);
break;
}
if(InSimulationMode) {
switch(wParam) {
case ' ':
SimulateOneCycle(TRUE);
break;
case 'R':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
StartSimulation();
break;
case 'H':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
StopSimulation();
break;
case VK_DOWN:
if(ScrollYOffset < ScrollYOffsetMax)
ScrollYOffset++;
RefreshScrollbars();
InvalidateRect(MainWindow, NULL, FALSE);
break;
case VK_UP:
if(ScrollYOffset > 0)
ScrollYOffset--;
RefreshScrollbars();
InvalidateRect(MainWindow, NULL, FALSE);
break;
case VK_LEFT:
ScrollXOffset -= FONT_WIDTH;
if(ScrollXOffset < 0) ScrollXOffset = 0;
RefreshScrollbars();
InvalidateRect(MainWindow, NULL, FALSE);
break;
case VK_RIGHT:
ScrollXOffset += FONT_WIDTH;
if(ScrollXOffset >= ScrollXOffsetMax)
ScrollXOffset = ScrollXOffsetMax;
RefreshScrollbars();
InvalidateRect(MainWindow, NULL, FALSE);
break;
case VK_RETURN:
case VK_ESCAPE:
ToggleSimulationMode();
break;
}
break;
}
switch(wParam) {
case VK_F5:
CompileProgram(FALSE);
break;
case VK_UP:
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(PushRungUp());
} else {
MoveCursorKeyboard(wParam);
}
break;
case VK_DOWN:
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(PushRungDown());
} else {
MoveCursorKeyboard(wParam);
}
break;
case VK_RIGHT:
case VK_LEFT:
MoveCursorKeyboard(wParam);
break;
case VK_RETURN:
CHANGING_PROGRAM(EditSelectedElement());
break;
case VK_DELETE:
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(DeleteSelectedRung());
} else {
CHANGING_PROGRAM(DeleteSelectedFromProgram());
}
break;
case VK_OEM_1:
CHANGING_PROGRAM(AddComment(_("--add comment here--")));
break;
case 'C':
CHANGING_PROGRAM(AddContact());
break;
// TODO: rather country-specific here
case VK_OEM_2:
CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_RISING));
break;
case VK_OEM_5:
CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_FALLING));
break;
case 'L':
CHANGING_PROGRAM(AddCoil());
break;
case 'R':
CHANGING_PROGRAM(MakeResetOnlySelected());
break;
case 'E':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
ExportDialog();
} else {
CHANGING_PROGRAM(AddReset());
}
break;
case 'S':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
SaveProgram();
UpdateMainWindowTitleBar();
} else {
CHANGING_PROGRAM(MakeSetOnlySelected());
}
break;
case 'N':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
if(CheckSaveUserCancels()) break;
if(!ProgramChangedNotSaved) {
int r = MessageBox(MainWindow,
_("Start new program?"),
"LDmicro", MB_YESNO | MB_DEFBUTTON2 |
MB_ICONQUESTION);
if(r == IDNO) break;
}
NewProgram();
strcpy(CurrentSaveFile, "");
strcpy(CurrentCompileFile, "");
GenerateIoListDontLoseSelection();
RefreshScrollbars();
UpdateMainWindowTitleBar();
} else {
CHANGING_PROGRAM(NegateSelected());
}
break;
case 'A':
CHANGING_PROGRAM(MakeNormalSelected());
break;
case 'T':
CHANGING_PROGRAM(AddTimer(ELEM_RTO));
break;
case 'O':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
if(CheckSaveUserCancels()) break;
OpenDialog();
} else {
CHANGING_PROGRAM(AddTimer(ELEM_TON));
}
break;
case 'F':
CHANGING_PROGRAM(AddTimer(ELEM_TOF));
break;
case 'U':
CHANGING_PROGRAM(AddCounter(ELEM_CTU));
break;
case 'I':
CHANGING_PROGRAM(AddCounter(ELEM_CTD));
break;
case 'J':
CHANGING_PROGRAM(AddCounter(ELEM_CTC));
break;
case 'M':
CHANGING_PROGRAM(AddMove());
break;
case 'P':
CHANGING_PROGRAM(AddReadAdc());
break;
case VK_OEM_PLUS:
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(AddMath(ELEM_ADD));
} else {
CHANGING_PROGRAM(AddCmp(ELEM_EQU));
}
break;
case VK_OEM_MINUS:
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
} else {
CHANGING_PROGRAM(AddMath(ELEM_SUB));
}
break;
case '8':
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(AddMath(ELEM_MUL));
}
break;
case 'D':
CHANGING_PROGRAM(AddMath(ELEM_DIV));
break;
case VK_OEM_PERIOD:
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(AddCmp(ELEM_GRT));
} else {
CHANGING_PROGRAM(AddCmp(ELEM_GEQ));
}
break;
case VK_OEM_COMMA:
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(AddCmp(ELEM_LES));
} else {
CHANGING_PROGRAM(AddCmp(ELEM_LEQ));
}
break;
case 'V':
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(InsertRung(TRUE));
}
break;
case '6':
if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
CHANGING_PROGRAM(InsertRung(FALSE));
}
break;
case 'Z':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
UndoUndo();
}
break;
case 'Y':
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
UndoRedo();
}
break;
default:
break;
}
if(wParam != VK_SHIFT && wParam != VK_CONTROL) {
InvalidateRect(MainWindow, NULL, FALSE);
}
break;
}
case WM_LBUTTONDBLCLK: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if(InSimulationMode) {
EditElementMouseDoubleclick(x, y);
} else {
CHANGING_PROGRAM(EditElementMouseDoubleclick(x, y));
}
InvalidateRect(MainWindow, NULL, FALSE);
break;
}
case WM_LBUTTONDOWN: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if((y > (IoListTop - 9)) && (y < (IoListTop + 3))) {
POINT pt;
pt.x = x; pt.y = y;
ClientToScreen(MainWindow, &pt);
MouseY = pt.y;
MouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL,
(HOOKPROC)MouseHook, Instance, 0);
}
if(!InSimulationMode) MoveCursorMouseClick(x, y);
SetFocus(MainWindow);
InvalidateRect(MainWindow, NULL, FALSE);
break;
}
case WM_MOUSEMOVE: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if((y > (IoListTop - 9)) && (y < (IoListTop + 3))) {
SetCursor(LoadCursor(NULL, IDC_SIZENS));
} else {
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
break;
}
case WM_MOUSEWHEEL: {
if((GET_WHEEL_DELTA_WPARAM(wParam)) > 0) {
VscrollProc(SB_LINEUP);
} else {
VscrollProc(SB_LINEDOWN);
}
break;
}
case WM_SIZE:
MainWindowResized();
break;
case WM_NOTIFY: {
NMHDR *h = (NMHDR *)lParam;
if(h->hwndFrom == IoList) {
IoListProc(h);
}
return 0;
}
case WM_VSCROLL:
VscrollProc(wParam);
break;
case WM_HSCROLL:
HscrollProc(wParam);
break;
case WM_COMMAND:
ProcessMenu(LOWORD(wParam));
InvalidateRect(MainWindow, NULL, FALSE);
break;
case WM_CLOSE:
case WM_DESTROY:
if(CheckSaveUserCancels()) break;
PostQuitMessage(0);
return 1;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 1;
}
//-----------------------------------------------------------------------------
// Create our window class; nothing exciting.
//-----------------------------------------------------------------------------
static BOOL MakeWindowClass()
{
WNDCLASSEX wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC |
CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.hInstance = Instance;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = "LDmicro";
wc.lpszMenuName = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
IMAGE_ICON, 32, 32, 0);
wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
IMAGE_ICON, 16, 16, 0);
return RegisterClassEx(&wc);
}
//-----------------------------------------------------------------------------
// Entry point into the program.
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, INT nCmdShow)
{
Instance = hInstance;
MainHeap = HeapCreate(0, 1024*64, 0);
MakeWindowClass();
MakeDialogBoxClass();
HMENU top = MakeMainWindowMenus();
MainWindow = CreateWindowEx(0, "LDmicro", "",
WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX,
10, 10, 800, 600, NULL, top, Instance, NULL);
ThawWindowPos(MainWindow);
IoListHeight = 100;
ThawDWORD(IoListHeight);
InitCommonControls();
InitForDrawing();
MakeMainWindowControls();
MainWindowResized();
NewProgram();
strcpy(CurrentSaveFile, "");
// Check if we're running in non-interactive mode; in that case we should
// load the file, compile, and exit.
while(isspace(*lpCmdLine)) {
lpCmdLine++;
}
if(memcmp(lpCmdLine, "/c", 2)==0) {
RunningInBatchMode = TRUE;
char *err =
"Bad command line arguments: run 'ldmicro /c src.ld dest.hex'";
char *source = lpCmdLine + 2;
while(isspace(*source)) {
source++;
}
if(*source == '\0') { Error(err); exit(-1); }
char *dest = source;
while(!isspace(*dest) && *dest) {
dest++;
}
if(*dest == '\0') { Error(err); exit(-1); }
*dest = '\0'; dest++;
while(isspace(*dest)) {
dest++;
}
if(*dest == '\0') { Error(err); exit(-1); }
if(!LoadProjectFromFile(source)) {
Error("Couldn't open '%s', running non-interactively.", source);
exit(-1);
}
strcpy(CurrentCompileFile, dest);
GenerateIoList(-1);
CompileProgram(FALSE);
exit(0);
}
// We are running interactively, or we would already have exited. We
// can therefore show the window now, and otherwise set up the GUI.
ShowWindow(MainWindow, SW_SHOW);
SetTimer(MainWindow, TIMER_BLINK_CURSOR, 800, BlinkCursor);
if(strlen(lpCmdLine) > 0) {
char line[MAX_PATH];
if(*lpCmdLine == '"') {
strcpy(line, lpCmdLine+1);
} else {
strcpy(line, lpCmdLine);
}
if(strchr(line, '"')) *strchr(line, '"') = '\0';
char *s;
GetFullPathName(line, sizeof(CurrentSaveFile), CurrentSaveFile, &s);
if(!LoadProjectFromFile(CurrentSaveFile)) {
NewProgram();
Error(_("Couldn't open '%s'."), CurrentSaveFile);
CurrentSaveFile[0] = '\0';
}
UndoFlush();
}
GenerateIoListDontLoseSelection();
RefreshScrollbars();
UpdateMainWindowTitleBar();
MSG msg;
DWORD ret;
while(ret = GetMessage(&msg, NULL, 0, 0)) {
if(msg.hwnd == IoList && msg.message == WM_KEYDOWN) {
if(msg.wParam == VK_TAB) {
SetFocus(MainWindow);
continue;
}
}
if(msg.message == WM_KEYDOWN && msg.wParam != VK_UP &&
msg.wParam != VK_DOWN && msg.wParam != VK_RETURN && msg.wParam
!= VK_SHIFT)
{
if(msg.hwnd == IoList) {
msg.hwnd = MainWindow;
SetFocus(MainWindow);
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
FreezeWindowPos(MainWindow);
FreezeDWORD(IoListHeight);
return 0;
}