comparison src/integration.c @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children ddada568db54
comparison
equal deleted inserted replaced
6:c2daee826b8f 7:3fc0f57ecb91
1 /* vi:set ts=8 sw=8:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 * Visual Workshop integration by Gordon Prieur
5 *
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
9 */
10
11 /*
12 * Integration with Sun Workshop.
13 *
14 * This file should not change much, it's also used by other editors that
15 * connect to Workshop. Consider changing workshop.c instead.
16 */
17 /*
18 -> consider using MakeSelectionVisible instead of gotoLine hacks
19 to show the line properly
20 -> consider using glue instead of our own message wrapping functions
21 (but can only use glue if we don't have to distribute source)
22 */
23
24 #include "vim.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <fcntl.h>
30
31 #ifdef INET_SOCKETS
32 #include <netdb.h>
33 #include <netinet/in.h>
34 #else
35 #include <sys/un.h>
36 #endif
37
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/param.h>
42 #ifdef HAVE_LIBGEN_H
43 # include <libgen.h>
44 #endif
45 #include <unistd.h>
46 #include <string.h>
47
48 #include <X11/Intrinsic.h>
49 #include <Xm/Xm.h>
50 #include <Xm/AtomMgr.h>
51 #include <Xm/PushB.h>
52
53 #ifdef HAVE_X11_XPM_H
54 # include <X11/xpm.h>
55 #else
56 # ifdef HAVE_XM_XPMP_H
57 # include <Xm/XpmP.h>
58 # endif
59 #endif
60
61 #ifdef HAVE_UTIL_DEBUG_H
62 # include <util/debug.h>
63 #endif
64 #ifdef HAVE_UTIL_MSGI18N_H
65 # include <util/msgi18n.h>
66 #endif
67
68 #include "integration.h" /* <EditPlugin/integration.h> */
69 #ifdef HAVE_FRAME_H
70 # include <frame.h>
71 #endif
72
73 #ifndef MAX
74 # define MAX(a, b) (a) > (b) ? (a) : (b)
75 #endif
76
77 #ifndef NOCATGETS
78 # define NOCATGETS(x) x
79 #endif
80
81 /* Functions private to this file */
82 static void workshop_connection_closed(void);
83 static void messageFromEserve(XtPointer clientData, int *NOTUSED1, XtInputId *NOTUSED2);
84 static void workshop_disconnect(void);
85 static void workshop_sensitivity(int num, char *table);
86 static void adjust_sign_name(char *filename);
87 static void process_menuItem(char *);
88 static void process_toolbarButton(char *);
89 static void workshop_set_option_first(char *name, char *value);
90
91
92 #define CMDBUFSIZ 2048
93
94 #ifdef DEBUG
95 static FILE *dfd;
96 static void pldebug(char *, ...);
97 static void unrecognised_message(char *);
98
99 #define HANDLE_ERRORS(cmd) else unrecognised_message(cmd);
100 #else
101 #define HANDLE_ERRORS(cmd)
102 #endif
103
104 /*
105 * Version number of the protocol between an editor and eserve.
106 * This number should be incremented when the protocol
107 * is changed.
108 */
109 #define PROTOCOL_VERSION "4.0.0"
110
111 static int sd = -1;
112 static XtInputId inputHandler; /* Cookie for input */
113
114 Boolean save_files = True; /* When true, save all files before build actions */
115
116 void
117 workshop_connection_closed(void)
118 {
119 /*
120 * socket closed on other end
121 */
122 XtRemoveInput(inputHandler);
123 inputHandler = 0;
124 sd = -1;
125 }
126
127 static char *
128 getCommand(void)
129 {
130 int len; /* length of this command */
131 char lenbuf[7]; /* get the length string here */
132 char *newcb; /* used to realloc cmdbuf */
133 static char *cmdbuf;/* get the command string here */
134 static int cbsize;/* size of cmdbuf */
135
136 if ((len = read(sd, &lenbuf, 6)) == 6) {
137 lenbuf[6] = 0; /* Terminate buffer such that atoi() works right */
138 len = atoi(lenbuf);
139 if (cbsize < (len + 1)) {
140 newcb = (char *) realloc(cmdbuf,
141 MAX((len + 256), CMDBUFSIZ));
142 if (newcb != NULL) {
143 cmdbuf = newcb;
144 cbsize = MAX((len + 256), CMDBUFSIZ);
145 }
146 }
147 if (cbsize >= len && (len = read(sd, cmdbuf, len)) > 0) {
148 cmdbuf[len] = 0;
149 return cmdbuf;
150 } else {
151 return NULL;
152 }
153 } else {
154 if (len == 0) { /* EOF */
155 workshop_connection_closed();
156 }
157 return NULL;
158 }
159
160 }
161
162 /*ARGSUSED*/
163 void
164 messageFromEserve(XtPointer clientData, int *NOTUSED1, XtInputId *NOTUSED2)
165 {
166 char *cmd; /* the 1st word of the command */
167
168 cmd = getCommand();
169 if (cmd == NULL) {
170 /* We're being shut down by eserve and the "quit" message
171 * didn't arrive before the socket connection got closed */
172 return;
173 }
174 #ifdef DEBUG
175 pldebug("%s\n", cmd);
176 #endif
177 switch (*cmd) {
178 case 'a':
179 if (cmd[1] == 'c' &&
180 strncmp(cmd, NOCATGETS("ack "), 4) == 0) {
181 int ackNum;
182 char buf[20];
183
184 ackNum = atoi(&cmd[4]);
185 sprintf(buf, NOCATGETS("ack %d\n"), ackNum);
186 write(sd, buf, strlen(buf));
187 } else if (strncmp(cmd,
188 NOCATGETS("addMarkType "), 12) == 0) {
189 int idx;
190 char *color;
191 char *sign;
192
193 idx = atoi(strtok(&cmd[12], " "));
194 color = strtok(NULL, NOCATGETS("\001"));
195 sign = strtok(NULL, NOCATGETS("\001"));
196 /* Skip space that separates names */
197 if (color) {
198 color++;
199 }
200 if (sign) {
201 sign++;
202 }
203 /* Change sign name to accomodate a different size? */
204 adjust_sign_name(sign);
205 workshop_add_mark_type(idx, color, sign);
206 }
207 HANDLE_ERRORS(cmd);
208 break;
209
210 case 'b':
211 if (strncmp(cmd,
212 NOCATGETS("balloon "), 8) == 0) {
213 char *tip;
214
215 tip = strtok(&cmd[8], NOCATGETS("\001"));
216 workshop_show_balloon_tip(tip);
217 }
218 HANDLE_ERRORS(cmd);
219 break;
220
221 case 'c':
222 if (strncmp(cmd,
223 NOCATGETS("changeMarkType "), 15) == 0) {
224 char *file;
225 int markId;
226 int type;
227
228 file = strtok(&cmd[15], " ");
229 markId = atoi(strtok(NULL, " "));
230 type = atoi(strtok(NULL, " "));
231 workshop_change_mark_type(file, markId, type);
232 }
233 HANDLE_ERRORS(cmd);
234 break;
235
236 case 'd':
237 if (strncmp(cmd, NOCATGETS("deleteMark "), 11) == 0) {
238 char *file;
239 int markId;
240
241 file = strtok(&cmd[11], " ");
242 markId = atoi(strtok(NULL, " "));
243 workshop_delete_mark(file, markId);
244 }
245 HANDLE_ERRORS(cmd);
246 break;
247
248 case 'f':
249 if (cmd[1] == 'o' &&
250 strncmp(cmd, NOCATGETS("footerMsg "), 10) == 0) {
251 int severity;
252 char *message;
253
254 severity =
255 atoi(strtok(&cmd[10], " "));
256 message = strtok(NULL, NOCATGETS("\001"));
257
258 workshop_footer_message(message, severity);
259 } else if (strncmp(cmd,
260 NOCATGETS("frontFile "), 10) == 0) {
261 char *file;
262
263 file = strtok(&cmd[10], " ");
264 workshop_front_file(file);
265 }
266 HANDLE_ERRORS(cmd);
267 break;
268
269 case 'g':
270 if (cmd[1] == 'e' &&
271 strncmp(cmd, NOCATGETS("getMarkLine "), 12) == 0) {
272 char *file;
273 int markid;
274 int line;
275 char buf[100];
276
277 file = strtok(&cmd[12], " ");
278 markid = atoi(strtok(NULL, " "));
279 line = workshop_get_mark_lineno(file, markid);
280 sprintf(buf, NOCATGETS("markLine %s %d %d\n"),
281 file, markid, line);
282 write(sd, buf, strlen(buf));
283 } else if (cmd[1] == 'o' && cmd[4] == 'L' &&
284 strncmp(cmd, NOCATGETS("gotoLine "), 9) == 0) {
285 char *file;
286 int lineno;
287
288 file = strtok(&cmd[9], " ");
289 lineno = atoi(strtok(NULL, " "));
290 workshop_goto_line(file, lineno);
291 } else if (strncmp(cmd,
292 NOCATGETS("gotoMark "), 9) == 0) {
293 char *file;
294 int markId;
295 char *message;
296
297 file = strtok(&cmd[9], " ");
298 markId = atoi(strtok(NULL, " "));
299 message = strtok(NULL, NOCATGETS("\001"));
300 workshop_goto_mark(file, markId, message);
301 #ifdef NOHANDS_SUPPORT_FUNCTIONS
302 } else if (strcmp(cmd, NOCATGETS("getCurrentFile")) == 0) {
303 char *f = workshop_test_getcurrentfile();
304 char buffer[2*MAXPATHLEN];
305 sprintf(buffer, NOCATGETS("currentFile %d %s"),
306 f ? strlen(f) : 0, f ? f : "");
307 workshop_send_message(buffer);
308 } else if (strcmp(cmd, NOCATGETS("getCursorRow")) == 0) {
309 int row = workshop_test_getcursorrow();
310 char buffer[2*MAXPATHLEN];
311 sprintf(buffer, NOCATGETS("cursorRow %d"), row);
312 workshop_send_message(buffer);
313 } else if (strcmp(cmd, NOCATGETS("getCursorCol")) == 0) {
314 int col = workshop_test_getcursorcol();
315 char buffer[2*MAXPATHLEN];
316 sprintf(buffer, NOCATGETS("cursorCol %d"), col);
317 workshop_send_message(buffer);
318 } else if (strcmp(cmd, NOCATGETS("getCursorRowText")) == 0) {
319 char *t = workshop_test_getcursorrowtext();
320 char buffer[2*MAXPATHLEN];
321 sprintf(buffer, NOCATGETS("cursorRowText %d %s"),
322 t ? strlen(t) : 0, t ? t : "");
323 workshop_send_message(buffer);
324 } else if (strcmp(cmd, NOCATGETS("getSelectedText")) == 0) {
325 char *t = workshop_test_getselectedtext();
326 char buffer[2*MAXPATHLEN];
327 sprintf(buffer, NOCATGETS("selectedText %d %s"),
328 t ? strlen(t) : 0, t ? t : "");
329 workshop_send_message(buffer);
330 #endif
331 }
332 HANDLE_ERRORS(cmd);
333 break;
334
335 case 'l':
336 if (strncmp(cmd, NOCATGETS("loadFile "), 9) == 0) {
337 char *file;
338 int line;
339 char *frameid;
340
341 file = strtok(&cmd[9], " ");
342 line = atoi(strtok(NULL, " "));
343 frameid = strtok(NULL, " ");
344 workshop_load_file(file, line, frameid);
345 }
346 HANDLE_ERRORS(cmd);
347 break;
348
349 case 'm': /* Menu, minimize, maximize */
350 if (cmd[1] == 'e' && cmd[4] == 'B' &&
351 strncmp(cmd, NOCATGETS("menuBegin "), 10) == 0) {
352 workshop_menu_begin(&cmd[10]);
353 } else if (cmd[1] == 'e' && cmd[4] == 'I' &&
354 strncmp(cmd, NOCATGETS("menuItem "), 9) == 0) {
355 process_menuItem(cmd);
356 } else if (cmd[1] == 'e' && cmd[4] == 'E' &&
357 strcmp(cmd, NOCATGETS("menuEnd")) == 0) {
358 workshop_menu_end();
359 } else if (cmd[1] == 'a' &&
360 strcmp(cmd, NOCATGETS("maximize")) == 0) {
361 workshop_maximize();
362 } else if (strcmp(cmd, NOCATGETS("minimize")) == 0) {
363 workshop_minimize();
364 }
365 HANDLE_ERRORS(cmd);
366 break;
367
368 case 'o':
369 if (cmd[1] == 'p' &&
370 strcmp(cmd, NOCATGETS("option"))) {
371 char *name;
372 char *value;
373
374 name = strtok(&cmd[7], " ");
375 value = strtok(NULL, " ");
376 workshop_set_option_first(name, value);
377 }
378 HANDLE_ERRORS(cmd);
379 break;
380
381 case 'p':
382 if (strcmp(cmd, NOCATGETS("ping")) == 0) {
383 #if 0
384 int pingNum;
385
386 pingNum = atoi(&cmd[5]);
387 workshop_send_ack(ackNum);
388 WHAT DO I DO HERE?
389 #endif
390 }
391 HANDLE_ERRORS(cmd);
392 break;
393
394 case 'q':
395 if (strncmp(cmd, NOCATGETS("quit"), 4) == 0) {
396
397 /* Close the connection. It's important to do
398 * that now, since workshop_quit might be
399 * looking at open files. For example, if you
400 * have modified one of the files without
401 * saving, NEdit will ask you what you want to
402 * do, and spin loop by calling
403 * XtAppProcessEvent while waiting for your
404 * reply. In this case, if we still have an
405 * input handler and the socket has been
406 * closed on the other side when eserve
407 * expired, we will hang in IoWait.
408 */
409 workshop_disconnect();
410
411 workshop_quit();
412 }
413 HANDLE_ERRORS(cmd);
414 break;
415
416 case 'r':
417 if (cmd[1] == 'e' &&
418 strncmp(cmd, NOCATGETS("reloadFile "), 11) == 0) {
419 char *file;
420 int line;
421
422 file = strtok(&cmd[11], " ");
423 line = atoi(strtok(NULL, " "));
424 workshop_reload_file(file, line);
425 }
426 HANDLE_ERRORS(cmd);
427 break;
428
429 case 's':
430 if (cmd[1] == 'e' && cmd[2] == 't' &&
431 strncmp(cmd, NOCATGETS("setMark "), 8) == 0) {
432 char *file;
433 int line;
434 int markId;
435 int type;
436
437 file = strtok(&cmd[8], " ");
438 line = atoi(strtok(NULL, " "));
439 markId = atoi(strtok(NULL, " "));
440 type = atoi(strtok(NULL, " "));
441 workshop_set_mark(file, line, markId, type);
442 } else if (cmd[1] == 'h' &&
443 strncmp(cmd, NOCATGETS("showFile "), 9) == 0) {
444 workshop_show_file(&cmd[9]);
445 } else if (cmd[1] == 'u' &&
446 strncmp(cmd, NOCATGETS("subMenu "), 8) == 0) {
447 char *label;
448
449 label = strtok(&cmd[8], NOCATGETS("\001"));
450 workshop_submenu_begin(label);
451 } else if (cmd[1] == 'u' &&
452 strcmp(cmd, NOCATGETS("subMenuEnd")) == 0) {
453 workshop_submenu_end();
454 } else if (cmd[1] == 'e' && cmd[2] == 'n' &&
455 strncmp(cmd, NOCATGETS("sensitivity "), 12) == 0) {
456 int num;
457 char *bracket;
458 char *table;
459
460 num = atoi(strtok(&cmd[12], " "));
461 bracket = strtok(NULL, " ");
462 if (*bracket != '[') {
463 fprintf(stderr, NOCATGETS("Parsing "
464 "error for sensitivity\n"));
465 } else {
466 table = strtok(NULL, NOCATGETS("]"));
467 workshop_sensitivity(num, table);
468 }
469 } else if (cmd[1] == 'e' && cmd[2] == 'n' && cmd[3] == 'd' &&
470 strncmp(cmd, NOCATGETS("sendVerb "), 9) == 0) {
471 /* Send the given verb back (used for the
472 * debug.lineno callback (such that other tools
473 * can obtain the position coordinates or the
474 * selection) */
475 char *verb;
476
477 verb = strtok(&cmd[9], " ");
478 workshop_perform_verb(verb, NULL);
479 } else if (cmd[1] == 'a' &&
480 strncmp(cmd, NOCATGETS("saveFile "), 9) == 0) {
481 workshop_save_file(&cmd[9]);
482 #ifdef NOHANDS_SUPPORT_FUNCTIONS
483 } else if (strncmp(cmd, NOCATGETS("saveSensitivity "), 16) == 0) {
484 char *file;
485
486 file = strtok(&cmd[16], " ");
487 workshop_save_sensitivity(file);
488 #endif
489 }
490 HANDLE_ERRORS(cmd);
491 break;
492
493 case 't': /* Toolbar */
494 if (cmd[8] == 'e' &&
495 strncmp(cmd, NOCATGETS("toolbarBegin"), 12) == 0) {
496 workshop_toolbar_begin();
497 } else if (cmd[8] == 'u' &&
498 strncmp(cmd, NOCATGETS("toolbarButton"), 13) == 0) {
499 process_toolbarButton(cmd);
500 } else if (cmd[7] == 'E' &&
501 strcmp(cmd, NOCATGETS("toolbarEnd")) == 0) {
502 workshop_toolbar_end();
503 }
504 HANDLE_ERRORS(cmd);
505 break;
506
507 #ifdef DEBUG
508 default:
509 unrecognised_message(cmd);
510 break;
511 #endif
512 }
513 }
514
515 static void
516 process_menuItem(
517 char *cmd)
518 {
519 char *label = strtok(&cmd[9], NOCATGETS("\001"));
520 char *verb = strtok(NULL, NOCATGETS("\001"));
521 char *acc = strtok(NULL, NOCATGETS("\001"));
522 char *accText = strtok(NULL, NOCATGETS("\001"));
523 char *name = strtok(NULL, NOCATGETS("\001"));
524 char *sense = strtok(NULL, NOCATGETS("\n"));
525 char *filepos = strtok(NULL, NOCATGETS("\n"));
526 if (*acc == '-') {
527 acc = NULL;
528 }
529 if (*accText == '-') {
530 accText = NULL;
531 }
532 workshop_menu_item(label, verb, acc, accText, name, filepos, sense);
533
534 }
535
536
537 static void
538 process_toolbarButton(
539 char *cmd) /* button definition */
540 {
541 char *label = strtok(&cmd[14], NOCATGETS("\001"));
542 char *verb = strtok(NULL, NOCATGETS("\001"));
543 char *senseVerb = strtok(NULL, NOCATGETS("\001"));
544 char *filepos = strtok(NULL, NOCATGETS("\001"));
545 char *help = strtok(NULL, NOCATGETS("\001"));
546 char *sense = strtok(NULL, NOCATGETS("\001"));
547 char *file = strtok(NULL, NOCATGETS("\001"));
548 char *left = strtok(NULL, NOCATGETS("\n"));
549
550 if (!strcmp(label, NOCATGETS("-"))) {
551 label = NULL;
552 }
553 if (!strcmp(help, NOCATGETS("-"))) {
554 help = NULL;
555 }
556 if (!strcmp(file, NOCATGETS("-"))) {
557 file = NULL;
558 }
559 if (!strcmp(senseVerb, NOCATGETS("-"))) {
560 senseVerb = NULL;
561 }
562 workshop_toolbar_button(label, verb, senseVerb, filepos, help,
563 sense, file, left);
564 }
565
566
567 #ifdef DEBUG
568 void
569 unrecognised_message(
570 char *cmd)
571 {
572 pldebug("Unrecognised eserve message:\n\t%s\n", cmd);
573 /* abort(); */
574 }
575 #endif
576
577
578 /* Change sign name to accomodate a different size:
579 * Create the filename based on the height. The filename format
580 * of multisize icons are:
581 * x.xpm : largest icon
582 * x1.xpm : smaller icon
583 * x2.xpm : smallest icon */
584 void
585 adjust_sign_name(char *filename)
586 {
587 char *s;
588 static int fontSize = -1;
589
590 if (fontSize == -1)
591 fontSize = workshop_get_font_height();
592 if (fontSize == 0)
593 return;
594 if (filename[0] == '-')
595 return;
596
597 /* This is ugly: later we should instead pass the fontheight over
598 * to eserve on startup and let eserve just send the right filenames
599 * to us in the first place
600
601 * I know that the filename will end with 1.xpm (see
602 * GuiEditor.cc`LispPrintSign if you wonder why) */
603 s = filename+strlen(filename)-5;
604 if (fontSize <= 11)
605 strcpy(s, "2.xpm");
606 else if (fontSize <= 15)
607 strcpy(s, "1.xpm");
608 else
609 strcpy(s, ".xpm");
610 }
611
612 /* Were we invoked by WorkShop? This function can be used early during startup
613 if you want to do things differently if the editor is started standalone
614 or in WorkShop mode. For example, in standalone mode you may not want to
615 add a footer/message area or a sign gutter. */
616 int
617 workshop_invoked()
618 {
619 static int result = -1;
620 if (result == -1) {
621 result = (getenv(NOCATGETS("SPRO_EDITOR_SOCKET")) != NULL);
622 }
623 return result;
624 }
625
626 /* Connect back to eserve */
627 void workshop_connect(XtAppContext context)
628 {
629 #ifdef INET_SOCKETS
630 struct sockaddr_in server;
631 struct hostent * host;
632 int port;
633 #else
634 struct sockaddr_un server;
635 #endif
636 char buf[32];
637 char * address;
638 #ifdef DEBUG
639 char *file;
640 #endif
641
642 address = getenv(NOCATGETS("SPRO_EDITOR_SOCKET"));
643 if (address == NULL) {
644 return;
645 }
646
647 #ifdef INET_SOCKETS
648 port = atoi(address);
649
650 if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
651 PERROR(NOCATGETS("workshop_connect"));
652 return;
653 }
654
655 /* Get the server internet address and put into addr structure */
656 /* fill in the socket address structure and connect to server */
657 memset((char *)&server, '\0', sizeof(server));
658 server.sin_family = AF_INET;
659 server.sin_port = port;
660 if ((host = gethostbyname(NOCATGETS("localhost"))) == NULL) {
661 PERROR(NOCATGETS("gethostbyname"));
662 sd = -1;
663 return;
664 }
665 memcpy((char *)&server.sin_addr, host->h_addr, host->h_length);
666 #else
667 if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
668 PERROR(NOCATGETS("workshop_connect"));
669 return;
670 }
671
672 server.sun_family = AF_UNIX;
673 strcpy(server.sun_path, address);
674 #endif
675 /* Connect to server */
676 if (connect(sd, (struct sockaddr *)&server, sizeof(server))) {
677 if (errno == ECONNREFUSED) {
678 close(sd);
679 #ifdef INET_SOCKETS
680 if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
681 PERROR(NOCATGETS("workshop_connect"));
682 return;
683 }
684 #else
685 if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
686 PERROR(NOCATGETS("workshop_connect"));
687 return;
688 }
689 #endif
690 if (connect(sd, (struct sockaddr *)&server,
691 sizeof(server))) {
692 PERROR(NOCATGETS("workshop_connect"));
693 return;
694 }
695
696 } else {
697 PERROR(NOCATGETS("workshop_connect"));
698 return;
699 }
700 }
701
702 /* tell notifier we are interested in being called
703 * when there is input on the editor connection socket
704 */
705 inputHandler = XtAppAddInput(context, sd, (XtPointer) XtInputReadMask,
706 messageFromEserve, NULL);
707 #ifdef DEBUG
708 if ((file = getenv(NOCATGETS("SPRO_PLUGIN_DEBUG"))) != NULL) {
709 char buf[BUFSIZ];
710
711 unlink(file);
712 sprintf(buf, "date > %s", file);
713 system(buf);
714 dfd = fopen(file, "a");
715 } else {
716 dfd = NULL;
717 }
718 #endif
719
720 sprintf(buf, NOCATGETS("connected %s %s %s\n"),
721 workshop_get_editor_name(),
722 PROTOCOL_VERSION,
723 workshop_get_editor_version());
724 write(sd, buf, strlen(buf));
725
726 sprintf(buf, NOCATGETS("ack 1\n"));
727 write(sd, buf, strlen(buf));
728 }
729
730 void workshop_disconnect()
731 {
732 /* Probably need to send some message here */
733
734 /*
735 * socket closed on other end
736 */
737 XtRemoveInput(inputHandler);
738 close(sd);
739 inputHandler = 0;
740 sd = -1;
741
742 }
743
744 /*
745 * Utility functions
746 */
747
748 /* Set icon for the window */
749 void
750 workshop_set_icon(Display *display, Widget shell, char **xpmdata,
751 int width, int height)
752 {
753 Pixel bgPixel;
754 XpmAttributes xpmAttributes;
755 XSetWindowAttributes attr;
756 Window iconWindow;
757 int depth;
758 int screenNum;
759 Pixmap pixmap;
760
761 /* Create the pixmap/icon window which is shown when you
762 * iconify the sccs viewer
763 * This code snipped was adapted from Sun WorkShop's source base,
764 * setIcon.cc.
765 */
766 XtVaGetValues(shell, XmNbackground, &bgPixel, NULL);
767 screenNum = XScreenNumberOfScreen(XtScreen(shell));
768 depth = DisplayPlanes(display, screenNum);
769 xpmAttributes.valuemask = XpmColorSymbols;
770 xpmAttributes.numsymbols = 1;
771 xpmAttributes.colorsymbols =
772 (XpmColorSymbol *)XtMalloc(sizeof (XpmColorSymbol) *
773 xpmAttributes.numsymbols);
774 xpmAttributes.colorsymbols[0].name = NOCATGETS("BgColor");
775 xpmAttributes.colorsymbols[0].value = NULL;
776 xpmAttributes.colorsymbols[0].pixel = bgPixel;
777 if (XpmCreatePixmapFromData(display,
778 RootWindow(display, screenNum), xpmdata, &pixmap,
779 NULL, &xpmAttributes) >= 0) {
780 attr.background_pixmap = pixmap;
781 iconWindow = XCreateWindow(display, RootWindow(display,
782 screenNum), 0, 0, width, height, 0, depth,
783 (unsigned int)CopyFromParent,
784 CopyFromParent, CWBackPixmap, &attr);
785
786 XtVaSetValues(shell,
787 XtNiconWindow, iconWindow, NULL);
788 }
789 XtFree((char *)xpmAttributes.colorsymbols);
790 }
791
792 /* Minimize and maximize shells. From libutil's shell.cc. */
793
794 /* utility functions from libutil's shell.cc */
795 static Boolean
796 isWindowMapped(Display *display, Window win)
797 {
798 XWindowAttributes winAttrs;
799 XGetWindowAttributes(display,
800 win,
801 &winAttrs);
802 if (winAttrs.map_state == IsViewable) {
803 return(True);
804 } else {
805 return(False);
806 }
807 }
808
809 static Boolean
810 isMapped(Widget widget)
811 {
812 if (widget == NULL) {
813 return(False);
814 }
815
816 if (XtIsRealized(widget) == False) {
817 return(False);
818 }
819
820 return(isWindowMapped(XtDisplay(widget), XtWindow(widget)));
821 }
822
823 static Boolean
824 widgetIsIconified(
825 Widget w)
826 {
827 Atom wm_state;
828 Atom act_type; /* actual Atom type returned */
829 int act_fmt; /* actual format returned */
830 u_long nitems_ret; /* number of items returned */
831 u_long bytes_after; /* number of bytes remaining */
832 u_long *property; /* actual property returned */
833
834 /*
835 * If a window is iconified its WM_STATE is set to IconicState. See
836 * ICCCM Version 2.0, section 4.1.3.1 for more details.
837 */
838
839 wm_state = XmInternAtom(XtDisplay(w), NOCATGETS("WM_STATE"), False);
840 if (XtWindow(w) != 0) { /* only check if window exists! */
841 XGetWindowProperty(XtDisplay(w), XtWindow(w), wm_state, 0L, 2L,
842 False, AnyPropertyType, &act_type, &act_fmt, &nitems_ret,
843 &bytes_after, (u_char **) &property);
844 if (nitems_ret == 2 && property[0] == IconicState) {
845 return True;
846 }
847 }
848
849 return False;
850
851 } /* end widgetIsIconified */
852
853 void
854 workshop_minimize_shell(Widget shell)
855 {
856 if (shell != NULL &&
857 XtIsObject(shell) &&
858 XtIsRealized(shell) == True) {
859 if (isMapped(shell) == True) {
860 XIconifyWindow(XtDisplay(shell), XtWindow(shell),
861 XScreenNumberOfScreen(XtScreen(shell)));
862 }
863 XtVaSetValues(shell,
864 XmNiconic, True,
865 NULL);
866 }
867 }
868
869 void workshop_maximize_shell(Widget shell)
870 {
871 if (shell != NULL &&
872 XtIsRealized(shell) == True &&
873 widgetIsIconified(shell) == True &&
874 isMapped(shell) == False) {
875 XtMapWidget(shell);
876 /* This used to be
877 XtPopdown(shell);
878 XtPopup(shell, XtGrabNone);
879 However, I found that that would drop any transient
880 windows that had been iconified with the window.
881 According to the ICCCM, XtMapWidget should be used
882 to bring a window from Iconic to Normal state.
883 However, Rich Mauri did a lot of work on this during
884 Bart, and found that XtPopDown,XtPopup was required
885 to fix several bugs involving multiple CDE workspaces.
886 I've tested it now and things seem to work fine but
887 I'm leaving this note for history in case this needs
888 to be revisited.
889 */
890 }
891 }
892
893
894 Boolean workshop_get_width_height(int *width, int *height)
895 {
896 static int wid = 0;
897 static int hgt = 0;
898 static Boolean firstTime = True;
899 static Boolean success = False;
900
901 if (firstTime) {
902 char *settings;
903
904 settings = getenv(NOCATGETS("SPRO_GUI_WIDTH_HEIGHT"));
905 if (settings != NULL) {
906 wid = atoi(settings);
907 settings = strrchr(settings, ':');
908 if (settings++ != NULL) {
909 hgt = atoi(settings);
910 }
911 if (wid > 0 && hgt > 0) {
912 success = True;
913 }
914 firstTime = False;
915 }
916 }
917
918 if (success) {
919 *width = wid;
920 *height = hgt;
921 }
922 return success;
923 }
924
925
926 Boolean workshop_get_rows_cols(int *rows, int *cols)
927 {
928 static int r = 0;
929 static int c = 0;
930 static Boolean firstTime = True;
931 static Boolean success = False;
932
933 if (firstTime) {
934 char *settings;
935
936 settings = getenv(NOCATGETS("SPRO_GUI_ROWS_COLS"));
937 if (settings != NULL) {
938 r = atoi(settings);
939 settings = strrchr(settings, ':');
940 if (settings++ != NULL) {
941 c = atoi(settings);
942 }
943 if (r > 0 && c > 0) {
944 success = True;
945 }
946 firstTime = False;
947 }
948 }
949
950 if (success) {
951 *rows = r;
952 *cols = c;
953 }
954 return success;
955 }
956
957 /*
958 * Toolbar code
959 */
960
961 void workshop_sensitivity(int num, char *table)
962 {
963 /* build up a verb table */
964 VerbSense *vs;
965 int i;
966 char *s;
967 if ((num < 1) || (num > 500)) {
968 return;
969 }
970
971 vs = (VerbSense *)malloc((num+1)*sizeof(VerbSense));
972
973 /* Point to the individual names (destroys the table string, but
974 * that's okay -- this is more efficient than duplicating strings) */
975 s = table;
976 for (i = 0; i < num; i++) {
977 while (*s == ' ') {
978 s++;
979 }
980 vs[i].verb = s;
981 while (*s && (*s != ' ') && (*s != '\001')) {
982 s++;
983 }
984 if (*s == 0) {
985 vs[i].verb = NULL;
986 break;
987 }
988 if (*s == '\001') {
989 *s = 0;
990 s++;
991 }
992 *s = 0;
993 s++;
994 while (*s == ' ') {
995 s++;
996 }
997 if (*s == '1') {
998 vs[i].sense = 1;
999 } else {
1000 vs[i].sense = 0;
1001 }
1002 s++;
1003 }
1004 vs[i].verb = NULL;
1005
1006 workshop_frame_sensitivities(vs);
1007
1008 free(vs);
1009 }
1010
1011 /*
1012 * Options code
1013 */
1014 /* Set an editor option.
1015 * IGNORE an option if you do not recognize it.
1016 */
1017 void workshop_set_option_first(char *name, char *value)
1018 {
1019 /* Currently value can only be on/off. This may change later (for
1020 * example to set an option like "balloon evaluate delay", but
1021 * for now just convert it into a boolean */
1022 Boolean on = !strcmp(value, "on");
1023
1024 if (!strcmp(name, "workshopkeys")) {
1025 workshop_hotkeys(on);
1026 } else if (!strcmp(name, "savefiles")) {
1027 save_files = on;
1028 } else if (!strcmp(name, "balloon")) {
1029 workshop_balloon_mode(on);
1030 } else if (!strcmp(name, "balloondelay")) {
1031 int delay = atoi(value);
1032 /* Should I validate the number here?? */
1033 workshop_balloon_delay(delay);
1034 } else {
1035 /* Let editor interpret it */
1036 workshop_set_option(name, value);
1037 }
1038 }
1039
1040
1041
1042 /*
1043 * Send information to eserve on certain editor events
1044 * You must make sure these are called when necessary
1045 */
1046
1047 void workshop_file_closed(char *filename)
1048 {
1049 char buffer[2*MAXPATHLEN];
1050 sprintf(buffer, NOCATGETS("deletedFile %s\n"), filename);
1051 write(sd, buffer, strlen(buffer));
1052 }
1053
1054 void workshop_file_closed_lineno(char *filename, int lineno)
1055 {
1056 char buffer[2*MAXPATHLEN];
1057 sprintf(buffer, NOCATGETS("deletedFile %s %d\n"), filename, lineno);
1058 write(sd, buffer, strlen(buffer));
1059 }
1060
1061 void workshop_file_opened(char *filename, int readOnly)
1062 {
1063 char buffer[2*MAXPATHLEN];
1064 sprintf(buffer, NOCATGETS("loadedFile %s %d\n"), filename, readOnly);
1065 write(sd, buffer, strlen(buffer));
1066 }
1067
1068
1069 void workshop_file_saved(char *filename)
1070 {
1071 char buffer[2*MAXPATHLEN];
1072 sprintf(buffer, NOCATGETS("savedFile %s\n"), filename);
1073 write(sd, buffer, strlen(buffer));
1074
1075 /* Let editor report any moved marks that the eserve client
1076 * should deal with (for example, moving location-based breakpoints) */
1077 workshop_moved_marks(filename);
1078 }
1079
1080 void workshop_move_mark(char *filename, int markId, int newLineno)
1081 {
1082 char buffer[2*MAXPATHLEN];
1083 sprintf(buffer, NOCATGETS("moveMark %s %d %d\n"), filename, markId, newLineno);
1084 write(sd, buffer, strlen(buffer));
1085 }
1086
1087 void workshop_file_modified(char *filename)
1088 {
1089 char buffer[2*MAXPATHLEN];
1090 sprintf(buffer, NOCATGETS("modifiedFile %s\n"), filename);
1091 write(sd, buffer, strlen(buffer));
1092 }
1093
1094 void workshop_frame_moved(int new_x, int new_y, int new_w, int new_h)
1095 {
1096 char buffer[200];
1097
1098 if (sd >= 0)
1099 {
1100 sprintf(buffer, NOCATGETS("frameAt %d %d %d %d\n"),
1101 new_x, new_y, new_w, new_h);
1102 write(sd, buffer, strlen(buffer));
1103 }
1104 }
1105
1106 /* A button in the toolbar has been pushed.
1107 * Clientdata is a pointer used by the editor code to figure out the
1108 * positions for this toolbar (probably by storing a window pointer,
1109 * and then fetching the current buffer for that window and looking up
1110 * cursor and selection positions etc.) */
1111 void workshop_perform_verb(char *verb, void *clientData)
1112 {
1113 char *filename;
1114 int curLine;
1115 int curCol;
1116 int selStartLine;
1117 int selStartCol;
1118 int selEndLine;
1119 int selEndCol;
1120 int selLength;
1121 char *selection;
1122
1123 char buf[2*MAXPATHLEN];
1124 /* Later: needsFilePos indicates whether or not we need to fetch all this
1125 * info for this verb... for now, however, it looks as if
1126 * eserve parsing routines depend on it always being present */
1127
1128 if (workshop_get_positions(clientData,
1129 &filename,
1130 &curLine,
1131 &curCol,
1132 &selStartLine,
1133 &selStartCol,
1134 &selEndLine,
1135 &selEndCol,
1136 &selLength,
1137 &selection)) {
1138 if (selection == NULL) {
1139 selection = NOCATGETS("");
1140 }
1141
1142 /* Should I save the files??? This is currently done by checking
1143 if the verb is one of a few recognized ones. Later we can pass
1144 this list from eserve to the editor (it's currently hardcoded in
1145 vi and emacs as well). */
1146 if (save_files) {
1147 if (!strcmp(verb, "build.build") || !strcmp(verb, "build.build-file") ||
1148 !strcmp(verb, "debug.fix") || !strcmp(verb, "debug.fix-all")) {
1149 workshop_save_files();
1150 }
1151 }
1152
1153 sprintf(buf, NOCATGETS("toolVerb %s %s %d,%d %d,%d %d,%d %d %s\n"),
1154 verb,
1155 filename,
1156 curLine, curCol,
1157 selStartLine, selStartCol,
1158 selEndLine, selEndCol,
1159 selLength,
1160 selection);
1161 write(sd, buf, strlen(buf));
1162 if (*selection) {
1163 free(selection);
1164 }
1165 }
1166 }
1167
1168 /* Send a message to eserve */
1169 void workshop_send_message(char *buf)
1170 {
1171 write(sd, buf, strlen(buf));
1172 }
1173
1174 /* Some methods, like currentFile, cursorPos, etc. are missing here.
1175 * But it looks like these are used for NoHands testing only so we
1176 * won't bother requiring editors to implement these
1177 */
1178
1179
1180 #ifdef DEBUG
1181
1182 void
1183 pldebug(
1184 char *fmt, /* a printf style format line */
1185 ...)
1186 {
1187 va_list ap;
1188
1189 if (dfd != NULL) {
1190 va_start(ap, fmt);
1191 vfprintf(dfd, fmt, ap);
1192 va_end(ap);
1193 fflush(dfd);
1194 }
1195
1196 } /* end pldebug */
1197
1198 #endif