/*--- wvgroup.c ------------------------------------------- */ /* This file contains the window procedure for the Group Viewing window * for WinVN. */ #include #ifndef MAC #include "winundoc.h" #endif #include "WVglob.h" #include "WinVn.h" #ifdef MAC #include "MRRM1.h" #include #endif long NewCursorToTextLine (int X, int Y, TypDoc * DocPtr); long search_headers (TypDoc * HeaderDoc, TypHeader *headers, long artindex, long num_headers); LPSTR string_compare_insensitive (LPSTR a, LPSTR b); /*--- FUNCTION: WinVnViewWndProc -------------------------------------------- * * Window procedure for a Group window, which contains the subjects * of the various articles in a newsgroup. * Note that there may be several different Group windows active; * this routine gets called any time anything happens to any of them. */ long FAR PASCAL WinVnViewWndProc (hWnd, message, wParam, lParam) HWND hWnd; UINT message; WPARAM wParam; LPARAM lParam; { FARPROC lpProcAbout; HMENU hMenu; PAINTSTRUCT ps; /* paint structure */ HDC hDC; /* handle to display context */ RECT clientRect; /* selection rectangle */ HDC hDCView; TypDoc *ThisDoc; int ih, j; int iart; long artindex; int found; int imemo; int CtrlState; TypBlock far *BlockPtr, far * ArtBlockPtr; TypLine far *LinePtr, far * ArtLinePtr; HANDLE hBlock; unsigned int Offset; char mybuf[MAXINTERNALLINE]; TypDoc *MyDoc; TypLineID MyLineID; POINT ptCursor; int X, Y, nWidth, nHeight; int mylen; int OldSel = FALSE; /* We know what *window* is being acted on, but we must find * out which *document* is being acted on. There's a one-to-one * relationship between the two, and we find out which document * corresponds to this window by scanning the GroupDocs array. */ for (ih = 0, found = FALSE; !found && ih < MAXGROUPWNDS; ih++) { if (GroupDocs[ih].hDocWnd == hWnd) { found = TRUE; ThisDoc = &(GroupDocs[ih]); } } if (!found) { ThisDoc = CommDoc; } switch (message) { case WM_ACTIVATE: if (wParam) { ActiveGroupDoc = ThisDoc; #if 0 SetMenuBar (groupMenuBar); DrawMenuBar (); #endif } /* fall through */ case WM_SYSCOMMAND: return (DefWindowProc (hWnd, message, wParam, lParam)); case WM_SIZE: GetClientRect (hWnd, &clientRect); ThisDoc->ScXWidth = clientRect.right; ThisDoc->ScYHeight = clientRect.bottom; ThisDoc->ScYLines = (clientRect.bottom - clientRect.top - TopSpace) / LineHeight; ThisDoc->ScXChars = (clientRect.right - clientRect.left - SideSpace) / CharWidth; break; case WM_DESTROY: /* Unlink all the article windows that belong to this group */ UpdateSeenArts (ThisDoc); UnlinkArtsInGroup (ThisDoc); ThisDoc->InUse = FALSE; if (ThisDoc == CommDoc) { CommBusy = FALSE; CommDoc = (TypDoc *) NULL; } /* Clear the pointer in the line for this group in the */ /* NetDoc document. This pointer currently points */ /* to the current document, which we are wiping out */ /* with the destruction of this window. */ LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID, &BlockPtr, &LinePtr); { TypGroup far *group = (TypGroup far *) (((char far *) LinePtr) + sizeof (TypLine)); group->SubjDoc = (TypDoc *) NULL; /* Free the header table */ if (group->header_handle) GlobalFree (group->header_handle); group->header_handle = (HANDLE) NULL; group->total_headers = 0; } UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID); /* Clear document */ FreeDoc (ThisDoc); /* If there's another group window, make it the active */ /* group window so we don't create a new one if the */ /* New Group flag is FALSE. */ for (j = MAXGROUPWNDS - 1; j >= 0; j--) { if (GroupDocs[j].InUse) { ActiveGroupDoc = &(GroupDocs[j]); break; } } break; case WM_KEYDOWN: /* See if this key should be mapped to a scrolling event * for which we have programmed the mouse. If so, * construct the appropriate mouse call and call the mouse code. */ #ifndef MAC if (wParam == VK_F6) { NextWindow (ThisDoc); } else { CtrlState = GetKeyState (VK_CONTROL) < 0; for (j = 0; j < NUMKEYS; j++) { if (wParam == key2scroll[j].wVirtKey && CtrlState == key2scroll[j].CtlState) { SendMessage (hWnd, key2scroll[j].iMessage, key2scroll[j].wRequest, 0L); break; } } } #endif break; case WM_CHAR: /* Carriage Return means the same as double-clicking * on where the cursor is currently pointing. */ if (wParam == '\r') { GetCursorPos (&ptCursor); ScreenToClient (hWnd, &ptCursor); #ifdef MAC X = ptCursor.h; Y = ptCursor.v; #else X = ptCursor.x; Y = ptCursor.y; #endif goto getarticle; } else { } break; case WM_LBUTTONDOWN: /* Clicking the left button on an article name toggles the * selected/not selected status of that article. * Currently selected articles are displayed in reverse video. */ break; case WM_LBUTTONDBLCLK: /* Double-clicking on an article subject creates an "Article" * window, whose purpose is to display the article. */ X = LOWORD (lParam); Y = HIWORD (lParam); getarticle:; artindex = NewCursorToTextLine (X, Y, ThisDoc); if (artindex > -1 ) { ViewArticle (ThisDoc, artindex, FALSE); } break; case WM_VSCROLL: NewScrollIt (ThisDoc, wParam, lParam); break; case WM_PAINT: { HANDLE hBlock; int MyLen, width; unsigned int Offset; int VertLines, HorzChars; int EndofDoc = FALSE; int RangeHigh, CurPos; int RestX, indicatorwidth, Xtext; char far *textptr; char indicator; char *indcptr; TypArticle far *MyArt; unsigned int artindex; long int artnum; TypBlock far *BlockPtr; TypLine far *LinePtr; TypBlock far *NetBlockPtr; TypLine far *NetLinePtr; TypGroup far *group; TypHeader *headers; HANDLE header_handle; char scratch_line [MAXINTERNALLINE]; long int OldHighestSeen; HANDLE hBlackBrush; DWORD MyColors[4], MyBack[4]; #ifdef MAC RECT myRect; POINT myPoint; #endif DWORD Rop; int MyColorMask = 0; int PrevColorMask = MyColorMask; SIZE string_size; /* MyColors and MyBack are arrays of colors used to display text * foreground and background. * The ColorMask variables are indices into these arrays. * We set and clear bits in these indices depending upon * whether the article has been selected or seen. */ #define SEEN_MASK 1 #define SELECT_MASK 2 hDC = BeginPaint (hWnd, &ps); GetClientRect (hWnd, &clientRect); SelectObject (hDC, hFont); VertLines = ( (ThisDoc->ScYLines > (ThisDoc->TotalLines - ThisDoc->TopLineOrd)) ? (ThisDoc->TotalLines - ThisDoc->TopLineOrd ) : ThisDoc->ScYLines); HorzChars = ThisDoc->ScXChars; MyColors[0] = GetTextColor (hDC); /* black */ MyColors[1] = GroupSeenColor; MyColors[2] = GetBkColor (hDC); /* white */ MyColors[3] = MyColors[1]; /* blue */ MyBack[0] = MyColors[2];/* white */ MyBack[1] = MyColors[2]; MyBack[2] = MyColors[0]; MyBack[3] = MyColors[0]; /* LockLine (ThisDoc->hCurTopScBlock, ThisDoc->TopScOffset, ThisDoc->TopScLineID, &BlockPtr, &LinePtr); */ /* Update the scroll bar thumb position. */ CurPos = ThisDoc->TopLineOrd; if (CurPos < 0) CurPos = 0; RangeHigh = ThisDoc->TotalLines - VertLines; if (RangeHigh < 0) RangeHigh = 0; SetScrollRange (hWnd, SB_VERT, 0, RangeHigh, FALSE); SetScrollPos (hWnd, SB_VERT, CurPos, TRUE); GetTextExtentPoint (hDC, "s 99999 ", 8, &string_size); indicatorwidth = string_size.cx; LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID, &NetBlockPtr, &NetLinePtr); group = (TypGroup far *) ((char far *) NetLinePtr + sizeof (TypLine)); header_handle = group->header_handle; if (header_handle) headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof(char *)); OldHighestSeen = group->HighestPrevSeen; UnlockLine (NetBlockPtr, NetLinePtr, &hBlock, &Offset, &MyLineID); #ifdef MAC myRect.right = ThisDoc->DocClipRect.right; myRect.top = 0; myRect.bottom = LineHeight; #endif /* Now paint this stuff on the screen. */ X = SideSpace; Xtext = X + indicatorwidth; Y = StartPen; artindex = ThisDoc->TopLineOrd; if (ThisDoc->TotalLines) do { artnum = headers[artindex].number; indicator = ' '; if(headers[artindex].Seen) { indicator = 's'; } else if(OldHighestSeen) { if(headers[artindex].number > OldHighestSeen) { indicator = 'n'; } } if(ThisDoc->FindOffset == artindex) indicator = '>'; sprintf (scratch_line, "%c %u %6.6Fs %20.20Fs %4Fd %Fs ", indicator, headers[artindex].number, headers[artindex].date, headers[artindex].from, headers[artindex].lines, headers[artindex].subject); MyLen = lstrlen (scratch_line); /* Figure out the color of this line. */ if (headers[artindex].Seen) { MyColorMask |= SEEN_MASK; } else { MyColorMask &= (0xff - SEEN_MASK); } if (headers[artindex].Selected) { MyColorMask |= SELECT_MASK; Rop = BLACKNESS; } else { MyColorMask &= 0xff - SELECT_MASK; Rop = WHITENESS; } if (MyColorMask != PrevColorMask) { SetTextColor (hDC, MyColors[MyColorMask]); SetBkColor (hDC, MyBack[MyColorMask]); PrevColorMask = MyColorMask; } /* Now write out the line. */ GetTextExtentPoint (hDC, scratch_line, MyLen, &string_size); width = string_size.cx; TextOut (hDC, X, Y, scratch_line, MyLen); #ifdef MAC GetPen (&myPoint); myRect.left = myPoint.h; FillRect (&myRect, white); myRect.top += LineHeight; myRect.bottom += LineHeight; #else RestX = X + width; PatBlt (hDC, RestX, Y, clientRect.right - RestX, LineHeight, Rop); #endif #if 0 if (MyLen < HorzChars) { RestX = X + width; /* TextOut(hDC,RestX,Y,Blanks,MAXINTERNALLINE); */ PatBlt (hDC, RestX, Y, clientRect.right - RestX, LineHeight, Rop); } #endif Y += LineHeight; artindex++; } while (--VertLines > 0 ); if (header_handle) GlobalUnlock (header_handle); /* We've reached the end of the data to be displayed */ /* on this window. If there's more screen real estate */ /* left, just blank it out. */ SetTextColor (hDC, MyColors[0]); SetBkColor (hDC, MyBack[0]); PatBlt (hDC, 0, Y, clientRect.right, clientRect.bottom - Y, PATCOPY); EndPaint (hWnd, &ps); break; } case WM_COMMAND: switch (wParam) { case IDV_EXIT: DestroyWindow (hWnd); break; case IDV_NEXT: break; case IDM_FIND: case IDM_FIND_NEXT_SAME: FindDoc = ThisDoc; if (wParam == IDM_FIND) DialogBox (hInst, "WinVnFind", hWnd, lpfnWinVnFindDlg); if(strcmp(FindDoc->SearchStr,"")) { TypGroup far * group; TypHeader *headers; HANDLE header_handle; int starting_at; LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID, &BlockPtr, &LinePtr); group = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)); header_handle = group->header_handle; headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof(char *)); UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID); /* 'Find Next' will start one line after */ starting_at = ThisDoc->TopLineOrd + ((wParam == IDM_FIND) ? 0 : 1); /* back up one if we're at the end */ if (starting_at >= group->total_headers) starting_at--; found = search_headers (ThisDoc, headers, starting_at, group->total_headers); if (found == -1) { strcpy (mybuf, "\""); strcat (mybuf, ThisDoc->SearchStr); strcat (mybuf, "\" not found."); MessageBox (hWnd, mybuf, "Not found", MB_OK); } else { ThisDoc->TopLineOrd = found; ThisDoc->FindOffset = found; InvalidateRect(ThisDoc->hDocWnd,NULL,FALSE); } GlobalUnlock (header_handle); } break; case IDV_CREATE: /* We are creating the skeleton text of a new posting. * Most of the work is done by CreatePostingWnd and * CreatePostingText. Here we have to identify * the newsgroup for those routines. * Get the newsgroup from the line in NetDoc that * points to this document. */ LockLine (ThisDoc->hParentBlock, ThisDoc->ParentOffset, ThisDoc->ParentLineID, &BlockPtr, &LinePtr); ExtractTextLine (ThisDoc->ParentDoc, LinePtr, mybuf, MAXINTERNALLINE); UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID); NewsgroupsPtr = mybuf; CreatePostingWnd ((TypDoc *) NULL, DOCTYPE_POSTING); break; } break; default: return (DefWindowProc (hWnd, message, wParam, lParam)); } return (0); } /* --- function CopyNonBlank ---------------------------------------- * * Copy a string, terminating at the first blank or zero byte. * * Entry strtarg FWA of target area. * strsource FWA of source string. * maxchars the maximum number of characters to copy * if no blank or zero is encountered. * * Exit returns the number of characters copied. * * I believe this routine is no longer used, but I'm leaving it here. */ int CopyNonBlank (strtarg, strsource, maxchars) char *strtarg, *strsource; int maxchars; { int j; for (j = 0; j < maxchars && strsource[j] != ' '; j++) strtarg[j] = strsource[j]; return (j); } /* --- function FileLength ------------------------------------------- * * Find the size, in bytes, of a file. * * Entry hFile handle of the file in question. * * Exit returns the length of the file in bytes. * * This routine is no longer used. */ long FileLength (hFile) HANDLE hFile; { long lCurrentPos = _llseek (hFile, 0L, 1); long lFileLength = _llseek (hFile, 0L, 2); _llseek (hFile, lCurrentPos, 0); return lFileLength; } /*-- function ViewArticle ------------------------------------------------- * * View a given article. Either create a new window for it or * recycle an existing window. * This function requests an article from the server, so there * must not already be a transaction in progress. * * Entry Doc points to the document for this group. * artindex index into header array for this group. * Reuse is TRUE if we ought to reuse the * currently active article window (if any). */ void ViewArticle (Doc, artindex, Reuse) TypDoc *Doc; long artindex; BOOL Reuse; { TypDoc *MyDoc; TypGroup far *GroupDoc; BOOL newdoc; BOOL found; int docnum; HANDLE hBlock; unsigned int Offset; TypLineID MyLineID; TypBlock far *BlockPtr; TypLine far *LinePtr; HWND hWndArt; int width; char mybuf[MAXINTERNALLINE]; long int artnum,oldindex; char far *lpsz; HWND hWndGroup = Doc->hDocWnd; char far *lpszGroupName; HANDLE header_handle; TypHeader * headers; LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &BlockPtr, &LinePtr); GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)); header_handle = GroupDoc->header_handle; headers = (TypHeader *) ((char *)GlobalLock (header_handle) + sizeof(char *)) ; if (MyDoc = headers[artindex].ArtDoc) { /* We already have a document containing the article */ /* so just activate it. */ /* ShowWindow(MyDoc->hDocWnd,SW_SHOW); */ SetActiveWindow (MyDoc->hDocWnd); SetFocus (MyDoc->hDocWnd); goto endit; } if (CommBusy) { MessageBox (hWndGroup, "Sorry, I am already busy retrieving information from the server.\n\ Try again in a little while.", "Can't request text of article", MB_OK | MB_ICONASTERISK); goto endit; } newdoc = FALSE; if ((NewArticleWindow && !Reuse) || !ActiveArticleDoc || !(ActiveArticleDoc->InUse)) { found = FALSE; for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++) { if (!ArticleDocs[docnum].InUse) { found = TRUE; newdoc = TRUE; CommDoc = &(ArticleDocs[docnum]); break; } } if (!found) { MessageBox (hWndGroup, "You have too many article windows \ active;\nClose one or uncheck the option \"New Window for each Article\".", "Can't open new window", MB_OK | MB_ICONASTERISK); goto endit; } } else { /* Must reuse old window for this article. */ CommDoc = ActiveArticleDoc; if (CommDoc->ParentDoc == Doc) { oldindex = CommDoc->ParentOffset; /* more slot abusage */ headers[oldindex].ArtDoc = (TypDoc *) NULL; } /* clear out old doc */ FreeDoc (CommDoc); } headers[artindex].Seen = TRUE; InvalidateRect (hWndGroup, NULL, FALSE); lpsz = (char far *) headers[artindex].subject; strcpy (mybuf, "Retrieving \""); lstrcat (mybuf, lpsz); lstrcat (mybuf, "\""); if (newdoc) { if (xScreen > 88 * CharWidth) { width = 88 * CharWidth; } else { width = xScreen - 1 * CharWidth; } hWndArt = CreateWindow ("WinVnArt", mybuf, WS_OVERLAPPEDWINDOW | WS_VSCROLL , xScreen - (width + (docnum) * CharWidth), /* Initial X pos */ (int) (yScreen * 3 / 8) + (docnum) * LineHeight, /* Initial Y pos */ (int) width, /* Initial X Width */ (int) (yScreen * 5 / 8) - (1 * LineHeight), /* Initial Y height */ NULL, NULL, hInst, NULL); if (!hWndArt) return; /* ??? */ #ifndef MAC ShowWindow (hWndArt, SW_SHOWNORMAL); #else MyShowWindow (hWndArt, SW_SHOWNORMAL); #endif } else { hWndArt = CommDoc->hDocWnd; SetWindowText (hWndArt, mybuf); } /* Now that we have created the window, create the corresponding * document, and make the new window active. */ InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE); CommDoc->InUse = TRUE; CommDoc->LastSeenLineID = artindex; /* Keep an index with the article */ SetActiveWindow (hWndArt); SetFocus (hWndArt); headers[artindex].ArtDoc = CommDoc; CommDoc->ParentOffset = artindex; InvalidateRect (hWndArt, NULL, FALSE); UpdateWindow (hWndArt); CommLinePtr = CommLineIn; CommBusy = TRUE; CommState = ST_ARTICLE_RESP; /* If we're not already in this group on the server, * send out a GROUP command for this window so we get back * into the right Group. */ LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &BlockPtr, &LinePtr); lpszGroupName = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup); if (lstrcmp (CurrentGroup, lpszGroupName)) { CommState = ST_GROUP_REJOIN; strcpy (mybuf, "GROUP "); lstrcat (mybuf, lpszGroupName); /* lstrcat(mybuf,"\r"); */ mylstrncpy (CurrentGroup, lpsz, MAXGROUPNAME); PutCommLine (mybuf, lstrlen (mybuf)); } UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID); artnum = headers[artindex].number; sprintf (mybuf, "ARTICLE %ld\r", artnum); PutCommLine (mybuf, lstrlen (mybuf)); endit: GlobalUnlock (header_handle); } void view_article_by_message_id (Doc, article_request, artindex) TypDoc *Doc; char far * article_request; long artindex; { TypDoc *MyDoc; TypGroup far *GroupDoc; BOOL newdoc; BOOL found; int docnum; HANDLE hBlock; unsigned int Offset; TypLineID MyLineID; TypBlock far *BlockPtr; TypLine far *LinePtr; HWND hWndArt; int width; char mybuf[MAXINTERNALLINE]; long int artnum; char far *lpsz; HWND hWndGroup = Doc->hDocWnd; char far *lpszGroupName; HANDLE header_handle; TypHeader * headers; LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &BlockPtr, &LinePtr); GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)); header_handle = GroupDoc->header_handle; headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof(char *)) ; if (CommBusy) { MessageBox (hWndGroup, "Sorry, I am already busy retrieving information from the server.\n\ Try again in a little while.", "Can't request text of article", MB_OK | MB_ICONASTERISK); goto endit; } newdoc = FALSE; if ((NewArticleWindow) || !ActiveArticleDoc || !(ActiveArticleDoc->InUse)) { found = FALSE; for (docnum = 0; docnum < MAXARTICLEWNDS; docnum++) { if (!ArticleDocs[docnum].InUse) { found = TRUE; newdoc = TRUE; CommDoc = &(ArticleDocs[docnum]); break; } } if (!found) { MessageBox (hWndGroup, "You have too many article windows \ active;\nClose one or uncheck the option \"New Window for each Article\".", "Can't open new window", MB_OK | MB_ICONASTERISK); goto endit; } } else { /* Must reuse old window for this article. */ CommDoc = ActiveArticleDoc; /* sever the article/artindex connection */ headers[CommDoc->ParentOffset].ArtDoc = (TypDoc *) NULL; /* clear out old doc */ FreeDoc (CommDoc); } headers[artindex].Seen = TRUE; InvalidateRect (hWndGroup, NULL, FALSE); strcpy (mybuf, "Retrieving \""); lstrcat (mybuf, article_request); lstrcat (mybuf, "\""); if (newdoc) { if (xScreen > 88 * CharWidth) { width = 88 * CharWidth; } else { width = xScreen - 1 * CharWidth; } hWndArt = CreateWindow ("WinVnArt", mybuf, WS_OVERLAPPEDWINDOW | WS_VSCROLL , xScreen - (width + (docnum) * CharWidth), /* Initial X pos */ (int) (yScreen * 3 / 8) + (docnum) * LineHeight, /* Initial Y pos */ (int) width, /* Initial X Width */ (int) (yScreen * 5 / 8) - (1 * LineHeight), /* Initial Y height */ NULL, NULL, hInst, NULL); if (!hWndArt) return; /* ??? */ #ifndef MAC ShowWindow (hWndArt, SW_SHOWNORMAL); #else MyShowWindow (hWndArt, SW_SHOWNORMAL); #endif } else { hWndArt = CommDoc->hDocWnd; SetWindowText (hWndArt, mybuf); } /* Now that we have created the window, create the corresponding * document, and make the new window active. */ InitDoc (CommDoc, hWndArt, Doc, DOCTYPE_ARTICLE); CommDoc->InUse = TRUE; CommDoc->LastSeenLineID = artindex; /* Keep an index with the article */ SetActiveWindow (hWndArt); SetFocus (hWndArt); headers[artindex].ArtDoc = CommDoc; InvalidateRect (hWndArt, NULL, FALSE); UpdateWindow (hWndArt); CommLinePtr = CommLineIn; CommBusy = TRUE; CommState = ST_ARTICLE_RESP; /* If we're not already in this group on the server, * send out a GROUP command for this window so we get back * into the right Group. */ LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &BlockPtr, &LinePtr); lpszGroupName = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup); if (lstrcmp (CurrentGroup, lpszGroupName)) { CommState = ST_GROUP_REJOIN; strcpy (mybuf, "GROUP "); lstrcat (mybuf, lpszGroupName); /* lstrcat(mybuf,"\r"); */ mylstrncpy (CurrentGroup, lpsz, MAXGROUPNAME); PutCommLine (mybuf, lstrlen (mybuf)); } UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID); artnum = headers[artindex].number; lstrcpy (mybuf, article_request); PutCommLine (mybuf, lstrlen (mybuf)); endit: GlobalUnlock (header_handle); } /*-- Function UnlinkArtsInGroup --------------------------------------- * * Modify all the article documents and all the article windows currently * associated with a group so that none of them points to that group. * Used when the group window is going away or is being recycled. * * Entry GroupDoc points to the document to which references * should be eliminated. */ void UnlinkArtsInGroup (GroupDoc) TypDoc *GroupDoc; { int iart; for (iart = 0; iart < MAXARTICLEWNDS; iart++) { if (ArticleDocs[iart].InUse && ArticleDocs[iart].ParentDoc == GroupDoc) { ArticleDocs[iart].ParentDoc = (TypDoc *) NULL; ArticleDocs[iart].hParentBlock = 0; } } } /*--- function UpdateSeenArts ------------------------------------------- * * Given a Group document, update the TypGroup line for * that document in the Net document with respect to which * articles have been seen. * This routine would typically be called just before a Group document * is going to be destroyed or erased. That would be the time to * take the information in the TypArticle structures of each line * in the document and transfer it to the line in the NetDoc document * corresponding to this group. * * This routine has to take information of the form: * 123:Unseen; 124:Seen; 125:Unseen; 126:Unseen; 127:Seen; 128:Seen; 129:Seen * found in the TypArticle structures in consecutive lines in the document * and transform it to the general form used by .newsrc files: * 124,127-129 * (though we are using our internal representation & not ASCII characters). * * Entry Doc points to the document for this group. * * Exit The line in the Net document corresponding to this * group has been updated. */ void UpdateSeenArts (Doc) TypDoc *Doc; { TypRange MyRange, *RangePtr; TypGroup *group; TypLine far *LinePtr, far * ParentLine; TypBlock far *BlockPtr, far * ParentBlock; HANDLE hLine,header_handle; TypLine *LocalLinePtr; TypHeader * headers; TypArticle far *Art; BOOL InSeen = TRUE; unsigned int MyLength; unsigned int maxRanges; long artindex; /* Get the line in the Net document that corresponds to this * group. Make a local copy of it and set RangePtr to point to * the first range in that line. We will ignore the old line's * "seen" data and create the information afresh from what we * have in this document. */ LockLine (Doc->hParentBlock, Doc->ParentOffset, Doc->ParentLineID, &ParentBlock, &ParentLine); hLine = LocalAlloc (LMEM_MOVEABLE, BLOCK_SIZE); LocalLinePtr = (TypLine *) LocalLock (hLine); group = (TypGroup *) ((char *) LocalLinePtr + sizeof (TypLine)); MoveBytes (ParentLine, LocalLinePtr, ParentLine->length); header_handle = group->header_handle; headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof (char *)); group->nRanges = 0; maxRanges = ((Doc->BlockSize - Doc->SplitSize) - ParentLine->length + group->nRanges * sizeof (TypRange)) / sizeof (TypRange) - 1; RangePtr = (TypRange *) ((char *) LocalLinePtr + sizeof (TypLine) + RangeOffset (group->NameLen)); MyRange.First = 1; /* Get the first line in this document. * If it cannot be found, just set Last=First and skip the * proceeding processing. Otherwise, assume we've seen everything * up to but not including the first article in the document. */ /* LockLine (Doc->hFirstBlock, sizeof (TypBlock), 0L, &BlockPtr, &LinePtr); */ artindex = 0; if (!Doc->TotalLines) { MyRange.Last = 1; } else { MyRange.Last = headers[artindex].number - 1; /* Loop to scan through the document, fabricating article ranges. */ do { if (headers[artindex].Seen) { if (InSeen) { /* Continuing a sequence of seen articles. */ MyRange.Last = headers[artindex].number; } else { /* Starting a new sequence of seen articles. */ MyRange.First = headers[artindex].number; MyRange.Last = headers[artindex].number; InSeen = TRUE; } } else { if (InSeen) { /* Ending a sequence of seen articles. */ InSeen = FALSE; *(RangePtr++) = MyRange; (group->nRanges)++; } else { /* Continuing a sequence of unseen articles. */ } } } while ((group->nRanges < maxRanges) && ((++artindex < Doc->TotalLines))); if (InSeen) { *(RangePtr++) = MyRange; (group->nRanges)++; } } MyLength = sizeof (TypLine) + RangeOffset (group->NameLen) + sizeof (TypRange) * (group->nRanges) + sizeof (int); LocalLinePtr->length = MyLength; *(int *) ((char *) LocalLinePtr + MyLength - sizeof (int)) = MyLength; ReplaceLine (LocalLinePtr, &ParentBlock, &ParentLine); GlobalUnlock (ParentBlock->hCurBlock); LocalUnlock (hLine); LocalFree (hLine); } /*-- function CursorToTextLine ---------------------------------------- * * Routine to locate a text line in a document, based on the * cursor position. Used to figure out which line is being selected * when a user clicks a mouse button. * * Entry X, Y are the position of the cursor. * DocPtr points to the current document. * * Exit *LinePtr points to the current line, if one was found. * *BlockPtr points to the current block, if found. * Function returns TRUE iff a line was found that corresponds * to the cursor position. */ long NewCursorToTextLine (X, Y, DocPtr) int X; int Y; TypDoc *DocPtr; { int found; int SelLine; if (Y < TopSpace || Y > TopSpace + DocPtr->ScYLines * LineHeight || X < SideSpace) { /* Cursor is in no-man's-land at edge of window. */ return(-1); } else { SelLine = (Y - TopSpace) / LineHeight; return ( DocPtr->TopLineOrd + SelLine); } } long search_headers (TypDoc * HeaderDoc, TypHeader *headers, long artindex, long num_headers) { do { if (string_compare_insensitive (headers[artindex].subject, HeaderDoc->SearchStr)) return (artindex); /* return the index */ } while (artindex++ < (num_headers - 1)); return (-1); /* not found */ } LPSTR string_compare_insensitive (LPSTR a, LPSTR b) { int lena = lstrlen (a); int lenb = lstrlen (b); int count; for ( count = lena - lenb + 1 ; count > 0 ; count--, a++) if (_strnicmp (a,b,lenb) == 0) return a; return (NULL); }