1 // Shell interface for e93 (currently using John Ousterhout's Tcl)
2 // Copyright (C) 2000 Core Technologies.
3 
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16 
17 #include	"includes.h"
18 
19 #define MAX_SCROLL_SPEED	2
20 
21 static Tcl_Trace
22 	tclTrace;					// the TCL trace object we created
23 
InsertBufferTaskData(EDITOR_BUFFER * buffer,UINT8 * data,UINT32 numBytes)24 void InsertBufferTaskData(EDITOR_BUFFER *buffer,UINT8 *data,UINT32 numBytes)
25 // Insert numBytes of data into buffer, update any
26 // views of buffer that may need it
27 // if there is a problem, ignore it!
28 {
29 	UINT32
30 		topLine,
31 		numLines,
32 		numPixels;
33 	INT32
34 		leftPixel;
35 	EDITOR_VIEW
36 		*currentView;
37 	UINT32
38 		tempPosition,
39 		startPosition,
40 		endPosition;
41 	UINT32
42 		startLine;
43 	CHUNK_HEADER
44 		*startLineChunk;
45 	UINT32
46 		startLineOffset;
47 
48 // run through the views, see which ones will need to be homed after the insert:
49 // The ones to home, are those that had the cursor in view at the time of the insert
50 
51 	GetSelectionEndPositions(buffer->selectionUniverse,&startPosition,&endPosition);
52 	currentView=buffer->firstView;					// get first view of this buffer (if any)
53 	while(currentView)								// walk through all views, update each one as needed
54 	{
55 		GetEditorViewTextInfo(currentView,&topLine,&numLines,&leftPixel,&numPixels);
56 		PositionToLinePosition(buffer->textUniverse,startPosition,&startLine,&tempPosition,&startLineChunk,&startLineOffset);
57 		currentView->wantHome=(startLine>=topLine&&startLine<topLine+numLines);	// see if the cursor is on the view
58 		currentView=currentView->nextBufferView;	// locate next view of this buffer
59 	}
60 
61 	EditorAuxInsert(buffer,data,numBytes);			// drop the text into buffer
62 
63 	GetSelectionEndPositions(buffer->selectionUniverse,&startPosition,&endPosition);	// get selection position after the insertion
64 // now home all of those views that need it
65 	currentView=buffer->firstView;					// get first view of this buffer (if any)
66 	while(currentView)								// walk through all views, update each one as needed
67 	{
68 		if(currentView->wantHome)
69 		{
70 			EditorHomeViewToRange(currentView,startPosition,endPosition,false,HT_NONE,HT_LENIENT);
71 		}
72 		currentView=currentView->nextBufferView;	// locate next view of this buffer
73 	}
74 }
75 
Safe_Tcl_Eval(Tcl_Interp * interp,char * cmd,int * tclResult)76 static bool Safe_Tcl_Eval(Tcl_Interp *interp, char *cmd, int *tclResult)
77 // Some Tcl commands can delete the text of the script which
78 // is running. If this happens, it is possible for things to get confused.
79 // For example: a menu command could try to delete itself, which would cause
80 // the script of the command to be deleted mid-execution.
81 // To solve this problem, the script is copied into a safe temporary place
82 // then executed, and finally deleted
83 // If there is a problem copying the script, this will return false, and not
84 // execute it. If this returns true, the result from the Tcl_Eval command will
85 // be placed in tclResult
86 {
87 	char
88 		*scriptCopy;
89 
90 	if((scriptCopy=(char *)MNewPtr(strlen(cmd)+1)))
91 	{
92 		strcpy(scriptCopy,cmd);								// copy the command
93 		*tclResult=Tcl_Eval(tclInterpreter,scriptCopy);		// execute it
94 		MDisposePtr(scriptCopy);							// get rid of the copy
95 		return(true);
96 	}
97 	return(false);
98 }
99 
HandleBoundKeyEvent(UINT32 keyCode,UINT32 modifierValue)100 bool HandleBoundKeyEvent(UINT32 keyCode,UINT32 modifierValue)
101 // See if there is a bound key that matches the given code, and modifiers
102 // if there is, perform the script given by the bound key, and return true
103 // if there is not, return false
104 {
105 	EDITOR_KEY_BINDING
106 		*entry;
107 	int
108 		tclResult;
109 	const char
110 		*stringResult;
111 
112 	if((entry=LocateKeyBindingMatch(keyCode,modifierValue)))
113 	{
114 		ClearUserAbort();
115 		if(Safe_Tcl_Eval(tclInterpreter,GetKeyBindingText(entry),&tclResult))
116 		{
117 			stringResult=Tcl_GetStringResult(tclInterpreter);
118 			if((tclResult!=TCL_OK)&&stringResult[0])
119 			{
120 				ReportMessage("Bound key execution error:\n%.256s\n",stringResult);
121 			}
122 		}
123 		else
124 		{
125 			ReportMessage("Bound key execution error:\nOut of memory\n");
126 		}
127 		return(true);
128 	}
129 	return(false);
130 }
131 
HandleMenuEvent(EDITOR_MENU * menu)132 void HandleMenuEvent(EDITOR_MENU *menu)
133 // When a menu event arrives, this is called (by the menu manager of the GUI) to handle the menu
134 {
135 	int
136 		tclResult;
137 	const char
138 		*stringResult;
139 
140 	ClearUserAbort();
141 	if(Safe_Tcl_Eval(tclInterpreter,GetEditorMenuDataText(menu),&tclResult))
142 	{
143 		stringResult=Tcl_GetStringResult(tclInterpreter);
144 		if((tclResult!=TCL_OK)&&stringResult[0])
145 		{
146 			ReportMessage("Menu execution error:\n%.256s\n",stringResult);
147 		}
148 	}
149 	else
150 	{
151 		ReportMessage("Menu execution error:\nOut of memory\n");
152 	}
153 }
154 
HandleNonStandardControlEvent(const char * string)155 void HandleNonStandardControlEvent(const char *string)
156 // When the GUI needs to implement some GUI specific control, it can call this to execute shell
157 // commands for that control
158 {
159 	int
160 		tclResult;
161 	const char
162 		*stringResult;
163 
164 	ClearUserAbort();
165 	tclResult=Tcl_Eval(tclInterpreter,string);
166 	stringResult=Tcl_GetStringResult(tclInterpreter);
167 	if((tclResult!=TCL_OK)&&stringResult[0])
168 	{
169 		ReportMessage("Execution error:\n%.256s\n",stringResult);
170 	}
171 }
172 
HandleViewEvent(VIEW_EVENT * event)173 void HandleViewEvent(VIEW_EVENT *event)
174 // High level view events are handled here.
175 {
176 	UINT32
177 		topLine,
178 		numLines,
179 		numPixels;
180 	INT32
181 		leftPixel;
182 	UINT8
183 		key;
184 	static UINT32
185 		scrollTimeout;
186 	static UINT32
187 		scrollSpeed;
188 	UINT16
189 		trackMode;							// tells editor how to place cursor when cursor position keys arrive
190 	EDITOR_KEY
191 		*keyEventData;
192 	VIEW_POS_EVENT_DATA
193 		*posEventData;
194 	VIEW_CLICK_EVENT_DATA
195 		*clickEventData;
196 	bool
197 		result;
198 	bool
199 		didSomething;
200 
201 	keyEventData=NULL;						// keep compiler quiet
202 	result=true;							// success until further notice
203 	switch(event->eventType)
204 	{
205 		case VET_KEYDOWN:
206 		case VET_KEYREPEAT:
207 			keyEventData=(EDITOR_KEY *)event->eventData;
208 			if(keyEventData->isVirtual)
209 			{
210 				switch(keyEventData->keyCode)
211 				{
212 					case VVK_SCROLLUP:
213 						if(event->eventType==VET_KEYDOWN)
214 						{
215 							scrollSpeed=1;
216 							scrollTimeout=0;
217 						}
218 						EditorVerticalScroll(event->view,-((INT32)scrollSpeed));
219 						if(scrollTimeout++>3)
220 						{
221 							if(scrollSpeed<MAX_SCROLL_SPEED)
222 							{
223 								scrollSpeed++;
224 							}
225 							scrollTimeout=0;
226 						}
227 						break;
228 					case VVK_SCROLLDOWN:
229 						if(event->eventType==VET_KEYDOWN)
230 						{
231 							scrollSpeed=1;
232 							scrollTimeout=0;
233 						}
234 						EditorVerticalScroll(event->view,scrollSpeed);
235 						if(scrollTimeout++>3)
236 						{
237 							if(scrollSpeed<MAX_SCROLL_SPEED)
238 							{
239 								scrollSpeed++;
240 							}
241 							scrollTimeout=0;
242 						}
243 						break;
244 					case VVK_DOCUMENTPAGEUP:
245 						EditorVerticalScrollByPages(event->view,-1);
246 						break;
247 					case VVK_DOCUMENTPAGEDOWN:
248 						EditorVerticalScrollByPages(event->view,1);
249 						break;
250 					case VVK_SCROLLLEFT:
251 						EditorHorizontalScroll(event->view,-HORIZONTAL_SCROLL_THRESHOLD);
252 						break;
253 					case VVK_SCROLLRIGHT:
254 						EditorHorizontalScroll(event->view,HORIZONTAL_SCROLL_THRESHOLD);
255 						break;
256 					case VVK_DOCUMENTPAGELEFT:
257 						EditorHorizontalScrollByPages(event->view,-1);
258 						break;
259 					case VVK_DOCUMENTPAGERIGHT:
260 						EditorHorizontalScrollByPages(event->view,1);
261 						break;
262 					case VVK_RETURN:
263 						EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
264 						if((result=EditorAutoIndent(event->view->parentBuffer)))
265 						{
266 							EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
267 						}
268 						break;
269 					case VVK_TAB:
270 						key='\t';				// insert a tab
271 						EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
272 						if((result=EditorInsert(event->view->parentBuffer,&key,1)))
273 						{
274 							EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
275 						}
276 						break;
277 					case VVK_LEFTARROW:
278 						if(!(keyEventData->modifiers&EEM_CTL))
279 						{
280 							if(!(keyEventData->modifiers&EEM_MOD0))
281 							{
282 								if(!(keyEventData->modifiers&EEM_SHIFT))
283 								{
284 									if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_CHAR)))
285 									{
286 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
287 									}
288 								}
289 								else
290 								{
291 									if((result=EditorMoveSelection(event->view,RPM_mBACKWARD|RPM_CHAR)))
292 									{
293 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
294 									}
295 								}
296 							}
297 							else
298 							{
299 								if(!(keyEventData->modifiers&EEM_SHIFT))
300 								{
301 									if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_LINEEDGE)))
302 									{
303 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
304 									}
305 								}
306 								else
307 								{
308 									if((result=EditorMoveSelection(event->view,RPM_mBACKWARD|RPM_LINEEDGE)))
309 									{
310 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
311 									}
312 								}
313 							}
314 						}
315 						else
316 						{
317 							if(!(keyEventData->modifiers&EEM_SHIFT))
318 							{
319 								if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_WORD)))
320 								{
321 									EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
322 								}
323 							}
324 							else
325 							{
326 								if((result=EditorMoveSelection(event->view,RPM_mBACKWARD|RPM_WORD)))
327 								{
328 									EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
329 								}
330 							}
331 						}
332 						break;
333 					case VVK_RIGHTARROW:
334 						if(!(keyEventData->modifiers&EEM_CTL))
335 						{
336 							if(!(keyEventData->modifiers&EEM_MOD0))
337 							{
338 								if(!(keyEventData->modifiers&EEM_SHIFT))
339 								{
340 									if((result=EditorMoveCursor(event->view,RPM_CHAR)))
341 									{
342 										EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
343 									}
344 								}
345 								else
346 								{
347 									if((result=EditorMoveSelection(event->view,RPM_CHAR)))
348 									{
349 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
350 									}
351 								}
352 							}
353 							else
354 							{
355 								if(!(keyEventData->modifiers&EEM_SHIFT))
356 								{
357 									if((result=EditorMoveCursor(event->view,RPM_LINEEDGE)))
358 									{
359 										EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
360 									}
361 								}
362 								else
363 								{
364 									if((result=EditorMoveSelection(event->view,RPM_LINEEDGE)))
365 									{
366 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
367 									}
368 								}
369 							}
370 						}
371 						else
372 						{
373 							if(!(keyEventData->modifiers&EEM_SHIFT))
374 							{
375 								if((result=EditorMoveCursor(event->view,RPM_WORD)))
376 								{
377 									EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
378 								}
379 							}
380 							else
381 							{
382 								if((result=EditorMoveSelection(event->view,RPM_WORD)))
383 								{
384 									EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
385 								}
386 							}
387 						}
388 						break;
389 					case VVK_UPARROW:
390 						if(!(keyEventData->modifiers&EEM_CTL))
391 						{
392 							if(!(keyEventData->modifiers&EEM_MOD0))
393 							{
394 								if(!(keyEventData->modifiers&EEM_SHIFT))
395 								{
396 									if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_LINE)))
397 									{
398 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
399 									}
400 								}
401 								else
402 								{
403 									if((result=EditorMoveSelection(event->view,RPM_mBACKWARD|RPM_LINE)))
404 									{
405 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
406 									}
407 								}
408 							}
409 							else
410 							{
411 								if(!(keyEventData->modifiers&EEM_SHIFT))
412 								{
413 									if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_PAGE)))
414 									{
415 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
416 									}
417 								}
418 								else
419 								{
420 									if((result=EditorMoveSelection(event->view,RPM_mBACKWARD|RPM_PAGE)))
421 									{
422 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_LENIENT,HT_LENIENT);
423 									}
424 								}
425 							}
426 						}
427 						else
428 						{
429 							EditorVerticalScroll(event->view,-1);
430 						}
431 						break;
432 					case VVK_DOWNARROW:
433 						if(!(keyEventData->modifiers&EEM_CTL))
434 						{
435 							if(!(keyEventData->modifiers&EEM_MOD0))
436 							{
437 								if(!(keyEventData->modifiers&EEM_SHIFT))
438 								{
439 									if((result=EditorMoveCursor(event->view,RPM_LINE)))
440 									{
441 										EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
442 									}
443 								}
444 								else
445 								{
446 									if((result=EditorMoveSelection(event->view,RPM_LINE)))
447 									{
448 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_SEMISTRICT,HT_SEMISTRICT);
449 									}
450 								}
451 							}
452 							else
453 							{
454 								if(!(keyEventData->modifiers&EEM_SHIFT))
455 								{
456 									if((result=EditorMoveCursor(event->view,RPM_PAGE)))
457 									{
458 										EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
459 									}
460 								}
461 								else
462 								{
463 									if((result=EditorMoveSelection(event->view,RPM_PAGE)))
464 									{
465 										EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_LENIENT,HT_LENIENT);
466 									}
467 								}
468 							}
469 						}
470 						else
471 						{
472 							EditorVerticalScroll(event->view,1);
473 						}
474 						break;
475 					case VVK_BACKSPACE:
476 						if(!(keyEventData->modifiers&EEM_CTL)&&!(keyEventData->modifiers&EEM_MOD1))
477 						{
478 							if(!(keyEventData->modifiers&EEM_MOD0))
479 							{
480 								if(!(keyEventData->modifiers&EEM_SHIFT))
481 								{
482 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
483 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_CHAR)))
484 									{
485 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
486 									}
487 								}
488 								else
489 								{
490 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
491 									if((result=EditorDelete(event->view,RPM_CHAR)))
492 									{
493 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
494 									}
495 								}
496 							}
497 							else
498 							{
499 								if(!(keyEventData->modifiers&EEM_SHIFT))
500 								{
501 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
502 									if((result=EditorDelete(event->view,RPM_LINEEDGE)))
503 									{
504 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
505 									}
506 								}
507 								else
508 								{
509 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
510 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_LINEEDGE)))
511 									{
512 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
513 									}
514 								}
515 							}
516 						}
517 						else
518 						{
519 							if(!(keyEventData->modifiers&EEM_MOD0))
520 							{
521 								if(!(keyEventData->modifiers&EEM_SHIFT))
522 								{
523 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
524 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_WORD)))
525 									{
526 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
527 									}
528 								}
529 								else
530 								{
531 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
532 									if((result=EditorDelete(event->view,RPM_WORD)))
533 									{
534 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
535 									}
536 								}
537 							}
538 							else
539 							{
540 								if(!(keyEventData->modifiers&EEM_SHIFT))
541 								{
542 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
543 									if((result=EditorDelete(event->view,RPM_DOCEDGE)))
544 									{
545 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
546 									}
547 								}
548 								else
549 								{
550 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
551 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_DOCEDGE)))
552 									{
553 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
554 									}
555 								}
556 							}
557 						}
558 						break;
559 					case VVK_DELETE:
560 						if(!(keyEventData->modifiers&EEM_CTL)&&!(keyEventData->modifiers&EEM_MOD1))
561 						{
562 							if(!(keyEventData->modifiers&EEM_MOD0))
563 							{
564 								if(!(keyEventData->modifiers&EEM_SHIFT))
565 								{
566 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
567 									if((result=EditorDelete(event->view,RPM_CHAR)))
568 									{
569 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
570 									}
571 								}
572 								else
573 								{
574 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
575 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_CHAR)))
576 									{
577 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
578 									}
579 								}
580 							}
581 							else
582 							{
583 								if(!(keyEventData->modifiers&EEM_SHIFT))
584 								{
585 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
586 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_LINEEDGE)))
587 									{
588 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
589 									}
590 								}
591 								else
592 								{
593 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
594 									if((result=EditorDelete(event->view,RPM_LINEEDGE)))
595 									{
596 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
597 									}
598 								}
599 							}
600 						}
601 						else
602 						{
603 							if(!(keyEventData->modifiers&EEM_MOD0))
604 							{
605 								if(!(keyEventData->modifiers&EEM_SHIFT))
606 								{
607 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
608 									if((result=EditorDelete(event->view,RPM_WORD)))
609 									{
610 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
611 									}
612 								}
613 								else
614 								{
615 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
616 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_WORD)))
617 									{
618 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
619 									}
620 								}
621 							}
622 							else
623 							{
624 								if(!(keyEventData->modifiers&EEM_SHIFT))
625 								{
626 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
627 									if((result=EditorDelete(event->view,RPM_mBACKWARD|RPM_DOCEDGE)))
628 									{
629 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
630 									}
631 								}
632 								else
633 								{
634 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
635 									if((result=EditorDelete(event->view,RPM_DOCEDGE)))
636 									{
637 										EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
638 									}
639 								}
640 							}
641 						}
642 						break;
643 					case VVK_HOME:
644 						if(!(keyEventData->modifiers&EEM_MOD0))
645 						{
646 							if(!(keyEventData->modifiers&EEM_SHIFT))
647 							{
648 								if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_LINEEDGE)))
649 								{
650 									EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
651 								}
652 							}
653 							else
654 							{
655 								if((result=EditorExpandNormalSelection(event->view,RPM_mBACKWARD|RPM_LINEEDGE)))
656 								{
657 									EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
658 								}
659 							}
660 						}
661 						else
662 						{
663 							if(!(keyEventData->modifiers&EEM_SHIFT))
664 							{
665 								if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_DOCEDGE)))
666 								{
667 									EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
668 								}
669 							}
670 							else
671 							{
672 								if((result=EditorExpandNormalSelection(event->view,RPM_mBACKWARD|RPM_DOCEDGE)))
673 								{
674 									EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
675 								}
676 							}
677 						}
678 						break;
679 					case VVK_END:
680 						if(!(keyEventData->modifiers&EEM_MOD0))
681 						{
682 							if(!(keyEventData->modifiers&EEM_SHIFT))
683 							{
684 								if((result=EditorMoveCursor(event->view,RPM_LINEEDGE)))
685 								{
686 									EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
687 								}
688 							}
689 							else
690 							{
691 								if((result=EditorExpandNormalSelection(event->view,RPM_LINEEDGE)))
692 								{
693 									EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
694 								}
695 							}
696 						}
697 						else
698 						{
699 							if(!(keyEventData->modifiers&EEM_SHIFT))
700 							{
701 								if((result=EditorMoveCursor(event->view,RPM_DOCEDGE)))
702 								{
703 									EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
704 								}
705 							}
706 							else
707 							{
708 								if((result=EditorExpandNormalSelection(event->view,RPM_DOCEDGE)))
709 								{
710 									EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
711 								}
712 							}
713 						}
714 						break;
715 					case VVK_PAGEUP:
716 						if(keyEventData->modifiers&EEM_CTL)
717 						{
718 							EditorVerticalScrollByPages(event->view,-1);
719 						}
720 						else
721 						{
722 							if(!(keyEventData->modifiers&EEM_SHIFT))
723 							{
724 								if((result=EditorMoveCursor(event->view,RPM_mBACKWARD|RPM_PAGE)))
725 								{
726 									EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);
727 								}
728 							}
729 							else
730 							{
731 								if((result=EditorMoveSelection(event->view,RPM_mBACKWARD|RPM_PAGE)))
732 								{
733 									EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_LENIENT,HT_LENIENT);
734 								}
735 							}
736 						}
737 						break;
738 					case VVK_PAGEDOWN:
739 						if(keyEventData->modifiers&EEM_CTL)
740 						{
741 							EditorVerticalScrollByPages(event->view,1);
742 						}
743 						else
744 						{
745 							if(!(keyEventData->modifiers&EEM_SHIFT))
746 							{
747 								if((result=EditorMoveCursor(event->view,RPM_PAGE)))
748 								{
749 									EditorHomeViewToSelectionEdge(event->view,true,HT_LENIENT,HT_LENIENT);
750 								}
751 							}
752 							else
753 							{
754 								if((result=EditorMoveSelection(event->view,RPM_PAGE)))
755 								{
756 									EditorHomeViewToSelectionEdge(event->view,!event->view->parentBuffer->currentIsStart,HT_LENIENT,HT_LENIENT);
757 								}
758 							}
759 						}
760 						break;
761 					case VVK_UNDO:
762 						if((result=EditorUndo(event->view->parentBuffer,&didSomething)))
763 						{
764 							if(didSomething)
765 							{
766 								EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
767 							}
768 							else
769 							{
770 								EditorBeep();
771 							}
772 						}
773 						break;
774 					case VVK_REDO:
775 						if((result=EditorRedo(event->view->parentBuffer,&didSomething)))
776 						{
777 							if(didSomething)
778 							{
779 								EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
780 							}
781 							else
782 							{
783 								EditorBeep();
784 							}
785 						}
786 						break;
787 					case VVK_UNDOTOGGLE:
788 						if((result=EditorToggleUndo(event->view->parentBuffer,&didSomething)))
789 						{
790 							if(didSomething)
791 							{
792 								EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
793 							}
794 							else
795 							{
796 								EditorBeep();
797 							}
798 						}
799 						break;
800 					case VVK_CUT:
801 						if((result=EditorCut(event->view->parentBuffer,EditorGetCurrentClipboard())))
802 						{
803 							EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
804 						}
805 						break;
806 					case VVK_COPY:
807 						result=EditorCopy(event->view->parentBuffer,EditorGetCurrentClipboard());
808 						break;
809 					case VVK_PASTE:
810 						EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
811 						if((result=EditorColumnarPaste(event->view->parentBuffer,event->view,EditorGetCurrentClipboard())))
812 						{
813 							EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
814 						}
815 						break;
816 				}
817 			}
818 			else
819 			{
820 				key=(UINT8)keyEventData->keyCode;
821 				if(keyEventData->modifiers&EEM_MOD0)		// check for bound keys
822 				{
823 					switch(key)
824 					{
825 						case 'j':
826 							UniverseSanityCheck(event->view->parentBuffer->textUniverse);
827 							break;
828 						case 'a':
829 							result=EditorSelectAll(event->view->parentBuffer);
830 							break;
831 						case 'y':
832 							if((result=EditorRedo(event->view->parentBuffer,&didSomething)))
833 							{
834 								if(didSomething)
835 								{
836 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
837 								}
838 								else
839 								{
840 									EditorBeep();
841 								}
842 							}
843 							break;
844 						case 'u':
845 							if((result=EditorUndo(event->view->parentBuffer,&didSomething)))
846 							{
847 								if(didSomething)
848 								{
849 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
850 								}
851 								else
852 								{
853 									EditorBeep();
854 								}
855 							}
856 							break;
857 						case 'z':
858 							if((result=EditorToggleUndo(event->view->parentBuffer,&didSomething)))
859 							{
860 								if(didSomething)
861 								{
862 									EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
863 								}
864 								else
865 								{
866 									EditorBeep();
867 								}
868 							}
869 							break;
870 						case 'x':
871 							if((result=EditorCut(event->view->parentBuffer,EditorGetCurrentClipboard())))
872 							{
873 								EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
874 							}
875 							break;
876 						case 'c':
877 							result=EditorCopy(event->view->parentBuffer,EditorGetCurrentClipboard());
878 							break;
879 						case 'v':
880 							EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
881 							if((result=EditorColumnarPaste(event->view->parentBuffer,event->view,EditorGetCurrentClipboard())))
882 							{
883 								EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);
884 							}
885 							break;
886 					}
887 				}
888 				else
889 				{
890 					EditorHomeViewToSelectionEdge(event->view,false,HT_SEMISTRICT,HT_SEMISTRICT);	// if position was off the view, put it well back on
891 					if((result=EditorInsert(event->view->parentBuffer,&key,1)))
892 					{
893 						EditorHomeViewToSelectionEdge(event->view,false,HT_LENIENT,HT_LENIENT);			// now scroll into position
894 					}
895 				}
896 			}
897 			break;
898 		case VET_POSITIONVERTICAL:
899 			posEventData=(VIEW_POS_EVENT_DATA *)event->eventData;
900 			GetEditorViewTextInfo(event->view,&topLine,&numLines,&leftPixel,&numPixels);
901 			SetViewTopLeft(event->view,posEventData->position,leftPixel);
902 			break;
903 		case VET_POSITIONHORIZONTAL:
904 			posEventData=(VIEW_POS_EVENT_DATA *)event->eventData;
905 			GetEditorViewTextInfo(event->view,&topLine,&numLines,&leftPixel,&numPixels);
906 			SetViewTopLeft(event->view,topLine,posEventData->position);
907 			break;
908 		case VET_CLICK:
909 		case VET_CLICKHOLD:
910 			clickEventData=(VIEW_CLICK_EVENT_DATA *)event->eventData;
911 			trackMode=0;								// default mode
912 			if(event->eventType==VET_CLICKHOLD)			// see if still tracking
913 			{
914 				trackMode|=TM_mREPEAT;
915 			}
916 			if(clickEventData->modifiers&EEM_SHIFT)		// attempting to extend current selection?
917 			{
918 				trackMode|=TM_mCONTINUE;
919 			}
920 			if(clickEventData->modifiers&EEM_CTL)		// attempting to XOR to current selection?
921 			{
922 				trackMode|=TM_mXOR;
923 			}
924 			if((clickEventData->modifiers&EEM_MOD0)||(clickEventData->keyCode==2))
925 			{
926 				trackMode|=TM_mCOLUMNAR;
927 			}
928 			if((clickEventData->modifiers&EEM_MOD1)||(clickEventData->keyCode==1))
929 			{
930 				trackMode|=TM_mHAND;
931 			}
932 			switch((clickEventData->modifiers&EEM_STATE0)>>EES_STATE0)
933 			{
934 				case 0:
935 					trackMode|=TM_CHAR;
936 					break;
937 				case 1:
938 					trackMode|=TM_WORD;
939 					break;
940 				case 2:
941 					trackMode|=TM_LINE;
942 					break;
943 				default:
944 					trackMode|=TM_ALL;
945 					break;
946 			}
947 			result=EditorTrackViewPointer(event->view,clickEventData->xClick,clickEventData->yClick,trackMode);
948 			break;
949 	}
950 	// @@@ check "result" -- complain if something went wrong
951 	ResetEditorViewCursorBlink(event->view);	// reset cursor blinking (if we even have a cursor that blinks)
952 }
953 
HandleShellCommand(const char * command,int argc,char * argv[])954 bool HandleShellCommand(const char *command,int argc,char *argv[])
955 // Sometimes things in the gui request that the shell should perform
956 // certain operations. This is the interface to allow that to happen
957 // If the operation has some sort of failure (defined by the operation)
958 // this will report it (if there is a message) and return false
959 {
960 	char
961 		*list;
962 	int
963 		tclResult;
964 	const char
965 		*stringResult;
966 
967 	list=Tcl_Merge(argc,argv);
968 	ClearUserAbort();
969 	tclResult=Tcl_VarEval(tclInterpreter,"ShellCommand ",command," ",list,(char *)NULL);
970 	Tcl_Free(list);
971 	stringResult=Tcl_GetStringResult(tclInterpreter);
972 	if(tclResult!=TCL_OK)
973 	{
974 		if(stringResult[0])
975 		{
976 			ReportMessage("Shell command execution error:\n%.256s\n",stringResult);
977 		}
978 		return(false);
979 	}
980 	return(true);
981 }
982 
ExecuteStartupScript(Tcl_Interp * interpreter)983 static bool ExecuteStartupScript(Tcl_Interp *interpreter)
984 // Locate the startup script file, open, and execute it
985 // if there is any problem, SetError, and return false
986 {
987 	bool
988 		fail;
989 	char
990 		scriptPath[MAX_PATH_NAME_LENGTH];
991 
992 	fail=false;
993 	if(LocateStartupScript(scriptPath))
994 	{
995 		ClearUserAbort();
996 		Tcl_SetVar(interpreter,"SCRIPTPATH",scriptPath,TCL_LEAVE_ERR_MSG);
997 		if(Tcl_EvalFile(interpreter,scriptPath)!=TCL_OK)
998 		{
999 			SetError("%s:%d:%s",scriptPath,Tcl_GetErrorLine(interpreter),Tcl_GetStringResult(interpreter));
1000 			fail=true;
1001 		}
1002 	}
1003 	else
1004 	{
1005 		fail=true;
1006 	}
1007 	return(!fail);
1008 }
1009 
TraceCheckAbortProc(ClientData clientData,Tcl_Interp * interpreter,int level,const char * command,Tcl_Command commandToken,int objc,Tcl_Obj * const objv[])1010 static int TraceCheckAbortProc(ClientData clientData,Tcl_Interp *interpreter,int level,const char *command,Tcl_Command commandToken,int objc,Tcl_Obj *const objv[])
1011 // This is a small trick on Tcl. We tell it we want to trace, but really, we want
1012 // to check to see if the user is trying to abort the execution of a script.
1013 // So, every time we are called, we check to see if the user is aborting.
1014 {
1015 	if(CheckUserAbort())
1016 	{
1017 		Tcl_AppendResult(interpreter,"Script Aborted",NULL);
1018 		return(TCL_ERROR);
1019 	}
1020 	return(TCL_OK);
1021 }
1022 
UnSetUpTclAbortHandling(Tcl_Interp * interpreter)1023 static void UnSetUpTclAbortHandling(Tcl_Interp *interpreter)
1024 // Undo what SetUpTclAbortHandling did
1025 {
1026 	Tcl_DeleteTrace(interpreter,tclTrace);
1027 }
1028 
SetUpTclAbortHandling(Tcl_Interp * interpreter)1029 static bool SetUpTclAbortHandling(Tcl_Interp *interpreter)
1030 // Set up Tcl so that it manages to call CheckUserAbort, and if CheckUserAbort
1031 // returns true, then we force an error in the Tcl control Flow.
1032 {
1033 	if((tclTrace=Tcl_CreateObjTrace(interpreter,0,TCL_ALLOW_INLINE_COMPILATION,TraceCheckAbortProc,(ClientData)NULL,(Tcl_CmdObjTraceDeleteProc *)NULL)))
1034 	{
1035 		return(true);
1036 	}
1037 	return(false);
1038 }
1039 
ShellDoBackground()1040 void ShellDoBackground()
1041 // Handle Tcl's event loop
1042 {
1043 	while(Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT))
1044 		;							// tell Tcl to check for any events it may care about
1045 }
1046 
UnInitTcl()1047 static void UnInitTcl()
1048 // Undo what InitTcl did
1049 {
1050 	UnSetUpTclAbortHandling(tclInterpreter);
1051 	UnInitChannels(tclInterpreter);
1052 	Tcl_DeleteInterp(tclInterpreter);	// have TCL clean up
1053 }
1054 
InitTcl(char * pathName)1055 static bool InitTcl(char *pathName)
1056 // Initialize Tcl
1057 {
1058 	if((tclInterpreter=Tcl_CreateInterp()))
1059 	{
1060 		if(InitChannels(tclInterpreter))
1061 		{
1062 			if(Tcl_Init(tclInterpreter)==TCL_OK)
1063 			{
1064 			    Tcl_FindExecutable(pathName);
1065 
1066 				if(SetUpTclAbortHandling(tclInterpreter))
1067 				{
1068 					return(true);
1069 				}
1070 			}
1071 			else
1072 			{
1073 				SetError("Failed to Tcl_Init(): %s",Tcl_GetStringResult(tclInterpreter));
1074 			}
1075 			UnInitChannels(tclInterpreter);
1076 		}
1077 		else
1078 		{
1079 			SetError("Failed to redirect Tcl's stdout and stderr");
1080 		}
1081 		Tcl_DeleteInterp(tclInterpreter);						// have TCL clean up
1082 	}
1083 	else
1084 	{
1085 		SetError("Failed to create TCL interpreter");
1086 	}
1087 	return(false);
1088 }
1089 
ShellLoop(int argc,char * argv[])1090 void ShellLoop(int argc,char *argv[])
1091 // This is where the editor shell gets and handles events
1092 // it actually defers to the gui level to call us back with events
1093 {
1094 	ClearErrorTrace();
1095 	if(InitTcl(argv[0]))
1096 	{
1097 		ClearErrorTrace();
1098 		if(CreateEditorShellCommands(tclInterpreter))
1099 		{
1100 			ClearErrorTrace();
1101 			if(AddSupplementalShellCommands(tclInterpreter))
1102 			{
1103 				ClearErrorTrace();
1104 				if(ExecuteStartupScript(tclInterpreter))			// deal with the start-up script, leave if there is a problem
1105 				{
1106 					EditorEventLoop(argc,argv);						// pass initial parameters
1107 				}
1108 				else
1109 				{
1110 					fprintf(stderr,"Failed to execute startup script: %s\n",GetErrorTrace());
1111 				}
1112 			}
1113 			else
1114 			{
1115 				fprintf(stderr,"Failed to add supplemental shell commands: %s\n",GetErrorTrace());
1116 			}
1117 		}
1118 		else
1119 		{
1120 			fprintf(stderr,"Failed to create editor shell commands: %s\n",GetErrorTrace());
1121 		}
1122 		UnInitTcl();
1123 	}
1124 	else
1125 	{
1126 		fprintf(stderr,"Failed to initialize Tcl: %s\n",GetErrorTrace());
1127 	}
1128 }
1129