diff options
author | Xavier Leroy <xavier.leroy@inria.fr> | 2001-11-06 12:36:24 +0000 |
---|---|---|
committer | Xavier Leroy <xavier.leroy@inria.fr> | 2001-11-06 12:36:24 +0000 |
commit | 2f391a02b894fcf9d2580a04ab9ac66ed8040862 (patch) | |
tree | 565d50a408f3cc517a0c94843181fe76013f7b70 /win32caml | |
parent | 839a289aa108482e203053488863d622985bc8b0 (diff) |
GUI Win32 pour le toplevel (J.Navia)
git-svn-id: http://caml.inria.fr/svn/ocaml/trunk@3988 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02
Diffstat (limited to 'win32caml')
-rw-r--r-- | win32caml/Makefile | 22 | ||||
-rw-r--r-- | win32caml/inria.h | 112 | ||||
-rw-r--r-- | win32caml/inriares.h | 47 | ||||
-rw-r--r-- | win32caml/libgraph.h | 107 | ||||
-rw-r--r-- | win32caml/menu.c | 508 | ||||
-rw-r--r-- | win32caml/ocaml.c | 791 | ||||
-rw-r--r-- | win32caml/ocaml.ico | bin | 0 -> 766 bytes | |||
-rw-r--r-- | win32caml/ocaml.rc | 112 | ||||
-rw-r--r-- | win32caml/startocaml.c | 317 |
9 files changed, 2016 insertions, 0 deletions
diff --git a/win32caml/Makefile b/win32caml/Makefile new file mode 100644 index 000000000..efb548d2b --- /dev/null +++ b/win32caml/Makefile @@ -0,0 +1,22 @@ +CC=cl /nologo +CFLAGS=/MT -O /Zi + +OBJS=startocaml.obj ocaml.res ocaml.obj menu.obj + +LIBS=kernel32.lib advapi32.lib gdi32.lib user32.lib comdlg32.lib comctl32.lib + +ocaml.exe: $(OBJS) + $(CC) $(CFLAGS) -o ocaml.exe $(OBJS) $(LIBS) + +ocaml.res: ocaml.rc ocaml.ico + rc ocaml.rc + +$(OBJS): inria.h inriares.h + +clean: + rm -f ocaml.exe ocaml.res *.obj + +.SUFFIXES: .c .obj + +.c.obj: + $(CC) $(CFLAGS) -c $*.c diff --git a/win32caml/inria.h b/win32caml/inria.h new file mode 100644 index 000000000..8ac701e41 --- /dev/null +++ b/win32caml/inria.h @@ -0,0 +1,112 @@ +/***********************************************************************/ +/* */ +/* Objective Caml */ +/* */ +/* Developed by Jacob Navia. */ +/* Copyright 2001 Institut National de Recherche en Informatique et */ +/* en Automatique. All rights reserved. This file is distributed */ +/* under the terms of the GNU Library General Public License. */ +/* */ +/***********************************************************************/ + +/* $Id$ */ + +/*------------------------------------------------------------------------ + Module: D:\lcc\inria\inria.h + Author: Jacob + Project: + State: + Creation Date: June 2001 + Description: The user interface works as follows: + 1: At startup it will look for the path to the + ocaml interpreter in the registry using the + key HKEY_CURRENT_USER\SOFTWARE\ocaml. If not + found will prompt the user. + 2: It will start the ocaml interpreter with + its standard output and standard input + connected to two pipes in a dedicated thread. + 3: It will open a window containing an edit + field. The output from the interpreter will be + shown in the edit field, and the input of the + user in the edit field will be sent to the + interpreter when the user types return. + 4: Line editing is provided by moving to the + desired line with the arrows, then pressing + return; If we aren't in the last input line, + the input will be copied to the last line and + sent to the interpreter. + 5: The GUI ensures that when we exit the ocaml + interpreter is stopped by sending the + character string "#quit;;\nCtrl-Z" + 6: A history of all lines sent to the interpreter + is maintained in a simple linked list. The + History dialog box shows that, and allows the + user to choose a given input line. + 7: Memory limits. The edit buffer can be of an + arbitrary length, i.e. maybe 7-8MB or more, + there are no fixed limits. The History list + will always grow too, so memory consumption + could be "high" after several days of + uninterrupted typing at the keyboard. For that + cases it is recommended to stop the GUI and + get some sleep... + 9: The GUI will start a timer, looking 4 times a + second if the interpreter has written + something in the pipe. This is enough for most + applications. +------------------------------------------------------------------------*/ + +#include <windows.h> + +// In this structure should go eventually all global variables scattered +// through the program. +typedef struct _programParams { + HFONT hFont; // The handle of the current font + COLORREF TextColor; // The text color + char CurrentWorkingDir[MAX_PATH];// The current directory +} PROGRAM_PARAMS; + +//**************** Global variables *********************** +extern PROGRAM_PARAMS ProgramParams; + +extern COLORREF BackColor; // The background color +extern HBRUSH BackgroundBrush; // A brush built with the background color +extern char LibDir[]; // The lib directory +extern char OcamlPath[]; // The Path to ocaml.exe +extern HANDLE hInst; // The instance handle for this application +extern HWND hwndSession; // The current session window handle +extern LOGFONT CurrentFont; // The current font characteristics +extern HWND hwndMain,hwndMDIClient; // Window handles of frame and mdi window + +// ***************** Function prototypes ****************** +int WriteToPipe(char *data); // Writes to the pipe +int ReadFromPipe(char *data,int len);// Reads from the pipe +int AskYesOrNo(char *msg); //Ditto! +int BrowseForFile(char *fname,char *path); +void GotoEOF(void); // Positions the cursor at the end of the text +void ShowDbgMsg(char *msg); // Shows an error message +void HandleCommand(HWND hwnd, WPARAM wParam,LPARAM lParam); +int GetOcamlPath(void); // Finds where ocaml.exe is +void ForceRepaint(void); // Ditto. +void AddLineToControl(char *buf); +char *GetHistoryLine(int n); // Gets the nth history line base 1. +int StartOcaml(void); +// **************** User defined window messages ************* +#define WM_NEWLINE (WM_USER+6000) +#define WM_TIMERTICK (WM_USER+6001) +#define WM_QUITOCAML (WM_USER+6002) +// ********************** Structures *********************** +typedef struct tagPosition { + int line; + int col; +} POSITION; + +// Simple linked list for holding the history lines +typedef struct tagHistory { + struct tagHistory *Next; + char *Text; +} HISTORYLINE; + +extern void *SafeMalloc(int); +extern HISTORYLINE *History; // The root of the history lines + diff --git a/win32caml/inriares.h b/win32caml/inriares.h new file mode 100644 index 000000000..45d835211 --- /dev/null +++ b/win32caml/inriares.h @@ -0,0 +1,47 @@ +/* Weditres generated include file. Do NOT edit */ +#define IDD_ABOUT 100 +#define IDM_NEW 200 +#define IDM_OPEN 210 +#define IDM_SAVE 220 +#define IDM_SAVEAS 230 +#define IDM_CLOSE 240 +#define IDM_PRINT 250 +#define IDM_PRINTSU 260 +#define IDM_PRINTPRE 265 +#define IDM_PAGESETUP 267 +#define IDM_EXIT 270 +#define IDM_HISTORY 281 +#define IDM_GC 282 +#define IDD_HISTORY 300 +#define IDLIST 301 +#define IDM_EDITUNDO 310 +#define IDM_EDITCUT 320 +#define IDM_EDITCOPY 330 +#define IDM_EDITPASTE 340 +#define IDM_EDITCLEAR 350 +#define IDM_EDITDELETE 360 +#define IDM_EDITREPLACE 370 +#define IDM_EDITREDO 380 +#define IDM_WINDOWTILE 410 +#define IDM_WINDOWCASCADE 420 +#define IDM_WINDOWICONS 430 +#define IDM_WINDOWCLOSEALL 440 +#define IDM_PROPERTIES 450 +#define IDM_ABOUT 500 +#define IDM_HELP 510 +#define IDMAINMENU 600 +#define IDM_FIND 700 +#define IDAPPLICON 710 +#define IDI_CHILDICON 800 +#define IDAPPLCURSOR 810 +#define OCAML_ICON 1000 +#define IDS_FILEMENU 2000 +#define IDS_HELPMENU 2010 +#define IDS_SYSMENU 2030 +#define IDM_STATUSBAR 3000 +#define IDM_WINDOWCHILD 3010 +#define ID_TOOLBAR 5000 +#define IDACCEL 10000 +#define IDM_FONT 40002 +#define IDM_COLORTEXT 40004 +#define IDM_BACKCOLOR 40005 diff --git a/win32caml/libgraph.h b/win32caml/libgraph.h new file mode 100644 index 000000000..1248d3037 --- /dev/null +++ b/win32caml/libgraph.h @@ -0,0 +1,107 @@ +/***********************************************************************/ +/* */ +/* Objective Caml */ +/* */ +/* Jacob Navia, after Xavier Leroy */ +/* */ +/* Copyright 2001 Institut National de Recherche en Informatique et */ +/* en Automatique. All rights reserved. This file is distributed */ +/* under the terms of the GNU Library General Public License. */ +/* */ +/***********************************************************************/ + +/* $Id$ */ + +#include <stdio.h> +#include <windows.h> + +struct canvas { + int w, h; /* Dimensions of the drawable */ + HWND win; /* The drawable itself */ + HDC gc; /* The associated graphics context */ +}; + +extern HWND grdisplay; /* The display connection */ +//extern int grscreen; /* The screen number */ +//extern Colormap grcolormap; /* The color map */ +//extern struct canvas grwindow; /* The graphics window */ +//extern struct canvas grbstore; /* The pixmap used for backing store */ +//extern int grwhite, grblack; /* Black and white pixels for X */ +//extern int grbackground; /* Background color for X +// (used for CAML color -1) */ +extern COLORREF grbackground; +extern BOOL grdisplay_mode; /* Display-mode flag */ +extern BOOL grremember_mode; /* Remember-mode flag */ +extern int grx, gry; /* Coordinates of the current point */ +extern int grcolor; /* Current *CAML* drawing color (can be -1) */ +extern HFONT * grfont; /* Current font */ + +extern BOOL direct_rgb; +extern int byte_order; +extern int bitmap_unit; +extern int bits_per_pixel; + +#define Wcvt(y) (grwindow.height - 1 - (y)) +#define Bcvt(y) (grwindow.height - 1 - (y)) +#define WtoB(y) ((y) + WindowRect.bottom - grwindow.h) +//#define BtoW(y) ((y) + WindowRect.bottom - grbstore.h) + +#define DEFAULT_SCREEN_WIDTH 1024 +#define DEFAULT_SCREEN_HEIGHT 768 +#define BORDER_WIDTH 2 +#define WINDOW_NAME "Caml graphics" +#define ICON_NAME "Caml graphics" +#define DEFAULT_EVENT_MASK \ + (ExposureMask | KeyPressMask | StructureNotifyMask) +#define DEFAULT_FONT "fixed" +#define SIZE_QUEUE 256 + +/* To handle events asynchronously */ +#ifdef HAS_ASYNC_IO +#define USE_ASYNC_IO +#define EVENT_SIGNAL SIGIO +#else +#ifdef HAS_SETITIMER +#define USE_INTERVAL_TIMER +#define EVENT_SIGNAL SIGALRM +#else +#define USE_ALARM +#define EVENT_SIGNAL SIGALRM +#endif +#endif + +void gr_fail(char *fmt, char *arg); +void gr_check_open(void); +unsigned long gr_pixel_rgb(int rgb); +int gr_rgb_pixel(long unsigned int pixel); +void gr_enqueue_char(unsigned char c); +void gr_init_color_cache(void); + +// Windows specific definitions +extern RECT WindowRect; +extern int grCurrentColor; + +typedef struct tagWindow { + HDC gc; + HDC gcBitmap; + HWND hwnd; + HBRUSH CurrentBrush; + HPEN CurrentPen; + DWORD CurrentColor; + int width; + int height; + int grx; + int gry; + HBITMAP hBitmap; + HFONT CurrentFont; + int CurrentFontSize; + HDC tempDC; // For image operations; +} GR_WINDOW; + +extern GR_WINDOW grwindow; +HFONT CreationFont(char *name); +extern int MouseLbuttonDown,MouseMbuttonDown,MouseRbuttonDown; +extern HANDLE EventHandle; +extern int InspectMessages; +extern MSG msg; + diff --git a/win32caml/menu.c b/win32caml/menu.c new file mode 100644 index 000000000..d0ffbc7e0 --- /dev/null +++ b/win32caml/menu.c @@ -0,0 +1,508 @@ +/***********************************************************************/ +/* */ +/* Objective Caml */ +/* */ +/* Developed by Jacob Navia. */ +/* Copyright 2001 Institut National de Recherche en Informatique et */ +/* en Automatique. All rights reserved. This file is distributed */ +/* under the terms of the GNU Library General Public License. */ +/* */ +/***********************************************************************/ + +/* $Id$ */ + +#include <stdio.h> +#include <windows.h> +#include "inria.h" +#include "inriares.h" + +LOGFONT CurrentFont; +int CurrentFontFamily = (FIXED_PITCH | FF_MODERN); +int CurrentFontStyle; +char CurrentFontName[64] = "Courier"; +/*------------------------------------------------------------------------ + Procedure: OpenMlFile ID:1 + Purpose: Opens a file, either a source file (*.ml) or an *.cmo + file. + Input: A buffer where the name will be stored, and its + length + Output: The user's choice will be stored in the buffer. + Errors: None +------------------------------------------------------------------------*/ +int OpenMlFile(char *fname,int lenbuf) +{ + OPENFILENAME ofn; + int r; + char *p,defext[5],tmp[512]; + + memset(&ofn,0,sizeof(OPENFILENAME)); + memset(tmp,0,sizeof(tmp)); + fname[0] = 0; + strcpy(tmp,"ocaml sources|*.ml|bytecode object files|*.cmo|All files|*.*"); + p = tmp; + while (*p) { + if (*p == '|') + *p = 0; + p++; + } + strcpy(defext,"ml"); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwndMain; + ofn.lpstrFilter = tmp; + ofn.nFilterIndex = 1; + ofn.hInstance = hInst; + ofn.lpstrFile = fname; + ofn.lpstrTitle = "Open file"; + ofn.lpstrInitialDir = LibDir; + ofn.nMaxFile = lenbuf; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_LONGNAMES | + OFN_HIDEREADONLY |OFN_EXPLORER; + r = GetOpenFileName(&ofn); + if (r == 0) + return 0; + else return 1; +} +/*------------------------------------------------------------------------ + Procedure: GetSaveName ID:1 + Purpose: Get a name to save the current session (Save as menu + item) + Input: A buffer where the name of the file will be stored, + and its length + Output: The name of the file choosen by the user will be + stored in the buffer + Errors: none +------------------------------------------------------------------------*/ +int GetSaveName(char *fname,int lenbuf) +{ + OPENFILENAME ofn; + int r; + char *p,defext[5],tmp[512]; + + memset(&ofn,0,sizeof(OPENFILENAME)); + memset(tmp,0,sizeof(tmp)); + fname[0] = 0; + strcpy(tmp,"Text files|*.txt"); + p = tmp; + while (*p) { + if (*p == '|') + *p = 0; + p++; + } + strcpy(defext,"txt"); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwndMain; + ofn.lpstrFilter = tmp; + ofn.nFilterIndex = 1; + ofn.hInstance = hInst; + ofn.lpstrFile = fname; + ofn.lpstrTitle = "Save as"; + ofn.lpstrInitialDir = LibDir; + ofn.nMaxFile = lenbuf; + ofn.Flags = OFN_NOCHANGEDIR | OFN_LONGNAMES | + OFN_HIDEREADONLY |OFN_EXPLORER; + r = GetSaveFileName(&ofn); + if (r == 0) + return 0; + else return 1; +} +/*------------------------------------------------------------------------ + Procedure: BrowseForFile ID:1 + Purpose: Let's the user browse for a certain kind of file. + Currently this is only used when browsing for + ocaml.exe. + Input: The name of the file to browse for, and the path + where the user's choice will be stored. + Output: 1 if user choosed a path, zero otherwise + Errors: None +------------------------------------------------------------------------*/ +int BrowseForFile(char *fname,char *path) +{ + OPENFILENAME ofn; + char *p,tmp[512],browsefor[512]; + int r; + + memset(tmp,0,sizeof(tmp)); + strncpy(tmp,fname,sizeof(tmp)-1); + p = tmp; + while (*p) { + if (*p == '|') + *p = 0; + p++; + } + memset(&ofn,0,sizeof(OPENFILENAME)); + ofn.lpstrFilter = tmp; + ofn.nFilterIndex = 1; + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwndMain; + ofn.hInstance = hInst; + ofn.lpstrFilter = tmp; + ofn.lpstrFile = path; + wsprintf(browsefor,"Open %s",fname); + ofn.lpstrTitle = browsefor; + ofn.lpstrInitialDir = "c:\\"; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_LONGNAMES | + OFN_HIDEREADONLY |OFN_EXPLORER; + r = GetOpenFileName(&ofn); + if (r == 0) + return 0; + else return 1; +} + +/*------------------------------------------------------------------------ + Procedure: CallChangeFont ID:1 + Purpose: Calls the standard windows font change dialog. If the + user validates a font, it will destroy the current + font, and recreate a new font with the given + parameters. + Input: The calling window handle + Output: Zero if the user cancelled, 1 otherwise. + Errors: None +------------------------------------------------------------------------*/ +static int CallChangeFont(HWND hwnd) +{ + LOGFONT lf; + CHOOSEFONT cf; + int r; + HWND hwndChild; + + memset(&cf, 0, sizeof(CHOOSEFONT)); + memcpy(&lf, &CurrentFont, sizeof(LOGFONT)); + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = hwnd; + cf.lpLogFont = &lf; + cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_APPLY | CF_INITTOLOGFONTSTRUCT; + cf.nFontType = SCREEN_FONTTYPE; + r = ChooseFont(&cf); + if (!r) + return (0); + DeleteObject(ProgramParams.hFont); + memcpy(&CurrentFont, &lf, sizeof(LOGFONT)); + ProgramParams.hFont = CreateFontIndirect(&CurrentFont); + strcpy(CurrentFontName, CurrentFont.lfFaceName); + CurrentFontFamily = lf.lfPitchAndFamily; + CurrentFontStyle = lf.lfWeight; + hwndChild = (HWND) GetWindowLong(hwndSession, DWL_USER); + SendMessage(hwndChild,WM_SETFONT,(WPARAM)ProgramParams.hFont,0); + ForceRepaint(); + return (1); +} + +/*------------------------------------------------------------------------ + Procedure: CallDlgProc ID:1 + Purpose: Calls a dialog box procedure + Input: The function to call, and the numerical ID of the + resource where the dialog box is stored + Output: Returns the result of the dialog box. + Errors: None +------------------------------------------------------------------------*/ +int CallDlgProc(BOOL (CALLBACK *fn)(HWND,UINT,WPARAM,LPARAM), int id) +{ + int result; + + result = DialogBoxParam(hInst, MAKEINTRESOURCE(id), GetActiveWindow(), + fn, 0); + return result; +} + + +/*------------------------------------------------------------------------ + Procedure: CallChangeColor ID:1 + Purpose: Calls the standard color dialog of windows, starting + with the given color reference. The result is the + same as the input if the user cancels, or another + color if the user validates another one. + Input: The starting color + Output: The color the user has choosen. + Errors: None +------------------------------------------------------------------------*/ +static COLORREF CallChangeColor(COLORREF InitialColor) +{ + CHOOSECOLOR CC; + COLORREF CustColors[16]; + int r, g, b, i; + memset(&CC, 0, sizeof(CHOOSECOLOR)); + r = g = b = 0; + for (i = 0; i < 16; i++) { + CustColors[i] = RGB(r, g, b); + if (r < 255) + r += 127; + else if (g < 255) + g += 127; + else if (b < 255) + g += 127; + } + CC.lStructSize = sizeof(CHOOSECOLOR); + CC.hwndOwner = hwndMain; + CC.hInstance = hInst; + CC.rgbResult = InitialColor; + CC.lpCustColors = CustColors; + CC.Flags = CC_RGBINIT; + if (!ChooseColor(&CC)) + return (InitialColor); + return (CC.rgbResult); +} + +/*------------------------------------------------------------------------ + Procedure: CallPrintSetup ID:1 + Purpose: Calls the printer setup dialog. Currently it is not + connected to the rest of the software, since printing + is not done yet + Input: None + Output: 1 if OK, 0, user cancelled + Errors: None +------------------------------------------------------------------------*/ +static int CallPrintSetup(void) +{ + PAGESETUPDLG sd; + int r; + + memset(&sd,0,sizeof(sd)); + sd.lStructSize = sizeof(sd); + sd.Flags = PSD_RETURNDEFAULT; + r = PageSetupDlg(&sd); + if (!r) + return 0; + sd.Flags = 0; + r = PageSetupDlg(&sd); + return r; +} + + +/*------------------------------------------------------------------------ + Procedure: Undo ID:1 + Purpose: Send an UNDO command to the edit field. + Input: The parent window of the control + Output: None + Errors: None +------------------------------------------------------------------------*/ +void Undo(HWND hwnd) +{ + HWND hEdit; + + hEdit = (HWND)GetWindowLong(hwnd,DWL_USER); + SendMessage(hEdit,EM_UNDO,0,0); +} + +/*------------------------------------------------------------------------ + Procedure: ForceRepaint ID:1 + Purpose: Forces a complete redraw of the edit control of the + current session. + Input: None + Output: None + Errors: None +------------------------------------------------------------------------*/ +void ForceRepaint(void) +{ + HWND hwndEdit = (HWND)GetWindowLong(hwndSession,DWL_USER); + InvalidateRect(hwndEdit,NULL,1); +} + +/*------------------------------------------------------------------------ + Procedure: AddLineToControl ID:1 + Purpose: It will ad the given text at the end of the edit + control, then it will send a return character to it. + This simulates user input. The history will not be + modified by this procedure. + Input: The text to be added + Output: None + Errors: If the line is empty, nothing will be done +------------------------------------------------------------------------*/ +void AddLineToControl(char *buf) +{ + HWND hEditCtrl; + + if (*buf == 0) + return; + hEditCtrl = (HWND)GetWindowLong(hwndSession,DWL_USER); + GotoEOF(); + SendMessage(hEditCtrl,EM_REPLACESEL,0,(LPARAM)buf); + SendMessage(hEditCtrl,WM_CHAR,'\r',0); +} + +/*------------------------------------------------------------------------ + Procedure: AboutDlgProc ID:1 + Purpose: Shows the "About" dialog box + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +static BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_CLOSE) + EndDialog(hDlg,1); + return 0; +} +/*------------------------------------------------------------------------ + Procedure: HistoryDlgProc ID:1 + Purpose: Shows the history of the session. Only input lines + are shown. A double click in a line will make this + dialog box procedure return the index of the selected + line (1 based). If the windows is closed (what is + equivalent to cancel), the return value is zero. + Input: Normal windows callback + Output: + Errors: +------------------------------------------------------------------------*/ +static BOOL CALLBACK HistoryDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HISTORYLINE *rvp; + int idx; + RECT rc; + + switch (message) { + case WM_INITDIALOG: + SendDlgItemMessage(hDlg,IDLIST,WM_SETFONT,(WPARAM)ProgramParams.hFont,0); + rvp = History; + idx = 0; + while (rvp) { + SendDlgItemMessage(hDlg,IDLIST,LB_INSERTSTRING,0,(LPARAM)rvp->Text); + SendDlgItemMessage(hDlg,IDLIST,LB_SETITEMDATA,0,(LPARAM)idx); + rvp = rvp->Next; + idx++; + } + SendDlgItemMessage(hDlg,IDLIST,LB_SETCURSEL,(LPARAM)idx-1,0); + return 1; + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDLIST: + switch(HIWORD(wParam)) { + case LBN_DBLCLK: + idx = SendDlgItemMessage(hDlg,IDLIST,LB_GETCURSEL,0,0); + if (idx == LB_ERR) + break; + idx = SendDlgItemMessage(hDlg,IDLIST,LB_GETITEMDATA,idx,0); + EndDialog(hDlg,idx+1); + return 1; + } + break; + } + break; + case WM_SIZE: + GetClientRect(hDlg,&rc); + MoveWindow(GetDlgItem(hDlg,IDLIST),0,0,rc.right,rc.bottom,1); + break; + + case WM_CLOSE: + EndDialog(hDlg,0); + break; + } + return 0; +} +/*------------------------------------------------------------------------ + Procedure: SaveText ID:1 + Purpose: Saves the contents of the session transcript. It will + loop for each line and write it to the specified file + Input: The name of the file where the session will be saved + Output: The session is saved + Errors: If it can't open the file for writing it will show an + error box +------------------------------------------------------------------------*/ +static void SaveText(char *fname) +{ + int i,len; + HWND hEdit = (HWND)GetWindowLong(hwndSession,DWL_USER); + int linesCount = SendMessage(hEdit,EM_GETLINECOUNT,0,0); + FILE *f; + char *buf = SafeMalloc(8192); + + f = fopen(fname,"wb"); + if (f == NULL) { + wsprintf("Impossible to open %s for writing",fname); + ShowDbgMsg(buf); + return; + } + for (i=0; i<linesCount;i++) { + *(unsigned short *)buf = 8100; + len = SendMessage(hEdit,EM_GETLINE,i,(LPARAM)buf); + buf[len] = 0; + strcat(buf,"\r\n"); + fwrite(buf,1,len+2,f); + } + fclose(f); + free(buf); +} + +/*------------------------------------------------------------------------ + Procedure: HandleCommand ID:1 + Purpose: Handles all menu commands. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +void HandleCommand(HWND hwnd, WPARAM wParam,LPARAM lParam) +{ + char *fname; + int r; + + switch(LOWORD(wParam)) { + case IDM_OPEN: + fname = SafeMalloc(512); + if (OpenMlFile(fname,512)) { + char *buf = SafeMalloc(512); + char *p = strrchr(fname,'.'); + if (p && !stricmp(p,".ml")) { + wsprintf(buf,"#use \"%s\";;",fname); + AddLineToControl(buf); + } + else if (p && !stricmp(p,".cmo")) { + wsprintf(buf,"#load \"%s\";;",fname); + AddLineToControl(buf); + } + free(buf); + } + free(fname); + break; + case IDM_GC: + AddLineToControl("Gc.full_major();;"); + break; + case IDM_SAVE: + fname = SafeMalloc(512); + if (GetSaveName(fname,512)) { + SaveText(fname); + } + free(fname); + break; + case IDM_HISTORY: + r = CallDlgProc(HistoryDlgProc,IDD_HISTORY); + if (r) { + AddLineToControl(GetHistoryLine(r-1)); + } + break; + case IDM_PRINTSU: + CallPrintSetup(); + break; + case IDM_FONT: + CallChangeFont(hwndMain); + break; + case IDM_COLORTEXT: + ProgramParams.TextColor = CallChangeColor(ProgramParams.TextColor); + ForceRepaint(); + break; + case IDM_BACKCOLOR: + BackColor = CallChangeColor(BackColor); + DeleteObject(BackgroundBrush); + BackgroundBrush = CreateSolidBrush(BackColor); + ForceRepaint(); + break; + case IDM_EDITUNDO: + Undo(hwnd); + break; + case IDM_WINDOWTILE: + SendMessage(hwndMDIClient,WM_MDITILE,0,0); + break; + case IDM_WINDOWCASCADE: + SendMessage(hwndMDIClient,WM_MDICASCADE,0,0); + break; + case IDM_WINDOWICONS: + SendMessage(hwndMDIClient,WM_MDIICONARRANGE,0,0); + break; + case IDM_EXIT: + PostMessage(hwnd,WM_CLOSE,0,0); + break; + case IDM_ABOUT: + CallDlgProc(AboutDlgProc,IDD_ABOUT); + break; + } +} + diff --git a/win32caml/ocaml.c b/win32caml/ocaml.c new file mode 100644 index 000000000..cddbbcbe8 --- /dev/null +++ b/win32caml/ocaml.c @@ -0,0 +1,791 @@ +/***********************************************************************/ +/* */ +/* Objective Caml */ +/* */ +/* Developed by Jacob Navia. */ +/* Copyright 2001 Institut National de Recherche en Informatique et */ +/* en Automatique. All rights reserved. This file is distributed */ +/* under the terms of the GNU Library General Public License. */ +/* */ +/***********************************************************************/ + +/* $Id$ */ + +/*@@ Wedit generated application. Written Sat Jun 02 18:22:38 2001 + @@header: D:\lcc\inria\inriares.h + @@resources: D:\lcc\inria\inria.rc + Do not edit outside the indicated areas */ +/*<---------------------------------------------------------------------->*/ +/*<---------------------------------------------------------------------->*/ +#include <windows.h> +#include <windowsx.h> +#include <commctrl.h> +#include <string.h> +#include <direct.h> +#include <Richedit.h> +#include "inriares.h" +#include "inria.h" +int EditControls = 10000; +static WNDPROC lpEProc; +static char lineBuffer[1024*32]; +int ReadToLineBuffer(void); +int AddLineBuffer(void); +static int busy; +static DWORD TimerId; +POSITION LastPromptPosition; +char LibDir[512]; +char OcamlPath[512]; +HBRUSH BackgroundBrush; +COLORREF BackColor = RGB(255,255,255); +PROGRAM_PARAMS ProgramParams; +HISTORYLINE *History; +/*<----------------- global variables --------------------------------------->*/ +HANDLE hInst; // Instance handle +HWND hwndMain; //Main window handle +HWND hwndSession; +HWND hwndMDIClient; //Mdi client window handle +static LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam); +static LRESULT CALLBACK MdiChildWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam); +PROCESS_INFORMATION pi; +HWND hWndStatusbar; + +/*------------------------------------------------------------------------ + Procedure: UpdateStatusBar ID:1 + Purpose: Updates the statusbar control with the appropiate + text + Input: lpszStatusString: Charactar string that will be shown + partNumber: index of the status bar part number. + displayFlags: Decoration flags + Output: none + Errors: none + +------------------------------------------------------------------------*/ +void UpdateStatusBar(LPSTR lpszStatusString, WORD partNumber, WORD displayFlags) +{ + SendMessage(hWndStatusbar, + SB_SETTEXT, + partNumber | displayFlags, + (LPARAM)lpszStatusString); +} + + +/*------------------------------------------------------------------------ + Procedure: MsgMenuSelect ID:1 + Purpose: Shows in the status bar a descriptive explaation of + the purpose of each menu item.The message + WM_MENUSELECT is sent when the user starts browsing + the menu for each menu item where the mouse passes. + Input: Standard windows. + Output: The string from the resources string table is shown + Errors: If the string is not found nothing will be shown. +------------------------------------------------------------------------*/ +LRESULT MsgMenuSelect(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam) +{ + static char szBuffer[256]; + UINT nStringID = 0; + UINT fuFlags = GET_WM_MENUSELECT_FLAGS(wparam, lparam) & 0xffff; + UINT uCmd = GET_WM_MENUSELECT_CMD(wparam, lparam); + HMENU hMenu = GET_WM_MENUSELECT_HMENU(wparam, lparam); + + szBuffer[0] = 0; // First reset the buffer + if (fuFlags == 0xffff && hMenu == NULL) // Menu has been closed + nStringID = 0; + + else if (fuFlags & MFT_SEPARATOR) // Ignore separators + nStringID = 0; + + else if (fuFlags & MF_POPUP) // Popup menu + { + if (fuFlags & MF_SYSMENU) // System menu + nStringID = IDS_SYSMENU; + else + // Get string ID for popup menu from idPopup array. + nStringID = 0; + } // for MF_POPUP + else // Must be a command item + nStringID = uCmd; // String ID == Command ID + + // Load the string if we have an ID + if (0 != nStringID) + LoadString(hInst, nStringID, szBuffer, sizeof(szBuffer)); + // Finally... send the string to the status bar + UpdateStatusBar(szBuffer, 0, 0); + return 0; +} + +/*------------------------------------------------------------------------ + Procedure: TimerProc ID:1 + Purpose: This procedure will be called by windows about 4 + times a second. It will just send a message to the + mdi child window to look at the pipe. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +static VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) +{ + SendMessage(hwndSession, WM_TIMERTICK, 0, 0); +} + +/*------------------------------------------------------------------------ + Procedure: InitializeStatusBar ID:1 + Purpose: Initialize the status bar + Input: hwndParent: the parent window + nrOfParts: The status bar can contain more than one + part. What is difficult, is to figure out how this + should be drawn. So, for the time being only one is + being used... + Output: The status bar is created + Errors: +------------------------------------------------------------------------*/ +void InitializeStatusBar(HWND hwndParent,int nrOfParts) +{ + const int cSpaceInBetween = 8; + int ptArray[40]; // Array defining the number of parts/sections + RECT rect; + HDC hDC; + + /* * Fill in the ptArray... */ + + hDC = GetDC(hwndParent); + GetClientRect(hwndParent, &rect); + + ptArray[nrOfParts-1] = rect.right; + //---TODO--- Add code to calculate the size of each part of the status + // bar here. + + ReleaseDC(hwndParent, hDC); + SendMessage(hWndStatusbar, + SB_SETPARTS, + nrOfParts, + (LPARAM)(LPINT)ptArray); + + UpdateStatusBar("Ready", 0, 0); +} + + +/*------------------------------------------------------------------------ + Procedure: CreateSBar ID:1 + Purpose: Calls CreateStatusWindow to create the status bar + Input: hwndParent: the parent window + initial text: the initial contents of the status bar + Output: + Errors: +------------------------------------------------------------------------*/ +static BOOL CreateSBar(HWND hwndParent,char *initialText,int nrOfParts) +{ + hWndStatusbar = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_BORDER|SBARS_SIZEGRIP, + initialText, + hwndParent, + IDM_STATUSBAR); + if(hWndStatusbar) + { + InitializeStatusBar(hwndParent,nrOfParts); + return TRUE; + } + + return FALSE; +} +/*------------------------------------------------------------------------ + Procedure: InitApplication ID:1 + Purpose: Registers two window classes: the "inria" window + class with the main window, and the mdi child + window's window class. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +static BOOL InitApplication(void) +{ + WNDCLASS wc; + + memset(&wc,0,sizeof(WNDCLASS)); + wc.style = CS_HREDRAW|CS_VREDRAW |CS_DBLCLKS ; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.hInstance = hInst; + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszClassName = "inriaWndClass"; + wc.lpszMenuName = MAKEINTRESOURCE(IDMAINMENU); + wc.hCursor = LoadCursor(NULL,IDC_ARROW); + wc.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(OCAML_ICON)); + if (!RegisterClass(&wc)) + return 0; + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)MdiChildWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 20; + wc.hInstance = hInst; // Owner of this class + wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(OCAML_ICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Default color + wc.lpszMenuName = NULL; + wc.lpszClassName = "MdiChildWndClass"; + if (!RegisterClass((LPWNDCLASS)&wc)) + return FALSE; + return 1; +} + +/*------------------------------------------------------------------------ + Procedure: CreateinriaWndClassWnd ID:1 + Purpose: Creates the main window + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +HWND CreateinriaWndClassWnd(void) +{ + return CreateWindow("inriaWndClass","Ocaml", + WS_MINIMIZEBOX|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_MAXIMIZEBOX|WS_CAPTION|WS_BORDER|WS_SYSMENU|WS_THICKFRAME, + CW_USEDEFAULT,0,CW_USEDEFAULT,0, + NULL, + NULL, + hInst, + NULL); +} + +/*------------------------------------------------------------------------ + Procedure: MDICmdFileNew ID:1 + Purpose: Creates a new session window. Note that multiple + windows with multiple sessions are possible. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +static HWND MDICmdFileNew(char *title, int show) +{ + HWND hwndChild; + char rgch[150]; + static int cUntitled; + MDICREATESTRUCT mcs; + + if (title == NULL) + wsprintf(rgch,"Session%d", cUntitled++); + else { + strncpy(rgch,title,149); + rgch[149] = 0; + } + + // Create the MDI child window + + mcs.szClass = "MdiChildWndClass"; // window class name + mcs.szTitle = rgch; // window title + mcs.hOwner = hInst; // owner + mcs.x = CW_USEDEFAULT; // x position + mcs.y = CW_USEDEFAULT; // y position + mcs.cx = CW_USEDEFAULT; // width + mcs.cy = CW_USEDEFAULT; // height + mcs.style = 0; // window style + mcs.lParam = 0; // lparam + + hwndChild = (HWND) SendMessage(hwndMDIClient, + WM_MDICREATE, + 0, + (LPARAM)(LPMDICREATESTRUCT) &mcs); + + if (hwndChild != NULL && show) + ShowWindow(hwndChild, SW_SHOW); + + return hwndChild; +} +static HWND CreateMdiClient(HWND hwndparent) +{ + CLIENTCREATESTRUCT ccs = {0}; + HWND hwndMDIClient; + int icount = GetMenuItemCount(GetMenu(hwndparent)); + + // Find window menu where children will be listed + ccs.hWindowMenu = GetSubMenu(GetMenu(hwndparent), icount-2); + ccs.idFirstChild = IDM_WINDOWCHILD; + + // Create the MDI client filling the client area + hwndMDIClient = CreateWindow("mdiclient", + NULL, + WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | + WS_HSCROLL, + 0, 0, 0, 0, + hwndparent, + (HMENU)0xCAC, + hInst, + (LPVOID)&ccs); + + ShowWindow(hwndMDIClient, SW_SHOW); + + return hwndMDIClient; +} + +void GotoEOF(void) +{ + HWND hEdit = (HWND)GetWindowLong(hwndSession,DWL_USER); + int linesCount = SendMessage(hEdit,EM_GETLINECOUNT,0,0); + int lineindex = SendMessage(hEdit,EM_LINEINDEX,linesCount-1,0); + int lastLineLength = SendMessage(hEdit,EM_LINELENGTH,linesCount-1,0); + + lineindex += lastLineLength; + SendMessage(hEdit,EM_SETSEL,lineindex,lineindex); +} + +int GetCurLineIndex(HWND hEdit) +{ + return SendMessage(hEdit,EM_LINEFROMCHAR,(WPARAM)-1,0); +} + +int GetNumberOfLines(HWND hEdit) +{ + return SendMessage(hEdit,EM_GETLINECOUNT,0,0); +} + +static int GetWordUnderCursor(HWND hwndEditControl,char *buf,int len) +{ + char *line,*p,*pstart,*pend; + int lineidx,start,end,length,offset,cursorpos,startingChar; + + SendMessage(hwndEditControl,EM_GETSEL,(WPARAM)&start,(LPARAM)&end); + lineidx = SendMessage(hwndEditControl,EM_EXLINEFROMCHAR,0,start); + startingChar = SendMessage(hwndEditControl,EM_LINEINDEX,lineidx,0); + start -= startingChar; + end -= startingChar; + lineidx = SendMessage(hwndEditControl,EM_LINEFROMCHAR,start,0); + length = SendMessage(hwndEditControl,EM_LINELENGTH,lineidx,0); + offset = SendMessage(hwndEditControl,EM_LINEINDEX,lineidx,0); + line = SafeMalloc(length+1); + memset(line,0,length+1); + *(unsigned short *)line = length; + SendMessage(hwndEditControl,EM_GETLINE,lineidx,(LPARAM)line); + cursorpos = start-offset; + p = line + cursorpos; + pstart = p; + while (*pstart + && *pstart != ' ' + && *pstart != '\t' + && *pstart != '(' + && pstart > line) + pstart--; + pend = p; + while (*pend + && *pend != ' ' + && *pend != '\t' + && *pend != '(' + && pend < line + length) + pend++; + if (*pstart == ' ' || *pstart == '\t') + pstart++; + if (*pend == ' ' || *pend == '\t') + pend--; + memcpy(buf,pstart,1+pend-pstart); + buf[pend-pstart] = 0; + free(line); + return 1; +} + +void DoHelp(HWND hwnd) +{ + char word[256]; + GetWordUnderCursor(hwnd,word,sizeof(word)); + MessageBox(NULL,word,"Aide pour:",MB_OK); +} + + +static LRESULT CALLBACK SubClassEdit(HWND hwnd, UINT msg, WPARAM mp1, LPARAM mp2) +{ + LRESULT r; + int postit=0,nl; + if (msg == WM_CHAR && mp1 == '\r') { + if (!busy) { + CallWindowProc(lpEProc,hwnd,WM_KEYDOWN,VK_END,1); + CallWindowProc(lpEProc,hwnd,WM_KEYUP,VK_END,1); + r = GetCurLineIndex(hwnd); + nl = GetNumberOfLines(hwnd); + if (r != nl-1) { + PostMessage(GetParent(hwnd),WM_NEWLINE,0,0); + return 0; + } + postit = 1; + } + + } + else if (msg == WM_KEYDOWN && mp1 == VK_F1) { + DoHelp(hwnd); + } + r = CallWindowProc(lpEProc, hwnd, msg, mp1, mp2); + if (postit) + PostMessage(GetParent(hwnd),WM_NEWLINE,0,0); + return r; +} + +static void SubClassEditField(HWND hwnd) +{ + if (lpEProc == NULL) { + lpEProc = (WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC); + } + SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) SubClassEdit); +} + +void AddToHistory(char *text) +{ + HISTORYLINE *newLine; + + while (*text == ' ') + text++; // skip leading blanks + if (*text == 0) + return; + if (History && !strstr(History->Text,";;")) { + char *p = History->Text; + int len = strlen(p)+strlen(text) + 1 + 1; // space and zero terminator + History->Text = SafeMalloc(len); + strcpy(History->Text,p); + strcat(History->Text," "); + strcat(History->Text,text); + free(p); + return; + } + newLine = SafeMalloc(sizeof(HISTORYLINE)); + newLine->Next = History; + newLine->Text = SafeMalloc(strlen(text)+1); + strcpy(newLine->Text,text); + History = newLine; +} + +char *GetHistoryLine(int n) +{ + HISTORYLINE *rvp = History; + int i; + + for (i=0; i<n; i++) { + rvp = rvp->Next; + } + if (rvp) + return &rvp->Text[0]; + else + return ""; +} + +/*------------------------------------------------------------------------ + Procedure: SendLastLine ID:1 + Purpose: Sends the data in the line containing the cursor to + the interpreter. If this is NOT the last line, copy + the line to the end of the text. + Input: The edit control window handle + Output: None explicit + Errors: None +------------------------------------------------------------------------*/ +void SendLastLine(HWND hEdit) +{ + int curline = GetCurLineIndex(hEdit); + char *p,linebuffer[2048]; + int linescount = GetNumberOfLines(hEdit); + + memset(linebuffer,0,sizeof(linebuffer)); + *(unsigned short *)linebuffer = sizeof(linebuffer)-1; + if (curline != linescount-1) + SendMessage(hEdit,EM_GETLINE,curline,(LPARAM)linebuffer); + else + SendMessage(hEdit,EM_GETLINE,curline-1,(LPARAM)linebuffer); + if (linebuffer[0] == '#' && linebuffer[1] == ' ') + memmove(linebuffer,linebuffer+2,strlen(linebuffer)+1); + // Record user input! + AddToHistory(linebuffer); + strcat(linebuffer,"\n"); + WriteToPipe(linebuffer); + if (curline != linescount-1) { + // Copy the line sent to the end of the text + p = strrchr(linebuffer,'\n'); + if (p) { + *p = 0; + } + busy = 1; + AddLineToControl(linebuffer); + busy = 0; + } +} +/*------------------------------------------------------------------------ + Procedure: SetLastPrompt ID:1 + Purpose: Record the position of the last prompt ("# ") sent by + the interpreter. This isn't really used yet. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +void SetLastPrompt(HWND hEdit) +{ + DWORD startpos,endpos; + SendMessage(hEdit,EM_GETSEL,(WPARAM)&startpos,(LPARAM)&endpos); + LastPromptPosition.line = SendMessage(hEdit,EM_LINEFROMCHAR,(WPARAM)-1,0); + LastPromptPosition.col = startpos; +} + +/*------------------------------------------------------------------------ + Procedure: MdiChildWndProc ID:1 + Purpose: The edit control is enclosed in a normal MDI window. + This is the window procedure for that window. When it + receives the WM_CREATE message, it will create the + edit control. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +static LRESULT CALLBACK MdiChildWndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam) +{ + HWND hwndChild; + RECT rc; + HDC hDC; + + switch(msg) { + case WM_CREATE: + GetClientRect(hwnd,&rc); + hwndChild= CreateWindow("EDIT", + NULL, + WS_CHILD | WS_VISIBLE | + ES_MULTILINE | + WS_VSCROLL | WS_HSCROLL | + ES_AUTOHSCROLL | ES_AUTOVSCROLL, + 0, + 0, + (rc.right-rc.left), + (rc.bottom-rc.top), + hwnd, + (HMENU) EditControls++, + hInst, + NULL); + SetWindowLong(hwnd, DWL_USER, (DWORD) hwndChild); + SendMessage(hwndChild, WM_SETFONT, (WPARAM) ProgramParams.hFont, 0L); + SubClassEditField(hwndChild); + break; + // Resize the edit control + case WM_SIZE: + hwndChild = (HWND) GetWindowLong(hwnd, DWL_USER); + MoveWindow(hwndChild, 0, 0, LOWORD(lparam), HIWORD(lparam), TRUE); + break; + // Always set the focus to the edit control. + case WM_SETFOCUS: + hwndChild = (HWND) GetWindowLong(hwnd, DWL_USER); + SetFocus(hwndChild); + break; + // Repainting of the edit control about to happen. + // Set the text color and the background color + case WM_CTLCOLOREDIT: + hDC = (HDC)wparam; + SetTextColor(hDC,ProgramParams.TextColor); + SetBkColor(hDC,BackColor); + return (LRESULT)BackgroundBrush; + // Take care of erasing the background color to avoid flicker + case WM_ERASEBKGND: + GetWindowRect(hwnd,&rc); + hDC = (HDC)wparam; + FillRect(hDC,&rc,BackgroundBrush); + return 1; + // A carriage return has been pressed. Send the data to the interpreted. + // This message is posted by the subclassed edit field. + case WM_NEWLINE: + if (busy) + break; + hwndChild = (HWND) GetWindowLong(hwnd, DWL_USER); + SendLastLine(hwndChild); + break; + // The timer will call us 4 times a second. Look if the interpreter + // has written something in its end of the pipe. + case WM_TIMERTICK: + hwndChild = (HWND) GetWindowLong(hwnd, DWL_USER); + if (ReadToLineBuffer()) { + char *p; + // Ok we read something. Display it. + AddLineBuffer(); + p = strrchr(lineBuffer,'\r'); + if (p && !strcmp(p,"\r\n# ")) { + if (p[4] == 0) { + SetLastPrompt(hwndChild); + } + } + + } + break; + + } + return DefMDIChildProc(hwnd, msg, wparam, lparam); +} + + +/*------------------------------------------------------------------------ + Procedure: MainWndProc ID:1 + Purpose: Window procedure for the frame window, that contains + the menu. The messages handled are: + WM_CREATE: Creates the mdi child window + WM_SIZE: resizes the status bar and the mdi child + window + WM_COMMAND: Sends the command to the dispatcher + WM_CLOSE: If the user confirms, it exists the program + WM_QUITOCAML: Stops the program unconditionally. + Input: Standard windows callback + Output: + Errors: +------------------------------------------------------------------------*/ +static LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) +{ + switch (msg) { + // Create the MDI client invisible window + case WM_CREATE: + hwndMDIClient = CreateMdiClient(hwnd); + TimerId = SetTimer((HWND) 0, 0, 100, (TIMERPROC) TimerProc); + break; + // Move the child windows + case WM_SIZE: + SendMessage(hWndStatusbar,msg,wParam,lParam); + InitializeStatusBar(hWndStatusbar,1); + // Position the MDI client window between the tool and status bars + if (wParam != SIZE_MINIMIZED) { + RECT rc, rcClient; + + GetClientRect(hwnd, &rcClient); + GetWindowRect(hWndStatusbar, &rc); + ScreenToClient(hwnd, (LPPOINT)&rc.left); + rcClient.bottom = rc.top; + MoveWindow(hwndMDIClient,rcClient.left,rcClient.top,rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, TRUE); + } + + return 0; + // Dispatch the menu commands + case WM_COMMAND: + HandleCommand(hwnd, wParam,lParam); + return 0; + // If user confirms close + case WM_CLOSE: + if (!AskYesOrNo("Quit Ocaml?")) + return 0; + break; + // End application + case WM_DESTROY: + PostQuitMessage(0); + break; + // The interpreter has exited. Force close of the application + case WM_QUITOCAML: + DestroyWindow(hwnd); + return 0; + case WM_USER+1000: + // TestGraphics(); + break; + default: + return DefFrameProc(hwnd,hwndMDIClient,msg,wParam,lParam); + } + return DefFrameProc(hwnd,hwndMDIClient,msg,wParam,lParam); +} + +/*------------------------------------------------------------------------ + Procedure: CreationCourier ID:1 + Purpose: Creates the courier font + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +static HFONT CreationCourier(int flag) +{ + LOGFONT CurrentFont; + memset(&CurrentFont, 0, sizeof(LOGFONT)); + CurrentFont.lfCharSet = ANSI_CHARSET; + CurrentFont.lfWeight = FW_NORMAL; + if (flag) + CurrentFont.lfHeight = 18; + else + CurrentFont.lfHeight = 15; + CurrentFont.lfPitchAndFamily = (BYTE) (FIXED_PITCH | FF_MODERN); + strcpy(CurrentFont.lfFaceName, "Courier"); /* Courier */ + return (CreateFontIndirect(&CurrentFont)); +} + +/*------------------------------------------------------------------------ + Procedure: ReadToLineBuffer ID:1 + Purpose: Reads into the line buffer the characters written by + the interpreter + Input: None + Output: The number of characters read + Errors: None +------------------------------------------------------------------------*/ +int ReadToLineBuffer(void) +{ + memset(lineBuffer,0,sizeof(lineBuffer)); + return ReadFromPipe(lineBuffer,sizeof(lineBuffer)); +} + +/*------------------------------------------------------------------------ + Procedure: AddLineBuffer ID:1 + Purpose: Sends the contents of the line buffer to the edit + control + Input: None + Output: + Errors: +------------------------------------------------------------------------*/ +int AddLineBuffer(void) +{ + HWND hEditCtrl; + + hEditCtrl = (HWND)GetWindowLong(hwndSession,DWL_USER); + return SendMessage(hEditCtrl,EM_REPLACESEL,0,(LPARAM)lineBuffer); + +} + +/*------------------------------------------------------------------------ + Procedure: Setup ID:1 + Purpose: Handles GUI initialization (Fonts, brushes, colors, + etc) + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +static int Setup(HANDLE *phAccelTable) +{ + if (!InitApplication()) + return 0; + ProgramParams.hFont = CreationCourier(1); + ProgramParams.TextColor = RGB(0,0,0); + GetObject(ProgramParams.hFont,sizeof(LOGFONT),&CurrentFont); + BackgroundBrush = CreateSolidBrush(BackColor); + *phAccelTable = LoadAccelerators(hInst,MAKEINTRESOURCE(IDACCEL)); + return 1; +} + + +/*------------------------------------------------------------------------ + Procedure: WinMain ID:1 + Purpose: Entry point for windows programs. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) +{ + MSG msg; + HANDLE hAccelTable; + + // Setup the hInst global + hInst = hInstance; + // Do the setup + if (!Setup(&hAccelTable)) + return 0; + // Create main window and exit if this fails + if ((hwndMain = CreateinriaWndClassWnd()) == (HWND)0) + return 0; + // Create the status bar + CreateSBar(hwndMain,"Ready",2); + // Show the window + ShowWindow(hwndMain,SW_SHOW); + // Create the session window + hwndSession = MDICmdFileNew("Session transcript",0); + // Get the path to ocaml.exe + GetOcamlPath(); + // Start the interpreter + StartOcaml(); + // Show the session window + ShowWindow(hwndSession, SW_SHOW); + // Maximize it + SendMessage(hwndMDIClient, WM_MDIMAXIMIZE, (WPARAM) hwndSession, 0); + + PostMessage(hwndMain,WM_USER+1000,0,0); + while (GetMessage(&msg,NULL,0,0)) { + if (!TranslateMDISysAccel(hwndMDIClient, &msg)) + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { + TranslateMessage(&msg); // Translates virtual key codes + DispatchMessage(&msg); // Dispatches message to window + } + } + WriteToPipe("#quit;;\r\n\032"); + KillTimer((HWND) 0, TimerId); + return msg.wParam; +} diff --git a/win32caml/ocaml.ico b/win32caml/ocaml.ico Binary files differnew file mode 100644 index 000000000..13560db45 --- /dev/null +++ b/win32caml/ocaml.ico diff --git a/win32caml/ocaml.rc b/win32caml/ocaml.rc new file mode 100644 index 000000000..7865b1b40 --- /dev/null +++ b/win32caml/ocaml.rc @@ -0,0 +1,112 @@ +/***********************************************************************/ +/* */ +/* Objective Caml */ +/* */ +/* Developed by Jacob Navia. */ +/* Copyright 2001 Institut National de Recherche en Informatique et */ +/* en Automatique. All rights reserved. This file is distributed */ +/* under the terms of the GNU Library General Public License. */ +/* */ +/***********************************************************************/ + +/* $Id$ */ + +/* Wedit generated resource file */ +#include <windows.h> +#include "inriares.h" + +1000 ICON "ocaml.ico" +IDMAINMENU MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Open...", IDM_OPEN + MENUITEM "&Save", IDM_SAVE + MENUITEM "Save &As...", IDM_SAVEAS + MENUITEM "&Close", IDM_CLOSE + MENUITEM SEPARATOR + MENUITEM "&Print", IDM_PRINT + MENUITEM "P&rint Setup...", IDM_PRINTSU + MENUITEM SEPARATOR + MENUITEM "E&xit", IDM_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo Alt+BkSp", IDM_EDITUNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t Shift+Del", IDM_EDITCUT + MENUITEM "&Copy Ctrl+Ins", IDM_EDITCOPY + MENUITEM "&Paste Shift+Ins", IDM_EDITPASTE + MENUITEM "&Delete Del", IDM_EDITCLEAR + END + POPUP "Workspace" + BEGIN + MENUITEM "Font", IDM_FONT + MENUITEM "Text Color", IDM_COLORTEXT + MENUITEM "Background color", IDM_BACKCOLOR + MENUITEM SEPARATOR + MENUITEM "&History", IDM_HISTORY + MENUITEM "&Garbage collect", IDM_GC + END + POPUP "&Window" + BEGIN + MENUITEM "&Tile", IDM_WINDOWTILE + MENUITEM "&Cascade", IDM_WINDOWCASCADE + MENUITEM "Arrange &Icons", IDM_WINDOWICONS + MENUITEM "Close &All", IDM_WINDOWCLOSEALL + END + POPUP "&Help" + BEGIN + MENUITEM "&About...", IDM_ABOUT + END +END +BARMDI ACCELERATORS +BEGIN + 81, IDM_EXIT, VIRTKEY, CONTROL +END + +IDD_ABOUT DIALOGEX 7, 29, 236, 81 +STYLE DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CLIENTEDGE | WS_EX_TOOLWINDOW +CAPTION "About Ocaml" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "The Objective Caml system for windows", 101, 56, 9, 126, 12 + LTEXT "Windows Interface 2.0", 102, 78, 21, 72, 12 + LTEXT "Copyright 1996-2001", 103, 84, 42, 66, 10 + CTEXT "Institut National de Recherche en Informatique et Automatique", 104, 15, 56, 211, 10 + CTEXT "Réalisé par Jacob Navia 2001", 105, 19, 66, 207, 12 +END + +IDD_HISTORY DIALOGEX 6, 18, 261, 184 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Session History" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDLIST, 7, 7, 247, 173, LBS_USETABSTOPS | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP +END +STRINGTABLE +BEGIN + 3010, "Switches to " + 2010, "Get help" + 2000, "Create, open, save, or print documents" + 500, "Displays information about this application" + 440, "Closes all open windows" + 430, "Arranges minimized window icons" + 420, "Arranges windows as overlapping tiles" + 410, "Arranges windows as non-overlapping tiles" + 350, "Removes the selection without putting it on the clipboard" + 340, "Inserts the clipboard contents at the insertion point" + 330, "Copies the selection and puts it on the clipboard" + 320, "Cuts the selection and puts it on the clipboard" + 310, "Reverses the last action" + 270, "Quits this application" + 260, "Changes the printer selection or configuration" + 250, "Prints the active document" + 240, "Closes the active document" + 230, "Saves the active document under a different name" + 220, "Saves the active document" + 210, "Opens an existing document" + 200, "Creates a new session" +END diff --git a/win32caml/startocaml.c b/win32caml/startocaml.c new file mode 100644 index 000000000..7d3d12f2d --- /dev/null +++ b/win32caml/startocaml.c @@ -0,0 +1,317 @@ +/***********************************************************************/ +/* */ +/* Objective Caml */ +/* */ +/* Developed by Jacob Navia. */ +/* Copyright 2001 Institut National de Recherche en Informatique et */ +/* en Automatique. All rights reserved. This file is distributed */ +/* under the terms of the GNU Library General Public License. */ +/* */ +/***********************************************************************/ + +/* $Id$ */ + +#include <windows.h> +#include <stdio.h> +#include <windows.h> +#include <direct.h> +#include "inria.h" +extern int _get_osfhandle(int); +PROCESS_INFORMATION pi; +#define BUFSIZE 4096 +STARTUPINFO startInfo; + +/*------------------------------------------------------------------------ + Procedure: ShowDbgMsg ID:1 + Purpose: Puts up a dialog box with a message, forcing it to + the foreground. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +void ShowDbgMsg(char *str) +{ + HWND hWnd; + char p[20], message[255]; + hWnd = hwndMain; + if (IsIconic(hWnd)){ + ShowWindow(hWnd,SW_RESTORE); + } + strncpy(message, str, 254); + message[254] = 0; + strcpy(p, "Error"); + MessageBox(hWnd, message, p, MB_OK | MB_ICONHAND|MB_TASKMODAL|MB_SETFOREGROUND); +} + +int AskYesOrNo(char *msg) +{ + HWND hwnd; + int r; + + hwnd = hwndMain; + r = MessageBox(hwnd, msg, "Ocaml", MB_YESNO | MB_SETFOREGROUND); + if (r == IDYES) + return (TRUE); + return (FALSE); +} + + +static DWORD OcamlStatus; + +static int RegistryError(void) +{ + char buf[512]; + + wsprintf(buf,"Error %d writing to the registry",GetLastError()); + ShowDbgMsg(buf); + return 0; +} + +static int DoCreateKey(HANDLE hkey,char *name,HKEY *hresult) +{ + unsigned long disp; + + return RegCreateKeyEx(hkey,name,0,NULL,0,KEY_ALL_ACCESS,NULL,hresult,&disp); +} + +/*------------------------------------------------------------------------ + Procedure: GetOcamlPath ID:1 + Purpose: Reads the registry key + HKEY_CURRENT_USER\Software\Ocaml, and creates it if + it doesn't exists. If any error occurs, i.e. the + given path doesn't exist, or the key didn't exist, it + will put up a browse dialog box to allow the user to + enter the path. The path will be verified that it + points to a file that exists. If that file is in a + directory called 'bin', it will look for another + directory in the same level called lib' and set the + Lib path to that. + Input: None explicit + Output: 1 means sucess, zero failure + Errors: Almost all system calls will be verified +------------------------------------------------------------------------*/ +int GetOcamlPath(void) +{ + HKEY hkeySoftware,hkeyOcaml; + DWORD dwType; + unsigned long siz; + char *p,buf[512]; + FILE *f; + + if (RegOpenKeyExA(HKEY_CURRENT_USER,"Software",0,KEY_QUERY_VALUE,&hkeySoftware) != ERROR_SUCCESS) + return 0; + if (DoCreateKey(hkeySoftware,"ocaml",&hkeyOcaml) != ERROR_SUCCESS) { + return RegistryError(); + } + dwType = REG_SZ; + siz = sizeof(buf); + memset(buf,0,sizeof(buf)); + RegQueryValueExA(hkeyOcaml,"InterpreterPath",0,&dwType,buf,&siz); + if (buf[0] == 0) { + if (!BrowseForFile("Ocaml interpreter|ocaml.exe",buf)) { + ShowDbgMsg("Impossible to find ocaml.exe. I quit"); + RegCloseKey(hkeyOcaml); + RegCloseKey(hkeySoftware); + exit(0); + } + RegSetValueEx(hkeyOcaml,"InterpreterPath",0,REG_SZ,buf,strlen(buf)+1); + } + f = fopen(buf,"r"); + if (f == NULL) { + char *errormsg = malloc(1024); + wsprintf(errormsg,"Incorrect path for ocaml.exe:\n%s",buf); + ShowDbgMsg(errormsg); + free(errormsg); + buf[0] = 0; + RegSetValueEx(hkeyOcaml,"InterpreterPath",0,REG_SZ,buf,1); + RegCloseKey(hkeyOcaml); + RegCloseKey(hkeySoftware); + return GetOcamlPath(); + } + else fclose(f); + RegCloseKey(hkeyOcaml); + RegCloseKey(hkeySoftware); + strcpy(OcamlPath,buf); + p = strrchr(OcamlPath,'\\'); + if (p) { + *p = 0; + strcpy(LibDir,OcamlPath); + *p = '\\'; + p = strrchr(LibDir,'\\'); + if (p && !stricmp(p,"\\bin")) { + *p = 0; + strcat(LibDir,"\\lib"); + } + } + return 1; +} + +static HANDLE hChildStdinRd, hChildStdinWr,hChildStdoutRd, hChildStdoutWr; +/*------------------------------------------------------------------------ + Procedure: IsWindowsNT ID:1 + Purpose: Returns 1 if we are running under windows NT, zero + otherwise. + Input: None + Output: 1 or zero + Errors: +------------------------------------------------------------------------*/ +int IsWindowsNT(void) +{ + OSVERSIONINFO osv; + + osv.dwOSVersionInfoSize = sizeof(osv); + GetVersionEx(&osv); + return(osv.dwPlatformId == VER_PLATFORM_WIN32_NT); +} + +/*------------------------------------------------------------------------ + Procedure: DoStartOcaml ID:1 + Purpose: Starts the ocaml interpreter ocaml.exe. The standard + input of the interpreter will be connected to a pipe, + and the standard output and standard error to another + pipe. The interpreter starts as a hidden process, + showing only in the task list. Since this is in an + own thread, its workings are independent of the rest + of the program. After starting the interpreter, the + thread waits in case the interpreter exits, for + instance if the user or some program types #quit;;. + In this case, the waiting thread awakens and exits + the user interface. + Input: Not used. It uses the OcamlPath global variable, that + is supposed to be correct, no test for its validity + are done here. + Output: None visible + Errors: If any system call for whatever reason fails, the + thread will exit. No error message is shown. +------------------------------------------------------------------------*/ +int _stdcall DoStartOcaml(HWND hwndParent) +{ + char *cmdline; + int processStarted; + LPSECURITY_ATTRIBUTES lpsa=NULL; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + // Under windows NT/2000/Whistler we have to initialize the security descriptors + // This is not necessary under windows 98/95. + if (IsWindowsNT()) { + InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd,TRUE,NULL,FALSE); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = &sd; + lpsa = &sa; + } + memset(&startInfo,0,sizeof(STARTUPINFO)); + startInfo.cb = sizeof(STARTUPINFO); + // Create a pipe for the child process's STDOUT. + if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &sa, 0)) + return 0; + // Create a pipe for the child process's STDIN. + if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &sa, 0)) + return 0; + // Setup the start info structure + startInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + startInfo.wShowWindow = SW_HIDE; + startInfo.hStdOutput = hChildStdoutWr; + startInfo.hStdError = hChildStdoutWr; + startInfo.hStdInput = hChildStdinRd; + cmdline = OcamlPath; + // Let's go: start the ocaml interpreter + processStarted = CreateProcess(NULL,cmdline,lpsa,lpsa,1, + CREATE_NEW_PROCESS_GROUP|NORMAL_PRIORITY_CLASS, + NULL,ProgramParams.CurrentWorkingDir,&startInfo,&pi); + if (processStarted) { + WaitForSingleObject(pi.hProcess,INFINITE); + GetExitCodeProcess(pi.hProcess,(unsigned long *)&OcamlStatus); + CloseHandle(pi.hProcess); + PostMessage(hwndMain,WM_QUITOCAML,0,0); + } + else { + char *msg = malloc(1024); + wsprintf(msg,"Impossible to start ocaml.exe in:\n%s",cmdline); + ShowDbgMsg(msg); + free(msg); + } + return 0; +} + +/*------------------------------------------------------------------------ + Procedure: WriteToPipe ID:1 + Purpose: Writes the given character string to the standard + input of the interpreter + Input: The character string (zero terminated) to be written + Output: The number of characters written or zero if an error + occurs + Errors: None +------------------------------------------------------------------------*/ +int WriteToPipe(char *data) +{ + DWORD dwWritten; + if (! WriteFile(hChildStdinWr, data, strlen(data), + &dwWritten, NULL)) + return 0; + return dwWritten; + +} + +/*------------------------------------------------------------------------ + Procedure: ReadFromPipe ID:1 + Purpose: Reads from the standard output of the interpreter and + stores the data in the given buffer up to the given + length. This is done in a non-blocking manner, i.e. + it is safe to call this even if there is no data + available. + Input: The buffer to be used and its length. + Output: Returns the number of characters read from the pipe. + Errors: None explicit +------------------------------------------------------------------------*/ +int ReadFromPipe(char *data,int len) +{ + DWORD dwRead; + + PeekNamedPipe(hChildStdoutRd,data,len,NULL,&dwRead,NULL); + if (dwRead == 0) + return 0; + + // Read output from the child process, and write to parent's STDOUT. + if( !ReadFile( hChildStdoutRd, data, len, &dwRead, + NULL) || dwRead == 0) + return 0; + return dwRead; +} + +static DWORD tid; +/*------------------------------------------------------------------------ + Procedure: StartOcaml ID:1 + Purpose: Starts the thread that will call the ocaml.exe + program. + Input: + Output: + Errors: +------------------------------------------------------------------------*/ +int StartOcaml(void) +{ + getcwd(ProgramParams.CurrentWorkingDir,sizeof(ProgramParams.CurrentWorkingDir)); + CreateThread(NULL,0,DoStartOcaml,hwndMain,0,&tid); + return 1; +} + + +void *SafeMalloc(int size) +{ + void *result; + + if (size < 0) { + char message[1024]; + +error: + sprintf(message,"Can't allocate %d bytes",size); + MessageBox(NULL,message,"Ocaml",MB_OK); + exit(-1); + } + result = malloc(size); + if (result == NULL) + goto error; + return result; +} |