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