- Change dispatcher lock release to be more like documented in Windows Internals...
[reactos.git] / irc / TechBot / CHMLibrary / HtmlHelpSystem.cs
1 using System;
2 using System.IO;
3 using System.Collections;
4 using System.Data;
5
6 using HtmlHelp.ChmDecoding;
7
8 namespace HtmlHelp
9 {
10 /// <summary>
11 /// The class <c>HtmlHelpSystem</c> implements the main object for reading chm files
12 /// </summary>
13 public sealed class HtmlHelpSystem
14 {
15 /// <summary>
16 /// Private shared instance of current HtmlHelpSystem class
17 /// </summary>
18 private static HtmlHelpSystem _current=null;
19 /// <summary>
20 /// Internal member storing the attached files
21 /// </summary>
22 private ArrayList _chmFiles = new ArrayList();
23 /// <summary>
24 /// Internal member storing a merged table of contents
25 /// </summary>
26 private TableOfContents _toc = new TableOfContents();
27 /// <summary>
28 /// Internal member storing a merged index
29 /// </summary>
30 private Index _index = new Index();
31 /// <summary>
32 /// URL prefix for specifying a chm destination
33 /// </summary>
34 private static string _urlPrefix = "ms-its:";
35 /// <summary>
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.
38 /// </summary>
39 private static bool _useHH2TreePics = false;
40 /// <summary>
41 /// Internal member storing the read information types
42 /// </summary>
43 private ArrayList _informationTypes = new ArrayList();
44 /// <summary>
45 /// Internal member storing the read categories
46 /// </summary>
47 private ArrayList _categories = new ArrayList();
48
49 /// <summary>
50 /// Gets/Sets the url prefix for specifying a chm destination
51 /// </summary>
52 public static string UrlPrefix
53 {
54 get { return _urlPrefix; }
55 set { _urlPrefix = value; }
56 }
57
58 public CHMStream.CHMStream BaseStream
59 {
60 get
61 {
62 CHMFile chm=(CHMFile)_chmFiles[0];
63 return chm.BaseStream;
64 }
65 }
66
67 /// <summary>
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.
70 /// </summary>
71 public static bool UseHH2TreePics
72 {
73 get { return _useHH2TreePics; }
74 set { _useHH2TreePics = value; }
75 }
76
77 /// <summary>
78 /// Gets the current HtmlHelpSystem instance
79 /// </summary>
80 public static HtmlHelpSystem Current
81 {
82 get
83 {
84 return _current;
85 }
86 }
87
88 /// <summary>
89 /// Standard constructor
90 /// </summary>
91 public HtmlHelpSystem() : this("")
92 {
93 }
94
95 /// <summary>
96 /// Constructor of the reader class
97 /// </summary>
98 /// <param name="chmFile">chm file to attach with the reader</param>
99 public HtmlHelpSystem(string chmFile)
100 {
101 _current = this;
102 OpenFile(chmFile);
103 }
104
105
106 /// <summary>
107 /// Opens a chm file and creates
108 /// </summary>
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)
112 {
113 OpenFile(chmFile, null);
114 }
115
116 /// <summary>
117 /// Opens a chm file and creates
118 /// </summary>
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)
123 {
124 if( File.Exists(chmFile ) )
125 {
126 _chmFiles.Clear();
127 _toc.Clear();
128 _index.Clear();
129 _informationTypes.Clear();
130 _categories.Clear();
131
132 CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);
133
134 _toc = new TableOfContents( newFile.TOC );
135 _index = new Index( newFile.IndexKLinks, newFile.IndexALinks );
136
137 _chmFiles.Add(newFile);
138 // add all infotypes and categories of the read file to this system instance
139 MergeFileInfoTypesCategories(newFile);
140
141 // check if the file has a merged files list
142 if( newFile.MergedFiles.Length > 0 )
143 {
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;
147
148 for(int i=0; i<newFile.MergedFiles.Length; i++)
149 {
150 string sFile = newFile.MergedFiles[i];
151
152 if(sFile.Length > 0)
153 {
154 if(sFile[1] != ':') // no full path setting
155 {
156 sFile = Path.Combine(sPath, sFile);
157 }
158
159 MergeFile(sFile, dmpInfo, true);
160 }
161 }
162
163 // if (newFile.MergLinks.Count>0)
164 // RecalculateMergeLinks(newFile);
165
166 RemoveMergeLinks(); // clear all merge-links which have no target !
167 }
168 }
169 }
170
171 /// <summary>
172 /// Merges a chm file to the current help contents
173 /// </summary>
174 /// <param name="chmFile">full file path of the chm file to merge</param>
175 public void MergeFile(string chmFile)
176 {
177 MergeFile(chmFile, null);
178 }
179
180 /// <summary>
181 /// Merges a chm file to the current help contents
182 /// </summary>
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)
186 {
187 MergeFile(chmFile, dmpInfo, false);
188 }
189
190 /// <summary>
191 /// Merges a chm file to the current help contents
192 /// </summary>
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)
198 {
199 if( File.Exists(chmFile ) )
200 {
201 if( _chmFiles.Count == 1)
202 {
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;
209
210 _toc = new TableOfContents();
211 _index = new Index();
212
213 _toc.MergeToC( atoc );
214 _index.MergeIndex( alinks, IndexType.AssiciativeLinks );
215 _index.MergeIndex( klinks, IndexType.KeywordLinks );
216 }
217
218 CHMFile newFile = new CHMFile(this, chmFile, dmpInfo);
219
220 if(mergedFileList) // if we've called this method due to a merged file list merge
221 {
222 RecalculateMergeLinks(newFile);
223
224 _toc.MergeToC( newFile.TOC, _chmFiles );
225 _index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );
226 _index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );
227
228 _chmFiles.Add(newFile);
229
230 // add all infotypes and categories of the read file to this system instance
231 MergeFileInfoTypesCategories(newFile);
232 }
233 else
234 {
235 _toc.MergeToC( newFile.TOC, _chmFiles );
236 _index.MergeIndex( newFile.IndexALinks, IndexType.AssiciativeLinks );
237 _index.MergeIndex( newFile.IndexKLinks, IndexType.KeywordLinks );
238
239 _chmFiles.Add(newFile);
240
241 // add all infotypes and categories of the read file to this system instance
242 MergeFileInfoTypesCategories(newFile);
243
244 // check if the file has a merged files list
245 if( newFile.MergedFiles.Length > 0 )
246 {
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;
250
251 for(int i=0; i<newFile.MergedFiles.Length; i++)
252 {
253 string sFile = newFile.MergedFiles[i];
254
255 if(sFile.Length > 0)
256 {
257 if(sFile[1] != ':') // no full path setting
258 {
259 sFile = Path.Combine(sPath, sFile);
260 }
261
262 MergeFile(sFile, dmpInfo, true);
263 }
264 }
265
266 RemoveMergeLinks(); // clear all merge-links which have no target !
267 }
268 }
269 }
270 }
271
272 /// <summary>
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
275 /// merged file.
276 /// </summary>
277 /// <param name="currentFile">Current CHMFile instance</param>
278 internal void RecalculateMergeLinks(CHMFile currentFile)
279 {
280 foreach(CHMFile curFile in _chmFiles)
281 {
282 if( curFile.MergLinks.Count > 0)
283 {
284 for(int i=0; i<curFile.MergLinks.Count; i++)
285 {
286 TOCItem curItem = curFile.MergLinks[i] as TOCItem;
287
288 string sMerge = curItem.MergeLink;
289 string [] sSplit = sMerge.Split( new char[]{':'} );
290
291 string sFName = "";
292 string sTarget = "";
293
294 if( sSplit.Length > 3) // merge info contains path name
295 {
296 sFName = sSplit[0] + ":" + sSplit[1];
297 sTarget = sSplit[3];
298 }
299 else if( sSplit.Length == 3)// merge info contains only file name
300 {
301 FileInfo fi = new FileInfo(currentFile.ChmFilePath);
302 string sPath = fi.DirectoryName;
303
304 string sFile = sSplit[0];
305
306 if(sFile.Length > 0)
307 {
308 if(sFile[1] != ':') // no full path setting
309 {
310 sFile = Path.Combine(sPath, sFile);
311 }
312 }
313
314 sFName = sFile;
315 sTarget = sSplit[2];
316 }
317
318 ArrayList arrToc = null;
319 if( (sFName.Length>0) && (sTarget.Length>0) )
320 {
321 // if this link points into the current file
322 if( sFName.ToLower() == currentFile.ChmFilePath.ToLower() )
323 {
324 if(sTarget.ToLower().IndexOf(".hhc") >= 0)
325 {
326 string sfCheck = sTarget;
327
328 // remove prefixing ./
329 while( (sfCheck[0]=='.') || (sfCheck[0]=='/') )
330 {
331 sfCheck = sfCheck.Substring(1);
332 }
333
334 if( currentFile.ContentsFile.ToLower() != sfCheck )
335 {
336 arrToc = currentFile.ParseHHC( sTarget );
337
338 if( arrToc.Count > 0)
339 {
340 }
341 }
342 else
343 {
344 arrToc = currentFile.TOC;
345 }
346
347 // target points to a complete TOC
348 int nCnt = 0;
349
350 foreach(TOCItem chkItem in arrToc)
351 {
352 if(nCnt == 0)
353 {
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;
363
364 MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
365 }
366 else
367 {
368 ArrayList checkList = null;
369
370 if(curItem.Parent != null)
371 checkList = curItem.Parent.Children;
372 else
373 checkList = curFile.TOC;
374
375 int nIdx = checkList.IndexOf(curItem);
376 if((nIdx+nCnt)>checkList.Count)
377 checkList.Add(chkItem);
378 else
379 checkList.Insert(nIdx+nCnt, chkItem);
380
381 curFile.MergLinks.Add(chkItem);
382 MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
383 }
384
385 nCnt++;
386 }
387 }
388 else
389 {
390
391 // target points to a single topic
392 TOCItem chkItem = currentFile.GetTOCItemByLocal(sTarget);
393 if(chkItem != null)
394 {
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;
404
405 curFile.MergLinks.Add(chkItem);
406 MarkChildrenAdded(chkItem.Children, curFile.MergLinks);
407 }
408 }
409 }
410 }
411 }
412 }
413 }
414 }
415
416 /// <summary>
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.
420 /// </summary>
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)
424 {
425 foreach(TOCItem curItem in tocs)
426 {
427 if(!merged.Contains(curItem))
428 {
429 merged.Add(curItem);
430
431 MarkChildrenAdded(curItem.Children, merged);
432 }
433 }
434 }
435
436 /// <summary>
437 /// Removes merge-links from the toc of files which were not loaded
438 /// </summary>
439 internal void RemoveMergeLinks()
440 {
441 foreach(CHMFile curFile in _chmFiles)
442 {
443 if( curFile.MergLinks.Count > 0)
444 {
445 while(curFile.MergLinks.Count > 0)
446 {
447 TOCItem curItem = curFile.MergLinks[0] as TOCItem;
448 if(curItem.MergeLink.Length > 0)
449 curFile.RemoveTOCItem(curItem);
450
451 curFile.MergLinks.RemoveAt(0);
452 }
453 }
454 }
455 }
456
457 /// <summary>
458 /// Merges the information types and categories read by the CHMFile instance
459 /// into the system instance
460 /// </summary>
461 /// <param name="chmFile">file instance</param>
462 private void MergeFileInfoTypesCategories(CHMFile chmFile)
463 {
464 if(chmFile.HasInformationTypes)
465 {
466 for(int i=0; i<chmFile.InformationTypes.Count;i++)
467 {
468 InformationType curType = chmFile.InformationTypes[i] as InformationType;
469 InformationType sysType = GetInformationType( curType.Name );
470
471 if( sysType == null)
472 _informationTypes.Add(curType);
473 else
474 curType.ReferenceCount++;
475 }
476 }
477
478 if(chmFile.HasCategories)
479 {
480 for(int i=0; i<chmFile.Categories.Count;i++)
481 {
482 Category curCat = chmFile.Categories[i] as Category;
483 Category sysCat = GetCategory( curCat.Name );
484
485 if(sysCat == null)
486 _categories.Add(curCat);
487 else
488 curCat.ReferenceCount++;
489 }
490 }
491 }
492
493 /// <summary>
494 /// Removes the information types and categories read by the CHMFile instance
495 /// </summary>
496 /// <param name="chmFile">file instance</param>
497 private void RemoveFileInfoTypesCategories(CHMFile chmFile)
498 {
499 if(chmFile.HasInformationTypes)
500 {
501 for(int i=0; i<chmFile.InformationTypes.Count;i++)
502 {
503 InformationType curType = chmFile.InformationTypes[i] as InformationType;
504 InformationType sysType = GetInformationType( curType.Name );
505
506 if(sysType != null)
507 {
508 sysType.ReferenceCount--;
509
510 if(sysType.ReferenceCount<=0)
511 _informationTypes.Remove(sysType);
512 }
513 }
514 }
515
516 if(chmFile.HasCategories)
517 {
518 for(int i=0; i<chmFile.Categories.Count;i++)
519 {
520 Category curCat = chmFile.Categories[i] as Category;
521 Category sysCat = GetCategory( curCat.Name );
522
523 if(sysCat != null)
524 {
525 sysCat.ReferenceCount--;
526
527 if(sysCat.ReferenceCount<=0)
528 _categories.Remove(sysCat);
529 }
530 }
531 }
532 }
533
534 /// <summary>
535 /// Removes a chm file from the internal file collection
536 /// </summary>
537 /// <param name="chmFile">full file path of the chm file to remove</param>
538 public void RemoveFile(string chmFile)
539 {
540 int nIdx = -1;
541 CHMFile removeInstance=null;
542
543 foreach(CHMFile curFile in _chmFiles)
544 {
545 nIdx++;
546
547 if( curFile.ChmFilePath.ToLower() == chmFile.ToLower() )
548 {
549 removeInstance = curFile;
550 break;
551 }
552 }
553
554 if(nIdx >= 0)
555 {
556 _toc.Clear(); // forces a rebuild of the merged toc
557 _index.Clear(); // force a rebuild of the merged index
558
559 RemoveFileInfoTypesCategories(removeInstance);
560 _chmFiles.RemoveAt(nIdx);
561 }
562 }
563
564 /// <summary>
565 /// Closes all files and destroys TOC/index
566 /// </summary>
567 public void CloseAllFiles()
568 {
569 for(int i=0; i < _chmFiles.Count; i++)
570 {
571 CHMFile curFile = _chmFiles[i] as CHMFile;
572
573 _chmFiles.RemoveAt(i);
574 curFile.Dispose();
575 i--;
576 }
577
578 _chmFiles.Clear();
579 _toc.Clear();
580 _index.Clear();
581 _informationTypes.Clear();
582 _categories.Clear();
583 }
584
585 /// <summary>
586 /// Gets an array of loaded chm files.
587 /// </summary>
588 public CHMFile[] FileList
589 {
590 get
591 {
592 CHMFile[] ret = new CHMFile[ _chmFiles.Count ];
593 for(int i=0;i<_chmFiles.Count;i++)
594 ret[i] = (CHMFile)_chmFiles[i];
595
596 return ret;
597 }
598 }
599
600 /// <summary>
601 /// Returns true if the HtmlHelpSystem instance contains 1 or more information types
602 /// </summary>
603 public bool HasInformationTypes
604 {
605 get { return (_informationTypes.Count>0); }
606 }
607
608 /// <summary>
609 /// Returns true if the HtmlHelpSystem instance contains 1 or more categories
610 /// </summary>
611 public bool HasCategories
612 {
613 get { return (_categories.Count>0); }
614 }
615
616 /// <summary>
617 /// Gets an ArrayList of <see cref="InformationType">InformationType</see> items
618 /// </summary>
619 public ArrayList InformationTypes
620 {
621 get { return _informationTypes; }
622 }
623
624 /// <summary>
625 /// Gets an ArrayList of <see cref="Category">Category</see> items
626 /// </summary>
627 public ArrayList Categories
628 {
629 get { return _categories; }
630 }
631
632 /// <summary>
633 /// Gets the information type specified by its name
634 /// </summary>
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)
638 {
639 if(HasInformationTypes)
640 {
641 for(int i=0; i<_informationTypes.Count;i++)
642 {
643 InformationType iT = _informationTypes[i] as InformationType;
644
645 if(iT.Name == name)
646 return iT;
647 }
648 }
649
650 return null;
651 }
652
653 /// <summary>
654 /// Gets the category specifiyd by its name
655 /// </summary>
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)
659 {
660 if(HasCategories)
661 {
662 for(int i=0; i<_categories.Count;i++)
663 {
664 Category cat = _categories[i] as Category;
665
666 if(cat.Name == name)
667 return cat;
668 }
669 }
670
671 return null;
672 }
673
674 /// <summary>
675 /// Gets the default topic
676 /// </summary>
677 public string DefaultTopic
678 {
679 get
680 {
681 if( _chmFiles.Count > 0 )
682 {
683 foreach(CHMFile curFile in _chmFiles)
684 {
685 if( curFile.DefaultTopic.Length > 0)
686 {
687 return curFile.FormURL( curFile.DefaultTopic );
688 }
689 }
690 }
691
692 return "about:blank";
693 }
694 }
695
696 /// <summary>
697 /// Gets a merged table of contents of all opened chm files
698 /// </summary>
699 public TableOfContents TableOfContents
700 {
701 get
702 {
703 if( _chmFiles.Count > 0 )
704 {
705 if( _toc.Count() <= 0)
706 {
707 // merge toc of files
708 foreach(CHMFile curFile in _chmFiles)
709 {
710 _toc.MergeToC( curFile.TOC );
711 }
712 }
713 }
714
715 return _toc;
716 }
717 }
718
719 /// <summary>
720 /// Gets a merged index of all opened chm files
721 /// </summary>
722 public Index Index
723 {
724 get
725 {
726 if( _chmFiles.Count > 0 )
727 {
728 if( (_index.Count(IndexType.KeywordLinks)+_index.Count(IndexType.AssiciativeLinks)) <= 0)
729 {
730 // merge index files
731 foreach(CHMFile curFile in _chmFiles)
732 {
733 _index.MergeIndex( curFile.IndexKLinks, IndexType.KeywordLinks);
734 _index.MergeIndex( curFile.IndexALinks, IndexType.AssiciativeLinks);
735 }
736 }
737 }
738
739 return _index;
740 }
741 }
742
743 /// <summary>
744 /// Gets a flag if the current instance offers a table of contents
745 /// </summary>
746 public bool HasTableOfContents
747 {
748 get
749 {
750 return (TableOfContents.Count() > 0);
751 }
752 }
753
754 /// <summary>
755 /// Gets a flag if the current instance offers an index
756 /// </summary>
757 public bool HasIndex
758 {
759 get
760 {
761 return (HasALinks || HasKLinks);
762 }
763 }
764
765 /// <summary>
766 /// Gets a flag if the index holds klinks
767 /// </summary>
768 public bool HasKLinks
769 {
770 get
771 {
772 return (_index.Count(IndexType.KeywordLinks) > 0);
773 }
774 }
775
776 /// <summary>
777 /// Gets a flag if the index holds alinks
778 /// </summary>
779 public bool HasALinks
780 {
781 get
782 {
783 return (_index.Count(IndexType.AssiciativeLinks) > 0);
784 }
785 }
786
787 /// <summary>
788 /// Gets a flag if the current instance supports fulltext searching
789 /// </summary>
790 public bool FullTextSearch
791 {
792 get
793 {
794 bool bRet = false;
795
796 foreach(CHMFile curFile in _chmFiles)
797 {
798 bRet |= curFile.FullTextSearch;
799 }
800
801 return bRet;
802 }
803 }
804
805 /// <summary>
806 /// Performs a full-text search over the chm files
807 /// </summary>
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)
814 {
815 return PerformSearch(words, -1, partialMatches, titleOnly);
816 }
817
818 /// <summary>
819 /// Performs a full-text search over the chm files
820 /// </summary>
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)
828 {
829 if( ! FullTextSearch )
830 return null;
831
832 DataTable dtResult = null;
833
834 int nCnt = 0;
835
836 foreach(CHMFile curFile in _chmFiles)
837 {
838 if(nCnt == 0)
839 {
840 if(curFile.FullTextSearchEngine.CanSearch)
841 {
842 if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))
843 {
844 dtResult = curFile.FullTextSearchEngine.Hits;
845 dtResult.DefaultView.Sort = "Rating DESC";
846 }
847 }
848 }
849 else
850 {
851 if(curFile.FullTextSearchEngine.CanSearch)
852 {
853 if(curFile.FullTextSearchEngine.Search(words, MaxResults, partialMatches, titleOnly))
854 {
855 DataTable table = curFile.FullTextSearchEngine.Hits;
856
857 // append rows from 2nd file
858 foreach(DataRow curRow in table.Rows)
859 {
860 dtResult.ImportRow( curRow );
861 }
862
863 dtResult.DefaultView.Sort = "Rating DESC";
864
865 // adjust max hits
866 if(MaxResults >= 0)
867 {
868 if(dtResult.DefaultView.Count > MaxResults)
869 {
870 for(int i=MaxResults-1; i<dtResult.DefaultView.Count-1;i++)
871 {
872 if(dtResult.DefaultView[i].Row.RowState != DataRowState.Deleted)
873 {
874 dtResult.DefaultView[i].Row.Delete();
875 dtResult.DefaultView[i].Row.AcceptChanges();
876 i--;
877 }
878 }
879
880 dtResult.AcceptChanges();
881 dtResult.DefaultView.Sort = "Rating DESC";
882 }
883 }
884 }
885 }
886 }
887
888 nCnt++;
889 }
890
891 return dtResult;
892 }
893 }
894 }