1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: graphpccode.cpp
6  * Interface for Win32 (Win9x/NT/2K/XP) computers
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "graphpcstdafx.h"
33 #include "graphpc.h"
34 #include "graphpcdoc.h"
35 #include "graphpcview.h"
36 #include "graphpcmainframe.h"
37 #include "graphpcchildframe.h"
38 #include "graphpcmsgview.h"
39 #include "graphpcdialog.h"
40 #include "graphpcdialoglistbox.h"
41 
42 #include "global.h"
43 #include "database.h"
44 #include "egraphics.h"
45 #include "usr.h"
46 #include "usrtrack.h"
47 #include "eio.h"
48 #include "edialogs.h"
49 #if LANGTCL
50 #  include "dblang.h"
51 #endif
52 
53 /* #define USEDIRECTSOUND 1 */		/* uncomment to use direct-sound for playing sounds */
54 
55 #include <io.h>
56 #include <signal.h>
57 #include <sys/stat.h>
58 #include <direct.h>
59 #include <sys/timeb.h>
60 #include <sys/utime.h>
61 #include <afxmt.h>
62 #include <MMSystem.h>	/* need link with "winmm.lib" */
63 
64 #ifdef _UNICODE
65 #  define estat     _wstat
66 #  define estatstr  _stat
67 #  define eaccess   _waccess
68 #  define emkdir    _wmkdir
69 #  define egetcwd   _wgetcwd
70 #  define ecreat    _wcreat
71 #  define efullpath _wfullpath
72 #else
73 #  define estat     stat
74 #  define estatstr  stat
75 #  define eaccess   access
76 #  define emkdir    mkdir
77 #  define egetcwd   getcwd
78 #  define ecreat    creat
79 #  define efullpath _fullpath
80 #endif
81 
82 /****** windows and control ******/
83 extern CElectricApp  gra_app;					/* this application */
84 static WINDOWFRAME  *gra_cureditwindowframe;	/* the current window frame */
85 static WINDOWFRAME  *gra_palettewindowframe = NOWINDOWFRAME;	/* window frame with palette */
86 static INTBIG        gra_windownumberindex = 0;	/* counter for window and dialog indices */
87 static INTBIG        gra_newwindowoffset = 0;	/* offset of the next window (stagger them) */
88 static CHAR         *gra_initialdirectory;		/* location where Electric started */
89 static BOOLEAN       gra_creatingwindow = FALSE;/* TRUE if creating a window */
90 static BOOLEAN       gra_noflush = FALSE;		/* TRUE to supress display flushing */
91 
92 /****** the messages window ******/
93 #define MAXTYPEDLINE        256					/* max chars on input line */
94 
95 	   CChildFrame  *gra_messageswindow;		/* the messages window */
96 static CRichEditCtrl *gra_editCtrl;				/* the rich-edit control for messages text */
97 static BOOLEAN       gra_messagescurrent;		/* true if messages window is current */
98 static INTBIG        gra_messagesleft;			/* left bound of messages window screen */
99 static INTBIG        gra_messagesright;			/* right bound of messages window screen */
100 static INTBIG        gra_messagestop;			/* top bound of messages window screen */
101 static INTBIG        gra_messagesbottom;		/* bottom bound of messages window screen */
102 
103 /****** for text drawing ******/
104 static CDC          *gra_texthdc;				/* device context for text buffer */
105 static HBITMAP       gra_textbitmap;			/* bitmap for text buffer */
106 static BITMAPINFO   *gra_textbitmapinfo;		/* bitmap information structure */
107 static UCHAR1       *gra_textdatabuffer;		/* data in text buffer */
108 static UCHAR1      **gra_textrowstart;			/* row starts for text buffer */
109 static INTBIG        gra_textbufwid;			/* width of text buffer */
110 static INTBIG        gra_textbufhei;			/* height of text buffer */
111 static BOOLEAN       gra_textbufinited = FALSE;	/* true if text buffer is initialized */
112 
113 /****** for offscreen copying at a larger scale ******/
114 static BOOLEAN       gra_biggeroffbufinited = FALSE;	/* true if bigger offscreen buffer is initialized */
115 static INTBIG        gra_biggeroffbufwid;		/* width of bigger offscreen buffer */
116 static INTBIG        gra_biggeroffbufhei;		/* height of bigger offscreen buffer */
117 static UCHAR1      **gra_biggeroffrowstart;		/* row starts for bigger offscreen buffer */
118 static BITMAPINFO   *gra_biggeroffbitmapinfo;	/* bitmap information structure */
119 static HBITMAP       gra_biggeroffbitmap;		/* bitmap for bigger offscreen buffer */
120 static UCHAR1       *gra_biggeroffdatabuffer;	/* data in bigger offscreen buffer */
121 static CDC          *gra_biggeroffhdc;			/* device context for bigger offscreen buffer */
122 
123 /****** the status bar ******/
124 #define MAXSTATUSLINES        1
125 
126 static STATUSFIELD  *gra_statusfields[100];
127 static CHAR         *gra_statusfieldtext[100];
128 static INTBIG        gra_indicatorcount = 0;
129 static UINT gra_indicators[] = {ID_INDICATOR_ELECTRIC01, ID_INDICATOR_ELECTRIC02,
130 	ID_INDICATOR_ELECTRIC03, ID_INDICATOR_ELECTRIC04, ID_INDICATOR_ELECTRIC05,
131 	ID_INDICATOR_ELECTRIC06, ID_INDICATOR_ELECTRIC07, ID_INDICATOR_ELECTRIC08,
132 	ID_INDICATOR_ELECTRIC09, ID_INDICATOR_ELECTRIC10, ID_INDICATOR_ELECTRIC11,
133 	ID_INDICATOR_ELECTRIC12, ID_INDICATOR_ELECTRIC13, ID_INDICATOR_ELECTRIC14,
134 	ID_INDICATOR_ELECTRIC15, ID_INDICATOR_ELECTRIC16, ID_INDICATOR_ELECTRIC17};
135 
136 /****** the dialogs ******/
137 #define PROGRESSOFFSET           100
138 #define MAXSCROLLMULTISELECT    1000
139 #define MAXLOCKS                   3			/* maximum locked pairs of scroll lists */
140 #ifndef SPI_GETSNAPTODEFBUTTON
141 #  define SPI_GETSNAPTODEFBUTTON  95			/* why isn't this defined in system headers? */
142 #endif
143 
144 #define NOTDIALOG ((TDIALOG *)-1)
145 
146 typedef struct Itdialog
147 {
148 	CElectricDialog *window;
149 	DIALOG          *itemdesc;
150 	INTBIG           windindex;
151 	INTBIG           defaultbutton;
152 	POINT            firstpoint;
153 	INTBIG           numlocks, lock1[MAXLOCKS], lock2[MAXLOCKS], lock3[MAXLOCKS];
154 	INTBIG           redrawitem;
155 	INTBIG           useritemdoubleclick;
156 	INTBIG           usertextsize;
157 	void           (*redrawroutine)(RECTAREA*, void*);
158 	INTBIG           dialoghit;
159 	struct Itdialog *nexttdialog;
160 	void           (*diaeachdown)(INTBIG x, INTBIG y);
161 	void           (*modelessitemhit)(void *dia, INTBIG item);
162 } TDIALOG;
163 
164 static TDIALOG      *gra_firstactivedialog = NOTDIALOG;
165 static CBrush       *gra_dialogoffbrush = 0;
166 static CBrush       *gra_dialogbkgrbrush = 0;
167 static TDIALOG      *gra_trackingdialog = 0;
168 
169 /****** events ******/
170 #define CHARREAD             0377				/* character that was read */
171 #define ISKEYSTROKE          0400				/* set if key typed */
172 #define ISBUTTON            01000				/* set if button pushed (or released) */
173 #define BUTTONUP            02000				/* set if button was released */
174 #define SHIFTISDOWN         04000				/* set if shift key was held down */
175 #define ALTISDOWN          010000				/* set if alt key was held down */
176 #define CONTROLISDOWN      020000				/* set if control key was held down */
177 #define DOUBLECLICK        040000				/* set if this is second click */
178 #define MOTION            0100000				/* set if mouse motion detected */
179 #define WINDOWSIZE        0200000				/* set if window grown */
180 #define WINDOWMOVE        0400000				/* set if window moved */
181 #define MENUEVENT        01000000				/* set if menu entry selected (values in cursor) */
182 #define FILEREPLY        02000000				/* set if file selected in standard file dialog */
183 #define DIAITEMCLICK     04000000				/* set if item clicked in dialog */
184 #define DIASCROLLSEL    010000000				/* set if scroll item selected in dialog */
185 #define DIAEDITTEXT     020000000				/* set if edit text changed in dialog */
186 #define DIAPOPUPSEL     040000000				/* set if popup item selected in dialog */
187 #define DIASETCONTROL  0100000000				/* set if control changed in dialog */
188 #define DIAUSERMOUSE   0200000000				/* set if mouse moved in user area of dialog */
189 #define DIAENDDIALOG   0400000000				/* set when dialog terminates */
190 #define WHICHBUTTON   07000000000				/* which button was pushed */
191 #define ISLEFT                  0				/*   the left button */
192 #define ISMIDDLE      01000000000				/*   the middle button */
193 #define ISRIGHT       02000000000				/*   the right button */
194 #define ISWHLFWD      03000000000				/*   forward on the mouse wheel */
195 #define ISWHLBKW      04000000000				/*   backward on the mouse wheel */
196 #define POPUPSELECT  010000000000				/* popup selection made */
197 #define NOEVENT                -1				/* set if nothing happened */
198 
199 #define FLUSHTICKS    120
200 
201 struct		/* only used to communicate mouse coordinates for dialog logging */
202 {
203 	INTBIG x;
204 	INTBIG y;
205 } gra_action;
206 
207 #define EVENTQUEUESIZE	100
208 
209 typedef struct
210 {
211 	INTBIG           cursorx, cursory;			/* current position of mouse */
212 	INTBIG           inputstate;				/* current state of device input */
213 	INTBIG           special;					/* current "special" code for keyboard */
214 	UINTBIG          eventtime;					/* time this event happened */
215 } MYEVENTQUEUE;
216 
217 static MYEVENTQUEUE  gra_eventqueue[EVENTQUEUESIZE];
218 static MYEVENTQUEUE *gra_eventqueuehead;		/* points to next event in queue */
219 static MYEVENTQUEUE *gra_eventqueuetail;		/* points to first free event in queue */
220 static UINTBIG       gra_eventtime;				/* time last event happened */
221 static void         *gra_eventqueuemutex = 0;	/* mutex for event queue */
222 static INTBIG        gra_inputstate;			/* current state of device input */
223 static INTBIG        gra_inputspecial;			/* current "special" keyboard value */
224 static INTBIG        gra_cursorx, gra_cursory;	/* current position of mouse */
225 static INTBIG        gra_logrecordcount = 0;	/* count for session flushing */
226 static INTBIG        gra_lastloggedaction = NOEVENT;
227 static INTBIG        gra_lastloggedx, gra_lastloggedy;
228 static INTBIG        gra_lastloggedindex;
229 static UINTBIG       gra_logbasetime;			/* base of time for log output */
230 static UINTBIG       gra_lastplaybacktime = 0;
231 static CHAR         *gra_logfile, *gra_logfilesave;
232 
233 
234 /****** pulldown menus ******/
235 #define IDR_MENU           3000					/* base ID for menus */
236 
237 static CMenu        *gra_hMenu;					/* System Menu */
238 	   CMenu       **gra_pulldownmenus;			/* list of Windows pulldown menus */
239 static CHAR        **gra_pulldowns;				/* list of Electric pulldown menu names */
240 	   INTBIG        gra_pulldownmenucount;		/* number of pulldown menus */
241 static int           gra_menures[] = {ID_ELECTRIC_MENU01, ID_ELECTRIC_MENU02,
242 	ID_ELECTRIC_MENU03, ID_ELECTRIC_MENU04, ID_ELECTRIC_MENU05, ID_ELECTRIC_MENU06,
243 	ID_ELECTRIC_MENU07, ID_ELECTRIC_MENU08, ID_ELECTRIC_MENU09, ID_ELECTRIC_MENU10,
244 	ID_ELECTRIC_MENU11, ID_ELECTRIC_MENU12, ID_ELECTRIC_MENU13, ID_ELECTRIC_MENU14,
245 	ID_ELECTRIC_MENU15, ID_ELECTRIC_MENU16, ID_ELECTRIC_MENU17, ID_ELECTRIC_MENU18,
246 	ID_ELECTRIC_MENU19, ID_ELECTRIC_MENU20, ID_ELECTRIC_MENU21, ID_ELECTRIC_MENU22,
247 	ID_ELECTRIC_MENU23, ID_ELECTRIC_MENU24, ID_ELECTRIC_MENU25, ID_ELECTRIC_MENU26,
248 	ID_ELECTRIC_MENU27, ID_ELECTRIC_MENU28, ID_ELECTRIC_MENU29};
249 
250 /****** mouse buttons ******/
251 #define BUTTONS              45					/* cannot exceed NUMBUTS in "usr.h" */
252 #define REALBUTS              5					/* actual number of buttons */
253 
254 struct
255 {
256 	CHAR  *name;			/* button name */
257 	INTBIG unique;			/* number of letters that make it unique */
258 } gra_buttonname[BUTTONS] =
259 {
260 	{x_("LEFT"), 1},  {x_("MIDDLE"),  1}, {x_("RIGHT"),  1}, {x_("FORWARD"),   1},{x_("BACKWARD"),   1},	/* 0: unshifted */
261 	{x_("SLEFT"),2},  {x_("SMIDDLE"), 2}, {x_("SRIGHT"), 2}, {x_("SFORWARD"),  2},{x_("SBACKWARD"),  2},	/* 5: shift held down */
262 	{x_("CLEFT"),2},  {x_("CMIDDLE"), 2}, {x_("CRIGHT"), 2}, {x_("CFORWARD"),  2},{x_("CBACKWARD"),  2},	/* 10: control held down */
263 	{x_("ALEFT"),2},  {x_("AMIDDLE"), 2}, {x_("ARIGHT"), 2}, {x_("AFORWARD"),  2},{x_("ABACKWARD"),  2},	/* 15: alt held down */
264 	{x_("SCLEFT"),3}, {x_("SCMIDDLE"),3}, {x_("SCRIGHT"),3}, {x_("SCFORWARD"), 3},{x_("SCBACKWARD"), 3},	/* 20: control/shift held down */
265 	{x_("SALEFT"),3}, {x_("SAMIDDLE"),3}, {x_("SARIGHT"),3}, {x_("SAFORWARD"), 3},{x_("SABACKWARD"), 3},	/* 25: shift/alt held down */
266 	{x_("CALEFT"),3}, {x_("CAMIDDLE"),3}, {x_("CARIGHT"),3}, {x_("CAFORWARD"), 3},{x_("CABACKWARD"), 3},	/* 30: control/alt held down */
267 	{x_("SCALEFT"),4},{x_("SCAMIDDLE"),4},{x_("SCARIGHT"),4},{x_("SCAFORWARD"),4},{x_("SCABACKWARD"),4},	/* 35: shift/control/alt held down */
268 	{x_("DLEFT"),2},  {x_("DMIDDLE"), 2}, {x_("DRIGHT"), 2}, {x_("DFORWARD"),  2},{x_("DBACKWARD"),  2}		/* 40: double-click */
269 };
270 
271 /****** cursors ******/
272 static HCURSOR       gra_normalCurs;			/* the default cursor */
273 static HCURSOR       gra_wantttyCurs;			/* a "use the TTY" cursor */
274 static HCURSOR       gra_penCurs;				/* a "draw with pen" cursor */
275 static HCURSOR       gra_menuCurs;				/* a menu selection cursor */
276 static HCURSOR       gra_handCurs;				/* a hand dragging cursor */
277 static HCURSOR       gra_techCurs;				/* a technology edit cursor */
278 static HCURSOR       gra_ibeamCurs;				/* a text edit cursor */
279 static HCURSOR       gra_waitCurs;				/* an hourglass cursor */
280 static HCURSOR       gra_lrCurs;				/* a left/right pointing cursor */
281 static HCURSOR       gra_udCurs;				/* an up/down pointing cursor */
282 
283 #ifdef WINSAVEDBOX
284 
285 /****** rectangle saving ******/
286 #define NOSAVEDBOX ((SAVEDBOX *)-1)
287 
288 typedef struct Isavedbox
289 {
290 	HBITMAP           hBox;
291 	BITMAPINFO       *bminfo;
292 	CDC              *hMemDC;
293 	UCHAR1           *data;
294 	UCHAR1          **rowstart;
295 	WINDOWPART       *win;
296 	INTBIG            lx, hx, ly, hy;
297 	struct Isavedbox *nextsavedbox;
298 } SAVEDBOX;
299 
300 static SAVEDBOX     *gra_firstsavedbox = NOSAVEDBOX;
301 #endif
302 
303 /****** fonts ******/
304 #define MAXCACHEDFONTS 200
305 #define FONTHASHSIZE   211						/* must be prime */
306 
307 typedef struct
308 {
309 	CFont   *font;
310 	INTBIG   face;
311 	INTBIG   italic;
312 	INTBIG   bold;
313 	INTBIG   underline;
314 	INTBIG   size;
315 } FONTHASH;
316 
317 static FONTHASH      gra_fonthash[FONTHASHSIZE];
318 static CFont        *gra_fontcache[MAXCACHEDFONTS];
319 static INTBIG        gra_numfaces = 0;
320 static CHAR        **gra_facelist;
321 static INTBIG        gra_textrotation = 0;
322 static BOOLEAN       gra_texttoosmall = FALSE;
323 
324 /****** miscellaneous ******/
325 #define PALETTEWIDTH        136					/* width of palette */
326 #define MAXPATHLEN          256					/* max chars in file path */
327 #define SBARWIDTH            15					/* width of scroll bars */
328 #define MAXLOCALSTRING      256					/* size of "gra_localstring" */
329 
330 static INTBIG        gra_screenleft,			/* left bound of any editor window screen */
331                      gra_screenright,			/* right bound of any editor window screen */
332                      gra_screentop,				/* top bound of any editor window screen */
333                      gra_screenbottom;			/* bottom bound of any editor window screen */
334 static CHAR          gra_localstring[MAXLOCALSTRING];	/* local string */
335 static void         *gra_fileliststringarray = 0;
336 static time_t        gra_timebasesec;
337 static INTBIG        gra_timebasems;
338 static UINTBIG       gra_timeoffset = 0;		/* offset to system time */
339 
340 /****** prototypes for internal routines ******/
341 static void         gra_addeventtoqueue(INTBIG state, INTBIG special, INTBIG x, INTBIG y);
342 static BOOLEAN      gra_addfiletolist(CHAR *file);
343 static BOOLEAN      gra_buildoffscreenbuffer(WINDOWFRAME *wf, INTBIG wid, INTBIG hei, CDC **hdc,
344 						HBITMAP *bitmap, BITMAPINFO **bminfo, UCHAR1 **databuffer, UCHAR1 ***rowstart);
345 static BOOLEAN      gra_buildwindow(WINDOWFRAME*, BOOLEAN, RECTAREA*);
346 static void         gra_copyhighlightedtoclipboard(void);
347 static CFont       *gra_createtextfont(INTBIG, CHAR*, INTBIG, INTBIG, INTBIG);
348 static BOOLEAN      gra_diaeachdownhandler(INTBIG x, INTBIG y);
349 static INTBIG       gra_dodialogisinsideuserdrawn(TDIALOG *dia, int x, int y);
350 static int APIENTRY gra_enumfaces(LPLOGFONT lpLogFont, LPTEXTMETRIC lpTEXTMETRICs, DWORD fFontType, LPVOID lpData);
351 static void         gra_floatpalette(void);
352 static void         gra_freewindowframe(WINDOWFRAME *wf);
353 static BOOLEAN      gra_getbiggeroffscreenbuffer(WINDOWFRAME *wf, INTBIG wid, INTBIG hei);
354 static void         gra_getdevices(void);
355 static INTBIG       gra_getdialogitem(TDIALOG *dia, int x, int y);
356 static RECT        *gra_geteditorwindowlocation(void);
357 static TDIALOG     *gra_getdialogfromindex(INTBIG index);
358 static WINDOWFRAME *gra_getframefromindex(INTBIG index);
359 static CFont       *gra_gettextfont(WINDOWPART*, TECHNOLOGY*, UINTBIG*);
360 static HWND         gra_getwindow(WINDOWPART *win);
361 static BOOLEAN      gra_initdialog(DIALOG *dialog, TDIALOG *dia, BOOLEAN modeless);
362 static void         gra_initfilelist(void);
363 static BOOLEAN      gra_loggetnextaction(CHAR *message);
364 static BOOLEAN      gra_logreadline(CHAR *string, INTBIG limit);
365 static void         gra_logwriteaction(INTBIG inputstate, INTBIG special, INTBIG cursorx, INTBIG cursory, void *extradata);
366 static void         gra_logwritecomment(CHAR *comment);
367 static INTBIG       gra_makebutton(INTBIG);
368 static BOOLEAN      gra_makeeditwindow(WINDOWFRAME*);
369 static HICON        gra_makeicon(INTBIG data);
370 static BOOLEAN      gra_makemessageswindow(void);
371 static CMenu       *gra_makepdmenu(POPUPMENU *);
372 static BOOLEAN      gra_messagesnotvisible(void);
373 static INTBIG       gra_nativepopuptif(POPUPMENU **menu, BOOLEAN header, INTBIG left, INTBIG top);
374 static void         gra_nextevent(void);
375 static INTBIG       gra_pulldownindex(POPUPMENU *);
376 static void         gra_redrawdisplay(WINDOWFRAME*);
377 static void         gra_redrawstatusindicators(void);
378 static void         gra_reloadmap(void);
379 static BOOLEAN      gra_remakeeditwindow(WINDOWFRAME*);
380 static void         gra_removewindowextent(RECT *r, CWnd *wnd);
381 static void         gra_setdefaultcursor(void);
382 static INTBIG       gra_setregistry(HKEY key, CHAR *subkey, CHAR *value, CHAR *newstring);
383 static void         gra_tomessagesbottom(void);
384 #if LANGTCL
385        INTBIG       gra_initializetcl(void);
386 #endif
387 
388 #ifdef USEDIRECTSOUND
389 #  include <dsound.h>	/* need link with "dsound.lib" */
390   typedef struct
391   {
392 	IDirectSound       *gpds;
393 	IDirectSoundBuffer *secondarybuffer;
394   } SOUND;
395   static void         gra_playsound(SOUND *sound);
396   static SOUND       *gra_initsound(CHAR *filename);
397   extern "C" { extern HINSTANCE g_hDSoundLib; }
398   typedef HRESULT (WINAPI *PFN_DSCREATE)(LPGUID lpguid, LPDIRECTSOUND *ppDS, IUnknown FAR *pUnkOuter);
399   static PFN_DSCREATE gra_DSCreate;
400 #endif
401 
402 /****** prototypes for externally called routines ******/
403        void         gra_activateframe(CChildFrame *frame, BOOL bActivate);
404        void         gra_buttonaction(int state, UINT nFlags, CPoint point, CWnd *frm);
405        int          gra_closeframe(CChildFrame *frame);
406        int          gra_closeworld(void);
407        void         gra_diaredrawitem(CElectricDialog*);
408        int          gra_dodialoglistkey(CElectricDialog *diawin, UINT nKey, CListBox* pListBox, UINT nIndex);
409        void         gra_dodialogtextchange(CElectricDialog *diawin, int nID);
410        INTBIG       gra_getregistry(HKEY key, CHAR *subkey, CHAR *value, CHAR *result);
411        void         gra_itemclicked(CElectricDialog *diawin, int nID);
412        void         gra_itemdoubleclicked(CElectricDialog *diawin, int nID);
413        void         gra_itemvscrolled(void *vdia, int nID);
414        void         gra_keyaction(UINT nChar, INTBIG special, UINT nRepCnt);
415        void         gra_mouseaction(UINT nFlags, CPoint point, CWnd *frm);
416        void         gra_mousewheelaction(UINT nFlags, short zDelta, CPoint point, CWnd *frm);
417        void         gra_movedwindow(CChildFrame *frame, int x, int y);
418        void         gra_nativemenudoone(INTBIG low, INTBIG high);
419        void         gra_onint(void);
420        void         gra_repaint(CChildFrame *frame, CPaintDC *dc);
421        void         gra_resize(CChildFrame *frame, int cx, int cy);
422        void         gra_resizemain(int cx, int cy);
423        int          gra_setpropercursor(CWnd *wnd, int x, int y);
424        void         gra_setrect(WINDOWFRAME*, INTBIG, INTBIG, INTBIG, INTBIG);
425 	   void         gra_timerticked(void);
426 
427 /******************** INITIALIZATION ********************/
428 
429 /*
430  * routines to establish the default display
431  */
graphicsoptions(CHAR * name,INTBIG * argc,CHAR1 ** argv)432 void graphicsoptions(CHAR *name, INTBIG *argc, CHAR1 **argv)
433 {
434 	us_erasech = BACKSPACEKEY;
435 	us_killch = 025;
436 }
437 
438 /*
439  * routine to initialize the display device
440  */
initgraphics(BOOLEAN messages)441 BOOLEAN initgraphics(BOOLEAN messages)
442 {
443 	CHAR username[256];
444 	UINTBIG size;
445 	INTBIG i;
446 	REGISTER void *infstr;
447 
448 	gra_inputstate = NOEVENT;
449 	gra_pulldownmenucount = 0;	/* number of pulldown menus */
450 	gra_hMenu = 0;
451 
452 	/* setup globals that describe location of windows */
453 	gra_getdevices();
454 
455 	/* remember the initial directory and the log file locations */
456 	(void)allocstring(&gra_initialdirectory, currentdirectory(), db_cluster);
457 	size = 256;
458 	GetUserName(username, &size);
459 	infstr = initinfstr();
460 	addstringtoinfstr(infstr, gra_initialdirectory);
461 	addstringtoinfstr(infstr, x_("electric_"));
462 	addstringtoinfstr(infstr, username);
463 	addstringtoinfstr(infstr, x_(".log"));
464 	(void)allocstring(&gra_logfile, returninfstr(infstr), db_cluster);
465 	infstr = initinfstr();
466 	addstringtoinfstr(infstr, gra_initialdirectory);
467 	addstringtoinfstr(infstr, x_("electriclast_"));
468 	addstringtoinfstr(infstr, username);
469 	addstringtoinfstr(infstr, x_(".log"));
470 	(void)allocstring(&gra_logfilesave, returninfstr(infstr), db_cluster);
471 
472 	/* create the scrolling messages window */
473 	if (!messages) gra_messageswindow = 0; else
474 	{
475 	   if (gra_makemessageswindow()) error(_("Cannot create messages window"));
476 	}
477 
478 	/* get cursors */
479 	gra_normalCurs = gra_app.LoadStandardCursor(IDC_ARROW);
480 	gra_waitCurs = gra_app.LoadStandardCursor(IDC_WAIT);
481 	gra_ibeamCurs = gra_app.LoadStandardCursor(IDC_IBEAM);
482 	gra_wantttyCurs = gra_app.LoadCursor(IDC_CURSORTTY);
483 	if (gra_wantttyCurs == 0) gra_wantttyCurs = gra_normalCurs;
484 	gra_penCurs = gra_app.LoadCursor(IDC_CURSORPEN);
485 	if (gra_penCurs == 0) gra_penCurs = gra_normalCurs;
486 	gra_handCurs = gra_app.LoadCursor(IDC_CURSORHAND);
487 	if (gra_handCurs == 0) gra_handCurs = gra_normalCurs;
488 	gra_menuCurs = gra_app.LoadCursor(IDC_CURSORMENU);
489 	if (gra_menuCurs == 0) gra_menuCurs = gra_normalCurs;
490 	gra_techCurs = gra_app.LoadCursor(IDC_CURSORTECH);
491 	if (gra_techCurs == 0) gra_techCurs = gra_normalCurs;
492 	gra_lrCurs = gra_app.LoadStandardCursor(IDC_SIZEWE);
493 	if (gra_lrCurs == 0) gra_lrCurs = gra_normalCurs;
494 	gra_udCurs = gra_app.LoadStandardCursor(IDC_SIZENS);
495 	if (gra_udCurs == 0) gra_udCurs = gra_normalCurs;
496 
497 	/* initialize the cursor */
498 	SetCursor(0);
499 	us_normalcursor = NORMALCURSOR;
500 	us_cursorstate = us_normalcursor;
501 
502 	/* initialize font cache */
503 	for(i=0; i<MAXCACHEDFONTS; i++) gra_fontcache[i] = 0;
504 	for(i=0; i<FONTHASHSIZE; i++) gra_fonthash[i].font = 0;
505 
506 	gra_eventqueuehead = gra_eventqueuetail = gra_eventqueue;
507 	el_firstwindowframe = el_curwindowframe = NOWINDOWFRAME;
508 	gra_cureditwindowframe = NOWINDOWFRAME;
509 
510 	/* get mutex */
511 	(void)ensurevalidmutex(&gra_eventqueuemutex, TRUE);
512 
513 	return(FALSE);
514 }
515 
gra_makemessageswindow(void)516 BOOLEAN gra_makemessageswindow(void)
517 {
518 	RECT rmsg;
519 	CCreateContext context;
520 
521 	/* create a context to tell this window to be an EditView */
522 	context.m_pNewViewClass = RUNTIME_CLASS(CElectricMsgView);
523 	context.m_pCurrentDoc = NULL;
524 	context.m_pNewDocTemplate = NULL;
525 	context.m_pLastView = NULL;
526 	context.m_pCurrentFrame = NULL;
527 
528 	/* set messages window location */
529 	rmsg.left = gra_messagesleft;
530 	rmsg.right = gra_messagesright;
531 	rmsg.top = gra_messagestop;
532 	rmsg.bottom = gra_messagesbottom;
533 
534 	/* create the window */
535 	gra_messageswindow = new CChildFrame();
536 	gra_messageswindow->Create(NULL, _("Electric Messages"),
537 		WS_CHILD|WS_OVERLAPPEDWINDOW|WS_VISIBLE, rmsg, NULL, &context);
538 	gra_messageswindow->InitialUpdateFrame(NULL, TRUE);
539 	CRichEditView *msgView = (CRichEditView *)gra_messageswindow->GetActiveView();
540 	gra_editCtrl = &msgView->GetRichEditCtrl();
541 
542 	gra_messagescurrent = FALSE;
543 	return(FALSE);
544 }
545 
546 /*
547  * Routine to examine the display devices available and to setup globals that describe
548  * the editing windows and messages window extents.  On exit, the globals "gra_screenleft",
549  * "gra_screenright", "gra_screentop", and "gra_screenbottom" will describe the area
550  * for the editing windows and the variables "gra_messagesleft", "gra_messagesright",
551  * "gra_messagestop", and "gra_messagesbottom" will describe the messages window.
552  */
gra_getdevices(void)553 void gra_getdevices(void)
554 {
555 	RECT rDesktop;
556 
557 	/* obtain the current device */
558 	gra_app.m_pMainWnd->GetClientRect(&rDesktop);
559 	rDesktop.bottom -= 22;		/* status bar height? */
560 
561 	gra_screenleft   = 0;
562 	gra_screenright  = rDesktop.right-rDesktop.left;
563 	gra_screentop    = 0;
564 	gra_screenbottom = rDesktop.bottom-rDesktop.top;
565 
566 	gra_messagesleft   = gra_screenleft + PALETTEWIDTH;
567 	gra_messagesright  = gra_screenright - PALETTEWIDTH;
568 	gra_messagestop    = gra_screentop - 2 + (gra_screenbottom-gra_screentop) * 4 / 5;
569 	gra_messagesbottom = gra_screenbottom;
570 }
571 
572 /******************** TERMINATION ********************/
573 
termgraphics(void)574 void termgraphics(void)
575 {
576 	REGISTER WINDOWFRAME *wf;
577 	REGISTER INTBIG i;
578 
579 	gra_termgraph();
580 
581 	while (el_firstwindowframe != NOWINDOWFRAME)
582 	{
583 		wf = el_firstwindowframe;
584 		el_firstwindowframe = el_firstwindowframe->nextwindowframe;
585 		delete (CChildFrame *)wf->wndframe;
586 		gra_freewindowframe(wf);
587 	}
588 	if (gra_messageswindow != 0)
589 		delete gra_messageswindow;
590 
591 	if (gra_textbufinited)
592 	{
593 		efree((CHAR *)gra_textrowstart);
594 		efree((CHAR *)gra_textbitmapinfo);
595 		DeleteObject(gra_textbitmap);
596 		delete gra_texthdc;
597 	}
598 
599 	if (gra_biggeroffbufinited)
600 	{
601 		efree((CHAR *)gra_biggeroffrowstart);
602 		efree((CHAR *)gra_biggeroffbitmapinfo);
603 		DeleteObject(gra_biggeroffbitmap);
604 		delete gra_biggeroffhdc;
605 	}
606 
607 	if (gra_pulldownmenucount != 0)
608 	{
609 		efree((CHAR *)gra_pulldownmenus);
610 		for(i=0; i<gra_pulldownmenucount; i++) efree(gra_pulldowns[i]);
611 		efree((CHAR *)gra_pulldowns);
612 	}
613 
614 	if (gra_fileliststringarray != 0)
615 		killstringarray(gra_fileliststringarray);
616 	efree((CHAR *)gra_initialdirectory);
617 	efree((CHAR *)gra_logfile);
618 	efree((CHAR *)gra_logfilesave);
619 
620 	/* free cached fonts */
621 	for(i=0; i<MAXCACHEDFONTS; i++)
622 	{
623 		if (gra_fontcache[i] != 0) delete gra_fontcache[i];
624 		gra_fontcache[i] = 0;
625 	}
626 	for(i=0; i<FONTHASHSIZE; i++)
627 	{
628 		if (gra_fonthash[i].font != 0) delete gra_fonthash[i].font;
629 		gra_fonthash[i].font = 0;
630 	}
631 	for(i=0; i<gra_numfaces; i++) efree((CHAR *)gra_facelist[i]);
632 	if (gra_numfaces > 0) efree((CHAR *)gra_facelist);
633 }
634 
exitprogram(void)635 void exitprogram(void)
636 {
637 	exit(0);
638 }
639 
640 /******************** WINDOW CONTROL ********************/
641 
newwindowframe(BOOLEAN floating,RECTAREA * r)642 WINDOWFRAME *newwindowframe(BOOLEAN floating, RECTAREA *r)
643 {
644 	WINDOWFRAME *wf, *oldlisthead;
645 
646 	/* allocate one */
647 	wf = (WINDOWFRAME *)emalloc((sizeof (WINDOWFRAME)), us_tool->cluster);
648 	if (wf == 0) return(NOWINDOWFRAME);
649 	wf->numvar = 0;
650 	wf->rowstart = 0;
651 	wf->windindex = gra_windownumberindex++;
652 
653 	/* insert window-frame in linked list */
654 	oldlisthead = el_firstwindowframe;
655 	wf->nextwindowframe = el_firstwindowframe;
656 	el_firstwindowframe = wf;
657 	el_curwindowframe = wf;
658 	if (floating) gra_palettewindowframe = wf; else
659 		gra_cureditwindowframe = wf;
660 
661 	/* load an editor window into this frame */
662 	wf->pColorPalette = 0;
663 	if (gra_buildwindow(wf, floating, r))
664 	{
665 		efree((CHAR *)wf);
666 		el_firstwindowframe = oldlisthead;
667 		return(NOWINDOWFRAME);
668 	}
669 	return(wf);
670 }
671 
killwindowframe(WINDOWFRAME * owf)672 void killwindowframe(WINDOWFRAME *owf)
673 {
674 	WINDOWFRAME *wf, *lastwf;
675 
676 	/* kill the actual window */
677 	((CChildFrame *)owf->wndframe)->DestroyWindow();
678 
679 	/* find this frame in the list */
680 	lastwf = NOWINDOWFRAME;
681 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
682 	{
683 		if (wf == owf) break;
684 		lastwf = wf;
685 	}
686 	if (wf == NOWINDOWFRAME) return;
687 	if (lastwf == NOWINDOWFRAME) el_firstwindowframe = owf->nextwindowframe; else
688 		lastwf->nextwindowframe = owf->nextwindowframe;
689 	if (el_curwindowframe == owf) el_curwindowframe = NOWINDOWFRAME;
690 	if (gra_cureditwindowframe == owf) gra_cureditwindowframe = NOWINDOWFRAME;
691 
692 	gra_freewindowframe(owf);
693 }
694 
gra_freewindowframe(WINDOWFRAME * wf)695 void gra_freewindowframe(WINDOWFRAME *wf)
696 {
697 	if (wf->rowstart != 0)
698 	{
699 		efree((CHAR *)wf->rowstart);
700 		efree((CHAR *)wf->bminfo);
701 		DeleteObject(wf->hBitmap);
702 		delete ((CDC *)wf->hDCOff);
703 		efree((CHAR *)wf->pColorPalette);
704 	}
705 	efree((CHAR *)wf);
706 }
707 
getwindowframe(BOOLEAN canfloat)708 WINDOWFRAME *getwindowframe(BOOLEAN canfloat)
709 {
710 	if (el_curwindowframe != NOWINDOWFRAME)
711 	{
712 		if (canfloat || !el_curwindowframe->floating)
713 			return(el_curwindowframe);
714 	}
715 	if (gra_cureditwindowframe != NOWINDOWFRAME)
716 		return(gra_cureditwindowframe);
717 	return(NOWINDOWFRAME);
718 }
719 
720 /*
721  * routine to return size of window frame "wf" in "wid" and "hei"
722  */
getwindowframesize(WINDOWFRAME * wf,INTBIG * wid,INTBIG * hei)723 void getwindowframesize(WINDOWFRAME *wf, INTBIG *wid, INTBIG *hei)
724 {
725 	*wid = wf->swid;
726 	*hei = wf->shei;
727 }
728 
729 /*
730  * Routine to get the extent of the messages window.
731  */
getmessagesframeinfo(INTBIG * top,INTBIG * left,INTBIG * bottom,INTBIG * right)732 void getmessagesframeinfo(INTBIG *top, INTBIG *left, INTBIG *bottom, INTBIG *right)
733 {
734 	RECT fr;
735 	CWnd *parent;
736 
737 	parent = gra_messageswindow->GetParent();
738 	gra_messageswindow->GetWindowRect(&fr);
739 	parent->ScreenToClient(&fr);
740 	*top = fr.top;
741 	*left = fr.left;
742 	*bottom = fr.bottom;
743 	*right = fr.right;
744 }
745 
746 /*
747  * Routine to set the size and position of the messages window.
748  */
setmessagesframeinfo(INTBIG top,INTBIG left,INTBIG bottom,INTBIG right)749 void setmessagesframeinfo(INTBIG top, INTBIG left, INTBIG bottom, INTBIG right)
750 {
751 	(void)gra_messageswindow->SetWindowPos(0, left, top, right-left, bottom-top,
752 		SWP_NOZORDER);
753 }
754 
sizewindowframe(WINDOWFRAME * wf,INTBIG wid,INTBIG hei)755 void sizewindowframe(WINDOWFRAME *wf, INTBIG wid, INTBIG hei)
756 {
757 	RECT fr, cr;
758 	int framewid, framehei;
759 	WINDOWPLACEMENT wp;
760 
761 	/* if window frame already this size, stop now */
762 	if (wid == wf->swid && hei == wf->shei) return;
763 
764 	/* resize window if needed */
765 	((CChildFrame *)wf->wndframe)->GetClientRect(&cr);
766 	if (wid != cr.right-cr.left || hei != cr.bottom-cr.top)
767 	{
768 		((CChildFrame *)wf->wndframe)->GetWindowPlacement(&wp);
769 		fr = wp.rcNormalPosition;
770 
771 		framewid = (fr.right - fr.left) - (cr.right - cr.left);
772 		framehei = (fr.bottom - fr.top) - (cr.bottom - cr.top);
773 
774 		/* determine new window size */
775 		wid += framewid;
776 		hei += framehei;
777 
778 		/* resize the window */
779 		if (((CChildFrame *)wf->wndframe)->SetWindowPos(0, 0, 0, wid, hei, SWP_NOMOVE|SWP_NOZORDER) == 0)
780 		{
781 			/* failed to resize window */
782 			return;
783 		}
784 	}
785 
786 	/* rebuild the offscreen windows */
787 	if (gra_remakeeditwindow(wf))
788 	{
789 //		fr.left += 1;   fr.right -= 2;   fr.bottom -= 2;
790 //		SetWindowPos(wf->realwindow, NULL, 0, 0, fr.right-fr.left, fr.bottom-fr.top, SWP_NOZORDER | SWP_NOMOVE);
791 		return;
792 	}
793 	gra_reloadmap();
794 }
795 
movewindowframe(WINDOWFRAME * wf,INTBIG left,INTBIG top)796 void movewindowframe(WINDOWFRAME *wf, INTBIG left, INTBIG top)
797 {
798 	RECT fr;
799 	WINDOWPLACEMENT wp;
800 
801 	((CChildFrame *)wf->wndframe)->GetWindowPlacement(&wp);
802 	fr = wp.rcNormalPosition;
803 
804 	/* determine new window location */
805 	if (left == fr.left && top == fr.top) return;
806 	((CChildFrame *)wf->wndframe)->SetWindowPos(0, left, top, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
807 }
808 
809 /*
810  * Routine to close the messages window if it is in front.  Returns true if the
811  * window was closed.
812  */
closefrontmostmessages(void)813 BOOLEAN closefrontmostmessages(void)
814 {
815 	if (gra_messagescurrent && gra_messageswindow != 0)
816 	{
817 		gra_messageswindow->DestroyWindow();
818 		gra_messagescurrent = FALSE;
819 		gra_messageswindow = 0;
820 		return(TRUE);
821 	}
822 	return(FALSE);
823 }
824 
825 /*
826  * Routine to bring window "win" to the front.
827  */
bringwindowtofront(WINDOWFRAME * fwf)828 void bringwindowtofront(WINDOWFRAME *fwf)
829 {
830 	REGISTER WINDOWFRAME *wf;
831 
832 	/* validate the window frame */
833 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
834 		if (wf == fwf) break;
835 	if (wf == NOWINDOWFRAME)
836 	{
837 		ttyputmsg(x_("Warning: attempt to manipulate deleted window"));
838 		return;
839 	}
840 
841 	/* bring it to the top */
842 	((CChildFrame *)fwf->wndframe)->BringWindowToTop();
843 }
844 
845 /*
846  * Routine to organize the windows according to "how" (0: tile horizontally,
847  * 1: tile vertically, 2: cascade).
848  */
adjustwindowframe(INTBIG how)849 void adjustwindowframe(INTBIG how)
850 {
851 	RECT r;
852 	REGISTER INTBIG children, ret;
853 	REGISTER WINDOWFRAME *wf;
854 	HWND *kids;
855 	CWnd *parent, *compmenu;
856 
857 	/* figure out how many windows need to be rearranged */
858 	children = 0;
859 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
860 		if (!wf->floating) children++;
861 	if (children <= 0) return;
862 
863 	/* make a list of windows to rearrange */
864 	kids = (HWND *)emalloc(children * (sizeof (HWND)), el_tempcluster);
865 	if (kids == 0) return;
866 	children = 0;
867 	compmenu = NULL;
868 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
869 	{
870 		if (!wf->floating)
871 		{
872 			kids[children++] = wf->realwindow;
873 			parent = ((CChildFrame *)wf->wndframe)->GetParent();
874 		} else
875 		{
876 			compmenu = (CChildFrame *)wf->wndframe;
877 		}
878 	}
879 
880 	/* determine area for windows */
881 	parent->GetClientRect(&r);
882 	if (compmenu != NULL)
883 	{
884 		/* remove component menu from area of tiling */
885 		gra_removewindowextent(&r, compmenu);
886 	}
887 	if (gra_messageswindow != 0)
888 	{
889 		/* remove messages menu from area of tiling */
890 		gra_removewindowextent(&r, gra_messageswindow);
891 	}
892 
893 	/* rearrange the windows */
894 	switch (how)
895 	{
896 		case 0:		/* tile horizontally */
897 			ret = TileWindows(parent->m_hWnd, MDITILE_HORIZONTAL, &r, children, kids);
898 			break;
899 		case 1:		/* tile vertically */
900 			ret = TileWindows(parent->m_hWnd, MDITILE_VERTICAL, &r, children, kids);
901 			break;
902 		case 2:		/* cascade */
903 			ret = CascadeWindows(parent->m_hWnd, 0, &r, children, kids);
904 			break;
905 	}
906 	if (ret == 0)
907 	{
908 		ret = GetLastError();
909 		ttyputerr(_("Window rearrangement failed (error %ld)"), ret);
910 	}
911 	efree((CHAR *)kids);
912 }
913 
914 /*
915  * Helper routine to remove the location of window "wnd" from the rectangle "r".
916  */
gra_removewindowextent(RECT * r,CWnd * wnd)917 void gra_removewindowextent(RECT *r, CWnd *wnd)
918 {
919 	RECT er;
920 	CWnd *parent;
921 
922 	parent = wnd->GetParent();
923 	wnd->GetWindowRect(&er);
924 	parent->ScreenToClient(&er);
925 	if (er.right-er.left > er.bottom-er.top)
926 	{
927 		/* horizontal occluding window */
928 		if (er.bottom - r->top < r->bottom - er.top)
929 		{
930 			/* occluding window on top */
931 			r->top = er.bottom;
932 		} else
933 		{
934 			/* occluding window on bottom */
935 			r->bottom = er.top;
936 		}
937 	} else
938 	{
939 		/* vertical occluding window */
940 		if (er.right - r->left < r->right - er.left)
941 		{
942 			/* occluding window on left */
943 			r->left = er.right;
944 		} else
945 		{
946 			/* occluding window on right */
947 			r->right = er.left;
948 		}
949 	}
950 }
951 
952 /*
953  * routine to return the important pieces of information in placing the floating
954  * palette with the fixed menu.  The maximum screen size is placed in "wid" and
955  * "hei", and the amount of space that is being left for this palette on the
956  * side of the screen is placed in "palettewidth".
957  */
getpaletteparameters(INTBIG * wid,INTBIG * hei,INTBIG * palettewidth)958 void getpaletteparameters(INTBIG *wid, INTBIG *hei, INTBIG *palettewidth)
959 {
960 	RECT r;
961 	CMainFrame *wnd;
962 
963 	wnd = (CMainFrame *)AfxGetMainWnd();
964 	wnd->GetClientRect(&r);
965 	*hei = r.bottom - r.top - 50;
966 	*wid = r.right - r.left;
967 	*palettewidth = PALETTEWIDTH;
968 }
969 
970 /*
971  * Routine called when the component menu has moved to a different location
972  * on the screen (left/right/top/bottom).  Resets local state of the position.
973  */
resetpaletteparameters(void)974 void resetpaletteparameters(void)
975 {
976 }
977 
gra_geteditorwindowlocation(void)978 RECT *gra_geteditorwindowlocation(void)
979 {
980 	static RECT redit;
981 
982 	redit.top = gra_newwindowoffset + gra_screentop;
983 	redit.bottom = redit.top + (gra_screenbottom-gra_screentop) * 4 / 5 - 1;
984 	redit.left = gra_newwindowoffset + gra_screenleft + PALETTEWIDTH;
985 	redit.right = redit.left + (gra_screenright-gra_screenleft-PALETTEWIDTH) * 4 / 5;
986 
987 	/* is the editing window visible on this display? */
988 	if ((redit.bottom > gra_screenbottom) || (redit.right > gra_screenright))
989 	{
990 		gra_newwindowoffset = 0;
991 		redit.top = gra_screentop;
992 		redit.bottom = redit.top + (gra_screenbottom-gra_screentop) * 3 / 5;
993 		redit.left = gra_screenleft + PALETTEWIDTH;
994 		redit.right = redit.left + (gra_screenright-gra_screenleft-PALETTEWIDTH) * 4 / 5;
995 	}
996 	gra_newwindowoffset += 30;
997 	return(&redit);
998 }
999 
1000 /*
1001  * routine to build a new window frame
1002  */
gra_buildwindow(WINDOWFRAME * wf,BOOLEAN floating,RECTAREA * r)1003 BOOLEAN gra_buildwindow(WINDOWFRAME *wf, BOOLEAN floating, RECTAREA *r)
1004 {
1005 	RECT redit;
1006 	REGISTER INTBIG i;
1007 	REGISTER VARIABLE *varred, *vargreen, *varblue;
1008 	CChildFrame *tempFrame;
1009 
1010 	wf->floating = floating;
1011 	wf->wndframe = 0;
1012 	if (r != 0)
1013 	{
1014 		redit.left = r->left;   redit.right = r->right;
1015 		redit.top = r->top;     redit.bottom = r->bottom;
1016 	}
1017 	if (!floating)
1018 	{
1019 		/* get editor window location */
1020 		if (r == 0) redit = *gra_geteditorwindowlocation();
1021 
1022 		/* create the editing window */
1023 		tempFrame = new CChildFrame();
1024 		tempFrame->Create(NULL, x_("Electric"),
1025 			WS_CHILD|WS_OVERLAPPEDWINDOW|WS_VISIBLE, redit, NULL, NULL);
1026 		wf->wndframe = tempFrame;
1027 		wf->realwindow = tempFrame->m_hWnd;
1028 	} else
1029 	{
1030 		/* default palette window location */
1031 		if (r == 0)
1032 		{
1033 			redit.top = gra_screentop + 2;
1034 			redit.bottom = (gra_screentop+gra_screenbottom)/2;
1035 			redit.left = gra_screenleft + 1;
1036 			redit.right = redit.left + PALETTEWIDTH/2;
1037 		}
1038 
1039 		/* create the floating window */
1040 		gra_creatingwindow = TRUE;
1041 		tempFrame = new CChildFrame();
1042 		tempFrame->m_wndFloating = 1;
1043 		tempFrame->Create(NULL, x_("Components"),
1044 			WS_CHILD|WS_VISIBLE|WS_CAPTION, redit, NULL, NULL);
1045 		gra_creatingwindow = FALSE;
1046 		wf->wndframe = tempFrame;
1047 		wf->realwindow = tempFrame->m_hWnd;
1048 	}
1049 	if (wf->realwindow == 0) return(TRUE);
1050 
1051 	wf->hDC = (CDC *)((CChildFrame *)wf->wndframe)->GetDC();
1052 	((CChildFrame *)wf->wndframe)->ReleaseDC((CDC *)wf->hDC);
1053 	((CChildFrame *)wf->wndframe)->ShowWindow(SW_SHOW);
1054 	wf->hPalette = 0;
1055 
1056 	/* load any map the first time to establish the map segment length */
1057 	el_maplength = 256;
1058 
1059 	/* create a color palette for the buffer */
1060 	varred   = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
1061 	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
1062 	varblue  = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
1063 	if (varred != NOVARIABLE && vargreen != NOVARIABLE && varblue != NOVARIABLE)
1064 	{
1065 		wf->pColorPalette = (LOGPALETTE *)emalloc(256 * (sizeof (PALETTEENTRY)) + 2 * (sizeof (WORD)),
1066 			db_cluster);
1067 		if (wf->pColorPalette == 0) return(TRUE);
1068 		wf->pColorPalette->palVersion = 0x300;
1069 		wf->pColorPalette->palNumEntries = 256;
1070 		for(i=0; i<256; i++)
1071 		{
1072 			wf->pColorPalette->palPalEntry[i].peRed   = (UCHAR1)((INTBIG *)varred->addr)[i];
1073 			wf->pColorPalette->palPalEntry[i].peGreen = (UCHAR1)((INTBIG *)vargreen->addr)[i];
1074 			wf->pColorPalette->palPalEntry[i].peBlue  = (UCHAR1)((INTBIG *)varblue->addr)[i];
1075 			wf->pColorPalette->palPalEntry[i].peFlags = 0;
1076 		}
1077 		wf->pColorPalette->palPalEntry[255].peRed   = (UCHAR1)(255-((INTBIG *)varred->addr)[255]);
1078 		wf->pColorPalette->palPalEntry[255].peGreen = (UCHAR1)(255-((INTBIG *)vargreen->addr)[255]);
1079 		wf->pColorPalette->palPalEntry[255].peBlue  = (UCHAR1)(255-((INTBIG *)varblue->addr)[255]);
1080 		wf->hPalette = (CPalette *)new CPalette();
1081 		((CPalette *)wf->hPalette)->CreatePalette(wf->pColorPalette);
1082 	}
1083 
1084 	/* build the offscreen buffer for the window */
1085 	if (gra_makeeditwindow(wf))
1086 	{
1087 /*		DestroyWindow(wf->realwindow); */
1088 		return(TRUE);
1089 	}
1090 
1091 	return(FALSE);
1092 }
1093 
1094 /*
1095  * Routine to redraw editing window "wf" due to a drag or grow
1096  */
gra_redrawdisplay(WINDOWFRAME * wf)1097 void gra_redrawdisplay(WINDOWFRAME *wf)
1098 {
1099 	us_beginchanges();
1100 	us_drawmenu(-1, wf);
1101 	us_endchanges(NOWINDOWPART);
1102 	us_state |= HIGHLIGHTSET;
1103 	us_showallhighlight();
1104 }
1105 
1106 /*
1107  *  Routine to allocate the offscreen buffer that corresponds with the window
1108  *  "wf->realwindow".  The buffer is 8-bits deep and is stored in "wf->window".
1109  *  Returns true if memory cannot be allocated.
1110  */
gra_makeeditwindow(WINDOWFRAME * wf)1111 BOOLEAN gra_makeeditwindow(WINDOWFRAME *wf)
1112 {
1113 	RECT r;
1114 
1115 	/* get information about the window that was just created */
1116 	((CChildFrame *)wf->wndframe)->GetClientRect(&r);
1117 
1118 	/* set frame characteristics */
1119 	wf->swid = r.right - r.left;
1120 	wf->shei = r.bottom - r.top;
1121 	wf->revy = wf->shei - 1;
1122 	wf->offscreendirty = FALSE;
1123 
1124 	/* allocate the main offscreen buffer */
1125 	if (gra_buildoffscreenbuffer(wf, wf->swid, wf->shei, (CDC **)&wf->hDCOff, &wf->hBitmap,
1126 		&wf->bminfo, &wf->data, &wf->rowstart)) return(TRUE);
1127 
1128 	/* attach the color palette to drawing window section */
1129 	if (wf->hPalette != 0)
1130 	{
1131 		(void)((CDC *)wf->hDCOff)->SelectPalette((CPalette *)wf->hPalette, TRUE);
1132 		(void)((CDC *)wf->hDCOff)->RealizePalette();
1133 	}
1134 
1135 	return(FALSE);
1136 }
1137 
1138 /*
1139  * Routine to build an offscreen buffer that is "wid" by "hei" and based on window frame "wf".
1140  * The device context is placed in "hdc", the bitmap in "bitmap", the actual offscreen memory in
1141  * "databuffer" and the row start array in "rowstart".  Returns true on error.
1142  */
gra_buildoffscreenbuffer(WINDOWFRAME * wf,INTBIG wid,INTBIG hei,CDC ** hdc,HBITMAP * bitmap,BITMAPINFO ** bmiInfo,UCHAR1 ** databuffer,UCHAR1 *** rowstart)1143 BOOLEAN gra_buildoffscreenbuffer(WINDOWFRAME *wf, INTBIG wid, INTBIG hei, CDC **hdc,
1144 	HBITMAP *bitmap, BITMAPINFO **bmiInfo, UCHAR1 **databuffer, UCHAR1 ***rowstart)
1145 {
1146 	BITMAPINFOHEADER bmiHeader;
1147 	REGISTER VARIABLE *varred, *vargreen, *varblue;
1148 	RGBQUAD bmiColors[256];
1149 	REGISTER INTBIG i, j;
1150 	REGISTER BOOLEAN retval;
1151 	REGISTER INTBIG size;
1152 	REGISTER UCHAR1 *ptr;
1153 
1154 	/* make the info structure */
1155 	*bmiInfo = (BITMAPINFO *)emalloc(sizeof(bmiHeader) + sizeof(bmiColors), db_cluster);
1156 	if (*bmiInfo == 0) return(TRUE);
1157 
1158 	/* grab the device context */
1159 	wf->hDC = (CDC *)((CChildFrame *)wf->wndframe)->GetDC();
1160 
1161 	/* setup the header block */
1162 	bmiHeader.biSize = (DWORD)sizeof(bmiHeader);
1163 	bmiHeader.biWidth = (LONG)wid;
1164 	bmiHeader.biHeight = (LONG)-hei;
1165 	bmiHeader.biPlanes = 1;
1166 	bmiHeader.biBitCount = 8;
1167 	bmiHeader.biCompression = (DWORD)BI_RGB;
1168 	bmiHeader.biSizeImage = 0;
1169 	bmiHeader.biXPelsPerMeter = 0;
1170 	bmiHeader.biYPelsPerMeter = 0;
1171 	bmiHeader.biClrUsed = 256;
1172 	bmiHeader.biClrImportant = 0;
1173 
1174 	/* get color map information */
1175 	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
1176 	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
1177 	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
1178 	if (varred != NOVARIABLE && vargreen != NOVARIABLE && varblue != NOVARIABLE)
1179 	{
1180 		for(i=0; i<255; i++)
1181 		{
1182 			bmiColors[i].rgbRed = (UCHAR1)((INTBIG *)varred->addr)[i];
1183 			bmiColors[i].rgbGreen = (UCHAR1)((INTBIG *)vargreen->addr)[i];
1184 			bmiColors[i].rgbBlue = (UCHAR1)((INTBIG *)varblue->addr)[i];
1185 			bmiColors[i].rgbReserved = 0;
1186 		}
1187 		bmiColors[255].rgbRed = (UCHAR1)(255-((INTBIG *)varred->addr)[255]);
1188 		bmiColors[255].rgbGreen = (UCHAR1)(255-((INTBIG *)vargreen->addr)[255]);
1189 		bmiColors[255].rgbBlue = (UCHAR1)(255-((INTBIG *)varblue->addr)[255]);
1190 	}
1191 
1192 	/* create a device context for this offscreen buffer */
1193 	retval = TRUE;
1194 	*hdc = new CDC();
1195 	BOOL result = (*hdc)->CreateCompatibleDC((CDC *)wf->hDC);
1196 	if (result)
1197 	{
1198 		/* prepare structure describing offscreen buffer */
1199 		ptr = (UCHAR1 *)*bmiInfo;
1200 		memcpy(ptr, &bmiHeader, sizeof(bmiHeader));
1201 		ptr += sizeof(bmiHeader);
1202 		memcpy(ptr, &bmiColors, sizeof(bmiColors));
1203 
1204 		/* allocate offscreen buffer */
1205 		*bitmap = CreateDIBSection((*hdc)->m_hDC, *bmiInfo, DIB_RGB_COLORS, (void **)databuffer, NULL, 0);
1206 		if (*bitmap != 0)
1207 		{
1208 			(*hdc)->SelectObject(CBitmap::FromHandle(*bitmap));
1209 
1210 			/* setup row pointers to offscreen buffer */
1211 			*rowstart = (UCHAR1 **)emalloc(hei * (sizeof (UCHAR1 *)), db_cluster);
1212 			if (*rowstart == 0) return(TRUE);
1213 			size = (wid+3)/4*4;
1214 			for(j=0; j<hei; j++) (*rowstart)[j] = (*databuffer) + (size * j);
1215 			retval = FALSE;
1216 		}
1217 	}
1218 
1219 	/* release the device context */
1220 	((CChildFrame *)wf->wndframe)->ReleaseDC((CDC *)wf->hDC);
1221 	return(retval);
1222 }
1223 
1224 /*
1225  * Routine to rebuild the offscreen buffer when its size or depth has changed.
1226  * Returns true if the window cannot be rebuilt.
1227  */
gra_remakeeditwindow(WINDOWFRAME * wf)1228 BOOLEAN gra_remakeeditwindow(WINDOWFRAME *wf)
1229 {
1230 	/* free memory associated with the frame */
1231 	if (wf->rowstart != 0)
1232 	{
1233 		efree((CHAR *)wf->rowstart);
1234 		efree((CHAR *)wf->bminfo);
1235 		DeleteObject(wf->hBitmap);
1236 		delete (CDC *)wf->hDCOff;
1237 	}
1238 	wf->rowstart = 0;
1239 
1240 	return(gra_makeeditwindow(wf));
1241 }
1242 
1243 /******************** MISCELLANEOUS EXTERNAL ROUTINES ********************/
1244 
1245 /*
1246  * return true if the capabilities in "want" are present
1247  */
graphicshas(INTBIG want)1248 BOOLEAN graphicshas(INTBIG want)
1249 {
1250 	/* only 1 status area, shared by each frame */
1251 	if ((want&CANSTATUSPERFRAME) != 0) return(FALSE);
1252 
1253 	/* cannot run subprocesses */
1254 	if ((want&CANRUNPROCESS) != 0) return(FALSE);
1255 
1256 	return(TRUE);
1257 }
1258 
1259 /*
1260  * Routine to make sound "sound".  If sounds are turned off, no
1261  * sound is made (unless "force" is TRUE)
1262  */
ttybeep(INTBIG sound,BOOLEAN force)1263 void ttybeep(INTBIG sound, BOOLEAN force)
1264 {
1265 	void *infstr;
1266 #ifdef USEDIRECTSOUND
1267 	static BOOLEAN clicksoundinited = FALSE;
1268 	static SOUND *clicksound = 0;
1269 #endif
1270 
1271 	if ((us_tool->toolstate & TERMBEEP) != 0 || force)
1272 	{
1273 		switch (sound)
1274 		{
1275 			case SOUNDBEEP:
1276 				MessageBeep(MB_ICONASTERISK);
1277 				break;
1278 			case SOUNDCLICK:
1279 				if ((us_useroptions&NOEXTRASOUND) != 0) break;
1280 #ifdef USEDIRECTSOUND
1281 				/* this crashed on some Windows 2000 systems (a laptop) */
1282 				/* try "waveOutOpen()" and others... */
1283 				if (!clicksoundinited)
1284 				{
1285 					infstr = initinfstr();
1286 					formatinfstr(infstr, x_("%sclick.wav"), el_libdir);
1287 					clicksound = gra_initsound(returninfstr(infstr));
1288 					clicksoundinited = TRUE;
1289 				}
1290 				if (clicksound != 0)
1291 					gra_playsound(clicksound);
1292 #else
1293 				infstr = initinfstr();
1294 				formatinfstr(infstr, x_("%sclick.wav"), el_libdir);
1295 				PlaySound(returninfstr(infstr), 0, SND_FILENAME);
1296 #endif
1297 				break;
1298 		}
1299 	}
1300 }
1301 
1302 #ifdef USEDIRECTSOUND
1303 
1304 /*
1305  * Routine to create a SOUND object for the WAVE file "filename".  Returns zero on error.
1306  */
gra_initsound(CHAR * filename)1307 SOUND *gra_initsound(CHAR *filename)
1308 {
1309 	UINTBIG i, buffersize, len1, len2;
1310 	UCHAR1 *data, *ptr1 = NULL, *ptr2 = NULL;
1311 	SOUND *sound;
1312 	CMainFrame *frame;
1313 	WORD extrabytes;
1314 	DSBUFFERDESC sbuf;
1315 	IDirectSoundBuffer *primarybuffer;
1316 	WAVEFORMATEX *waveinfo;
1317 	HMMIO mmio;
1318 	MMIOINFO mmioinfo;
1319 	MMCKINFO mmiobufheader, mmiobuf;
1320 	PCMWAVEFORMAT formatchunk;
1321 
1322 	/* make the object */
1323 	sound = (SOUND *)emalloc(sizeof (SOUND), us_tool->cluster);
1324 	if (sound == 0) return(0);
1325 	sound->gpds = 0;
1326 	sound->secondarybuffer = 0;
1327 
1328 	gra_DSCreate = (PFN_DSCREATE)GetProcAddress(g_hDSoundLib, x_("DirectSoundCreate"));
1329 	if (gra_DSCreate == NULL)
1330 	{
1331 		return(0);
1332 	}
1333 
1334 	/* open the direct sound system */
1335 	if (DirectSoundCreate(NULL, &sound->gpds, NULL) != DS_OK) return(0);
1336 	frame = (CMainFrame *)AfxGetMainWnd();
1337 	if (sound->gpds->SetCooperativeLevel(frame->m_hWnd, DSSCL_PRIORITY) != DS_OK) return(0);
1338 
1339 	/* make a primary sound buffer */
1340 	ZeroMemory(&sbuf, sizeof(DSBUFFERDESC));
1341 	sbuf.dwSize = sizeof (DSBUFFERDESC);
1342 	sbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
1343 	if (sound->gpds->CreateSoundBuffer(&sbuf, &primarybuffer, NULL) != DS_OK) return(0);
1344 
1345 	/* get the wave file */
1346 	mmio = mmioOpen(filename, NULL, MMIO_ALLOCBUF|MMIO_READ);
1347 	if (mmio == NULL) return(0);
1348 	if (mmioDescend(mmio, &mmiobufheader, NULL, 0) != 0) return(0);
1349 	if (mmiobufheader.ckid != FOURCC_RIFF ||
1350 		mmiobufheader.fccType != mmioFOURCC('W', 'A', 'V', 'E')) return(0);
1351 
1352 	/* find the format chunk of the wave file */
1353 	mmiobuf.ckid = mmioFOURCC('f', 'm', 't', ' ');
1354 	if (mmioDescend(mmio, &mmiobuf, &mmiobufheader, MMIO_FINDCHUNK) != 0) return(0);
1355 	if (mmiobuf.cksize < sizeof(PCMWAVEFORMAT)) return(0);
1356 	if (mmioRead(mmio, (UCHAR1 *)&formatchunk, sizeof(formatchunk)) != sizeof(formatchunk))
1357 		return(0);
1358 
1359 	/* allocate the waveformat structure */
1360 	extrabytes = 0;
1361 	if (formatchunk.wf.wFormatTag != WAVE_FORMAT_PCM)
1362 	{
1363 		if (mmioRead(mmio, (UCHAR1 *)&extrabytes, sizeof(extrabytes)) != sizeof(extrabytes))
1364 			return(0);
1365 	}
1366 	waveinfo = (WAVEFORMATEX *)emalloc(sizeof(WAVEFORMATEX)+extrabytes, db_cluster);
1367 	if (waveinfo == 0) return(0);
1368 
1369 	/* load the waveformat structure */
1370 	memcpy(waveinfo, &formatchunk, sizeof(formatchunk));
1371 	waveinfo->cbSize = extrabytes;
1372 	if (extrabytes != 0)
1373 	{
1374 		if (mmioRead(mmio, (UCHAR1 *)(((UCHAR1 *)&waveinfo->cbSize)+sizeof(extrabytes)),
1375 			extrabytes) != extrabytes) return(0);
1376 	}
1377 
1378 	/* exit the format chunk of the wave file */
1379 	if (mmioAscend(mmio, &mmiobuf, 0) != 0) return(0);
1380 
1381 	/* find the data chunk of the wave file */
1382 	(void)mmioSeek(mmio, mmiobufheader.dwDataOffset + sizeof(FOURCC), SEEK_SET);
1383 	mmiobuf.ckid = mmioFOURCC('d', 'a', 't', 'a');
1384 	if (mmioDescend(mmio, &mmiobuf, &mmiobufheader, MMIO_FINDCHUNK) != 0) return(0);
1385 
1386 	/* allocate space for the data */
1387 	data = (UCHAR1 *)emalloc(mmiobuf.cksize, db_cluster);
1388 	if (data == 0) return(0);
1389 
1390 	/* get the data from the wave file */
1391 	if (mmioGetInfo(mmio, &mmioinfo, 0) != 0) return(0);
1392 	buffersize = mmiobuf.cksize;
1393 	if (buffersize > mmiobuf.cksize) buffersize = mmiobuf.cksize;
1394 	mmiobuf.cksize -= buffersize;
1395 
1396 	/* copy the data */
1397 	for (i = 0; i < buffersize; i++)
1398 	{
1399 		if (mmioinfo.pchNext == mmioinfo.pchEndRead)
1400 		{
1401 			if (mmioAdvance(mmio, &mmioinfo, MMIO_READ) != 0) return(0);
1402 			if (mmioinfo.pchNext == mmioinfo.pchEndRead) return(0);
1403 		}
1404 		data[i] = *mmioinfo.pchNext++;
1405 	}
1406 
1407 	/* clean up the wave file */
1408 	if (mmioSetInfo(mmio, &mmioinfo, 0) != 0) return(0);
1409 	mmioClose(mmio, 0);
1410 
1411 	/* create a sound buffer */
1412 	ZeroMemory(&sbuf, sizeof(DSBUFFERDESC));
1413 	sbuf.dwSize = sizeof (DSBUFFERDESC);
1414 	sbuf.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_STATIC;
1415 	sbuf.dwBufferBytes = buffersize;
1416 	sbuf.lpwfxFormat = waveinfo;
1417 	if (sound->gpds->CreateSoundBuffer(&sbuf, &sound->secondarybuffer, NULL) != 0) return(0);
1418 
1419 	/* load the data into the buffer */
1420 	if (sound->secondarybuffer->Lock(0, buffersize, (void **)&ptr1, &len1,
1421 		(void **)&ptr2, &len2, 0L) != 0) return(0);
1422 	CopyMemory(ptr1, data, buffersize);
1423 	if (sound->secondarybuffer->Unlock(ptr1, buffersize, ptr2, 0) != 0) return(0);
1424 	return(sound);
1425 }
1426 
1427 /*
1428  * Routine to play sound "sound" and only return when it is finished playing.
1429  */
gra_playsound(SOUND * sound)1430 void gra_playsound(SOUND *sound)
1431 {
1432 	UINTBIG status;
1433 
1434 	if (sound->secondarybuffer == 0) return;
1435 
1436 	/* play the sound */
1437 	sound->secondarybuffer->SetCurrentPosition(0);
1438 	if (sound->secondarybuffer->Play(0, 0, 0) != DS_OK) return;
1439 
1440 	/* query the sound */
1441 	for(;;)
1442 	{
1443 		if (sound->secondarybuffer->GetStatus(&status) != DS_OK) return;
1444 		if ((status&DSBSTATUS_PLAYING) == 0) break;
1445 	}
1446 }
1447 #endif
1448 
1449 extern "C" {
1450 DIALOGITEM db_severeerrordialogitems[] =
1451 {
1452  /*  1 */ {0, {80,8,104,72}, BUTTON, N_("Exit")},
1453  /*  2 */ {0, {80,96,104,160}, BUTTON, N_("Save")},
1454  /*  3 */ {0, {80,184,104,256}, BUTTON, N_("Continue")},
1455  /*  4 */ {0, {8,8,72,256}, MESSAGE, x_("")}
1456 };
1457 DIALOG db_severeerrordialog = {{50,75,163,341}, N_("Fatal Error"), 0, 4, db_severeerrordialogitems, 0, 0};
1458 };
1459 
1460 #define DSER_EXIT      1		/* Exit (button) */
1461 #define DSER_SAVE      2		/* Save (button) */
1462 #define DSER_CONTINUE  3		/* Continue (button) */
1463 #define DSER_ERRMSG    4		/* Error message (stat text) */
1464 
error(CHAR * s,...)1465 void error(CHAR *s, ...)
1466 {
1467 	va_list ap;
1468 	CHAR line[500];
1469 	REGISTER INTBIG itemHit, *curstate;
1470 	REGISTER INTBIG retval;
1471 	REGISTER LIBRARY *lib;
1472 	REGISTER void *dia;
1473 
1474 	/* disable any session logging */
1475 	if (us_logplay != NULL)
1476 	{
1477 		xclose(us_logplay);
1478 		us_logplay = NULL;
1479 	}
1480 
1481 	/* build the error message */
1482 	var_start(ap, s);
1483 	evsnprintf(line, 500, s, ap);
1484 	va_end(ap);
1485 
1486 	/* display the severe error dialog box */
1487 	dia = DiaInitDialog(&db_severeerrordialog);
1488 	if (dia) return;
1489 
1490 	/* load the message */
1491 	DiaSetText(dia, DSER_ERRMSG, line);
1492 
1493 	/* loop until done */
1494 	for(;;)
1495 	{
1496 		itemHit = DiaNextHit(dia);
1497 		if (itemHit == DSER_EXIT) exitprogram();
1498 		if (itemHit == DSER_SAVE)
1499 		{
1500 			/* save libraries: make sure that backups are kept */
1501 			curstate = io_getstatebits();
1502 			if ((curstate[0]&BINOUTBACKUP) == BINOUTNOBACK)
1503 			{
1504 				curstate[0] |= BINOUTONEBACK;
1505 				io_setstatebits(curstate);
1506 			}
1507 
1508 			/* save each modified library */
1509 			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1510 			{
1511 				if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
1512 				if ((lib->userbits&(LIBCHANGEDMAJOR|LIBCHANGEDMINOR)) == 0) continue;
1513 
1514 				/* save the library in binary format */
1515 				makeoptionstemporary(lib);
1516 				retval = asktool(io_tool, x_("write"), (INTBIG)lib, (INTBIG)x_("binary"));
1517 				restoreoptionstate(lib);
1518 				if (retval != 0) return;
1519 			}
1520 		}
1521 		if (itemHit == DSER_CONTINUE) break;
1522 	}
1523 	DiaDoneDialog(dia);
1524 }
1525 
1526 /*
1527  * Routine to get the environment variable "name" and return its value.
1528  */
egetenv(CHAR * name)1529 CHAR *egetenv(CHAR *name)
1530 {
1531 	return(0);
1532 }
1533 
1534 /*
1535  * Routine to get the current language that Electric speaks.
1536  */
elanguage(void)1537 CHAR *elanguage(void)
1538 {
1539 #ifdef INTERNATIONAL
1540 	return(x_("fr"));
1541 #else
1542 	return(x_("en"));
1543 #endif
1544 }
1545 
1546 /*
1547  * Routine to fork a new process.  Returns the child process number if this is the
1548  * parent thread.  Returns 0 if this is the child thread.
1549  * Returns 1 if forking is not possible (process 1 is INIT on UNIX and can't possibly
1550  * be assigned to a normal process).
1551  */
efork(void)1552 INTBIG efork(void)
1553 {
1554 	return(1);
1555 }
1556 
1557 /*
1558  * Routine to run the string "command" in a shell.
1559  * Returns nonzero if the command cannot be run.
1560  */
esystem(CHAR * command)1561 INTBIG esystem(CHAR *command)
1562 {
1563 	return(1);
1564 }
1565 
1566 /*
1567  * Routine to execute the program "program" with the arguments "args"
1568  */
eexec(CHAR * program,CHAR * args[])1569 void eexec(CHAR *program, CHAR *args[])
1570 {
1571 }
1572 
1573 /*
1574  * routine to send signal "signal" to process "process".
1575  */
ekill(INTBIG process)1576 INTBIG ekill(INTBIG process)
1577 {
1578 	return(1);
1579 }
1580 
1581 /*
1582  * routine to wait for the completion of child process "process"
1583  */
ewait(INTBIG process)1584 void ewait(INTBIG process)
1585 {
1586 }
1587 
1588 /*
1589  * Routine to return the number of processors on this machine.
1590  */
enumprocessors(void)1591 INTBIG enumprocessors(void)
1592 {
1593 	SYSTEM_INFO si;
1594 
1595 	GetSystemInfo(&si);
1596 	return(si.dwNumberOfProcessors);
1597 }
1598 
1599 #define MFCTHREADS 1			/* comment out to use WIN32 versions */
1600 
1601 #define MUTEXCLASS CSemaphore
1602 //#define MUTEXCLASS CCriticalSection
1603 //#define MUTEXCLASS CSingleLock
1604 
1605 /*
1606  * Routine to create a new thread that calls "function" with "argument".
1607  */
enewthread(void * (* function)(void *),void * argument)1608 void enewthread(void* (*function)(void*), void *argument)
1609 {
1610 #ifdef MFCTHREADS
1611 	CWinThread *thread = AfxBeginThread((AFX_THREADPROC)function, argument);
1612 #else
1613 	UINTBIG threadid;
1614 
1615 	if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)function, argument, 0, &threadid) == 0)
1616 	{
1617 		/* function failed */
1618 		ttyputmsg(x_("Failed to create a thread"));
1619 	}
1620 #endif
1621 }
1622 
1623 /*
1624  * Routine that creates a mutual-exclusion object and returns it.
1625  */
emakemutex(void)1626 void *emakemutex(void)
1627 {
1628 #ifdef MFCTHREADS
1629 	MUTEXCLASS *mutex;
1630 
1631 	mutex = new MUTEXCLASS();
1632 	mutex->Unlock();
1633 	return((void *)mutex);
1634 #else
1635 	HANDLE mutex;
1636 	static INTBIG mutexindex = 1;
1637 	CHAR mutexname[50];
1638 
1639 	esnprintf(mutexname, 50, x_("mutex%ld"), mutexindex++);
1640 	mutex = ::CreateMutex(NULL, FALSE, mutexname);
1641 	return((void *)mutex);
1642 #endif
1643 }
1644 
1645 /*
1646  * Routine that locks mutual-exclusion object "vmutex".  If the object is already
1647  * locked, the routine blocks until it is unlocked.
1648  */
emutexlock(void * vmutex)1649 void emutexlock(void *vmutex)
1650 {
1651 #ifdef MFCTHREADS
1652 	MUTEXCLASS *mutex;
1653 
1654 	mutex = (MUTEXCLASS *)vmutex;
1655 	mutex->Lock();
1656 #else
1657 	HANDLE mutex;
1658 	int result;
1659 
1660 	mutex = (HANDLE)vmutex;
1661 	result = WaitForSingleObject(mutex, INFINITE);
1662 	if (result != WAIT_OBJECT_0)
1663 		ttyputerr(x_("Wait on mutex %ld returned %ld"), mutex, result);
1664 #endif
1665 }
1666 
1667 /*
1668  * Routine that unlocks mutual-exclusion object "vmutex".
1669  */
emutexunlock(void * vmutex)1670 void emutexunlock(void *vmutex)
1671 {
1672 #ifdef MFCTHREADS
1673 	MUTEXCLASS *mutex;
1674 
1675 	mutex = (MUTEXCLASS *)vmutex;
1676 	mutex->Unlock();
1677 #else
1678 	HANDLE mutex;
1679 
1680 	mutex = (HANDLE)vmutex;
1681 	ReleaseMutex(mutex);
1682 #endif
1683 }
1684 
1685 /*
1686  * Routine to determine the list of printers and return it.
1687  * The list terminates with a zero.
1688  */
eprinterlist(void)1689 CHAR **eprinterlist(void)
1690 {
1691 	static CHAR *lplist[1];
1692 
1693 	lplist[0] = 0;
1694 	return(lplist);
1695 }
1696 
1697 /*
1698  * Routine to establish the library directories from the environment.
1699  * The registry entry for this is stored in:
1700  *   HKEY_LOCAL_MACHINE\Software\Static Free Software\Electric\ELECTRIC_LIBDIR
1701  * If this directory is valid (i.e. has the file "cadrc" in it) then it is
1702  * used.  If the directory is not valid, the registry entry is removed.
1703  */
setupenvironment(void)1704 void setupenvironment(void)
1705 {
1706 	CHAR tmp[256], subkey[256], value[256], elibname[256], testfile[256], applocation[256];
1707 	REGISTER INTBIG doupdate;
1708 	FILE *io;
1709 
1710 	/* default library location */
1711 	(void)allocstring(&el_libdir, LIBDIR, db_cluster);
1712 
1713 	/* see if the registry has an overriding library directory path */
1714 	estrcpy(subkey, x_("Software\\Static Free Software\\Electric"));
1715 	estrcpy(value, x_("ELECTRIC_LIBDIR"));
1716 	if (gra_getregistry(HKEY_LOCAL_MACHINE, subkey, value, tmp) == 0)
1717 	{
1718 		if (tmp[0] != 0)
1719 		{
1720 			estrcpy(testfile, tmp);
1721 			estrcat(testfile, x_("cadrc"));
1722 			io = efopen(testfile, x_("r"));
1723 			if (io != 0)
1724 			{
1725 				/* valid "cadrc" file in directory, use it */
1726 				fclose(io);
1727 				(void)reallocstring(&el_libdir, tmp, db_cluster);
1728 			} else
1729 			{
1730 				/* key points to bogus directory: delete it */
1731 				(void)gra_setregistry(HKEY_LOCAL_MACHINE, subkey, value, x_(""));
1732 			}
1733 		}
1734 	}
1735 
1736 	/*
1737 	 * linking an extension to an application is described in the article:
1738 	 * "Fusing Your Applications to the System Through the Windows95 Shell"
1739 	 */
1740 
1741 	/* see if the ".elib" extension is defined */
1742 	estrcpy(subkey, x_(".elib"));
1743 	if (gra_getregistry(HKEY_CLASSES_ROOT, subkey, x_(""), elibname) == 0)
1744 	{
1745 		/* determine location of application */
1746 		GetModuleFileName(0, applocation, 256);
1747 
1748 		/* see what the registry thinks is the application location */
1749 		doupdate = 1;
1750 		esnprintf(subkey, 256, x_("%s\\shell\\open\\command\\"), elibname);
1751 		if (gra_getregistry(HKEY_CLASSES_ROOT, subkey, x_(""), tmp) == 0)
1752 		{
1753 			if (estrcmp(applocation, tmp) == 0) doupdate = 0;
1754 		}
1755 
1756 		/* if the application location must be updated, do it now */
1757 		if (doupdate != 0)
1758 		{
1759 			if (gra_setregistry(HKEY_CLASSES_ROOT, subkey, x_(""), applocation) != 0)
1760 				ttyputerr(_("Error setting key"));
1761 		}
1762 	}
1763 
1764 	/* set the machine name */
1765 	nextchangequiet();
1766 	(void)setval((INTBIG)us_tool, VTOOL, x_("USER_machine"),
1767 		(INTBIG)x_("Windows"), VSTRING|VDONTSAVE);
1768 }
1769 
1770 /*
1771  * Routine to change the global "el_libdir" as well as the registry.
1772  */
setlibdir(CHAR * libdir)1773 void setlibdir(CHAR *libdir)
1774 {
1775 	CHAR *pp, tmp[256], subkey[256], value[256];
1776 	BOOLEAN doupdate;
1777 	REGISTER void *infstr;
1778 
1779 	/* make sure UNIX '/' isn't in use */
1780 	for(pp = libdir; *pp != 0; pp++)
1781 		if (*pp == '/') *pp = DIRSEP;
1782 
1783 	infstr = initinfstr();
1784 	libdir = efullpath(tmp, libdir, 256);
1785 	addstringtoinfstr(infstr, libdir);
1786 	if (libdir[estrlen(libdir)-1] != DIRSEP) addtoinfstr(infstr, DIRSEP);
1787 	(void)reallocstring(&el_libdir, returninfstr(infstr), db_cluster);
1788 
1789 	/* see what the current registry key says */
1790 	doupdate = TRUE;
1791 	estrcpy(subkey, x_("Software\\Static Free Software\\Electric"));
1792 	estrcpy(value, x_("ELECTRIC_LIBDIR"));
1793 	if (gra_getregistry(HKEY_LOCAL_MACHINE, subkey, value, tmp) == 0)
1794 	{
1795 		/* if the current registry entry is correct, do not update */
1796 		if (estrcmp(el_libdir, tmp) == 0) doupdate = FALSE;
1797 	}
1798 
1799 	/* if the key must be updated, do it now */
1800 	if (doupdate)
1801 	{
1802 		if (gra_setregistry(HKEY_LOCAL_MACHINE, subkey, value, el_libdir) != 0)
1803 			ttyputerr(_("Error setting key"));
1804 	}
1805 }
1806 
1807 /*
1808  * Routine to querry key "key", subkey "subkey", value "value" of the registry and
1809  * return the value found in "result".  Returns zero if successful, nonzero if the value
1810  * was not found in the registry.
1811  */
gra_getregistry(HKEY key,CHAR * subkey,CHAR * value,CHAR * result)1812 INTBIG gra_getregistry(HKEY key, CHAR *subkey, CHAR *value, CHAR *result)
1813 {
1814 	HKEY hKey;
1815 	DWORD size;
1816 	INTBIG retval, err;
1817 
1818 	if (RegOpenKeyEx(key, subkey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) return(1);
1819 	size = 256;
1820 	err = RegQueryValueEx(hKey, value, NULL, NULL, (UCHAR1 *)result, &size);
1821 	if (err == ERROR_SUCCESS) retval = 0; else
1822  		retval = 1;
1823 	RegCloseKey(hKey);
1824 	return(retval);
1825 }
1826 
1827 /*
1828  * Routine to set key "key", subkey "subkey", value "value" of the registry to
1829  * the string in "newstring".  Returns zero if successful, nonzero if the registry
1830  * was not changed.
1831  */
gra_setregistry(HKEY key,CHAR * subkey,CHAR * value,CHAR * newstring)1832 INTBIG gra_setregistry(HKEY key, CHAR *subkey, CHAR *value, CHAR *newstring)
1833 {
1834 	HKEY hKey;
1835 	INTBIG err;
1836 
1837 	if (RegCreateKey(key, subkey, &hKey) != ERROR_SUCCESS) return(1);
1838 	err = RegSetValueEx(hKey, value, 0, REG_SZ, (UCHAR1 *)newstring,
1839 		estrlen(newstring)*SIZEOFCHAR);
1840 	RegCloseKey(hKey);
1841 	if (err != 0) return(1);
1842 	return(0);
1843 }
1844 
1845 /*
1846  * Routine to force the module to return with some event so that the user interface will
1847  * relinquish control and let a slice happen.
1848  */
forceslice(void)1849 void forceslice(void)
1850 {
1851 }
1852 
1853 /******************** MESSAGES WINDOW ROUTINES ********************/
1854 
1855 /*
1856  * Routine to put the string "s" into the messages window.
1857  * Pops up the messages window if "important" is true.
1858  */
putmessagesstring(CHAR * s,BOOLEAN important)1859 void putmessagesstring(CHAR *s, BOOLEAN important)
1860 {
1861 	REGISTER INTBIG line, pos1, pos2;
1862 
1863 	/* ensure window exists, is noniconic, and is on top */
1864 	if (important)
1865 	{
1866 		if (gra_messageswindow == 0)
1867 		   (void)gra_makemessageswindow();
1868 		if (gra_messageswindow->IsIconic())
1869 			gra_messageswindow->ShowWindow(SW_RESTORE);
1870 		if (gra_messagesnotvisible())
1871 			gra_messageswindow->BringWindowToTop();
1872 	}
1873 
1874 	/* get next line and load it up */
1875 	if (gra_messageswindow == 0) return;
1876 
1877 	/* if messages buffer is running out of space, clear from the top */
1878 	line = gra_editCtrl->GetLineCount();
1879 	pos1 = gra_editCtrl->LineIndex(line-1);
1880 	pos2 = gra_editCtrl->GetLimitText();
1881 	if (pos2-pos1 < 2000)
1882 	{
1883 		gra_editCtrl->SetSel(0, 2000);
1884 		gra_editCtrl->ReplaceSel(x_("..."));
1885 	}
1886 
1887 	gra_tomessagesbottom();
1888 	gra_editCtrl->ReplaceSel(s);
1889 	gra_editCtrl->ReplaceSel(x_("\r\n"));
1890 }
1891 
gra_tomessagesbottom(void)1892 void gra_tomessagesbottom(void)
1893 {
1894 	int pos;
1895 
1896 	pos = gra_editCtrl->GetLimitText();
1897 	gra_editCtrl->SetSel(pos, pos);
1898 }
1899 
1900 /*
1901  * Routine to return the name of the key that ends a session from the messages window.
1902  */
getmessageseofkey(void)1903 CHAR *getmessageseofkey(void)
1904 {
1905 	return(_("ESC"));
1906 }
1907 
1908 /*
1909  * Routine to get a string from the scrolling messages window.  Returns zero if end-of-file
1910  * (^D) is typed.
1911  */
getmessagesstring(CHAR * prompt)1912 CHAR *getmessagesstring(CHAR *prompt)
1913 {
1914 	long line, pos, startpos, startposonline, charcount, pos2, i;
1915 	CHAR cTmpCh, *pt, ch[2];
1916 	static CHAR typedline[MAXTYPEDLINE];
1917 
1918 	/* ensure window exists, is noniconic, and is on top */
1919 	if (gra_messageswindow == 0)
1920 	   (void)gra_makemessageswindow();
1921 	if (gra_messageswindow->IsIconic())
1922 		gra_messageswindow->ShowWindow(SW_RESTORE);
1923 	if (gra_messagesnotvisible())
1924 		gra_messageswindow->BringWindowToTop();
1925 	flushscreen();
1926 
1927 	/* advance to next line and allocate maximum number of characters */
1928 	gra_messageswindow->ActivateFrame();
1929 	gra_tomessagesbottom();
1930 	gra_editCtrl->ReplaceSel(prompt);
1931 	gra_editCtrl->GetSel(startpos, pos2);
1932 	line = gra_editCtrl->GetLineCount() - 1;
1933 	startposonline = estrlen(prompt);
1934 	for(;;)
1935 	{
1936 		/* continue to force the messages window up */
1937 		if (gra_messageswindow == 0)
1938 		   (void)gra_makemessageswindow();
1939 		if (gra_messageswindow->IsIconic())
1940 			gra_messageswindow->ShowWindow(SW_RESTORE);
1941 
1942 		gra_nextevent();
1943 		if (gra_inputstate == NOEVENT) continue;
1944 		if ((gra_inputstate&ISKEYSTROKE) != 0 && gra_inputspecial == 0)
1945 		{
1946 			cTmpCh = (CHAR)(gra_inputstate & CHARREAD);
1947 			if (cTmpCh == CTRLDKEY || cTmpCh == ESCKEY)
1948 			{
1949 				/* ESC (eof) */
1950 				gra_editCtrl->GetSel(pos, pos2);
1951 				if (startpos == pos)
1952 				{
1953 					gra_editCtrl->ReplaceSel(x_("\r\n"));
1954 					gra_inputstate = NOEVENT;
1955 					return(NULL);
1956 				}
1957 				cTmpCh = 0x0D;
1958 			}
1959 			if (cTmpCh == BACKSPACEKEY)
1960 			{
1961 				/* backspace */
1962 				gra_editCtrl->GetSel(pos, pos2);
1963 				if (pos <= startpos) continue;
1964 				gra_editCtrl->SetSel(pos-1, pos);
1965 				ch[0] = 0;
1966 				gra_editCtrl->ReplaceSel(ch);
1967 				continue;
1968 			}
1969 			if (cTmpCh == 0x0A || cTmpCh == 0x0D)
1970 			{
1971 				/* end of line */
1972 				charcount = gra_editCtrl->GetLine(line, typedline, MAXTYPEDLINE);
1973 				typedline[charcount] = 0;
1974 				for(i=startposonline; typedline[i] != 0; i++)
1975 					if (typedline[i] == '\r' || typedline[i] == '\n')
1976 				{
1977 					typedline[i] = 0;
1978 					break;
1979 				}
1980 				pt = &typedline[startposonline];
1981 				gra_inputstate = NOEVENT;
1982 				return(pt);
1983 			}
1984 			gra_tomessagesbottom();
1985 			ch[0] = cTmpCh;   ch[1] = 0;
1986 			gra_editCtrl->ReplaceSel(ch);
1987 		}
1988 		gra_inputstate = NOEVENT;
1989 	}
1990 }
1991 
1992 /*
1993  * routine to select fonts in the messages window
1994  */
setmessagesfont(void)1995 void setmessagesfont(void)
1996 {
1997 	CFont *fnt;
1998 	CFontDialog dlg;
1999 	LOGFONT lf;
2000 
2001 	if (dlg.DoModal() == IDOK)
2002 	{
2003 		/* Retrieve the dialog data */
2004 		dlg.GetCurrentFont(&lf);
2005 		fnt = new CFont();
2006 		fnt->CreateFontIndirect(&lf);
2007 		gra_editCtrl->SetFont(fnt);
2008 	}
2009 }
2010 
2011 /*
2012  * routine to cut text from the messages window if it is current.  Returns true
2013  * if sucessful.
2014  */
cutfrommessages(void)2015 BOOLEAN cutfrommessages(void)
2016 {
2017 	if (gra_messageswindow != 0)
2018 	{
2019 		if (!gra_messageswindow->IsIconic())
2020 		{
2021 			CMDIFrameWnd *parent = gra_messageswindow->GetMDIFrame();
2022 			CWnd *wnd = parent->MDIGetActive();
2023 			if (gra_messageswindow == wnd)
2024 			{
2025 				/* do the cut */
2026 				gra_editCtrl->Cut();
2027 				return(TRUE);
2028 			}
2029 		}
2030 	}
2031 	if (el_curwindowpart == NOWINDOWPART) return(FALSE);
2032 	if ((el_curwindowpart->state&WINDOWTYPE) == DISPWINDOW)
2033 		gra_copyhighlightedtoclipboard();
2034 	return(FALSE);
2035 }
2036 
2037 /*
2038  * routine to copy text from the messages window if it is current.  Returns true
2039  * if sucessful.
2040  */
copyfrommessages(void)2041 BOOLEAN copyfrommessages(void)
2042 {
2043 	if (gra_messageswindow != 0)
2044 	{
2045 		if (!gra_messageswindow->IsIconic())
2046 		{
2047 			CMDIFrameWnd *parent = gra_messageswindow->GetMDIFrame();
2048 			CWnd *wnd = parent->MDIGetActive();
2049 			if (gra_messageswindow == wnd)
2050 			{
2051 				/* do the copy */
2052 				gra_editCtrl->Copy();
2053 				return(TRUE);
2054 			}
2055 		}
2056 	}
2057 	if (el_curwindowpart == NOWINDOWPART) return(FALSE);
2058 	if ((el_curwindowpart->state&WINDOWTYPE) == DISPWINDOW)
2059 		gra_copyhighlightedtoclipboard();
2060 	return(FALSE);
2061 }
2062 
2063 /*
2064  * Routine to build the bigger offscreen buffer for copying at higher resolution.
2065  */
gra_getbiggeroffscreenbuffer(WINDOWFRAME * wf,INTBIG wid,INTBIG hei)2066 BOOLEAN gra_getbiggeroffscreenbuffer(WINDOWFRAME *wf, INTBIG wid, INTBIG hei)
2067 {
2068 	/* see if text buffer needs to be (re)initialized */
2069 	if (gra_biggeroffbufinited)
2070 	{
2071 		if (wid > gra_biggeroffbufwid || hei > gra_biggeroffbufhei)
2072 		{
2073 			efree((CHAR *)gra_biggeroffrowstart);
2074 			efree((CHAR *)gra_biggeroffbitmapinfo);
2075 			DeleteObject(gra_biggeroffbitmap);
2076 			delete gra_biggeroffhdc;
2077 			gra_biggeroffbufinited = FALSE;
2078 		}
2079 	}
2080 
2081 	/* allocate text buffer if needed */
2082 	if (!gra_biggeroffbufinited)
2083 	{
2084 		if (gra_buildoffscreenbuffer(wf, wid, hei, &gra_biggeroffhdc, &gra_biggeroffbitmap,
2085 			&gra_biggeroffbitmapinfo, &gra_biggeroffdatabuffer, &gra_biggeroffrowstart)) return(TRUE);
2086 
2087 		/* remember information about text buffer */
2088 		gra_biggeroffbufwid = wid;   gra_biggeroffbufhei = hei;
2089 		gra_biggeroffbufinited = TRUE;
2090 	}
2091 	return(FALSE);
2092 }
2093 
2094 /*
2095  * routine to copy the highlighted graphics to the clipboard.
2096  */
gra_copyhighlightedtoclipboard(void)2097 void gra_copyhighlightedtoclipboard(void)
2098 {
2099 	REGISTER INTBIG wid, hei, *dest, x, y, r, g, b, index, hsize, dsize, rowbytes,
2100 		*redmap, *greenmap, *bluemap, savewid, savehei, bigwid, bighei, resfactor;
2101 	INTBIG lx, hx, ly, hy;
2102 	REGISTER UCHAR1 *source, **saverowstart, *savedata;
2103 	REGISTER UCHAR1 *data, *store;
2104 	REGISTER WINDOWFRAME *wf;
2105 	REGISTER VARIABLE *varred, *vargreen, *varblue, *var;
2106 	HGLOBAL h;
2107 	BITMAPINFOHEADER *bmiHeader;
2108 	BITMAPINFO *savebmiinfo;
2109 	HBITMAP savebitmap;
2110 	BOOLEAN savedirty;
2111 	CDC *saveoff;
2112 
2113 	/* determine the size of the area to copy */
2114 	if (us_getareabounds(&lx, &hx, &ly, &hy) == NONODEPROTO) return;
2115 
2116 	/* get the copy resolution scale factor */
2117 	var = getval((INTBIG)io_tool, VTOOL, VINTEGER, x_("IO_print_resolution_scale"));
2118 	if (var == NOVARIABLE) resfactor = 1; else
2119 		resfactor = var->addr;
2120 
2121 	/* make the window bigger */
2122 	wf = el_curwindowpart->frame;
2123 	bigwid = wf->swid * resfactor;
2124 	bighei = wf->shei * resfactor;
2125 	if (gra_getbiggeroffscreenbuffer(wf, bigwid, bighei)) return;
2126 	saveoff = (CDC *)wf->hDCOff;     wf->hDCOff = (void *)gra_biggeroffhdc;
2127 	savebitmap = wf->hBitmap;        wf->hBitmap = gra_biggeroffbitmap;
2128 	savebmiinfo = wf->bminfo;        wf->bminfo = gra_biggeroffbitmapinfo;
2129 	savedata = wf->data;             wf->data = gra_biggeroffdatabuffer;
2130 	saverowstart = wf->rowstart;     wf->rowstart = gra_biggeroffrowstart;
2131 	savewid = wf->swid;              wf->swid = bigwid;
2132 	savehei = wf->shei;              wf->shei = bighei;
2133 	savedirty = wf->offscreendirty;  wf->offscreendirty = FALSE;
2134 	wf->revy = wf->shei - 1;
2135 	if (wf->hPalette != 0)
2136 	{
2137 		(void)((CDC *)wf->hDCOff)->SelectPalette((CPalette *)wf->hPalette, TRUE);
2138 		(void)((CDC *)wf->hDCOff)->RealizePalette();
2139 	}
2140 	el_curwindowpart->uselx *= resfactor;
2141 	el_curwindowpart->usehx *= resfactor;
2142 	el_curwindowpart->usely *= resfactor;
2143 	el_curwindowpart->usehy *= resfactor;
2144 	computewindowscale(el_curwindowpart);
2145 
2146 	/* redraw the window in the larger buffer */
2147 	gra_noflush = TRUE;
2148 	us_redisplaynow(el_curwindowpart, TRUE);
2149 	us_endchanges(NOWINDOWPART);
2150 	gra_noflush = FALSE;
2151 
2152 	/* determine the size of the part to copy */
2153 	(void)us_makescreen(&lx, &ly, &hx, &hy, el_curwindowpart);
2154 
2155 	/* restore the normal state of the window */
2156 	wf->hDCOff = saveoff;
2157 	wf->hBitmap = savebitmap;
2158 	wf->bminfo = savebmiinfo;
2159 	wf->data = savedata;
2160 	wf->rowstart = saverowstart;
2161 	wf->swid = savewid;
2162 	wf->shei = savehei;
2163 	wf->revy = wf->shei - 1;
2164 	wf->offscreendirty = savedirty;
2165 	if (wf->hPalette != 0)
2166 	{
2167 		(void)((CDC *)wf->hDCOff)->SelectPalette((CPalette *)wf->hPalette, TRUE);
2168 		(void)((CDC *)wf->hDCOff)->RealizePalette();
2169 	}
2170 	el_curwindowpart->uselx /= resfactor;
2171 	el_curwindowpart->usehx /= resfactor;
2172 	el_curwindowpart->usely /= resfactor;
2173 	el_curwindowpart->usehy /= resfactor;
2174 	computewindowscale(el_curwindowpart);
2175 
2176 	/* get information about the area to be copied */
2177 	wid = hx - lx + 1;
2178 	hei = hy - ly + 1;
2179 	rowbytes = wid*4;
2180 	dsize = rowbytes * hei;
2181 
2182 	/* get colormap information */
2183 	varred   = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
2184 	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
2185 	varblue  = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
2186 	if (varred == NOVARIABLE || vargreen == NOVARIABLE || varblue == NOVARIABLE) return;
2187 	redmap = (INTBIG *)varred->addr;
2188 	greenmap = (INTBIG *)vargreen->addr;
2189 	bluemap = (INTBIG *)varblue->addr;
2190 
2191 	hsize = sizeof (BITMAPINFOHEADER);
2192 	hsize += 12;		/* why is this needed? */
2193 	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, hsize + dsize);
2194 	if (h == 0) return;
2195 	store = *((UCHAR1 **)h);
2196 	bmiHeader = (BITMAPINFOHEADER *)store;
2197 	bmiHeader->biSize = (DWORD)sizeof(BITMAPINFOHEADER);
2198 	bmiHeader->biWidth = wid;
2199 	bmiHeader->biHeight = hei;
2200 	bmiHeader->biPlanes = 1;
2201 	bmiHeader->biBitCount = 32;
2202 	bmiHeader->biCompression = (DWORD)BI_RGB;
2203 	data = &store[hsize];
2204 
2205 	/* load the image data */
2206 	for(y=0; y<hei; y++)
2207 	{
2208 		dest = (INTBIG *)(&data[y * rowbytes]);
2209 		source = &gra_biggeroffrowstart[bighei-1-(y+ly)][lx];
2210 		for(x=0; x<wid; x++)
2211 		{
2212 			index = ((*source++) & ~HIGHLIT) & 0xFF;
2213 			r = redmap[index];
2214 			g = greenmap[index];
2215 			b = bluemap[index];
2216 			*dest++ = b | (g << 8) | (r << 16);
2217 		}
2218 	}
2219 
2220 	/* put the image into the clipboard */
2221 	if (OpenClipboard(0) != 0)
2222 	{
2223 		EmptyClipboard();
2224 		SetClipboardData(CF_DIB, h);
2225 		CloseClipboard();
2226 	}
2227 }
2228 
2229 /*
2230  * routine to paste text to the messages window if it is current.  Returns true
2231  * if sucessful.
2232  */
pastetomessages(void)2233 BOOLEAN pastetomessages(void)
2234 {
2235 	if (gra_messageswindow == 0) return(FALSE);
2236 	if (gra_messageswindow->IsIconic()) return(FALSE);
2237 	CMDIFrameWnd *parent = gra_messageswindow->GetMDIFrame();
2238 	CWnd *wnd = parent->MDIGetActive();
2239 	if (gra_messageswindow != wnd) return(FALSE);
2240 
2241 	/* do the copy */
2242 	gra_editCtrl->Paste();
2243 	return(TRUE);
2244 }
2245 
2246 /*
2247  * routine to get the contents of the system cut buffer
2248  */
getcutbuffer(void)2249 CHAR *getcutbuffer(void)
2250 {
2251 	CHAR *ptr;
2252 	REGISTER void *infstr;
2253 
2254 	if (OpenClipboard(NULL) == 0) return(x_(""));
2255 	ptr = (CHAR *)GetClipboardData(CF_TEXT);
2256 	infstr = initinfstr();
2257 	if (ptr != 0)
2258 		addstringtoinfstr(infstr, ptr);
2259 	CloseClipboard();
2260 	return(returninfstr(infstr));
2261 }
2262 
2263 /*
2264  * routine to set the contents of the system cut buffer to "msg"
2265  */
setcutbuffer(CHAR * msg)2266 void setcutbuffer(CHAR *msg)
2267 {
2268 	HGLOBAL hnd;
2269 
2270 	if (OpenClipboard(NULL) == 0) return;
2271 
2272 	/* Remove the current Clipboard contents */
2273 	if (EmptyClipboard() == 0)
2274 	{
2275 		CloseClipboard();
2276 		return;
2277 	}
2278 
2279 	hnd = GlobalAlloc(GHND, estrlen(msg)+SIZEOFCHAR);
2280 	estrcpy(*((CHAR **)hnd), msg);
2281 	(void)SetClipboardData(CF_TEXT, hnd);
2282 
2283 	CloseClipboard();
2284 }
2285 
2286 /*
2287  * Routine to return true if the messages window is obscured by
2288  * an editor window.  Should ignore edit windows below this!!!
2289  */
gra_messagesnotvisible(void)2290 BOOLEAN gra_messagesnotvisible(void)
2291 {
2292 	RECT mr, r;
2293 	CWnd *wnd;
2294 	WINDOWFRAME *wf;
2295 
2296 	gra_messageswindow->GetWindowRect(&mr);
2297 	mr.top += 2;   mr.bottom -= 2;
2298 	mr.left += 2;  mr.right -= 2;
2299 
2300 	/* look for "previous" windows that may obscure this one */
2301 	wnd = gra_messageswindow;
2302 	for(;;)
2303 	{
2304 		wnd = wnd->GetNextWindow(GW_HWNDPREV);
2305 		if (wnd == 0) break;
2306 
2307 		/* ignore palette */
2308 		for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
2309 			if ((CChildFrame *)wf->wndframe == wnd) break;
2310 		if (wf != NOWINDOWFRAME && wf->floating) continue;
2311 		wnd->GetWindowRect(&r);
2312 		if (r.left < mr.right && r.right > mr.left &&
2313 			r.top < mr.bottom && r.bottom > mr.top) return(TRUE);
2314 	}
2315 	return(FALSE);
2316 }
2317 
2318 /*
2319  * Routine to clear all text from the messages window.
2320  */
clearmessageswindow(void)2321 void clearmessageswindow(void)
2322 {
2323 	CHAR ch[1];
2324 	int pos;
2325 
2326 	if (gra_messageswindow == 0) return;
2327 	pos = gra_editCtrl->GetLimitText();
2328 	gra_editCtrl->SetSel(0, pos);
2329 	ch[0] = 0;
2330 	gra_editCtrl->ReplaceSel(ch);
2331 }
2332 
2333 /******************** STATUS BAR ROUTINES ********************/
2334 
2335 /*
2336  * Routine to return the number of status lines on the display.
2337  */
ttynumstatuslines(void)2338 INTBIG ttynumstatuslines(void)
2339 {
2340 	return(MAXSTATUSLINES);
2341 }
2342 
2343 /*
2344  * Routine to free status field object "sf".
2345  */
ttyfreestatusfield(STATUSFIELD * sf)2346 void ttyfreestatusfield(STATUSFIELD *sf)
2347 {
2348 	INTBIG i, j;
2349 
2350 	for(i=0; i<gra_indicatorcount; i++)
2351 	{
2352 		if (gra_statusfields[i] != sf) continue;
2353 
2354 		/* remove the field */
2355 		efree(gra_statusfieldtext[i]);
2356 		for(j=i+1; j<gra_indicatorcount; j++)
2357 		{
2358 			gra_statusfields[j-1] = gra_statusfields[j];
2359 			gra_statusfieldtext[j-1] = gra_statusfieldtext[j];
2360 		}
2361 		gra_indicatorcount--;
2362 		gra_redrawstatusindicators();
2363 		break;
2364 	}
2365 
2366 	efree(sf->label);
2367 	efree((CHAR *)sf);
2368 }
2369 
2370 /*
2371  * Routine to display "message" in the status "field" of window "wf" (uses all windows
2372  * if "wf" is zero).  If "cancrop" is false, field cannot be cropped and should be
2373  * replaced with "*" if there isn't room.
2374  */
ttysetstatusfield(WINDOWFRAME * wf,STATUSFIELD * field,CHAR * message,BOOLEAN cancrop)2375 void ttysetstatusfield(WINDOWFRAME *wf, STATUSFIELD *field, CHAR *message, BOOLEAN cancrop)
2376 {
2377 	INTBIG len, i, j, pos, newpos;
2378 	CMainFrame *wnd;
2379 
2380 	/* figure out which indicator this is */
2381 	if (field == 0) return;
2382 
2383 	/* construct the status line */
2384 	len = estrlen(field->label);
2385 	if (len + estrlen(message) >= MAXLOCALSTRING)
2386 	{
2387 		estrcpy(gra_localstring, field->label);
2388 		i = MAXLOCALSTRING - len - 1;
2389 		estrncat(gra_localstring, message, i);
2390 		gra_localstring[MAXLOCALSTRING-1] = 0;
2391 	} else
2392 	{
2393 		esnprintf(gra_localstring, 256, x_("%s%s"), field->label, message);
2394 	}
2395 	len = estrlen(gra_localstring);
2396 	while (len > 0 && gra_localstring[len-1] == ' ') gra_localstring[--len] = 0;
2397 
2398 	/* if this is a title bar setting, do it now */
2399 	if (field->line == 0)
2400 	{
2401 		if (wf == NOWINDOWFRAME) return;
2402 		if (*gra_localstring == 0) estrcpy(gra_localstring, _("*** NO CELL ***"));
2403 		((CChildFrame *)wf->wndframe)->SetWindowText(gra_localstring);
2404 		return;
2405 	}
2406 
2407 	/* ignore null fields */
2408 	if (*field->label == 0) return;
2409 
2410 	/* see if this indicator is in the list */
2411 	wnd = (CMainFrame *)AfxGetMainWnd();
2412 	for(i=0; i<gra_indicatorcount; i++)
2413 	{
2414 		if (gra_statusfields[i]->line != field->line ||
2415 			gra_statusfields[i]->startper != field->startper ||
2416 			gra_statusfields[i]->endper != field->endper) continue;
2417 
2418 		/* load the string */
2419 		(void)reallocstring(&gra_statusfieldtext[i], gra_localstring, db_cluster);
2420 		wnd->m_wndStatusBar.SetPaneText(i+1, gra_localstring);
2421 		return;
2422 	}
2423 
2424 	/* code cannot be called by multiple procesors: uses globals */
2425 	NOT_REENTRANT;
2426 
2427 	/* not found: find where this field fits in the order */
2428 	newpos = (field->line-1) * 100 + field->startper;
2429 	for(i=0; i<gra_indicatorcount; i++)
2430 	{
2431 		pos = (gra_statusfields[i]->line-1) * 100 + gra_statusfields[i]->startper;
2432 		if (newpos < pos) break;
2433 	}
2434 	for(j=gra_indicatorcount; j>i; j--)
2435 	{
2436 		gra_statusfieldtext[j] = gra_statusfieldtext[j-1];
2437 		gra_statusfields[j] = gra_statusfields[j-1];
2438 	}
2439 	gra_statusfields[i] = field;
2440 	(void)allocstring(&gra_statusfieldtext[i], gra_localstring, db_cluster);
2441 	gra_indicatorcount++;
2442 
2443 	/* reload all indicators */
2444 	gra_redrawstatusindicators();
2445 }
2446 
gra_redrawstatusindicators(void)2447 void gra_redrawstatusindicators(void)
2448 {
2449 	INTBIG i, wid, screenwid, totalwid;
2450 	RECT r;
2451 	CMainFrame *wnd;
2452 
2453 	wnd = (CMainFrame *)AfxGetMainWnd();
2454 	wnd->GetClientRect(&r);
2455 	screenwid = r.right - r.left - gra_indicatorcount * 5;
2456 	wnd->m_wndStatusBar.SetIndicators(gra_indicators, gra_indicatorcount+1);
2457 	totalwid = 0;
2458 	for(i=0; i<gra_indicatorcount; i++)
2459 		totalwid += gra_statusfields[i]->endper - gra_statusfields[i]->startper;
2460 	for(i=0; i<=gra_indicatorcount; i++)
2461 	{
2462 		if (i == 0)
2463 		{
2464 			wnd->m_wndStatusBar.SetPaneInfo(i, gra_indicators[i], SBPS_NOBORDERS, 0);
2465 			wnd->m_wndStatusBar.SetPaneText(i, x_(""));
2466 		} else
2467 		{
2468 			wid = gra_statusfields[i-1]->endper - gra_statusfields[i-1]->startper;
2469 			wid = wid * screenwid / totalwid;
2470 			wnd->m_wndStatusBar.SetPaneInfo(i, gra_indicators[i], SBPS_NORMAL, wid);
2471 			wnd->m_wndStatusBar.SetPaneText(i, gra_statusfieldtext[i-1]);
2472 		}
2473 	}
2474 }
2475 
2476 /******************** GRAPHICS CONTROL ROUTINES ********************/
2477 
flushscreen(void)2478 void flushscreen(void)
2479 {
2480 	WINDOWFRAME *wf;
2481 
2482 	if (gra_noflush) return;
2483 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
2484 	{
2485 		if (wf->wndframe == 0) continue;
2486 
2487 		/* if screen has not changed, stop now */
2488 		if (!wf->offscreendirty) continue;
2489 		wf->offscreendirty = FALSE;
2490 
2491 		/* refresh screen */
2492 		wf->hDC = (CDC *)((CChildFrame *)wf->wndframe)->GetDC();
2493 		((CDC *)wf->hDC)->BitBlt(wf->copyleft, wf->copytop, wf->copyright-wf->copyleft,
2494 			   wf->copybottom-wf->copytop, (CDC *)wf->hDCOff, wf->copyleft, wf->copytop, SRCCOPY);
2495 		((CChildFrame *)wf->wndframe)->ReleaseDC((CDC *)wf->hDC);
2496 	}
2497 }
2498 
2499 /*
2500  * Routine to accumulate the rectangle of change to the offscreen PixMap
2501  */
gra_setrect(WINDOWFRAME * wf,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy)2502 void gra_setrect(WINDOWFRAME *wf, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy)
2503 {
2504 	static INTBIG checktimefreq = 0;
2505 
2506 	if (wf->offscreendirty)
2507 	{
2508 		if (lx < wf->copyleft)   wf->copyleft = lx;
2509 		if (hx > wf->copyright)  wf->copyright = hx;
2510 		if (ly < wf->copytop)    wf->copytop = ly;
2511 		if (hy > wf->copybottom) wf->copybottom = hy;
2512 	} else
2513 	{
2514 		wf->copyleft = lx;
2515 		wf->copyright = hx;
2516 		wf->copytop = ly;
2517 		wf->copybottom = hy;
2518 		wf->offscreendirty = TRUE;
2519 		wf->starttime = ticktime();
2520 	}
2521 
2522 	/* flush the screen every two seconds */
2523 	checktimefreq++;
2524 	if (checktimefreq > 10000)
2525 	{
2526 		checktimefreq = 0;
2527 		if (ticktime() - wf->starttime > FLUSHTICKS) flushscreen();
2528 	}
2529 }
2530 
gra_reloadmap(void)2531 void gra_reloadmap(void)
2532 {
2533 	REGISTER VARIABLE *varred, *vargreen, *varblue;
2534 
2535 	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
2536 	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
2537 	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
2538 	if (varred == NOVARIABLE || vargreen == NOVARIABLE || varblue == NOVARIABLE) return;
2539 	colormapload((INTBIG *)varred->addr, (INTBIG *)vargreen->addr, (INTBIG *)varblue->addr, 0, 255);
2540 }
2541 
colormapload(INTBIG * red,INTBIG * green,INTBIG * blue,INTBIG low,INTBIG high)2542 void colormapload(INTBIG *red, INTBIG *green, INTBIG *blue, INTBIG low, INTBIG high)
2543 {
2544 	INTBIG i;
2545 	INTBIG forecolor, backcolor;
2546 	REGISTER WINDOWFRAME *wf;
2547 	RGBQUAD bmiColors[256];
2548 
2549 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
2550 	{
2551 		/* do not touch the color map when in B&W mode */
2552 		if (wf->pColorPalette == 0) continue;
2553 
2554 		i = GetDIBColorTable(((CDC *)wf->hDCOff)->m_hDC, 0, 256, bmiColors);
2555 		for(i=low; i<=high; i++)
2556 		{
2557 			bmiColors[i].rgbRed = (UCHAR1)red[i-low];
2558 			bmiColors[i].rgbGreen = (UCHAR1)green[i-low];
2559 			bmiColors[i].rgbBlue = (UCHAR1)blue[i-low];
2560 			bmiColors[i].rgbReserved = 0;
2561 			if (i == 255)
2562 			{
2563 				bmiColors[i].rgbRed = (UCHAR1)(255-red[i-low]);
2564 				bmiColors[i].rgbGreen = (UCHAR1)(255-green[i-low]);
2565 				bmiColors[i].rgbBlue = (UCHAR1)(255-blue[i-low]);
2566 			}
2567 
2568 			wf->pColorPalette->palPalEntry[i].peRed = bmiColors[i].rgbRed;
2569 			wf->pColorPalette->palPalEntry[i].peGreen = bmiColors[i].rgbGreen;
2570 			wf->pColorPalette->palPalEntry[i].peBlue = bmiColors[i].rgbBlue;
2571 			wf->pColorPalette->palPalEntry[i].peFlags = 0;
2572 		}
2573 		i = SetDIBColorTable(((CDC *)wf->hDCOff)->m_hDC, 0, 256, bmiColors);
2574 
2575 		if (wf->hPalette != 0)
2576 			delete (CPalette *)wf->hPalette;
2577 
2578 		wf->hPalette = (CPalette *)new CPalette();
2579 		((CPalette *)wf->hPalette)->CreatePalette(wf->pColorPalette);
2580 
2581 		/* graphics window */
2582 		((CDC *)wf->hDCOff)->SelectPalette((CPalette *)wf->hPalette, TRUE);
2583 		((CDC *)wf->hDCOff)->RealizePalette();
2584 		gra_setrect(wf, 0, wf->swid-1, 0, wf->shei-1);
2585 	}
2586 
2587 	/* change the messages window to conform */
2588 	if (low == 0 && high == 255 && gra_messageswindow != 0)
2589 	{
2590 		forecolor = (blue[CELLTXT] << 16) | (green[CELLTXT] << 8) | red[CELLTXT];
2591 		backcolor = (blue[0] << 16) | (green[0] << 8) | red[0];
2592 		CElectricMsgView *msgView = (CElectricMsgView *)gra_messageswindow;
2593 		msgView->SetColors((COLORREF)forecolor, (COLORREF)backcolor);
2594 	}
2595 }
2596 
2597 /*
2598  * helper routine to set the cursor shape to "state"
2599  */
setdefaultcursortype(INTBIG state)2600 void setdefaultcursortype(INTBIG state)
2601 {
2602 	if (us_cursorstate == state) return;
2603 
2604 	switch (state)
2605 	{
2606 		case NORMALCURSOR:  SetCursor(gra_normalCurs);   break;
2607 		case WANTTTYCURSOR: SetCursor(gra_wantttyCurs);  break;
2608 		case PENCURSOR:     SetCursor(gra_penCurs);      break;
2609 		case NULLCURSOR:    SetCursor(gra_normalCurs);   break;
2610 		case MENUCURSOR:    SetCursor(gra_menuCurs);     break;
2611 		case HANDCURSOR:    SetCursor(gra_handCurs);     break;
2612 		case TECHCURSOR:    SetCursor(gra_techCurs);     break;
2613 		case IBEAMCURSOR:   SetCursor(gra_ibeamCurs);    break;
2614 		case WAITCURSOR:    SetCursor(gra_waitCurs);     break;
2615 		case LRCURSOR:      SetCursor(gra_lrCurs);       break;
2616 		case UDCURSOR:      SetCursor(gra_udCurs);       break;
2617 	}
2618 	ShowCursor(TRUE);
2619 	us_cursorstate = state;
2620 }
2621 
2622 /*
2623  * Routine to change the default cursor (to indicate modes).
2624  */
setnormalcursor(INTBIG curs)2625 void setnormalcursor(INTBIG curs)
2626 {
2627 	us_normalcursor = curs;
2628 	setdefaultcursortype(us_normalcursor);
2629 }
2630 
2631 /******************** GRAPHICS LINES ********************/
2632 
screendrawline(WINDOWPART * win,INTBIG x1,INTBIG y1,INTBIG x2,INTBIG y2,GRAPHICS * desc,INTBIG texture)2633 void screendrawline(WINDOWPART *win, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2, GRAPHICS *desc, INTBIG texture)
2634 {
2635 	gra_drawline(win, x1, y1, x2, y2, desc, texture);
2636 }
2637 
screeninvertline(WINDOWPART * win,INTBIG x1,INTBIG y1,INTBIG x2,INTBIG y2)2638 void screeninvertline(WINDOWPART *win, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2)
2639 {
2640 	gra_invertline(win, x1, y1, x2, y2);
2641 }
2642 
2643 /******************** GRAPHICS POLYGONS ********************/
2644 
screendrawpolygon(WINDOWPART * win,INTBIG * x,INTBIG * y,INTBIG count,GRAPHICS * desc)2645 void screendrawpolygon(WINDOWPART *win, INTBIG *x, INTBIG *y, INTBIG count, GRAPHICS *desc)
2646 {
2647 	gra_drawpolygon(win, x, y, count, desc);
2648 }
2649 
2650 /******************** GRAPHICS BOXES ********************/
2651 
screendrawbox(WINDOWPART * win,INTBIG lowx,INTBIG highx,INTBIG lowy,INTBIG highy,GRAPHICS * desc)2652 void screendrawbox(WINDOWPART *win, INTBIG lowx, INTBIG highx, INTBIG lowy, INTBIG highy, GRAPHICS *desc)
2653 {
2654 	gra_drawbox(win, lowx, highx, lowy, highy, desc);
2655 }
2656 
2657 /*
2658  * routine to invert the bits in the box from (lowx, lowy) to (highx, highy)
2659  */
screeninvertbox(WINDOWPART * win,INTBIG lowx,INTBIG highx,INTBIG lowy,INTBIG highy)2660 void screeninvertbox(WINDOWPART *win, INTBIG lowx, INTBIG highx, INTBIG lowy, INTBIG highy)
2661 {
2662 	gra_invertbox(win, lowx, highx, lowy, highy);
2663 }
2664 
2665 /*
2666  * routine to move bits on the display starting with the area at
2667  * (sx,sy) and ending at (dx,dy).  The size of the area to be
2668  * moved is "wid" by "hei".
2669  */
screenmovebox(WINDOWPART * win,INTBIG sx,INTBIG sy,INTBIG wid,INTBIG hei,INTBIG dx,INTBIG dy)2670 void screenmovebox(WINDOWPART *win, INTBIG sx, INTBIG sy, INTBIG wid, INTBIG hei, INTBIG dx, INTBIG dy)
2671 {
2672 #ifdef WINSAVEDBOX
2673 	REGISTER INTBIG totop, tobottom, toleft, toright, fromleft, fromtop;
2674 	REGISTER WINDOWFRAME *wf;
2675 
2676 	wf = win->frame;
2677 
2678 	/* setup source rectangle */
2679 	fromleft = sx;
2680 	fromtop = wf->revy + 1 - sy - hei;
2681 
2682 	/* setup destination rectangle */
2683 	toleft = dx;
2684 	toright = toleft + wid;
2685 	totop = wf->revy + 1 - dy - hei;
2686 	tobottom = totop + hei;
2687 
2688 	((CDC *)wf->hDCOff)->BitBlt(toleft, totop, wid, hei, (CDC *)wf->hDCOff,
2689 		fromleft, fromtop, SRCCOPY);
2690 
2691 	gra_setrect(wf, toleft, toright, totop, tobottom);
2692 #else
2693 	gra_movebox(win, sx, sy, wid, hei, dx, dy);
2694 #endif
2695 }
2696 
2697 /*
2698  * routine to save the contents of the box from "lx" to "hx" in X and from
2699  * "ly" to "hy" in Y.  A code is returned that identifies this box for
2700  * overwriting and restoring.  The routine returns -1 if there is a error.
2701  */
screensavebox(WINDOWPART * win,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy)2702 INTBIG screensavebox(WINDOWPART *win, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy)
2703 {
2704 #ifdef WINSAVEDBOX
2705 	SAVEDBOX *box;
2706 	REGISTER INTBIG xsize, ysize;
2707 	REGISTER INTBIG i, x, y;
2708 	REGISTER WINDOWFRAME *wf;
2709 	REGISTER UCHAR1 *source, *dest;
2710 
2711 	wf = win->frame;
2712 	i = ly;
2713 	ly = wf->revy-hy;
2714 	hy = wf->revy-i;
2715 	xsize = hx-lx+1;
2716 	ysize = hy-ly+1;
2717 	box = (SAVEDBOX *)emalloc((sizeof (SAVEDBOX)), us_tool->cluster);
2718 	if (box == 0) return(-1);
2719 
2720 	if (gra_buildoffscreenbuffer(wf, xsize, ysize, &box->hMemDC,
2721 		&box->hBox, &box->bminfo, &box->data, &box->rowstart)) return(-1);
2722 
2723 	box->win = win;
2724 	box->nextsavedbox = gra_firstsavedbox;
2725 	gra_firstsavedbox = box;
2726 	box->lx = lx;           box->hx = hx;
2727 	box->ly = ly;           box->hy = hy;
2728 
2729 	/* move the bits */
2730 	for(y=0; y<ysize; y++)
2731 	{
2732 		source = &wf->rowstart[ly+y][lx];
2733 		dest = box->rowstart[y];
2734 		for(x=0; x<xsize; x++) *dest++ = *source++;
2735 	}
2736 
2737 	return((INTBIG)box);
2738 #else
2739 	return(gra_savebox(win, lx, hx, ly, hy));
2740 #endif
2741 }
2742 
2743 /*
2744  * routine to shift the saved box "code" so that it is restored in a different location,
2745  * offset by (dx,dy)
2746  */
screenmovesavedbox(INTBIG code,INTBIG dx,INTBIG dy)2747 void screenmovesavedbox(INTBIG code, INTBIG dx, INTBIG dy)
2748 {
2749 #ifdef WINSAVEDBOX
2750 	REGISTER SAVEDBOX *box;
2751 
2752 	if (code == -1) return;
2753 	box = (SAVEDBOX *)code;
2754 	box->lx += dx;       box->hx += dx;
2755 	box->ly -= dy;       box->hy -= dy;
2756 #else
2757 	gra_movesavedbox(code, dx, dy);
2758 #endif
2759 }
2760 
2761 /*
2762  * routine to restore saved box "code" to the screen.  "destroy" is:
2763  *  0   restore box, do not free memory
2764  *  1   restore box, free memory
2765  * -1   free memory
2766  */
screenrestorebox(INTBIG code,INTBIG destroy)2767 void screenrestorebox(INTBIG code, INTBIG destroy)
2768 {
2769 #ifdef WINSAVEDBOX
2770 	REGISTER SAVEDBOX *box, *lbox, *tbox;
2771 	REGISTER INTBIG x, y, xsize, ysize;
2772 	REGISTER UCHAR1 *source, *dest;
2773 	REGISTER WINDOWFRAME *wf;
2774 
2775 	/* get the box */
2776 	if (code == -1) return;
2777 	box = (SAVEDBOX *)code;
2778 
2779 	/* move the bits */
2780 	if (destroy >= 0)
2781 	{
2782 		wf = box->win->frame;
2783 		xsize = box->hx - box->lx + 1;
2784 		ysize = box->hy - box->ly + 1;
2785 		for(y=0; y<ysize; y++)
2786 		{
2787 			dest = &wf->rowstart[box->ly+y][box->lx];
2788 			source = box->rowstart[y];
2789 			for(x=0; x<xsize; x++) *dest++ = *source++;
2790 		}
2791 		efree((CHAR *)box->rowstart);
2792 		efree((CHAR *)box->bminfo);
2793 		DeleteObject(box->hBox);
2794 		delete box->hMemDC;
2795 		gra_setrect(wf, box->lx, box->hx+1, box->ly, box->hy+1);
2796 	}
2797 
2798 	/* destroy this box's memory if requested */
2799 	if (destroy != 0)
2800 	{
2801 		lbox = NOSAVEDBOX;
2802 		for(tbox = gra_firstsavedbox; tbox != NOSAVEDBOX; tbox = tbox->nextsavedbox)
2803 		{
2804 			if (tbox == box)
2805 				break;
2806 			lbox = tbox;
2807 		}
2808 		if (lbox == NOSAVEDBOX)
2809 			gra_firstsavedbox = box->nextsavedbox;
2810 		else
2811 			lbox->nextsavedbox = box->nextsavedbox;
2812 		efree((CHAR *)box);
2813 	}
2814 #else
2815 	(void)gra_restorebox(code, destroy);
2816 #endif
2817 }
2818 
2819 /******************** GRAPHICS TEXT ********************/
2820 
2821 /*
2822  * Routine to find face with name "facename" and to return
2823  * its index in list of used fonts. If font was not used before,
2824  * then it is appended to list of used fonts.
2825  * If font "facename" is not available on the system, -1 is returned.
2826  */
screenfindface(CHAR * facename)2827 INTBIG screenfindface(CHAR *facename)
2828 {
2829 	INTBIG i;
2830 
2831 	for (i = 0; i < gra_numfaces; i++)
2832 		if (namesame(facename, gra_facelist[i]) == 0) return(i);
2833 	return(-1);
2834 }
2835 
2836 /*
2837  * Routine to return the number of typefaces used (when "all" is FALSE)
2838  * or available on the system (when "all" is TRUE)
2839  * and to return their names in the array "list".
2840  * "screenfindface
2841  */
screengetfacelist(CHAR *** list,BOOLEAN all)2842 INTBIG screengetfacelist(CHAR ***list, BOOLEAN all)
2843 {
2844 	CDC *curdc;
2845 	CChildFrame *child;
2846 	REGISTER INTBIG i;
2847 
2848 	if (gra_numfaces == 0)
2849 	{
2850 		if (el_curwindowframe != NOWINDOWFRAME)
2851 			child = (CChildFrame *)el_curwindowframe->wndframe; else
2852 				child = gra_messageswindow;
2853 		curdc = (CDC *)child->GetDC();
2854 		EnumFonts(curdc->m_hDC, NULL, (FONTENUMPROC)gra_enumfaces, (LPARAM)NULL);
2855 		if (gra_numfaces > VTMAXFACE)
2856 		{
2857 			ttyputerr(_("Warning: found %ld fonts, but can only keep track of %d"), gra_numfaces,
2858 				VTMAXFACE);
2859 			for(i=VTMAXFACE; i<gra_numfaces; i++)
2860 			{
2861 				efree((CHAR *)gra_facelist[i]);
2862 				gra_facelist[i] = 0;
2863 			}
2864 			gra_numfaces = VTMAXFACE;
2865 		}
2866 		esort(&gra_facelist[1], gra_numfaces-1, sizeof (CHAR *), sort_stringascending);
2867 	}
2868 	*list = gra_facelist;
2869 	return(gra_numfaces);
2870 }
2871 
2872 /*
2873  * Routine to return the default typeface used on the screen.
2874  */
screengetdefaultfacename(void)2875 CHAR *screengetdefaultfacename(void)
2876 {
2877 	return(x_("Arial"));
2878 }
2879 
2880 /*
2881  * Callback routine to get the number of fonts
2882  */
gra_enumfaces(LPLOGFONT lpLogFont,LPTEXTMETRIC lpTEXTMETRICs,DWORD fFontType,LPVOID lpData)2883 int APIENTRY gra_enumfaces(LPLOGFONT lpLogFont, LPTEXTMETRIC lpTEXTMETRICs, DWORD fFontType, LPVOID lpData)
2884 {
2885 	INTBIG newcellotal, i;
2886 	CHAR **newfacelist;
2887 
2888 	if (gra_numfaces == 0) newcellotal = 2; else
2889 		newcellotal = gra_numfaces + 1;
2890 	newfacelist = (CHAR **)emalloc(newcellotal * (sizeof (CHAR *)), us_tool->cluster);
2891 	if (newfacelist == 0) return(0);
2892 	for(i=0; i<gra_numfaces; i++)
2893 		newfacelist[i] = gra_facelist[i];
2894 	for(i=gra_numfaces; i<newcellotal; i++)
2895 		newfacelist[i] = 0;
2896 	if (gra_numfaces > 0) efree((CHAR *)gra_facelist);
2897 	gra_facelist = newfacelist;
2898 	if (gra_numfaces == 0)
2899 		(void)allocstring(&gra_facelist[gra_numfaces++], _("DEFAULT FACE"), us_tool->cluster);
2900 	if (gra_facelist[gra_numfaces] != 0) efree((CHAR *)gra_facelist[gra_numfaces]);
2901 	(void)allocstring(&gra_facelist[gra_numfaces++], lpLogFont->lfFaceName, us_tool->cluster);
2902 	return(1);
2903 }
2904 
screensettextinfo(WINDOWPART * win,TECHNOLOGY * tech,UINTBIG * descript)2905 void screensettextinfo(WINDOWPART *win, TECHNOLOGY *tech, UINTBIG *descript)
2906 {
2907 	REGISTER WINDOWFRAME *wf;
2908 
2909 	wf = win->frame;
2910 	wf->hFont = 0;
2911 	wf->hFont = gra_gettextfont(win, tech, descript);
2912 	if (wf->hFont == 0) return;
2913 	((CDC *)wf->hDCOff)->SelectObject((CFont *)wf->hFont);
2914 }
2915 
gra_gettextfont(WINDOWPART * win,TECHNOLOGY * tech,UINTBIG * descript)2916 CFont *gra_gettextfont(WINDOWPART *win, TECHNOLOGY *tech, UINTBIG *descript)
2917 {
2918 	static CFont *txteditor = 0, *txtmenu = 0;
2919 	CFont *theFont;
2920 	REGISTER INTBIG font, size, italic, bold, underline, face, i;
2921 	REGISTER UINTBIG hash;
2922 	REGISTER CHAR *facename, **list;
2923 
2924 	gra_texttoosmall = FALSE;
2925 	font = TDGETSIZE(descript);
2926 	if (font == TXTEDITOR)
2927 	{
2928 		if (txteditor == 0) txteditor = gra_createtextfont(12, x_("Lucida Console"), 0, 0, 0);
2929 		return(txteditor);
2930 	}
2931 	if (font == TXTMENU)
2932 	{
2933 		if (txtmenu == 0)   txtmenu = gra_createtextfont(13, x_("MS Sans Serif"), 0, 0, 0);
2934 		return(txtmenu);
2935 	}
2936 	size = truefontsize(font, win, tech);
2937 	if (size < 1)
2938 	{
2939 		gra_texttoosmall = TRUE;
2940 		return(0);
2941 	}
2942 	if (size < MINIMUMTEXTSIZE) size = MINIMUMTEXTSIZE;
2943 
2944 	face = TDGETFACE(descript);
2945 	italic = TDGETITALIC(descript);
2946 	bold = TDGETBOLD(descript);
2947 	underline = TDGETUNDERLINE(descript);
2948 	gra_textrotation = TDGETROTATION(descript);
2949 	if (face == 0 && italic == 0 && bold == 0 && underline == 0)
2950 	{
2951 		if (size >= MAXCACHEDFONTS) size = MAXCACHEDFONTS-1;
2952 		if (gra_fontcache[size] == 0)
2953 			gra_fontcache[size] = gra_createtextfont(size, x_("Arial"), 0, 0, 0);
2954 		return(gra_fontcache[size]);
2955 	} else
2956 	{
2957 		hash = size + 3*italic + 5*bold + 7*underline + 11*face;
2958 		hash %= FONTHASHSIZE;
2959 		for(i=0; i<FONTHASHSIZE; i++)
2960 		{
2961 			if (gra_fonthash[hash].font == 0) break;
2962 			if (gra_fonthash[hash].face == face && gra_fonthash[hash].size == size &&
2963 				gra_fonthash[hash].italic == italic && gra_fonthash[hash].bold == bold &&
2964 				gra_fonthash[hash].underline == underline)
2965 					return(gra_fonthash[hash].font);
2966 			hash++;
2967 			if (hash >= FONTHASHSIZE) hash = 0;
2968 		}
2969 		facename = x_("Arial");
2970 		if (face > 0)
2971 		{
2972 			if (gra_numfaces == 0) (void)screengetfacelist(&list, FALSE);
2973 			if (face < gra_numfaces)
2974 				facename = gra_facelist[face];
2975 		}
2976 		theFont = gra_createtextfont(size, facename, italic, bold, underline);
2977 		if (gra_fonthash[hash].font == 0)
2978 		{
2979 			gra_fonthash[hash].font = theFont;
2980 			gra_fonthash[hash].face = face;
2981 			gra_fonthash[hash].size = size;
2982 			gra_fonthash[hash].italic = italic;
2983 			gra_fonthash[hash].bold = bold;
2984 			gra_fonthash[hash].underline = underline;
2985 		}
2986 		return(theFont);
2987 	}
2988 }
2989 
gra_createtextfont(INTBIG fontSize,CHAR * face,INTBIG italic,INTBIG bold,INTBIG underline)2990 CFont *gra_createtextfont(INTBIG fontSize, CHAR *face, INTBIG italic, INTBIG bold,
2991 	INTBIG underline)
2992 {
2993 	LOGFONT lf;
2994 	CFont *hf;
2995 
2996 	lf.lfHeight = -fontSize;
2997 	estrcpy(lf.lfFaceName, face);
2998 	lf.lfWidth = 0;
2999 	lf.lfEscapement = 0;
3000 	lf.lfOrientation = 0;
3001 	if (bold != 0) lf.lfWeight = FW_BOLD; else
3002 		lf.lfWeight = FW_NORMAL;
3003 	if (italic != 0) lf.lfItalic = 1; else
3004 		lf.lfItalic = 0;
3005 	if (underline != 0) lf.lfUnderline = 1; else
3006 		lf.lfUnderline = 0;
3007 	lf.lfStrikeOut = 0;
3008 	lf.lfCharSet = DEFAULT_CHARSET;
3009 	lf.lfOutPrecision = OUT_STROKE_PRECIS;
3010 	lf.lfClipPrecision = CLIP_STROKE_PRECIS;
3011 	lf.lfQuality = DEFAULT_QUALITY;
3012 	lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
3013 
3014 	hf = new CFont();
3015 	hf->CreateFontIndirect(&lf);
3016 	return(hf);
3017 }
3018 
screengettextsize(WINDOWPART * win,CHAR * str,INTBIG * x,INTBIG * y)3019 void screengettextsize(WINDOWPART *win, CHAR *str, INTBIG *x, INTBIG *y)
3020 {
3021 	INTBIG len;
3022 	CSize textSize;
3023 	REGISTER WINDOWFRAME *wf;
3024 
3025 	if (gra_texttoosmall)
3026 	{
3027 		*x = *y = 0;
3028 		return;
3029 	}
3030 	wf = win->frame;
3031 	len = estrlen(str);
3032 	textSize = ((CDC *)wf->hDCOff)->GetTextExtent(str, len);
3033 	switch (gra_textrotation)
3034 	{
3035 		case 0:			/* normal */
3036 			*x = textSize.cx;
3037 			*y = textSize.cy+1;
3038 			break;
3039 		case 1:			/* 90 degrees counterclockwise */
3040 			*x = -textSize.cy+1;
3041 			*y = textSize.cx;
3042 			break;
3043 		case 2:			/* 180 degrees */
3044 			*x = -textSize.cx;
3045 			*y = -textSize.cy+1;
3046 			break;
3047 		case 3:			/* 90 degrees clockwise */
3048 			*x = textSize.cy+1;
3049 			*y = -textSize.cx;
3050 			break;
3051 	}
3052 }
3053 
screendrawtext(WINDOWPART * win,INTBIG atx,INTBIG aty,CHAR * s,GRAPHICS * desc)3054 void screendrawtext(WINDOWPART *win, INTBIG atx, INTBIG aty, CHAR *s, GRAPHICS *desc)
3055 {
3056 	if (gra_texttoosmall) return;
3057 	gra_drawtext(win, atx, aty, gra_textrotation, s, desc);
3058 }
3059 
3060 /*
3061  * Routine to convert the string "msg" (to be drawn into window "win") into an
3062  * array of pixels.  The size of the array is returned in "wid" and "hei", and
3063  * the pixels are returned in an array of character vectors "rowstart".
3064  * The routine returns true if the operation cannot be done.
3065  */
gettextbits(WINDOWPART * win,CHAR * msg,INTBIG * wid,INTBIG * hei,UCHAR1 *** rowstart)3066 BOOLEAN gettextbits(WINDOWPART *win, CHAR *msg, INTBIG *wid, INTBIG *hei, UCHAR1 ***rowstart)
3067 {
3068 	REGISTER INTBIG i, len, j;
3069 	CSize textSize;
3070 	REGISTER WINDOWFRAME *wf;
3071 	REGISTER UCHAR1 *ptr;
3072 
3073 	/* copy the string and correct it */
3074 	len = estrlen(msg);
3075 	if (len == 0 || gra_texttoosmall)
3076 	{
3077 		*wid = *hei = 0;
3078 		return(FALSE);
3079 	}
3080 	/* determine size of string */
3081 	wf = win->frame;
3082 	textSize = ((CDC *)wf->hDCOff)->GetTextExtent(msg, len);
3083 	*wid = textSize.cx;
3084 	*hei = textSize.cy;
3085 
3086 	/* see if text buffer needs to be (re)initialized */
3087 	if (gra_textbufinited)
3088 	{
3089 		if (*wid > gra_textbufwid || *hei > gra_textbufhei)
3090 		{
3091 			efree((CHAR *)gra_textrowstart);
3092 			efree((CHAR *)gra_textbitmapinfo);
3093 			DeleteObject(gra_textbitmap);
3094 			delete gra_texthdc;
3095 			gra_textbufinited = FALSE;
3096 		}
3097 	}
3098 
3099 	/* allocate text buffer if needed */
3100 	if (!gra_textbufinited)
3101 	{
3102 		if (gra_buildoffscreenbuffer(wf, *wid, *hei, &gra_texthdc, &gra_textbitmap,
3103 			&gra_textbitmapinfo, &gra_textdatabuffer, &gra_textrowstart)) return(TRUE);
3104 		gra_texthdc->SetTextColor(PALETTEINDEX(1));
3105 		gra_texthdc->SetROP2(R2_COPYPEN);
3106 		gra_texthdc->SetBkMode(TRANSPARENT);
3107 		gra_texthdc->SetTextAlign(TA_TOP<<8);
3108 
3109 		/* remember information about text buffer */
3110 		gra_textbufwid = *wid;   gra_textbufhei = *hei;
3111 		gra_textbufinited = TRUE;
3112 	}
3113 
3114 	/* clear the text buffer */
3115 	for(i=0; i < *hei; i++)
3116 	{
3117 		ptr = gra_textrowstart[i];
3118 		for(j=0; j < *wid; j++) *ptr++ = 0;
3119 	}
3120 
3121 	/* write to the text buffer */
3122 	gra_texthdc->SelectObject((CFont *)wf->hFont);
3123 	gra_texthdc->TextOut(0, 0, msg, len);
3124 
3125 	*rowstart = gra_textrowstart;
3126 	return(FALSE);
3127 }
3128 
3129 /******************** CIRCLE DRAWING ********************/
3130 
screendrawcircle(WINDOWPART * win,INTBIG atx,INTBIG aty,INTBIG radius,GRAPHICS * desc)3131 void screendrawcircle(WINDOWPART *win, INTBIG atx, INTBIG aty, INTBIG radius, GRAPHICS *desc)
3132 {
3133 	gra_drawcircle(win, atx, aty, radius, desc);
3134 }
3135 
screendrawthickcircle(WINDOWPART * win,INTBIG atx,INTBIG aty,INTBIG radius,GRAPHICS * desc)3136 void screendrawthickcircle(WINDOWPART *win, INTBIG atx, INTBIG aty, INTBIG radius,
3137 	GRAPHICS *desc)
3138 {
3139 	gra_drawthickcircle(win, atx, aty, radius, desc);
3140 }
3141 
3142 /******************** FILLED CIRCLE DRAWING ********************/
3143 
3144 /*
3145  * routine to draw a filled-in circle of radius "radius"
3146  */
screendrawdisc(WINDOWPART * win,INTBIG atx,INTBIG aty,INTBIG radius,GRAPHICS * desc)3147 void screendrawdisc(WINDOWPART *win, INTBIG atx, INTBIG aty, INTBIG radius, GRAPHICS *desc)
3148 {
3149 	gra_drawdisc(win, atx, aty, radius, desc);
3150 }
3151 
3152 /******************** ARC DRAWING ********************/
3153 
3154 /*
3155  * draws a thin arc centered at (centerx, centery), clockwise,
3156  * passing by (x1,y1) and (x2,y2)
3157  */
screendrawcirclearc(WINDOWPART * win,INTBIG centerx,INTBIG centery,INTBIG p1_x,INTBIG p1_y,INTBIG p2_x,INTBIG p2_y,GRAPHICS * desc)3158 void screendrawcirclearc(WINDOWPART *win, INTBIG centerx, INTBIG centery, INTBIG p1_x, INTBIG p1_y,
3159 	INTBIG p2_x, INTBIG p2_y, GRAPHICS *desc)
3160 {
3161 	gra_drawcirclearc(win, centerx, centery, p1_x, p1_y, p2_x, p2_y, FALSE, desc);
3162 }
3163 
3164 /*
3165  * draws a thick arc centered at (centerx, centery), clockwise,
3166  * passing by (x1,y1) and (x2,y2)
3167  */
screendrawthickcirclearc(WINDOWPART * win,INTBIG centerx,INTBIG centery,INTBIG p1_x,INTBIG p1_y,INTBIG p2_x,INTBIG p2_y,GRAPHICS * desc)3168 void screendrawthickcirclearc(WINDOWPART *win, INTBIG centerx, INTBIG centery, INTBIG p1_x, INTBIG p1_y,
3169 	INTBIG p2_x, INTBIG p2_y, GRAPHICS *desc)
3170 {
3171 	gra_drawcirclearc(win, centerx, centery, p1_x, p1_y, p2_x, p2_y, TRUE, desc);
3172 }
3173 
3174 /******************** GRID CONTROL ********************/
3175 
3176 /*
3177  * grid drawing routine
3178  */
screendrawgrid(WINDOWPART * win,POLYGON * obj)3179 void screendrawgrid(WINDOWPART *win, POLYGON *obj)
3180 {
3181 	gra_drawgrid(win, obj);
3182 }
3183 
3184 /******************** MOUSE CONTROL ********************/
3185 
3186 /*
3187  * routine to return the number of buttons on the mouse
3188  */
buttoncount(void)3189 INTBIG buttoncount(void)
3190 {
3191 	return(mini(BUTTONS, NUMBUTS));
3192 }
3193 
3194 /*
3195  * routine to tell whether button "but" is a double-click
3196  */
doublebutton(INTBIG b)3197 BOOLEAN doublebutton(INTBIG b)
3198 {
3199 	if (b >= BUTTONS - REALBUTS) return(TRUE);
3200 	return(FALSE);
3201 }
3202 
3203 /*
3204  * routine to tell whether button "but" is a context button (right)
3205  */
contextbutton(INTBIG b)3206 BOOLEAN contextbutton(INTBIG b)
3207 {
3208 	if ((b%5) == 2) return(TRUE);
3209 	return(FALSE);
3210 }
3211 
3212 /*
3213  * routine to tell whether button "but" has the "shift" key held
3214  */
shiftbutton(INTBIG b)3215 BOOLEAN shiftbutton(INTBIG b)
3216 {
3217 	b = b / REALBUTS;
3218 
3219 	/* this "switch" statement is taken from the array "gra_buttonname" */
3220 	switch (b)
3221 	{
3222 		case 0: return(FALSE);	/* no shift keys */
3223 		case 1: return(TRUE);	/* shift */
3224 		case 2: return(FALSE);	/* control */
3225 		case 3: return(FALSE);	/* alt */
3226 		case 4: return(TRUE);	/* shift-control */
3227 		case 5: return(TRUE);	/* shift-alt */
3228 		case 6: return(FALSE);	/* control-alt */
3229 		case 7: return(TRUE);	/* shift-control-alt */
3230 		case 8: return(FALSE);	/* double-click */
3231 	}
3232 	return(FALSE);
3233 }
3234 
3235 /*
3236  * routine to tell whether button "but" is a "mouse wheel" button
3237  */
wheelbutton(INTBIG b)3238 BOOLEAN wheelbutton(INTBIG b)
3239 {
3240 	b = b % REALBUTS;
3241 	if (b == 3 || b == 4) return(TRUE);
3242 	return(FALSE);
3243 }
3244 
3245 /*
3246  * routine to return the name of button "b" (from 0 to "buttoncount()").
3247  * The number of letters unique to the button is placed in "important".
3248  */
buttonname(INTBIG b,INTBIG * important)3249 CHAR *buttonname(INTBIG b, INTBIG *important)
3250 {
3251 	*important = gra_buttonname[b].unique;
3252 	return(gra_buttonname[b].name);
3253 }
3254 
3255 /*
3256  * routine to convert from "state" (the typical input parameter)
3257  * to button numbers (the table "gra_buttonname")
3258  */
gra_makebutton(INTBIG state)3259 INTBIG gra_makebutton(INTBIG state)
3260 {
3261 	REGISTER INTBIG base;
3262 
3263 	switch (state&WHICHBUTTON)
3264 	{
3265 		case ISLEFT:    base = 0;   break;
3266 		case ISMIDDLE:  base = 1;   break;
3267 		case ISRIGHT:   base = 2;   break;
3268 		case ISWHLFWD:  base = 3;   break;
3269 		case ISWHLBKW:  base = 4;   break;
3270 	}
3271 
3272 	if ((state&DOUBLECLICK) != 0) return(base+REALBUTS*8);
3273 	switch (state & (SHIFTISDOWN|CONTROLISDOWN|ALTISDOWN))
3274 	{
3275 		case SHIFTISDOWN                        : return(base+REALBUTS*1);
3276 		case             CONTROLISDOWN          : return(base+REALBUTS*2);
3277 		case                           ALTISDOWN: return(base+REALBUTS*3);
3278 		case SHIFTISDOWN|CONTROLISDOWN          : return(base+REALBUTS*4);
3279 		case SHIFTISDOWN|              ALTISDOWN: return(base+REALBUTS*5);
3280 		case             CONTROLISDOWN|ALTISDOWN: return(base+REALBUTS*6);
3281 		case SHIFTISDOWN|CONTROLISDOWN|ALTISDOWN: return(base+REALBUTS*7);
3282 	}
3283 	return(base);
3284 }
3285 
3286 /*
3287  * routine to wait for a button push and return its index (0 based) in "*but".
3288  * The coordinates of the cursor are placed in "*x" and "*y".  If there is no
3289  * button push, the value of "*but" is negative.
3290  */
waitforbutton(INTBIG * x,INTBIG * y,INTBIG * but)3291 void waitforbutton(INTBIG *x, INTBIG *y, INTBIG *but)
3292 {
3293 	if (gra_inputstate != NOEVENT && (gra_inputstate&(ISBUTTON|BUTTONUP)) == ISBUTTON)
3294 	{
3295 		*but = gra_makebutton(gra_inputstate);
3296 		*x = gra_cursorx;
3297 		*y = gra_cursory;
3298 		gra_inputstate = NOEVENT;
3299 		if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
3300 		return;
3301 	}
3302 	gra_nextevent();
3303 	if (gra_inputstate != NOEVENT && (gra_inputstate&(ISBUTTON|BUTTONUP)) == ISBUTTON)
3304 	{
3305 		*but = gra_makebutton(gra_inputstate);
3306 		*x = gra_cursorx;
3307 		*y = gra_cursory;
3308 		gra_inputstate = NOEVENT;
3309 		if (us_cursorstate != IBEAMCURSOR) setdefaultcursortype(us_normalcursor);
3310 		return;
3311 	}
3312 	*but = -1;
3313 }
3314 
3315 /*
3316  * routine to do modal loop, calling "charhandler" for each typed key and "buttonhandler"
3317  * for each pushed button. The "charhandler" routine is called with the character value
3318  * that was typed. The "buttonhandler" routine is called with coordinates of cursor.
3319  * The "charhandler" and "buttonhandler" returns true to abort loop.
3320  * The value of "cursor" determines cursor appearance.
3321  */
modalloop(BOOLEAN (* charhandler)(INTSML chr,INTBIG special),BOOLEAN (* buttonhandler)(INTBIG x,INTBIG y,INTBIG but),INTBIG cursor)3322 void modalloop(BOOLEAN (*charhandler)(INTSML chr, INTBIG special),
3323 	BOOLEAN (*buttonhandler)(INTBIG x, INTBIG y, INTBIG but), INTBIG cursor)
3324 {
3325 	INTBIG oldnormalcursor, special, x, y, but;
3326 	INTSML chr;
3327 
3328 	/* set cursor */
3329 	oldnormalcursor = us_normalcursor;
3330 	setnormalcursor(cursor);
3331 
3332 	for (;;)
3333 	{
3334 		if (ttydataready())
3335 		{
3336 			chr = getnxtchar(&special);
3337 			if ((*charhandler)(chr, special)) break;
3338 		} else
3339 		{
3340 			waitforbutton(&x, &y, &but);
3341 			if (but >= 0 && buttonhandler != 0)
3342 				if ((*buttonhandler)(x, y, but)) break;
3343 		}
3344 	}
3345 
3346 	/* restore cursor */
3347 	setnormalcursor(oldnormalcursor);
3348 }
3349 
3350 /*
3351  * routine to track the cursor until a button is released, calling "whileup" for
3352  * each co-ordinate when the mouse moves before the first button push, calling
3353  * "whendown" once when the button goes down, calling "eachdown" for each
3354  * co-ordinate when the mouse moves after the button is pushed, calling
3355  * "eachchar" for each key that is typed at any time, and calling "done" once
3356  * when done.  The "whendown" and "done" routines are called with no parameters;
3357  * "whileup" and "eachdown" are called with the X and Y coordinates of the
3358  * cursor; and "eachchar" is called with the X, Y, and character value that was
3359  * typed.  The "whileup", "eachdown", and "eachchar" routines return nonzero to
3360  * abort tracking.
3361  * If "waitforpush" is true then the routine will wait for a button to
3362  * actually be pushed before tracking (otherwise it will begin tracking
3363  * immediately).  The value of "purpose" determines what the cursor will look
3364  * like during dragging: 0 for normal (the standard cursor), 1 for drawing (a pen),
3365  * 2 for dragging (a hand), 3 for popup menu selection (a horizontal arrow), 4 for
3366  * hierarchical popup menu selection (arrow, stays at end).
3367  */
trackcursor(BOOLEAN waitforpush,BOOLEAN (* whileup)(INTBIG,INTBIG),void (* whendown)(void),BOOLEAN (* eachdown)(INTBIG,INTBIG),BOOLEAN (* eachchar)(INTBIG,INTBIG,INTSML),void (* done)(void),INTBIG purpose)3368 void trackcursor(BOOLEAN waitforpush, BOOLEAN (*whileup)(INTBIG, INTBIG),
3369 	void (*whendown)(void), BOOLEAN (*eachdown)(INTBIG, INTBIG),
3370 	BOOLEAN (*eachchar)(INTBIG, INTBIG, INTSML), void (*done)(void), INTBIG purpose)
3371 {
3372 	REGISTER BOOLEAN keepon;
3373 	INTBIG action, oldcursor;
3374 
3375 	/* change the cursor to an appropriate icon */
3376 	oldcursor = us_normalcursor;
3377 	switch (purpose)
3378 	{
3379 		case TRACKDRAWING:
3380 			setnormalcursor(PENCURSOR);
3381 			break;
3382 		case TRACKDRAGGING:
3383 			setnormalcursor(HANDCURSOR);
3384 			break;
3385 		case TRACKSELECTING:
3386 		case TRACKHSELECTING:
3387 			setnormalcursor(MENUCURSOR);
3388 			break;
3389 	}
3390 
3391 	/* now wait for a button to go down, if requested */
3392 	keepon = FALSE;
3393 	if (waitforpush)
3394 	{
3395 		while (!keepon)
3396 		{
3397 			gra_nextevent();
3398 			if (gra_inputstate == NOEVENT) continue;
3399 			action = gra_inputstate;
3400 			gra_inputstate = NOEVENT;
3401 
3402 			/* if button just went down, stop this loop */
3403 			if ((action&ISBUTTON) != 0 && (action&BUTTONUP) == 0) break;
3404 			if ((action&MOTION) != 0)
3405 			{
3406 				keepon = (*whileup)(gra_cursorx, gra_cursory);
3407 			} else if ((action&ISKEYSTROKE) != 0 && gra_inputspecial == 0)
3408 			{
3409 				keepon = (*eachchar)(gra_cursorx, gra_cursory, (INTSML)(action&CHARREAD));
3410 			}
3411 			if (el_pleasestop != 0) keepon = TRUE;
3412 		}
3413 	}
3414 
3415 	/* button is now down, real tracking begins */
3416 	if (!keepon)
3417 	{
3418 		(*whendown)();
3419 		keepon = (*eachdown)(gra_cursorx, gra_cursory);
3420 	}
3421 
3422 	/* now track while the button is down */
3423 	while (!keepon)
3424 	{
3425 		gra_nextevent();
3426 		us_endchanges(NOWINDOWPART);
3427 
3428 		/* for each motion, report the coordinates */
3429 		if (gra_inputstate == NOEVENT) continue;
3430 		action = gra_inputstate;
3431 		gra_inputstate = NOEVENT;
3432 		if ((action&ISBUTTON) != 0 && (action&BUTTONUP) != 0) break;
3433 		if ((action&MOTION) != 0)
3434 		{
3435 			keepon = (*eachdown)(gra_cursorx, gra_cursory);
3436 		} else if ((action&ISKEYSTROKE) != 0 && gra_inputspecial == 0)
3437 		{
3438 			keepon = (*eachchar)(gra_cursorx, gra_cursory, (INTSML)(action&CHARREAD));
3439 		}
3440 		if (el_pleasestop != 0) keepon = TRUE;
3441 	}
3442 
3443 	/* inform the user that all is done */
3444 	(*done)();
3445 
3446 	/* restore the state of the world */
3447 	setnormalcursor(oldcursor);
3448 }
3449 
3450 /*
3451  * routine to read the current co-ordinates of the tablet and return them
3452  * in "*x" and "*y".
3453  */
readtablet(INTBIG * x,INTBIG * y)3454 void readtablet(INTBIG *x, INTBIG *y)
3455 {
3456 	*x = gra_cursorx;
3457 	*y = gra_cursory;
3458 }
3459 
3460 /*
3461  * routine to turn off the cursor tracking if it is on
3462  */
stoptablet(void)3463 void stoptablet(void)
3464 {
3465 	if (us_cursorstate != IBEAMCURSOR)
3466 		setdefaultcursortype(us_normalcursor);
3467 }
3468 
3469 /******************** KEYBOARD CONTROL ********************/
3470 
3471 /*
3472  * routine to get the next character from the keyboard
3473  */
getnxtchar(INTBIG * special)3474 INTSML getnxtchar(INTBIG *special)
3475 {
3476 	REGISTER INTSML i;
3477 
3478 	if (gra_inputstate != NOEVENT && (gra_inputstate&ISKEYSTROKE) != 0)
3479 	{
3480 		i = (INTSML)(gra_inputstate & CHARREAD);
3481 		*special = gra_inputspecial;
3482 		gra_inputstate = NOEVENT;
3483 		return(i);
3484 	}
3485 	if (us_cursorstate != IBEAMCURSOR)
3486 		setdefaultcursortype(WANTTTYCURSOR);
3487 	for(;;)
3488 	{
3489 		gra_nextevent();
3490 		if (gra_inputstate != NOEVENT && (gra_inputstate&ISKEYSTROKE) != 0)
3491 			break;
3492 	}
3493 	i = (INTSML)(gra_inputstate & CHARREAD);
3494 	*special = gra_inputspecial;
3495 	gra_inputstate = NOEVENT;
3496 	if (us_cursorstate != IBEAMCURSOR)
3497 		setdefaultcursortype(us_normalcursor);
3498 	return(i);
3499 }
3500 
checkforinterrupt(void)3501 void checkforinterrupt(void)
3502 {
3503 	MSG msg;
3504 
3505 	while ( ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
3506 	{
3507 		if (!gra_app.PumpMessage())
3508 		{
3509 			::PostQuitMessage(0);
3510 			break;
3511 		}
3512 	}
3513 	setdefaultcursortype(WAITCURSOR);
3514 }
3515 
3516 /*
3517  * Routine to return which "bucky bits" are held down (shift, control, etc.)
3518  */
getbuckybits(void)3519 INTBIG getbuckybits(void)
3520 {
3521 	REGISTER INTBIG bits;
3522 
3523 	bits = 0;
3524 	if ((GetKeyState(VK_CONTROL)&0x8000) != 0) bits |= CONTROLDOWN|ACCELERATORDOWN;
3525 	if ((GetKeyState(VK_SHIFT)&0x8000) != 0) bits |= SHIFTDOWN;
3526 	if ((GetKeyState(VK_MENU)&0x8000) != 0) bits |= OPTALTMETDOWN;
3527 	return(bits);
3528 }
3529 
3530 /*
3531  * routine to tell whether data is waiting at the terminal.  Returns true
3532  * if data is ready.
3533  */
ttydataready(void)3534 BOOLEAN ttydataready(void)
3535 {
3536 	/* see if something is already pending */
3537 	if (gra_inputstate != NOEVENT)
3538 	{
3539 		if ((gra_inputstate&ISKEYSTROKE) != 0) return(TRUE);
3540 	}
3541 
3542 	/* wait for something and analyze it */
3543 	gra_nextevent();
3544 	if (gra_inputstate != NOEVENT && (gra_inputstate&ISKEYSTROKE) != 0) return(TRUE);
3545 	return(FALSE);
3546 }
3547 
3548 /****************************** FILES ******************************/
3549 
3550 /*
3551  * Routine to prompt for multiple files of type "filetype", giving the
3552  * message "msg".  Returns a string that contains all of the file names,
3553  * separated by the NONFILECH (a character that cannot be in a file name).
3554  */
3555 #define MAXMULTIFILECHARS 4000
multifileselectin(CHAR * msg,INTBIG filetype)3556 CHAR *multifileselectin(CHAR *msg, INTBIG filetype)
3557 {
3558 	CHAR fs[MAXMULTIFILECHARS], prompt[256], *extension, *winfilter, *shortname,
3559 		*longname, *pt;
3560 	CFileDialog *fileDlg;
3561 	REGISTER INTBIG which;
3562 	REGISTER INTBIG ret;
3563 	INTBIG mactype;
3564 	BOOLEAN binary;
3565 	REGISTER void *infstr;
3566 
3567 	/* build filter string */
3568 	describefiletype(filetype, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
3569 	estrcpy(fs, longname);
3570 	estrcat(fs, x_(" ("));
3571 	estrcat(fs, winfilter);
3572 	estrcat(fs, x_(")|"));
3573 	estrcat(fs, winfilter);
3574 	estrcat(fs, _("|All Files (*.*)|*.*||"));
3575 
3576 	fileDlg = new CFileDialog(TRUE, NULL, NULL, NULL, fs, NULL);
3577 	if (fileDlg == NULL) return(0);
3578 	fs[0] = 0;
3579 	fileDlg->m_ofn.lpstrFile = fs;
3580 	fileDlg->m_ofn.nMaxFile = MAXMULTIFILECHARS;
3581 	fileDlg->m_ofn.Flags |= OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT |
3582 		OFN_EXPLORER;
3583 	esnprintf(prompt, 256, _("%s Selection"), msg);
3584 	fileDlg->m_ofn.lpstrTitle = prompt;
3585 	pt = 0;
3586 	ret = fileDlg->DoModal();
3587 	if (ret == IDOK)
3588 	{
3589 		pt = fs;
3590 		infstr = initinfstr();
3591 		which = 0;
3592 		while (*pt != 0)
3593 		{
3594 			if (which == 0)
3595 			{
3596 				estrcpy(gra_localstring, pt);
3597 			} else
3598 			{
3599 				if (which > 1) addtoinfstr(infstr, NONFILECH);
3600 				addstringtoinfstr(infstr, gra_localstring);
3601 				addtoinfstr(infstr, DIRSEP);
3602 				addstringtoinfstr(infstr, pt);
3603 			}
3604 			which++;
3605 			while (*pt != 0) pt++;
3606 			pt++;
3607 		}
3608 		if (which == 1) addstringtoinfstr(infstr, gra_localstring);
3609 		pt = returninfstr(infstr);
3610 	}
3611 	delete fileDlg;
3612 	return(pt);
3613 }
3614 
3615 /*
3616  * Routine to display a standard file prompt dialog and return the selected file.
3617  * The prompt message is in "msg" and the kind of file is in "filetype".  The default
3618  * output file name is in "defofile" (only used if "filetype" is negative).
3619  */
fileselect(CHAR * msg,INTBIG filetype,CHAR * defofile)3620 CHAR *fileselect(CHAR *msg, INTBIG filetype, CHAR *defofile)
3621 {
3622 	REGISTER INTBIG i;
3623 	CHAR ofile[256], fs[256], prompt[256], *extension, *winfilter, *shortname,
3624 		*longname;
3625 	CFileDialog *fileDlg;
3626 	INTBIG mactype;
3627 	BOOLEAN binary;
3628 
3629 	/* build filter string */
3630 	describefiletype(filetype, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
3631 	estrcpy(fs, longname);
3632 	estrcat(fs, x_(" ("));
3633 	estrcat(fs, winfilter);
3634 	estrcat(fs, x_(")|"));
3635 	estrcat(fs, winfilter);
3636 	estrcat(fs, x_("|"));
3637 	estrcat(fs, _("All Files"));
3638 	estrcat(fs, x_(" (*.*)|*.*||"));
3639 
3640 	if (us_logplay != NULL)
3641 	{
3642 		if (gra_loggetnextaction(gra_localstring)) return(0);
3643 		if (gra_inputstate != FILEREPLY) gra_localstring[0] = 0;
3644 	} else
3645 	{
3646 		gra_localstring[0] = 0;
3647 		if ((filetype & FILETYPEWRITE) == 0)
3648 		{
3649 			/* open file dialog */
3650 			fileDlg = new CFileDialog(TRUE, NULL, NULL, NULL, fs, NULL);
3651 			if (fileDlg == NULL) gra_localstring[0] = 0; else
3652 			{
3653 				fileDlg->m_ofn.Flags |= OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
3654 				esnprintf(prompt, 256, _("%s Selection"), msg);
3655 				fileDlg->m_ofn.lpstrTitle = prompt;
3656 				if (fileDlg->DoModal() == IDOK)
3657 					estrcpy(gra_localstring, fileDlg->GetPathName());
3658 				delete fileDlg;
3659 			}
3660 		} else
3661 		{
3662 			/* save file dialog */
3663 			for(i = estrlen(defofile)-1; i > 0; i--)
3664 				if (defofile[i] == ':' || defofile[i] == '/' || defofile[i] == '\\') break;
3665 			if (i > 0) i++;
3666 			(void)estrcpy(ofile, &defofile[i]);
3667 			fileDlg = new CFileDialog(FALSE, extension, ofile, NULL, fs, NULL);
3668 			if (fileDlg != NULL)
3669 			{
3670 				fileDlg->m_ofn.Flags |= OFN_OVERWRITEPROMPT;
3671 				esnprintf(prompt, 256, _("%s Creation"), msg);
3672 				fileDlg->m_ofn.lpstrTitle = prompt;
3673 				if (fileDlg->DoModal() == IDOK)
3674 					estrcpy(gra_localstring, fileDlg->GetPathName());
3675 				delete fileDlg;
3676 			}
3677 		}
3678 	}
3679 
3680 	/* log this result */
3681 	gra_logwriteaction(FILEREPLY, 0, 0, 0, gra_localstring);
3682 
3683 	if (gra_localstring[0] == 0) return((CHAR *)NULL); else
3684 		return(gra_localstring);
3685 }
3686 
3687 /*
3688  * Helper routine to initialize the list of files in a directory.
3689  */
gra_initfilelist(void)3690 void gra_initfilelist(void)
3691 {
3692 	if (gra_fileliststringarray == 0)
3693 	{
3694 		gra_fileliststringarray = newstringarray(db_cluster);
3695 		if (gra_fileliststringarray == 0) return;
3696 	}
3697 	clearstrings(gra_fileliststringarray);
3698 }
3699 
3700 /*
3701  * Helper routine to add "file" to the list of files in a directory.
3702  * Returns true on error.
3703  */
gra_addfiletolist(CHAR * file)3704 BOOLEAN gra_addfiletolist(CHAR *file)
3705 {
3706 	addtostringarray(gra_fileliststringarray, file);
3707 	return(FALSE);
3708 }
3709 
3710 /*
3711  * Routine to search for all of the files/directories in directory "directory" and
3712  * return them in the array of strings "filelist".  Returns the number of files found.
3713  */
filesindirectory(CHAR * directory,CHAR *** filelist)3714 INTBIG filesindirectory(CHAR *directory, CHAR ***filelist)
3715 {
3716 	HANDLE handle;
3717 	WIN32_FIND_DATA data;
3718 	BOOL found;
3719 	CHAR *dir;
3720 	INTBIG len;
3721 	REGISTER void *infstr;
3722 
3723 	/* search for all files in directory */
3724 	infstr = initinfstr();
3725 	addstringtoinfstr(infstr, directory);
3726 	addstringtoinfstr(infstr, x_("*.*"));
3727 	dir = returninfstr(infstr);
3728 
3729 	handle = FindFirstFile(dir, &data);
3730 	if (handle == INVALID_HANDLE_VALUE) return(0);
3731 
3732 	gra_initfilelist();
3733 	for (found = 1; found; found = FindNextFile(handle, &data))
3734 	{
3735 		if (gra_addfiletolist(data.cFileName)) return(0);
3736 	}
3737 	FindClose(handle);
3738 
3739 	*filelist = getstringarray(gra_fileliststringarray, &len);
3740 	return(len);
3741 }
3742 
3743 /* routine to convert a path name with "~" to a real path */
truepath(CHAR * line)3744 CHAR *truepath(CHAR *line)
3745 {
3746 	/* only have tilde parsing on UNIX */
3747 	return(line);
3748 }
3749 
3750 /*
3751  * Routine to return the full path to file "file".
3752  */
fullfilename(CHAR * file)3753 CHAR *fullfilename(CHAR *file)
3754 {
3755 	static CHAR fullfile[MAXPATHLEN];
3756 
3757 	/* is this a hack?  Full path defined when it starts with "LETTER:" */
3758 	if (isalpha(file[0]) != 0 && file[1] == ':') return(file);
3759 
3760 	/* full path also defined by "\\" at the start */
3761 	if (file[0] == '\\' && file[1] == '\\') return(file);
3762 
3763 	/* build the proper path */
3764 	estrcpy(fullfile, currentdirectory());
3765 	estrcat(fullfile, file);
3766 	return(fullfile);
3767 }
3768 
3769 /*
3770  * routine to rename file "file" to "newfile"
3771  * returns nonzero on error
3772  */
erename(CHAR * file,CHAR * newfile)3773 INTBIG erename(CHAR *file, CHAR *newfile)
3774 {
3775 #ifdef _UNICODE
3776 	return(_wrename(file, newfile));
3777 #else
3778 	return(rename(file, newfile));
3779 #endif
3780 }
3781 
3782 /*
3783  * routine to delete file "file"
3784  */
eunlink(CHAR * file)3785 INTBIG eunlink(CHAR *file)
3786 {
3787 #ifdef _UNICODE
3788 	return(_wunlink(file));
3789 #else
3790 	return(_unlink(file));
3791 #endif
3792 }
3793 
3794 /*
3795  * Routine to return information about the file or directory "name":
3796  *  0: does not exist
3797  *  1: is a file
3798  *  2: is a directory
3799  *  3: is a locked file (read-only)
3800  */
fileexistence(CHAR * name)3801 INTBIG fileexistence(CHAR *name)
3802 {
3803 	struct estatstr buf;
3804 
3805 	if (estat(name, &buf) < 0) return(0);
3806 	if ((buf.st_mode & S_IFMT) == S_IFDIR) return(2);
3807 
3808 	/* a file: see if it is writable */
3809 	if (eaccess(name, 2) == 0) return(1);
3810 	return(3);
3811 }
3812 
3813 /*
3814  * Routine to create a directory.
3815  * Returns true on error.
3816  */
createdirectory(CHAR * dirname)3817 BOOLEAN createdirectory(CHAR *dirname)
3818 {
3819 	if (emkdir(dirname) == 0) return(FALSE);
3820 	return(TRUE);
3821 }
3822 
3823 /*
3824  * Routine to return the current directory name
3825  */
currentdirectory(void)3826 CHAR *currentdirectory(void)
3827 {
3828 	static CHAR line[MAXPATHLEN+1];
3829 	REGISTER INTBIG len;
3830 
3831 	egetcwd(line, MAXPATHLEN);
3832 	len = estrlen(line);
3833 	if (line[len-1] != DIRSEP)
3834 	{
3835 		line[len++] = DIRSEP;
3836 		line[len] = 0;
3837 	}
3838 	return(line);
3839 }
3840 
3841 /*
3842  * Routine to return the home directory (returns 0 if it doesn't exist)
3843  */
hashomedir(void)3844 CHAR *hashomedir(void)
3845 {
3846 	return(0);
3847 }
3848 
3849 /*
3850  * Routine to return the path to the "options" library.
3851  */
optionsfilepath(void)3852 CHAR *optionsfilepath(void)
3853 {
3854 	CHAR username[256];
3855 	UINTBIG size;
3856 	REGISTER void *infstr;
3857 
3858 	infstr = initinfstr();
3859 	addstringtoinfstr(infstr, el_libdir);
3860 	addstringtoinfstr(infstr, x_("electricoptions_"));
3861 	size = 256;
3862 	GetUserName(username, &size);
3863 	addstringtoinfstr(infstr, username);
3864 	addstringtoinfstr(infstr, x_(".elib"));
3865 	return(returninfstr(infstr));
3866 }
3867 
3868 /*
3869  * Routine to obtain the modification date on file "filename".
3870  */
filedate(CHAR * filename)3871 time_t filedate(CHAR *filename)
3872 {
3873 	struct estatstr buf;
3874 	time_t thetime;
3875 
3876 	estat(filename, &buf);
3877 	thetime = buf.st_mtime;
3878 	thetime -= machinetimeoffset();
3879 	return(thetime);
3880 }
3881 
3882 /*
3883  * Routine to lock a resource called "lockfilename" by creating such a file
3884  * if it doesn't exist.  Returns true if successful, false if unable to
3885  * lock the file.
3886  */
lockfile(CHAR * lockfilename)3887 BOOLEAN lockfile(CHAR *lockfilename)
3888 {
3889 	int fd;
3890 
3891 	fd = ecreat(lockfilename, 0);
3892 	if (fd == -1) return(FALSE);
3893 	if (close(fd) == -1) return(FALSE);
3894 	return(TRUE);
3895 }
3896 
3897 /*
3898  * Routine to unlock a resource called "lockfilename" by deleting such a file.
3899  */
unlockfile(CHAR * lockfilename)3900 void unlockfile(CHAR *lockfilename)
3901 {
3902 	INTBIG attrs;
3903 	attrs = GetFileAttributes(lockfilename);
3904 	if ((attrs & FILE_ATTRIBUTE_READONLY) != 0)
3905 	{
3906 		if (SetFileAttributes(lockfilename, attrs & ~FILE_ATTRIBUTE_READONLY) == 0)
3907 		{
3908 			ttyputerr(_("Error removing readonly bit on %s (error %ld)"),
3909 				lockfilename, GetLastError());
3910 		}
3911 	}
3912 
3913 	if (DeleteFile(lockfilename) == 0)
3914 		ttyputerr(_("Error unlocking %s (error %ld)"), lockfilename, GetLastError());
3915 }
3916 
3917 /*
3918  * Routine to show file "filename" in a browser window.
3919  * Returns true if the operation cannot be done.
3920  */
browsefile(CHAR * filename)3921 BOOLEAN browsefile(CHAR *filename)
3922 {
3923 	CMainFrame *wnd;
3924 	INTBIG err;
3925 
3926 	wnd = (CMainFrame *)AfxGetMainWnd();
3927 	err = (INTBIG)ShellExecute(wnd->m_hWnd, x_("open"), filename, x_(""),
3928 		x_("C:\\Temp\\"), SW_SHOWNORMAL);
3929 	if (err > 32) return(FALSE);
3930 	ttyputmsg(_("Could not browse %s (error %ld)"), filename, err);
3931 	return(TRUE);
3932 }
3933 
3934 /****************************** CHANNELS ******************************/
3935 
3936 /*
3937  * routine to create a pipe connection between the channels in "channels"
3938  */
epipe(int channels[2])3939 INTBIG epipe(int channels[2])
3940 {
3941 	return(0);
3942 }
3943 
3944 /*
3945  * Routine to set channel "channel" into an appropriate mode for single-character
3946  * interaction (i.e. break mode).
3947  */
setinteractivemode(int channel)3948 void setinteractivemode(int channel)
3949 {
3950 }
3951 
3952 /*
3953  * Routine to replace channel "channel" with a pointer to file "file".
3954  * Returns a pointer to the original channel.
3955  */
channelreplacewithfile(int channel,CHAR * file)3956 INTBIG channelreplacewithfile(int channel, CHAR *file)
3957 {
3958 	return(0);
3959 }
3960 
3961 /*
3962  * Routine to replace channel "channel" with new channel "newchannel".
3963  * Returns a pointer to the original channel.
3964  */
channelreplacewithchannel(int channel,int newchannel)3965 INTBIG channelreplacewithchannel(int channel, int newchannel)
3966 {
3967 	return(0);
3968 }
3969 
3970 /*
3971  * Routine to restore channel "channel" to the pointer that was returned
3972  * by "channelreplacewithfile" or "channelreplacewithchannel" (and is in "saved").
3973  */
channelrestore(int channel,INTBIG saved)3974 void channelrestore(int channel, INTBIG saved)
3975 {
3976 }
3977 
3978 /*
3979  * Routine to read "count" bytes from channel "channel" into "addr".
3980  * Returns the number of bytes read.
3981  */
eread(int channel,UCHAR1 * addr,INTBIG count)3982 INTBIG eread(int channel, UCHAR1 *addr, INTBIG count)
3983 {
3984 	return(0);
3985 }
3986 
3987 /*
3988  * Routine to write "count" bytes to channel "channel" from "addr".
3989  * Returns the number of bytes written.
3990  */
ewrite(int channel,UCHAR1 * addr,INTBIG count)3991 INTBIG ewrite(int channel, UCHAR1 *addr, INTBIG count)
3992 {
3993 	return(0);
3994 }
3995 
3996 /*
3997  * routine to close a channel in "channel"
3998  */
eclose(int channel)3999 INTBIG eclose(int channel)
4000 {
4001 	return(0);
4002 }
4003 
4004 /*************************** TIME ROUTINES ***************************/
4005 
machinetimeoffset(void)4006 time_t machinetimeoffset(void)
4007 {
4008 	return(0);
4009 }
4010 
4011 /* returns the time at which the current event occurred */
eventtime(void)4012 UINTBIG eventtime(void)
4013 {
4014 	return(gra_eventtime);
4015 }
4016 
4017 /* returns the current time in 60ths of a second */
ticktime(void)4018 UINTBIG ticktime(void)
4019 {
4020 	UINTBIG msTime;
4021 
4022 	/* convert milliseconds to ticks */
4023 	msTime = GetTickCount();
4024 	return(msTime*6/100 + gra_timeoffset);
4025 }
4026 
4027 /* returns the double-click interval in 60ths of a second */
doubleclicktime(void)4028 INTBIG doubleclicktime(void)
4029 {
4030 	INTBIG dct;
4031 
4032 	dct = GetDoubleClickTime();
4033 	dct = dct * 60 / 1000;
4034 	return(dct);
4035 }
4036 
4037 /*
4038  * Routine to wait "ticks" sixtieths of a second and then return.
4039  */
gotosleep(INTBIG ticks)4040 void gotosleep(INTBIG ticks)
4041 {
4042 	Sleep(ticks * 100 / 6);
4043 }
4044 
4045 /*
4046  * Routine to start counting time.
4047  */
starttimer(void)4048 void starttimer(void)
4049 {
4050 	struct timeb timeptr;
4051 
4052 	/* code cannot be called by multiple procesors: uses globals */
4053 	NOT_REENTRANT;
4054 
4055 	ftime(&timeptr);
4056 	gra_timebasesec = timeptr.time;
4057 	gra_timebasems = timeptr.millitm;
4058 }
4059 
4060 /*
4061  * Routine to stop counting time and return the number of elapsed seconds
4062  * since the last call to "starttimer()".
4063  */
endtimer(void)4064 float endtimer(void)
4065 {
4066 	float seconds;
4067 	INTBIG milliseconds;
4068 	struct timeb timeptr;
4069 
4070 	ftime(&timeptr);
4071 	milliseconds = (timeptr.time - gra_timebasesec) * 1000 + (timeptr.millitm-gra_timebasems);
4072 	seconds = ((float)milliseconds) / 1000.0f;
4073 	return(seconds);
4074 }
4075 
4076 /*************************** EVENT ROUTINES ***************************/
4077 
4078 #define INITIALTIMERDELAY 8
4079 
4080 INTBIG gra_lasteventstate = 0;
4081 INTBIG gra_lasteventx;
4082 INTBIG gra_lasteventy;
4083 INTBIG gra_lasteventtime;
4084 INTBIG gra_cureventtime = 0;
4085 
gra_timerticked(void)4086 void gra_timerticked(void)
4087 {
4088 	gra_cureventtime++;
4089 	if (gra_cureventtime > gra_lasteventtime+INITIALTIMERDELAY)
4090 	{
4091 		/* see if the last event was a mouse button going down */
4092 		if ((gra_lasteventstate&(ISBUTTON|BUTTONUP)) == ISBUTTON)
4093 		{
4094 			/* turn it into a null motion */
4095 			gra_addeventtoqueue((gra_lasteventstate & ~ISBUTTON) | MOTION, 0,
4096 				gra_lasteventx, gra_lasteventy);
4097 			gra_lasteventtime -= INITIALTIMERDELAY;
4098 			return;
4099 		}
4100 
4101 		/* see if the last event was a motion with button down */
4102 		if ((gra_lasteventstate&(MOTION|BUTTONUP)) == MOTION)
4103 		{
4104 			/* repeat the last event */
4105 			gra_addeventtoqueue(gra_lasteventstate, 0, gra_lasteventx, gra_lasteventy);
4106 			gra_lasteventtime -= INITIALTIMERDELAY;
4107 			return;
4108 		}
4109 	}
4110 }
4111 
gra_addeventtoqueue(INTBIG state,INTBIG special,INTBIG x,INTBIG y)4112 void gra_addeventtoqueue(INTBIG state, INTBIG special, INTBIG x, INTBIG y)
4113 {
4114 	MYEVENTQUEUE *next, *prev;
4115 
4116 	if (db_multiprocessing) emutexlock(gra_eventqueuemutex);
4117 
4118 	next = gra_eventqueuetail + 1;
4119 	if (next >= &gra_eventqueue[EVENTQUEUESIZE])
4120 		next = gra_eventqueue;
4121 	if (next == gra_eventqueuehead)
4122 	{
4123 		/* queue is full: see if last event was repeated */
4124 		if (gra_eventqueuetail == gra_eventqueue)
4125 			prev = &gra_eventqueue[EVENTQUEUESIZE-1]; else
4126 				prev = gra_eventqueuetail - 1;
4127 		if (prev->inputstate == state)
4128 		{
4129 			prev->cursorx = x;
4130 			prev->cursory = y;
4131 		} else
4132 		{
4133 			MessageBeep(MB_ICONASTERISK);
4134 		}
4135 	} else
4136 	{
4137 		gra_lasteventstate = gra_eventqueuetail->inputstate = state;
4138 		gra_eventqueuetail->special = special;
4139 		gra_lasteventx = gra_eventqueuetail->cursorx = x;
4140 		gra_lasteventy = gra_eventqueuetail->cursory = y;
4141 		gra_eventqueuetail->eventtime = ticktime();
4142 		gra_lasteventtime = gra_cureventtime;
4143 		gra_eventqueuetail = next;
4144 	}
4145 
4146 	if (db_multiprocessing) emutexunlock(gra_eventqueuemutex);
4147 }
4148 
4149 /*
4150  * Routine to get the next Electric input action and set the global "gra_inputstate"
4151  * accordingly.
4152  */
gra_nextevent(void)4153 void gra_nextevent(void)
4154 {
4155 	MSG msg;
4156 	REGISTER INTBIG windowindex, i, j, trueItem;
4157 	BOOLEAN saveslice;
4158 	REGISTER INTBIG x, y;
4159 	BOOLEAN verbose;
4160 	POPUPMENU *pm;
4161 	REGISTER POPUPMENUITEM *mi;
4162 	extern BOOLEAN el_inslice;
4163 
4164 	flushscreen();
4165 
4166 	/* process events */
4167 	while ( ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
4168 	{
4169 		if (!gra_app.PumpMessage())
4170 		{
4171 			::PostQuitMessage(0);
4172 			break;
4173 		}
4174 	}
4175 
4176 	if (us_logplay != NULL)
4177 	{
4178 		/* playing back log file: wait for an event */
4179 		gra_inputstate = NOEVENT;
4180 		static INTBIG lastx = 0, lasty = 0;
4181 		while (gra_eventqueuehead != gra_eventqueuetail)
4182 		{
4183 			gra_inputstate = gra_eventqueuehead->inputstate;
4184 			gra_inputspecial = gra_eventqueuehead->special;
4185 			gra_cursorx = gra_eventqueuehead->cursorx;
4186 			gra_cursory = gra_eventqueuehead->cursory;
4187 			us_state &= ~GOTXY;
4188 			gra_eventqueuehead++;
4189 			if (gra_eventqueuehead >= &gra_eventqueue[EVENTQUEUESIZE])
4190 				gra_eventqueuehead = gra_eventqueue;
4191 
4192 			/* mouse click terminates */
4193 			if ((gra_inputstate&ISBUTTON) != 0)
4194 			{
4195 				ttyputerr(_("Session playback aborted by mouse click"));
4196 				xclose(us_logplay);
4197 				us_logplay = NULL;
4198 				gra_inputstate = NOEVENT;
4199 				return;
4200 			}
4201 
4202 			/* stop if this is the last event in the queue */
4203 			if (gra_eventqueuehead == gra_eventqueuetail) break;
4204 
4205 			/* stop if the mouse moved */
4206 			if (gra_cursorx != lastx || gra_cursory != lasty) break;
4207 
4208 			/* stop if this and the next event are not motion */
4209 			if ((gra_inputstate&MOTION) == 0) break;
4210 			if ((gra_eventqueuehead->inputstate&MOTION) == 0) break;
4211 		}
4212 		lastx = gra_cursorx;   lasty = gra_cursory;
4213 
4214 		if (gra_inputstate != NOEVENT || gra_firstactivedialog != NOTDIALOG)
4215 		{
4216 			/* replace this with the logged action */
4217 			(void)gra_loggetnextaction(0);
4218 		}
4219 	} else
4220 	{
4221 		/* normal interaction: get next event */
4222 		gra_inputstate = NOEVENT;
4223 		while (gra_eventqueuehead != gra_eventqueuetail)
4224 		{
4225 			gra_inputstate = gra_eventqueuehead->inputstate;
4226 			gra_inputspecial = gra_eventqueuehead->special;
4227 			gra_cursorx = gra_eventqueuehead->cursorx;
4228 			gra_cursory = gra_eventqueuehead->cursory;
4229 			gra_eventtime = gra_eventqueuehead->eventtime;
4230 			us_state &= ~GOTXY;
4231 			gra_eventqueuehead++;
4232 			if (gra_eventqueuehead >= &gra_eventqueue[EVENTQUEUESIZE])
4233 				gra_eventqueuehead = gra_eventqueue;
4234 
4235 			/* stop if this is the last event in the queue */
4236 			if (gra_eventqueuehead == gra_eventqueuetail) break;
4237 
4238 			/* stop if this and the next event are not motion */
4239 			if ((gra_inputstate&MOTION) == 0) break;
4240 			if ((gra_eventqueuehead->inputstate&MOTION) == 0) break;
4241 		}
4242 	}
4243 
4244 	/* record valid events */
4245 	if (gra_inputstate != NOEVENT)
4246 	{
4247 		if (gra_inputstate == WINDOWSIZE || gra_inputstate == WINDOWMOVE)
4248 		{
4249 			windowindex = gra_cursorx;
4250 			x = gra_cursory >> 16;
4251 			y = gra_cursory & 0xFFFF;
4252 			gra_logwriteaction(gra_inputstate, 0, x, y, (void *)windowindex);
4253 			gra_inputstate = NOEVENT;
4254 		} else
4255 		{
4256 			windowindex = -1;
4257 			if (el_curwindowframe != NOWINDOWFRAME) windowindex = el_curwindowframe->windindex;
4258 			gra_logwriteaction(gra_inputstate, gra_inputspecial, gra_cursorx,
4259 				gra_cursory, (void *)windowindex);
4260 		}
4261 
4262 		/* handle menu events */
4263 		if (gra_inputstate == MENUEVENT)
4264 		{
4265 			gra_inputstate = NOEVENT;
4266 			i = gra_cursorx;
4267 			if (i >= 0 && i < gra_pulldownmenucount)
4268 			{
4269 				pm = us_getpopupmenu(gra_pulldowns[i]);
4270 				for(trueItem=j=0; j < pm->total; j++)
4271 				{
4272 					mi = &pm->list[j];
4273 					if (mi->response->active < 0 && *mi->attribute == 0) continue;
4274 					trueItem++;
4275 					if (trueItem != gra_cursory) continue;
4276 					us_state |= DIDINPUT;
4277 					us_state &= ~GOTXY;
4278 					setdefaultcursortype(us_normalcursor);
4279 
4280 					/* special case for "system" commands": don't erase */
4281 					us_forceeditchanges();
4282 					saveslice = el_inslice;
4283 					el_inslice = TRUE;
4284 					if ((us_tool->toolstate&ECHOBIND) != 0) verbose = TRUE; else
4285 						verbose = FALSE;
4286 					us_execute(mi->response, verbose, TRUE, TRUE);
4287 					el_inslice = saveslice;
4288 					db_setcurrenttool(us_tool);
4289 					setactivity(mi->attribute);
4290 					break;
4291 				}
4292 			}
4293 		}
4294 	}
4295 }
4296 
gra_buttonaction(int state,UINT nFlags,CPoint point,CWnd * frm)4297 void gra_buttonaction(int state, UINT nFlags, CPoint point, CWnd *frm)
4298 {
4299 	WINDOWFRAME *wf;
4300 	INTBIG event, itemtype, x, y, item;
4301 	TDIALOG *dia;
4302 
4303 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4304 		if ((CChildFrame *)wf->wndframe == frm) break;
4305 	if (wf == NOWINDOWFRAME)
4306 	{
4307 		for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
4308 			if (dia->window == frm) break;
4309 		if (dia == NOTDIALOG) return;
4310 
4311 		item = gra_getdialogitem(dia, point.x, point.y);
4312 		if (item == 0) return;
4313 		itemtype = dia->itemdesc->list[item-1].type;
4314 		if ((itemtype&ITEMTYPE) == USERDRAWN || (itemtype&ITEMTYPE) == ICON ||
4315 			(dia->modelessitemhit != 0 && gra_trackingdialog == 0))
4316 		{
4317 			if (state == 0)
4318 			{
4319 				gra_itemclicked((CElectricDialog *)frm, item-1+ID_DIALOGITEM_0);
4320 				return;
4321 			}
4322 			if (state == 2)
4323 			{
4324 				gra_itemdoubleclicked((CElectricDialog *)frm, item-1);
4325 				return;
4326 			}
4327 		}
4328 	}
4329 
4330 	/* set appropriate button */
4331 	if ((nFlags & MK_LBUTTON) != 0) event = ISBUTTON|ISLEFT;
4332 	if ((nFlags & MK_MBUTTON) != 0) event = ISBUTTON|ISMIDDLE;
4333 	if ((nFlags & MK_RBUTTON) != 0) event = ISBUTTON|ISRIGHT;
4334 
4335 	/* add in extras */
4336 	if ((nFlags & MK_SHIFT) != 0) event |= SHIFTISDOWN;
4337 	if ((nFlags & MK_CONTROL) != 0) event |= CONTROLISDOWN;
4338 	if ((GetKeyState(VK_MENU)&0x8000) != 0) event |= ALTISDOWN;
4339 	if (state == 2 && (event&(SHIFTISDOWN|CONTROLISDOWN|ALTISDOWN)) == 0)
4340 		event |= DOUBLECLICK;
4341 	if (state == 1)
4342 		event |= BUTTONUP;
4343 
4344 	x = point.x;
4345 	if (wf == NOWINDOWFRAME) y = point.y; else
4346 		y = wf->revy - point.y;
4347 	us_state |= DIDINPUT;
4348 	gra_addeventtoqueue(event, 0, x, y);
4349 }
4350 
gra_mouseaction(UINT nFlags,CPoint point,CWnd * frm)4351 void gra_mouseaction(UINT nFlags, CPoint point, CWnd *frm)
4352 {
4353 	WINDOWFRAME *wf;
4354 	INTBIG event, x, y, inmenu;
4355 	REGISTER VARIABLE *var;
4356 	CHAR *str;
4357 	static BOOLEAN overrodestatus = FALSE;
4358 	COMMANDBINDING commandbinding;
4359 	TDIALOG *dia;
4360 
4361 	/* find the appropriate window */
4362 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4363 		if ((CChildFrame *)wf->wndframe == frm) break;
4364 	inmenu = 0;
4365 	if (wf != NOWINDOWFRAME)
4366 	{
4367 		/* report the menu if over one */
4368 		if ((nFlags & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) == 0 && wf->floating)
4369 		{
4370 			gra_cursorx = point.x;
4371 			gra_cursory = wf->revy - point.y;
4372 			us_state &= ~GOTXY;
4373 			if (us_menuxsz > 0 && us_menuysz > 0)
4374 			{
4375 				x = (gra_cursorx-us_menulx) / us_menuxsz;
4376 				y = (gra_cursory-us_menuly) / us_menuysz;
4377 				if (x >= 0 && y >= 0 && x < us_menux && y < us_menuy)
4378 				{
4379 					var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
4380 					if (var != NOVARIABLE)
4381 					{
4382 						if (us_menupos <= 1) str = ((CHAR **)var->addr)[y * us_menux + x]; else
4383 							str = ((CHAR **)var->addr)[x * us_menuy + y];
4384 						us_parsebinding(str, &commandbinding);
4385 						if (*commandbinding.command != 0)
4386 						{
4387 							if (commandbinding.nodeglyph != NONODEPROTO)
4388 							{
4389 								ttysetstatusfield(NOWINDOWFRAME, us_statusarc, describearcproto(us_curarcproto), TRUE);
4390 								ttysetstatusfield(NOWINDOWFRAME, us_statusnode, us_describemenunode(&commandbinding), TRUE);
4391 								overrodestatus = TRUE;
4392 								inmenu = 1;
4393 							}
4394 							if (commandbinding.arcglyph != NOARCPROTO)
4395 							{
4396 								ttysetstatusfield(NOWINDOWFRAME, us_statusarc, describearcproto(commandbinding.arcglyph), TRUE);
4397 								if (us_curnodeproto == NONODEPROTO) str = x_(""); else
4398 									str = describenodeproto(us_curnodeproto);
4399 								ttysetstatusfield(NOWINDOWFRAME, us_statusnode, str, TRUE);
4400 								overrodestatus = TRUE;
4401 								inmenu = 1;
4402 							}
4403 						}
4404 						us_freebindingparse(&commandbinding);
4405 					}
4406 				}
4407 			}
4408 		}
4409 	}
4410 	if (inmenu == 0 && overrodestatus)
4411 	{
4412 		ttysetstatusfield(NOWINDOWFRAME, us_statusarc, describearcproto(us_curarcproto), TRUE);
4413 		if (us_curnodeproto == NONODEPROTO) str = x_(""); else
4414 			str = describenodeproto(us_curnodeproto);
4415 		ttysetstatusfield(NOWINDOWFRAME, us_statusnode, str, TRUE);
4416 		overrodestatus = FALSE;
4417 	}
4418 
4419 	if (gra_inputstate != NOEVENT) return;
4420 
4421 	if (wf == NOWINDOWFRAME)
4422 	{
4423 		for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
4424 			if (dia->window == frm) break;
4425 		if (dia == NOTDIALOG) return;
4426 		if (gra_dodialogisinsideuserdrawn(dia, point.x, point.y) == 0) return;
4427 	}
4428 
4429 	x = point.x;
4430 	if (wf == NOWINDOWFRAME) y = point.y; else
4431 		y = wf->revy - point.y;
4432 	event = MOTION;
4433 
4434 	if ((nFlags & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) == 0)
4435 		event |= BUTTONUP;
4436 	gra_addeventtoqueue(event, 0, x, y);
4437 }
4438 
4439 extern "C" INTBIG sim_window_wavexbar;
4440 
gra_setpropercursor(CWnd * frm,int x,int y)4441 int gra_setpropercursor(CWnd *frm, int x, int y)
4442 {
4443 	WINDOWFRAME *wf;
4444 	INTBIG lx, hx, ly, hy, xfx, xfy;
4445 	REGISTER WINDOWPART *w;
4446 	REGISTER EDITOR *e;
4447 
4448 	/* find the appropriate window */
4449 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4450 		if ((CChildFrame *)wf->wndframe == frm) break;
4451 	if (wf != NOWINDOWFRAME)
4452 	{
4453 		/* if the window is known, set the cursor appropriately */
4454 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
4455 		{
4456 			if (w->frame != wf) continue;
4457 			y = wf->revy - y;
4458 
4459 			/* see if the cursor is over a window partition separator */
4460 			us_gettruewindowbounds(w, &lx, &hx, &ly, &hy);
4461 			if (x >= lx-1 && x <= lx+1 && y > ly+1 && y < hy-1 &&
4462 				us_hasotherwindowpart(lx-10, y, w))
4463 			{
4464 				setdefaultcursortype(LRCURSOR);
4465 				return(1);
4466 			} else if (x >= hx-1 && x <= hx+1 && y > ly+1 && y < hy-1 &&
4467 				us_hasotherwindowpart(hx+10, y, w))
4468 			{
4469 				setdefaultcursortype(LRCURSOR);
4470 				return(1);
4471 			} else if (y >= ly-1 && y <= ly+1 && x > lx+1 && x < hx-1 &&
4472 				us_hasotherwindowpart(x, ly-10, w))
4473 			{
4474 				setdefaultcursortype(UDCURSOR);
4475 				return(1);
4476 			} else if (y >= hy-1 && y <= hy+1 && x > lx+1 && x < hx-1 &&
4477 				us_hasotherwindowpart(x, hy+10, w))
4478 			{
4479 				setdefaultcursortype(UDCURSOR);
4480 				return(1);
4481 			}
4482 			if (x < w->uselx || x > w->usehx || y < w->usely || y > w->usehy) continue;
4483 			if ((w->state&WINDOWTYPE) == WAVEFORMWINDOW)
4484 			{
4485 				xfx = muldiv(x - w->uselx, w->screenhx - w->screenlx,
4486 					w->usehx - w->uselx) + w->screenlx;
4487 				xfy = muldiv(y - w->usely, w->screenhy - w->screenly,
4488 					w->usehy - w->usely) + w->screenly;
4489 				if (abs(xfx - sim_window_wavexbar) < 2 && xfy >= 560)
4490 				{
4491 					setdefaultcursortype(LRCURSOR);
4492 					return(1);
4493 				}
4494 			}
4495 			if ((w->state&WINDOWTYPE) == POPTEXTWINDOW ||
4496 				(w->state&WINDOWTYPE) == TEXTWINDOW)
4497 			{
4498 				e = w->editor;
4499 				if ((e->state&EDITORTYPE) == PACEDITOR)
4500 				{
4501 					if (x <= w->usehx - SBARWIDTH && y >= w->usely + SBARWIDTH && y < e->revy)
4502 					{
4503 						setdefaultcursortype(IBEAMCURSOR);
4504 						return(1);
4505 					}
4506 				}
4507 			}
4508 			break;
4509 		}
4510 	}
4511 	setdefaultcursortype(us_normalcursor);
4512 	return(0);
4513 }
4514 
gra_mousewheelaction(UINT nFlags,short zDelta,CPoint point,CWnd * frm)4515 void gra_mousewheelaction(UINT nFlags, short zDelta, CPoint point, CWnd *frm)
4516 {
4517 	WINDOWFRAME *wf;
4518 	INTBIG event;
4519 	INTBIG x, y;
4520 
4521 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4522 		if ((CChildFrame *)wf->wndframe == frm) break;
4523 	if (wf == NOWINDOWFRAME) return;
4524 
4525 	/* set appropriate "button" */
4526 	if (zDelta > 0) event = ISBUTTON|ISWHLFWD; else
4527 		event = ISBUTTON|ISWHLBKW;
4528 
4529 	/* add in extras */
4530 	if ((nFlags & MK_SHIFT) != 0) event |= SHIFTISDOWN;
4531 	if ((nFlags & MK_CONTROL) != 0) event |= CONTROLISDOWN;
4532 	if ((GetKeyState(VK_MENU)&0x8000) != 0) event |= ALTISDOWN;
4533 
4534 	x = point.x;
4535 	if (wf == NOWINDOWFRAME) y = point.y; else
4536 		y = wf->revy - point.y;
4537 	us_state |= DIDINPUT;
4538 	gra_addeventtoqueue(event, 0, x, y);
4539 }
4540 
gra_keyaction(UINT nChar,INTBIG special,UINT nRepCnt)4541 void gra_keyaction(UINT nChar, INTBIG special, UINT nRepCnt)
4542 {
4543 	POINT pt, p2;
4544 	CWnd *wnd;
4545 	INTBIG event, x, y;
4546 
4547 	/* determine corner of window */
4548 	if (el_curwindowframe == NOWINDOWFRAME) wnd = AfxGetMainWnd(); else
4549 		wnd = (CWnd *)el_curwindowframe->wndframe;
4550 	p2.x = p2.y = 0;
4551 	wnd->MapWindowPoints(0, &p2, 1);
4552 
4553 	/* determine current cursor coordinates */
4554 	GetCursorPos(&pt);
4555 	x = pt.x - p2.x;
4556 	y = pt.y - p2.y;
4557 	if (el_curwindowframe != NOWINDOWFRAME)
4558 		y = el_curwindowframe->revy - y;
4559 
4560 	/* queue the event */
4561 	if (nChar == 015) nChar = 012;
4562 	event = (nChar & CHARREAD) | ISKEYSTROKE;
4563 	us_state |= DIDINPUT;
4564 	gra_addeventtoqueue(event, special, x, y);
4565 }
4566 
gra_setdefaultcursor(void)4567 void gra_setdefaultcursor(void)
4568 {
4569 	int curstate;
4570 
4571 	curstate = us_cursorstate;
4572 	us_cursorstate++;
4573 	setdefaultcursortype(curstate);
4574 }
4575 
gra_activateframe(CChildFrame * frame,BOOL bActivate)4576 void gra_activateframe(CChildFrame *frame, BOOL bActivate)
4577 {
4578 	REGISTER WINDOWFRAME *wf;
4579 	REGISTER WINDOWPART *w;
4580 
4581 	if (!bActivate)
4582 	{
4583 		if (frame == gra_messageswindow) gra_messagescurrent = FALSE; else
4584 			el_curwindowframe = NOWINDOWFRAME;
4585 		return;
4586 	}
4587 
4588 	if (frame == gra_messageswindow) gra_messagescurrent = TRUE; else
4589 	{
4590 		for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4591  			if ((CChildFrame *)wf->wndframe == frame) break;
4592 		if (wf != NOWINDOWFRAME)
4593 		{
4594 			el_curwindowframe = wf;
4595 			gra_cureditwindowframe = wf;
4596 
4597 			/* see if the change of window frame invalidates the current window */
4598 			if (el_curwindowpart == NOWINDOWPART || el_curwindowpart->frame != wf)
4599 			{
4600 				/* must choose new window (if not broadcasting) */
4601 				if (db_broadcasting == 0)
4602 				{
4603 					for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
4604 					{
4605 						if (w->frame == wf)
4606 						{
4607 							(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key,
4608 								(INTBIG)w, VWINDOWPART|VDONTSAVE);
4609 							(void)setval((INTBIG)el_curlib, VLIBRARY, x_("curnodeproto"),
4610 								(INTBIG)w->curnodeproto, VNODEPROTO);
4611 							break;
4612 						}
4613 					}
4614 				}
4615 			}
4616 		}
4617 	}
4618 
4619 	/* if another windows was activated, make sure the palette is on top */
4620 	gra_floatpalette();
4621 }
4622 
4623 /*
4624  * Routine to ensure that the floating component menu is on top
4625  */
gra_floatpalette(void)4626 void gra_floatpalette(void)
4627 {
4628 	REGISTER WINDOWFRAME *wf;
4629 
4630 	if (gra_creatingwindow) return;
4631 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4632 	{
4633 		if (!wf->floating) continue;
4634 		((CChildFrame *)wf->wndframe)->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0,
4635 			SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
4636 		break;
4637 	}
4638 }
4639 
gra_closeframe(CChildFrame * frame)4640 int gra_closeframe(CChildFrame *frame)
4641 {
4642 	WINDOWFRAME *wf;
4643 	CHAR *par[2];
4644 
4645 	if (frame == gra_messageswindow)
4646 	{
4647 		gra_messagescurrent = FALSE;
4648 		gra_messageswindow = 0;
4649 		return(1);
4650 	}
4651 
4652 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4653 	{
4654 		if (frame == (CChildFrame *)wf->wndframe) break;
4655 	}
4656 	if (wf == NOWINDOWFRAME) return(0);
4657 
4658 //	((CChildFrame *)wf->wndframe)->SetActiveWindow();
4659 	par[0] = x_("delete");
4660 	us_window(1, par);
4661 	return(0);
4662 }
4663 
gra_closeworld(void)4664 int gra_closeworld(void)
4665 {
4666 	if (us_preventloss(NOLIBRARY, 0, TRUE)) return(1);  /* keep working */
4667 	bringdown();
4668 	return(0);
4669 }
4670 
gra_repaint(CChildFrame * frame,CPaintDC * dc)4671 void gra_repaint(CChildFrame *frame, CPaintDC *dc)
4672 {
4673 	WINDOWFRAME *wf;
4674 
4675 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4676 	{
4677 		if (frame == (CChildFrame *)wf->wndframe)
4678 		{
4679 			dc->BitBlt(0, 0, wf->swid, wf->shei, (CDC *)wf->hDCOff, 0, 0, SRCCOPY);
4680 			break;
4681 		}
4682 	}
4683 	gra_floatpalette();
4684 }
4685 
gra_resize(CChildFrame * frame,int cx,int cy)4686 void gra_resize(CChildFrame *frame, int cx, int cy)
4687 {
4688 	WINDOWFRAME *wf;
4689 	RECT r;
4690 	WINDOWPLACEMENT wp;
4691 
4692 	if (frame->IsIconic()) return;
4693 
4694 	if (frame == gra_messageswindow)
4695 	{
4696 		gra_messageswindow->GetWindowPlacement(&wp);
4697 		r = wp.rcNormalPosition;
4698 		gra_messagesleft = r.left;
4699 		gra_messagesright = r.right;
4700 		gra_messagestop = r.top;
4701 		gra_messagesbottom = r.bottom;
4702 		return;
4703 	}
4704 
4705 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4706 	{
4707 		if (frame == (CChildFrame *)wf->wndframe)
4708 		{
4709 			sizewindowframe(wf, cx, cy);
4710 			gra_redrawdisplay(wf);
4711 			gra_addeventtoqueue(WINDOWSIZE, 0, wf->windindex, ((cx & 0xFFFF) << 16) | (cy & 0xFFFF));
4712 			break;
4713 		}
4714 	}
4715 	gra_floatpalette();
4716 }
4717 
gra_movedwindow(CChildFrame * frame,int x,int y)4718 void gra_movedwindow(CChildFrame *frame, int x, int y)
4719 {
4720 	RECT fr, cr, r;
4721 	int framewid, framehei;
4722 	WINDOWPLACEMENT wp;
4723 	WINDOWFRAME *wf;
4724 
4725 	if (frame == gra_messageswindow)
4726 	{
4727 		gra_messageswindow->GetWindowPlacement(&wp);
4728 		r = wp.rcNormalPosition;
4729 		gra_messagesleft = r.left;
4730 		gra_messagesright = r.right;
4731 		gra_messagestop = r.top;
4732 		gra_messagesbottom = r.bottom;
4733 		return;
4734 	}
4735 
4736 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4737 	{
4738 		if (frame == (CChildFrame *)wf->wndframe)
4739 		{
4740 			((CChildFrame *)wf->wndframe)->GetWindowPlacement(&wp);
4741 			fr = wp.rcNormalPosition;
4742 			((CChildFrame *)wf->wndframe)->GetClientRect(&cr);
4743 			framewid = (fr.right - fr.left) - (cr.right - cr.left);
4744 			framehei = (fr.bottom - fr.top) - (cr.bottom - cr.top);
4745 			x -= framewid/2;   y -= (framehei-framewid/2);
4746 			gra_addeventtoqueue(WINDOWMOVE, 0, wf->windindex, ((x & 0xFFFF) << 16) | (y & 0xFFFF));
4747 			break;
4748 		}
4749 	}
4750 	gra_floatpalette();
4751 }
4752 
gra_resizemain(int cx,int cy)4753 void gra_resizemain(int cx, int cy)
4754 {
4755 	WINDOWPLACEMENT wndpl;
4756 	CHAR tmp[256], subkey[256], value[256];
4757 	CMainFrame *wnd;
4758 
4759 	gra_redrawstatusindicators();
4760 	if (gra_palettewindowframe != NOWINDOWFRAME)
4761 		us_drawmenu(-1, gra_palettewindowframe);
4762 
4763 	/* remember this size in the registry */
4764 	wnd = (CMainFrame *)AfxGetMainWnd();
4765 	wnd->GetWindowPlacement(&wndpl);
4766 	estrcpy(subkey, x_("Software\\Static Free Software\\Electric"));
4767 	estrcpy(value, x_("Window_Location"));
4768 	esnprintf(tmp, 256, x_("%ld,%ld,%ld,%ld,%d"), wndpl.rcNormalPosition.top, wndpl.rcNormalPosition.left,
4769 		wndpl.rcNormalPosition.bottom, wndpl.rcNormalPosition.right, wndpl.showCmd);
4770 	(void)gra_setregistry(HKEY_LOCAL_MACHINE, subkey, value, tmp);
4771 }
4772 
4773 /* handle interrupts */
gra_onint(void)4774 void gra_onint(void)
4775 {
4776 	el_pleasestop = 1;
4777 	ttyputerr(_("Interrupted..."));
4778 }
4779 
4780 /*************************** SESSION LOGGING ROUTINES ***************************/
4781 
4782 extern "C" {
4783 /* Session Playback */
4784 DIALOGITEM gra_sesplaydialogitems[] =
4785 {
4786  /*  1 */ {0, {100,132,124,212}, BUTTON, N_("Yes")},
4787  /*  2 */ {0, {100,8,124,88}, BUTTON, N_("No")},
4788  /*  3 */ {0, {8,8,24,232}, MESSAGE, N_("Electric has found a session log file")},
4789  /*  4 */ {0, {24,8,40,232}, MESSAGE, N_("which may be from a recent crash.")},
4790  /*  5 */ {0, {52,8,68,232}, MESSAGE, N_("Do you wish to replay this session")},
4791  /*  6 */ {0, {68,8,84,232}, MESSAGE, N_("and reconstruct the lost work?")}
4792 };
4793 DIALOG gra_sesplaydialog = {{75,75,208,316}, N_("Replay Log?"), 0, 6, gra_sesplaydialogitems, 0, 0};
4794 };
4795 
4796 /* special items for the session playback dialog: */
4797 #define DSPL_YES    1		/* Yes (button) */
4798 #define DSPL_NO     2		/* No (button) */
4799 
4800 /*
4801  * routine to create a session logging file
4802  */
logstartrecord(void)4803 void logstartrecord(void)
4804 {
4805 	UCHAR1 count;
4806 	REGISTER INTBIG itemhit, filestatus;
4807 	REGISTER LIBRARY *lib;
4808 	REGISTER WINDOWFRAME *wf;
4809 	REGISTER WINDOWPART *w;
4810 	RECT fr;
4811 	WINDOWPLACEMENT wp;
4812 	REGISTER VARIABLE *var;
4813 	REGISTER void *dia;
4814 
4815 	/* if there is already a log file, it may be from a previous crash */
4816 	filestatus = fileexistence(gra_logfile);
4817 	if (filestatus == 1 || filestatus == 3)
4818 	{
4819 		dia = DiaInitDialog(&gra_sesplaydialog);
4820 		for(;;)
4821 		{
4822 			itemhit = DiaNextHit(dia);
4823 			if (itemhit == DSPL_YES || itemhit == DSPL_NO) break;
4824 		}
4825 		DiaDoneDialog(dia);
4826 		if (itemhit == DSPL_YES)
4827 		{
4828 			if (fileexistence(gra_logfilesave) == 1)
4829 				eunlink(gra_logfilesave);
4830 			erename(gra_logfile, gra_logfilesave);
4831 			(void)logplayback(gra_logfilesave);
4832 		}
4833 	}
4834 
4835 	us_logrecord = xcreate(gra_logfile, us_filetypelog, 0, 0);
4836 	if (us_logrecord == 0) return;
4837 	gra_logbasetime = ticktime();
4838 
4839 	/* document the header */
4840 	xprintf(us_logrecord, x_("; ============= The header:\n"));
4841 	xprintf(us_logrecord, x_("; LC n           Library count\n"));
4842 	xprintf(us_logrecord, x_("; LD name file   Library read from disk\n"));
4843 	xprintf(us_logrecord, x_("; LM name file   Library not read from disk\n"));
4844 	xprintf(us_logrecord, x_("; WO n           Window offset currently 'n'\n"));
4845 	xprintf(us_logrecord, x_("; WT n           Window count\n"));
4846 	xprintf(us_logrecord, x_("; WC n x y w h   Component window, index 'n' at (x,y), size (wXh)\n"));
4847 	xprintf(us_logrecord, x_("; WE n x y w h   Edit window, index 'n' at (x,y), size (wXh)\n"));
4848 	xprintf(us_logrecord, x_("; WP n           Window has 'n' partitions\n"));
4849 	xprintf(us_logrecord, x_("; WB lx hx ly hy slx shx sly shy state cell loc    Window partition (background)\n"));
4850 	xprintf(us_logrecord, x_("; WF lx hx ly hy slx shx sly shy state cell loc    Window partition (foreground)\n"));
4851 	xprintf(us_logrecord, x_("; CI n           Current window index\n"));
4852 	xprintf(us_logrecord, x_("; CT tech        Current technology\n"));
4853 
4854 	/* log current libraries */
4855 	count = 0;
4856 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
4857 		if ((lib->userbits&HIDDENLIBRARY) == 0) count++;
4858 	xprintf(us_logrecord, x_("LC %d\n"), count);
4859 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
4860 	{
4861 		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
4862 		if ((lib->userbits&READFROMDISK) != 0)
4863 		{
4864 			xprintf(us_logrecord, x_("LD %s %s\n"), lib->libname, lib->libfile);
4865 		} else
4866 		{
4867 			xprintf(us_logrecord, x_("LM %s %s\n"), lib->libname, lib->libfile);
4868 		}
4869 	}
4870 
4871 	/* log current windows */
4872 	xprintf(us_logrecord, x_("WO %ld\n"), gra_newwindowoffset);
4873 	count = 0;
4874 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe) count++;
4875 	xprintf(us_logrecord, x_("WT %d\n"), count);
4876 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
4877 	{
4878 		((CChildFrame *)wf->wndframe)->GetWindowPlacement(&wp);
4879 		fr = wp.rcNormalPosition;
4880 		if (wf->floating != 0)
4881 		{
4882 			xprintf(us_logrecord, x_("WC %ld %ld %ld %ld %ld\n"), wf->windindex, fr.left, fr.top,
4883 				fr.right-fr.left, fr.bottom-fr.top);
4884 		} else
4885 		{
4886 			xprintf(us_logrecord, x_("WE %ld %ld %ld %ld %ld\n"), wf->windindex, fr.left, fr.top,
4887 				fr.right-fr.left, fr.bottom-fr.top);
4888 		}
4889 
4890 		count = 0;
4891 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
4892 			if (w->frame == wf) count++;
4893 		xprintf(us_logrecord, x_("WP %d\n"), count);
4894 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
4895 		{
4896 			if (w->frame != wf) continue;
4897 			if (w == el_curwindowpart)
4898 			{
4899 				xprintf(us_logrecord, x_("WF %ld %ld %ld %ld %ld %ld %ld %ld %ld %s %s\n"),
4900 					w->uselx, w->usehx, w->usely, w->usehy, w->screenlx, w->screenhx,
4901 					w->screenly, w->screenhy, w->state, describenodeproto(w->curnodeproto),
4902 					w->location);
4903 			} else
4904 			{
4905 				xprintf(us_logrecord, x_("WB %ld %ld %ld %ld %ld %ld %ld %ld %ld %s %s\n"),
4906 					w->uselx, w->usehx, w->usely, w->usehy, w->screenlx, w->screenhx,
4907 					w->screenly, w->screenhy, w->state, describenodeproto(w->curnodeproto),
4908 					w->location);
4909 			}
4910 		}
4911 	}
4912 	xprintf(us_logrecord, x_("CI %ld\n"), gra_windownumberindex);
4913 
4914 	/* log current technology (macros store this in %H) */
4915 	var = getval((INTBIG)us_tool, VTOOL, VSTRING, x_("USER_local_caph"));
4916 	if (var != NOVARIABLE)
4917 	{
4918 		/* technology name found in local variable */
4919 		xprintf(us_logrecord, x_("CT %s\n"), (CHAR *)var->addr);
4920 	} else
4921 	{
4922 		/* just write the current technology name */
4923 		xprintf(us_logrecord, x_("CT %s\n"), el_curtech->techname);
4924 	}
4925 
4926 	/* document the body */
4927 	xprintf(us_logrecord, x_("; ============= The body:\n"));
4928 	xprintf(us_logrecord, x_("; KT k x y s w t Key 'k' typed at (x,y), special 's', window 'w', time 't'\n"));
4929 	xprintf(us_logrecord, x_("; BP n x y s w t Button 'n' pressed at (x,y), special 's', window 'w', time 't'\n"));
4930 	xprintf(us_logrecord, x_("; BD n x y s w t Button 'n' double-clicked at (x,y), special 's', window 'w', time 't'\n"));
4931 	xprintf(us_logrecord, x_("; BR n x y s w t Button 'n' released at (x,y), special 's', window 'w', time 't'\n"));
4932 	xprintf(us_logrecord, x_("; MP x y s w     Motion with button pressed at (x,y), special 's', window 'w'\n"));
4933 	xprintf(us_logrecord, x_("; MR x y s w     Motion with button released at (x,y), special 's', window 'w'\n"));
4934 	xprintf(us_logrecord, x_("; ME m i t       Invoked menu 'm', item 'i', time 't'\n"));
4935 	xprintf(us_logrecord, x_("; WS w x y t     Window 'w' grows to (xXy), time 't'\n"));
4936 	xprintf(us_logrecord, x_("; WM w x y t     Window 'w' moves to (x,y), time 't'\n"));
4937 	xprintf(us_logrecord, x_("; FS path        File selected is 'path'\n"));
4938 	xprintf(us_logrecord, x_("; PM v t         Popup menu selected 'v', time 't'\n"));
4939 	xprintf(us_logrecord, x_("; DI w i         Item 'i' of dialog 'w' selected\n"));
4940 	xprintf(us_logrecord, x_("; DS w i c vals  Dialog 'w' scroll item 'i' selects 'c' lines in 'vals'\n"));
4941 	xprintf(us_logrecord, x_("; DE w i hc s    Dialog 'w' edit item 'i' hit character 'hc', text now 's'\n"));
4942 	xprintf(us_logrecord, x_("; DP w i e       Dialog 'w' popup item 'i' set to entry 'e'\n"));
4943 	xprintf(us_logrecord, x_("; DC w i v       Dialog 'w' item 'i' set to value 'v'\n"));
4944 	xprintf(us_logrecord, x_("; DM w x y       Dialog 'w' coordinates at (x,y)\n"));
4945 	xprintf(us_logrecord, x_("; DD w           Dialog 'w' done\n"));
4946 }
4947 
4948 /*
4949  * routine to begin playback of session logging file "file".  The routine
4950  * returns true if there is an error.
4951  */
logplayback(CHAR * file)4952 BOOLEAN logplayback(CHAR *file)
4953 {
4954 	CHAR *filename, tempstring[300], *pt, *start;
4955 	UCHAR1 count, wcount, i, j, cur, fromdisk;
4956 	BOOLEAN floating;
4957 	REGISTER WINDOWFRAME *wf;
4958 	RECTAREA r;
4959 	REGISTER FILE *saveio;
4960 	REGISTER WINDOWPART *w, *nextw;
4961 	REGISTER INTBIG wid, hei, uselx, usehx, usely, usehy, sindex;
4962 	REGISTER INTBIG screenlx, screenhx, screenly, screenhy, state;
4963 	REGISTER LIBRARY *lib, *firstlib;
4964 
4965 	us_logplay = xopen(file, us_filetypelog, x_(""), &filename);
4966 	if (us_logplay == NULL) return(TRUE);
4967 	ttyputmsg(_("Playing log file..."));
4968 	ttyputmsg(_("   Move mouse continuously to advance playback"));
4969 	ttyputmsg(_("   Click mouse to abort playback"));
4970 	gra_lastplaybacktime = 0;
4971 
4972 	/* get current libraries */
4973 	(void)gra_logreadline(tempstring, 300);
4974 	if (estrncmp(tempstring, x_("LC"), 2) != 0)
4975 	{
4976 		ttyputerr(_("Log file is corrupt (error %d)"), 1);
4977 		return(TRUE);
4978 	}
4979 	count = eatoi(&tempstring[3]);
4980 	firstlib = NOLIBRARY;
4981 	for(i=0; i<count; i++)
4982 	{
4983 		(void)gra_logreadline(tempstring, 300);
4984 		if (estrncmp(tempstring, x_("LD"), 2) == 0)
4985 		{
4986 			fromdisk = 1;
4987 		} else if (estrncmp(tempstring, x_("LM"), 2) == 0)
4988 		{
4989 			fromdisk = 0;
4990 		} else
4991 		{
4992 			ttyputerr(_("Log file is corrupt (error %d)"), 2);
4993 			return(TRUE);
4994 		}
4995 		start = &tempstring[3];
4996 		for(pt = start; *pt != 0; pt++) if (*pt == ' ') break;
4997 		if (*pt == 0)
4998 		{
4999 			ttyputerr(_("Log file is corrupt (error %d)"), 3);
5000 			return(TRUE);
5001 		}
5002 		*pt++ = 0;
5003 		lib = getlibrary(start);
5004 		if (lib == NOLIBRARY)
5005 		{
5006 			/* read library file "gra_localstring" */
5007 			lib = newlibrary(start, pt);
5008 			if (lib == NOLIBRARY) continue;
5009 			if (fromdisk != 0)
5010 			{
5011 				saveio = us_logplay;
5012 				us_logplay = 0;
5013 				(void)asktool(io_tool, x_("read"), (INTBIG)lib, (INTBIG)x_("binary"), 0);
5014 				us_logplay = saveio;
5015 			}
5016 		}
5017 		if (firstlib == NOLIBRARY) firstlib = lib;
5018 	}
5019 	selectlibrary(firstlib, TRUE);
5020 
5021 	/* delete all existing windows */
5022 	for(w = el_topwindowpart; w != NOWINDOWPART; w = nextw)
5023 	{
5024 		nextw = w->nextwindowpart;
5025 		db_retractwindowpart(w);
5026 	}
5027 	el_curwindowpart = NOWINDOWPART;
5028 
5029 	/* get current windows */
5030 	(void)gra_logreadline(tempstring, 300);
5031 	if (estrncmp(tempstring, x_("WO"), 2) != 0)
5032 	{
5033 		ttyputerr(_("Log file is corrupt (error %d)"), 4);
5034 		return(TRUE);
5035 	}
5036 	gra_newwindowoffset = eatoi(&tempstring[3]);
5037 
5038 	(void)gra_logreadline(tempstring, 300);
5039 	if (estrncmp(tempstring, x_("WT"), 2) != 0)
5040 	{
5041 		ttyputerr(_("Log file is corrupt (error %d)"), 5);
5042 		return(TRUE);
5043 	}
5044 	count = eatoi(&tempstring[3]);
5045 
5046 	for(i=0; i<count; i++)
5047 	{
5048 		(void)gra_logreadline(tempstring, 300);
5049 		if (estrncmp(tempstring, x_("WC"), 2) == 0)
5050 		{
5051 			floating = 1;
5052 		} else if (estrncmp(tempstring, x_("WE"), 2) == 0)
5053 		{
5054 			floating = 0;
5055 		} else
5056 		{
5057 			ttyputerr(_("Log file is corrupt (error %d)"), 6);
5058 			return(TRUE);
5059 		}
5060 		pt = &tempstring[3];
5061 		sindex = eatoi(getkeyword(&pt, x_(" ")));
5062 		r.left = eatoi(getkeyword(&pt, x_(" ")));
5063 		r.top = eatoi(getkeyword(&pt, x_(" ")));
5064 		wid = eatoi(getkeyword(&pt, x_(" ")));
5065 		hei = eatoi(getkeyword(&pt, x_(" ")));
5066 		r.right = r.left + (INTSML)wid;
5067 		r.bottom = r.top + (INTSML)hei;
5068 		if (floating)
5069 		{
5070 			/* get the floating window frame */
5071 			for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
5072 				if (wf->floating) break;
5073 			if (wf == NOWINDOWFRAME) wf = newwindowframe(TRUE, &r);
5074 		} else
5075 		{
5076 			/* create a new window frame */
5077 			wf = newwindowframe(FALSE, &r);
5078 		}
5079 		wf->windindex = sindex;
5080 
5081 		(void)gra_logreadline(tempstring, 300);
5082 		if (estrncmp(tempstring, x_("WP"), 2) != 0)
5083 		{
5084 			ttyputerr(_("Log file is corrupt (error %d)"), 7);
5085 			return(TRUE);
5086 		}
5087 		wcount = eatoi(&tempstring[3]);
5088 		for(j=0; j<wcount; j++)
5089 		{
5090 			(void)gra_logreadline(tempstring, 300);
5091 			if (estrncmp(tempstring, x_("WB"), 2) == 0)
5092 			{
5093 				cur = 0;
5094 			} else if (estrncmp(tempstring, x_("WF"), 2) == 0)
5095 			{
5096 				cur = 1;
5097 			} else
5098 			{
5099 				ttyputerr(_("Log file is corrupt (error %d)"), 8);
5100 				return(TRUE);
5101 			}
5102 			pt = &tempstring[3];
5103 			uselx = eatoi(getkeyword(&pt, x_(" ")));
5104 			usehx = eatoi(getkeyword(&pt, x_(" ")));
5105 			usely = eatoi(getkeyword(&pt, x_(" ")));
5106 			usehy = eatoi(getkeyword(&pt, x_(" ")));
5107 			screenlx = eatoi(getkeyword(&pt, x_(" ")));
5108 			screenhx = eatoi(getkeyword(&pt, x_(" ")));
5109 			screenly = eatoi(getkeyword(&pt, x_(" ")));
5110 			screenhy = eatoi(getkeyword(&pt, x_(" ")));
5111 			state = eatoi(getkeyword(&pt, x_(" ")));
5112 			start = getkeyword(&pt, x_(" "));
5113 			while (*pt != 0 && *pt != ' ') pt++;
5114 			if (*pt == ' ') pt++;
5115 
5116 			w = newwindowpart(pt, NOWINDOWPART);
5117 			w->buttonhandler = DEFAULTBUTTONHANDLER;
5118 			w->charhandler = DEFAULTCHARHANDLER;
5119 			w->changehandler = DEFAULTCHANGEHANDLER;
5120 			w->termhandler = DEFAULTTERMHANDLER;
5121 			w->redisphandler = DEFAULTREDISPHANDLER;
5122 			w->uselx = uselx;   w->usehx = usehx;
5123 			w->usely = usely;   w->usehy = usehy;
5124 			w->screenlx = screenlx;   w->screenhx = screenhx;
5125 			w->screenly = screenly;   w->screenhy = screenhy;
5126 			computewindowscale(w);
5127 			w->state = state;
5128 			w->curnodeproto = getnodeproto(start);
5129 			w->frame = wf;
5130 			if (cur != 0) el_curwindowpart = w;
5131 			us_redisplay(w);
5132 		}
5133 	}
5134 	(void)gra_logreadline(tempstring, 300);
5135 	if (estrncmp(tempstring, x_("CI"), 2) != 0)
5136 	{
5137 		ttyputerr(_("Log file is corrupt (error %d)"), 9);
5138 		return(TRUE);
5139 	}
5140 	gra_windownumberindex = eatoi(&tempstring[3]);
5141 
5142 	/* switch to proper technology */
5143 	(void)gra_logreadline(tempstring, 300);
5144 	if (estrncmp(tempstring, x_("CT"), 2) != 0)
5145 	{
5146 		ttyputerr(_("Log file is corrupt (error %d)"), 10);
5147 		return(TRUE);
5148 	}
5149 	us_ensurepropertechnology(NONODEPROTO, &tempstring[3], TRUE);
5150 
5151 	return(FALSE);
5152 }
5153 
5154 /*
5155  * routine to terminate session logging
5156  */
logfinishrecord(void)5157 void logfinishrecord(void)
5158 {
5159 	if (us_logrecord != NULL)
5160 	{
5161 		xclose(us_logrecord);
5162 		if (fileexistence(gra_logfilesave) == 1)
5163 			eunlink(gra_logfilesave);
5164 		erename(gra_logfile, gra_logfilesave);
5165 	}
5166 	us_logrecord = NULL;
5167 }
5168 
5169 /*
5170  * Routine to log an event (if logging) of type "inputstate".
5171  * The event has parameters (cursorx,cursory) and "extradata", depending on "inputstate":
5172  *   WINDOWSIZE:    window "extradata" is now "cursorx" x "cursory"
5173  *   WINDOWMOVE:    window "extradata" is now at (cursorx, cursory)
5174  *   MENUEVENT:     selected menu "cursorx", item "cursory"
5175  *   FILEREPLY:     file selected by standard-file dialog is in "extradata"
5176  *   POPUPSELECT:   popup menu returned "cursorx"
5177  *   DIAITEMCLICK:  dialog "special" item "cursorx" clicked
5178  *   DIASCROLLSEL:  dialog "special" scroll item "cursorx" set to "cursory" entries in "extradata"
5179  *   DIAEDITTEXT:   dialog "special" edit item "cursorx" changed to "extradata"
5180  *   DIAPOPUPSEL:   dialog "special" popup item "cursorx" set to entry "cursory"
5181  *   DIASETCONTROL: dialog "special" control item "cursorx" changed to "cursory"
5182  *   DIAENDDIALOG:  dialog "special" terminated
5183  *   all others:    cursor in (cursorx,cursory) and window index in "extradata"
5184  */
gra_logwriteaction(INTBIG inputstate,INTBIG special,INTBIG cursorx,INTBIG cursory,void * extradata)5185 void gra_logwriteaction(INTBIG inputstate, INTBIG special, INTBIG cursorx, INTBIG cursory,
5186 	void *extradata)
5187 {
5188 	REGISTER CHAR *filename;
5189 	REGISTER INTBIG i, j, trueItem;
5190 	REGISTER POPUPMENU *pm;
5191 	REGISTER POPUPMENUITEM *mi;
5192 
5193 	if (us_logrecord == NULL) return;
5194 
5195 	/* ignore redundant cursor motion */
5196 	if (inputstate == MOTION || inputstate == (MOTION|BUTTONUP))
5197 	{
5198 		i = (INTBIG)extradata;
5199 		if (inputstate == gra_lastloggedaction && cursorx == gra_lastloggedx &&
5200 			cursory == gra_lastloggedy && i == gra_lastloggedindex)
5201 				return;
5202 	}
5203 	gra_lastloggedaction = inputstate;
5204 	gra_lastloggedx = cursorx;
5205 	gra_lastloggedy = cursory;
5206 	gra_lastloggedindex = (INTBIG)extradata;
5207 
5208 	if ((inputstate&MOTION) != 0)
5209 	{
5210 		if ((inputstate&BUTTONUP) != 0)
5211 		{
5212 			xprintf(us_logrecord, x_("MR %ld %ld %ld %ld\n"),
5213 				cursorx, cursory, special, extradata);
5214 		} else
5215 		{
5216 			xprintf(us_logrecord, x_("MP %ld %ld %ld %ld\n"),
5217 				cursorx, cursory, special, extradata);
5218 		}
5219 	} else if ((inputstate&ISKEYSTROKE) != 0)
5220 	{
5221 		xprintf(us_logrecord, x_("KT '%s' %ld %ld %ld %ld\n"),
5222 			us_describeboundkey((INTSML)(inputstate&CHARREAD), special, 1),
5223 				cursorx, cursory, extradata, ticktime()-gra_logbasetime);
5224 	} else if ((inputstate&ISBUTTON) != 0)
5225 	{
5226 		if ((inputstate&BUTTONUP) != 0)
5227 		{
5228 			xprintf(us_logrecord, x_("BR %ld %ld %ld %ld %ld %ld\n"),
5229 				inputstate&(WHICHBUTTON|SHIFTISDOWN|ALTISDOWN|CONTROLISDOWN),
5230 					cursorx, cursory, special, extradata, ticktime()-gra_logbasetime);
5231 		} else
5232 		{
5233 			if ((inputstate&DOUBLECLICK) != 0)
5234 			{
5235 				xprintf(us_logrecord, x_("BD %ld %ld %ld %ld %ld %ld\n"),
5236 					inputstate&(WHICHBUTTON|SHIFTISDOWN|ALTISDOWN|CONTROLISDOWN),
5237 						cursorx, cursory, special, extradata, ticktime()-gra_logbasetime);
5238 			} else
5239 			{
5240 				xprintf(us_logrecord, x_("BP %ld %ld %ld %ld %ld %ld\n"),
5241 					inputstate&(WHICHBUTTON|SHIFTISDOWN|ALTISDOWN|CONTROLISDOWN),
5242 						cursorx, cursory, special, extradata, ticktime()-gra_logbasetime);
5243 			}
5244 		}
5245 	} else if (inputstate == MENUEVENT)
5246 	{
5247 		xprintf(us_logrecord, x_("ME %ld %ld %ld"), cursorx, cursory,
5248 			ticktime()-gra_logbasetime);
5249 		if (cursorx >= 0 && cursorx < gra_pulldownmenucount)
5250 		{
5251 			pm = us_getpopupmenu(gra_pulldowns[cursorx]);
5252 			for(trueItem=j=0; j < pm->total; j++)
5253 			{
5254 				mi = &pm->list[j];
5255 				if (mi->response->active < 0 && *mi->attribute == 0) continue;
5256 				trueItem++;
5257 				if (trueItem == cursory) break;
5258 			}
5259 			if (j < pm->total)
5260 				xprintf(us_logrecord, x_("   ; command=%s"),
5261 					us_stripampersand(mi->attribute));
5262 		}
5263 		xprintf(us_logrecord, x_("\n"));
5264 	} else if (inputstate == FILEREPLY)
5265 	{
5266 		filename = (CHAR *)extradata;
5267 		xprintf(us_logrecord, x_("FS %s\n"), filename);
5268 	} else if (inputstate == POPUPSELECT)
5269 	{
5270 		xprintf(us_logrecord, x_("PM %ld %ld\n"), cursorx, ticktime()-gra_logbasetime);
5271 	} else if (inputstate == DIAEDITTEXT)
5272 	{
5273 		filename = (CHAR *)extradata;
5274 		xprintf(us_logrecord, x_("DE %ld %ld %s\n"), special, cursorx, filename);
5275 	} else if (inputstate == DIASCROLLSEL)
5276 	{
5277 		xprintf(us_logrecord, x_("DS %ld %ld %ld"), special, cursorx, cursory);
5278 		for(i=0; i<cursory; i++) xprintf(us_logrecord, x_(" %ld"), ((INTBIG *)extradata)[i]);
5279 		xprintf(us_logrecord, x_("\n"));
5280 	} else if (inputstate == DIAPOPUPSEL)
5281 	{
5282 		xprintf(us_logrecord, x_("DP %ld %ld %ld\n"), special, cursorx, cursory);
5283 	} else if (inputstate == DIASETCONTROL)
5284 	{
5285 		xprintf(us_logrecord, x_("DC %ld %ld %ld\n"), special, cursorx, cursory);
5286 	} else if (inputstate == DIAITEMCLICK)
5287 	{
5288 		xprintf(us_logrecord, x_("DI %ld %ld\n"), special, cursorx);
5289 	} else if (inputstate == DIAUSERMOUSE)
5290 	{
5291 		xprintf(us_logrecord, x_("DM %ld %ld %ld\n"), special, cursorx, cursory);
5292 	} else if (inputstate == DIAENDDIALOG)
5293 	{
5294 		xprintf(us_logrecord, x_("DD %ld\n"), special);
5295 	} else if (inputstate == WINDOWMOVE)
5296 	{
5297 		xprintf(us_logrecord, x_("WM %ld %ld %ld %ld\n"), extradata,
5298 			cursorx, cursory, ticktime()-gra_logbasetime);
5299 	} else if (inputstate == WINDOWSIZE)
5300 	{
5301 		xprintf(us_logrecord, x_("WS %ld %ld %ld %ld\n"), extradata,
5302 			cursorx, cursory, ticktime()-gra_logbasetime);
5303 	} else
5304 	{
5305 		ttyputmsg(x_("Unknown event being logged: %ld"), inputstate);
5306 	}
5307 
5308 	/* flush the log file every so often */
5309 	gra_logrecordcount++;
5310 	if (gra_logrecordcount >= us_logflushfreq)
5311 	{
5312 		gra_logrecordcount = 0;
5313 		xflushbuf(us_logrecord);
5314 	}
5315 }
5316 
gra_logwritecomment(CHAR * comment)5317 void gra_logwritecomment(CHAR *comment)
5318 {
5319 	if (us_logrecord == 0) return;
5320 	xprintf(us_logrecord, x_("; %s\n"), comment);
5321 }
5322 
gra_loggetnextaction(CHAR * message)5323 BOOLEAN gra_loggetnextaction(CHAR *message)
5324 {
5325 	REGISTER BOOLEAN eof;
5326 	CHAR tempstring[300], *pt, *start;
5327 	REGISTER TDIALOG *dia;
5328 	REGISTER WINDOWFRAME *wf;
5329 	INTSML boundkey;
5330 	INTBIG sellist[MAXSCROLLMULTISELECT], i, x, y, item, count;
5331 	UINTBIG nowtime;
5332 
5333 	eof = gra_logreadline(tempstring, 300);
5334 	if (stopping(STOPREASONPLAYBACK)) eof = TRUE;
5335 	if (eof)
5336 	{
5337 		/* stop playback */
5338 		ttyputmsg(_("End of session playback file"));
5339 		xclose(us_logplay);
5340 		us_logplay = NULL;
5341 		return(TRUE);
5342 	}
5343 
5344 	/* load the event structure */
5345 	if (estrncmp(tempstring, x_("WS"), 2) == 0)
5346 	{
5347 		gra_inputstate = NOEVENT;
5348 		pt = &tempstring[3];
5349 		x = eatoi(getkeyword(&pt, x_(" ")));
5350 		y = eatoi(getkeyword(&pt, x_(" ")));
5351 		wf = gra_getframefromindex(eatoi(getkeyword(&pt, x_(" "))));
5352 		nowtime = eatoi(getkeyword(&pt, x_(" ")));
5353 		gra_timeoffset += nowtime - gra_lastplaybacktime;
5354 		gra_lastplaybacktime = nowtime;
5355 		if (wf != NOWINDOWFRAME)
5356 			sizewindowframe(wf, x, y);
5357 		return(FALSE);
5358 	}
5359 	if (estrncmp(tempstring, x_("WM"), 2) == 0)
5360 	{
5361 		gra_inputstate = NOEVENT;
5362 		pt = &tempstring[3];
5363 		x = eatoi(getkeyword(&pt, x_(" ")));
5364 		y = eatoi(getkeyword(&pt, x_(" ")));
5365 		wf = gra_getframefromindex(eatoi(getkeyword(&pt, x_(" "))));
5366 		nowtime = eatoi(getkeyword(&pt, x_(" ")));
5367 		gra_timeoffset += nowtime - gra_lastplaybacktime;
5368 		gra_lastplaybacktime = nowtime;
5369 		if (wf != NOWINDOWFRAME)
5370 			movewindowframe(wf, x, y);
5371 		return(FALSE);
5372 	}
5373 	if (estrncmp(tempstring, x_("DI"), 2) == 0)
5374 	{
5375 		gra_inputstate = NOEVENT;
5376 		pt = &tempstring[3];
5377 		dia = gra_getdialogfromindex(eatoi(getkeyword(&pt, x_(" "))));
5378 		if (dia == NOTDIALOG) return(FALSE);
5379 		item = eatoi(getkeyword(&pt, x_(" ")));
5380 		if (dia->modelessitemhit == 0)
5381 		{
5382 			/* current modal dialog */
5383 			dia->dialoghit = item;
5384 		} else
5385 		{
5386 			/* a modeless dialog */
5387 			(*dia->modelessitemhit)(dia, item);
5388 		}
5389 		return(FALSE);
5390 	}
5391 	if (estrncmp(tempstring, x_("DS"), 2) == 0)
5392 	{
5393 		gra_inputstate = NOEVENT;
5394 		pt = &tempstring[3];
5395 		dia = gra_getdialogfromindex(eatoi(getkeyword(&pt, x_(" "))));
5396 		if (dia == NOTDIALOG) return(FALSE);
5397 		item = eatoi(getkeyword(&pt, x_(" ")));
5398 		count = eatoi(getkeyword(&pt, x_(" ")));
5399 		for(i=0; i<count; i++) sellist[i] = eatoi(getkeyword(&pt, x_(" ")));
5400 		if (count == 1)
5401 		{
5402 			DiaSelectLine(dia, item, sellist[0]);
5403 		} else
5404 		{
5405 			DiaSelectLines(dia, item, count, sellist);
5406 		}
5407 		return(FALSE);
5408 	}
5409 	if (estrncmp(tempstring, x_("DP"), 2) == 0)
5410 	{
5411 		gra_inputstate = NOEVENT;
5412 		pt = &tempstring[3];
5413 		dia = gra_getdialogfromindex(eatoi(getkeyword(&pt, x_(" "))));
5414 		if (dia == NOTDIALOG) return(FALSE);
5415 		item = eatoi(getkeyword(&pt, x_(" ")));
5416 		i = eatoi(getkeyword(&pt, x_(" ")));
5417 		DiaSetPopupEntry(dia, item, i);
5418 		return(FALSE);
5419 	}
5420 	if (estrncmp(tempstring, x_("DC"), 2) == 0)
5421 	{
5422 		gra_inputstate = NOEVENT;
5423 		pt = &tempstring[3];
5424 		dia = gra_getdialogfromindex(eatoi(getkeyword(&pt, x_(" "))));
5425 		if (dia == NOTDIALOG) return(FALSE);
5426 		item = eatoi(getkeyword(&pt, x_(" ")));
5427 		i = eatoi(getkeyword(&pt, x_(" ")));
5428 		DiaSetControl(dia, item, i);
5429 		return(FALSE);
5430 	}
5431 	if (estrncmp(tempstring, x_("DE"), 2) == 0)
5432 	{
5433 		gra_inputstate = NOEVENT;
5434 		pt = &tempstring[3];
5435 		dia = gra_getdialogfromindex(eatoi(getkeyword(&pt, x_(" "))));
5436 		if (dia == NOTDIALOG) return(FALSE);
5437 		item = eatoi(getkeyword(&pt, x_(" ")));
5438 		while (*pt != 0 && *pt != ' ') pt++;
5439 		if (*pt == ' ') pt++;
5440 		DiaSetText(dia, item, pt);
5441 		if (dia->modelessitemhit == 0)
5442 		{
5443 			/* current modal dialog */
5444 			dia->dialoghit = item;
5445 		} else
5446 		{
5447 			/* a modeless dialog */
5448 			(*dia->modelessitemhit)(dia, item);
5449 		}
5450 		return(FALSE);
5451 	}
5452 	if (estrncmp(tempstring, x_("DM"), 2) == 0)
5453 	{
5454 		gra_inputstate = DIAUSERMOUSE;
5455 		pt = &tempstring[3];
5456 		dia = gra_getdialogfromindex(eatoi(getkeyword(&pt, x_(" "))));
5457 		if (dia == NOTDIALOG) return(FALSE);
5458 		gra_action.x = eatoi(getkeyword(&pt, x_(" ")));
5459 		gra_action.y = eatoi(getkeyword(&pt, x_(" ")));
5460 		return(FALSE);
5461 	}
5462 	if (estrncmp(tempstring, x_("DD"), 2) == 0)
5463 	{
5464 		pt = &tempstring[3];
5465 		dia = gra_getdialogfromindex(eatoi(getkeyword(&pt, x_(" "))));
5466 		gra_inputstate = DIAENDDIALOG;
5467 		return(FALSE);
5468 	}
5469 	if (estrncmp(tempstring, x_("ME"), 2) == 0)
5470 	{
5471 		gra_inputstate = MENUEVENT;
5472 		pt = &tempstring[3];
5473 		gra_cursorx = eatoi(getkeyword(&pt, x_(" ")));
5474 		gra_cursory = eatoi(getkeyword(&pt, x_(" ")));
5475 		nowtime = eatoi(getkeyword(&pt, x_(" ")));
5476 		gra_timeoffset += nowtime - gra_lastplaybacktime;
5477 		gra_lastplaybacktime = nowtime;
5478 		return(FALSE);
5479 	}
5480 	if (estrncmp(tempstring, x_("FS"), 2) == 0)
5481 	{
5482 		gra_inputstate = FILEREPLY;
5483 		pt = &tempstring[3];
5484 		if (message != 0) estrcpy(message, pt);
5485 		return(FALSE);
5486 	}
5487 	if (estrncmp(tempstring, x_("PM"), 2) == 0)
5488 	{
5489 		gra_inputstate = POPUPSELECT;
5490 		pt = &tempstring[3];
5491 		gra_cursorx = eatoi(getkeyword(&pt, x_(" ")));
5492 		nowtime = eatoi(getkeyword(&pt, x_(" ")));
5493 		gra_timeoffset += nowtime - gra_lastplaybacktime;
5494 		gra_lastplaybacktime = nowtime;
5495 		return(FALSE);
5496 	}
5497 
5498 	if (estrncmp(tempstring, x_("MR"), 2) == 0 || estrncmp(tempstring, x_("MP"), 2) == 0)
5499 	{
5500 		pt = &tempstring[3];
5501 		gra_inputstate = MOTION;
5502 		gra_cursorx = eatoi(getkeyword(&pt, x_(" ")));
5503 		gra_cursory = eatoi(getkeyword(&pt, x_(" ")));
5504 		gra_inputspecial = eatoi(getkeyword(&pt, x_(" ")));
5505 		i = eatoi(getkeyword(&pt, x_(" ")));
5506 		if (tempstring[1] == 'R') gra_inputstate |= BUTTONUP;
5507 	} else if (estrncmp(tempstring, x_("KT"), 2) == 0)
5508 	{
5509 		start = &tempstring[4];
5510 		for(i = estrlen(start)-1; i>0; i--)
5511 			if (start[i] == '\'') break;
5512 		if (i <= 0) return(FALSE);
5513 		start[i] = 0;
5514 		pt = &start[i+2];
5515 		(void)us_getboundkey(start, &boundkey, &gra_inputspecial);
5516 		gra_inputstate = ISKEYSTROKE | (boundkey & CHARREAD);
5517 		gra_cursorx = eatoi(getkeyword(&pt, x_(" ")));
5518 		gra_cursory = eatoi(getkeyword(&pt, x_(" ")));
5519 		i = eatoi(getkeyword(&pt, x_(" ")));
5520 		nowtime = eatoi(getkeyword(&pt, x_(" ")));
5521 		gra_timeoffset += nowtime - gra_lastplaybacktime;
5522 		gra_lastplaybacktime = nowtime;
5523 	} else if (tempstring[0] == 'B')
5524 	{
5525 		pt = &tempstring[3];
5526 		gra_inputstate = ISBUTTON | eatoi(getkeyword(&pt, x_(" ")));
5527 		gra_cursorx = eatoi(getkeyword(&pt, x_(" ")));
5528 		gra_cursory = eatoi(getkeyword(&pt, x_(" ")));
5529 		gra_inputspecial = eatoi(getkeyword(&pt, x_(" ")));
5530 		i = eatoi(getkeyword(&pt, x_(" ")));
5531 		if (tempstring[1] == 'D') gra_inputstate |= DOUBLECLICK;
5532 		if (tempstring[1] == 'R') gra_inputstate |= BUTTONUP;
5533 		nowtime = eatoi(getkeyword(&pt, x_(" ")));
5534 		gra_timeoffset += nowtime - gra_lastplaybacktime;
5535 		gra_lastplaybacktime = nowtime;
5536 	}
5537 	wf = gra_getframefromindex(i);
5538 	if (wf != NOWINDOWFRAME && wf != el_curwindowframe)
5539 		((CChildFrame *)wf->wndframe)->ActivateFrame();
5540 	us_state &= ~GOTXY;
5541 	return(FALSE);
5542 }
5543 
gra_logreadline(CHAR * string,INTBIG limit)5544 BOOLEAN gra_logreadline(CHAR *string, INTBIG limit)
5545 {
5546 	for(;;)
5547 	{
5548 		if (xfgets(string, limit, us_logplay) != 0) return(TRUE);
5549 		if (string[0] != ';') break;
5550 	}
5551 	return(FALSE);
5552 }
5553 
gra_getframefromindex(INTBIG index)5554 WINDOWFRAME *gra_getframefromindex(INTBIG index)
5555 {
5556 	REGISTER WINDOWFRAME *wf;
5557 
5558 	for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
5559 		if (wf->windindex == index) return(wf);
5560 	return(NOWINDOWFRAME);
5561 }
5562 
gra_getdialogfromindex(INTBIG index)5563 TDIALOG *gra_getdialogfromindex(INTBIG index)
5564 {
5565 	REGISTER TDIALOG *dia;
5566 
5567 	for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
5568 		if (dia->windindex == index) return(dia);
5569 	return(NOTDIALOG);
5570 }
5571 
5572 /****************************** MENUS ******************************/
5573 
getacceleratorstrings(CHAR ** acceleratorstring,CHAR ** acceleratorprefix)5574 void getacceleratorstrings(CHAR **acceleratorstring, CHAR **acceleratorprefix)
5575 {
5576 	*acceleratorstring = _("Ctrl");
5577 	*acceleratorprefix = _("Ctrl-");
5578 }
5579 
getinterruptkey(void)5580 CHAR *getinterruptkey(void)
5581 {
5582 	return(_("Windows-C"));
5583 }
5584 
nativepopupmenu(POPUPMENU ** menu,BOOLEAN header,INTBIG left,INTBIG top)5585 INTBIG nativepopupmenu(POPUPMENU **menu, BOOLEAN header, INTBIG left, INTBIG top)
5586 {
5587 	INTBIG retval;
5588 
5589 	retval = gra_nativepopuptif(menu, header, left, top);
5590 	gra_logwriteaction(POPUPSELECT, 0, retval, 0, 0);
5591 	return(retval);
5592 }
5593 
gra_nativepopuptif(POPUPMENU ** menu,BOOLEAN header,INTBIG left,INTBIG top)5594 INTBIG gra_nativepopuptif(POPUPMENU **menu, BOOLEAN header, INTBIG left, INTBIG top)
5595 {
5596 	CMainFrame *wnd;
5597 	INTBIG j, k, pindex, submenus;
5598 	REGISTER POPUPMENUITEM *mi, *submi;
5599 	REGISTER POPUPMENU *themenu, **subpmlist;
5600 	REGISTER USERCOM *uc;
5601 	HMENU popmenu, subpopmenu, *submenulist;
5602 	POINT p, p2;
5603 	UINT flags;
5604 
5605 	while (us_logplay != NULL)
5606 	{
5607 		if (gra_loggetnextaction(0)) break;
5608 		j = gra_inputstate;
5609 		gra_inputstate = NOEVENT;
5610 		if (j == POPUPSELECT) return(gra_cursorx);
5611 	}
5612 
5613 	themenu = *menu;
5614 	wnd = (CMainFrame *)AfxGetMainWnd();
5615 	flags = TPM_NONOTIFY | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON;
5616 	if (left < 0 || top < 0)
5617 	{
5618 		p2.x = p2.y = 0;
5619 		wnd->MapWindowPoints(0, &p2, 1);
5620 		GetCursorPos(&p);
5621 		left = p.x - p2.x;
5622 		top = p.y - p2.y;
5623 		flags |= TPM_CENTERALIGN|TPM_VCENTERALIGN;
5624 	} else
5625 	{
5626 		if (el_curwindowpart != NOWINDOWPART)
5627 			top = el_curwindowpart->frame->revy - top;
5628 		flags |= TPM_LEFTALIGN;
5629 	}
5630 	popmenu = CreatePopupMenu();
5631 	if (popmenu == 0) return(-1);
5632 	if (header)
5633 	{
5634 		if (AppendMenu(popmenu, MF_STRING, 0, themenu->header) == 0) return(-1);
5635 		if (AppendMenu(popmenu, MF_SEPARATOR, 0, 0) == 0) return(-1);
5636 	}
5637 
5638 	/* count the number of submenus */
5639 	submenus = 0;
5640 	for(j=0; j < themenu->total; j++)
5641 	{
5642 		mi = &themenu->list[j];
5643 		if (*mi->attribute != 0 && mi->response != NOUSERCOM &&
5644 			mi->response->menu != NOPOPUPMENU) submenus++;
5645 	}
5646 	if (submenus > 0)
5647 	{
5648 		submenulist = (HMENU *)emalloc(submenus * (sizeof (HMENU)), us_tool->cluster);
5649 		if (submenulist == 0) return(-1);
5650 		subpmlist = (POPUPMENU **)emalloc(submenus * (sizeof (POPUPMENU *)), us_tool->cluster);
5651 		if (subpmlist == 0) return(-1);
5652 	}
5653 
5654 	/* load the menus */
5655 	submenus = 0;
5656 	for(j=0; j < themenu->total; j++)
5657 	{
5658 		mi = &themenu->list[j];
5659 		mi->changed = FALSE;
5660 		if (*mi->attribute == 0)
5661 		{
5662 			if (AppendMenu(popmenu, MF_SEPARATOR, 0, 0) == 0) return(-1);
5663 		} else
5664 		{
5665 			uc = mi->response;
5666 			if (uc != NOUSERCOM && uc->menu != NOPOPUPMENU)
5667 			{
5668 				subpopmenu = CreatePopupMenu();
5669 				if (subpopmenu == 0) return(-1);
5670 				submenulist[submenus] = subpopmenu;
5671 				subpmlist[submenus] = uc->menu;
5672 				submenus++;
5673 
5674 				for(k=0; k < uc->menu->total; k++)
5675 				{
5676 					submi = &uc->menu->list[k];
5677 					if (*submi->attribute == 0)
5678 					{
5679 						if (AppendMenu(subpopmenu, MF_SEPARATOR, 0, 0) == 0) return(-1);
5680 					} else
5681 					{
5682 						if (AppendMenu(subpopmenu, MF_STRING, (submenus << 16) | (k+1), submi->attribute) == 0) return(-1);
5683 					}
5684 				}
5685 				if (InsertMenu(popmenu, j+2, MF_POPUP | MF_BYPOSITION,
5686 					(DWORD)subpopmenu, mi->attribute) == 0)
5687 						return(0);
5688 			} else
5689 			{
5690 				if (AppendMenu(popmenu, MF_STRING, j+1, mi->attribute) == 0) return(-1);
5691 			}
5692 		}
5693 	}
5694 	pindex = TrackPopupMenu(popmenu, flags, left, top, 0, wnd->m_hWnd, 0) - 1;
5695 	if (pindex < 0) return(-1);
5696 	j = pindex >> 16;
5697 	if (j != 0) *menu = subpmlist[j-1];
5698  	DestroyMenu(popmenu);
5699 	for(j=0; j<submenus; j++)
5700 		DestroyMenu(submenulist[j]);
5701 	if (submenus > 0)
5702 	{
5703 		efree((CHAR *)submenulist);
5704 		efree((CHAR *)subpmlist);
5705 	}
5706 	return(pindex & 0xFFFF);
5707 }
5708 
5709 /*
5710  * routine to handle the menu
5711  */
gra_nativemenudoone(INTBIG item,INTBIG menuindex)5712 void gra_nativemenudoone(INTBIG item, INTBIG menuindex)
5713 {
5714 	gra_addeventtoqueue(MENUEVENT, 0, menuindex, item);
5715 }
5716 
5717 /*
5718  * Routine to establish the "count" pulldown menu names in "par" as the pulldown menu bar
5719  */
nativemenuload(INTBIG count,CHAR * par[])5720 void nativemenuload(INTBIG count, CHAR *par[])
5721 {
5722 	REGISTER INTBIG i, menuindex;
5723 	REGISTER POPUPMENU *pm;
5724 	POPUPMENU *pulls[25];
5725 
5726 	if (gra_hMenu == 0)
5727 	{
5728 		gra_hMenu = new CMenu();
5729 		gra_hMenu->CreateMenu();
5730 	}
5731 
5732 	for(i=0; i<count; i++)
5733 	{
5734 		pm = us_getpopupmenu(par[i]);
5735 		if (pm == NOPOPUPMENU) continue;
5736 		pulls[i] = pm;
5737 
5738 		menuindex = gra_pulldownindex(pm);
5739 		if (menuindex < 0) continue;
5740 		if (gra_hMenu->InsertMenu(menuindex, MF_ENABLED | MF_POPUP | MF_BYPOSITION,
5741 			(DWORD)gra_pulldownmenus[menuindex]->m_hMenu, pm->header) == 0)
5742 		{
5743 			ttyputerr(_("Item %s can't be inserted in menu bar"), par[i]);
5744 			return;
5745 		}
5746 	}
5747 	CWnd* parent = AfxGetMainWnd();
5748 	CMenu* lastMenu = parent->GetMenu();
5749 	if (parent->SetMenu(gra_hMenu) == 0)
5750 	{
5751 		ttyputerr(_("SetMenu failed"));
5752 		return;
5753 	}
5754 	gra_hMenu->Detach();
5755 	parent->DrawMenuBar();
5756 }
5757 
5758 /*
5759  * Routine to create a pulldown menu from popup menu "pm".  Returns an index to
5760  * the table of pulldown menus (-1 on error).
5761  */
gra_pulldownindex(POPUPMENU * pm)5762 INTBIG gra_pulldownindex(POPUPMENU *pm)
5763 {
5764 	REGISTER INTBIG i, pindex;
5765 	CMenu **newpulldownmenus;
5766 	CHAR **newpulldowns;
5767 
5768 	for(i=0; i<gra_pulldownmenucount; i++)
5769 		if (namesame(gra_pulldowns[i], pm->name) == 0) return(i);
5770 
5771 	/* allocate new space with one more */
5772 	newpulldownmenus = (CMenu **)emalloc((gra_pulldownmenucount+1) *
5773 		(sizeof (CMenu *)), us_tool->cluster);
5774 	if (newpulldownmenus == 0) return(-1);
5775 	newpulldowns = (CHAR **)emalloc((gra_pulldownmenucount+1) *
5776 		(sizeof (CHAR *)), us_tool->cluster);
5777 	if (newpulldowns == 0) return(-1);
5778 
5779 	/* copy former arrays then delete them */
5780 	for(i=0; i<gra_pulldownmenucount; i++)
5781 	{
5782 		newpulldownmenus[i] = gra_pulldownmenus[i];
5783 		newpulldowns[i] = gra_pulldowns[i];
5784 	}
5785 	if (gra_pulldownmenucount != 0)
5786 	{
5787 		efree((CHAR *)gra_pulldownmenus);
5788 		efree((CHAR *)gra_pulldowns);
5789 	}
5790 
5791 	gra_pulldownmenus = newpulldownmenus;
5792 	gra_pulldowns = newpulldowns;
5793 
5794 	pindex = gra_pulldownmenucount++;
5795 	(void)allocstring(&gra_pulldowns[pindex], pm->name, us_tool->cluster);
5796 	gra_pulldownmenus[pindex] = gra_makepdmenu(pm);
5797 	if (gra_pulldownmenus[pindex] == 0) return(-1);
5798 	return(pindex);
5799 }
5800 
gra_makepdmenu(POPUPMENU * pm)5801 CMenu *gra_makepdmenu(POPUPMENU *pm)
5802 {
5803 	CMenu *hPrevMenu, *hMenu;
5804 	REGISTER USERCOM *uc;
5805 	REGISTER POPUPMENUITEM *mi;
5806 	REGISTER INTBIG j, submenuindex, len, idIndex;
5807 	INTSML key;
5808 	INTBIG special;
5809 	REGISTER INTBIG flags;
5810 	REGISTER CHAR *pt;
5811 	CHAR myline[100];
5812 
5813 	hMenu = new CMenu();
5814 	if (hMenu->CreatePopupMenu() == 0) return(0);
5815 
5816 	idIndex = 0;
5817 	for(j=0; j < pm->total; j++)
5818 	{
5819 		mi = &pm->list[j];
5820 		uc = mi->response;
5821 		if (uc->active < 0)
5822 		{
5823 			if (*mi->attribute == 0)
5824 			{
5825 				if (hMenu->AppendMenu(MF_SEPARATOR) == 0) return(0);
5826 			} else
5827 			{
5828 				if (hMenu->AppendMenu(MF_STRING | MF_DISABLED, gra_menures[idIndex++], mi->attribute) == 0) return(0);
5829 			}
5830 			continue;
5831 		}
5832 
5833 		if (uc->menu != NOPOPUPMENU)
5834 		{
5835 			hPrevMenu = hMenu;
5836 			idIndex++;
5837 			submenuindex = gra_pulldownindex(uc->menu);
5838 			if (hPrevMenu->InsertMenu(-1, MF_POPUP | MF_BYPOSITION,
5839 				(DWORD)gra_pulldownmenus[submenuindex]->m_hMenu, mi->attribute) == 0)
5840 					return(0);
5841 			continue;
5842 		}
5843 		flags = 0;
5844 		if (mi->attribute[0] == '>')
5845 		{
5846 			flags = MF_CHECKED;
5847 			estrcpy(myline, &mi->attribute[1]);
5848 		} else
5849 		{
5850 			estrcpy(myline, mi->attribute);
5851 		}
5852 		len = estrlen(myline);
5853 		if (myline[len-1] == '<') myline[len-1] = 0;
5854 		for(pt = myline; *pt != 0; pt++) if (*pt == '/' || *pt == '\\') break;
5855 		if (*pt != 0)
5856 		{
5857 			(void)us_getboundkey(pt, &key, &special);
5858 			esnprintf(pt, 50, x_("\t%s"), us_describeboundkey(key, special, 1));
5859 		}
5860 		if (hMenu->AppendMenu(MF_STRING|flags, gra_menures[idIndex++], myline) == 0) return(0);
5861 	}
5862 	return(hMenu);
5863 }
5864 
5865 /* routine to redraw entry "pindex" of popupmenu "pm" because it changed */
nativemenurename(POPUPMENU * pm,INTBIG pindex)5866 void nativemenurename(POPUPMENU *pm, INTBIG pindex)
5867 {
5868 	INTBIG i, j, k, len, key, idIndex;
5869 	REGISTER INTBIG flags;
5870 	CHAR line[100], *pt, slash;
5871 	USERCOM *uc;
5872 	REGISTER POPUPMENUITEM *mi;
5873 
5874 	/* find this pulldown menu */
5875 	for(i=0; i<gra_pulldownmenucount; i++)
5876 		if (namesame(gra_pulldowns[i], pm->name) == 0) break;
5877 	if (i >= gra_pulldownmenucount) return;
5878 
5879 	/* make sure the menu didn't change size */
5880 	j = (INTBIG)gra_pulldownmenus[i]->GetMenuItemCount();
5881 	if (pm->total != j)
5882 	{
5883 		if (pm->total > j)
5884 		{
5885 			/* must add new entries */
5886 			for(k=j; k<pm->total; k++)
5887 				gra_pulldownmenus[i]->AppendMenu(MF_STRING, gra_menures[k], x_("X"));
5888 		} else
5889 		{
5890 			/* must delete extra entries */
5891 			for(k=pm->total; k<j; k++)
5892 				gra_pulldownmenus[i]->RemoveMenu(pm->total, MF_BYPOSITION);
5893 		}
5894 	}
5895 
5896 	idIndex = 0;
5897 	for(j=0; j < pm->total; j++)
5898 	{
5899 		mi = &pm->list[j];
5900 		uc = mi->response;
5901 		if (j == pindex) break;
5902 		if (uc->active < 0 && *mi->attribute == 0) continue;
5903 		idIndex++;
5904 	}
5905 	if (uc->active < 0)
5906 	{
5907 		if (*pm->list[pindex].attribute == 0)
5908 		{
5909 			gra_pulldownmenus[i]->ModifyMenu(pindex, MF_BYPOSITION|MF_SEPARATOR);
5910 			return;
5911 		} else
5912 		{
5913 			gra_pulldownmenus[i]->EnableMenuItem(pindex, MF_BYPOSITION|MF_GRAYED);
5914 		}
5915 	} else
5916 	{
5917 		gra_pulldownmenus[i]->EnableMenuItem(pindex, MF_BYPOSITION|MF_ENABLED);
5918 	}
5919 
5920 	/* copy and examine the menu string */
5921 	flags = 0;
5922 	if (pm->list[pindex].attribute[0] == '>')
5923 	{
5924 		flags = MF_CHECKED;
5925 		(void)estrcpy(line, &pm->list[pindex].attribute[1]);
5926 	} else
5927 	{
5928 		(void)estrcpy(line, pm->list[pindex].attribute);
5929 	}
5930 	len = estrlen(line);
5931 	if (line[len-1] == '<') line[len-1] = 0;
5932 
5933 	/* handle single-key equivalents */
5934 	for(pt = line; *pt != 0; pt++) if (*pt == '/' || *pt == '\\') break;
5935 	if (*pt != 0)
5936 	{
5937 		slash = *pt;
5938 		*pt++ = 0;
5939 		len = estrlen(line);
5940 		if (*pt != 0)
5941 		{
5942 			if (estrlen(pt) > 1)
5943 			{
5944 				esnprintf(&line[len], 50, x_("\t%s"), pt);
5945 			} else
5946 			{
5947 				key = *pt & 0xFF;
5948 				if (slash == '/')
5949 				{
5950 					esnprintf(&line[len], 50, x_("\tCtrl-%c"), key);
5951 				} else
5952 				{
5953 					esnprintf(&line[len], 50, x_("\t%c"), key);
5954 				}
5955 			}
5956 		}
5957 	}
5958 	gra_pulldownmenus[i]->ModifyMenu(pindex, MF_BYPOSITION|MF_STRING|flags, gra_menures[idIndex], line);
5959 }
5960 
5961 /****************************** DIALOGS ******************************/
5962 
5963 /*
5964  * Routine to initialize a dialog described by "dialog".
5965  * Returns the address of the dialog object (0 if dialog cannot be initialized).
5966  */
DiaInitDialog(DIALOG * dialog)5967 void *DiaInitDialog(DIALOG *dialog)
5968 {
5969 	TDIALOG *dia;
5970 
5971 	dia = (TDIALOG *)emalloc(sizeof (TDIALOG), db_cluster);
5972 	if (dia == 0) return(0);
5973 	if (gra_initdialog(dialog, dia, FALSE)) return(0);
5974 	return(dia);
5975 }
5976 
5977 /*
5978  * Routine to initialize dialog "dialog" in modeless style, calling
5979  * "itemhit" for each hit.
5980  */
DiaInitDialogModeless(DIALOG * dialog,void (* itemhit)(void * dia,INTBIG item))5981 void *DiaInitDialogModeless(DIALOG *dialog, void (*itemhit)(void *dia, INTBIG item))
5982 {
5983 	TDIALOG *dia;
5984 
5985 	dia = (TDIALOG *)emalloc(sizeof (TDIALOG), db_cluster);
5986 	if (dia == 0) return(0);
5987 	if (gra_initdialog(dialog, dia, TRUE)) return(0);
5988 	dia->modelessitemhit = itemhit;
5989 	return(dia);
5990 }
5991 
5992 /* Internal routine to initialize dialog structure "dialog" into "dia" */
gra_initdialog(DIALOG * dialog,TDIALOG * dia,BOOLEAN modeless)5993 BOOLEAN gra_initdialog(DIALOG *dialog, TDIALOG *dia, BOOLEAN modeless)
5994 {
5995 	DLGTEMPLATE *dtheader;
5996 	WORD *dtmenu, *dtclass, *dttitle;
5997 	int headerlength, menulength, classlength, titlelength, itemslength,
5998 		*itemlengths, i, j, len, itemtype, style, itemclass, totallen,
5999 		dialogdbnx, dialogdbny, dialogdbux, dialogdbuy;
6000 	INTBIG dbu, dbuL, dbuH;
6001 	DLGITEMTEMPLATE **dtitems;
6002 	RECT r;
6003 	UCHAR1 *descblock, *pt;
6004 	CHAR *title, *msg;
6005 	WORD output[100];
6006 	wchar_t *woutput;
6007 	CProgressCtrl *prog;
6008 	CListBoxEx *list;
6009 	CStatic *stat;
6010 	CWnd *wnd;
6011 	CSize textSize;
6012 	REGISTER void *infstr;
6013 	HFONT hf;
6014 
6015 	/* add this to the list of active dialogs */
6016 	dia->nexttdialog = gra_firstactivedialog;
6017 	gra_firstactivedialog = dia;
6018 
6019 	/* be sure the dialog is translated */
6020 	DiaTranslate(dialog);
6021 
6022 	/*
6023 	 * for small fonts, dbuH = 16, dbuL = 8,
6024 	 * for large fonts, dbuH = 20, dbuL = 10
6025 	 */
6026 	dbu = GetDialogBaseUnits();
6027 	dbuL = dbu & 0xFFFF;
6028 	dbuH = (dbu >> 16) & 0xFFFF;
6029 	dialogdbnx = 2 * (46-dbuL)-1;   dialogdbux = 14 * dbuL;
6030 	dialogdbny = 131 * 8;           dialogdbuy = 105 * dbuH;
6031 
6032 	dia->window = new CElectricDialog();
6033 	dia->itemdesc = dialog;
6034 	dia->windindex = gra_windownumberindex++;
6035 	dia->numlocks = 0;
6036 	dia->modelessitemhit = 0;
6037 	dia->redrawroutine = 0;
6038 
6039 	/* compute size of some items to automatically scale them */
6040 #ifdef INTERNATIONAL
6041 	CDC *dc;
6042 	CFont *cf;
6043 	hf = (HFONT)GetStockObject(SYSTEM_FONT);
6044 	cf = CFont::FromHandle(hf);
6045 	dc = AfxGetApp()->m_pMainWnd->GetDC();
6046 	dc->SelectObject(cf);
6047 	for(i=0; i<dialog->items; i++)
6048 	{
6049 		itemtype = dialog->list[i].type & ITEMTYPE;
6050 		switch (itemtype)
6051 		{
6052 			case DEFBUTTON:
6053 			case BUTTON:
6054 			case CHECK:
6055 			case RADIO:
6056 			case MESSAGE:
6057 				int offset, amt;
6058 				if (itemtype == BUTTON || itemtype == DEFBUTTON) offset = 8; else
6059 					if (itemtype == CHECK || itemtype == RADIO) offset = 16; else
6060 						offset = 0;
6061 				pt = dialog->list[i].msg;
6062 				textSize = dc->GetTextExtent(pt, estrlen(pt));
6063 				j = textSize.cx * dialogdbnx / dialogdbux;
6064 				amt = (j + offset) - (dialog->list[i].r.right - dialog->list[i].r.left);
6065 				if (amt > 0)
6066 				{
6067 					for(j=0; j<dialog->items; j++)
6068 					{
6069 						if (j == i) continue;
6070 						if (dialog->list[j].r.left >= dialog->list[i].r.right &&
6071 							dialog->list[j].r.left < dialog->list[i].r.right+amt)
6072 						{
6073 							dialog->list[j].r.left += amt;
6074 							dialog->list[j].r.right += amt;
6075 						}
6076 					}
6077 					dialog->list[i].r.right += amt;
6078 				}
6079 				break;
6080 		}
6081 	}
6082 	for(i=0; i<dialog->items; i++)
6083 	{
6084 		j = dialog->list[i].r.right + 5;
6085 		if (j < dialog->windowRect.right - dialog->windowRect.left) continue;
6086 		dialog->windowRect.right = dialog->windowRect.left + j;
6087 	}
6088 	AfxGetApp()->m_pMainWnd->ReleaseDC(dc);
6089 #endif
6090 
6091 	/* determine item lengths */
6092 	itemlengths = (int *)emalloc(dialog->items * (sizeof (int)), us_tool->cluster);
6093 	if (itemlengths == 0) return(TRUE);
6094 	dtitems = (DLGITEMTEMPLATE **)emalloc(dialog->items * (sizeof (DLGITEMTEMPLATE *)), us_tool->cluster);
6095 	if (dtitems == 0) return(TRUE);
6096 	itemslength = 0;
6097 	for(i=0; i<dialog->items; i++)
6098 	{
6099 		itemlengths[i] = (sizeof DLGITEMTEMPLATE) + 3 * (sizeof (WORD));
6100 		itemtype = dialog->list[i].type;
6101 		switch (itemtype&ITEMTYPE)
6102 		{
6103 			case BUTTON:
6104 			case DEFBUTTON:
6105 			case CHECK:
6106 			case RADIO:
6107 			case EDITTEXT:
6108 			case MESSAGE:
6109 				itemlengths[i] += (estrlen(dialog->list[i].msg) + 1) * (sizeof (WORD));
6110 				break;
6111 			default:
6112 				itemlengths[i] += 1 * (sizeof (WORD));
6113 				break;
6114 		}
6115 		itemlengths[i] = (itemlengths[i] + 3) & ~3;
6116 		itemslength += itemlengths[i];
6117 	}
6118 
6119 	/* allocate space for entire dialog template */
6120 	headerlength = sizeof (DLGTEMPLATE);
6121 	menulength = sizeof (WORD);
6122 	classlength = sizeof (WORD);
6123 	if (dialog->movable != 0) title = dialog->movable; else
6124 		title = x_("");
6125 	titlelength = (estrlen(title) + 1) * (sizeof (WORD));
6126 	i = headerlength + menulength + classlength + titlelength;
6127 	totallen = (i + 3) & ~3;
6128 	descblock = (UCHAR1 *)emalloc(totallen + itemslength, us_tool->cluster);
6129 	if (descblock == 0) return(TRUE);
6130 	pt = descblock;
6131 	dtheader = (DLGTEMPLATE *)pt;  pt += headerlength;
6132 	dtmenu = (WORD *)pt;           pt += menulength;
6133 	dtclass = (WORD *)pt;          pt += classlength;
6134 	dttitle = (WORD *)pt;          pt += titlelength;
6135 	for(i=0; i<dialog->items; i++)
6136 	{
6137 		pt = (UCHAR1 *)(((DWORD)pt + 3) & ~3);
6138 		dtitems[i] = (DLGITEMTEMPLATE *)pt;
6139 		pt += itemlengths[i];
6140 	}
6141 
6142 	/* load dialog template header */
6143 	dtheader->style = WS_VISIBLE | WS_DLGFRAME | WS_POPUP /* | DS_ABSALIGN */;
6144 	if (dialog->movable != 0) dtheader->style |= WS_CAPTION | DS_MODALFRAME;
6145 	dtheader->dwExtendedStyle = WS_EX_CONTROLPARENT;
6146 	dtheader->cdit = (unsigned short)dialog->items;
6147 	dtheader->x = dialog->windowRect.left;
6148 	dtheader->y = dialog->windowRect.top;
6149 	dtheader->cx = (dialog->windowRect.right - dialog->windowRect.left) * dialogdbnx / dialogdbux;
6150 	dtheader->cy = (dialog->windowRect.bottom - dialog->windowRect.top) * dialogdbny / dialogdbuy;
6151 
6152 	/* no menu or class in this dialog */
6153 	dtmenu[0] = 0;
6154 	dtclass[0] = 0;
6155 
6156 	/* load the dialog title */
6157 	woutput = (wchar_t *)output;
6158 #ifdef _UNICODE
6159 	len = estrlen(title);
6160 	for(j=0; j<len; j++) *dttitle++ = title[j];
6161 #else
6162 	len = MultiByteToWideChar(CP_ACP, 0, title, estrlen(title), woutput, 100);
6163 	for(j=0; j<len; j++) *dttitle++ = output[j];
6164 #endif
6165 	*dttitle++ = 0;
6166 
6167 	/* find the default button */
6168 	dia->defaultbutton = 1;
6169 	for(i=0; i<dialog->items; i++)
6170 	{
6171 		itemtype = dialog->list[i].type;
6172 		if ((itemtype&ITEMTYPE) == DEFBUTTON) dia->defaultbutton = i+1;
6173 	}
6174 
6175 	/* load the items */
6176 	for(i=0; i<dialog->items; i++)
6177 	{
6178 		dtitems[i]->x = dialog->list[i].r.left * dialogdbnx / dialogdbux;
6179 		dtitems[i]->y = dialog->list[i].r.top * dialogdbny / dialogdbuy;
6180 		dtitems[i]->cx = (dialog->list[i].r.right - dialog->list[i].r.left) * dialogdbnx / dialogdbux;
6181 		dtitems[i]->cy = (dialog->list[i].r.bottom - dialog->list[i].r.top) * dialogdbny / dialogdbuy;
6182 		dtitems[i]->dwExtendedStyle = 0;
6183 		dtitems[i]->id = i+ID_DIALOGITEM_0;
6184 		itemtype = dialog->list[i].type;
6185 		switch (itemtype&ITEMTYPE)
6186 		{
6187 			case BUTTON:
6188 			case DEFBUTTON:
6189 				itemclass = 0x80;
6190 				style = WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | WS_TABSTOP;
6191 				if (i+1 == dia->defaultbutton) style |= BS_DEFPUSHBUTTON;
6192 				break;
6193 			case CHECK:
6194 				itemclass = 0x80;
6195 				style = WS_VISIBLE | WS_CHILD | BS_CHECKBOX | WS_TABSTOP;
6196 				break;
6197 			case RADIO:
6198 				itemclass = 0x80;
6199 				style = WS_VISIBLE | WS_CHILD | BS_RADIOBUTTON | WS_TABSTOP;
6200 				break;
6201 			case EDITTEXT:
6202 				itemclass = 0x81;
6203 				style = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | WS_TABSTOP;
6204 				if (dtitems[i]->cy > 10) style |= ES_MULTILINE;
6205 				dtitems[i]->y--;
6206 				dtitems[i]->cy += 2;
6207 				break;
6208 			case MESSAGE:
6209 				itemclass = 0x82;
6210 				style = WS_CHILD | WS_VISIBLE | SS_LEFT;
6211 				if ((itemtype&INACTIVE) == 0) style |= SS_NOTIFY;
6212 				dtitems[i]->y--;
6213 				dtitems[i]->cy += 2;
6214 				break;
6215 			case PROGRESS:
6216 			case SCROLL:
6217 			case SCROLLMULTI:
6218 				/* dummy: create it for real later */
6219 				itemclass = 0x82;
6220 				style = WS_CHILD;
6221 				dtitems[i]->id += PROGRESSOFFSET;
6222 				break;
6223 			case USERDRAWN:
6224 				itemclass = 0x82;
6225 				style = WS_CHILD | WS_VISIBLE;
6226 				dtitems[i]->x += 1000;
6227 				dtitems[i]->y += 1000;
6228 				break;
6229 			case POPUP:
6230 				itemclass = 0x85;
6231 				dtitems[i]->cy *= 8;
6232 				dtitems[i]->y -= 2;
6233 				style = WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWNLIST | WS_TABSTOP;
6234 				break;
6235 			case ICON:
6236 				itemclass = 0x82;
6237 				style = WS_CHILD | WS_VISIBLE;
6238 				dtitems[i]->x += 1000;
6239 				dtitems[i]->y += 1000;
6240 				if ((itemtype&INACTIVE) == 0) style |= SS_NOTIFY;
6241 				break;
6242 			default:
6243 				itemclass = 0x82;
6244 				style = WS_CHILD | SS_LEFT;
6245 				break;
6246 		}
6247 		dtitems[i]->style = style;
6248 		pt = ((UCHAR1 *)dtitems[i]) + (sizeof (DLGITEMTEMPLATE));
6249 		dtclass = (WORD *)pt;
6250 		*dtclass++ = 0xFFFF;
6251 		*dtclass++ = itemclass;
6252 		switch (itemtype&ITEMTYPE)
6253 		{
6254 			case BUTTON:
6255 			case DEFBUTTON:
6256 			case CHECK:
6257 			case RADIO:
6258 			case EDITTEXT:
6259 			case MESSAGE:
6260 				msg = dialog->list[i].msg;
6261 				woutput = (wchar_t *)output;
6262 #ifdef _UNICODE
6263 				len = estrlen(msg);
6264 				for(j=0; j<len; j++) *dtclass++ = msg[j];
6265 #else
6266 				len = MultiByteToWideChar(CP_ACP, 0, msg, estrlen(msg), woutput, 100);
6267 				for(j=0; j<len; j++) *dtclass++ = output[j];
6268 #endif
6269 				*dtclass++ = 0;
6270 				break;
6271 			default:
6272 				*dtclass++ = 0;
6273 				break;
6274 		}
6275 		*dtclass++ = 0;
6276 	}
6277 
6278 	/* create the dialog */
6279 	if (dia->window->CreateIndirect(dtheader) == 0)
6280 	{
6281 		return(TRUE);
6282 	}
6283 
6284 	/* finish initialization */
6285 	wnd = 0;
6286 	for(i=0; i<dialog->items; i++)
6287 	{
6288 		itemtype = dialog->list[i].type;
6289 		switch (itemtype&ITEMTYPE)
6290 		{
6291 			case EDITTEXT:
6292 				if (wnd == 0)
6293 					wnd = dia->window->GetDlgItem(i+ID_DIALOGITEM_0);
6294 				(void)allocstring((CHAR **)&dialog->list[i].data, dialog->list[i].msg, el_tempcluster);
6295 				break;
6296 			case PROGRESS:
6297 				stat = (CStatic *)dia->window->GetDlgItem(i+ID_DIALOGITEM_0+PROGRESSOFFSET);
6298 				stat->GetWindowRect(&r);
6299 				dia->window->ScreenToClient(&r);
6300 				prog = new CProgressCtrl();
6301 				prog->Create(WS_CHILD | WS_VISIBLE, r, dia->window, i+ID_DIALOGITEM_0);
6302 				prog->ModifyStyleEx(0, WS_EX_CLIENTEDGE);	/* makes it look 3D */
6303 				break;
6304 			case SCROLL:
6305 			case SCROLLMULTI:
6306 				r.left = dialog->list[i].r.left;
6307 				r.top = dialog->list[i].r.top;
6308 				r.right = dialog->list[i].r.right;
6309 				r.bottom = dialog->list[i].r.bottom;
6310 				style = WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_USETABSTOPS |
6311 					WS_VSCROLL | WS_HSCROLL | LBS_WANTKEYBOARDINPUT | LBS_SORT | WS_TABSTOP;
6312 				if ((itemtype&ITEMTYPE) == SCROLLMULTI)
6313 					style |= LBS_EXTENDEDSEL;
6314 				if ((itemtype&INACTIVE) == 0) style |= LBS_NOTIFY;
6315 				list = new CListBoxEx();
6316 				list->vdia = dia;
6317 				list->Create(style, r, dia->window, i+ID_DIALOGITEM_0);
6318 				list->ModifyStyleEx(0, WS_EX_CLIENTEDGE);	/* makes it look 3D */
6319 				hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
6320 				list->SetFont(CFont::FromHandle(hf));
6321 				list->SetTabStops(1);
6322 				break;
6323 			case ICON:
6324 				dialog->list[i].data = (INTBIG)dialog->list[i].msg;
6325 				break;
6326 		}
6327 	}
6328 	if (wnd == 0)
6329 		wnd = dia->window->GetDlgItem(dia->defaultbutton-1+ID_DIALOGITEM_0);
6330 	dia->window->GotoDlgCtrl(wnd);
6331 	dia->window->SetDefID(dia->defaultbutton-1+ID_DIALOGITEM_0);
6332 
6333 	/* determine location of dialog in screen coordinates */
6334 	dia->window->GetWindowRect(&r);
6335 
6336 	/*
6337 	 * For some reason, the dialog doesn't always appear where we want it.
6338 	 * Probably because it is forced to be placed relative to the previous dialog,
6339 	 * rather than at an absolute location, as we want.
6340 	 *
6341 	 * The hack is to move the dialog after creating it.  This causes a side-effect
6342 	 * when the mouse is set to "snap to cursor": the snapping happens too soon and
6343 	 * the cursor is in the wrong place.  So we must move the cursor too.
6344 	 */
6345 	if (r.left != dtheader->x || r.top != dtheader->y)
6346 	{
6347 		BOOL res;
6348 		INTBIG dx, dy;
6349 		POINT cp;
6350 
6351 		dia->window->SetWindowPos(0, dtheader->x, dtheader->y, 0, 0,
6352 			SWP_NOZORDER|SWP_NOSIZE);
6353 		dx = dtheader->x - r.left;
6354 		dy = dtheader->y - r.top;
6355 		dia->window->GetWindowRect(&r);
6356 
6357 		/* see if mouse is set to "snap to cursor" */
6358 		SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &res, 0);
6359 		if (res)
6360 		{
6361 			/* mouse snapping on: adjust position */
6362 			GetCursorPos(&cp);
6363 			SetCursorPos(cp.x + dx, cp.y + dy);
6364 		}
6365 	}
6366 
6367 	dia->firstpoint.x = r.left;
6368 	dia->firstpoint.y = r.top;
6369 
6370 	dia->useritemdoubleclick = 0;
6371 	dia->usertextsize = 8;
6372 
6373 	efree((CHAR *)itemlengths);
6374 	efree((CHAR *)dtitems);
6375 	efree((CHAR *)descblock);
6376 
6377 	if (*title == 0) title = _("UNTITLED");
6378 	infstr = initinfstr();
6379 	formatinfstr(infstr, _("Start dialog %s"), title);
6380 	gra_logwritecomment(returninfstr(infstr));
6381 	dia->dialoghit = -1;
6382 	return(FALSE);
6383 }
6384 
6385 /*
6386  * Routine to handle actions and return the next item hit.
6387  */
DiaNextHit(void * vdia)6388 INTBIG DiaNextHit(void *vdia)
6389 {
6390 	INTBIG item;
6391 	TDIALOG *dia;
6392 
6393 	dia = (TDIALOG *)vdia;
6394 	for(;;)
6395 	{
6396 		flushscreen();
6397 		gra_nextevent();
6398 		if (dia->dialoghit != -1)
6399 		{
6400 			item = dia->dialoghit;
6401 			dia->dialoghit = -1;
6402 			return(item);
6403 		}
6404 	}
6405 }
6406 
DiaDoneDialog(void * vdia)6407 void DiaDoneDialog(void *vdia)
6408 {
6409 	REGISTER INTBIG dx, dy, i, itemtype;
6410 	RECT rect;
6411 	TDIALOG *dia, *ldia, *odia;
6412 
6413 	dia = (TDIALOG *)vdia;
6414 
6415 	/* remove this from the list of active dialogs */
6416 	ldia = NOTDIALOG;
6417 	for(odia = gra_firstactivedialog; odia != NOTDIALOG; odia = odia->nexttdialog)
6418 	{
6419 		if (odia == dia) break;
6420 		ldia = odia;
6421 	}
6422 	if (odia != NOTDIALOG)
6423 	{
6424 		if (ldia == NOTDIALOG) gra_firstactivedialog = dia->nexttdialog; else
6425 			ldia->nexttdialog = dia->nexttdialog;
6426 	}
6427 
6428 	/* if playing back, search for end-dialog marker */
6429 	while (us_logplay != NULL)
6430 	{
6431 		if (gra_loggetnextaction(0)) break;
6432 		if (gra_inputstate == DIAENDDIALOG) break;
6433 		gra_inputstate = NOEVENT;
6434 	}
6435 	gra_inputstate = NOEVENT;
6436 	gra_logwriteaction(DIAENDDIALOG, dia->windindex, 0, 0, 0);
6437 
6438 	/* free memory used by edit text fields */
6439 	for(i=0; i<dia->itemdesc->items; i++)
6440 	{
6441 		itemtype = dia->itemdesc->list[i].type;
6442 		if ((itemtype&ITEMTYPE) == EDITTEXT)
6443 			efree((CHAR *)dia->itemdesc->list[i].data);
6444 	}
6445 
6446 	/* update dialog location if it was moved */
6447 	dia->window->GetWindowRect(&rect);
6448 	dx = rect.left - dia->firstpoint.x;
6449 	dy = rect.top - dia->firstpoint.y;
6450 	dia->itemdesc->windowRect.left += (INTSML)dx;
6451 	dia->itemdesc->windowRect.right += (INTSML)dx;
6452 	dia->itemdesc->windowRect.top += (INTSML)dy;
6453 	dia->itemdesc->windowRect.bottom += (INTSML)dy;
6454 
6455 	dia->window->EndDialog(0);
6456 
6457 	/* free the "dia" structure */
6458 	efree((CHAR *)dia);
6459 }
6460 
6461 /*
6462  * Routine to change the size of the dialog
6463  */
DiaResizeDialog(void * vdia,INTBIG wid,INTBIG hei)6464 void DiaResizeDialog(void *vdia, INTBIG wid, INTBIG hei)
6465 {
6466 	TDIALOG *dia;
6467 	RECT rect;
6468 #define WINDOWPADWIDTH        7
6469 #define WINDOWPADHEIGHT      26
6470 
6471 	dia = (TDIALOG *)vdia;
6472 	dia->window->GetWindowRect(&rect);
6473 	dia->window->SetWindowPos(NULL, rect.left, rect.top, wid+WINDOWPADWIDTH, hei+WINDOWPADHEIGHT,
6474 		SWP_NOMOVE | SWP_NOZORDER);
6475 }
6476 
6477 /*
6478  * Force the dialog to be visible.
6479  */
DiaBringToTop(void * vdia)6480 void DiaBringToTop(void *vdia)
6481 {
6482 	TDIALOG *dia;
6483 
6484 	dia = (TDIALOG *)vdia;
6485 }
6486 
6487 /*
6488  * Routine to set the text in item "item" to "msg"
6489  */
DiaSetText(void * vdia,INTBIG item,CHAR * msg)6490 void DiaSetText(void *vdia, INTBIG item, CHAR *msg)
6491 {
6492 	INTBIG type, highlight, len;
6493 	REGISTER CHAR *pt;
6494 	CWnd *wnd;
6495 	CEdit *edit;
6496 	TDIALOG *dia;
6497 
6498 	dia = (TDIALOG *)vdia;
6499 	highlight = 0;
6500 	if (item < 0)
6501 	{
6502 		item = -item;
6503 		highlight = 1;
6504 	}
6505 	item--;
6506 	for(pt = msg; *pt != 0; pt++) if (estrncmp(pt, x_("(c)"), 3) == 0)
6507 	{
6508 		(void)estrcpy(pt, x_("�"));		/* "copyright" character */
6509 		(void)estrcpy(&pt[1], &pt[3]);
6510 		break;
6511 	}
6512 	wnd = dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6513 	if (wnd == 0) return;
6514 	wnd->SetWindowText(msg);
6515 	type = dia->itemdesc->list[item].type;
6516 	if ((type&ITEMTYPE) == EDITTEXT)
6517 	{
6518 		edit = (CEdit *)wnd;
6519 		len = estrlen(msg);
6520 		if (highlight == 0) edit->SetSel(len, len, FALSE); else
6521 		{
6522 			edit->SetSel(0, len, FALSE);
6523 			dia->window->GotoDlgCtrl(wnd);
6524 		}
6525 		(void)reallocstring((CHAR **)&dia->itemdesc->list[item].data, msg, el_tempcluster);
6526 	}
6527 }
6528 
6529 /*
6530  * Routine to return the text in item "item"
6531  */
DiaGetText(void * vdia,INTBIG item)6532 CHAR *DiaGetText(void *vdia, INTBIG item)
6533 {
6534 	static int bufnum = 0;
6535 	static CHAR line[10][300];
6536 	CWnd *wnd;
6537 	TDIALOG *dia;
6538 
6539 	dia = (TDIALOG *)vdia;
6540 	item--;
6541 	wnd = dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6542 	if (wnd == 0) return(x_(""));
6543 	bufnum++;
6544 	if (bufnum >= 10) bufnum = 0;
6545 	wnd->GetWindowText(line[bufnum], 300);
6546 	return(line[bufnum]);
6547 }
6548 
6549 /*
6550  * Routine to set the value in item "item" to "value"
6551  */
DiaSetControl(void * vdia,INTBIG item,INTBIG value)6552 void DiaSetControl(void *vdia, INTBIG item, INTBIG value)
6553 {
6554 	CButton *but;
6555 	TDIALOG *dia;
6556 
6557 	dia = (TDIALOG *)vdia;
6558 	item--;
6559 	but = (CButton *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6560 	if (but == 0) return;
6561 	but->SetCheck(value);
6562 	gra_logwriteaction(DIASETCONTROL, dia->windindex, item + 1, value, 0);
6563 }
6564 
6565 /*
6566  * Routine to return the value in item "item"
6567  */
DiaGetControl(void * vdia,INTBIG item)6568 INTBIG DiaGetControl(void *vdia, INTBIG item)
6569 {
6570 	CButton *but;
6571 	TDIALOG *dia;
6572 
6573 	dia = (TDIALOG *)vdia;
6574 	item--;
6575 	but = (CButton *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6576 	if (but == 0) return(0);
6577 	return(but->GetCheck());
6578 }
6579 
6580 /*
6581  * Routine to check item "item" to make sure that there is
6582  * text in it.  If so, it returns true.  Otherwise it beeps and returns false.
6583  */
DiaValidEntry(void * vdia,INTBIG item)6584 BOOLEAN DiaValidEntry(void *vdia, INTBIG item)
6585 {
6586 	CHAR *msg;
6587 	TDIALOG *dia;
6588 
6589 	dia = (TDIALOG *)vdia;
6590 	msg = DiaGetText(dia, item);
6591 	while (*msg == ' ' || *msg == '\t') msg++;
6592 	if (*msg != 0) return(TRUE);
6593 	ttybeep(SOUNDBEEP, TRUE);
6594 	return(FALSE);
6595 }
6596 
6597 /*
6598  * Routine to dim item "item"
6599  */
DiaDimItem(void * vdia,INTBIG item)6600 void DiaDimItem(void *vdia, INTBIG item)
6601 {
6602 	CWnd *wnd, *focus;
6603 	TDIALOG *dia;
6604 
6605 	dia = (TDIALOG *)vdia;
6606 	item--;
6607 	wnd = dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6608 	focus = CWnd::GetFocus();
6609 	if (focus == wnd)
6610 		dia->window->NextDlgCtrl();
6611 	wnd->EnableWindow(0);
6612 }
6613 
6614 /*
6615  * Routine to un-dim item "item"
6616  */
DiaUnDimItem(void * vdia,INTBIG item)6617 void DiaUnDimItem(void *vdia, INTBIG item)
6618 {
6619 	CWnd *wnd;
6620 	TDIALOG *dia;
6621 
6622 	dia = (TDIALOG *)vdia;
6623 	item--;
6624 	wnd = dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6625 	wnd->EnableWindow(1);
6626 }
6627 
6628 /*
6629  * Routine to change item "item" to be a message rather
6630  * than editable text
6631  */
DiaNoEditControl(void * vdia,INTBIG item)6632 void DiaNoEditControl(void *vdia, INTBIG item)
6633 {
6634 	TDIALOG *dia;
6635 
6636 	dia = (TDIALOG *)vdia;
6637 	DiaDimItem(dia, item);
6638 }
6639 
6640 /*
6641  * Routine to change item "item" to be editable text rather
6642  * than a message
6643  */
DiaEditControl(void * vdia,INTBIG item)6644 void DiaEditControl(void *vdia, INTBIG item)
6645 {
6646 	TDIALOG *dia;
6647 
6648 	dia = (TDIALOG *)vdia;
6649 	DiaUnDimItem(dia, item);
6650 }
6651 
DiaOpaqueEdit(void * vdia,INTBIG item)6652 void DiaOpaqueEdit(void *vdia, INTBIG item)
6653 {
6654 	CEdit *edit;
6655 	TDIALOG *dia;
6656 
6657 	dia = (TDIALOG *)vdia;
6658 	item--;
6659 	edit = (CEdit *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6660 	edit->ModifyStyle(0, ES_PASSWORD);
6661 	edit->SetPasswordChar('*');
6662 }
6663 
6664 /*
6665  * Routine to cause item "item" to be the default button
6666  */
DiaDefaultButton(void * vdia,INTBIG item)6667 void DiaDefaultButton(void *vdia, INTBIG item)
6668 {
6669 	TDIALOG *dia;
6670 
6671 	dia = (TDIALOG *)vdia;
6672 	dia->defaultbutton = item;
6673 	dia->window->SetDefID(dia->defaultbutton-1+ID_DIALOGITEM_0);
6674 }
6675 
6676 /*
6677  * Routine to change the icon in item "item" to be the 32x32 bitmap (128 bytes) at "addr".
6678  */
DiaChangeIcon(void * vdia,INTBIG item,UCHAR1 * addr)6679 void DiaChangeIcon(void *vdia, INTBIG item, UCHAR1 *addr)
6680 {
6681 	HICON map;
6682 	INTBIG x, y;
6683 	CDC *dc;
6684 	RECT rr;
6685 	TDIALOG *dia;
6686 	COLORREF backc;
6687 
6688 	dia = (TDIALOG *)vdia;
6689 	item--;
6690 	dia->itemdesc->list[item].data = (INTBIG)addr;
6691 	map = gra_makeicon(dia->itemdesc->list[item].data);
6692 
6693 	/* redraw */
6694 	dc = dia->window->GetDC();
6695 
6696 	/* erase the area */
6697 	rr.left = dia->itemdesc->list[item].r.left;
6698 	rr.right = dia->itemdesc->list[item].r.right;
6699 	rr.top = dia->itemdesc->list[item].r.top;
6700 	rr.bottom = dia->itemdesc->list[item].r.bottom;
6701 	if (gra_dialogbkgrbrush == 0)
6702 	{
6703 		backc = 0;
6704 		for(y=rr.top; y<rr.bottom; y++)
6705 		{
6706 			for(x=rr.left; x<rr.right; x++)
6707 			{
6708 				backc = dia->window->GetDC()->GetPixel(x, y);
6709 				if (backc != 0) break;
6710 			}
6711 			if (backc != 0) break;
6712 		}
6713 		gra_dialogbkgrbrush = new CBrush(backc);
6714 	}
6715 	dc->FillRect(&rr, gra_dialogbkgrbrush);
6716 
6717 	/* draw the icon */
6718 	x = dia->itemdesc->list[item].r.left;
6719 	y = dia->itemdesc->list[item].r.top;
6720 	dc->DrawIcon(x, y, map);
6721 	DestroyIcon(map);
6722 	dia->window->ReleaseDC(dc);
6723 }
6724 
6725 /*
6726  * Routine to change item "item" into a popup with "count" entries
6727  * in "names".
6728  */
DiaSetPopup(void * vdia,INTBIG item,INTBIG count,CHAR ** names)6729 void DiaSetPopup(void *vdia, INTBIG item, INTBIG count, CHAR **names)
6730 {
6731 	INTBIG i;
6732 	CComboBox *cb;
6733 	TDIALOG *dia;
6734 
6735 	dia = (TDIALOG *)vdia;
6736 	item--;
6737 	cb = (CComboBox *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6738 	if (cb == 0) return;
6739 	cb->ResetContent();
6740 	for(i=0; i<count; i++) cb->AddString(names[i]);
6741 	cb->SetCurSel(0);
6742 }
6743 
6744 /*
6745  * Routine to change popup item "item" so that the current entry is "entry".
6746  */
DiaSetPopupEntry(void * vdia,INTBIG item,INTBIG entry)6747 void DiaSetPopupEntry(void *vdia, INTBIG item, INTBIG entry)
6748 {
6749 	CComboBox *cb;
6750 	TDIALOG *dia;
6751 
6752 	dia = (TDIALOG *)vdia;
6753 	item--;
6754 	cb = (CComboBox *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6755 	if (cb == 0) return;
6756 	cb->SetCurSel(entry);
6757 }
6758 
6759 /*
6760  * Routine to return the current item in popup menu item "item".
6761  */
DiaGetPopupEntry(void * vdia,INTBIG item)6762 INTBIG DiaGetPopupEntry(void *vdia, INTBIG item)
6763 {
6764 	CComboBox *cb;
6765 	TDIALOG *dia;
6766 
6767 	dia = (TDIALOG *)vdia;
6768 	item--;
6769 	cb = (CComboBox *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6770 	if (cb == 0) return(0);
6771 	return(cb->GetCurSel());
6772 }
6773 
DiaInitTextDialog(void * vdia,INTBIG item,BOOLEAN (* toplist)(CHAR **),CHAR * (* nextinlist)(void),void (* donelist)(void),INTBIG sortpos,INTBIG flags)6774 void DiaInitTextDialog(void *vdia, INTBIG item, BOOLEAN (*toplist)(CHAR **),
6775 	CHAR *(*nextinlist)(void), void (*donelist)(void), INTBIG sortpos, INTBIG flags)
6776 {
6777 	long add, remove;
6778 	static CFont *fnt = 0;
6779 	LOGFONT lf;
6780 	CListBoxEx *list;
6781 	TDIALOG *dia;
6782 
6783 	dia = (TDIALOG *)vdia;
6784 	item--;
6785 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6786 	if (list == 0) return;
6787 
6788 	add = remove = 0;
6789 	if ((flags&SCHORIZBAR) == 0) remove |= WS_HSCROLL; else
6790 	{
6791 		add |= WS_HSCROLL;
6792 		list->SetHorizontalExtent(1000);
6793 	}
6794 	list->ModifyStyle(remove, add, SWP_NOSIZE);
6795 	dia->itemdesc->list[item].data = flags;
6796 	if ((flags&SCFIXEDWIDTH) != 0)
6797 	{
6798 		if (fnt == 0)
6799 		{
6800 			lf.lfHeight = -10;
6801 			estrcpy(lf.lfFaceName, x_("Lucida Console"));
6802 			lf.lfWidth = 0;
6803 			lf.lfEscapement = 0;
6804 			lf.lfOrientation = 0;
6805 			lf.lfWeight = FW_NORMAL;
6806 			lf.lfItalic = 0;
6807 			lf.lfUnderline = 0;
6808 			lf.lfStrikeOut = 0;
6809 			lf.lfCharSet = 0;
6810 			lf.lfOutPrecision = OUT_STROKE_PRECIS;
6811 			lf.lfClipPrecision = CLIP_STROKE_PRECIS;
6812 			lf.lfQuality = 1;
6813 			lf.lfPitchAndFamily = FF_DONTCARE | FF_SWISS;
6814 
6815 			fnt = new CFont();
6816 			fnt->CreateFontIndirect(&lf);
6817 		}
6818 		list->SetFont(fnt);
6819 	}
6820 	DiaLoadTextDialog(vdia, item+1, toplist, nextinlist, donelist, sortpos);
6821 }
6822 
DiaLoadTextDialog(void * vdia,INTBIG item,BOOLEAN (* toplist)(CHAR **),CHAR * (* nextinlist)(void),void (* donelist)(void),INTBIG sortpos)6823 void DiaLoadTextDialog(void *vdia, INTBIG item, BOOLEAN (*toplist)(CHAR **),
6824 		CHAR *(*nextinlist)(void), void (*donelist)(void), INTBIG sortpos)
6825 {
6826 	CHAR *next, line[256];
6827 	INTBIG i;
6828 	CListBoxEx *list;
6829 	TDIALOG *dia;
6830 
6831 	dia = (TDIALOG *)vdia;
6832 	item--;
6833 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6834 	if (list == 0) return;
6835 
6836 	/* clear the list */
6837 	list->ResetContent();
6838 
6839 	/* load the list */
6840 	line[0] = 0;
6841 	next = line;
6842 	(void)(*toplist)(&next);
6843 	for(i=0; ; i++)
6844 	{
6845 		next = (*nextinlist)();
6846 		if (next == 0) break;
6847 		if (sortpos < 0) list->InsertString(-1, next); else
6848 			list->AddString(next);
6849 	}
6850 	(*donelist)();
6851 	if (i > 0) list->SetCurSel(0);
6852 }
6853 
6854 /*
6855  * Routine to stuff line "line" at the end of the edit buffer.
6856  */
DiaStuffLine(void * vdia,INTBIG item,CHAR * line)6857 void DiaStuffLine(void *vdia, INTBIG item, CHAR *line)
6858 {
6859 	CListBoxEx *list;
6860 	TDIALOG *dia;
6861 
6862 	dia = (TDIALOG *)vdia;
6863 	item--;
6864 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6865 	if (list == 0) return;
6866 	list->InsertString(-1, line);
6867 }
6868 
6869 /*
6870  * Routine to select line "line" of scroll item "item".
6871  */
DiaSelectLine(void * vdia,INTBIG item,INTBIG line)6872 void DiaSelectLine(void *vdia, INTBIG item, INTBIG line)
6873 {
6874 	CListBoxEx *list;
6875 	INTBIG numitems, botitem, topitem, visibleitems, type;
6876 	CPoint pt;
6877 	BOOL outside;
6878 	TDIALOG *dia;
6879 
6880 	dia = (TDIALOG *)vdia;
6881 	item--;
6882 	type = dia->itemdesc->list[item].type;
6883 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6884 	if (list == 0) return;
6885 	pt.x = 0;   pt.y = 0;
6886 	topitem = list->ItemFromPoint(pt, outside);
6887 	pt.x = 0;   pt.y = 9999;
6888 	botitem = list->ItemFromPoint(pt, outside);
6889 	visibleitems = botitem - topitem;
6890 	numitems = list->GetCount();
6891 	if (line < topitem || line >= botitem)
6892 	{
6893 		topitem = line - visibleitems/2;
6894 		list->SetTopIndex(topitem);
6895 	}
6896 	if ((type&ITEMTYPE) == SCROLL)
6897 	{
6898 		list->SetCurSel(line);
6899 	} else
6900 	{
6901 		list->SetSel(-1, FALSE);
6902 		list->SetSel(line, TRUE);
6903 	}
6904 }
6905 
6906 /*
6907  * Routine to select "count" lines in "lines" of scroll item "item".
6908  */
DiaSelectLines(void * vdia,INTBIG item,INTBIG count,INTBIG * lines)6909 void DiaSelectLines(void *vdia, INTBIG item, INTBIG count, INTBIG *lines)
6910 {
6911 	CListBoxEx *list;
6912 	INTBIG numitems, botitem, topitem, visibleitems, i, low, high;
6913 	CPoint pt;
6914 	BOOL outside;
6915 	TDIALOG *dia;
6916 
6917 	dia = (TDIALOG *)vdia;
6918 	if (count <= 0) return;
6919 	item--;
6920 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6921 	if (list == 0) return;
6922 	pt.x = 0;   pt.y = 0;
6923 	topitem = list->ItemFromPoint(pt, outside);
6924 	pt.x = 0;   pt.y = 9999;
6925 	botitem = list->ItemFromPoint(pt, outside);
6926 	visibleitems = botitem - topitem;
6927 	numitems = list->GetCount();
6928 	low = high = lines[0];
6929 	for(i=1; i<count; i++)
6930 	{
6931 		if (lines[i] < low) low = lines[i];
6932 		if (lines[i] > high) high = lines[i];
6933 	}
6934 	if (high < topitem || low >= botitem)
6935 	{
6936 		topitem = low;
6937 		list->SetTopIndex(topitem);
6938 	}
6939 	list->SetSel(-1, FALSE);
6940 	for(i=0; i<count; i++)
6941 		list->SetSel(lines[i], TRUE);
6942 }
6943 
6944 /*
6945  * Returns the currently selected line in the scroll list "item".
6946  */
DiaGetCurLine(void * vdia,INTBIG item)6947 INTBIG DiaGetCurLine(void *vdia, INTBIG item)
6948 {
6949 	INTBIG line;
6950 	CListBoxEx *list;
6951 	TDIALOG *dia;
6952 
6953 	dia = (TDIALOG *)vdia;
6954 	item--;
6955 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6956 	if (list == 0) return(-1);
6957 	line = list->GetCurSel();
6958 	if (line == LB_ERR) return(-1);
6959 	return(line);
6960 }
6961 
6962 /*
6963  * Returns the currently selected lines in the scroll list "item".  The returned
6964  * array is terminated with -1.
6965  */
DiaGetCurLines(void * vdia,INTBIG item)6966 INTBIG *DiaGetCurLines(void *vdia, INTBIG item)
6967 {
6968 	REGISTER INTBIG total, i;
6969 	CListBoxEx *list;
6970 	static INTBIG selitemlist[MAXSCROLLMULTISELECT];
6971 	int selintlist[MAXSCROLLMULTISELECT];
6972 	TDIALOG *dia;
6973 
6974 	dia = (TDIALOG *)vdia;
6975 	item--;
6976 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6977 	if (list == 0) total = 0; else
6978 	{
6979 		total = list->GetSelItems(MAXSCROLLMULTISELECT-1, selintlist);
6980 		if (total == LB_ERR) total = 0;
6981 		for(i=0; i<total; i++) selitemlist[i] = selintlist[i];
6982 	}
6983 	selitemlist[total] = -1;
6984 	return(selitemlist);
6985 }
6986 
DiaGetNumScrollLines(void * vdia,INTBIG item)6987 INTBIG DiaGetNumScrollLines(void *vdia, INTBIG item)
6988 {
6989 	CListBoxEx *list;
6990 	TDIALOG *dia;
6991 
6992 	dia = (TDIALOG *)vdia;
6993 	item--;
6994 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
6995 	if (list == 0) return(0);
6996 	return (list->GetCount());
6997 }
6998 
DiaGetScrollLine(void * vdia,INTBIG item,INTBIG line)6999 CHAR *DiaGetScrollLine(void *vdia, INTBIG item, INTBIG line)
7000 {
7001 	static CHAR text[300];
7002 	CListBoxEx *list;
7003 	TDIALOG *dia;
7004 
7005 	dia = (TDIALOG *)vdia;
7006 	item--;
7007 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
7008 	if (list == 0) return(x_(""));
7009 	if (list->GetText(line, text) == -1) return(x_(""));
7010 	return(text);
7011 }
7012 
DiaSetScrollLine(void * vdia,INTBIG item,INTBIG line,CHAR * msg)7013 void DiaSetScrollLine(void *vdia, INTBIG item, INTBIG line, CHAR *msg)
7014 {
7015 	CListBoxEx *list;
7016 	TDIALOG *dia;
7017 
7018 	dia = (TDIALOG *)vdia;
7019 	item--;
7020 	list = (CListBoxEx *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
7021 	if (list == 0) return;
7022 	list->DeleteString(line);
7023 	list->InsertString(line, msg);
7024 	list->SetCurSel(line);
7025 }
7026 
DiaSynchVScrolls(void * vdia,INTBIG item1,INTBIG item2,INTBIG item3)7027 void DiaSynchVScrolls(void *vdia, INTBIG item1, INTBIG item2, INTBIG item3)
7028 {
7029 	TDIALOG *dia;
7030 
7031 	dia = (TDIALOG *)vdia;
7032 	if (item1 <= 0 || item1 > dia->itemdesc->items) return;
7033 	if (item2 <= 0 || item2 > dia->itemdesc->items) return;
7034 	if (item3 < 0 || item3 > dia->itemdesc->items) return;
7035 	if (dia->numlocks >= MAXLOCKS) return;
7036 	dia->lock1[dia->numlocks] = item1 - 1;
7037 	dia->lock2[dia->numlocks] = item2 - 1;
7038 	dia->lock3[dia->numlocks] = item3 - 1;
7039 	dia->numlocks++;
7040 }
7041 
DiaUnSynchVScrolls(void * vdia)7042 void DiaUnSynchVScrolls(void *vdia)
7043 {
7044 	TDIALOG *dia;
7045 
7046 	dia = (TDIALOG *)vdia;
7047 	dia->numlocks = 0;
7048 }
7049 
DiaItemRect(void * vdia,INTBIG item,RECTAREA * rect)7050 void DiaItemRect(void *vdia, INTBIG item, RECTAREA *rect)
7051 {
7052 	TDIALOG *dia;
7053 
7054 	dia = (TDIALOG *)vdia;
7055 	if (item <= 0 || item > dia->itemdesc->items) return;
7056 	item--;
7057 	*rect = dia->itemdesc->list[item].r;
7058 }
7059 
DiaPercent(void * vdia,INTBIG item,INTBIG percent)7060 void DiaPercent(void *vdia, INTBIG item, INTBIG percent)
7061 {
7062 	CProgressCtrl *prog;
7063 	TDIALOG *dia;
7064 
7065 	dia = (TDIALOG *)vdia;
7066 	item--;
7067 	prog = (CProgressCtrl *)dia->window->GetDlgItem(item+ID_DIALOGITEM_0);
7068 	prog->SetPos(percent);
7069 }
7070 
DiaRedispRoutine(void * vdia,INTBIG item,void (* routine)(RECTAREA *,void *))7071 void DiaRedispRoutine(void *vdia, INTBIG item, void (*routine)(RECTAREA*, void*))
7072 {
7073 	TDIALOG *dia;
7074 
7075 	dia = (TDIALOG *)vdia;
7076 	dia->redrawroutine = routine;
7077 	dia->redrawitem = item;
7078 }
7079 
DiaAllowUserDoubleClick(void * vdia)7080 void DiaAllowUserDoubleClick(void *vdia)
7081 {
7082 	TDIALOG *dia;
7083 
7084 	dia = (TDIALOG *)vdia;
7085 	dia->useritemdoubleclick = 1;
7086 }
7087 
DiaDrawRect(void * vdia,INTBIG item,RECTAREA * ur,INTBIG r,INTBIG g,INTBIG b)7088 void DiaDrawRect(void *vdia, INTBIG item, RECTAREA *ur, INTBIG r, INTBIG g, INTBIG b)
7089 {
7090 	RECT rr;
7091 	CDC *dc;
7092 	CBrush *brush;
7093 	COLORREF color;
7094 	TDIALOG *dia;
7095 
7096 	dia = (TDIALOG *)vdia;
7097 	rr.left = ur->left;
7098 	rr.right = ur->right;
7099 	rr.top = ur->top;
7100 	rr.bottom = ur->bottom;
7101 
7102 	color = (b << 16) | (g << 8) | r;
7103 	brush = new CBrush(color);
7104 	dc = dia->window->GetDC();
7105 	dc->FillRect(&rr, brush);
7106 	dia->window->ReleaseDC(dc);
7107 	delete brush;
7108 }
7109 
DiaFrameRect(void * vdia,INTBIG item,RECTAREA * ur)7110 void DiaFrameRect(void *vdia, INTBIG item, RECTAREA *ur)
7111 {
7112 	RECT r;
7113 	CDC *dc;
7114 	TDIALOG *dia;
7115 
7116 	dia = (TDIALOG *)vdia;
7117 	r.left = ur->left;
7118 	r.right = ur->right-1;
7119 	r.top = ur->top;
7120 	r.bottom = ur->bottom-1;
7121 
7122 	if (gra_dialogoffbrush == 0) gra_dialogoffbrush = new CBrush((COLORREF)0xFFFFFF);
7123 	dc = dia->window->GetDC();
7124 	dc->FillRect(&r, gra_dialogoffbrush);
7125 	dc->MoveTo(r.left, r.top);
7126 	dc->LineTo(r.right, r.top);
7127 	dc->LineTo(r.right, r.bottom);
7128 	dc->LineTo(r.left, r.bottom);
7129 	dc->LineTo(r.left, r.top);
7130 	dia->window->ReleaseDC(dc);
7131 }
7132 
DiaInvertRect(void * vdia,INTBIG item,RECTAREA * ur)7133 void DiaInvertRect(void *vdia, INTBIG item, RECTAREA *ur)
7134 {
7135 	RECT r;
7136 	CDC *dc;
7137 	TDIALOG *dia;
7138 
7139 	dia = (TDIALOG *)vdia;
7140 	r.left = ur->left;
7141 	r.right = ur->right-1;
7142 	r.top = ur->top;
7143 	r.bottom = ur->bottom-1;
7144 
7145 	dc = dia->window->GetDC();
7146 	dc->InvertRect(&r);
7147 	dia->window->ReleaseDC(dc);
7148 }
7149 
DiaDrawLine(void * vdia,INTBIG item,INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty,INTBIG mode)7150 void DiaDrawLine(void *vdia, INTBIG item, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, INTBIG mode)
7151 {
7152 	CDC *dc;
7153 	TDIALOG *dia;
7154 
7155 	dia = (TDIALOG *)vdia;
7156 	dc = dia->window->GetDC();
7157 	switch (mode)
7158 	{
7159 		case DLMODEON:     dc->SetROP2(R2_BLACK);   break;
7160 		case DLMODEOFF:    dc->SetROP2(R2_WHITE);   break;
7161 		case DLMODEINVERT: dc->SetROP2(R2_NOT);     break;
7162 	}
7163 	dc->MoveTo(fx, fy);
7164 	dc->LineTo(tx, ty);
7165 	dc->SetROP2(R2_COPYPEN);
7166 	dia->window->ReleaseDC(dc);
7167 }
7168 
DiaFillPoly(void * vdia,INTBIG item,INTBIG * x,INTBIG * y,INTBIG count,INTBIG r,INTBIG g,INTBIG b)7169 void DiaFillPoly(void *vdia, INTBIG item, INTBIG *x, INTBIG *y, INTBIG count, INTBIG r, INTBIG g, INTBIG b)
7170 {
7171 	CBrush *polybrush;
7172 	CPen *polypen;
7173 	COLORREF brushcolor;
7174 	POINT points[50];
7175 	INTBIG i;
7176 	CDC *dc;
7177 	TDIALOG *dia;
7178 
7179 	dia = (TDIALOG *)vdia;
7180 	for(i=0; i<count; i++)
7181 	{
7182 		points[i].x = x[i];
7183 		points[i].y = y[i];
7184 	}
7185 
7186 	brushcolor = (COLORREF)((b << 16) | (g << 8) | r);
7187 	polybrush = new CBrush(brushcolor);
7188 	polypen = new CPen(PS_SOLID, 0, brushcolor);
7189 	dc = dia->window->GetDC();
7190 	dc->SelectObject(polybrush);
7191 	dc->SelectObject(polypen);
7192 	dc->Polygon(points, count);
7193 	delete polybrush;
7194 	delete polypen;
7195 	dia->window->ReleaseDC(dc);
7196 }
7197 
DiaPutText(void * vdia,INTBIG item,CHAR * msg,INTBIG x,INTBIG y)7198 void DiaPutText(void *vdia, INTBIG item, CHAR *msg, INTBIG x, INTBIG y)
7199 {
7200 	CFont *font;
7201 	CDC *dc;
7202 	UINTBIG descript[TEXTDESCRIPTSIZE];
7203 	TDIALOG *dia;
7204 
7205 	dia = (TDIALOG *)vdia;
7206 	TDCLEAR(descript);
7207 	TDSETSIZE(descript, TXTSETPOINTS(dia->usertextsize));
7208 	font = gra_gettextfont(NOWINDOWPART, NOTECHNOLOGY, descript);
7209 	if (font == 0) return;
7210 	dc = dia->window->GetDC();
7211 	dc->SelectObject(font);
7212 	dc->SetBkMode(TRANSPARENT);
7213 	dc->TextOut(x, y, msg, estrlen(msg));
7214 	dia->window->ReleaseDC(dc);
7215 }
7216 
DiaSetTextSize(void * vdia,INTBIG size)7217 void DiaSetTextSize(void *vdia, INTBIG size)
7218 {
7219 	TDIALOG *dia;
7220 
7221 	dia = (TDIALOG *)vdia;
7222 	dia->usertextsize = size;
7223 }
7224 
DiaGetTextInfo(void * vdia,CHAR * msg,INTBIG * wid,INTBIG * hei)7225 void DiaGetTextInfo(void *vdia, CHAR *msg, INTBIG *wid, INTBIG *hei)
7226 {
7227 	CFont *font;
7228 	CSize textSize;
7229 	CDC *dc;
7230 	UINTBIG descript[TEXTDESCRIPTSIZE];
7231 	TDIALOG *dia;
7232 
7233 	dia = (TDIALOG *)vdia;
7234 	TDCLEAR(descript);
7235 	TDSETSIZE(descript, TXTSETPOINTS(dia->usertextsize));
7236 	font = gra_gettextfont(NOWINDOWPART, NOTECHNOLOGY, descript);
7237 	if (font == 0) return;
7238 	dc = dia->window->GetDC();
7239 	dc->SelectObject(font);
7240 	textSize = dc->GetTextExtent(msg, estrlen(msg));
7241 	*wid = textSize.cx;
7242 	*hei = textSize.cy+1;
7243 	dia->window->ReleaseDC(dc);
7244 }
7245 
DiaTrackCursor(void * vdia,void (* eachdown)(INTBIG x,INTBIG y))7246 void DiaTrackCursor(void *vdia, void (*eachdown)(INTBIG x, INTBIG y))
7247 {
7248 	TDIALOG *dia;
7249 
7250 	dia = (TDIALOG *)vdia;
7251 	gra_trackingdialog = dia;
7252 	dia->diaeachdown = eachdown;
7253 	trackcursor(FALSE, us_nullup, us_nullvoid, gra_diaeachdownhandler,
7254 		us_nullchar, us_nullvoid, TRACKNORMAL);
7255 	gra_trackingdialog = 0;
7256 }
7257 
gra_diaeachdownhandler(INTBIG ox,INTBIG oy)7258 BOOLEAN gra_diaeachdownhandler(INTBIG ox, INTBIG oy)
7259 {
7260 	INTBIG x, y;
7261 
7262 	if (gra_trackingdialog == 0) return(FALSE);
7263 	DiaGetMouse(gra_trackingdialog, &x, &y);
7264 	(void)((*gra_trackingdialog->diaeachdown)(x, y));
7265 	return(FALSE);
7266 }
7267 
DiaGetMouse(void * vdia,INTBIG * x,INTBIG * y)7268 void DiaGetMouse(void *vdia, INTBIG *x, INTBIG *y)
7269 {
7270 	POINT p, p2;
7271 	TDIALOG *dia;
7272 
7273 	dia = (TDIALOG *)vdia;
7274 	if (us_logplay != NULL)
7275 	{
7276 		if (!gra_loggetnextaction(0))
7277 		{
7278 			*x = gra_action.x;
7279 			*y = gra_action.y;
7280 		}
7281 	}
7282 	if (us_logplay == NULL)
7283 	{
7284 		p2.x = p2.y = 0;
7285 		dia->window->MapWindowPoints(0, &p2, 1);
7286 		GetCursorPos(&p);
7287 		*x = p.x - p2.x;   *y = p.y - p2.y;
7288 	}
7289 	gra_logwriteaction(DIAUSERMOUSE, dia->windindex, *x, *y, 0);
7290 }
7291 
7292 /************************* DIALOG SUPPORT *************************/
7293 
7294 /*
7295  * Routine to redraw user items and divider lines
7296  */
gra_diaredrawitem(CElectricDialog * cedia)7297 void gra_diaredrawitem(CElectricDialog *cedia)
7298 {
7299 	RECTAREA ra;
7300 	INTBIG i, itemtype, x, y;
7301 	HICON map;
7302 	CDC *dc;
7303 	TDIALOG *dia;
7304 
7305 	/* find the dialog that needs to be redrawn */
7306 	for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
7307 		if (dia->window == cedia) break;
7308 	if (dia == NOTDIALOG) return;
7309 
7310 	/* if it has a custom redraw routine, call it */
7311 	if (dia->redrawroutine != 0)
7312 	{
7313 		DiaItemRect(dia, dia->redrawitem, &ra);
7314 		(*dia->redrawroutine)(&ra, dia);
7315 	}
7316 
7317 	/* redraw any special items */
7318 	for(i=0; i<dia->itemdesc->items; i++)
7319 	{
7320 		itemtype = dia->itemdesc->list[i].type;
7321 		if ((itemtype&ITEMTYPE) == DIVIDELINE)
7322 		{
7323 			DiaDrawRect(dia, i+1, &dia->itemdesc->list[i].r, 0, 0, 0);
7324 		}
7325 		if ((itemtype&ITEMTYPE) == ICON)
7326 		{
7327 			if (dia->itemdesc->list[i].data != 0)
7328 			{
7329 				x = dia->itemdesc->list[i].r.left;
7330 				y = dia->itemdesc->list[i].r.top;
7331 				map = gra_makeicon(dia->itemdesc->list[i].data);
7332 				dc = dia->window->GetDC();
7333 				dc->DrawIcon(x, y, map);
7334 				dia->window->ReleaseDC(dc);
7335 				DestroyIcon(map);
7336 			}
7337 		}
7338 	}
7339 }
7340 
7341 /*
7342  * Routine called when a scroll item is scrolled vertically
7343  */
gra_itemvscrolled(void * vdia,int nID)7344 void gra_itemvscrolled(void *vdia, int nID)
7345 {
7346 	int value, i, item, itemtype;
7347 	CListBoxEx *list;
7348 	TDIALOG *dia;
7349 
7350 	dia = (TDIALOG *)vdia;
7351 	item = nID - ID_DIALOGITEM_0;
7352 	itemtype = dia->itemdesc->list[item].type & ITEMTYPE;
7353 	if (itemtype != SCROLL && itemtype != SCROLLMULTI) return;
7354 
7355 	list = (CListBoxEx *)dia->window->GetDlgItem(nID);
7356 	value = list->GetTopIndex();
7357 	for(i=0; i<dia->numlocks; i++)
7358 	{
7359 		if (dia->lock1[i] == item)
7360 		{
7361 			list = (CListBoxEx *)dia->window->GetDlgItem(dia->lock2[i]+ID_DIALOGITEM_0);
7362 			list->SetTopIndex(value);
7363 			if (dia->lock3[i] >= 0)
7364 			{
7365 				list = (CListBoxEx *)dia->window->GetDlgItem(dia->lock3[i]+ID_DIALOGITEM_0);
7366 				list->SetTopIndex(value);
7367 			}
7368 			return;
7369 		}
7370 		if (dia->lock2[i] == item)
7371 		{
7372 			list = (CListBoxEx *)dia->window->GetDlgItem(dia->lock1[i]+ID_DIALOGITEM_0);
7373 			list->SetTopIndex(value);
7374 			if (dia->lock3[i] >= 0)
7375 			{
7376 				list = (CListBoxEx *)dia->window->GetDlgItem(dia->lock3[i]+ID_DIALOGITEM_0);
7377 				list->SetTopIndex(value);
7378 			}
7379 			return;
7380 		}
7381 		if (dia->lock3[i] == item)
7382 		{
7383 			list = (CListBoxEx *)dia->window->GetDlgItem(dia->lock1[i]+ID_DIALOGITEM_0);
7384 			list->SetTopIndex(value);
7385 			list = (CListBoxEx *)dia->window->GetDlgItem(dia->lock2[i]+ID_DIALOGITEM_0);
7386 			list->SetTopIndex(value);
7387 			return;
7388 		}
7389 	}
7390 }
7391 
7392 /*
7393  * Routine called when an item is clicked
7394  */
gra_itemclicked(CElectricDialog * diawin,int nID)7395 void gra_itemclicked(CElectricDialog *diawin, int nID)
7396 {
7397 	int itemtype, line, count, item;
7398 	CListBoxEx *list;
7399 	CComboBox *cb;
7400 	int selintlist[MAXSCROLLMULTISELECT];
7401 	TDIALOG *dia;
7402 
7403 	for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
7404 		if (dia->window == diawin) break;
7405 	if (dia == NOTDIALOG) return;
7406 
7407 	if (nID == 1)
7408 	{
7409 		item = dia->defaultbutton;
7410 		if (dia->modelessitemhit != 0) (*dia->modelessitemhit)(dia, item); else
7411 			dia->dialoghit = item;
7412 		gra_logwriteaction(DIAITEMCLICK, dia->windindex, item, 0, 0);
7413 		return;
7414 	}
7415 	if (nID == 2)
7416 	{
7417 		item = 2;
7418 		if (dia->modelessitemhit != 0) (*dia->modelessitemhit)(dia, item); else
7419 			dia->dialoghit = item;
7420 		gra_logwriteaction(DIAITEMCLICK, dia->windindex, item, 0, 0);
7421 		return;
7422 	}
7423 
7424 	/* handle scroll areas */
7425 	itemtype = dia->itemdesc->list[nID-ID_DIALOGITEM_0].type;
7426 	if ((itemtype&ITEMTYPE) == SCROLL || (itemtype&ITEMTYPE) == SCROLLMULTI)
7427 	{
7428 		list = (CListBoxEx *)dia->window->GetDlgItem(nID);
7429 
7430 		/* log the selection */
7431 		if ((itemtype&ITEMTYPE) == SCROLLMULTI)
7432 		{
7433 			count = list->GetSelItems(MAXSCROLLMULTISELECT-1, selintlist);
7434 			if (count != LB_ERR)
7435 				gra_logwriteaction(DIASCROLLSEL, dia->windindex, nID - ID_DIALOGITEM_0 + 1, count, selintlist);
7436 		} else
7437 		{
7438 			line = list->GetCurSel();
7439 			selintlist[0] = line;
7440 			if (line != LB_ERR)
7441 				gra_logwriteaction(DIASCROLLSEL, dia->windindex, nID - ID_DIALOGITEM_0 + 1, 1, selintlist);
7442 		}
7443 
7444 		/* if no mouse selection allowed, deselect the list (leaves an outline, but oh well) */
7445 		if ((dia->itemdesc->list[nID-ID_DIALOGITEM_0].data&SCSELMOUSE) == 0)
7446 		{
7447 			if (list == 0) return;
7448 			list->SetCurSel(-1);
7449 			return;
7450 		}
7451 
7452 		/* ignore clicks in scroll areas that do not want hits reported */
7453 		if ((dia->itemdesc->list[nID-ID_DIALOGITEM_0].data&SCREPORT) == 0)
7454 			return;
7455 	}
7456 	if ((itemtype&ITEMTYPE) == POPUP)
7457 	{
7458 		cb = (CComboBox *)dia->window->GetDlgItem(nID);
7459 		gra_logwriteaction(DIAPOPUPSEL, dia->windindex, nID - ID_DIALOGITEM_0 + 1, cb->GetCurSel(), 0);
7460 	}
7461 
7462 	item = nID - ID_DIALOGITEM_0 + 1;
7463 	gra_logwriteaction(DIAITEMCLICK, dia->windindex, item, 0, 0);
7464 	if (dia->modelessitemhit != 0) (*dia->modelessitemhit)(dia, item); else
7465 		dia->dialoghit = item;
7466 }
7467 
7468 /*
7469  * Routine called when an item is double-clicked
7470  */
gra_itemdoubleclicked(CElectricDialog * diawin,int nID)7471 void gra_itemdoubleclicked(CElectricDialog *diawin, int nID)
7472 {
7473 	INTBIG itemtype, item;
7474 	BOOLEAN allowdouble;
7475 	TDIALOG *dia;
7476 
7477 	for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
7478 		if (dia->window == diawin) break;
7479 	if (dia == NOTDIALOG) return;
7480 
7481 	if (dia->window == 0) return;
7482 	itemtype = dia->itemdesc->list[nID].type;
7483 
7484 	allowdouble = FALSE;
7485 	if ((itemtype&ITEMTYPE) == SCROLL || (itemtype&ITEMTYPE) == SCROLLMULTI)
7486 	{
7487 		if ((dia->itemdesc->list[nID].data&SCDOUBLEQUIT) != 0) allowdouble = TRUE;
7488 	}
7489 	if ((itemtype&ITEMTYPE) == USERDRAWN)
7490 	{
7491 		if (dia->useritemdoubleclick != 0) allowdouble = TRUE;
7492 	}
7493 
7494 	if (allowdouble)
7495 	{
7496 		item = dia->defaultbutton;
7497 		if (dia->modelessitemhit != 0) (*dia->modelessitemhit)(dia, item); else
7498 			dia->dialoghit = item;
7499 		gra_logwriteaction(DIAITEMCLICK, dia->windindex, item, 0, 0);
7500 	}
7501 }
7502 
7503 /*
7504  * Called when a key is typed to a list box.  Returns nonzero to accept the
7505  * keystroke, zero to ignore typed keys in the list box.
7506  */
gra_dodialoglistkey(CElectricDialog * diawin,UINT nKey,CListBox * pListBox,UINT nIndex)7507 int gra_dodialoglistkey(CElectricDialog *diawin, UINT nKey, CListBox* pListBox, UINT nIndex)
7508 {
7509 	CListBoxEx *list;
7510 	INTBIG i, itemtype, line, count;
7511 	int selintlist[MAXSCROLLMULTISELECT];
7512 	TDIALOG *dia;
7513 
7514 	for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
7515 		if (dia->window == diawin) break;
7516 	if (dia == NOTDIALOG) return(0);
7517 
7518 	/* always allow arrow keys */
7519 	if (nKey == VK_DOWN || nKey == VK_UP) return(1);
7520 
7521 	for(i=0; i<dia->itemdesc->items; i++)
7522 	{
7523 		itemtype = dia->itemdesc->list[i].type;
7524 		if ((itemtype&ITEMTYPE) != SCROLL && (itemtype&ITEMTYPE) != SCROLLMULTI) continue;
7525 		list = (CListBoxEx *)dia->window->GetDlgItem(i+ID_DIALOGITEM_0);
7526 		if (list != pListBox) continue;
7527 		if ((dia->itemdesc->list[i].data&SCSELKEY) == 0) return(0);
7528 		if ((itemtype&ITEMTYPE) == SCROLLMULTI)
7529 		{
7530 			count = list->GetSelItems(MAXSCROLLMULTISELECT-1, selintlist);
7531 			if (count != LB_ERR)
7532 				gra_logwriteaction(DIASCROLLSEL, dia->windindex, i + 1, (int)count, selintlist);
7533 		} else
7534 		{
7535 			line = list->GetCurSel();
7536 			selintlist[0] = line;
7537 			if (line != LB_ERR)
7538 				gra_logwriteaction(DIASCROLLSEL, dia->windindex, i + 1, 1, selintlist);
7539 		}
7540 		break;
7541 	}
7542 
7543 	return(1);
7544 }
7545 
7546 /*
7547  * Routine called when a character is typed
7548  */
gra_dodialogtextchange(CElectricDialog * diawin,int nID)7549 void gra_dodialogtextchange(CElectricDialog *diawin, int nID)
7550 {
7551 	INTBIG item;
7552 	TDIALOG *dia;
7553 
7554 	for(dia = gra_firstactivedialog; dia != NOTDIALOG; dia = dia->nexttdialog)
7555 		if (dia->window == diawin) break;
7556 	if (dia == NOTDIALOG) return;
7557 
7558 	/* get current contents of the edit field */
7559 	item = nID + 1;
7560 	if (dia->modelessitemhit != 0) (*dia->modelessitemhit)(dia, item); else
7561 		dia->dialoghit = item;
7562 	gra_logwriteaction(DIAEDITTEXT, dia->windindex, item, 0, DiaGetText(dia, item));
7563 }
7564 
7565 /*
7566  * Routine to return a nonzero item number if point (x,y) is inside a user-drawn item
7567  */
gra_dodialogisinsideuserdrawn(TDIALOG * dia,int x,int y)7568 INTBIG gra_dodialogisinsideuserdrawn(TDIALOG *dia, int x, int y)
7569 {
7570 	INTBIG i, itemtype;
7571 
7572 	if (dia->window == 0) return(0);
7573 	i = gra_getdialogitem(dia, x, y);
7574 	itemtype = dia->itemdesc->list[i-1].type;
7575 	if ((itemtype&ITEMTYPE) != USERDRAWN &&
7576 		(itemtype&ITEMTYPE) != ICON) return(0);
7577 	return(i);
7578 }
7579 
7580 /*
7581  * Routine to return a nonzero item number if point (x,y) is inside a user-drawn item
7582  */
gra_getdialogitem(TDIALOG * dia,int x,int y)7583 INTBIG gra_getdialogitem(TDIALOG *dia, int x, int y)
7584 {
7585 	INTBIG i;
7586 
7587 	if (dia->window == 0) return(0);
7588 	for(i=0; i<dia->itemdesc->items; i++)
7589 	{
7590 		if (x < dia->itemdesc->list[i].r.left) continue;
7591 		if (x > dia->itemdesc->list[i].r.right) continue;
7592 		if (y < dia->itemdesc->list[i].r.top) continue;
7593 		if (y > dia->itemdesc->list[i].r.bottom) continue;
7594 		return(i+1);
7595 	}
7596 	return(0);
7597 }
7598 
7599 /*
7600  * Routine to make an icon from data
7601  */
gra_makeicon(INTBIG idata)7602 HICON gra_makeicon(INTBIG idata)
7603 {
7604 	UCHAR1 zero[128], *data;
7605 	int i;
7606 
7607 	data = (UCHAR1 *)idata;
7608 	for(i=0; i<128; i++)
7609 	{
7610 		zero[i] = 0;
7611 		data[i] = ~data[i];
7612 	}
7613 	HICON icon = CreateIcon(0, 32, 32, 1, 1, (UCHAR1 *)data, zero);
7614 	for(i=0; i<128; i++) data[i] = ~data[i];
7615 	return(icon);
7616 }
7617 
7618 /****************************** TCL SUPPORT ******************************/
7619 
7620 #if LANGTCL
gra_initializetcl(void)7621 INTBIG gra_initializetcl(void)
7622 {
7623 	INTBIG err;
7624 	CHAR *newArgv[2];
7625 	void *infstr;
7626 
7627 	/* set the program name/path */
7628 	newArgv[0] = x_("Electric");
7629 	newArgv[1] = NULL;
7630 	(void)Tcl_FindExecutable(newArgv[0]);
7631 
7632 	tcl_interp = Tcl_CreateInterp();
7633 	if (tcl_interp == 0) error(_("from Tcl_CreateInterp"));
7634 
7635 	/* tell Electric the TCL interpreter handle */
7636 	el_tclinterpreter(tcl_interp);
7637 
7638 	/* set the Tcl library directory - for internal Tcl use */
7639 	infstr = initinfstr();
7640 	addstringtoinfstr(infstr, el_libdir);
7641 	addstringtoinfstr(infstr, x_("tcl8.3"));
7642 	if (Tcl_SetVar(tcl_interp, x_("tcl_library"), returninfstr(infstr), TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
7643 	{
7644 		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
7645 		return(1);
7646 	}
7647 
7648 	/* Make command-line arguments available in the Tcl variables "argc" and "argv" */
7649 	Tcl_SetVar(tcl_interp, x_("argv"), x_(""), TCL_GLOBAL_ONLY);
7650 	Tcl_SetVar(tcl_interp, x_("argc"), x_("0"), TCL_GLOBAL_ONLY);
7651 	Tcl_SetVar(tcl_interp, x_("argv0"), x_("electric"), TCL_GLOBAL_ONLY);
7652 
7653 	/* Set the "tcl_interactive" variable */
7654 	Tcl_SetVar(tcl_interp, x_("tcl_interactive"), x_("1"), TCL_GLOBAL_ONLY);
7655 
7656 	/* initialize the interpreter */
7657 	err = Tcl_Init(tcl_interp);
7658 	if (err != TCL_OK) error(_("(from Tcl_Init) %s"), tcl_interp->result);
7659 
7660 	return(err);
7661 }
7662 #endif
7663 
7664 /****************************** PRINTING ******************************/
7665 
7666 /* call this routine to print the current window */
printewindow(void)7667 void printewindow(void)
7668 {
7669 	INTBIG pagewid, pagehei, pagehpixperinch, pagevpixperinch, ypos, i, l, style,
7670 		marginx, marginy, slx, shx, sly, shy, *curstate, xpos, boxsize, dashindent,
7671 		ulx, uhx, uly, uhy, width, height, centerx, centery, prod1, prod2, indent,
7672 		fontHeight, page, topmargin, leftmargin, savewid, savehei, resfactor,
7673 		saveslx, saveshx, savesly, saveshy, bigwid, bighei, wlx, whx, wly, why,
7674 		lastboxxpos, yposbox, topypos;
7675 	REGISTER CHAR *name, *header, *cptr;
7676 	REGISTER UCHAR1 **saverowstart, *savedata, *ptr;
7677 	WINDOWPART *win;
7678 	NODEPROTO *np;
7679 	WINDOWFRAME *wf;
7680 	REGISTER VARIABLE *var;
7681 	BOOLEAN savedirty, *done;
7682 	HCURSOR hCursorBusy, hCursorOld;
7683 	static DOCINFO di = {sizeof(DOCINFO), x_("Electric"), NULL};
7684 	RGBQUAD bmiColors[256];
7685 	BITMAPINFO *bmiInfo, *savebmiinfo;
7686 	CDC printDC, *pDCPrint, *saveoff;
7687 	HDC hPrnDC;
7688 	DEVMODE *dm;
7689 	CFont *fnt;
7690 	HBITMAP savebitmap;
7691 	REGISTER void *infstr;
7692 	CPen penBlack, penGrey;
7693 
7694 	/* get cell to plot */
7695 	win = el_curwindowpart;
7696 	if (win == NOWINDOWPART)
7697 	{
7698 		ttyputerr(_("No current window to print"));
7699 		return;
7700 	}
7701 	wf = win->frame;
7702 	np = win->curnodeproto;
7703 
7704 	/* get printing control bits */
7705 	curstate = io_getstatebits();
7706 
7707 	if (np == NONODEPROTO)
7708 	{
7709 		/* allow explorer window, but nothing else */
7710 		if ((win->state&WINDOWTYPE) != EXPLORERWINDOW)
7711 		{
7712 			ttyputerr(_("Cannot print this type of window"));
7713 			return;
7714 		}
7715 	}
7716 
7717 	/* create a print structure */
7718 	CPrintDialog printDlg(FALSE);
7719 
7720 	/* set requested orientation */
7721 	if (printDlg.GetDefaults() == 0) return;
7722 	dm = printDlg.GetDevMode();
7723 	if ((curstate[1]&PSAUTOROTATE) != 0 && np != NONODEPROTO &&
7724 		(np->cellview->viewstate&TEXTVIEW) == 0)
7725 	{
7726 		(void)io_getareatoprint(np, &wlx, &whx, &wly, &why, FALSE);
7727 		if (whx-wlx > why-wly) curstate[0] |= PSROTATE; else
7728 			curstate[0] &= ~PSROTATE;
7729 	}
7730 	if ((curstate[0]&PSROTATE) != 0) dm->dmOrientation = DMORIENT_LANDSCAPE; else
7731 		dm->dmOrientation = DMORIENT_PORTRAIT;
7732 	printDlg.m_pd.Flags &= ~PD_RETURNDEFAULT;
7733 
7734 	/* show the dialog */
7735 	if (printDlg.DoModal() != IDOK) return;
7736 
7737 	/* set cursor to busy */
7738 	hCursorBusy = LoadCursor(NULL, IDC_WAIT);
7739 	hCursorOld = SetCursor(hCursorBusy);
7740 
7741 	/* collect size information about the Printer DC */
7742 	hPrnDC = printDlg.m_pd.hDC;   /* GetPrinterDC(); */
7743 	if (hPrnDC == 0)
7744 	{
7745 		ttyputerr(_("Printing error: could not start print job"));
7746 		return;
7747 	}
7748 	pDCPrint = printDC.FromHandle(hPrnDC);
7749 	pagewid = pDCPrint->GetDeviceCaps(HORZRES);
7750 	pagehei = pDCPrint->GetDeviceCaps(VERTRES);
7751 	pagehpixperinch = pDCPrint->GetDeviceCaps(LOGPIXELSX);
7752 	pagevpixperinch = pDCPrint->GetDeviceCaps(LOGPIXELSY);
7753 
7754 	/* start printing */
7755 	pDCPrint->StartDoc(&di);
7756 
7757 	if (np == NONODEPROTO && (win->state&WINDOWTYPE) == EXPLORERWINDOW)
7758 	{
7759 		/* an explorer window: print text */
7760 		us_explorerreadoutinit(win);
7761 		pDCPrint->StartPage();
7762 
7763 		/* print the window */
7764 		fontHeight = MulDiv(10, GetDeviceCaps(hPrnDC, LOGPIXELSY), 72);
7765 		fnt = new CFont();
7766  		fnt->CreateFont(-fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET,
7767 			OUT_DEVICE_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
7768 			DEFAULT_PITCH | FF_DONTCARE, x_("Courier"));
7769 		pDCPrint->SelectObject(fnt);
7770 		pDCPrint->SetTextColor(RGB(0,0,0));
7771 		pDCPrint->SetBkColor(RGB(255,255,255));
7772 
7773 		var = getval((INTBIG)io_tool, VTOOL, VFRACT, x_("IO_postscript_margin"));
7774 		if (var == NOVARIABLE) i = muldiv(DEFAULTPSMARGIN, WHOLE, 75); else
7775 			i = var->addr;
7776 		topmargin = muldiv(i, pagevpixperinch, WHOLE);
7777 		leftmargin = muldiv(i, pagehpixperinch, WHOLE);
7778 
7779 		ypos = topmargin;
7780 		page = 1;
7781 		lastboxxpos = -1;
7782 		boxsize = fontHeight * 2 / 3;
7783 		dashindent = boxsize / 5;
7784 		penBlack.CreatePen(PS_SOLID | PS_COSMETIC, 1, RGB(0, 0, 0));
7785 		penGrey.CreatePen(PS_SOLID | PS_COSMETIC, 1, RGB(128, 128, 128));
7786 
7787 		pDCPrint->SelectObject(&penBlack);
7788 		for(;;)
7789 		{
7790 			name = us_explorerreadoutnext(win, &indent, &style, &done, 0);
7791 			if (name == 0) break;
7792 
7793 			/* insert page breaks */
7794 			if (ypos+fontHeight >= pagehei-topmargin)
7795 			{
7796 				ypos = topmargin;
7797 				pDCPrint->EndPage();
7798 				pDCPrint->StartPage();
7799 			}
7800 
7801 			/* print the header */
7802 			if (ypos == topmargin)
7803 			{
7804 				infstr = initinfstr();
7805 				formatinfstr(infstr, _("Cell Explorer    Page %ld"), page);
7806 				header = returninfstr(infstr);
7807 				pDCPrint->TextOut(leftmargin, 0, header, estrlen(header));
7808 				page++;
7809 				if (topmargin < fontHeight*2) ypos += fontHeight*2;
7810 			}
7811 			xpos = leftmargin + indent * fontHeight;
7812 
7813 			/* draw the box */
7814 			yposbox = ypos + (fontHeight-boxsize) / 2;
7815 			pDCPrint->MoveTo(xpos, yposbox);
7816 			pDCPrint->LineTo(xpos+boxsize, yposbox);
7817 			pDCPrint->LineTo(xpos+boxsize, yposbox+boxsize);
7818 			pDCPrint->LineTo(xpos, yposbox+boxsize);
7819 			pDCPrint->LineTo(xpos, yposbox);
7820 			if (style >= 2)
7821 			{
7822 				/* draw horizontal line in box */
7823 				pDCPrint->MoveTo(xpos+dashindent, yposbox+boxsize/2);
7824 				pDCPrint->LineTo(xpos+boxsize-dashindent, yposbox+boxsize/2);
7825 				if (style == 2)
7826 				{
7827 					/* draw vertical line in box */
7828 					pDCPrint->MoveTo(xpos+boxsize/2, yposbox+dashindent);
7829 					pDCPrint->LineTo(xpos+boxsize/2, yposbox+boxsize-dashindent);
7830 				}
7831 			}
7832 
7833 			/* draw the connecting lines */
7834 			if (indent > 0)
7835 			{
7836 				pDCPrint->SelectObject(&penGrey);
7837 				pDCPrint->MoveTo(xpos, yposbox+boxsize/2);
7838 				pDCPrint->LineTo(xpos-fontHeight+boxsize/2, yposbox+boxsize/2);
7839 				for(i=0; i<indent; i++)
7840 				{
7841 					if (done[i]) continue;
7842 					l = leftmargin+i*fontHeight+boxsize/2;
7843 					if (l == lastboxxpos)
7844 						topypos = yposbox+boxsize/2-fontHeight+boxsize/2; else
7845 							topypos = yposbox+boxsize/2-fontHeight;
7846 					pDCPrint->MoveTo(l, yposbox+boxsize/2);
7847 					pDCPrint->LineTo(l, topypos);
7848 				}
7849 				pDCPrint->SelectObject(&penBlack);
7850 			}
7851 			lastboxxpos = xpos + boxsize/2;
7852 
7853 			/* show the text */
7854 			pDCPrint->TextOut(xpos+boxsize+boxsize/2, ypos, name, estrlen(name));
7855 			ypos += fontHeight;
7856 		}
7857 		delete fnt;
7858 		pDCPrint->EndPage();
7859 	} else if (np != NONODEPROTO && (np->cellview->viewstate&TEXTVIEW) != 0)
7860 	{
7861 		/* a text window: print text */
7862 		pDCPrint->StartPage();
7863 
7864 		/* print the window */
7865 		fontHeight = MulDiv(10, GetDeviceCaps(hPrnDC, LOGPIXELSY), 72);
7866 		fnt = new CFont();
7867  		fnt->CreateFont(-fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET,
7868 			OUT_DEVICE_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
7869 			DEFAULT_PITCH | FF_DONTCARE, x_("Courier"));
7870 		pDCPrint->SelectObject(fnt);
7871 		pDCPrint->SetTextColor(RGB(0,0,0));
7872 		pDCPrint->SetBkColor(RGB(255,255,255));
7873 
7874 		var = getval((INTBIG)io_tool, VTOOL, VFRACT, x_("IO_postscript_margin"));
7875 		if (var == NOVARIABLE) i = muldiv(DEFAULTPSMARGIN, WHOLE, 75); else
7876 			i = var->addr;
7877 		topmargin = muldiv(i, pagevpixperinch, WHOLE);
7878 		leftmargin = muldiv(i, pagehpixperinch, WHOLE);
7879 
7880 		ypos = topmargin;
7881 		page = 1;
7882 		var = getvalkey((INTBIG)np, VNODEPROTO, VSTRING|VISARRAY, el_cell_message_key);
7883 		if (var != NOVARIABLE)
7884 		{
7885 			l = getlength(var);
7886 			for(i=0; i<l; i++)
7887 			{
7888 				cptr = ((CHAR **)var->addr)[i];
7889 				if (i == l-1 && *cptr == 0) continue;
7890 				if (ypos+fontHeight >= pagehei-topmargin)
7891 				{
7892 					ypos = topmargin;
7893 					pDCPrint->EndPage();
7894 					pDCPrint->StartPage();
7895 				}
7896 				if (ypos == topmargin)
7897 				{
7898 					/* print the header */
7899 					infstr = initinfstr();
7900 					formatinfstr(infstr, _("Library: %s   Cell: %s   Page %ld"), np->lib->libname,
7901 						describenodeproto(np), page);
7902 					header = returninfstr(infstr);
7903 					pDCPrint->TextOut(leftmargin, 0, header, estrlen(header));
7904 
7905 					if ((us_useroptions&NODATEORVERSION) == 0 && page == 1)
7906 					{
7907 						infstr = initinfstr();
7908 						if (np->creationdate != 0)
7909 							formatinfstr(infstr, _("Created: %s"), timetostring((time_t)np->creationdate));
7910 						if (np->revisiondate != 0)
7911 							formatinfstr(infstr, _("   Revised: %s"), timetostring((time_t)np->revisiondate));
7912 						header = returninfstr(infstr);
7913 						pDCPrint->TextOut(leftmargin, fontHeight, header, estrlen(header));
7914 					}
7915 					page++;
7916 					if (topmargin < fontHeight*2) ypos += fontHeight*2;
7917 				}
7918 				pDCPrint->TextOut(leftmargin, ypos, cptr, estrlen(cptr));
7919 				ypos += fontHeight;
7920 			}
7921 		}
7922 		delete fnt;
7923  		pDCPrint->EndPage();
7924 	} else if (np != NONODEPROTO)
7925 	{
7926 		/* a graphics window: get the print resolution scale factor */
7927 		var = getval((INTBIG)io_tool, VTOOL, VINTEGER, x_("IO_print_resolution_scale"));
7928 		if (var == NOVARIABLE) resfactor = 1; else
7929 			resfactor = var->addr;
7930 
7931 		/* make a bigger offscreen window */
7932 		bigwid = wf->swid * resfactor;
7933 		bighei = wf->shei * resfactor;
7934 		if (gra_getbiggeroffscreenbuffer(wf, bigwid, bighei)) return;
7935 		saveoff = (CDC *)wf->hDCOff;     wf->hDCOff = (void *)gra_biggeroffhdc;
7936 		savebitmap = wf->hBitmap;        wf->hBitmap = gra_biggeroffbitmap;
7937 		savebmiinfo = wf->bminfo;        wf->bminfo = gra_biggeroffbitmapinfo;
7938 		savedata = wf->data;             wf->data = gra_biggeroffdatabuffer;
7939 		saverowstart = wf->rowstart;     wf->rowstart = gra_biggeroffrowstart;
7940 		savewid = wf->swid;              wf->swid = bigwid;
7941 		savehei = wf->shei;              wf->shei = bighei;
7942 		savedirty = wf->offscreendirty;  wf->offscreendirty = FALSE;
7943 		wf->revy = wf->shei - 1;
7944 		if (wf->hPalette != 0)
7945 		{
7946 			(void)((CDC *)wf->hDCOff)->SelectPalette((CPalette *)wf->hPalette, TRUE);
7947 			(void)((CDC *)wf->hDCOff)->RealizePalette();
7948 		}
7949 		slx = win->uselx;   shx = win->usehx;
7950 		sly = win->usely;   shy = win->usehy;
7951 		win->uselx *= resfactor;
7952 		win->usehx *= resfactor;
7953 		win->usely *= resfactor;
7954 		win->usehy *= resfactor;
7955 		saveslx = win->screenlx;
7956 		saveshx = win->screenhx;
7957 		savesly = win->screenly;
7958 		saveshy = win->screenhy;
7959 		if ((win->state&WINDOWTYPE) == WAVEFORMWINDOW)
7960 		{
7961 			wlx = win->screenlx;
7962 			whx = win->screenhx;
7963 			wly = win->screenly;
7964 			why = win->screenhy;
7965 		} else
7966 		{
7967 			(void)io_getareatoprint(np, &wlx, &whx, &wly, &why, FALSE);
7968 			us_squarescreen(win, NOWINDOWPART, FALSE, &wlx, &whx, &wly, &why, 1);
7969 		}
7970 		win->screenlx = slx = wlx;
7971 		win->screenhx = shx = whx;
7972 		win->screenly = sly = wly;
7973 		win->screenhy = shy = why;
7974 		computewindowscale(win);
7975 
7976 		/* redraw the window in the larger buffer */
7977 		gra_noflush = TRUE;
7978 		if ((win->state&WINDOWTYPE) == WAVEFORMWINDOW)
7979 		{
7980 			(*win->redisphandler)(win);
7981 		} else
7982 		{
7983 			us_redisplaynow(win, TRUE);
7984 			us_endchanges(win);
7985 		}
7986 		gra_noflush = FALSE;
7987 		(void)us_makescreen(&slx, &sly, &shx, &shy, win);
7988 
7989 		/* restore the normal state of the window */
7990 		wf->hDCOff = saveoff;
7991 		wf->hBitmap = savebitmap;
7992 		wf->bminfo = savebmiinfo;
7993 		wf->data = savedata;
7994 		wf->rowstart = saverowstart;
7995 		wf->swid = savewid;
7996 		wf->shei = savehei;
7997 		wf->revy = wf->shei - 1;
7998 		wf->offscreendirty = savedirty;
7999 		if (wf->hPalette != 0)
8000 		{
8001 			(void)((CDC *)wf->hDCOff)->SelectPalette((CPalette *)wf->hPalette, TRUE);
8002 			(void)((CDC *)wf->hDCOff)->RealizePalette();
8003 		}
8004 		win->uselx /= resfactor;
8005 		win->usehx /= resfactor;
8006 		win->usely /= resfactor;
8007 		win->usehy /= resfactor;
8008 		win->screenlx = saveslx;
8009 		win->screenhx = saveshx;
8010 		win->screenly = savesly;
8011 		win->screenhy = saveshy;
8012 		computewindowscale(win);
8013 
8014 		marginx = pagehpixperinch / 2;
8015 		marginy = pagevpixperinch / 2;
8016 
8017 		/* make the plot have square pixels */
8018 		ulx = marginx;
8019 		uly = marginy;
8020 		uhx = pagewid-marginx;
8021 		uhy = pagehei-marginy;
8022 		prod1 = (shx - slx) * (uhy - uly);
8023 		prod2 = (shy - sly) * (uhx - ulx);
8024 		if (prod1 != prod2)
8025 		{
8026 			/* adjust the scale */
8027 			if (prod1 > prod2)
8028 			{
8029 				/* make it fill the width of the screen */
8030 				height = muldiv(uhx - ulx, shy - sly, shx - slx);
8031 				centery = (uly + uhy) / 2;
8032 				uly = centery - height/2;
8033 				uhy = uly + height;
8034 			} else
8035 			{
8036 				/* make it fill the height of the screen */
8037 				width = muldiv(uhy - uly, shx - slx, shy - sly);
8038 				centerx = (ulx + uhx) / 2;
8039 				ulx = centerx - width/2;
8040 				uhx = ulx + width;
8041 			}
8042 		}
8043 
8044 		/* adjust the background color to white */
8045 		i = GetDIBColorTable(gra_biggeroffhdc->m_hDC, 0, 256, bmiColors);
8046 		bmiColors[0].rgbRed = 0xFF;
8047 		bmiColors[0].rgbGreen = 0xFF;
8048 		bmiColors[0].rgbBlue = 0xFF;
8049 		bmiColors[0].rgbReserved = 0;
8050 
8051 		bmiInfo = (BITMAPINFO *)emalloc(sizeof(BITMAPINFOHEADER) + sizeof(bmiColors), db_cluster);
8052 		if (bmiInfo == 0) return;
8053 		ptr = (UCHAR1 *)bmiInfo;
8054 		memcpy(ptr, gra_biggeroffbitmapinfo, sizeof(BITMAPINFOHEADER));
8055 		ptr += sizeof(BITMAPINFOHEADER);
8056 		memcpy(ptr, &bmiColors, sizeof(bmiColors));
8057 
8058 		pDCPrint->StartPage();
8059 
8060 		/* print the window */
8061 		StretchDIBits(hPrnDC, ulx, uly, uhx-ulx, uhy-uly,
8062 			slx, sly, shx-slx+1, shy-sly+1,
8063 			gra_biggeroffdatabuffer, bmiInfo, DIB_RGB_COLORS, SRCCOPY);
8064 		efree((CHAR *)bmiInfo);
8065 
8066 		pDCPrint->EndPage();
8067 	}
8068 
8069 	/* finish printing */
8070 	pDCPrint->EndDoc();
8071 	pDCPrint->Detach();
8072 	DeleteDC(hPrnDC);
8073 
8074 	/* restore original cursor */
8075 	SetCursor(hCursorOld);
8076 }
8077