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