3 using System.Collections;
6 using HtmlHelp.ChmDecoding;
11 /// The class <c>HtmlHelpSystem</c> implements the main object for reading chm files
13 public sealed class HtmlHelpSystem
16 /// Private shared instance of current HtmlHelpSystem class
18 private static HtmlHelpSystem _current=null;
20 /// Internal member storing the attached files
22 private ArrayList _chmFiles = new ArrayList();
24 /// Internal member storing a merged table of contents
26 private TableOfContents _toc = new TableOfContents();
28 /// Internal member storing a merged index
30 private Index _index = new Index();
32 /// URL prefix for specifying a chm destination
34 private static string _urlPrefix = "ms-its:";
36 /// Internal flag specifying if the system should use the tree-images list
37 /// from HtmlHelp2. If false the standard CHM-Viewer pics will be used.
39 private static bool _useHH2TreePics = false;
41 /// Internal member storing the read information types
43 private ArrayList _informationTypes = new ArrayList();
45 /// Internal member storing the read categories
47 private ArrayList _categories = new ArrayList();
50 /// Gets/Sets the url prefix for specifying a chm destination
52 public static string UrlPrefix
54 get { return _urlPrefix; }
55 set { _urlPrefix = value; }
58 public CHMStream.CHMStream BaseStream
62 CHMFile chm=(CHMFile)_chmFiles[0];
63 return chm.BaseStream;
68 /// Gets/Sets the flag specifying if the system should use the tree-images list
69 /// from HtmlHelp2. If false the standard CHM-Viewer pics will be used.
71 public static bool UseHH2TreePics
73 get { return _useHH2TreePics; }
74 set { _useHH2TreePics = value; }
78 /// Gets the current HtmlHelpSystem instance
80 public static HtmlHelpSystem Current
89 /// Standard constructor
91 public HtmlHelpSystem() : this("")
96 /// Constructor of the reader class
98 /// <param name="chmFile">chm file to attach with the reader</param>
99 public HtmlHelpSystem(string chmFile)
107 /// Opens a chm file and creates
109 /// <param name="chmFile">full file path of the chm file to open</param>
110 /// <remarks>If you call this method, all existing merged files will be cleared.</remarks>
111 public void OpenFile(string chmFile)
113 OpenFile(chmFile, null);
117 /// Opens a chm file and creates
119 /// <param name="chmFile">full file path of the chm file to open</param>
120 /// <param name="dmpInfo">dumping info</param>
121 /// <remarks>If you call this method, all existing merged files will be cleared.</remarks>
122 public void OpenFile(string chmFile, DumpingInfo dmpInfo)
124 if( File.Exists(chmFile ) )
129 _informationTypes.Clear();
132 CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);
134 _toc = new TableOfContents( newFile.TOC );
135 _index = new Index( newFile.IndexKLinks, newFile.IndexALinks );
137 _chmFiles.Add(newFile);
138 // add all infotypes and categories of the read file to this system instance
139 MergeFileInfoTypesCategories(newFile);
141 // check if the file has a merged files list
142 if( newFile.MergedFiles.Length > 0 )
144 // extract the path of the chm file (usually merged files are in the same path)
145 FileInfo fi = new FileInfo(chmFile);
146 string sPath = fi.DirectoryName;
148 for(int i=0; i<newFile.MergedFiles.Length; i++)
150 string sFile = newFile.MergedFiles[i];
154 if(sFile[1] != ':') // no full path setting
156 sFile = Path.Combine(sPath, sFile);
159 MergeFile(sFile, dmpInfo, true);
163 // if (newFile.MergLinks.Count>0)
164 // RecalculateMergeLinks(newFile);
166 RemoveMergeLinks(); // clear all merge-links which have no target !
172 /// Merges a chm file to the current help contents
174 /// <param name="chmFile">full file path of the chm file to merge</param>
175 public void MergeFile(string chmFile)
177 MergeFile(chmFile, null);
181 /// Merges a chm file to the current help contents
183 /// <param name="chmFile">full file path of the chm file to merge</param>
184 /// <param name="dmpInfo">dumping info</param>
185 public void MergeFile(string chmFile, DumpingInfo dmpInfo)
187 MergeFile(chmFile, dmpInfo, false);
191 /// Merges a chm file to the current help contents
193 /// <param name="chmFile">full file path of the chm file to merge</param>
194 /// <param name="dmpInfo">dumping info</param>
195 /// <param name="mergedFileList">true if the merge is done because a merged file list
196 /// was found in the previously loaded CHM.</param>
197 internal void MergeFile(string chmFile, DumpingInfo dmpInfo, bool mergedFileList)
199 if( File.Exists(chmFile ) )
201 if( _chmFiles.Count == 1)
203 // if we open the first file, we directly point into the toc and index of this file.
204 // So that we don't merge the new toc's indexe's into the first file, we have to
205 // clone the internal arraylists first to a new instance of the toc/index holder classes.
206 ArrayList atoc = _toc.TOC;
207 ArrayList alinks = _index.ALinks;
208 ArrayList klinks = _index.KLinks;
210 _toc = new TableOfContents();
211 _index = new Index();
213 _toc.MergeToC( atoc );
214 _index.MergeIndex( alinks, IndexType.AssiciativeLinks );
215 _index.MergeIndex( klinks, IndexType.KeywordLinks );
218 CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);
220 if(mergedFileList) // if we've called this method due to a merged file list merge
222 RecalculateMergeLinks(newFile);
224 _toc.MergeToC( newFile.TOC, _chmFiles );
225 _index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );
226 _index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );
228 _chmFiles.Add(newFile);
230 // add all infotypes and categories of the read file to this system instance
231 MergeFileInfoTypesCategories(newFile);
235 _toc.MergeToC( newFile.TOC, _chmFiles );
236 _index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );
237 _index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );
239 _chmFiles.Add(newFile);
241 // add all infotypes and categories of the read file to this system instance
242 MergeFileInfoTypesCategories(newFile);
244 // check if the file has a merged files list
245 if( newFile.MergedFiles.Length > 0 )
247 // extract the path of the chm file (usually merged files are in the same path)
248 FileInfo fi = new FileInfo(chmFile);
249 string sPath = fi.DirectoryName;
251 for(int i=0; i<newFile.MergedFiles.Length; i++)
253 string sFile = newFile.MergedFiles[i];
257 if(sFile[1] != ':') // no full path setting
259 sFile = Path.Combine(sPath, sFile);
262 MergeFile(sFile, dmpInfo, true);
266 RemoveMergeLinks(); // clear all merge-links which have no target !
273 /// Checks all Merg-links read till now. Checks if the merg-link points to the
274 /// file <c>currentFile</c>. If yes the link will be replaced by the contents of the
277 /// <param name="currentFile">Current CHMFile instance</param>
278 internal void RecalculateMergeLinks(CHMFile currentFile)
280 foreach(CHMFile curFile in _chmFiles)
282 if( curFile.MergLinks.Count > 0)
284 for(int i=0; i<curFile.MergLinks.Count; i++)
286 TOCItem curItem = curFile.MergLinks[i] as TOCItem;
288 string sMerge = curItem.MergeLink;
289 string [] sSplit = sMerge.Split( new char[]{':'} );
294 if( sSplit.Length > 3) // merge info contains path name
296 sFName = sSplit[0] + ":" + sSplit[1];
299 else if( sSplit.Length == 3)// merge info contains only file name
301 FileInfo fi = new FileInfo(currentFile.ChmFilePath);
302 string sPath = fi.DirectoryName;
304 string sFile = sSplit[0];
308 if(sFile[1] != ':') // no full path setting
310 sFile = Path.Combine(sPath, sFile);
318 ArrayList arrToc = null;
319 if( (sFName.Length>0) && (sTarget.Length>0) )
321 // if this link points into the current file
322 if( sFName.ToLower() == currentFile.ChmFilePath.ToLower() )
324 if(sTarget.ToLower().IndexOf(".hhc") >= 0)
326 string sfCheck = sTarget;
328 // remove prefixing ./
329 while( (sfCheck[0]=='.') || (sfCheck[0]=='/') )
331 sfCheck = sfCheck.Substring(1);
334 if( currentFile.ContentsFile.ToLower() != sfCheck )
336 arrToc = currentFile.ParseHHC( sTarget );
338 if( arrToc.Count > 0)
344 arrToc = currentFile.TOC;
347 // target points to a complete TOC
350 foreach(TOCItem chkItem in arrToc)
354 curItem.AssociatedFile = currentFile;
355 curItem.Children = chkItem.Children;
356 curItem.ChmFile = currentFile.ChmFilePath;
357 curItem.ImageIndex = chkItem.ImageIndex;
358 curItem.Local = chkItem.Local;
359 curItem.MergeLink = chkItem.MergeLink;
360 curItem.Name = chkItem.Name;
361 curItem.TocMode = chkItem.TocMode;
362 curItem.TopicOffset = chkItem.TopicOffset;
364 MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
368 ArrayList checkList = null;
370 if(curItem.Parent != null)
371 checkList = curItem.Parent.Children;
373 checkList = curFile.TOC;
375 int nIdx = checkList.IndexOf(curItem);
376 if((nIdx+nCnt)>checkList.Count)
377 checkList.Add(chkItem);
379 checkList.Insert(nIdx+nCnt, chkItem);
381 curFile.MergLinks.Add(chkItem);
382 MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
391 // target points to a single topic
392 TOCItem chkItem = currentFile.GetTOCItemByLocal(sTarget);
395 curItem.AssociatedFile = currentFile;
396 curItem.Children = chkItem.Children;
397 curItem.ChmFile = currentFile.ChmFilePath;
398 curItem.ImageIndex = chkItem.ImageIndex;
399 curItem.Local = chkItem.Local;
400 curItem.MergeLink = chkItem.MergeLink;
401 curItem.Name = chkItem.Name;
402 curItem.TocMode = chkItem.TocMode;
403 curItem.TopicOffset = chkItem.TopicOffset;
405 curFile.MergLinks.Add(chkItem);
406 MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
417 /// Adds sub-items of an TOC-entry to the merg-linked list.
418 /// This will mark this item as "added" during the extra merge run
419 /// of the HtmlHelpSystem class.
421 /// <param name="tocs">TOCItem list</param>
422 /// <param name="merged">Arraylist which holds the merged-items</param>
423 internal void MarkChildrenAdded(ArrayList tocs, ArrayList merged)
425 foreach(TOCItem curItem in tocs)
427 if(!merged.Contains(curItem))
431 MarkChildrenAdded(curItem.Children, merged);
437 /// Removes merge-links from the toc of files which were not loaded
439 internal void RemoveMergeLinks()
441 foreach(CHMFile curFile in _chmFiles)
443 if( curFile.MergLinks.Count > 0)
445 while(curFile.MergLinks.Count > 0)
447 TOCItem curItem = curFile.MergLinks[0] as TOCItem;
448 if(curItem.MergeLink.Length > 0)
449 curFile.RemoveTOCItem(curItem);
451 curFile.MergLinks.RemoveAt(0);
458 /// Merges the information types and categories read by the CHMFile instance
459 /// into the system instance
461 /// <param name="chmFile">file instance</param>
462 private void MergeFileInfoTypesCategories(CHMFile chmFile)
464 if(chmFile.HasInformationTypes)
466 for(int i=0; i<chmFile.InformationTypes.Count;i++)
468 InformationType curType = chmFile.InformationTypes[i] as InformationType;
469 InformationType sysType = GetInformationType( curType.Name );
472 _informationTypes.Add(curType);
474 curType.ReferenceCount++;
478 if(chmFile.HasCategories)
480 for(int i=0; i<chmFile.Categories.Count;i++)
482 Category curCat = chmFile.Categories[i] as Category;
483 Category sysCat = GetCategory( curCat.Name );
486 _categories.Add(curCat);
488 curCat.ReferenceCount++;
494 /// Removes the information types and categories read by the CHMFile instance
496 /// <param name="chmFile">file instance</param>
497 private void RemoveFileInfoTypesCategories(CHMFile chmFile)
499 if(chmFile.HasInformationTypes)
501 for(int i=0; i<chmFile.InformationTypes.Count;i++)
503 InformationType curType = chmFile.InformationTypes[i] as InformationType;
504 InformationType sysType = GetInformationType( curType.Name );
508 sysType.ReferenceCount--;
510 if(sysType.ReferenceCount<=0)
511 _informationTypes.Remove(sysType);
516 if(chmFile.HasCategories)
518 for(int i=0; i<chmFile.Categories.Count;i++)
520 Category curCat = chmFile.Categories[i] as Category;
521 Category sysCat = GetCategory( curCat.Name );
525 sysCat.ReferenceCount--;
527 if(sysCat.ReferenceCount<=0)
528 _categories.Remove(sysCat);
535 /// Removes a chm file from the internal file collection
537 /// <param name="chmFile">full file path of the chm file to remove</param>
538 public void RemoveFile(string chmFile)
541 CHMFile removeInstance=null;
543 foreach(CHMFile curFile in _chmFiles)
547 if( curFile.ChmFilePath.ToLower() == chmFile.ToLower() )
549 removeInstance = curFile;
556 _toc.Clear(); // forces a rebuild of the merged toc
557 _index.Clear(); // force a rebuild of the merged index
559 RemoveFileInfoTypesCategories(removeInstance);
560 _chmFiles.RemoveAt(nIdx);
565 /// Closes all files and destroys TOC/index
567 public void CloseAllFiles()
569 for(int i=0; i < _chmFiles.Count; i++)
571 CHMFile curFile = _chmFiles[i] as CHMFile;
573 _chmFiles.RemoveAt(i);
581 _informationTypes.Clear();
586 /// Gets an array of loaded chm files.
588 public CHMFile[] FileList
592 CHMFile[] ret = new CHMFile[ _chmFiles.Count ];
593 for(int i=0;i<_chmFiles.Count;i++)
594 ret[i] = (CHMFile)_chmFiles[i];
601 /// Returns true if the HtmlHelpSystem instance contains 1 or more information types
603 public bool HasInformationTypes
605 get { return (_informationTypes.Count>0); }
609 /// Returns true if the HtmlHelpSystem instance contains 1 or more categories
611 public bool HasCategories
613 get { return (_categories.Count>0); }
617 /// Gets an ArrayList of <see cref="InformationType">InformationType</see> items
619 public ArrayList InformationTypes
621 get { return _informationTypes; }
625 /// Gets an ArrayList of <see cref="Category">Category</see> items
627 public ArrayList Categories
629 get { return _categories; }
633 /// Gets the information type specified by its name
635 /// <param name="name">name of the information type to receive</param>
636 /// <returns>Returns the Instance for the name or null if not found</returns>
637 public InformationType GetInformationType(string name)
639 if(HasInformationTypes)
641 for(int i=0; i<_informationTypes.Count;i++)
643 InformationType iT = _informationTypes[i] as InformationType;
654 /// Gets the category specifiyd by its name
656 /// <param name="name">name of the category</param>
657 /// <returns>Returns the Instance for the name or null if not found</returns>
658 public Category GetCategory(string name)
662 for(int i=0; i<_categories.Count;i++)
664 Category cat = _categories[i] as Category;
675 /// Gets the default topic
677 public string DefaultTopic
681 if( _chmFiles.Count > 0 )
683 foreach(CHMFile curFile in _chmFiles)
685 if( curFile.DefaultTopic.Length > 0)
687 return curFile.FormURL( curFile.DefaultTopic );
692 return "about:blank";
697 /// Gets a merged table of contents of all opened chm files
699 public TableOfContents TableOfContents
703 if( _chmFiles.Count > 0 )
705 if( _toc.Count() <= 0)
707 // merge toc of files
708 foreach(CHMFile curFile in _chmFiles)
710 _toc.MergeToC( curFile.TOC );
720 /// Gets a merged index of all opened chm files
726 if( _chmFiles.Count > 0 )
728 if( (_index.Count(IndexType.KeywordLinks)+_index.Count(IndexType.AssiciativeLinks)) <= 0)
731 foreach(CHMFile curFile in _chmFiles)
733 _index.MergeIndex( curFile.IndexKLinks, IndexType.KeywordLinks);
734 _index.MergeIndex( curFile.IndexALinks, IndexType.AssiciativeLinks);
744 /// Gets a flag if the current instance offers a table of contents
746 public bool HasTableOfContents
750 return (TableOfContents.Count() > 0);
755 /// Gets a flag if the current instance offers an index
761 return (HasALinks || HasKLinks);
766 /// Gets a flag if the index holds klinks
768 public bool HasKLinks
772 return (_index.Count(IndexType.KeywordLinks) > 0);
777 /// Gets a flag if the index holds alinks
779 public bool HasALinks
783 return (_index.Count(IndexType.AssiciativeLinks) > 0);
788 /// Gets a flag if the current instance supports fulltext searching
790 public bool FullTextSearch
796 foreach(CHMFile curFile in _chmFiles)
798 bRet |= curFile.FullTextSearch;
806 /// Performs a full-text search over the chm files
808 /// <param name="words">words to search</param>
809 /// <param name="partialMatches">true if partial word should be matched also
810 /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
811 /// <param name="titleOnly">true if titles only</param>
812 /// <returns>A DataTable containing the search hits</returns>
813 public DataTable PerformSearch(string words, bool partialMatches, bool titleOnly)
815 return PerformSearch(words, -1, partialMatches, titleOnly);
819 /// Performs a full-text search over the chm files
821 /// <param name="words">words to search</param>
822 /// <param name="MaxResults">maximal number of hits to return</param>
823 /// <param name="partialMatches">true if partial word should be matched also
824 /// ( if this is true a search of 'support' will match 'supports', otherwise not )</param>
825 /// <param name="titleOnly">true if titles only</param>
826 /// <returns>A DataTable containing the search hits</returns>
827 public DataTable PerformSearch(string words, int MaxResults, bool partialMatches, bool titleOnly)
829 if( ! FullTextSearch )
832 DataTable dtResult = null;
836 foreach(CHMFile curFile in _chmFiles)
840 if(curFile.FullTextSearchEngine.CanSearch)
842 if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))
844 dtResult = curFile.FullTextSearchEngine.Hits;
845 dtResult.DefaultView.Sort = "Rating DESC";
851 if(curFile.FullTextSearchEngine.CanSearch)
853 if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))
855 DataTable table = curFile.FullTextSearchEngine.Hits;
857 // append rows from 2nd file
858 foreach(DataRow curRow in table.Rows)
860 dtResult.ImportRow( curRow );
863 dtResult.DefaultView.Sort = "Rating DESC";
868 if(dtResult.DefaultView.Count > MaxResults)
870 for(int i=MaxResults-1; i<dtResult.DefaultView.Count-1;i++)
872 if(dtResult.DefaultView[i].Row.RowState != DataRowState.Deleted)
874 dtResult.DefaultView[i].Row.Delete();
875 dtResult.DefaultView[i].Row.AcceptChanges();
880 dtResult.AcceptChanges();
881 dtResult.DefaultView.Sort = "Rating DESC";