lazarus/components/chmhelp/lhelp/chmcontentprovider.pas
vincents 20f5b2b4ad lhelp: fixed compilation and cleanup
git-svn-id: trunk@17122 -
2008-10-24 12:36:38 +00:00

567 lines
14 KiB
ObjectPascal

unit chmcontentprovider;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
FileUtil, StdCtrls, ExtCtrls, ComCtrls, Controls, Buttons, Menus,
BaseContentProvider, FileContentProvider, IpHtml, ChmReader, ChmDataProvider;
type
{ TChmContentProvider }
TChmContentProvider = class(TFileContentProvider)
private
fTabsControl: TPageControl;
fContentsTab: TTabSheet;
fContentsPanel: TPanel;
fContentsTree: TTreeView;
fIndexTab: TTabSheet;
fIndexEdit: TEdit;
fIndexView: TListView;
fSearchTab: TTabSheet;
fKeywordLabel: TLabel;
fKeywordCombo: TComboBox;
fSearchBtn: TButton;
fResultsLabel: TLabel;
fSearchResults: TListBox;
fSplitter: TSplitter;
fHtml: TIpHtmlPanel;
fPopUp: TPopUpMenu;
fChmDataProvider: TIpChmDataProvider;
fStatusBar: TStatusBar;
fContext: THelpContext;
protected
fIsUsingHistory: Boolean;
fChms: TChmFileList;
fHistory: TStringList;
fHistoryIndex: Integer;
fStopTimer: Boolean;
fFillingToc: Boolean;
procedure AddHistory(URL: String);
procedure DoOpenChm(AFile: String);
procedure DoCloseChm;
procedure DoLoadContext(Context: THelpContext);
procedure DoLoadUrl(Url: String; AChm: TChmReader = nil);
procedure DoError(Error: Integer);
procedure NewChmOpened(ChmFileList: TChmFileList; Index: Integer);
procedure FillTOCTimer(Sender: TObject);
procedure IpHtmlPanelDocumentOpen(Sender: TObject);
procedure IpHtmlPanelHotChange(Sender: TObject);
procedure PopupCopyClick(Sender: TObject);
procedure ContentsTreeSelectionChanged(Sender: TObject);
procedure IndexViewDblClick(Sender: TObject);
procedure ViewMenuContentsClick(Sender: TObject);
procedure SetTitle(const ATitle: String);
procedure SearchEditChange(Sender: TObject);
public
function CanGoBack: Boolean; override;
function CanGoForward: Boolean; override;
function GetHistory: TStrings; override;
function LoadURL(const AURL: String; const AContext: THelpContext=-1): Boolean; override;
procedure GoHome; override;
procedure GoBack; override;
procedure GoForward; override;
class function GetProperContentProvider(const AURL: String): TBaseContentProviderClass; override;
constructor Create(AParent: TWinControl); override;
destructor Destroy; override;
end;
TTocTimer = class(TIdleTimer)
private
fChm: TChmReader;
end;
implementation
uses ChmSpecialParser;
{ TChmContentProvider }
procedure TChmContentProvider.AddHistory(URL: String);
begin
if fHistoryIndex < fHistory.Count then begin
while fHistory.Count-1 > fHistoryIndex do
fHistory.Delete(fHistory.Count-1);
end;
fHistory.Add(URL);
Inc(fHistoryIndex);
end;
procedure TChmContentProvider.DoOpenChm(AFile: String);
begin
if (fChms <> nil) and fChms.IsAnOpenFile(AFile) then Exit;
DoCloseChm;
if not FileExistsUTF8(AFile) or DirectoryExistsUTF8(AFile) then
begin
Exit;
end;
try
fChms := TChmFileList.Create(AFile);
if Not(fChms.Chm[0].IsValidFile) then begin
FreeAndNil(fChms);
//DoError(INVALID_FILE_TYPE);
Exit;
end;
TIpChmDataProvider(fHtml.DataProvider).Chm := fChms;
except
FreeAndNil(fChms);
//DoError(INVALID_FILE_TYPE);
Exit;
end;
if fChms = nil then Exit;
fChms.OnOpenNewFile := @NewChmOpened;
fHistoryIndex := -1;
fHistory.Clear;
// Code Here has been moved to the OpenFile handler
//FileMenuCloseItem.Enabled := True;
if fChms.Chm[0].Title <> '' then SetTitle(fChms.Chm[0].Title);
end;
procedure TChmContentProvider.DoCloseChm;
begin
fStopTimer := True;
if fChms<>nil then begin
FreeAndNil(fChms);
end;
end;
procedure TChmContentProvider.DoLoadContext(Context: THelpContext);
var
Str: String;
begin
if fChms = nil then exit;
Str := fChms.Chm[0].GetContextUrl(Context);
if Str <> '' then DoLoadUrl(Str);
end;
procedure TChmContentProvider.DoLoadUrl(Url: String; AChm: TChmReader = nil);
begin
if (fChms = nil) and (AChm = nil) then exit;
if fChms.ObjectExists(Url, AChm) = 0 then begin
fStatusBar.SimpleText := URL + ' not found!';
Exit;
end;
fIsUsingHistory := True;
fHtml.OpenURL(Url);
TIpChmDataProvider(fHtml.DataProvider).CurrentPath := ExtractFileDir(URL)+'/';
AddHistory(Url);
end;
procedure TChmContentProvider.DoError(Error: Integer);
begin
//what to do with these errors?
//INVALID_FILE_TYPE;
end;
procedure TChmContentProvider.NewChmOpened(ChmFileList: TChmFileList;
Index: Integer);
var
TImer: TTocTimer;
begin
if Index = 0 then begin
fContentsTree.Items.Clear;
if fContext > -1 then begin
DoLoadContext(fContext);
fContext := -1;
end
else if ChmFileList.Chm[Index].DefaultPage <> '' then begin
DoLoadUrl(ChmFileList.Chm[Index].DefaultPage);
end;
end;
if ChmFileList.Chm[Index].Title = '' then
ChmFileList.Chm[Index].Title := ExtractFileName(ChmFileList.FileName[Index]);
// Fill the table of contents. This actually works very well
Timer := TTocTimer.Create(fHtml);
if ChmFileList.ObjectExists(ChmFileList.Chm[Index].TOCFile) + ChmFileList.ObjectExists(ChmFileList.Chm[Index].IndexFile)> 25000 then
Timer.Interval := 500
else
Timer.Interval := 5;
Timer.OnTimer := @FillTOCTimer;
Timer.fChm := ChmFileList.Chm[Index];
Timer.Enabled := True;
fContentsTree.Visible := False;
end;
procedure TChmContentProvider.FillTOCTimer(Sender: TObject);
var
Stream: TMemoryStream;
fChm: TChmReader;
ParentNode: TTreeNode;
begin
if fFillingToc = True then begin
TTimer(Sender).Interval := 40;
exit;
end;
fFillingToc := True;
fStopTimer := False;
fContentsTree.Visible := False;
fChm := TTocTimer(Sender).fChm;
TTocTimer(Sender).Free;
if fChm <> nil then begin
ParentNode := fContentsTree.Items.AddChildObject(nil, fChm.Title, fChm);
Stream := TMemoryStream(fchm.GetObject(fChm.TOCFile));
if Stream <> nil then begin
Stream.position := 0;
with TContentsFiller.Create(fContentsTree, Stream, @fStopTimer) do begin
DoFill(ParentNode);
Free;
end;
end;
Stream.Free;
// we fill the index here too
Stream := fchms.GetObject(fChm.IndexFile);
if Stream <> nil then begin
Stream.position := 0;
with TIndexFiller.Create(fIndexView, Stream) do begin;
DoFill;
Free;
end;
Stream.Free;
end;
end;
if ParentNode.Index = 0 then ParentNode.Expanded := True;
fContentsTree.Visible := True;
fFillingToc := False;
fStopTimer := False;
end;
procedure TChmContentProvider.IpHtmlPanelDocumentOpen(Sender: TObject);
begin
// StatusBar1.Panels.Items[1] := fHtml.DataProvider.;
if fIsUsingHistory = False then
AddHistory(TIpChmDataProvider(fHtml.DataProvider).CurrentPage)
else fIsUsingHistory := False;
end;
procedure TChmContentProvider.IpHtmlPanelHotChange(Sender: TObject);
begin
fStatusBar.SimpleText := fHtml.HotURL;
end;
procedure TChmContentProvider.PopupCopyClick(Sender: TObject);
begin
fHtml.CopyToClipboard;
end;
procedure TChmContentProvider.ContentsTreeSelectionChanged(Sender: TObject);
var
ATreeNode: TContentTreeNode;
ARootNode: TTreeNode;
fChm: TChmReader = nil;
begin
if (fContentsTree.Selected = nil) then Exit;
if not(fContentsTree.Selected is TContentTreeNode) then
begin
fChm := TChmReader(fContentsTree.Selected.Data);
if fChm.DefaultPage <> '' then
DoLoadUrl(fChm.DefaultPage, fChm);
Exit;
end;
ATreeNode := TContentTreeNode(fContentsTree.Selected);
//find the chm associated with this branch
ARootNode := ATreeNode.Parent;
while ARootNode.Parent <> nil do
ARootNode := ARootNode.Parent;
fChm := TChmReader(ARootNode.Data);
if ATreeNode.Url <> '' then begin
DoLoadUrl(ATreeNode.Url, fChm);
end;
end;
procedure TChmContentProvider.IndexViewDblClick(Sender: TObject);
var
SelectedItem: TListItem;
RealItem: TIndexItem;
begin
SelectedItem := fIndexView.Selected;
if SelectedItem = nil then Exit;
RealItem := TIndexItem(SelectedItem);
if not fIndexEdit.Focused then
fIndexEdit.Text := Trim(RealItem.Caption);
if RealItem.Url <> '' then begin
DoLoadUrl(RealItem.Url);
end;
end;
procedure TChmContentProvider.ViewMenuContentsClick(Sender: TObject);
begin
//TMenuItem(Sender).Checked := not TMenuItem(Sender).Checked;
//fSplitter.Visible := TMenuItem(Sender).Checked;
//TabPanel.Visible := Splitter1.Visible;
end;
procedure TChmContentProvider.SetTitle(const ATitle: String);
begin
if fHtml.Parent = nil then exit;
TTabSheet(fHtml.Parent).Caption := ATitle;
end;
procedure TChmContentProvider.SearchEditChange(Sender: TObject);
var
I: Integer;
ItemName: String;
SearchText: String;
begin
if fIndexEdit.Text = '' then Exit;
if not fIndexEdit.Focused then Exit;
SearchText := LowerCase(fIndexEdit.Text);
for I := 0 to fIndexView.Items.Count-1 do begin
ItemName := LowerCase(Copy(fIndexView.Items.Item[I].Caption, 1, Length(SearchText)));
if ItemName = SearchText then begin
fIndexView.Items.Item[fIndexView.Items.Count-1].MakeVisible(False);
fIndexView.Items.Item[I].MakeVisible(False);
fIndexView.Items.Item[I].Selected := True;
Exit;
end;
end;
end;
function TChmContentProvider.CanGoBack: Boolean;
begin
Result := fHistoryIndex > 0;
end;
function TChmContentProvider.CanGoForward: Boolean;
begin
Result := fHistoryIndex < fHistory.Count-1
end;
function TChmContentProvider.GetHistory: TStrings;
begin
Result:= fHistory;
end;
function TChmContentProvider.LoadURL(const AURL: String; const AContext: THelpContext=-1): Boolean;
var
fFile: String;
fURL: String = '';
fPos: Integer;
begin
Result := False;
fFile := Copy(AUrl,8, Length(AURL));
fPos := Pos('://', fFile);
if fPos > 0 then begin
fURL := Copy(fFile, fPos+3, Length(fFIle));
fFile := Copy(fFIle, 1, fPos-1);
end;
//writeln(fURL);
//if fChms = nil then
DoOpenChm(fFile);
if fURL <> '' then
DoLoadUrl(fUrl)
else
GoHome;
Result := True;
end;
procedure TChmContentProvider.GoHome;
begin
if (fChms <> nil) and (fChms.Chm[0].DefaultPage <> '') then begin
DoLoadUrl(fChms.Chm[0].DefaultPage);
end;
end;
procedure TChmContentProvider.GoBack;
begin
if CanGoBack then begin
Dec(fHistoryIndex);
fIsUsingHistory:=True;
fHtml.OpenURL(fHistory.Strings[fHistoryIndex]);
end;
end;
procedure TChmContentProvider.GoForward;
begin
if CanGoForward then begin
Inc(fHistoryIndex);
fIsUsingHistory:=True;
fHtml.OpenURL(fHistory.Strings[fHistoryIndex]);
end;
end;
class function TChmContentProvider.GetProperContentProvider(const AURL: String
): TBaseContentProviderClass;
begin
Result:=TChmContentProvider;
end;
constructor TChmContentProvider.Create(AParent: TWinControl);
begin
inherited Create(AParent);
fHistory := TStringList.Create;
fTabsControl := TPageControl.Create(AParent);
with fTabsControl do begin
Width := 215;
Align := alLeft;
Parent := AParent;
Visible := True;
end;
fContentsTab := TTabSheet.Create(fTabsControl);
with fContentsTab do begin
Caption := 'Contents';
Parent := fTabsControl;
Visible := True;
end;
fContentsPanel := TPanel.Create(fContentsTab);
with fContentsPanel do begin
Parent := fContentsTab;
Align := alClient;
Caption := 'Table of Contents Loading. Please Wait...';
Visible := True;
end;
fContentsTree := TTreeView.Create(fContentsPanel);
with fContentsTree do begin
Parent := fContentsPanel;
Align := alClient;
Visible := True;
OnSelectionChanged := @ContentsTreeSelectionChanged;
end;
fIndexTab := TTabSheet.Create(fTabsControl);
with fIndexTab do begin
Caption := 'Index';
Parent := fTabsControl;
Visible := True;
end;
with TLabel.Create(fIndexTab) do begin
Caption := 'Search';
Align := alTop;
Parent := fIndexTab;
Top := 0;
Visible := True;
end;
fIndexEdit := TEdit.Create(fIndexTab);
with fIndexEdit do begin
Parent := fIndexTab;
Top := 10;
Align := alTop;
Visible := True;
BorderSpacing.Bottom := 15;
OnChange := @SearchEditChange;
end;
fIndexView := TListView.Create(fIndexTab);
with fIndexView do begin
Parent := fIndexTab;
Align := alClient;
Visible := True;
OnDblClick := @IndexViewDblClick;
end;
{
fSearchTab := TTabSheet.Create(fTabsControl);
with fSearchTab do begin
Caption := 'Search';
Parent := fTabsControl;
Visible := True;
BorderSpacing.Around := 3;
end;
fKeywordLabel := TLabel.Create(fSearchTab);
with fKeywordLabel do begin
Parent := fSearchTab;
Top := 5;
Caption := 'Keyword:';
Left := 0;
AutoSize := True;
end;
fKeywordCombo := TComboBox.Create(fSearchTab);
with fKeywordCombo do begin
Parent := fSearchTab;
Top := fKeywordLabel.Top + fKeywordLabel.Height + 5;
Left := 0;
Width := fSearchTab.ClientWidth;
Anchors := [akLeft, akRight, akTop];
end;
fSearchBtn := TButton.Create(fSearchTab);
with fSearchBtn do begin
Parent := fSearchTab
Top := fKeywordCombo.Top + fKeywordCombo.Height + 5;
Width := 105;
Left := fSearchTab.ClientWidth - Width;
Anchors := [akTop, akRight]
end;
fResultsLabel := TLabel.Create(fSearchTab);
with fResultsLabel do begin
Parent := fSearchTab;
Top := fSearchBtn.Top + fSearchBtn.Height + 15;
Caption := 'Search Results:';
Left := 0;
AutoSize := True;
end;
fSearchResults := TListBox.Create(fSearchTab);
with fSearchResults do begin
Parent := fSearchTab;
Top := fResultsLabel.Top + fResultsLabel.Height + 5;
Height := fSearchTab.ClientHeight - Top;
Anchors := [akTop, akBottom, akLeft, akRight];
end;
}
fSplitter := TSplitter.Create(Parent);
with fSplitter do begin
Align := alLeft;
Parent := AParent
end;
fPopUp := TPopupMenu.Create(fHtml);
fPopUp.Items.Add(TMenuItem.Create(fPopup));
with fPopUp.Items.Items[0] do begin
Caption := 'Copy';
OnClick := @PopupCopyClick;
end;
fChmDataProvider := TIpChmDataProvider.Create(fChms);
fHtml := TIpHtmlPanel.Create(Parent);
with fHtml do begin
DataProvider := fChmDataProvider;
OnDocumentOpen := @IpHtmlPanelDocumentOpen;
OnHotChange := @IpHtmlPanelHotChange;
PopupMenu := fPopUp;
Parent := AParent;
Align := alClient;
end;
fStatusBar := TStatusBar.Create(AParent);
with fStatusBar do begin
Parent := AParent;
Align := alBottom;
SimplePanel := True;
end;
end;
destructor TChmContentProvider.Destroy;
begin
DoCloseChm;
inherited Destroy;
end;
initialization
RegisterFileType('.chm', TChmContentProvider);
end.