Fix Xaw Chat Console
[xboard.git] / dialogs.c
index af8435a..8dbcf04 100644 (file)
--- a/dialogs.c
+++ b/dialogs.c
@@ -966,20 +966,35 @@ BoardOptionsProc ()
 
 Option textOptions[100];
 static void PutText P((char *text, int pos));
+static void NewChat P((char *name));
+static char clickedWord[MSG_SIZ], click;
 
 void
 SendString (char *p)
 {
-    char buf[MSG_SIZ], *q;
+    char buf[MSG_SIZ], buf2[MSG_SIZ], *q;
+    if(q = strstr(p, "$name")) { // in Xaw this is already intercepted
+       if(!shellUp[TextMenuDlg] || !clickedWord[0]) return;
+       strncpy(buf2, p, MSG_SIZ);
+       snprintf(buf2 + (q-p), MSG_SIZ -(q-p), "%s%s", clickedWord, q+5);
+        p = buf2;
+    }
+    if(!strcmp(p, "$chat")) { // special case for opening chat
+        NewChat(clickedWord);
+    } else
     if(q = strstr(p, "$input")) {
        if(!shellUp[TextMenuDlg]) return;
        strncpy(buf, p, MSG_SIZ);
        strncpy(buf + (q-p), q+6, MSG_SIZ-(q-p));
        PutText(buf, q-p);
-       return;
+    } else {
+       snprintf(buf, MSG_SIZ, "%s\n", p);
+       SendToICS(buf);
+    }
+    if(click) { // popped up by memo click
+       click = clickedWord[0] = 0;
+       PopDown(TextMenuDlg);
     }
-    snprintf(buf, MSG_SIZ, "%s\n", p);
-    SendToICS(buf);
 }
 
 void
@@ -1267,21 +1282,6 @@ IcsKey (int n)
     SetInsertPos(&boxOptions[INPUT], strlen(val));
 }
 
-static void
-PutText (char *text, int pos)
-{
-    char buf[MSG_SIZ], *p;
-
-    if(strstr(text, "$add ") == text) {
-       GetWidgetText(&boxOptions[INPUT], &p);
-       snprintf(buf, MSG_SIZ, "%s%s", p, text+5); text = buf;
-       pos += strlen(p) - 5;
-    }
-    SetWidgetText(&boxOptions[INPUT], text, TextMenuDlg);
-    SetInsertPos(&boxOptions[INPUT], pos);
-    HardSetFocus(&boxOptions[INPUT]);
-}
-
 void
 ICSInputBoxPopUp ()
 {
@@ -1325,7 +1325,7 @@ PopUpMoveDialog (char firstchar)
 
 void
 BoxAutoPopUp (char *buf)
-{
+{       // only used in Xaw. GTK calls ConsoleAutoPopUp in stead (when we type to board)
        if(!appData.autoBox) return;
        if(appData.icsActive) { // text typed to board in ICS mode: divert to ICS input box
            if(DialogExists(InputBoxDlg)) { // box already exists: append to current contents
@@ -1333,7 +1333,7 @@ BoxAutoPopUp (char *buf)
                GetWidgetText(&boxOptions[INPUT], &p);
                snprintf(newText, MSG_SIZ, "%s%c", p, *buf);
                SetWidgetText(&boxOptions[INPUT], newText, InputBoxDlg);
-               if(shellUp[InputBoxDlg]) HardSetFocus (&boxOptions[INPUT]); //why???
+               if(shellUp[InputBoxDlg]) HardSetFocus (&boxOptions[INPUT], InputBoxDlg); //why???
            } else icsText = buf; // box did not exist: make sure it pops up with char in it
            ICSInputBoxPopUp();
        } else PopUpMoveDialog(*buf);
@@ -1706,19 +1706,49 @@ PromotionPopUp (char choice)
 
 //---------------------------- Chat Windows ----------------------------------------------
 
-static char *line, *memo, *partner, *texts[MAX_CHAT], dirty[MAX_CHAT];
-static int activePartner, hidden = 1;
+static char *line, *memo, *chatMemo, *partner, *texts[MAX_CHAT], dirty[MAX_CHAT], *inputs[MAX_CHAT], *icsLine, *tmpLine;
+static int activePartner;
+int hidden = 1;
 
 void ChatSwitch P((int n));
 int  ChatOK P((int n));
 
 #define CHAT_ICS     6
 #define CHAT_PARTNER 8
-#define CHAT_OUT    10
-#define CHAT_PANE   11
-#define CHAT_IN     12
+#define CHAT_OUT    11
+#define CHAT_PANE   12
+#define CHAT_IN     13
 
 void PaneSwitch P((void));
+void ClearChat P((void));
+
+WindowPlacement wpTextMenu;
+
+int
+ContextMenu (Option *opt, int button, int x, int y, char *text, int index)
+{ // callback for ICS-output clicks; handles button 3, passes on other events
+  char *start, *end;
+  int h;
+  if(button == -3) return TRUE; // supress default GTK context menu on up-click
+  if(button != 3) return FALSE;
+  start = end = text + index; // figure out what text was clicked
+  while(isalnum(*end)) end++;
+  while(start > text && isalnum(start[-1])) start--;
+  clickedWord[0] = NULLCHAR;
+  if(end-start >= 80) end = start + 80; // intended for small words and numbers
+  strncpy(clickedWord, start, end-start); clickedWord[end-start] = NULLCHAR;
+  click = !shellUp[TextMenuDlg]; // request auto-popdown of textmenu when we popped it up
+  h = wpTextMenu.height; // remembered height of text menu
+  if(h <= 0) h = 65;     // when not available, position w.r.t. top
+  GetPlacement(ChatDlg, &wpTextMenu);
+  if(opt->target == (void*) &chatMemo) wpTextMenu.y += (wpTextMenu.height - 30)/2; // click in chat
+  wpTextMenu.x += x - 50; wpTextMenu.y += y - h + 50; // request positioning
+  if(wpTextMenu.x < 0) wpTextMenu.x = 0;
+  if(wpTextMenu.y < 0) wpTextMenu.y = 0;
+  wpTextMenu.width = wpTextMenu.height = -1;
+  IcsTextProc();
+  return TRUE;
+}
 
 Option chatOptions[] = {
 {  0,  0,   0, NULL, NULL, "", NULL, Label , N_("Chats:") },
@@ -1727,23 +1757,63 @@ Option chatOptions[] = {
 { 3, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
 { 4, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
 { 5, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
-{ 250, T_VSCRL | T_FILL | T_WRAP | T_TOP,    510, NULL, (void*) &memo, NULL, NULL, TextBox, "" },
+{ 250, T_VSCRL | T_FILL | T_WRAP | T_TOP,    510, NULL, (void*) &memo, NULL, (void*) &ContextMenu, TextBox, "" },
 {  0,  0,   0, NULL, NULL, "", NULL, Break , "" },
 { 0,   T_TOP,    100, NULL, (void*) &partner, NULL, NULL, TextBox, N_("Chat partner:") },
+{  0, SAME_ROW, 0, NULL, (void*) &ClearChat,  NULL, NULL, Button, N_("End Chat") },
 {  0, SAME_ROW, 0, NULL, (void*) &PaneSwitch, NULL, NULL, Button, N_("Hide") },
-{ 250, T_VSCRL | T_FILL | T_WRAP | T_TOP,    510, NULL, (void*) &memo, NULL, NULL, TextBox, "" },
+{ 250, T_VSCRL | T_FILL | T_WRAP | T_TOP,    510, NULL, (void*) &chatMemo, NULL, (void*) &ContextMenu, TextBox, "" },
 {  0,  0,   0, NULL, NULL, "", NULL, Break , "" },
 {  0,    0,  510, NULL, (void*) &line, NULL, NULL, TextBox, "" },
 { 0, NO_OK|SAME_ROW, 0, NULL, (void*) &ChatOK, NULL, NULL, EndMark , "" }
 };
 
-void
+static void
+PutText (char *text, int pos)
+{
+    char buf[MSG_SIZ], *p;
+    DialogClass dlg = ChatDlg;
+    Option *opt = &chatOptions[CHAT_IN];
+
+    if(strstr(text, "$add ") == text) {
+       GetWidgetText(&boxOptions[INPUT], &p);
+       snprintf(buf, MSG_SIZ, "%s%s", p, text+5); text = buf;
+       pos += strlen(p) - 5;
+    }
+    if(shellUp[InputBoxDlg]) opt = &boxOptions[INPUT], dlg = InputBoxDlg; // for the benefit of Xaw give priority to ICS Input Box
+    SetWidgetText(opt, text, dlg);
+    SetInsertPos(opt, pos);
+    HardSetFocus(opt, dlg);
+    CursorAtEnd(opt);
+}
+
+int
 IcsHist (int n, Option *opt, DialogClass dlg)
 {   // [HGM] input: let up-arrow recall previous line from history
     char *val = NULL; // to suppress spurious warning
+    int chat, start;
 
-    if(opt != &chatOptions[CHAT_IN]) return;
+    if(opt != &chatOptions[CHAT_IN] && !(opt == &chatOptions[CHAT_PARTNER] && n == 33)) return 0;
     switch(n) {
+      case 33: // <Esc>
+       if(hidden) BoardToTop();
+       else PaneSwitch();
+       break;
+      case 15:
+       NewChat(lastTalker);
+       break;
+      case 14:
+       for(chat=0; chat < MAX_CHAT; chat++) if(!chatPartner[chat][0]) break;
+       if(chat < MAX_CHAT) ChatSwitch(chat + 1);
+       break;
+      case 10: // <Tab>
+       chat = start = (activePartner - hidden + MAX_CHAT) % MAX_CHAT;
+        while(!dirty[chat = (chat + 1)%MAX_CHAT]) if(chat == start) break;
+       if(!dirty[chat])
+        while(!chatPartner[chat = (chat + 1)%MAX_CHAT][0]) if(chat == start) break;
+       if(chat == start && hidden) chat = 0; // if all unused, start left
+        ChatSwitch(chat + 1);
+       break;
       case 1:
        GetWidgetText(opt, &val);
        val = PrevInHistory(val);
@@ -1753,6 +1823,7 @@ IcsHist (int n, Option *opt, DialogClass dlg)
     }
     SetWidgetText(opt, val = val ? val : "", dlg);
     SetInsertPos(opt, strlen(val));
+    return 1;
 }
 
 void
@@ -1783,15 +1854,15 @@ ChatOK (int n)
        safeStrCpy(chatPartner[activePartner], partner, MSG_SIZ);
        SetWidgetText(&chatOptions[CHAT_OUT], "", -1); // clear text if we alter partner
        SetWidgetText(&chatOptions[CHAT_IN], "", ChatDlg); // clear text if we alter partner
-       SetWidgetLabel(&chatOptions[activePartner+1], chatPartner[activePartner] ? chatPartner[activePartner] : _("New Chat"));
-       HardSetFocus(&chatOptions[CHAT_IN]);
+       SetWidgetLabel(&chatOptions[activePartner+1], chatPartner[activePartner][0] ? chatPartner[activePartner] : _("New Chat"));
+       HardSetFocus(&chatOptions[CHAT_IN], 0);
     }
     if(line[0] || hidden) { // something was typed (for ICS commands we also allow empty line!)
        SetWidgetText(&chatOptions[CHAT_IN], "", ChatDlg);
        // from here on it could be back-end
        if(line[strlen(line)-1] == '\n') line[strlen(line)-1] = NULLCHAR;
        SaveInHistory(line);
-       if(hidden) snprintf(buf, MSG_SIZ, "%s\n", line); else // command for ICS
+       if(hidden || !*chatPartner[activePartner]) snprintf(buf, MSG_SIZ, "%s\n", line); else // command for ICS
        if(!strcmp("whispers", chatPartner[activePartner]))
              snprintf(buf, MSG_SIZ, "whisper %s\n", line); // WHISPER box uses "whisper" to send
        else if(!strcmp("shouts", chatPartner[activePartner]))
@@ -1810,17 +1881,30 @@ ChatOK (int n)
 }
 
 void
+DelayedSetText ()
+{
+    SetWidgetText(&chatOptions[CHAT_IN], tmpLine, ChatDlg);
+    SetInsertPos(&chatOptions[CHAT_IN], strlen(tmpLine));
+}
+
+void
 DelayedScroll ()
 {   // If we do this immediately it does it before shrinking the memo, so the lower half remains hidden (Ughh!)
     SetInsertPos(&chatOptions[CHAT_ICS], 999999);
+    SetWidgetText(&chatOptions[CHAT_IN], tmpLine, ChatDlg);
+    SetInsertPos(&chatOptions[CHAT_IN], strlen(tmpLine));
 }
 
 void
 ChatSwitch (int n)
 {
     int i, j;
+    char *v;
     Show(&chatOptions[CHAT_PANE], 0); // show
     if(hidden) ScheduleDelayedEvent(DelayedScroll, 50); // Awful!
+    else ScheduleDelayedEvent(DelayedSetText, 50);
+    GetWidgetText(&chatOptions[CHAT_IN], &v);
+    if(hidden) { ASSIGN(icsLine, v); } else { ASSIGN(inputs[activePartner], v); }
     hidden = 0;
     activePartner = --n;
     if(!texts[n]) texts[n] = strdup("");
@@ -1832,14 +1916,46 @@ ChatSwitch (int n)
        SetWidgetLabel(&chatOptions[++j], *chatPartner[i] ? chatPartner[i] : _("New Chat"));
        SetColor(dirty[i] ? "#FFC000" : "#FFFFFF", &chatOptions[j]);
     }
-    SetWidgetText(&chatOptions[CHAT_IN], "", ChatDlg);
-    HardSetFocus(&chatOptions[strcmp(chatPartner[n], "") ? CHAT_IN : CHAT_PARTNER]);
+    if(!inputs[n]) { ASSIGN(inputs[n], ""); }
+//    SetWidgetText(&chatOptions[CHAT_IN], inputs[n], ChatDlg); // does not work (in this widget only)
+//    SetInsertPos(&chatOptions[CHAT_IN], strlen(inputs[n]));
+    tmpLine = inputs[n]; // for the delayed event
+    HardSetFocus(&chatOptions[strcmp(chatPartner[n], "") ? CHAT_IN : CHAT_PARTNER], 0);
 }
 
 void
 PaneSwitch ()
 {
+    char *v;
     Show(&chatOptions[CHAT_PANE], hidden = 1); // hide
+    GetWidgetText(&chatOptions[CHAT_IN], &v);
+    ASSIGN(inputs[activePartner], v);
+    if(!icsLine) { ASSIGN(icsLine, ""); }
+    tmpLine = icsLine; ScheduleDelayedEvent(DelayedSetText, 50);
+//    SetWidgetText(&chatOptions[CHAT_IN], icsLine, ChatDlg); // does not work (in this widget only)
+//    SetInsertPos(&chatOptions[CHAT_IN], strlen(icsLine));
+}
+
+void
+ClearChat ()
+{   // clear the chat to make it free for other use
+    chatPartner[activePartner][0] = NULLCHAR;
+    ASSIGN(texts[activePartner], "");
+    ASSIGN(inputs[activePartner], "");
+    SetWidgetText(&chatOptions[CHAT_PARTNER], "", ChatDlg);
+    SetWidgetText(&chatOptions[CHAT_OUT], "", ChatDlg);
+    SetWidgetText(&chatOptions[CHAT_IN], "", ChatDlg);
+    SetWidgetLabel(&chatOptions[activePartner+1], _("New Chat"));
+    HardSetFocus(&chatOptions[CHAT_PARTNER], 0);
+}
+
+static void
+NewChat (char *name)
+{   // open a chat on program request. If no empty one available, use last
+    int i;
+    for(i=0; i<MAX_CHAT-1; i++) if(!chatPartner[i][0]) break;
+    safeStrCpy(chatPartner[i], name, MSG_SIZ);
+    ChatSwitch(i+1);
 }
 
 void
@@ -1856,8 +1972,26 @@ ChatProc ()
 {
     if(GenericPopUp(chatOptions, _("ICS Interaction"), ChatDlg, BoardWindow, NONMODAL, appData.topLevel))
        AddHandler(&chatOptions[CHAT_PARTNER], ChatDlg, 2), AddHandler(&chatOptions[CHAT_IN], ChatDlg, 2); // treats return as OK
-    PaneSwitch(); HardSetFocus(&chatOptions[CHAT_IN]);
+    Show(&chatOptions[CHAT_PANE], hidden = 1); // hide
+//    HardSetFocus(&chatOptions[CHAT_IN], 0);
     MarkMenu("View.OpenChatWindow", ChatDlg);
+    CursorAtEnd(&chatOptions[CHAT_IN]);
+}
+
+void
+ConsoleAutoPopUp (char *buf)
+{
+       if(!appData.autoBox) return;
+       if(appData.icsActive) { // text typed to board in ICS mode: divert to ICS input box
+           if(DialogExists(ChatDlg)) { // box already exists: append to current contents
+               char *p, newText[MSG_SIZ];
+               GetWidgetText(&chatOptions[CHAT_IN], &p);
+               snprintf(newText, MSG_SIZ, "%s%c", p, *buf);
+               SetWidgetText(&chatOptions[CHAT_IN], newText, ChatDlg);
+               if(shellUp[ChatDlg]) HardSetFocus (&chatOptions[CHAT_IN], ChatDlg); //why???
+           } else { ASSIGN(line, buf); } // box did not exist: make sure it pops up with char in it
+           ChatProc();
+       } else PopUpMoveDialog(*buf);
 }
 
 //--------------------------------- Game-List options dialog ------------------------------------------