home *** CD-ROM | disk | FTP | other *** search
- unit main;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- Menus, OleCtrls, StdCtrls, ComCtrls, ExtCtrls, Buttons, ActiveX, isp3;
-
- const
- efListGroups = 0;
- efGetArticleHeaders = 1;
- efGetArticleNumbers = 2;
- efGetArticle = 3;
-
- type
- TNewsForm = class(TForm)
- NNTP1: TNNTP;
- MainMenu1: TMainMenu;
- File1: TMenuItem;
- Exit1: TMenuItem;
- N1: TMenuItem;
- FileDisconnectItem: TMenuItem;
- FileConnectItem: TMenuItem;
- Panel1: TPanel;
- Bevel1: TBevel;
- StatusBar: TStatusBar;
- SmallImages: TImageList;
- Panel2: TPanel;
- NewsGroups: TTreeView;
- Bevel2: TBevel;
- Panel3: TPanel;
- Memo1: TMemo;
- Panel5: TPanel;
- Panel4: TPanel;
- ConnectBtn: TSpeedButton;
- RefreshBtn: TSpeedButton;
- Bevel3: TBevel;
- MsgHeaders: TListBox;
- Label1: TLabel;
- Label2: TLabel;
- procedure FileConnectItemClick(Sender: TObject);
- procedure NNTP1ProtocolStateChanged(Sender: TObject;
- ProtocolState: Smallint);
- procedure NNTP1StateChanged(Sender: TObject; State: Smallint);
- procedure Exit1Click(Sender: TObject);
- procedure MsgHeadersDblClick(Sender: TObject);
- procedure FormClose(Sender: TObject; var Action: TCloseAction);
- procedure NewsGroupsChange(Sender: TObject; Node: TTreeNode);
- procedure RefreshBtnClick(Sender: TObject);
- procedure FileDisconnectItemClick(Sender: TObject);
- procedure NNTP1Banner(Sender: TObject; const Banner: WideString);
- procedure NNTP1DocOutput(Sender: TObject; const DocOutput: DocOutput);
- procedure NNTP1Error(Sender: TObject; Number: Smallint;
- var Description: WideString; Scode: Integer; const Source,
- HelpFile: WideString; HelpContext: Integer;
- var CancelDisplay: WordBool);
- procedure NNTP1SelectGroup(Sender: TObject;
- const groupName: WideString; firstMessage, lastMessage,
- msgCount: Integer);
- private
- EventFlag: Integer;
- function NodePath(Node: TTreeNode): String;
- public
- Data: String;
- end;
-
- var
- NewsForm: TNewsForm;
- Remainder: String;
- Nodes: TStringList;
- CurrentGroup: String;
- GroupCount: Integer;
-
- implementation
-
- uses Connect;
-
- {$R *.DFM}
-
- { TParser }
-
- type
-
- TToken = (etEnd, etSymbol, etName, etLiteral);
-
- TParser = class
- private
- FFlags: Integer;
- FText: string;
- FSourcePtr: PChar;
- FSourceLine: Integer;
- FTokenPtr: PChar;
- FTokenString: string;
- FToken: TToken;
- procedure SkipBlanks;
- procedure NextToken;
- public
- constructor Create(const Text: string; Groups: Boolean);
- end;
-
- const
- sfAllowSpaces = 1;
-
- constructor TParser.Create(const Text: string; Groups: Boolean);
- begin
- FText := Text;
- FSourceLine := 1;
- FSourcePtr := PChar(Text);
- if Groups then
- FFlags := sfAllowSpaces
- else
- FFlags := 0;
- NextToken;
- end;
-
- procedure TParser.SkipBlanks;
- begin
- while True do
- begin
- case FSourcePtr^ of
- #0:
- begin
- if FSourcePtr^ = #0 then Exit;
- Continue;
- end;
- #10:
- Inc(FSourceLine);
- #33..#255:
- Exit;
- end;
- Inc(FSourcePtr);
- end;
- end;
-
- procedure TParser.NextToken;
- var
- P, TokenStart: PChar;
- begin
- SkipBlanks;
- FTokenString := '';
- P := FSourcePtr;
- while (P^ <> #0) and (P^ <= ' ') do Inc(P);
- FTokenPtr := P;
- case P^ of
- '0'..'9':
- begin
- TokenStart := P;
- Inc(P);
- while P^ in ['0'..'9'] do Inc(P);
- SetString(FTokenString, TokenStart, P - TokenStart);
- FToken := etLiteral;
- end;
- #13: Inc(FSourceLine);
- #0:
- FToken := etEnd;
- else
- begin
- TokenStart := P;
- Inc(P);
- if FFlags = sfAllowSpaces then
- while not (P^ in [#0, #13, ' ']) do Inc(P)
- else
- while not (P^ in [#0, #13]) do Inc(P);
- SetString(FTokenString, TokenStart, P - TokenStart);
- FToken := etSymbol;
- end;
- end;
- FSourcePtr := P;
- end;
-
- function FirstItem(var ItemList: ShortString): ShortString;
- var
- P: Integer;
- begin
- P := AnsiPos('.', ItemList);
- if P = 0 then
- begin
- Result := ItemList;
- P := Length(ItemList);
- end
- else
- Result := Copy(ItemList, 1, P - 1);
- Delete(ItemList, 1, P);
- end;
-
- procedure AddItem(GroupName: ShortString);
- var
- Index, i: Integer;
- Groups: Integer;
- Item: ShortString;
- TheNodes: TStringList;
- begin
- Groups := 1;
- for i := 0 to Length(GroupName) do
- if GroupName[i] = '.' then
- Inc(Groups);
- TheNodes := Nodes;
- for i := 0 to Groups - 1 do
- begin
- Item := FirstItem(GroupName);
- Index := TheNodes.IndexOf(Item);
- if Index = -1 then
- begin
- Index := TheNodes.AddObject(Item, TStringList.Create);
- TheNodes := TStringList(TheNodes.Objects[Index]);
- TheNodes.Sorted := True;
- end
- else
- TheNodes := TStringList(TheNodes.Objects[Index]);
- end;
- Inc(GroupCount);
- end;
-
- procedure ParseGroups(Data: String);
- var
- Parser: TParser;
- OldSrcLine: Integer;
- begin
- Parser := TParser.Create(Data, True);
- OldSrcLine := 0;
- while Parser.FToken <> etEnd do
- begin
- if Parser.FSourceLine <> OldSrcLine then
- begin
- AddItem(Parser.FTokenString);
- OldSrcLine := Parser.FSourceLine;
- end;
- Parser.NextToken;
- end;
- end;
-
- procedure ParseHeaders(Data: String);
- var
- Parser: TParser;
- MsgNo: LongInt;
- Header: String;
- OldSrcLine: Integer;
- begin
- Parser := TParser.Create(Data, False);
- while Parser.FToken <> etEnd do
- begin
- MsgNo := StrToInt(Parser.FTokenString);
- OldSrcLine := Parser.FSourceLine;
- Parser.NextToken;
- Header := '';
- while (OldSrcLine = Parser.FSourceLine) do
- begin
- Header := Header + ' ' + Parser.FTokenString;
- Parser.NextToken;
- if Parser.FToken = etEnd then
- Break;
- end;
- NewsForm.MsgHeaders.Items.AddObject(Header, Pointer(MsgNo));
- end;
- end;
-
- procedure DestroyList(AList: TStringList);
- var
- i: Integer;
- begin
- for i := 0 to AList.Count - 1 do
- if AList.Objects[i] <> nil then
- DestroyList(TStringList(AList.Objects[i]));
- AList.Free;
- end;
-
- procedure BuildTree(Parent: TTreeNode; List: TStrings);
- var
- i: Integer;
- Node: TTreeNode;
- begin
- for i := 0 to List.Count - 1 do
- if List.Objects[i] <> nil then
- begin
- Node := NewsForm.NewsGroups.Items.AddChild(Parent, List[i]);
- Node.ImageIndex := 0;
- Node.SelectedIndex := 1;
- BuildTree(Node, TStrings(List.Objects[i]));
- end
- else
- NewsForm.NewsGroups.Items.AddChild(Parent, List[i]);
- end;
-
- function TNewsForm.NodePath(Node: TTreeNode): String;
- begin
- if Node.Parent = nil then
- Result := Node.Text
- else
- Result := NodePath(Node.Parent) + '.' + Node.Text;
- end;
-
- procedure TNewsForm.FileConnectItemClick(Sender: TObject);
- begin
- ConnectDlg := TConnectDlg.Create(Self);
- try
- if ConnectDlg.ShowModal = mrOk then
- with NNTP1 do
- Connect(ConnectDlg.ServerEdit.Text, RemotePort);
- finally
- ConnectDlg.Free;
- end;
- end;
-
- procedure TNewsForm.NNTP1ProtocolStateChanged(Sender: TObject;
- ProtocolState: Smallint);
- begin
- case ProtocolState of
- nntpBase: ;
- nntpTransaction:
- begin
- EventFlag := efListGroups;
- Nodes := TStringList.Create;
- Nodes.Sorted := True;
- NNTP1.ListGroups;
- end;
- end;
- end;
-
- procedure TNewsForm.NNTP1StateChanged(Sender: TObject; State: Smallint);
- begin
- with Memo1.Lines do
- case NNTP1.State of
- prcConnecting : Add('Connecting');
- prcResolvingHost: Add('Resolving Host: ' + NNTP1.RemoteHost);
- prcHostResolved : Add('Host resolved');
- prcConnected :
- begin
- Add('Connected to: ' + NNTP1.RemoteHost);
- Statusbar.Panels[0].Text := 'Connected to: ' + NNTP1.RemoteHost;
- ConnectBtn.Enabled := False;
- FileConnectItem.Enabled := False;
- RefreshBtn.Enabled := True;
- end;
- prcDisconnecting: Text := NNTP1.ReplyString;
- prcDisconnected :
- begin
- Statusbar.Panels[0].Text := 'Disconnected';
- Caption := 'News Reader';
- Label1.Caption := '';
- ConnectBtn.Enabled := True;
- FileConnectItem.Enabled := True;
- RefreshBtn.Enabled := False;
- end;
- end;
- end;
-
- procedure TNewsForm.Exit1Click(Sender: TObject);
- begin
- if NNTP1.State <> prcDisconnected then
- begin
- if NNTP1.Busy then NNTP1.Cancel;
- NNTP1.Quit;
- while NNTP1.State <> prcDisconnected do
- Application.ProcessMessages;
- end;
- Close;
- end;
-
- procedure TNewsForm.MsgHeadersDblClick(Sender: TObject);
- var
- Article: Integer;
- begin
- if NNTP1.Busy then exit;
- EventFlag := efGetArticle;
- Memo1.Clear;
- if MsgHeaders.ItemIndex = -1 then exit;
- Caption := 'News Reader: ' + MsgHeaders.Items[MsgHeaders.ItemIndex];
- Article := Integer(MsgHeaders.Items.Objects[MsgHeaders.ItemIndex]);
- NNTP1.GetArticlebyArticleNumber(Article);
- end;
-
- procedure TNewsForm.FormClose(Sender: TObject; var Action: TCloseAction);
- begin
- if NNTP1.State <> prcDisconnected then
- begin
- if NNTP1.Busy then NNTP1.Cancel;
- NNTP1.Quit;
- while NNTP1.State <> prcDisconnected do
- Application.ProcessMessages;
- end;
- end;
-
- procedure TNewsForm.NewsGroupsChange(Sender: TObject; Node: TTreeNode);
- var
- NP: String;
- begin
- if (NNTP1.State = prcConnected) and not NNTP1.Busy then
- with MsgHeaders do
- begin
- Items.BeginUpdate;
- try
- Items.Clear;
- Memo1.Lines.Clear;
- NP := NodePath(NewsGroups.Selected);
- Statusbar.Panels[2].Text := 'Bytes: 0';
- Statusbar.Panels[1].Text := '0 Article(s)';
- if NNTP1.Busy then
- NNTP1.Cancel;
- NNTP1.SelectGroup(NP);
- Label1.Caption := 'Contents of ''' + NP + '''';
- finally
- Items.EndUpdate;
- end;
- end;
- end;
-
- procedure TNewsForm.RefreshBtnClick(Sender: TObject);
- begin
- if NewsGroups.Selected <> nil then
- NewsGroupsChange(nil, NewsGroups.Selected);
- end;
-
- procedure TNewsForm.FileDisconnectItemClick(Sender: TObject);
- begin
- if NNTP1.Busy then NNTP1.Cancel;
- NNTP1.Quit;
- while NNTP1.Busy do
- Application.ProcessMessages;
- with NewsGroups.Items do
- begin
- BeginUpdate;
- Clear;
- EndUpdate;
- end;
- MsgHeaders.Items.Clear;
- Memo1.Lines.Clear;
- end;
-
- procedure TNewsForm.NNTP1Banner(Sender: TObject; const Banner: WideString);
- begin
- Memo1.Lines.Add(Banner);
- end;
-
- procedure TNewsForm.NNTP1DocOutput(Sender: TObject;
- const DocOutput: DocOutput);
- begin
- Statusbar.Panels[2].Text := Format('Bytes: %d',[DocOutput.BytesTransferred]);
- case DocOutput.State of
- icDocBegin:
- begin
- if EventFlag = efListGroups then
- Memo1.Lines.Add('Retrieving news groups...');
- Data := '';
- GroupCount := 0;
- end;
- icDocData:
- begin
- Data := Data + DocOutput.DataString;
- if EventFlag = efGetArticle then
- Memo1.Lines.Add(Data);
- end;
- icDocEnd:
- begin
- case EventFlag of
- efListGroups:
- begin
- ParseGroups(Data);
- Memo1.Lines.Add('Done.'#13#10'Building news group tree...');
- NewsGroups.Items.BeginUpdate;
- try
- BuildTree(nil, Nodes);
- DestroyList(Nodes);
- Statusbar.Panels[1].Text := Format('%d Groups',[GroupCount]);
- finally
- NewsGroups.Items.EndUpdate;
- Memo1.Lines.Add('Done.');
- end;
- end;
- efGetArticleHeaders: ParseHeaders(Data);
- efGetArticle:
- begin
- Memo1.SelStart := 0;
- SendMessage(Memo1.Handle, EM_ScrollCaret, 0, 0);
- end;
- end;
- SetLength(Data, 0);
- end;
- end;
- Refresh;
- end;
-
- procedure TNewsForm.NNTP1Error(Sender: TObject; Number: Smallint;
- var Description: WideString; Scode: Integer; const Source,
- HelpFile: WideString; HelpContext: Integer; var CancelDisplay: WordBool);
- begin
- // MessageDlg(Description, mtError, [mbOk], 0);
- end;
-
- procedure TNewsForm.NNTP1SelectGroup(Sender: TObject;
- const groupName: WideString; firstMessage, lastMessage,
- msgCount: Integer);
- begin
- EventFlag := efGetArticleHeaders;
- Statusbar.Panels[1].Text := Format('%d Article(s)',[msgCount]);
- NNTP1.GetArticleHeaders('subject', FirstMessage, lastMessage);
- end;
-
- end.
-