mirror of
https://github.com/thiagoralves/OpenPLC-Ladder-Editor.git
synced 2025-05-08 18:54:14 +08:00
1177 lines
36 KiB
C++
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;
|
|
}
|