1 /*-*- c++ -*-********************************************************
2 * wxexthlp.cpp - an external help controller for wxWindows *
4 * (C) 1998 by Karsten Ballüder (Ballueder@usa.net) *
7 *******************************************************************/
9 # pragma implementation "wxexthlp.h"
13 #include "wx/helpbase.h"
14 #include "wx/generic/helpext.h"
15 #include "wx/string.h"
23 #define WXEXTHELP_INCLUDE_KBLIST
25 ** This class uses kbList, a simple linked list. Until someone
26 ** rewrites it to use wxList instead, I include the relevant bits and
27 ** pieces of kbList here. It's a tiny class anyway, so it won't make
28 ** a big difference. The comments probably take up more space than
32 #ifdef WXEXTHELP_INCLUDE_KBLIST
34 /********************* kbList.h, verbose copy: ****************************/
35 /**@name Double linked list implementation. */
38 /** kbListNode is a class used by kbList. It represents a single
39 element in the list. It is not intended for general use outside
44 /// pointer to next node or NULL
45 struct kbListNode
*next
;
46 /// pointer to previous node or NULL
47 struct kbListNode
*prev
;
48 /// pointer to the actual data
50 /** Constructor - it automatically links the node into the list, if
51 the iprev, inext parameters are given.
52 @param ielement pointer to the data for this node (i.e. the data itself)
53 @param iprev if not NULL, use this as previous element in list
54 @param inext if not NULL, use this as next element in list
56 kbListNode( void *ielement
,
57 kbListNode
*iprev
= NULL
,
58 kbListNode
*inext
= NULL
);
63 /** The main list class, handling void pointers as data.
69 /// An iterator class for kbList, just like for the STL classes.
73 /// the node to which this iterator points
78 @param n if not NULL, the node to which to point
80 iterator(kbListNode
*n
= NULL
);
81 /** Dereference operator.
82 @return the data pointer of the node belonging to this
87 /** This operator allows us to write if(i). It is <em>not</em> a
88 dereference operator and the result is always useless apart
89 from its logical value!
91 operator void*() const { return node
== NULL
? (void*)0 : (void*)(-1); }
93 /** Increment operator - prefix, goes to next node in list.
96 iterator
& operator++();
98 /** Decrement operator - prefix, goes to previous node in list.
101 iterator
& operator--();
103 /** Increment operator - prefix, goes to next node in list.
106 iterator
& operator++(int); //postfix
108 /** Decrement operator - prefix, goes to previous node in list.
111 iterator
& operator--(int); //postfix
113 /** Comparison operator.
114 @return true if not equal.
116 bool operator !=(iterator
const &) const;
118 /* Comparison operator.
119 @return true if equal
121 bool operator ==(iterator
const &) const;
123 /** Returns a pointer to the node associated with this iterator.
124 This function is not for general use and should be
125 protected. However, if protected, it cannot be called from
126 derived classes' iterators. (Is this a bug in gcc/egcs?)
127 @return the node pointer
129 inline kbListNode
* Node(void) const
134 @param ownsEntriesFlag if true, the list owns the entries and
135 will issue a delete on each of them when deleting them. If
136 false, the entries themselves will not get deleted. Do not use
137 this with array types!
139 kbList(bool ownsEntriesFlag
= true);
142 If entries are owned, they will all get deleted from here.
146 /** Tell list whether it owns objects. If owned, they can be
147 deleted by list. See the constructor for more details.
148 @param ownsflag if true, list will own entries
150 void ownsObjects(bool ownsflag
= true)
151 { ownsEntries
= ownsflag
; }
153 /** Query whether list owns entries.
154 @return true if list owns entries
156 bool ownsObjects(void)
157 { return ownsEntries
; }
159 /** Add an entry at the end of the list.
160 @param element pointer to data
162 void push_back(void *element
);
164 /** Add an entry at the head of the list.
165 @param element pointer to data
167 void push_front(void *element
);
169 /** Get element from end of the list and delete it.
170 NOTE: In this case the element's data will not get deleted by
171 the list. It is the responsibility of the caller to free it.
172 @return the element data
174 void *pop_back(void);
176 /** Get element from head of the list and delete it.
177 NOTE: In this case the element's data will not get deleted by
178 the list. It is the responsibility of the caller to free it.
179 @return the element data
181 void *pop_front(void);
183 /** Insert an element into the list.
184 @param i an iterator pointing to the element, before which the new one should be inserted
185 @param element the element data
187 void insert(iterator
& i
, void *element
);
189 /** Remove an element from the list _without_ deleting the object.
190 @param i iterator pointing to the element to be deleted
191 @return the value of the element just removed
193 void *remove(iterator
& i
) { void *p
= *i
; doErase(i
); return p
; }
195 /** Erase an element, move iterator to following element.
196 @param i iterator pointing to the element to be deleted
198 void erase(iterator
& i
) { deleteContent(i
); doErase(i
); }
201 @return iterator pointing to head of list
203 iterator
begin(void) const;
206 @return iterator pointing after the end of the list. This is an
207 invalid iterator which cannot be dereferenced or decremented. It is
208 only of use in comparisons. NOTE: this is different from STL!
211 iterator
end(void) const;
213 /* Get last element in list.
214 @return iterator pointing to the last element in the list.
217 iterator
tail(void) const;
219 /* Get the number of elements in the list.
220 @return number of elements in the list
222 unsigned size(void) const;
224 /* Query whether list is empty.
225 @return true if list is empty
227 inline bool empty(void) const
228 { return first
== NULL
; }
231 /// if true, list owns entries
233 /// pointer to first element in list
235 /// pointer to last element in list
238 /** Erase an element, move iterator to following element.
239 @param i iterator pointing to the element to be deleted
241 void doErase(iterator
& i
);
243 /** Deletes the actual content if ownsflag is set.
246 inline void deleteContent(iterator i
)
247 { if(ownsEntries
) delete *i
; }
251 /// forbid copy construction
252 kbList(kbList
const &foo
);
253 /// forbid assignments
254 kbList
& operator=(const kbList
& foo
);
257 /** Macro to define a kbList with a given name, having elements of
258 pointer to the given type. I.e. KBLIST_DEFINE(Int,int) would
259 create a kbListInt type holding int pointers.
261 #define KBLIST_DEFINE(name,type) \
262 class name : public kbList \
265 class iterator : public kbList::iterator \
268 inline iterator(kbList::iterator const & i) \
269 { node = i.Node(); } \
272 inline iterator(kbListNode *n = NULL) \
273 : kbList::iterator(n) {} \
274 inline type * operator*() \
275 /* the cast is needed for MS VC++ 5.0 */ \
276 { return (type *)((kbList::iterator *)this)->operator*() ; } \
278 inline name(bool ownsEntriesFlag = TRUE) \
279 : kbList(ownsEntriesFlag) {} \
281 inline type *pop_back(void) \
282 { return (type *) kbList::pop_back(); } \
284 inline type *pop_front(void) \
285 { return (type *) kbList::pop_front(); } \
287 type *remove(iterator& i) \
288 { return (type *)kbList::remove(i); } \
289 inline void erase(iterator & i) \
290 { deleteContent(i); kbList::erase(i); } \
292 inline iterator begin(void) const \
293 { return kbList::begin(); } \
295 inline iterator end(void) const \
296 { return kbList::end(); } \
298 inline iterator tail(void) const \
299 { return kbList::tail(); } \
303 while ( first != NULL ) \
305 next = first->next; \
307 delete (type *)first->element; \
313 inline void deleteContent(iterator i) \
314 { if(ownsEntries) delete *i; } \
318 /************************* copy of kbList.cpp: ****************************/
319 kbListNode::kbListNode( void *ielement
,
332 kbListNode::~kbListNode()
341 kbList::iterator::iterator(kbListNode
*n
)
347 kbList::iterator::operator*()
349 return node
->element
;
353 kbList::iterator::operator++()
355 node
= node
? node
->next
: NULL
;
360 kbList::iterator::operator--()
362 node
= node
? node
->prev
: NULL
;
366 kbList::iterator::operator++(int /* foo */)
372 kbList::iterator::operator--(int /* bar */)
379 kbList::iterator::operator !=(kbList::iterator
const & i
) const
381 return node
!= i
.node
;
385 kbList::iterator::operator ==(kbList::iterator
const & i
) const
387 return node
== i
.node
;
390 kbList::kbList(bool ownsEntriesFlag
)
394 ownsEntries
= ownsEntriesFlag
;
398 kbList::push_back(void *element
)
400 if(! first
) // special case of empty list
402 first
= new kbListNode(element
);
407 last
= new kbListNode(element
, last
);
411 kbList::push_front(void *element
)
413 if(! first
) // special case of empty list
419 first
= new kbListNode(element
, NULL
, first
);
423 kbList::pop_back(void)
427 bool ownsFlagBak
= ownsEntries
;
432 ownsEntries
= ownsFlagBak
;
437 kbList::pop_front(void)
441 bool ownsFlagBak
= ownsEntries
;
447 ownsEntries
= ownsFlagBak
;
453 kbList::insert(kbList::iterator
& i
, void *element
)
457 else if(i
.Node() == first
)
463 i
= kbList::iterator(new kbListNode(element
, i
.Node()->prev
, i
.Node()));
467 kbList::doErase(kbList::iterator
& i
)
473 if(! node
) // illegal iterator
479 // correct first/last:
482 if(node
== last
) // don't put else here!
491 // delete this node and contents:
492 // now done separately
497 // change the iterator to next element:
498 i
= kbList::iterator(next
);
505 while ( first
!= NULL
)
509 delete first
->element
;
516 kbList::begin(void) const
518 return kbList::iterator(first
);
522 kbList::tail(void) const
524 return kbList::iterator(last
);
528 kbList::end(void) const
530 return kbList::iterator(NULL
); // the one after the last
534 kbList::size(void) const // inefficient
538 for(i
= begin(); i
!= end(); i
++, count
++)
544 /************************* end of kbList code *****************************/
546 struct wxExtHelpMapEntry
551 wxExtHelpMapEntry(int iid
, wxString
const &iurl
, wxString
const &idoc
)
552 { id
= iid
; url
= iurl
; doc
= idoc
; }
554 KBLIST_DEFINE(wxExtHelpMapList
, wxExtHelpMapEntry
);
559 wxBusyCursor() { wxBeginBusyCursor(); }
560 ~wxBusyCursor() { wxEndBusyCursor(); }
563 IMPLEMENT_CLASS(wxExtHelpController
, wxHelpControllerBase
)
566 This class implements help via an external browser.
567 It requires the name of a directory containing the documentation
568 and a file mapping numerical Section numbers to relative URLS.
571 wxExtHelpController::wxExtHelpController(void)
574 m_BrowserName
= WXEXTHELP_DEFAULTBROWSER
;
575 m_BrowserIsNetscape
= WXEXTHELP_DEFAULTBROWSER_IS_NETSCAPE
;
577 char *browser
= getenv(WXEXTHELP_ENVVAR_BROWSER
);
580 m_BrowserName
= browser
;
581 browser
= getenv(WXEXTHELP_ENVVAR_BROWSERISNETSCAPE
);
582 m_BrowserIsNetscape
= browser
&& (atoi(browser
) != 0);
586 wxExtHelpController::~wxExtHelpController(void)
588 if(m_MapList
) delete m_MapList
;
592 wxExtHelpController::SetBrowser(wxString
const & browsername
, bool isNetscape
)
594 m_BrowserName
= browsername
;
595 m_BrowserIsNetscape
= isNetscape
;
598 /** This must be called to tell the controller where to find the
600 @param file - NOT a filename, but a directory name.
601 @return true on success
604 wxExtHelpController::Initialize(const wxString
& file
)
606 return LoadFile(file
);
611 wxExtHelpController::LoadFile(const wxString
& ifile
= "")
613 wxString mapFile
, file
, url
, doc
;
615 char buffer
[WXEXTHELP_BUFLEN
];
617 wxBusyCursor b
; // display a busy cursor
619 if(! ifile
.IsEmpty())
622 if(! wxIsAbsolutePath(file
))
624 file
= wxGetWorkingDirectory();
625 file
<< WXEXTHELP_SEPARATOR
<< ifile
;
630 if(! wxDirExists(file
))
633 mapFile
<< file
<< WXEXTHELP_SEPARATOR
<< WXEXTHELP_MAPFILE
;
635 else // try to reload old file
638 if(! wxFileExists(mapFile
))
641 if(m_MapList
) delete m_MapList
;
642 m_MapList
= new wxExtHelpMapList
;
645 FILE *input
= fopen(mapFile
.c_str(),"rt");
650 if(fgets(buffer
,WXEXTHELP_BUFLEN
,input
) && *buffer
!= WXEXTHELP_COMMENTCHAR
)
652 len
= strlen(buffer
);
653 if(buffer
[len
-1] == '\n')
654 buffer
[len
-1] = '\0'; // cut of trailing newline
655 if(sscanf(buffer
,"%d", &id
) != 1)
657 for(i
=0; isdigit(buffer
[i
])||isspace(buffer
[i
]); i
++)
658 ; // find begin of URL
660 while(buffer
[i
] && ! isspace(buffer
[i
]) && buffer
[i
] !=
661 WXEXTHELP_COMMENTCHAR
)
663 while(buffer
[i
] && buffer
[i
] != WXEXTHELP_COMMENTCHAR
)
667 doc
= (buffer
+ i
+ 1); // skip the comment character
668 m_MapList
->push_back(new wxExtHelpMapEntry(id
,url
,doc
));
673 }while(! feof(input
));
676 m_MapFile
= file
; // now it's valid
681 wxExtHelpController::CallBrowser(wxString
const &relativeURL
)
683 wxBusyCursor b
; // display a busy cursor
686 if(m_BrowserIsNetscape
) // try re-loading first
689 wxGetHomeDir(&lockfile
);
690 lockfile
<< WXEXTHELP_SEPARATOR
<< ".netscape/lock";
692 if(lstat(lockfile
.c_str(), &statbuf
) == 0)
693 // cannot use wxFileExists, because it's a link pointing to a
694 // non-existing location if(wxFileExists(lockfile))
697 command
<< m_BrowserName
<< " -remote openURL("
698 << "file://" << m_MapFile
699 << WXEXTHELP_SEPARATOR
<< relativeURL
<< ")";
700 success
= wxExecute(command
);
701 if(success
!= 0 ) // returns PID on success
705 command
= m_BrowserName
;
706 command
<< " file://"
707 << m_MapFile
<< WXEXTHELP_SEPARATOR
<< relativeURL
;
708 return wxExecute(command
) != 0;
712 wxExtHelpController::DisplayContents(void)
716 wxBusyCursor b
; // display a busy cursor
717 return KeywordSearch("");
721 wxExtHelpController::DisplaySection(int sectionNo
)
726 wxBusyCursor b
; // display a busy cursor
727 wxExtHelpMapList::iterator i
= m_MapList
->begin();
728 while(i
!= m_MapList
->end())
730 if((**i
).id
== sectionNo
)
731 return CallBrowser((**i
).url
);
738 wxExtHelpController::DisplayBlock(long blockNo
)
740 return DisplaySection((int)blockNo
);
744 wxExtHelpController::KeywordSearch(const wxString
& k
)
749 wxBusyCursor b
; // display a busy cursor
750 wxString
*choices
= new wxString
[m_NumOfEntries
];
751 wxString
*urls
= new wxString
[m_NumOfEntries
];
752 wxString compA
, compB
;
756 bool showAll
= k
.IsEmpty();
757 wxExtHelpMapList::iterator i
= m_MapList
->begin();
759 compA
= k
; compA
.LowerCase(); // we compare case insensitive
760 while(i
!= m_MapList
->end())
762 compB
= (**i
).doc
; compB
.LowerCase();
763 if((showAll
|| compB
.Contains(k
)) && ! compB
.IsEmpty())
765 urls
[idx
] = (**i
).url
;
767 // choices[idx] = (**i).doc.Contains((**i).doc.Before(WXEXTHELP_COMMENTCHAR));
768 //if(choices[idx].IsEmpty()) // didn't contain the ';'
769 // choices[idx] = (**i).doc;
771 for(j
=0;(**i
).doc
.c_str()[j
]
772 && (**i
).doc
.c_str()[j
] != WXEXTHELP_COMMENTCHAR
; j
++)
773 choices
[idx
] << (**i
).doc
.c_str()[j
];
780 rc
= CallBrowser(urls
[0]);
783 wxMessageBox(_("No entries found."));
788 idx
= wxGetSingleChoiceIndex(showAll
? _("Help Index") : _("Relevant entries:"),
789 showAll
? _("Help Index") : _("Entries found"),
792 rc
= CallBrowser(urls
[idx
]);
804 wxExtHelpController::Quit(void)
810 wxExtHelpController::OnQuit(void)