home *** CD-ROM | disk | FTP | other *** search
/ Network PC / Network PC.iso / amiga utilities / communication / bbs / termv4.6 / extras / source / term-source.lha / Chat.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-03-18  |  13.4 KB  |  705 lines

  1. /*
  2. **    Chat.c
  3. **
  4. **    Chat support code
  5. **
  6. **    Copyright © 1990-1996 by Olaf `Olsen' Barthel
  7. **        All Rights Reserved
  8. */
  9.  
  10. #ifndef _GLOBAL_H
  11. #include "Global.h"
  12. #endif
  13.  
  14.     // Maximum number of characters to enter per line
  15.  
  16. #define CHAT_LINE_SIZE    512
  17.  
  18.     // Local data
  19.  
  20. STATIC struct List    *ChatList;
  21. STATIC struct Node    *ChatNode;
  22. STATIC STRPTR         ChatBuffer,
  23.              ChatUndo,
  24.              ChatWork,
  25.              ChatTemp;
  26. STATIC LONG         ChatPosition;
  27. STATIC Object        *ChatGadget;
  28. STATIC BOOLEAN         ChatWasActive;
  29.  
  30.     /* ChatKey(REG(a1) ULONG *Msg,REG(a2) struct SGWork *Work):
  31.      *
  32.      *    String gadget editing hook code. This is where all the
  33.      *    magic happens.
  34.      */
  35.  
  36. STATIC ULONG __saveds __asm
  37. ChatKey(REG(a1) ULONG *Msg,REG(a2) struct SGWork *Work)
  38. {
  39.         /* Someone activated the string gadget and
  40.          * hit a key.
  41.          */
  42.  
  43.     if(*Msg == SGH_KEY)
  44.     {
  45.         BOOL    NeedChange    = FALSE,
  46.             DidSomething    = FALSE;
  47.  
  48.             // Remember that this gadget was activated
  49.  
  50.         Forbid();
  51.  
  52.         ChatWasActive = TRUE;
  53.  
  54.         Permit();
  55.  
  56.             // Clear the history list when pressing Amiga+Del/Amiga+Backspace
  57.  
  58.         if((Work -> IEvent -> ie_Qualifier & (AMIGARIGHT | AMIGALEFT)) && (Work -> IEvent -> ie_Code == DEL_CODE || Work -> IEvent -> ie_Code == BACKSPACE_CODE))
  59.         {
  60.             FreeList(ChatList);
  61.  
  62.             NewList(ChatList);
  63.  
  64.             ChatNode = NULL;
  65.  
  66.             Work -> Actions &= ~(SGA_USE | SGA_BEEP);
  67.  
  68.             return(TRUE);
  69.         }
  70.  
  71.             /* Right-Amiga-key was pressed, release the
  72.              * string gadget so user may select a menu
  73.              * item.
  74.              */
  75.  
  76.         if((Work -> IEvent -> ie_Qualifier & AMIGARIGHT) && Work -> IEvent -> ie_Code < 96)
  77.         {
  78.             if(!(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) && (Work -> IEvent -> ie_Code == KEYCODE_X || Work -> IEvent -> ie_Code == KEYCODE_Q))
  79.                 return(TRUE);
  80.             else
  81.             {
  82.                 Work -> Actions = (Work -> Actions & ~(SGA_USE | SGA_BEEP)) | SGA_END | SGA_REUSE;
  83.  
  84.                 return(TRUE);
  85.             }
  86.         }
  87.  
  88.             // This looks like a function key. Send the corresponding macro.
  89.  
  90.         if(Work -> IEvent -> ie_Code >= F01_CODE && Work -> IEvent -> ie_Code <= F10_CODE)
  91.         {
  92.             STRPTR    String;
  93.             LONG    Len,Index = Work -> IEvent -> ie_Code - F01_CODE;
  94.  
  95.             Forbid();
  96.  
  97.                 // Pick the right macro
  98.  
  99.             if(Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL)
  100.                 String = MacroKeys -> Keys[3][Index];
  101.             else
  102.             {
  103.                 if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
  104.                     String = MacroKeys -> Keys[2][Index];
  105.                 else
  106.                 {
  107.                     if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
  108.                         String = MacroKeys -> Keys[1][Index];
  109.                     else
  110.                         String = MacroKeys -> Keys[0][Index];
  111.                 }
  112.             }
  113.  
  114.                 // Anything to send at all?
  115.  
  116.             if(Len = strlen(String))
  117.             {
  118.                 struct DataMsg *Msg;
  119.  
  120.                 if(Msg = (struct DataMsg *)CreateMsgItem(sizeof(struct DataMsg) + Len + 1))
  121.                 {
  122.                     Msg -> Type = DATAMSGTYPE_SERIALCOMMAND;
  123.                     Msg -> Data = (STRPTR)(Msg + 1);
  124.  
  125.                     strcpy(Msg -> Data,String);
  126.  
  127.                         // Send the command
  128.  
  129.                     PutMsgItem(SpecialQueue,(struct MsgItem *)Msg);
  130.                 }
  131.             }
  132.  
  133.             Permit();
  134.  
  135.             return(TRUE);
  136.         }
  137.  
  138.             /* The user pressed the cursor-right key to
  139.              * move the cursor to the next word in the buffer.
  140.              */
  141.  
  142.         if(Work -> IEvent -> ie_Code == CURSORRIGHT && (Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL))
  143.         {
  144.             if(Work -> BufferPos != Work -> NumChars)
  145.             {
  146.                 LONG i,Position = -1;
  147.  
  148.                 for(i = Work -> BufferPos ; i < Work -> NumChars ; i++)
  149.                 {
  150.                     if(Work -> WorkBuffer[i] == ' ')
  151.                     {
  152.                         for( ; i < Work -> NumChars ; i++)
  153.                         {
  154.                             if(Work -> WorkBuffer[i] != ' ')
  155.                             {
  156.                                 Position = i;
  157.                                 break;
  158.                             }
  159.                         }
  160.  
  161.                         break;
  162.                     }
  163.                 }
  164.  
  165.                 if(Position != -1)
  166.                     Work -> BufferPos = Position;
  167.                 else
  168.                     Work -> BufferPos = Work -> NumChars;
  169.  
  170.                 Work -> EditOp = EO_MOVECURSOR;
  171.             }
  172.  
  173.             return(TRUE);
  174.         }
  175.  
  176.             /* The user pressed the cursor-right key to
  177.              * move the cursor to the previous word in the buffer.
  178.              */
  179.  
  180.         if(Work -> IEvent -> ie_Code == CURSORLEFT && (Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL))
  181.         {
  182.             if(Work -> BufferPos)
  183.             {
  184.                 LONG i,Position = -1;
  185.  
  186.                 for(i = Work -> BufferPos ; i >= 0 ; i--)
  187.                 {
  188.                     if(Work -> WorkBuffer[i] != ' ')
  189.                     {
  190.                         Position = i;
  191.                         break;
  192.                     }
  193.                 }
  194.  
  195.                 if(Position == -1)
  196.                     Position = 0;
  197.  
  198.                 if(Position)
  199.                 {
  200.                     i = Position;
  201.  
  202.                     Position = -1;
  203.  
  204.                     for( ; i >= 0 ; i--)
  205.                     {
  206.                         if(Work -> WorkBuffer[i] == ' ')
  207.                         {
  208.                             Position = i + 1;
  209.                             break;
  210.                         }
  211.                     }
  212.                 }
  213.  
  214.                 if(Position != -1)
  215.                     Work -> BufferPos = Position;
  216.                 else
  217.                     Work -> BufferPos = 0;
  218.  
  219.                 Work -> EditOp = EO_MOVECURSOR;
  220.             }
  221.  
  222.             DidSomething = TRUE;
  223.         }
  224.  
  225.             /* The user pressed the cursor-up key to
  226.              * scroll through the command history.
  227.              */
  228.  
  229.         if(Work -> IEvent -> ie_Code == CURSORUP)
  230.         {
  231.                 /* Shift key: jump to first command
  232.                  * history entry.
  233.                  */
  234.  
  235.             if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
  236.             {
  237.                 if(ChatList -> lh_Head -> ln_Succ)
  238.                     ChatNode = ChatList -> lh_Head;
  239.                 else
  240.                     ChatNode = NULL;
  241.  
  242.                 NeedChange = TRUE;
  243.             }
  244.             else
  245.             {
  246.                 if(ChatNode)
  247.                 {
  248.                     if(ChatNode -> ln_Pred -> ln_Pred)
  249.                     {
  250.                         ChatNode = ChatNode -> ln_Pred;
  251.  
  252.                         NeedChange = TRUE;
  253.                     }
  254.                 }
  255.                 else
  256.                 {
  257.                     if(ChatList -> lh_Head -> ln_Succ)
  258.                     {
  259.                         ChatNode = ChatList -> lh_TailPred;
  260.  
  261.                         NeedChange = TRUE;
  262.                     }
  263.                 }
  264.             }
  265.  
  266.             DidSomething = TRUE;
  267.         }
  268.  
  269.             /* The user pressed the cursor-down key to
  270.              * scroll through the command history.
  271.              */
  272.  
  273.         if(Work -> IEvent -> ie_Code == CURSORDOWN)
  274.         {
  275.                 /* Shift key: jump to last command
  276.                  * history entry.
  277.                  */
  278.  
  279.             if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
  280.             {
  281.                 if(ChatList -> lh_Head -> ln_Succ)
  282.                     ChatNode = ChatList -> lh_TailPred;
  283.                 else
  284.                     ChatNode = NULL;
  285.  
  286.                 NeedChange = TRUE;
  287.             }
  288.             else
  289.             {
  290.                 if(ChatNode)
  291.                 {
  292.                     if(ChatNode -> ln_Succ -> ln_Succ)
  293.                         ChatNode = ChatNode -> ln_Succ;
  294.                     else
  295.                         ChatNode = NULL;
  296.                 }
  297.  
  298.                 NeedChange = TRUE;
  299.             }
  300.  
  301.             DidSomething = TRUE;
  302.         }
  303.  
  304.             // Update the contents of the string gadget
  305.  
  306.         if(NeedChange)
  307.         {
  308.             LONG Len;
  309.  
  310.             if(ChatNode)
  311.                 strcpy(Work -> WorkBuffer,ChatNode -> ln_Name);
  312.             else
  313.                 Work -> WorkBuffer[0] = 0;
  314.  
  315.             strcpy(Work -> StringInfo -> UndoBuffer,Work -> PrevBuffer);
  316.  
  317.             Work -> StringInfo -> UndoPos = Work -> BufferPos;
  318.  
  319.             Len = strlen(Work -> WorkBuffer);
  320.  
  321.             if(Len < Work -> BufferPos)
  322.                 Work -> BufferPos = Len;
  323.  
  324.             Work -> NumChars    = Len;
  325.             Work -> Actions        = (Work -> Actions & ~SGA_BEEP) | SGA_USE | SGA_REDISPLAY;
  326.         }
  327.  
  328.             // User pressed return?
  329.  
  330.         if(Work -> Actions & SGA_END)
  331.         {
  332.                 // Deactivate the gadget?
  333.  
  334.             if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
  335.             {
  336.                 Work -> Actions &= ~SGA_END;
  337.  
  338.                 Forbid();
  339.  
  340.                 ChatWasActive = FALSE;
  341.  
  342.                 Permit();
  343.             }
  344.             else
  345.             {
  346.                 struct DataMsg    *Msg;
  347.                 LONG         Len = Work -> NumChars;
  348.  
  349.                     // If any text was entered, add it to the history list
  350.  
  351.                 if(Work -> WorkBuffer[0])
  352.                 {
  353.                     BOOL AddIt = TRUE;
  354.  
  355.                         // Don't add duplicates
  356.  
  357.                     if(ChatNode)
  358.                     {
  359.                         if(!Stricmp(ChatNode -> ln_Name,Work -> WorkBuffer))
  360.                             AddIt = FALSE;
  361.                     }
  362.  
  363.                         // Allocate space for the text, then add it
  364.  
  365.                     if(AddIt)
  366.                     {
  367.                         struct Node *SomeNode;
  368.  
  369.                         if(SomeNode = (struct Node *)AllocVecPooled(sizeof(struct Node) + Len + 1,MEMF_ANY))
  370.                         {
  371.                             strcpy(SomeNode -> ln_Name = (STRPTR)(SomeNode + 1),Work -> WorkBuffer);
  372.  
  373.                             AddTail(ChatList,SomeNode);
  374.                         }
  375.                     }
  376.                 }
  377.  
  378.                 ChatNode = NULL;
  379.  
  380.                     // Transfer the buffer
  381.  
  382.                 if(Msg = (struct DataMsg *)CreateMsgItem(sizeof(struct DataMsg) + Len + 3))
  383.                 {
  384.                     STRPTR Extra;
  385.  
  386.                     Msg -> Type = DATAMSGTYPE_SERIALCOMMAND;
  387.                     Msg -> Data = (STRPTR)(Msg + 1);
  388.  
  389.                     strcpy(Msg -> Data,Work -> WorkBuffer);
  390.  
  391.                         // Add EOL characters
  392.  
  393.                     switch(Config -> TerminalConfig -> SendCR)
  394.                     {
  395.                         case EOL_CR:
  396.  
  397.                             Extra = "\r";
  398.                             break;
  399.  
  400.                         case EOL_LF:
  401.  
  402.                             Extra = "\n";
  403.                             break;
  404.  
  405.                         case EOL_CRLF:
  406.  
  407.                             Extra = "\r\n";
  408.                             break;
  409.  
  410.                         case EOL_LFCR:
  411.  
  412.                             Extra = "\n\r";
  413.                             break;
  414.  
  415.                         default:
  416.  
  417.                             Extra = NULL;
  418.                             break;
  419.                     }
  420.  
  421.                     if(Extra)
  422.                         strcpy(Msg -> Data + Len,Extra);
  423.  
  424.                         // Clear the string gadget
  425.  
  426.                     Work -> WorkBuffer[0]    = 0;
  427.                     Work -> BufferPos    = 0;
  428.                     Work -> NumChars    = 0;
  429.                     Work -> Actions        = (Work -> Actions & ~(SGA_BEEP | SGA_END)) | SGA_USE | SGA_REDISPLAY;
  430.  
  431.                     Work -> StringInfo -> UndoPos        = 0;
  432.                     Work -> StringInfo -> UndoBuffer[0]    = 0;
  433.  
  434.                         // Send the line
  435.  
  436.                     PutMsgItem(SpecialQueue,(struct MsgItem *)Msg);
  437.                 }
  438.             }
  439.  
  440.             DidSomething = TRUE;
  441.         }
  442.  
  443.             // Control character entered?
  444.  
  445.         if(!DidSomething && Work -> Code && !(Work -> Code & 0xFFE0))
  446.         {
  447.             LONG Code = Work -> Code & 0x1F;
  448.  
  449.             if(Code == '\t' && Work -> IEvent -> ie_Code != TAB_CODE)
  450.                 return(TRUE);
  451.  
  452.             if(Code != '\r' && Code != '\b')
  453.             {
  454.                 struct DataMsg *Msg;
  455.  
  456.                 if(Code == ('S' & 0x1F) || Code == ('Q' & 0x1F))
  457.                 {
  458.                     if(Config -> SerialConfig -> xONxOFF)
  459.                     {
  460.                         Forbid();
  461.  
  462.                         if(Code == ('S' & 0x1F))
  463.                         {
  464.                             if(Status == STATUS_READY)
  465.                                 Status = STATUS_HOLDING;
  466.                         }
  467.                         else
  468.                         {
  469.                             if(Status == STATUS_HOLDING)
  470.                                 Status = STATUS_READY;
  471.                         }
  472.  
  473.                         Permit();
  474.                     }
  475.  
  476.                     if(!Config -> SerialConfig -> PassThrough)
  477.                         return(TRUE);
  478.                 }
  479.  
  480.                 if(Msg = (struct DataMsg *)CreateMsgItem(sizeof(struct DataMsg) + 2))
  481.                 {
  482.                     Work -> Actions &= ~SGA_USE;
  483.  
  484.                     Msg -> Type = DATAMSGTYPE_SERIALCOMMAND;
  485.                     Msg -> Data = (STRPTR)(Msg + 1);
  486.  
  487.                     Msg -> Data[0] = Code;
  488.                     Msg -> Data[1] = 0;
  489.  
  490.                     PutMsgItem(SpecialQueue,(struct MsgItem *)Msg);
  491.                 }
  492.             }
  493.         }
  494.  
  495.         return(TRUE);
  496.     }
  497.     else
  498.     {
  499.         if(*Msg == SGH_CLICK)
  500.         {
  501.                 // Remember activation
  502.  
  503.             Forbid();
  504.  
  505.             ChatWasActive = TRUE;
  506.  
  507.             Permit();
  508.  
  509.             return(TRUE);
  510.         }
  511.         else
  512.             return(FALSE);
  513.     }
  514. }
  515.  
  516.     /* HideChatGadget():
  517.      *
  518.      *    Remove the chat gadget, but don't free the buffers.
  519.      */
  520.  
  521. VOID
  522. HideChatGadget()
  523. {
  524.     if(ChatGadget)
  525.     {
  526.         GetAttr(STRINGA_BufferPos,ChatGadget,(ULONG *)&ChatPosition);
  527.  
  528.         RemoveGList(Window,(struct Gadget *)ChatGadget,1);
  529.  
  530.         DisposeObject(ChatGadget);
  531.  
  532.         ChatGadget = NULL;
  533.     }
  534. }
  535.  
  536.     /* DeleteChatGadget():
  537.      *
  538.      *    Remove the chat gadget, also take care of the buffers.
  539.      */
  540.  
  541. VOID
  542. DeleteChatGadget()
  543. {
  544.     HideChatGadget();
  545.  
  546.     FreeVecPooled(ChatBuffer);
  547.     ChatBuffer = NULL;
  548.  
  549.     DeleteList(ChatList);
  550.     ChatList = NULL;
  551. }
  552.  
  553.     /* UpdateChatGadget():
  554.      *
  555.      *    Redraw the chat gadget imagery.
  556.      */
  557.  
  558. VOID
  559. UpdateChatGadget()
  560. {
  561.     if(ChatGadget)
  562.     {
  563.         struct RastPort    *RPort = Window -> RPort;
  564.         LONG         Left,Right,Top;
  565.  
  566.             // Draw the separator bar
  567.  
  568.         if(StatusWindow || Config -> ScreenConfig -> StatusLine == STATUSLINE_DISABLED)
  569.             Top = UserFontHeight;
  570.         else
  571.             Top = StatusDisplayHeight + UserFontHeight;
  572.  
  573.         Top    = Window -> Height - (Window -> BorderBottom + Top + 2);
  574.         Left    = Window -> BorderLeft;
  575.         Right    = Window -> Width - (Window -> BorderRight + 1);
  576.  
  577.         SetAPen(RPort,Pens[SHADOWPEN]);
  578.         Move(RPort,Left,Top);
  579.         Draw(RPort,Right,Top);
  580.  
  581.         Top++;
  582.  
  583.         SetAPen(RPort,Pens[SHINEPEN]);
  584.         Move(RPort,Left,Top);
  585.         Draw(RPort,Right,Top);
  586.  
  587.             // Redraw the gadget
  588.  
  589.         RefreshGList((struct Gadget *)ChatGadget,Window,NULL,1);
  590.     }
  591. }
  592.  
  593.     /* ActivateChat(BOOL Reactivate):
  594.      *
  595.      *    Activate the chat gadget.
  596.      */
  597.  
  598. VOID
  599. ActivateChat(BOOL Reactivate)
  600. {
  601.     if(ChatGadget)
  602.     {
  603.         Forbid();
  604.  
  605.         if(Reactivate)
  606.             Reactivate = ChatWasActive;
  607.         else
  608.             Reactivate = TRUE;
  609.  
  610.         Permit();
  611.  
  612.         if(Reactivate)
  613.             ActivateGadget((struct Gadget *)ChatGadget,Window,NULL);
  614.     }
  615. }
  616.  
  617.     /* CreateChatGadget():
  618.      *
  619.      *    Create the chat gadget and add it to the main window.
  620.      */
  621.  
  622. BOOL
  623. CreateChatGadget()
  624. {
  625.     ChatWasActive = TRUE;
  626.  
  627.         // Allocate the history list
  628.  
  629.     if(!ChatList)
  630.     {
  631.         if(!(ChatList = (struct List *)AllocVecPooled(sizeof(struct MinList),MEMF_ANY)))
  632.             return(FALSE);
  633.         else
  634.             NewList(ChatList);
  635.  
  636.         ChatNode = NULL;
  637.     }
  638.  
  639.         // Allocate the undo/work/editing buffers
  640.  
  641.     if(!ChatBuffer)
  642.     {
  643.         if(!(ChatBuffer = (STRPTR)AllocVecPooled(5 * CHAT_LINE_SIZE,MEMF_ANY | MEMF_CLEAR)))
  644.         {
  645.             DeleteChatGadget();
  646.  
  647.             return(FALSE);
  648.         }
  649.         else
  650.         {
  651.             ChatUndo = ChatBuffer    + CHAT_LINE_SIZE;
  652.             ChatWork = ChatUndo    + CHAT_LINE_SIZE;
  653.             ChatTemp = ChatWork    + CHAT_LINE_SIZE;
  654.         }
  655.     }
  656.  
  657.         // Finally create the gadget
  658.  
  659.     if(!ChatGadget)
  660.     {
  661.         STATIC struct Hook ChatHook = { {NULL},(HOOKFUNC)ChatKey };
  662.  
  663.         LONG Bottom = Window -> BorderBottom + UserFontHeight - 1;
  664.  
  665.         if(!StatusWindow && Config -> ScreenConfig -> StatusLine != STATUSLINE_DISABLED && !Config -> ScreenConfig -> SplitStatus)
  666.             Bottom += StatusDisplayHeight;
  667.  
  668.         if(!(ChatGadget = NewObject(NULL,STRGCLASS,
  669.             GA_Left,        Window -> BorderLeft,
  670.             GA_Height,        UserFontHeight,
  671.             GA_RelBottom,        -Bottom,
  672.             GA_RelWidth,        -(Window -> BorderLeft + Window -> BorderRight),
  673.             GA_TabCycle,        FALSE,
  674.             STRINGA_TextVal,    ChatBuffer,
  675.             STRINGA_MaxChars,    CHAT_LINE_SIZE,
  676.             STRINGA_Buffer,        ChatBuffer,
  677.             STRINGA_BufferPos,    ChatPosition,
  678.             STRINGA_UndoBuffer,    ChatUndo,
  679.             STRINGA_WorkBuffer,    ChatWork,
  680.             STRINGA_NoFilterMode,    TRUE,
  681.  
  682.                 // NOTE: This should really look like below,
  683.                 //       according to the BOOPSI documentation.
  684.                 //       Even worse, the pen numbers are in reality
  685.                 //       truncated to a maximum of colour # 15.
  686.  
  687. //            STRINGA_Pens,        ((LONG)Pens[BACKGROUNDPEN] << 16) | Pens[TEXTPEN],
  688. //            STRINGA_ActivePens,    ((LONG)Pens[BACKGROUNDPEN] << 16) | Pens[TEXTPEN],
  689.  
  690.             STRINGA_Pens,        (Pens[BACKGROUNDPEN] << 8) | Pens[TEXTPEN],
  691.             STRINGA_ActivePens,    (Pens[BACKGROUNDPEN] << 8) | Pens[TEXTPEN],
  692.             STRINGA_EditHook,    &ChatHook,
  693.         TAG_DONE)))
  694.         {
  695.             DeleteChatGadget();
  696.  
  697.             return(FALSE);
  698.         }
  699.         else
  700.             AddGList(Window,(struct Gadget *)ChatGadget,(UWORD)~0,1,NULL);
  701.     }
  702.  
  703.     return(TRUE);
  704. }
  705.