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