home *** CD-ROM | disk | FTP | other *** search
/ WordPerfect for Linux Bible / WP4LinuxBible.iso / sdk / wpx / code / wt / client / wtclient.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-06-25  |  46.4 KB  |  1,688 lines

  1. /* SOURCE FILE *****************************************************
  2.  * WTCLIENT.C  -  Writing Tools API Client Sample Application
  3.  *******************************************************************
  4.  * Copyright (C) 1993 WordPerfect Corp., All Rights Reserved
  5.  *******************************************************************/
  6.  
  7. #include "wtclient.h" 
  8. #include "wtclient.xbm"     /* icon bitmap */
  9.  
  10. #include <X11/cursorfont.h>
  11. #include <Xm/Xm.h>
  12. #include <Xm/CascadeB.h>
  13. #include <Xm/CutPaste.h>
  14. #include <Xm/Form.h>
  15. #include <Xm/Protocols.h>
  16. #include <Xm/PushB.h>
  17. #include <Xm/RowColumn.h>
  18. #include <Xm/Separator.h>
  19. #include <Xm/Text.h>
  20. #include <unistd.h>
  21.  
  22. #include <wtapi.h>
  23. #include <wtcomm.h>
  24.  
  25. #define WT_INVOKE_MODE 0
  26.  
  27. /*----------------------------------------------------------
  28.         External Variables
  29. ------------------------------------------------------------*/
  30. extern WTCOMM commHandle;        /* conversation/comm. handle */
  31. extern BOOL Dirty;                /* has the file been modified */
  32.  
  33. /*----------------------------------------------------------
  34.         External Functions
  35. ------------------------------------------------------------*/
  36. void quitTool(char *);
  37. void FileMenuProc(Widget, CL_FILE);
  38. XmString StringCreate(char *);
  39. void NormalCursor(Widget);
  40. void WaitCursor(Widget);
  41. BOOL MsgBox(MSGBOX, Widget parent, char *, char *);
  42. void ReportError(char *);
  43. WTCOMM WtConnect(Widget, char *);
  44. void WtDisconnect(WTCOMM);
  45. /* char *getcwd(char *, int); */
  46. char *getenv(char *);
  47. WTSTATUS WTCInitSend(WTCOMM comm, WTCINITP msg);
  48.  
  49. /*----------------------------------------------------------
  50.         Menu Definitions
  51. ------------------------------------------------------------*/
  52. /* File menu items */
  53. static char *ClFileStr[] = {
  54.     "New", "Open...", "Save", "Save As...", "Print", "Exit", 0
  55. };
  56. static char ClFileMne[] = {
  57.     'N', 'O', 'S', 'A', 'P', 'x', 0
  58. };
  59.  
  60. /* Edit menu items */
  61. static char *ClEditStr[] = {
  62.     "Undo", "Cut", "Copy", "Paste", 0
  63. };
  64. static char ClEditMne[] = {
  65.     'U', 't', 'C', 'P', 0
  66. };
  67.  
  68. /* Help menu items */
  69. static char *ClHelpStr[] = {
  70.     "About WTClient...", 0
  71. };
  72. static char ClHelpMne[] = {
  73.     'A', 0
  74. };
  75.  
  76. /*----------------------------------------------------------
  77.         Internal Variables
  78. ------------------------------------------------------------*/
  79. WTPTR wtsess;
  80. Widget Client;
  81. XmFontList FontList;
  82. static Cursor waitcursor = (Cursor) 0;
  83. static Cursor normalcursor = (Cursor) 0;
  84. static Pixmap Icon;            /* Icon for sample client application */
  85. static Widget Text;            /* Text widget for client */
  86. static Widget editButtons[CL_EDIT_CNT];        /* Edit menu buttons */
  87. static Widget toolButtons[MAXTOOLS];        /* Tool menu buttons */
  88. static TOOLRECORD toolRecords[MAXTOOLS];    /* writing tool records */
  89. static int toolCount;                        /* number of tools on menu */
  90. static char *prevText;                        /* text before previous edit */
  91. static XmTextPosition    prevSelLeft = 0xff,    /* previous selection */
  92.                         prevSelRight = 0;
  93. static BOOL toolRunning = False;            /* is a tool currently running? */ 
  94. static WTCONTEXT sessionMem;
  95.  
  96. /*----------------------------------------------------------
  97.         Internal Functions
  98. ------------------------------------------------------------*/
  99. char *GetBuffer();
  100. WTSTATUS GetText(WTUNIT, WTPOS, WTCOUNT, WTUNIT, WTPOS, WTCOUNT, WTTBQTYPE,
  101.     WTSIZE, WTTBQTYPE *, WTBUFP, WTSIZE *);
  102. unsigned SelectedLen(void);
  103. WTSTATUS GoToPos(WTPOS, WTCOUNT, WTPOS, WTCOUNT, unsigned *);
  104. WTCOUNT ReplaceText(WTCOUNT, WTSIZE, WTBUFP);
  105. unsigned FindCurrBlock(unsigned);
  106. void NativeToWord(WTBUFP, WTSIZE *);
  107. void WordToNative(WTBUFP, WTSIZE *);
  108.  
  109. static void ClientInit(Widget);
  110. static void ClientDestroyCB(Widget, XtPointer, XtPointer);
  111. static void ClientExit();
  112. static void FileCB(Widget, XtPointer, XtPointer);
  113. static void EditCB(Widget, XtPointer, XmAnyCallbackStruct *);
  114. static void SetEditButtons(Widget, XtPointer, XtPointer);
  115. static void ToolsCB(Widget, XtPointer, XtPointer);
  116. static void HelpCB(Widget, XtPointer, XtPointer);
  117. static void TextCB(Widget, XtPointer, XtPointer);
  118. static void free_cursors(Widget);
  119. static void SetToolsMenu(BOOL);
  120. static BOOL launchTool(int);
  121. static BOOL ClInvokeWritingTool(int, short);
  122. static void SetCaretPos(int);
  123. static void updateDeleteCount(unsigned, unsigned, unsigned);
  124. static void updateInsertCount(unsigned, unsigned);
  125.  
  126. /*COMMENT***************************************************
  127. ;main
  128. Title:    WTAPI Sample Client Main Function
  129. In:        argc, argv
  130. Out:    none
  131. Xin:    none
  132. Xout:    none
  133. Return:    exit status
  134. Notes:
  135. ***********************************************************/
  136. main(int argc, char *argv[])
  137. {
  138.     XtAppContext appcontext;
  139.     Widget toplevel;
  140.  
  141.     toplevel = XtVaAppInitialize(&appcontext, "WTClient",
  142.         NULL, 0, &argc, (char **)argv, NULL,
  143.         XmNallowShellResize, True,
  144.         NULL);
  145.     ClientInit(toplevel);
  146.     XtAppMainLoop(appcontext);
  147. } /* main */
  148.  
  149. /*COMMENT***************************************************
  150. ;ClientInit
  151. Title:    Initialize WTAPI Sample Client Application
  152. In:        toplevel - toplevel widget of application
  153. Out:    none
  154. Xin:    none
  155. Xout:    none
  156. Return:    none
  157. Notes:
  158. ***********************************************************/
  159. static void ClientInit(Widget toplevel)
  160. {
  161.     Arg args[20];
  162.     Cardinal argcnt;
  163.     Atom deleteWindow;        /* for XmAddWMProtocol... */
  164.     XmString xmstr;            /* Motif compound string */
  165.     Widget    form,            /* main Client form */
  166.             menubar,        /* menu bar */
  167.             button,            /* cascade or push button */
  168.             pd,                /* pulldown menu */
  169.             separator;        /* separator */
  170.     CL_FILE file;            /* loop variable */
  171.     CL_EDIT edit;            /* loop variable */
  172.     CL_HELP help;            /* loop variable */
  173.     int tool;                /* loop variable */
  174.     XFontStruct *font;
  175.  
  176.     /*
  177.      * Set up icon and dialog destroy callbacks for main Client window.
  178.      */
  179.     Client = toplevel;
  180.     Icon = XCreateBitmapFromData(XtDisplay(toplevel),
  181.         RootWindowOfScreen(XtScreen(toplevel)),
  182.         wtclient_bits, wtclient_width, wtclient_height);
  183.     XtVaSetValues(Client,
  184.         XtNtitle, "WTAPI Sample Client Application",
  185.         XmNiconName, "WTClient",
  186.         XmNiconPixmap, Icon,
  187.         XmNdeleteResponse, XmDO_NOTHING,
  188.         NULL);
  189.     XtAddCallback(Client, XmNdestroyCallback, ClientDestroyCB, 0);
  190.     deleteWindow = XmInternAtom(XtDisplay(Client), "WM_DELETE_WINDOW", False);
  191.     XmAddWMProtocols(Client, &deleteWindow, 1);
  192.     XmAddWMProtocolCallback(Client, deleteWindow, ClientDestroyCB, 0);
  193.     /*
  194.      * Create the main Client form.
  195.      */
  196.     FontList = NULL;
  197.     if (font = XLoadQueryFont(XtDisplay(Client),
  198.         "-adobe-helvetica-medium-r-normal--14-100-100-100-p-76-iso8859-1"))
  199.     {
  200.         FontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
  201.     }
  202.     form = XtVaCreateManagedWidget("clform", xmFormWidgetClass, Client,
  203.         XmNfontList, FontList,
  204.         NULL);
  205.     /*
  206.      * Create the menu bar.
  207.      */
  208.     menubar = XmCreateMenuBar(form, "clmenu", 0, 0);
  209.     /*
  210.      * Create the File menu.
  211.      */
  212.     button = XtVaCreateManagedWidget("clfile",
  213.         xmCascadeButtonWidgetClass, menubar,
  214.         XmNlabelString, xmstr = StringCreate("File"),
  215.         XmNmnemonic, 'F',
  216.         XmNsubMenuId, pd = XmCreatePulldownMenu(menubar, "clFilePD", 0, 0),
  217.         XmNfontList, FontList,
  218.         NULL);
  219.     XmStringFree(xmstr);
  220.     for (file = CL_FILE_NEW; file < CL_FILE_CNT; file++) {
  221.         if (file == CL_FILE_EXIT) {
  222.             separator = XtVaCreateManagedWidget("sep",
  223.                 xmSeparatorWidgetClass, pd,
  224.                 NULL);
  225.         }
  226.         button = XtVaCreateManagedWidget(ClFileStr[file],
  227.             xmPushButtonWidgetClass, pd,
  228.             XmNlabelString, xmstr = StringCreate(ClFileStr[file]),
  229.             XmNmnemonic, ClFileMne[file],
  230.             XmNvisibleWhenOff, True,
  231.             XmNsensitive, True,
  232.             XmNfontList, FontList,
  233.             NULL);
  234.         XmStringFree(xmstr);
  235.         XtAddCallback(button, XmNactivateCallback, FileCB, (XtPointer)(int)file);
  236.     }
  237.     /*
  238.      * Create the Edit menu.
  239.      */
  240.     button = XtVaCreateManagedWidget("cledit",
  241.         xmCascadeButtonWidgetClass, menubar,
  242.         XmNlabelString, xmstr = StringCreate("Edit"),
  243.         XmNmnemonic, 'E',
  244.         XmNsubMenuId, pd = XmCreatePulldownMenu(menubar, "clEditPD", 0, 0),
  245.         XmNfontList, FontList,
  246.         NULL);
  247.     XmStringFree(xmstr);
  248.     XtAddCallback(button, XmNcascadingCallback, SetEditButtons, 0);
  249.     for (edit = CL_EDIT_UNDO; edit < CL_EDIT_CNT; edit++) {
  250.         editButtons[edit] = button = XtVaCreateManagedWidget(ClEditStr[edit],
  251.             xmPushButtonWidgetClass, pd,
  252.             XmNlabelString, xmstr = StringCreate(ClEditStr[edit]),
  253.             XmNmnemonic, ClEditMne[edit],
  254.             XmNvisibleWhenOff, True,
  255.             XmNsensitive, True,
  256.             XmNfontList, FontList,
  257.             NULL);
  258.         XmStringFree(xmstr);
  259.         XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)EditCB, (XtPointer)(int)edit);
  260.         if (edit == CL_EDIT_UNDO) {
  261.             separator = XtVaCreateManagedWidget("sep",
  262.                 xmSeparatorWidgetClass, pd,
  263.                 NULL);
  264.         }
  265.     }
  266.     /*
  267.      * Create the Tools menu.
  268.      */
  269.     button = XtVaCreateManagedWidget("cltools",
  270.         xmCascadeButtonWidgetClass, menubar,
  271.         XmNlabelString, xmstr = StringCreate("Tools"),
  272.         XmNmnemonic, 'T',
  273.         XmNsubMenuId, pd = XmCreatePulldownMenu(menubar, "clToolsPD", 0, 0),
  274.         XmNfontList, FontList,
  275.         NULL);
  276.     XmStringFree(xmstr);
  277.     if (GetToolsInfo(toolRecords, &toolCount)) {
  278.         for (tool = 0; tool < toolCount; tool++) {
  279.             toolButtons[tool] =
  280.                 button = XtVaCreateManagedWidget(toolRecords[tool].menu,
  281.                 xmPushButtonWidgetClass, pd,
  282.                 XmNlabelString, xmstr = StringCreate(toolRecords[tool].menu),
  283.                 XmNmnemonic, toolRecords[tool].mne,
  284.                 XmNvisibleWhenOff, True,
  285.                 XmNsensitive, True,
  286.                 XmNfontList, FontList,
  287.                 NULL);
  288.             XmStringFree(xmstr);
  289.             XtAddCallback(button, XmNactivateCallback, ToolsCB, (XtPointer)tool);
  290.         }
  291.     }
  292.     /*
  293.      * Create the Help menu.
  294.      */
  295.     button = XtVaCreateManagedWidget("clhelp",
  296.         xmCascadeButtonWidgetClass, menubar,
  297.         XmNlabelString, xmstr = StringCreate("Help"),
  298.         XmNmnemonic, 'H',
  299.         XmNsubMenuId, pd = XmCreatePulldownMenu(menubar, "clHelpPD", 0, 0),
  300.         XmNfontList, FontList,
  301.         NULL);
  302.     XtVaSetValues(menubar,
  303.         XmNmenuHelpWidget, button,
  304.         NULL);
  305.     XmStringFree(xmstr);
  306.     for (help = CL_HELP_ABOUT; help < CL_HELP_CNT; help++) {
  307.         button = XtVaCreateManagedWidget(ClHelpStr[help],
  308.             xmPushButtonWidgetClass, pd,
  309.             XmNlabelString, xmstr = StringCreate(ClHelpStr[help]),
  310.             XmNmnemonic, ClHelpMne[help],
  311.             XmNvisibleWhenOff, True,
  312.             XmNsensitive, True,
  313.             XmNfontList, FontList,
  314.             NULL);
  315.         XmStringFree(xmstr);
  316.         XtAddCallback(button, XmNactivateCallback, HelpCB, (XtPointer)(int)help);
  317.     }
  318.     XtVaSetValues(menubar,
  319.         XmNtopAttachment, XmATTACH_FORM,
  320.         XmNtopOffset, 0,
  321.         XmNleftAttachment, XmATTACH_FORM,
  322.         XmNleftOffset, 0,
  323.         XmNrightAttachment, XmATTACH_FORM,
  324.         XmNrightOffset, 0,
  325.         NULL);
  326.     XtManageChild(menubar);
  327.     /*
  328.      * Create the text box.
  329.      */
  330.     argcnt = 0;
  331.     XtSetArg(args[argcnt], XmNeditMode, XmMULTI_LINE_EDIT); argcnt++;
  332.     XtSetArg(args[argcnt], XmNcolumns, 60); argcnt++;
  333.     XtSetArg(args[argcnt], XmNrows, 15); argcnt++;
  334.     XtSetArg(args[argcnt], XmNbackground,
  335.         WhitePixelOfScreen(XtScreen(form))); argcnt++;
  336.     XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_WIDGET); argcnt++;
  337.     XtSetArg(args[argcnt], XmNtopWidget, menubar); argcnt++;
  338.     XtSetArg(args[argcnt], XmNtopOffset, 0); argcnt++;
  339.     XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
  340.     XtSetArg(args[argcnt], XmNleftOffset, 0); argcnt++;
  341.     XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
  342.     XtSetArg(args[argcnt], XmNrightOffset, 0); argcnt++;
  343.     XtSetArg(args[argcnt], XmNbottomAttachment, XmATTACH_FORM); argcnt++;
  344.     XtSetArg(args[argcnt], XmNbottomOffset, 0); argcnt++;
  345.     XtSetArg(args[argcnt], XmNfontList, FontList); argcnt ++;
  346.     Text = XmCreateScrolledText(form, "cltext", args, argcnt);
  347.     XtAddCallback(Text, XmNvalueChangedCallback, TextCB, 0);
  348.     XtUnmanageChild(XtParent(Text));
  349.     XtManageChild(Text);
  350.     XtManageChild(XtParent(Text));
  351.     /*
  352.      * Pop up the Client dialog.
  353.      */
  354.     XtPopup(Client, XtGrabNone);
  355. } /* ClientInit */
  356.  
  357. /*COMMENT***************************************************
  358. ;ClientDestroyCB
  359. Title:    Handle destroy of Client dialog.
  360. In:        none
  361. Out:    none
  362. Xin:    none
  363. Xout:    none
  364. Return:    none
  365. Notes:
  366. ***********************************************************/
  367. static void ClientDestroyCB(Widget w, XtPointer cldata, XtPointer cbdata)
  368. {
  369.     if (toolRunning) {
  370.         WtDisconnect(commHandle);
  371.     }
  372.     if (FontList) {
  373.         XmFontListFree(FontList);
  374.     }
  375.     if (Icon) {
  376.         XFreePixmap(XtDisplayOfObject(Client), Icon);    /* free pixmap */
  377.     }
  378.     free_cursors(Client);
  379.     Client = 0;
  380.     ClientExit();        /* clean up and exit client */
  381. } /* ClientDestroyCB */
  382.  
  383. /*COMMENT***************************************************
  384. ;ClientExit
  385. Title:    Clean up and exit WTAPI Sample Application
  386. In:        none
  387. Out:    none
  388. Xin:    none
  389. Xout:    none
  390. Return:    none
  391. Notes:
  392. ***********************************************************/
  393. static void ClientExit()
  394. {
  395.     exit(0);
  396. } /* ClientExit */
  397.  
  398. /*COMMENT***************************************************
  399. ;StringCreate
  400. Title:    Initialize WTAPI Sample Application
  401. In:        toplevel - toplevel widget of application
  402. Out:    none
  403. Xin:    none
  404. Xout:    none
  405. Return:    none
  406. Notes:
  407. ***********************************************************/
  408. XmString StringCreate(char *str)
  409. {
  410.     return(XmStringCreateLtoR(str, XmSTRING_DEFAULT_CHARSET));
  411. } /* StringCreate */
  412.  
  413. /*COMMENT***************************************************
  414. ;FileCB
  415. Title:    File menu actions.
  416. In:        cldata - File action to perform
  417. Out:    none
  418. Xin:    Client - toplevel Client widget
  419. Xout:    none
  420. Return:    none
  421. Notes:
  422. ***********************************************************/
  423. static void FileCB(Widget w, XtPointer cldata, XtPointer cbdata)
  424. {
  425.     CL_FILE file = (CL_FILE)(int)cldata;    /* which file action to perform */
  426.     FileMenuProc(Client, file);
  427. } /* FileCB */
  428.  
  429. /*COMMENT***************************************************
  430. ;EditCB
  431. Title:    File menu actions.
  432. In:        cldata - Edit action to perform
  433. Out:    none
  434. Xin:    Client - toplevel Client widget
  435. Xout:    none
  436. Return:    none
  437. Notes:
  438. ***********************************************************/
  439. static void EditCB(Widget w, XtPointer cldata, XmAnyCallbackStruct *cbdata)
  440. {
  441.     CL_EDIT edit = (CL_EDIT)(int)cldata;    /* which edit action to perform */
  442.     Time time = cbdata->event->xbutton.time;
  443.     char *txt, *prevtxt = 0;
  444.     XmTextPosition prevleftsel = 0, prevrightsel = 0;
  445.     int status = 0;            /* status of call to clipboard routines */
  446.     int len = 0;            /* length of text in clipboard */
  447.     BOOL changed = FALSE;    /* did text change? */
  448.  
  449.     /*
  450.      * If UNDO then we need to save the previous text and selection before
  451.      * we update them to be the current text and selection.
  452.      */
  453.     if (edit == CL_EDIT_UNDO) {
  454.         if (prevText) {
  455.             prevtxt = prevText;    /* use the same pointer */
  456.             prevText = 0;        /* we'll allocate more space for this later */
  457.         }
  458.         prevleftsel = prevSelLeft;
  459.         prevrightsel = prevSelRight;
  460.     }
  461.     /*
  462.      * Save the current text and selection as the previous text and selection
  463.      * for possible use with UNDO later.
  464.      */
  465.     if (prevText) {
  466.         XtFree(prevText);
  467.     }
  468.     prevText = XmTextGetString(Text);
  469.     if (!XmTextGetSelectionPosition(Text, &prevSelLeft, &prevSelRight)) {
  470.         prevSelLeft = prevSelRight = 0;
  471.     }
  472.     /*
  473.      * Perform the Edit action.
  474.      */
  475.     switch (edit) {
  476.         case CL_EDIT_UNDO:        /* Undo */
  477.             if (prevtxt) {
  478.                 XmTextSetString(Text, prevtxt);
  479.                 XtFree(prevtxt);
  480.                 if (prevleftsel < prevrightsel) {
  481.                     XmTextSetSelection(Text, prevleftsel, prevrightsel, time);
  482.                 }
  483.             } else {
  484.                 XmTextSetString(Text, "");
  485.                 XmTextClearSelection(Text, time);
  486.             }
  487.             break;
  488.         case CL_EDIT_CUT:        /* Cut */
  489.             if (!XmTextCut(Text, time)) {
  490.                 XBell(XtDisplay(Text), 100);    /* beep */
  491.             }
  492.             break;
  493.         case CL_EDIT_COPY:        /* Copy */
  494.             if (!XmTextCopy(Text, time)) {
  495.                 XBell(XtDisplay(Text), 100);    /* beep */
  496.             }
  497.             break;
  498.         case CL_EDIT_PASTE:        /* Paste */
  499.             if (!XmTextPaste(Text)) {
  500.                 XBell(XtDisplay(Text), 100);    /* beep */
  501.             }
  502.             break;
  503.         default:                /* unrecognized action */
  504.             break;                /* do nothing */
  505.     }
  506. } /* EditCB */
  507.  
  508. /*COMMENT***************************************************
  509. ;SetEditButtons
  510. Desc:    Set the sensitivity on the Edit buttons.
  511. In:        none
  512. Out:    none
  513. Xin:    none
  514. Xout:    none
  515. Return:    none
  516. ***********************************************************/
  517. static void SetEditButtons(Widget w, XtPointer cldata, XtPointer cbdata)
  518. {
  519.     char *sel;            /* selected text */
  520.     int status = 0;        /* status of call to clipboard routines */
  521.     int length = 0;        /* length of text in clipboard */
  522.     
  523.     /*
  524.      * If no edits yet then turn Undo off.
  525.      */
  526.     if (prevSelLeft == 0xff && prevSelRight == 0) {
  527.         XtSetSensitive(editButtons[CL_EDIT_UNDO], False);
  528.     } else {
  529.         XtSetSensitive(editButtons[CL_EDIT_UNDO], True);
  530.     }
  531.     /*
  532.      * If there is selected text in the box then turn on Cut and Copy.
  533.      */
  534.     sel = XmTextGetSelection(Text);
  535.     if (sel && *sel) {
  536.         XtSetSensitive(editButtons[CL_EDIT_CUT], True);
  537.         XtSetSensitive(editButtons[CL_EDIT_COPY], True);
  538.     } else {
  539.         XtSetSensitive(editButtons[CL_EDIT_CUT], False);
  540.         XtSetSensitive(editButtons[CL_EDIT_COPY], False);
  541.     }
  542.     if (sel) {
  543.         XtFree(sel);
  544.     }
  545.     /*
  546.      * Turn Paste on if there is text in the clipboard.
  547.      */
  548.     do {
  549.         status = XmClipboardInquireLength(XtDisplay(w), XtWindow(w),
  550.             "STRING", &length);
  551.     } while (status == ClipboardLocked);
  552.     if ((status != ClipboardNoData) && (length > 0)) {
  553.         XtSetSensitive(editButtons[CL_EDIT_PASTE], True);
  554.     } else {
  555.         XtSetSensitive(editButtons[CL_EDIT_PASTE], False);
  556.     }
  557. } /* SetEditButtons */
  558.  
  559. /*COMMENT***************************************************
  560. ;HelpCB
  561. Title:    Help menu actions.
  562. In:        cldata - Help action to perform
  563. Out:    none
  564. Xin:    Client - toplevel Client widget
  565. Xout:    none
  566. Return:    none
  567. Notes:
  568. ***********************************************************/
  569. static void HelpCB(Widget w, XtPointer cldata, XtPointer cbdata)
  570. {
  571.     CL_HELP help = (CL_HELP)(int)cldata;    /* which help action to perform */
  572.     switch (help) {
  573.         case CL_HELP_ABOUT:
  574.             MsgBox(MB_OK, Client, "About WTClient",
  575.               "Writing Tools API\nSample Client Application\n\nVersion 0.6");
  576.             break;
  577.         default:                /* unrecognized action */
  578.             break;                /* do nothing */
  579.     }
  580. } /* HelpCB */
  581.  
  582. /*COMMENT***************************************************
  583. ;ToolsCB
  584. Title:    Tools menu actions.
  585. In:        cldata - which Tool to invoke
  586. Out:    none
  587. Xin:    Client - toplevel Client widget
  588. Xout:    none
  589. Return:    none
  590. Notes:
  591. ***********************************************************/
  592. static void ToolsCB(Widget w, XtPointer cldata, XtPointer cbdata)
  593. {
  594.     int tool = (int)cldata;        /* which tool to invoke */
  595.     if (!launchTool(tool)) {
  596.         quitTool(0);
  597.     }
  598. } /* ToolsCB */
  599.  
  600. /*COMMENT********************************************************
  601. ;ReportError
  602. Title:    To report an error that has occurred while allocating memory for
  603.         the CD struct, locking the memory or while trying to load a
  604.         resource string.
  605. In:        wErrorType
  606. Out:    none
  607. Return:    none
  608. Notes:    
  609. *****************************************************************/
  610. void ReportError(char *message)
  611. {
  612.     MsgBox(MB_OK, Client, "WTClient - Error", message);
  613. } /* ReportError */
  614.  
  615. /*COMMENT********************************************************
  616. ;SetToolsMenu
  617. Title:    To report an error that has occurred while allocating memory for
  618.         the CD struct, locking the memory or while trying to load a
  619.         resource string.
  620. In:        wErrorType
  621. Out:    none
  622. Return:    none
  623. Notes:    
  624. *****************************************************************/
  625. static void SetToolsMenu(BOOL state)
  626. {
  627.     int tool;
  628.  
  629.     for (tool = 0; tool < toolCount; tool++) {
  630.         XtSetSensitive(toolButtons[tool], state);
  631.     }
  632. } /* SetToolsMenu */
  633.  
  634. /*COMMENT***************************************************
  635. ;launchTool
  636. Title:    Launch a writing tool
  637. In:        toolPos - tool position in tool record array
  638. Out:    none
  639. Xin:    none    
  640. Xout:    Dirty, hEditWnd
  641. Return:    NULL
  642. Notes:
  643. ***********************************************************/
  644. static BOOL launchTool(int tool)
  645. {
  646.     WTCINIT inmsg;
  647.     WTSTATUS status;
  648.     
  649.     if (!ClInvokeWritingTool(tool, WT_INVOKE_MODE)) {
  650.         return FALSE;
  651.     }
  652.     toolRunning = TRUE;
  653.     SetToolsMenu(FALSE);        /* disable tools menu */
  654.     wtsess = &sessionMem;
  655.     wtsess->lastBlock = -1;
  656.     wtsess->currBlock = -1;
  657.     wtsess->absPos = 0;        
  658.     
  659.     /* send WTC_INIT message */
  660.      inmsg.msgid = WTC_INIT;
  661.     inmsg.mode = WTM_NORMAL;
  662.     inmsg.version = WTAPI_VERSION;
  663.     inmsg.windowClient = XtWindow(Client);
  664.      status = WTCInitSend(commHandle, &inmsg);
  665.      if (status != WTS_OK) {
  666.         ReportError("Send of WTC_INIT from client to tool FAILED");
  667.     }
  668.     return TRUE;
  669. } /* launchTool */
  670.  
  671. /*COMMENT***************************************************
  672. ;quitTool
  673. Title:    Quit a writing tool
  674. In:        msg - output this message when quitting the tool
  675. Out:    none
  676. Xin:    none    
  677. Xout:    none
  678. Return:    NULL
  679. Notes:
  680. ***********************************************************/
  681. void quitTool(char *msg)
  682. {
  683.     if (toolRunning) {
  684.         WtDisconnect(commHandle);
  685.         toolRunning = FALSE;
  686.     }
  687.     commHandle = 0;
  688.     wtsess = 0x0;
  689.     SetToolsMenu(TRUE);        /* enable tools menu */
  690.     if (msg) {
  691.         ReportError(msg);
  692.     }
  693.     return;
  694. } /* quitTool */
  695.  
  696. /*COMMENT***************************************************
  697. ;GetText
  698. Title:    Get text to send to tool
  699. In:        startUnit - text unit to position from
  700.         startPos - type of positioning
  701.         startLoc - distance to desired position
  702.         endUnit - text unit to end at
  703.         endPos - type of positioning
  704.         endLoc - distance to desired position
  705.         type - request type ie. WTB_INIT, WTB_RESUME
  706.         maxSize - maximum size of return buffer
  707.         buffer - return buffer
  708. Out:    endType - end of buffer condition
  709. Return:    size of text in buffer
  710. Notes:
  711. ***********************************************************/
  712. WTSTATUS GetText(WTUNIT startUnit, WTPOS startPos, WTCOUNT startLoc, 
  713.     WTUNIT endUnit, WTPOS endPos, WTCOUNT endLoc, 
  714.     WTTBQTYPE type, WTSIZE maxSize, WTTBQTYPE *endType,
  715.     WTBUFP buffer, WTSIZE *len)
  716. {
  717.     XmTextPosition start, end;    /* start/end of text to get */
  718.     XmTextPosition left, right;    /* left/right position of selection */
  719.     char *editBuffer;            /* pointer to edit buffer */
  720.     char c;
  721.  
  722.     editBuffer = GetBuffer();    /* get edit buffer */
  723.     start = 0;                    /* default - start of buffer */
  724.     end = strlen(editBuffer);    /* default - end of buffer */
  725.     left = right = 0;            /* default - no selection */
  726.  
  727.     if (type == WTB_RESUME) {
  728.         start = wtsess->textInfo[wtsess->lastBlock].offset +
  729.                                     wtsess->textInfo[wtsess->lastBlock].len;
  730.     } else {
  731.         switch (startUnit) {       /* find starting position */
  732.             case WTU_SELECTION:
  733.                 if (XmTextGetSelectionPosition(Text, &left, &right) &&
  734.                     (left < right))
  735.                 {
  736.                     start = left;
  737.                 }
  738.                 break;
  739.             case WTU_DOCUMENT:
  740.                 start = 0;
  741.                 break;
  742.             case WTU_CHAR:
  743.                 switch(startPos) {
  744.                     case WTP_BEG:
  745.                         start = 0 + startLoc;
  746.                         break;
  747.                     case WTP_REL:
  748.                         start = wtsess->absPos + startLoc;
  749.                         break;
  750.                     case WTP_END:
  751.                         start = strlen(editBuffer) - startLoc;
  752.                         break;
  753.                 }
  754.                 break;
  755.             case WTU_WORD:
  756.                 /*
  757.                  * Back up from current position to first non-alphanumeric.
  758.                  * Start with character in front of insertion position.
  759.                  */
  760.                 start = XmTextGetInsertionPosition(Text);
  761.                 if (start > 0) {
  762.                     start--;
  763.                 }
  764.                 while (start > 0) {
  765.                     c = editBuffer[start];
  766.                     if (((c >= 'a') && (c <= 'z')) ||
  767.                         ((c >= 'A') && (c <= 'Z')) ||
  768.                         ((c >= '0') && (c <= '9')) ||
  769.                         (c == '_'))
  770.                     {
  771.                         start--;
  772.                     } else {
  773.                         break;
  774.                     }
  775.                 }
  776.                 /*
  777.                  * If start is on an alphanumeric then we are done.
  778.                  * If start is not on an alphanumeric then search forward
  779.                  * in the buffer for an alphanumeric or end of buffer.
  780.                  */
  781.                 while (start < strlen(editBuffer)) {
  782.                     c = editBuffer[start];
  783.                     if (((c >= 'a') && (c <= 'z')) ||
  784.                         ((c >= 'A') && (c <= 'Z')) ||
  785.                         ((c >= '0') && (c <= '9')) ||
  786.                         (c == '_'))
  787.                     {
  788.                         break;    /* found an alphanumeric */
  789.                     } else {
  790.                         start++;
  791.                     }
  792.                 }
  793.                 break;
  794.             default:
  795.                 return WTS_NOTSUPPORTED;
  796.         }
  797.     }
  798.  
  799.     switch (endUnit) {
  800.         case WTU_SELECTION:
  801.             if (XmTextGetSelectionPosition(Text, &left, &right) &&
  802.                 (left < right))
  803.             {
  804.                 end = right;
  805.             }
  806.             break;
  807.         case WTU_DOCUMENT:
  808.             end = strlen(editBuffer);
  809.             break;
  810.         case WTU_CHAR:
  811.             switch(endPos) {
  812.                 case WTP_BEG:
  813.                     end = 0 + endLoc;
  814.                     break;
  815.                 case WTP_REL:
  816.                     end = wtsess->absPos + endLoc;
  817.                     break;
  818.                 case WTP_END:
  819.                     end = strlen(editBuffer) - endLoc;
  820.                     break;
  821.             }
  822.             break;
  823.         case WTU_WORD:
  824.             /*
  825.              * Go forward from start to first non-alphanumeric.
  826.              */
  827.             end = start;
  828.             while (end < strlen(editBuffer)) {
  829.                 c = editBuffer[end];
  830.                 if (((c >= 'a') && (c <= 'z')) ||
  831.                     ((c >= 'A') && (c <= 'Z')) ||
  832.                     ((c >= '0') && (c <= '9')) ||
  833.                     (c == '_'))
  834.                 {
  835.                     end++;
  836.                 } else {
  837.                     break;
  838.                 }
  839.             }
  840.             break;
  841.         default:
  842.             return WTS_NOTSUPPORTED;
  843.     }
  844.  
  845.     if ((start > end) || (start < 0) || (end < 0)) {
  846.         return WTS_BADREQUEST;
  847.     }
  848.     if ((end - start) > maxSize) {    /* requested length greater than max */
  849.         *endType = WTE_FULL;
  850.         end = start + maxSize;        /* back up to beginning of word */
  851.         while (end > start) {
  852.             c = editBuffer[end];
  853.             if (((c >= 'a') && (c <= 'z')) ||
  854.                 ((c >= 'A') && (c <= 'Z')) ||
  855.                 ((c >= '0') && (c <= '9')) ||
  856.                 (c == '_'))
  857.             {
  858.                 end--;
  859.             } else {
  860.                 break;
  861.             }
  862.         }
  863.     } else {
  864.         *endType = WTE_ENDQUERY;    /* text fit in buffer */     
  865.     }
  866.     *len = end - start;            /* len of requested text */
  867.     
  868.     memcpy(buffer, editBuffer+start, (unsigned)*len);
  869.     free(editBuffer);
  870.  
  871.     wtsess->absPos = (unsigned)start + (unsigned)*len;
  872.  
  873.     /* update blockinfo */
  874.     if (*len == 0) {    /* there is no more text */
  875.         return WTS_BADREQUEST;
  876.     }
  877.     if (type == WTB_RESUME) {
  878.         if (wtsess->lastBlock >= MAXBLOCKS) {
  879.             *endType = WTE_ERROR;
  880.             *len = 0;
  881.              return WTS_CLIENTERROR;
  882.         }
  883.         wtsess->lastBlock = wtsess->currBlock = wtsess->lastBlock + 1;
  884.     } else {
  885.         wtsess->tounit = endUnit;
  886.         wtsess->topos = endPos;
  887.         wtsess->toloc = endLoc; 
  888.         wtsess->lastBlock = wtsess->currBlock = 0;    /* initial block */
  889.     }
  890.     wtsess->textInfo[wtsess->lastBlock].offset = (unsigned)start;
  891.     wtsess->textInfo[wtsess->lastBlock].len = (unsigned)*len;
  892.     return WTS_OK;
  893. } /* GetText */
  894.  
  895. /*COMMENT***************************************************
  896. ;SelectedLen
  897. Title:    Checks if selection is on
  898. In:        none
  899. Out:    none
  900. Return:    0 if selection is off, else length of selection
  901. Notes:
  902. ***********************************************************/
  903. unsigned SelectedLen(void)
  904. {
  905.     XmTextPosition start, end;
  906.  
  907.     if (XmTextGetSelectionPosition(Text, &start, &end) &&
  908.         (start < end))
  909.     {
  910.         return (end - start);
  911.     }
  912.     return 0;
  913. } /* SelectedLen */
  914.  
  915. /*COMMENT***************************************************
  916. ;GoToPos
  917. Title:    Goes to specified position in edit buffer
  918. In:        blockPos - block position
  919.         blockOffset - block offset
  920.         offsetPos - offset position
  921.         offset - offset
  922. Out:    position - new position
  923. Return:    WTS_OK or WTS_BADREQUEST
  924. Notes:
  925. ***********************************************************/
  926. WTSTATUS GoToPos(WTPOS blockPos, WTCOUNT blockOffset,
  927.     WTPOS offsetPos, WTCOUNT offset, unsigned *position)
  928. {
  929.     unsigned min, max;
  930.     int block;
  931.  
  932.     if (wtsess->lastBlock == -1) {
  933.         return WTS_BADREQUEST;
  934.     }
  935.     switch(blockPos) {
  936.         case WTP_BEG:
  937.             if (blockOffset < 0) {
  938.                 return WTS_BADREQUEST;
  939.             }
  940.             block = (int)blockOffset;
  941.             break;
  942.         case WTP_REL:
  943.             block = (int)wtsess->currBlock + (int)blockOffset;
  944.             break;
  945.         case WTP_END:
  946.             if (blockOffset < 0) {
  947.                 return WTS_BADREQUEST;
  948.             }
  949.             block = (int)wtsess->lastBlock - (int)blockOffset;
  950.             break;
  951.     }
  952.     if ((block < 0)||(block > wtsess->lastBlock)) {
  953.         return WTS_BADREQUEST;
  954.     }
  955.     switch(offsetPos) {
  956.         case WTP_BEG:
  957.             if (offset < 0) {
  958.                 return WTS_BADREQUEST;
  959.             }
  960.             *position = (int)wtsess->textInfo[block].offset + (int)offset;
  961.             break;
  962.         case WTP_REL:
  963.             if ((blockPos != WTP_REL) && (blockOffset != 0)) {
  964.                 return WTS_BADREQUEST;
  965.             }
  966.             *position = (int)wtsess->textInfo[block].offset + (int)offset;
  967.             break;
  968.         case WTP_END:
  969.             if (offset < 0) {
  970.                 return WTS_BADREQUEST;
  971.             }
  972.             *position = wtsess->textInfo[block].offset +
  973.                         wtsess->textInfo[block].len - (int)offset;
  974.             break;
  975.     }
  976.  
  977.     min = wtsess->textInfo[0].offset;
  978.     max = wtsess->textInfo[wtsess->lastBlock].offset +
  979.             wtsess->textInfo[wtsess->lastBlock].len;
  980.  
  981.     if ((*position < min) || (*position > max)) {
  982.         return WTS_BADREQUEST;
  983.     }
  984.  
  985.     if (!SelectedLen()) {
  986.         SetCaretPos((int)*position);
  987.     }
  988.     return WTS_OK;
  989. } /* GoToPos */
  990.  
  991. /*COMMENT***************************************************
  992. ;SetCaretPos
  993. Title:    Sets caret to indicated offset
  994. In:        offset - offset to place caret
  995. Out:    none
  996. Return:    none
  997. Notes:    if offset is -1 caret is placed at end of document
  998. ***********************************************************/
  999. static void SetCaretPos(int offset)
  1000. {
  1001.     XmTextPosition pos;
  1002.  
  1003.     if (offset == -1) {
  1004.         /* set to end of doc */
  1005.         pos = XmTextGetLastPosition(Text);
  1006.     } else {
  1007.         pos = offset;
  1008.     }
  1009.     XmTextSetInsertionPosition(Text, pos);
  1010. } /* SetCaretPos */
  1011.  
  1012. /*COMMENT***************************************************
  1013. ;Hilite
  1014. Title:    Highlights a region in the text
  1015. In:        count - number of bytes to highlight starting at current position
  1016. Out:    none
  1017. Return:    number of bytes highlighted
  1018. Notes:    
  1019. ***********************************************************/
  1020. long Hilite(long count)
  1021. {
  1022.     XmTextPosition start, end;
  1023.     Time time = XtLastTimestampProcessed(XtDisplay(Text));
  1024.     
  1025.     start = wtsess->absPos;        /* current position */
  1026.     if ((start + count) < 0) {
  1027.         end = 0;
  1028.     } else {
  1029.         end = start + count;
  1030.     }
  1031.     if (count < 0) {    /* backward hilite */
  1032.         XmTextSetSelection(Text, end, start, time);
  1033.     } else {            /* forward hilite */
  1034.         XmTextSetSelection(Text, start, end, time);
  1035.     }
  1036.     return (end - start);    
  1037. } /* Hilite */
  1038.  
  1039. /*COMMENT***************************************************
  1040. ;DeHilite
  1041. Title:    Dehighlights a region in the text
  1042. In:        count - number of bytes to dehighlight starting at current position
  1043. Out:    none
  1044. Return:    number of bytes dehighlighted
  1045. Notes:    if count = 0 then dehighlight all
  1046. ***********************************************************/
  1047. long DeHilite(long count)
  1048. {
  1049.     XmTextPosition start, end;
  1050.     XmTextPosition curStart, curEnd, newStart, newEnd, oldSelected;
  1051.     Time time = XtLastTimestampProcessed(XtDisplay(Text));
  1052.     
  1053.     if (!XmTextGetSelectionPosition(Text, &curStart, &curEnd)) {
  1054.         curStart = curEnd = 0;
  1055.     }
  1056.     oldSelected = curEnd - curStart;    /* len of previous highlighted region */
  1057.       if ((count == 0) || (oldSelected == 0)) {
  1058.         XmTextClearSelection(Text, time);    /* deselect all */
  1059.         SetCaretPos(wtsess->absPos);        /* put caret at current position */
  1060.         return (long)oldSelected;
  1061.     }
  1062.  
  1063.     /* locate start & end pos of dehilite region */
  1064.     if (count < 0) {    
  1065.         start = wtsess->absPos + count;
  1066.         if (start < 0) {
  1067.             start = 0;
  1068.         }
  1069.         end = wtsess->absPos;
  1070.     } else {
  1071.         start = wtsess->absPos;
  1072.         end = wtsess->absPos + count;
  1073.     }
  1074.  
  1075.     if ((((unsigned)start == curStart) && ((unsigned)end == curEnd)) || 
  1076.         (((unsigned)start < curStart) && ((unsigned)end > curEnd)) ||   
  1077.         (((unsigned)start < curStart) && ((unsigned)end == curEnd)) ||  
  1078.         (((unsigned)start == curStart) && ((unsigned)end > curEnd))) {  
  1079.         /* dehilite region covers hilite region */
  1080.         XmTextClearSelection(Text, time);    /* deselect all */
  1081.         SetCaretPos(wtsess->absPos);        /* put caret at current position */
  1082.         return (long)oldSelected;
  1083.     }
  1084.  
  1085.     /* leading dehighlight*/
  1086.     if (((unsigned)start <= curStart) && ((unsigned)end > curStart)) {
  1087.         newStart = (unsigned)end;
  1088.         newEnd = curEnd;
  1089.         XmTextSetSelection(Text, newStart, newEnd, time);
  1090.         return (long)oldSelected - (newEnd - newStart);
  1091.     }
  1092.     
  1093.     /* trailing dehighlight */
  1094.     if (((unsigned)start < curEnd) && ((unsigned)end >= curEnd)) {
  1095.         newStart = curStart;
  1096.         newEnd = (unsigned)start;
  1097.         XmTextSetSelection(Text, newStart, newEnd, time);
  1098.         return (long)oldSelected - (newEnd - newStart);
  1099.     }
  1100.     return 0;    /* dehilight region does not intersect highlight region */
  1101. } /* DeHilite */
  1102.  
  1103. /*COMMENT***************************************************
  1104. ;ReplaceText
  1105. Title:    sets caret to indicated offset
  1106. In:        delete - number of bytes to delete
  1107.         insert - number of bytes to insert
  1108.         buffer - new text
  1109. Out:    none
  1110. Return:    0 if selection is off, else len of selection
  1111. Notes:    if offset is -1 caret is placed at end of document
  1112. ***********************************************************/
  1113. WTCOUNT ReplaceText(WTCOUNT delete, WTSIZE insert, WTBUFP buffer)
  1114. {
  1115.     XmTextPosition startDel, endDel, selected, delCount;
  1116.     char blank=0x0;
  1117.  
  1118.     /* Find start & end positions of text to be deleted */
  1119.     if (delete < 0) {
  1120.         startDel = wtsess->absPos + delete;    /* delete backward */
  1121.         endDel = wtsess->absPos;
  1122.     } else {
  1123.         startDel = wtsess->absPos;            /* delete forward */
  1124.         endDel = wtsess->absPos + delete;
  1125.     }        
  1126.     if (startDel < 0) {        /* make sure we're not before begining */
  1127.         startDel = 0;
  1128.     }
  1129.     delCount = endDel - startDel;        /* actual delete count */
  1130.     
  1131.     /* Replace text */
  1132.     XmTextReplace(Text, startDel, endDel, buffer);
  1133.     
  1134.     /* Update block(s) where text was deleted */
  1135.     updateDeleteCount(FindCurrBlock(startDel), startDel, (unsigned)delCount);
  1136.     
  1137.     /* Update block where text was inserted */
  1138.     updateInsertCount(wtsess->currBlock, (unsigned)insert);
  1139.         
  1140.     /* Update absolute position */
  1141.     if (delete < 0) {    /* delete backwards */
  1142.         wtsess->absPos = (unsigned)startDel + (unsigned)insert;
  1143.         delCount *= -1;        /* deleted count is negative */
  1144.     } else {
  1145.         wtsess->absPos = (unsigned)startDel;    /* delete forward */
  1146.     }
  1147.     /* Make sure caret is in correct position */
  1148.     SetCaretPos(wtsess->absPos);
  1149.  
  1150.     return delCount;
  1151. } /* ReplaceText */
  1152.  
  1153. /*COMMENT***************************************************
  1154. ;FindCurrBlock
  1155. Title:    Finds the block indicated by position
  1156. In:        position - absolute position
  1157. Out:    none
  1158. Return:    block position is in
  1159. Notes:    block is -1 if error
  1160. ***********************************************************/
  1161. unsigned FindCurrBlock(unsigned position)
  1162. {
  1163.     int i;
  1164.     
  1165.     for (i = wtsess->lastBlock; i >= 0; --i) {
  1166.         if (position >= wtsess->textInfo[i].offset) {
  1167.             break;
  1168.         }
  1169.     }
  1170.     return i;
  1171. } /* FindCurrBlock */
  1172.  
  1173. /*COMMENT***************************************************
  1174. ;updateDeletCount
  1175. Title:    Deletes chars from indicated block or succeeding blocks
  1176. In:        blockNum - blocknumber to delete from
  1177.         pos - absolute position where deleting starts
  1178.         delcount - number of chars to delete
  1179. Out:    none
  1180. Return:    none
  1181. Notes:    none
  1182. ***********************************************************/
  1183. static
  1184. void updateDeleteCount(unsigned blockNum, unsigned pos, unsigned delCount)
  1185. {
  1186.     unsigned blockLen, blockOffset, nextDelCount;
  1187.     int i;
  1188.     
  1189.     blockLen = wtsess->textInfo[blockNum].len;
  1190.     blockOffset = wtsess->textInfo[blockNum].offset;
  1191.  
  1192.     if (((pos-blockOffset) + delCount) > blockLen) {    /* in multiple blocks */
  1193.         nextDelCount = delCount -        /* how much to delete from next block */
  1194.             (blockLen - (pos-blockOffset));
  1195.         delCount = delCount - nextDelCount;        /* how much left to delete */
  1196.         updateDeleteCount(blockNum+1, pos + delCount, nextDelCount);
  1197.     }
  1198.     wtsess->textInfo[blockNum].len -= delCount;
  1199.     for (i = blockNum+1; i <= wtsess->lastBlock; ++i) {
  1200.         wtsess->textInfo[i].offset -= delCount;
  1201.     }
  1202. } /* updateDeleteCount */
  1203.  
  1204. /*COMMENT***************************************************
  1205. ;updateInsertCount
  1206. Title:    Inserts chars at indicated block
  1207. In:        blockNum - blocknumber to insert
  1208.         delcount - number of chars to insert
  1209. Out:    none
  1210. Return:    none
  1211. Notes:    none
  1212. ***********************************************************/
  1213. static void updateInsertCount(unsigned blockNum, unsigned insertCount)
  1214. {
  1215.     int i;
  1216.     
  1217.     wtsess->textInfo[blockNum].len += insertCount;
  1218.     for (i = blockNum+1; i <= wtsess->lastBlock; ++i) {
  1219.         wtsess->textInfo[i].offset += insertCount;
  1220.     }
  1221. } /* updateInsertCount */
  1222.  
  1223. /*COMMENT********************************************************
  1224. ;NativeToWord
  1225. Title:    Convert native string to WP word string
  1226. In:        buffer - native string
  1227.         size - ptr to buffer size
  1228. Out:    buffer - word string
  1229. Return:    none
  1230. Notes:    
  1231. *****************************************************************/
  1232. void NativeToWord(WTBUFP buffer, WTSIZE *size)
  1233. {
  1234.     unsigned long i;
  1235.  
  1236.     if (*size == 0) {
  1237.         buffer[0] = buffer[1] = 0;
  1238.         return;
  1239.     }
  1240.  
  1241.     *size = (*size - 1) << 1;    /* double (size-1) */
  1242.  
  1243.     for (i = *size; i <= *size; i -= 2) {
  1244.         buffer[i] = buffer[i >> 1];    /* char */ 
  1245.         buffer[i+1] = 0x00;            /* char set 0 */
  1246.  
  1247.         if ((buffer[i] < 32) || (buffer[i] > 126)) {
  1248.             buffer[i] = 35;        /* undefined char is # */
  1249.         }
  1250.     }
  1251.     *size += 2;
  1252. } /* NativeToWord */
  1253.  
  1254. /*COMMENT********************************************************
  1255. ;WordToNative
  1256. Title:    convert WP word string to native string
  1257. In:        buffer - word string
  1258.         size - buffer size
  1259. Out:    buffer - native string
  1260. Return:    none
  1261. Notes:    
  1262. *****************************************************************/
  1263. void WordToNative(WTBUFP buffer, WTSIZE *size)
  1264. {
  1265.     unsigned long i;
  1266.  
  1267.     if (*size == 0) {
  1268.         buffer[0] = 0;
  1269.         return;
  1270.     }
  1271.  
  1272.     *size >>= 1;
  1273.     for (i = 1; i < *size; ++i) {
  1274.         buffer[i] = buffer[i << 1];        /* char */
  1275.         if ((buffer[i] < 32) || (buffer[i] > 126)) {
  1276.             buffer[i] = 35;        /* undefined char is # */
  1277.         }
  1278.     }
  1279.     buffer[i] = 0;
  1280. } /* WordToNative */
  1281.  
  1282. /*COMMENT***************************************************
  1283. ;SetNewBuffer
  1284. Title:    Initialize text buffer from file and update title
  1285. In:        buffer - text to put in text box
  1286.         filename - filename for title
  1287. Out:    none
  1288. Xin:    Client, Text
  1289. Xout:    none
  1290. Return:    none
  1291. Notes:
  1292. ***********************************************************/
  1293. void SetNewBuffer(char *buffer, char *filename)
  1294. {
  1295.     char title[80];
  1296.     
  1297.     XmTextReplace(Text, 0, XmTextGetLastPosition(Text), buffer);
  1298.     Dirty = FALSE;
  1299.     XmProcessTraversal(Text, XmTRAVERSE_CURRENT);    /* focus to text */
  1300.  
  1301.     sprintf(title, "WTClient - %s", filename);
  1302.     XtVaSetValues(Client, XtNtitle, title, NULL);
  1303. } /* SetNewBuffer */
  1304.  
  1305. /*COMMENT***************************************************
  1306. ;GetBuffer
  1307. Title:    Get text from edit buffer
  1308. In:        none
  1309. Out:    none
  1310. Xin:    Text
  1311. Xout:    none
  1312. Return:    none
  1313. Notes:
  1314. ***********************************************************/
  1315. char *GetBuffer()
  1316. {
  1317.     return(XmTextGetString(Text));
  1318. } /* GetBuffer */
  1319.  
  1320. /*COMMENT***************************************************
  1321. ;TextCB
  1322. Title:    Text changed, set Dirty flag (XmNvalueChangedCallback for Text)
  1323. In:        none
  1324. Out:    none
  1325. Xin:    none
  1326. Xout:    none
  1327. Return:    none
  1328. Notes:
  1329. ***********************************************************/
  1330. static void TextCB(Widget w, XtPointer cldata, XtPointer cbdata)
  1331. {
  1332.     Dirty = TRUE;
  1333. } /* TextCB */
  1334.  
  1335. /*COMMENT***************************************************
  1336. ;change_cursors
  1337. Title:    Turn on a cursor for a widget and its ancestors
  1338. In:        w = widget
  1339.         cursor = the cursor
  1340. Out:    none
  1341. Xin:    none
  1342. Xout:    none
  1343. Ret:    none
  1344. Notes:
  1345. ***********************************************************/
  1346. static void change_cursors(Widget w, Cursor cursor)
  1347. {
  1348.     while (w) {
  1349.         if (XtDisplayOfObject(w) && XtWindowOfObject(w)) {
  1350.             XDefineCursor(
  1351.                 XtDisplayOfObject(w), 
  1352.                 XtWindowOfObject(w), 
  1353.                 cursor);
  1354.             XSync(XtDisplayOfObject(w), False);
  1355.         }
  1356.         w = XtParent(w);
  1357.     }
  1358. } /* change_cursors */
  1359.  
  1360. /*COMMENT***************************************************
  1361. ;WaitCursor
  1362. Title:    Turn on wait cursor (Watch)
  1363. In:        w = widget
  1364. Out:    none
  1365. Xin:    none
  1366. Xout:    none
  1367. Ret:    none
  1368. Notes:
  1369. ***********************************************************/
  1370. void WaitCursor(Widget w)
  1371. {
  1372.     if (!waitcursor) {
  1373.         waitcursor = XCreateFontCursor(XtDisplayOfObject(w), XC_watch);
  1374.     }
  1375.     change_cursors(w, waitcursor);
  1376. } /* WaitCursor */
  1377.  
  1378. /*COMMENT***************************************************
  1379. ;NormalCursor
  1380. Title:    Turn on normal cursor (Arrow)
  1381. In:        w = widget
  1382. Out:    none
  1383. Xin:    none
  1384. Xout:    none
  1385. Ret:    none
  1386. Notes:
  1387. ***********************************************************/
  1388. void NormalCursor(Widget w)
  1389. {
  1390.     if (!normalcursor) {
  1391.         normalcursor = XCreateFontCursor(XtDisplayOfObject(w), XC_left_ptr);
  1392.     }
  1393.     change_cursors(w, normalcursor);
  1394. } /* NormalCursor */
  1395.  
  1396. /*COMMENTS********************************
  1397. ;free_cursors
  1398. Title:    Free cursors we created
  1399. In:        w - any widget on same display as cursors
  1400. Out:    none
  1401. Xin:    none
  1402. Xout:    none
  1403. Return:    none
  1404. Notes:
  1405. *END**************************************/
  1406. static void free_cursors(Widget w)
  1407. {
  1408.     if (normalcursor) {
  1409.         XFreeCursor(XtDisplay(w), normalcursor);
  1410.     }
  1411.     if (waitcursor) {
  1412.         XFreeCursor(XtDisplay(w), waitcursor);
  1413.     }
  1414.     normalcursor = 0;        /* for safety */
  1415.     waitcursor = 0;            /* for safety */
  1416. } /* free_cursors */
  1417.  
  1418. /*COMMENT***************************************************
  1419. ;ClInvokeWritingTool
  1420. Title:    Invoke a writing tool
  1421. In:        tool - index of tool in toolRecords
  1422.         mode - WT_INVOKE_MODE, WT_SETUP_MODE, WT_HELP_MODE
  1423. Out:    none
  1424. Xin:    none
  1425. Xout:    none
  1426. Return:    1 - success, 0 - error
  1427. Notes:    All input parameters are from wtapi.rc
  1428. ***********************************************************/
  1429. static BOOL ClInvokeWritingTool(int tool, short mode)
  1430. {
  1431.     extern int errno;        /* system errno */
  1432.     extern char *sys_errlist[];    /* text for system errors */
  1433.     extern int sys_nerr;    /* number of system errors in sys_errlist */
  1434.     int sv_errno = 0;        /* save errno */
  1435.     char error[256];        /* error message */
  1436.     BOOL found;                /* executable found? */
  1437.     char **argv;            /* arguments for exec of child tool */
  1438.     int argc = 0;            /* argument count */
  1439.     int argcnt;                /* number of argv's allocated */
  1440.     char *arg;                /* pointer to next arg in params */
  1441.     char initpath[256];        /* full path to executable */
  1442.     char fullpath[256];        /* full path to executable */
  1443.     char *p;                /* pointer */
  1444.     char *cwd;                /* pointer to current working directory */
  1445.     char buf[256];            /* buffer */
  1446.     int try;                /* loop variable */
  1447.     WTCOMM comm;            /* communication handle */
  1448.     String app_name = 0;    /* application name */
  1449.     String app_class = 0;    /* application class */
  1450.     int pid;                /* pid of forked process */
  1451.     int i;
  1452.  
  1453.     /*
  1454.      * Make sure the process to be executed exists and is executable.  The
  1455.      * following searches are done in order.  The search ends whenever an
  1456.      * executable file is found:
  1457.      *    Full path in wtapi.rc:
  1458.      *        1. Path as given.
  1459.      *        2. Find name of current directory.  Look for this directory in
  1460.      *            the given path.  If found, strip off path up to and including
  1461.      *            this dir and use the remainder of the path as a relative path
  1462.      *            for the relative path searches below.  (This handles the case
  1463.      *            of a directory being remote mounted to a mount point different
  1464.      *            than the original path.)
  1465.      *    Relative path in wtapi.rc:
  1466.      *        1. Relative to current directory.
  1467.      *        2. Relative to $HOME directory.
  1468.      *        3. Relative to root directory (/).
  1469.      */
  1470.     strcpy(initpath, toolRecords[tool].exe);/* exec path from wtapi.rc */
  1471.     errno = 0;
  1472.     found = FALSE;
  1473.     if (initpath[0] == '/') {    /* full path given in wtapi.rc */
  1474.         if (access(initpath, X_OK) == 0) {
  1475.             strcpy(fullpath, initpath);
  1476.             found = TRUE;
  1477.         } else {
  1478.             sv_errno = errno;    /* save errno of first try to report */
  1479.             /*
  1480.              * Check for current directory in path.  If found look relative
  1481.              * to current directory using remainder of path.
  1482.              */
  1483.             if (cwd = getcwd(buf, 256)) {
  1484.                 p = strrchr(cwd, '/');    /* find last '/' in path */
  1485.                 cwd = p+1;                /* get name of directory */
  1486.                 /* check for current dir in given path */
  1487.                 if (p = strstr(initpath, cwd)) {
  1488.                     p = strchr(p, '/');        /* skip to next '/' */
  1489.                     p++;                    /* skip past '/' */
  1490.                     strcpy(initpath, p);    /* new relative path */
  1491.                     /*
  1492.                      * This new relative path will be checked by the relative
  1493.                      * path code below.
  1494.                      */
  1495.                 }
  1496.             }
  1497.         }
  1498.     }
  1499.     if (initpath[0] != '/') {    /* relative path */
  1500.         for (try = 1; try > 0; try++) {
  1501.             switch (try) {
  1502.                 case 1:        /* Look relative to current directory */
  1503.                     if (cwd = getcwd(buf, 256)) {
  1504.                         if (cwd[strlen(cwd)-1] != '/') {
  1505.                             strcat(cwd, "/");    /* append a slash */
  1506.                         }
  1507.                     } else {
  1508.                         continue;
  1509.                     }
  1510.                     break;
  1511.                 case 2:        /* Look relative to $HOME directory */
  1512.                     if (cwd = getenv("HOME")) {
  1513.                         if (cwd[strlen(cwd)-1] != '/') {
  1514.                             strcat(cwd, "/");    /* append a slash */
  1515.                         }
  1516.                     } else {
  1517.                         continue;
  1518.                     }
  1519.                     break;
  1520.                 case 3:        /* Look relative to root directory */
  1521.                     cwd = strcpy(buf, "/");
  1522.                     break;
  1523.                 default:
  1524.                     try = -1;    /* give up */
  1525.                     continue;
  1526.             }
  1527.             strcpy(fullpath, cwd);
  1528.             strcat(fullpath, initpath);
  1529.             if (access(fullpath, X_OK) == 0) {
  1530.                 found = TRUE;
  1531.                 break;        /* exit loop, we found an executable path */
  1532.             } else {
  1533.                 if (!sv_errno) {
  1534.                     sv_errno = errno;    /* only save first errno */
  1535.                 }
  1536.             }
  1537.         }
  1538.     }
  1539.     if (!found) {    /* all attempts to find the executable failed */
  1540.         sprintf(error, "Failed to find executable: %s", toolRecords[tool].exe);
  1541.         if (sv_errno < sys_nerr) {
  1542.             strcat(error, "\n\nError:  ");
  1543.             strcat(error, sys_errlist[sv_errno]);
  1544.         }
  1545.         ReportError(error);
  1546.         return FALSE;    /* Failed to find tool to bring up */
  1547.     }
  1548.     /*
  1549.      * The full path is needed for communication.  Put it in toolRecords if
  1550.      * not already there.
  1551.      */
  1552.     if (strcmp(fullpath, toolRecords[tool].exe) != 0) {
  1553.         strcpy(toolRecords[tool].exe, fullpath);
  1554.     }
  1555.     /*
  1556.      * Initialize the WT client communication.
  1557.      */
  1558.     if (comm = WtConnect(Client, toolRecords[tool].exe)) {
  1559.         commHandle = comm;   /* successful */
  1560.     } else {
  1561.         ReportError("Could not initialize communication, tool not loaded.");
  1562.         return FALSE;
  1563.     }
  1564.     /*
  1565.      * Allocate space for argv parameters.
  1566.      */
  1567.     argcnt = 50;    /* start with 50, should be plenty */
  1568.     argv = (char **)calloc(argcnt, sizeof(char **));
  1569.     if (!argv) {
  1570.         WtDisconnect(comm);
  1571.         ReportError("Not enough memory.");
  1572.         return FALSE;
  1573.     }
  1574.     /*
  1575.      * Build the path argument to pass to the child tool process.
  1576.      */
  1577.     argv[0] = (char *)malloc(strlen(toolRecords[tool].exe)+1);
  1578.     if (!argv[0]) {
  1579.         free(argv);
  1580.         WtDisconnect(comm);
  1581.         ReportError("Not enough memory.");
  1582.         return FALSE;
  1583.     }
  1584.     strcpy(argv[0], toolRecords[tool].exe);
  1585.     argc = 1;
  1586.     /*
  1587.      * Build the WTAPI arguments to pass to the child (tool) process.
  1588.      */
  1589.     argv[argc] = (char *)malloc(strlen("-wtapi")+1);
  1590.     strcpy(argv[argc++], "-wtapi");
  1591.  
  1592.     argv[argc] = (char *)malloc(5);
  1593.     sprintf(argv[argc++], "%d", comm->tl_pipe.read);
  1594.  
  1595.     argv[argc] = (char *)malloc(5);
  1596.     sprintf(argv[argc++], "%d", comm->tl_pipe.write);
  1597.     /*
  1598.      * Add the parameters from the wtapi.rc file.
  1599.      */
  1600.     strcpy(buf, toolRecords[tool].params);    /* make a copy */
  1601.     arg = p = buf;
  1602.     while (*p) {
  1603.         if (argc >= (argcnt-1)) {    /* too many args, ignore the rest */
  1604.             break;
  1605.         }
  1606.         if (p = strpbrk(p, " \t\n\r")) {    /* find next whitespace */
  1607.             if (p == arg) {    /* starting on whitespace */ 
  1608.                 arg = ++p;    /* move to next char */
  1609.                 continue;
  1610.             }
  1611.             *p = 0;                /* null out white space */
  1612.         } else {
  1613.             p += strlen(p);        /* move p to end of string */
  1614.         }
  1615.         argv[argc] = (char *)malloc(strlen(arg)+1);
  1616.         if (!argv[argc]) {
  1617.             for (i = 0; i < argc; i++) {
  1618.                 if (argv[i]) {
  1619.                     free(argv[i]);
  1620.                 }
  1621.             }
  1622.             free(argv);
  1623.             WtDisconnect(comm);
  1624.             ReportError("Not enough memory.");
  1625.             return FALSE;
  1626.         }
  1627.         strcpy(argv[argc++], arg);
  1628.         arg = ++p;    /* move to next char */
  1629.     }
  1630.     /*
  1631.      * Get application class and client's window to pass along.
  1632.      * (These are optional and the tool should provide defaults if needed.)
  1633.      */
  1634.     /* -appClass */
  1635.     if ((argc + 3) < argcnt) {
  1636.         argv[argc] = (char *)malloc(strlen("-appClass")+1);
  1637.         strcpy(argv[argc++], "-appClass");
  1638.         XtGetApplicationNameAndClass(XtDisplay(Client), &app_name, &app_class);
  1639.         argv[argc] = (char *)malloc(strlen(app_class)+1);
  1640.         strcpy(argv[argc++], app_class);
  1641.     }
  1642.     /* -clwin */
  1643.     if ((argc + 3) < argcnt) {
  1644.         argv[argc] = (char *)malloc(strlen("-clwin")+1);
  1645.         strcpy(argv[argc++], "-clwin");
  1646.         sprintf(buf, "0x%x", XtWindow(Client));
  1647.         argv[argc] = (char *)malloc(strlen(buf)+1);
  1648.         strcpy(argv[argc++], buf);
  1649.     }
  1650.     argv[argc] = 0;        /* null terminate argv list */
  1651.     /*
  1652.      * Execute the Writing Tool.
  1653.      */
  1654.     if (pid = fork()) {        /* parent process */
  1655.         if (pid == -1) {    /* fork failed */
  1656.             for (i = 0; i < argc; i++) {
  1657.                 if (argv[i]) {
  1658.                     free(argv[i]);
  1659.                 }
  1660.             }
  1661.             free(argv);
  1662.             WtDisconnect(comm);
  1663.             sprintf(buf, "Fork failed for exec of: %s", argv[0]);
  1664.             ReportError(buf);
  1665.             return FALSE;
  1666.         }
  1667.     } else {    /* child process */
  1668.         execvp(argv[0], argv);
  1669.         exit(1);    /* exit of child on exec failure */
  1670.     }
  1671.     /*
  1672.      * Close tool's read/write file descriptors in the parent.
  1673.      */
  1674.     close(comm->tl_pipe.read);
  1675.     close(comm->tl_pipe.write);
  1676.     comm->tl_pipe.read = comm->tl_pipe.write = -1;    /* mark as closed */
  1677.     /*
  1678.      * Free memory allocated for argv parameters.
  1679.      */
  1680.     for (i = 0; i < argc; i++) {
  1681.         if (argv[i]) {
  1682.             free(argv[i]);
  1683.         }
  1684.     }
  1685.     free(argv);
  1686.     return TRUE;
  1687. } /* ClInvokeWritingTool */
  1688.