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