unit msghistory;

interface

uses
  Roster, Presence, XMLTag, Dockable, activex, mshtml,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, RichEdit2, ExRichEdit, Menus, ExtCtrls,
  ToolWin, OleCtrls, SHDocVw, ImgList, SHDocVw_TLB;

type
  TfmMsgHistory = class(TfrmDockable)
    MainMenu1: TMainMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    X1: TMenuItem;
    A1: TMenuItem;
    SaveDialog1: TSaveDialog;
    N4: TMenuItem;
    N5: TMenuItem;
    N6: TMenuItem;
    Panel3: TPanel;
    Panel1: TPanel;
    treeRoster: TTreeView;
    Splitter1: TSplitter;
    Panel2: TPanel;
    tmpEdit: TExRichEdit;
    HisView: TExRichEdit;
    ToolBar1: TToolBar;
    PopupMenu1: TPopupMenu;
    HisBrowser: TWebBrowser;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ImageList1: TImageList;
    procedure X1Click(Sender: TObject);
    procedure A1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure treeRosterCollapsed(Sender: TObject; Node: TTreeNode);
    procedure treeRosterExpanded(Sender: TObject; Node: TTreeNode);
    procedure treeRosterChange(Sender: TObject; Node: TTreeNode);
    procedure N6Click(Sender: TObject);
    procedure N5Click(Sender: TObject);
    procedure treeRosterMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FormShow(Sender: TObject);
    procedure treeRosterMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormDestroy(Sender: TObject);
    procedure ToolButton1Click(Sender: TObject);
    procedure ToolButton2Click(Sender: TObject);
    procedure ToolButton3Click(Sender: TObject);
    procedure ToolButton4Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    _jid_nodes: TStringlist;
    _grp_nodes: TSTringlist;

    function getNodeList(ritem: TJabberRosterItem): TList;
    function getNodeType(Node: TTreeNode): integer;
    procedure RemoveGroupNode(node: TTreeNode);
    function ExistsHistory(Jid: WideString): boolean;
  public
    { Public declarations }
    defaultJid: widestring;

    procedure Initialize();
    procedure ClearNodes();
    procedure ExpandNodes();

    procedure RemoveItemNodes(ritem: TJabberRosterItem);

    procedure DrawRoster;
    procedure RenderNode(ritem: TJabberRosterItem; p: TJabberPres);
    function RenderGroup(grp_idx: integer): TTreeNode;

    procedure ClearView;
    procedure ShowHistory(Jid: WideString);
    procedure showHis(XMl: Widestring);
    procedure showMsg(tag: TXMLTag);
  end;

var
  fmMsgHistory: TfmMsgHistory;

procedure ShowMsgHistory(jid: WideString = '');

implementation

uses Session, Jabber1, ExUtils, gnugettext, XMLUtils, XMLParser, JabberMsg,
  MsgDisplay, JabberID, RiserWindow, filectrl;

{$R *.dfm}

procedure ShowMsgHistory(jid: WideString);
begin
  with TfmMsgHistory.Create(Application) do
  begin
    if jid <> '' then
      defaultJid := jid;
    show;
  end;
end;
{ TfmHistoryMsg }

procedure TfmMsgHistory.X1Click(Sender: TObject);
begin
  Close;
end;

procedure TfmMsgHistory.A1Click(Sender: TObject);
var
  tmplist: TStringList;
begin
  if SaveDialog1.Execute then
    case SaveDialog1.FilterIndex of
      1: begin
          tmplist := TStringList.Create;
          HisView.SelectAll;
          tmplist.Add(HisView.RTFSelText);
          tmplist.SaveToFile(SaveDialog1.FileName);
          tmplist.Free;
        end;
    else
      HisView.WideLines.SaveToFile(SaveDialog1.FileName);
    end;
end;


function SlashSep(const Path, S: string): string;
begin
  if AnsiLastChar(Path)^ <> '\' then
    Result := Path + '\' + S
  else
    Result := Path + S;
end;

function ReadHistorys(const ParentDirectory: string;
  HistoryList: TStringList): Integer;
var
  Status: Integer;
  SearchRec: TSearchRec;
begin
  Result := 0;
  Status := FindFirst(SlashSep(ParentDirectory, '*.His'), faAnyFile, SearchRec);
  try
    while Status = 0 do
    begin
      if (SearchRec.Attr and faDirectory <> faDirectory) then
      begin
        if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
        begin
          HistoryList.Add(SearchRec.Name);
          Inc(Result);
        end;
      end;
      Status := FindNext(SearchRec);
    end;
  finally
    FindClose(SearchRec);
  end;
end;


procedure TfmMsgHistory.DrawRoster;
var
  i: integer;
  ri: TJabberRosterItem;
  p: TJabberPres;
  fn: string;
  flist: TstringList;
  tmpjid: TJabberID;
  node: Ttreenode;
begin
  Self.ClearNodes();
  treeRoster.Color := TColor(MainSession.prefs.getInt('roster_bg'));
  treeRoster.Items.BeginUpdate;

  // re-render each item
  with MainSession.Roster do begin
    for i := 0 to Count - 1 do begin
      ri := TJabberRosterItem(Objects[i]);
      p := MainSession.ppdb.FindPres(ri.JID.jid, '');
      RenderNode(ri, p);
    end;
  end;

  treeRoster.AlphaSort;

  fn := MainSession.Prefs.getString('log_path');

  if (Copy(fn, length(fn), 1) <> '\') then
    fn := fn + '\';
  fn := fn + MainSession.BareJid + '\';
  flist := TstringList.Create;
  if ReadHistorys(fn, flist) > 0 then
  begin
    node := treeRoster.Items.AddChild(nil, '会议');
    for i := 0 to flist.Count - 1 do
    begin
      if pos('_conference.', flist[i]) > 0 then
      begin
        tmpjid := TJabberID.Create(StringReplace(flist[i], '_conference.', '@conference.', []));
        if pos('conference.', tmpjid.domain) = 1 then
        begin
          treeRoster.Items.AddChild(node, tmpjid.user);
        end;
        tmpjid.free;
      end;
    end;
    if Node.Count = 0 then
      treeRoster.Items.Delete(Node);
  end;
  flist.free;

  treeRoster.Items.EndUpdate;
  Self.ExpandNodes();
  treeRoster.TopItem := treeRoster.Items[0];
end;

procedure TfmMsgHistory.RenderNode(ritem: TJabberRosterItem;
  p: TJabberPres);
var
  i, g, grp_idx: integer;
  cur_grp, tmps: string;
  cur_node, grp_node, n: TTreeNode;
  node_list: TList;
  tmp_grps: TStringlist;
  exp_grpnode: boolean;
  is_blocked: boolean;
  tmp: widestring;
  tmpindex: integer;
begin
  is_blocked := MainSession.isBlocked(ritem.jid);
    //如果检测到是自己的话，不把自己加入列表中
  if (ritem.jid.jid = MainSession.BareJid) and (p = nil) then
    exit;

  if (ritem.jid.user = '') then
    exit;

    // Render a specific roster item, with the given presence info.

    // First Cache some prefs

  exp_grpnode := false;

    {
    OK, here we want to bail on some circumstances
    if the roster item is NOT supposed to be shown
    based on preferences, and the state of the roster
    item, and the current presence info.
    }

  if (ritem.ask = 'subscribe') then begin
        // allow these items to pass thru
  end
  else if ((ritem.subscription = 'none') or
    (ritem.subscription = '') or
    (ritem.subscription = 'remove')) then begin
        // We aren't subscribed to these people,
        // or we are removing them from the roster
    RemoveItemNodes(ritem);
    exit;
  end;

    // Create a list to contain all nodes for this
    // roster item, and assign it to the .Data property
    // of the roster item object

    // node_list := TList(ritem.Data);
  node_list := Self.GetNodeList(ritem);
  if node_list = nil then begin
    node_list := TList.Create;
    _jid_nodes.AddObject(ritem.jid.full, node_list);
  end;

    // Create a temporary list of grps that this
    // contact should be in.
  tmp_grps := TStringlist.Create;
  tmp_grps.Assign(ritem.Groups);

    // If they aren't in any grps, put them into the Unfiled grp
  if tmp_grps.Count <= 0 then
    tmp_grps.Add(sGrpUnfiled);

    // Remove nodes that are in node_list but aren't in the grp list
    // This takes care of changing grps, or going to the offline grp
  for i := node_list.Count - 1 downto 0 do begin
    cur_node := TTreeNode(node_list[i]);
    grp_node := cur_node.Parent;
    cur_grp := grp_node.Text;
    if (tmp_grps.IndexOf(cur_grp) < 0) then begin
            // nuke this old node
      cur_node.Free;
      node_list.Delete(i);
    end;
  end;

    // determine the caption for the node
  if (ritem.Nickname <> '') then
    tmps := ritem.Nickname
  else
    tmps := ritem.jid.Full;

  if (ritem.ask = 'subscribe') then
    tmps := tmps + ' (Pending)';

    // For each grp in the temp. grp list,
    // Make sure a node already exists, or create one.
  for g := 0 to tmp_grps.Count - 1 do begin
    cur_grp := tmp_grps[g];

        // Make sure the grp exists in the GrpList
    grp_idx := _grp_nodes.IndexOf(cur_grp);
    if (grp_idx < 0) then
      grp_idx := _grp_nodes.Add(cur_grp);

        // Make sure we have a node for this grp and keep
        // a pointer to the node in the Roster's grp list
    grp_node := TTreeNode(_grp_nodes.Objects[grp_idx]);
    if (grp_node = nil) then begin
      grp_node := RenderGroup(grp_idx);
    end;

        // Expand any grps that are not supposed to be collapsed
    exp_grpnode := true;


        // Now that we are sure we have a grp_node,
        // check to see if this node exists under it
    cur_node := nil;
    for i := 0 to node_list.count - 1 do begin
      n := TTreeNode(node_list[i]);
      if n.HasAsParent(grp_node) then begin
        cur_node := n;
        break;
      end;
    end;

    if cur_node = nil then begin
            // add a node for this person under this group
      cur_node := treeRoster.Items.AddChild(grp_node, tmps);
      node_list.Add(cur_node);
    end;

    cur_node.Text := tmps;
    cur_node.Data := ritem;

        // setup the image

    if ((is_blocked) and (p = nil)) then
      tmpindex := ico_blockoffline
    else if (is_blocked) then
      tmpindex := ico_blocked
    else if p = nil then
      tmpindex := ico_Offline
    else begin
      if p.Show = 'away' then
        tmpindex := ico_Away
      else if p.Show = 'xa' then
        tmpindex := ico_XA
      else if p.Show = 'dnd' then
        tmpindex := ico_DND
      else
        tmpindex := ico_Online
    end;

    tmp := TJabberRosterItem(cur_node.Data).jid.domain;
    if pos('oicq.', tmp) = 1 then
    begin
      case tmpindex of
        ico_Offline: tmpindex := 49;
        ico_Online: tmpindex := 48;
        ico_Away: tmpindex := 51;
        ico_XA: tmpindex := 51;
        ico_DND: tmpindex := 50;
        ico_blocked: tmpindex := 52;
        ico_blockoffline: tmpindex := 52;
      end;
    end
    else
      if pos('msn.', tmp) = 1 then
      begin
        case tmpindex of
          ico_Offline: tmpindex := 44;
          ico_Online: tmpindex := 43;
          ico_Away: tmpindex := 46;
          ico_XA: tmpindex := 46;
          ico_DND: tmpindex := 45;
          ico_blocked: tmpindex := 47;
          ico_blockoffline: tmpindex := 47;
        end;
      end
      else
        if pos('icq.', tmp) = 1 then
        begin
          //画icq图标
          case tmpindex of
            ico_Offline: tmpindex := 54;
            ico_Online: tmpindex := 53;
            ico_Away: tmpindex := 56;
            ico_XA: tmpindex := 56;
            ico_DND: tmpindex := 55;
            ico_blocked: tmpindex := 57;
            ico_blockoffline: tmpindex := 57;
          end;
        end
        else
          if pos('aim.', tmp) = 1 then
          begin
          //画aim图标
            case tmpindex of
              ico_Offline: tmpindex := 59;
              ico_Online: tmpindex := 58;
              ico_Away: tmpindex := 61;
              ico_XA: tmpindex := 61;
              ico_DND: tmpindex := 60;
              ico_blocked: tmpindex := 62;
              ico_blockoffline: tmpindex := 62;
            end;
          end
          else
            if pos('yahoo.', tmp) = 1 then
            begin
          //画yahoo!图标
              case tmpindex of
                ico_Offline: tmpindex := 64;
                ico_Online: tmpindex := 63;
                ico_Away: tmpindex := 66;
                ico_XA: tmpindex := 66;
                ico_DND: tmpindex := 65;
                ico_blocked: tmpindex := 67;
                ico_blockoffline: tmpindex := 67;
              end;
            end;

    cur_node.ImageIndex := tmpindex;

    cur_node.SelectedIndex := cur_node.ImageIndex;
    if (exp_grpnode) then grp_node.Expand(true);
  end;

  tmp_grps.Free();

end;

procedure TfmMsgHistory.ClearNodes;
var
  i: integer;
  node_list: TList;
begin
  treeRoster.Items.BeginUpdate;

    // this clears the actual treeview
  treeRoster.Items.Clear;

    // wipe out pointers to the nodes in the lists
  for i := 0 to _grp_nodes.Count - 1 do
    _grp_nodes.Objects[i] := nil;

  for i := 0 to _jid_nodes.Count - 1 do begin
    node_list := TList(_jid_nodes.Objects[i]);
    node_list.Clear();
  end;

  treeRoster.Items.EndUpdate;
end;

procedure TfmMsgHistory.ExpandNodes;
var
  i: integer;
  n: TTreeNode;
begin
    // expand all nodes except special nodes
  for i := 0 to treeRoster.Items.Count - 1 do begin
    n := treeRoster.Items[i];
    if (n.Level = 0) then begin
      n.Expand(true);
    end;
  end;
end;

procedure TfmMsgHistory.RemoveItemNodes(ritem: TJabberRosterItem);
var
  n, p: TTreeNode;
  node_list: TList;
  i: integer;
begin
    // Remove the nodes for this item..
  node_list := GetNodeList(ritem);
  if node_list <> nil then begin
    for i := node_list.count - 1 downto 0 do begin
      n := TTreeNode(node_list[i]);
      p := n.Parent;
      n.Free;
      if (p.Count <= 0) then
        Self.RemoveGroupNode(p);
      node_list.Delete(i);
    end;
  end;
end;

function TfmMsgHistory.getNodeList(ritem: TJabberRosterItem): TList;
var
  jn_idx: integer;
begin
    // return a TList for this JID from the node_list list
  jn_idx := _jid_nodes.indexOf(ritem.jid.full);
  if (jn_idx < 0) then
    Result := nil
  else
    Result := TList(_jid_nodes.objects[jn_idx]);
end;

function TfmMsgHistory.getNodeType(Node: TTreeNode): integer;
var
  n: TTreeNode;
begin
    // return the type of node this is..
  if (Node = nil) then
    n := treeRoster.Selected
  else
    n := Node;

  Result := node_none;

  if (n = nil) then exit;

  if ((n.Level = 0) or
    ((treeRoster.SelectionCount > 1) and (node = nil))) then begin
    Result := node_grp;
  end

  else if (TObject(n.Data) is TJabberBookmark) then begin
    Result := node_bm;
  end

  else if (TObject(n.Data) is TJabberRosterItem) then begin
    Result := node_ritem;
  end;
end;

function TfmMsgHistory.RenderGroup(grp_idx: integer): TTreeNode;
var
  grp_node: TTreeNode;
  cur_grp: string;
begin
    // render this grp into the tree
  cur_grp := _grp_nodes[grp_idx];
  grp_node := treeRoster.Items.AddChild(nil, cur_grp);
  _grp_nodes.Objects[grp_idx] := grp_node;
  grp_node.ImageIndex := ico_Right;
  grp_node.SelectedIndex := ico_Right;
  grp_node.Data := nil;
  result := grp_node;
end;

procedure TfmMsgHistory.RemoveGroupNode(node: TTreeNode);
var
  grp_idx: integer;
  grp: string;
begin
    // remove the group node pointer from the list
  grp := node.Text;
  grp_idx := _grp_nodes.indexOfObject(node);
  if (grp_idx >= 0) then
    _grp_nodes.Objects[grp_idx] := nil;
  node.Free();
end;

procedure TfmMsgHistory.FormCreate(Sender: TObject);
begin
  coInitialize(nil);
  defaultJid := '';
//  TranslateProperties(Self);
  Initialize;
  DrawRoster;
  AssignDefaultFont(HisView.Font);
  AssignDefaultFont(tmpEdit.Font);
end;

procedure TfmMsgHistory.Initialize;
begin
  _jid_nodes := TStringlist.Create();
  _grp_nodes := TStringlist.Create();
end;

procedure TfmMsgHistory.treeRosterCollapsed(Sender: TObject;
  Node: TTreeNode);
begin
  if Node.Data = nil then begin
    Node.ImageIndex := ico_Right;
    Node.SelectedIndex := ico_Right;
  end;
end;

procedure TfmMsgHistory.treeRosterExpanded(Sender: TObject;
  Node: TTreeNode);
begin
  if Node.Data = nil then begin
    Node.ImageIndex := ico_Down;
    Node.SelectedIndex := ico_Down;
  end;
end;

procedure TfmMsgHistory.treeRosterChange(Sender: TObject; Node: TTreeNode);
begin
  ClearView;
  if treeRoster.Selected.Level = 1 then
    if treeRoster.Selected.Data <> nil then
      ShowHistory(TJabberRosterItem(treeRoster.Selected.Data).jid.jid)
    else
      ShowHistory(treeRoster.Selected.Text + '@conference.' + MainSession.Server);
end;

procedure TfmMsgHistory.N6Click(Sender: TObject);
begin
  ClearAllLogs;
  ClearView;
end;

procedure TfmMsgHistory.N5Click(Sender: TObject);
begin
  if treeRoster.Selected.Level = 1 then
  begin
    ClearLog(TJabberRosterItem(treeRoster.Selected.Data).jid.jid);
    ClearView;
  end;
end;

procedure TfmMsgHistory.ClearView;
begin
  HisView.ReadOnly := False;
  HisView.Clear;
  HisView.ReadOnly := True;
end;


procedure TfmMsgHistory.ShowHistory(Jid: WideString);
var
  fn: string;
  f: TextFile;
  Buf, tmp: WideString;
begin
  fn := MainSession.Prefs.getString('log_path');

  if (Copy(fn, length(fn), 1) <> '\') then
    fn := fn + '\';
  fn := fn + MainSession.BareJid + '\';

  fn := fn + MungeName(jid) + '.xml';
  if FileExists(fn) then
    HisBrowser.Navigate(fn)
  else
    HisBrowser.Navigate('about:blank');
  exit;


  // Munge the filename
  fn := fn + MungeName(jid) + '.his';
  if FileExists(fn) then begin
    AssignFile(f, fn);
{$I-}
    Reset(f);
{$I+}
    if IOResult <> 0 then
    begin
      ShowRiserWindow(nil, '无法打开历史记录文件！', ico_info);
      abort;
      //raise Exception.Create('无法打开历史记录文件！');
    end;
    Buf := '';
    while not eof(f) do
    begin
      Readln(f, tmp);
      Buf := Buf + Tmp;
    end;
    CloseFile(f);
    Buf := '<history>' + Buf + '</history>';
    showhis(Buf);
  end
  else
    ClearView;
end;

procedure TfmMsgHistory.showHis(XMl: Widestring);
var
  _parser: TXMLTagParser;
  His: TXMLTag;
  Msgs: TXMLTagList;
  i: Integer;
begin
  _parser := TXMLTagParser.Create;
  _parser.ParseString(XMl, '');
  His := _parser.popTag;
  Msgs := His.ChildTags;
  for i := 0 to Msgs.Count - 1 do
    showMsg(Msgs.Tags[i]);
  Msgs.Free;
  _parser.Free;
end;

procedure TfmMsgHistory.showMsg(tag: TXMLTag);
var
  m, etag: TXMLTag;
  subj_msg, msg: TJabberMessage;
begin

  Msg := TJabberMessage.Create(tag);
  if tag.GetAttribute('stamp') <> '' then
  try
    Msg.Time := StrToDateTime(tag.GetAttribute('stamp'))
  except
    Msg.Time := now;
  end;

//    if Msg.FromJID = MainSession.BareJid then
  Msg.Nick := tag.GetAttribute('nick');
  if Msg.nick = '' then
  begin
    with TJabberID.Create(Msg.FromJID) do
    begin
      Msg.Nick := user;
      Free;
    end;
  end;
  if Msg.nick = '' then
    Msg.Nick := MainSession.Username;
  Msg.IsMe := false;

  if not msg.broadcast then
  begin
      // only display + notify if we have something to display :)
    if (Msg.Subject <> '') then begin
      subj_msg := TJabberMessage.Create(tag);
      subj_msg.Body := '主题被更改为: ' + subj_msg.Subject;
      subj_msg.Subject := '';
      subj_msg.Nick := '';
      DisplayMsg(subj_msg, HisView, TmpEdit);
      subj_msg.Free();
    end;
  end;

  if ((Msg.Body <> '') or (Msg.rtf <> '')) then begin
    if msg.broadcast then
      DisplayBroadcastMsg(Msg, HisView, TmpEdit)
    else
      DisplayMsg(Msg, HisView, TmpEdit);
  end;

  Msg.Free();
end;

procedure TfmMsgHistory.treeRosterMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin
  treeRoster.Hint := '';
  if treeRoster.GetNodeAt(x, y) <> nil then
    with treeRoster.GetNodeAt(x, y) do
      if level = 1 then
        if treeRoster.GetNodeAt(x, y).Data <> nil then
          treeRoster.Hint := TJabberRosterItem(treeRoster.GetNodeAt(x, y).Data).jid.jid
        else
          treeRoster.Hint := treeRoster.GetNodeAt(x, y).Text + '@conference.' + MainSession.Server;
end;

procedure TfmMsgHistory.FormShow(Sender: TObject);
var
  i: integer;
begin
  inherited;
  HisBrowser.Navigate('about:blank');
  if defaultJid = '' then exit;
{  for i:= 0 to treeRoster.Items.Count-1 do
  begin
    if treeRoster.Items.Item[i].Level<>1 then
      Continue;
    if TJabberRosterItem(treeRoster.Items.Item[i].Data).jid.jid= defaultJid then
    begin
      treeRoster.Items.Item[i].Selected := true;
      exit;
    end;
  end;
  Panel1.Visible := false;
  treeRoster.Visible := false;
  Splitter1.Visible := false;
  N4.Visible := false;{}
  ShowHistory(defaultJid);
end;

procedure TfmMsgHistory.treeRosterMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  n: TTreeNode;
begin
    //如果不是左键则退出
  if Button <> mbLeft then Exit;

    // check to see if we're hitting a button
  n := treeRoster.GetNodeAt(X, Y);
  if n = nil then exit;

  if (n.Level > 1) then exit;

  if (X < (16 + 16 + n.Level * 19 + 5)) and
    (X > (16 + n.Level * 19 + 5)) then begin
        // clicking on a grp's widget
    if n.Expanded then
      n.Collapse(false)
    else
      n.Expand(false);
  end;
end;

function TfmMsgHistory.ExistsHistory(Jid: WideString): boolean;
var
  fn: string;
begin
  fn := MainSession.Prefs.getString('log_path');

  if (Copy(fn, length(fn), 1) <> '\') then
    fn := fn + '\';
  fn := fn + MainSession.BareJid + '\';

  // Munge the filename
  fn := fn + MungeName(jid) + '.his';
  result := FileExists(fn);
end;

procedure TfmMsgHistory.FormDestroy(Sender: TObject);
begin
  inherited;
  counInitialize;
end;

procedure TfmMsgHistory.ToolButton1Click(Sender: TObject);
const
  CMDSETID_Forms3: TGUID = '{DE4BA900-59CA-11CF-9592-444553540000}';
  IDM_FIND = 67;
begin
  inherited;
  (HisBrowser.Document as IOleCommandTarget).Exec(@CMDSETID_Forms3, IDM_FIND, OLECMDEXECOPT_PROMPTUSER, EmptyParam, EmptyParam);
end;

procedure TfmMsgHistory.ToolButton2Click(Sender: TObject);
begin
  inherited;
  HisBrowser.Refresh;
end;

procedure TfmMsgHistory.ToolButton3Click(Sender: TObject);
var
  fn: string;
begin
  inherited;
  fn := MainSession.Prefs.getString('log_path');

  if (Copy(fn, length(fn), 1) <> '\') then
    fn := fn + '\';
  fn := fn + MainSession.BareJid + '\';

  fn := fn + MungeName(defaultJid) + '.xml';
  DeleteFile(fn);
  HisBrowser.Navigate('about:blank');
end;

procedure TfmMsgHistory.ToolButton4Click(Sender: TObject);
begin
  inherited;
  frmAnyQ.BringToFront;
end;

procedure TfmMsgHistory.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  inherited;
  Action := caFree;
end;

initialization
  OleInitialize(nil);
finalization
  OleUninitialize;
end.
