1 #include "HelpViewer.h"
2 
3 #define IMAGECLASS HelpImg
4 #define IMAGEFILE <HelpViewer/HelpViewer.iml>
5 #include <Draw/iml_source.h>
6 
7 const char *TOPICLINK = "^topic`:`/`/";
8 
HelpViewer()9 HelpViewer::HelpViewer()
10 {
11 	// adds toolbar
12 	AddFrame(toolBar);
13 
14 	// setups splitter and its contents
15 	Add(splitter);
16 	splitter.Horz(tocPane, contentsPane).SetPos(2500);
17 	contentsPane.AutoHideSb();
18 	contentsPane.SetZoom(Zoom(1,1));
19 	contentsPane.HMargins(20);
20 
21 	tocPane.Add(mainTocTree.NoRoot().SizePos());
22 
23 	// setup TOC link callback
24 	mainTocTree.WhenSel = THISBACK(tocLinkCb);
25 	contentsPane.WhenLink << THISBACK(contentLinkCb);
26 
27 	// initialize link stack
28 	stack.Clear();
29 	tos = -1;
30 
31 	Sizeable().Zoomable();
32 
33 	// loads toolbar
34 	toolBar.Set(THISBACK(toolBarCb));
35 }
36 
37 // appends a treeCtrl to main tocCtrl
AppendTOC(TreeCtrl const & t,int curId,int destId)38 void HelpViewer::AppendTOC(TreeCtrl const &t, int curId, int destId)
39 {
40 	// adds root node of current level
41 	int destChild = mainTocTree.Add(destId, t.GetNode(curId));
42 
43 	// recursively add all children
44 	for(int i = 0; i < t.GetChildCount(curId); i++)
45 		AppendTOC(t, t.GetChild(curId, i), destChild);
46 }
47 
48 // Parses TOC and fills tocTree control
LoadTOC(String const & tocLink)49 bool HelpViewer::LoadTOC(String const &tocLink)
50 {
51 	// TOC is composed by QTF lines with a TOC link and label text
52 	// lines not containing the TOC link are simply ignored
53 	// TOC level is represented by number of TABS contained
54 	// inside a line; zero tabs means toplevel, id 0 of tocTree
55 	Vector<int>ids;
56 	ids.Add(0);
57 	int curLevel = 0;
58 	int curId = 0;
59 	TreeCtrl tocTree;
60 
61 	Topic t = GetTopic(tocLink);
62 	if(IsNull(t.text))
63 		return false;
64 
65 	String label = t.label;
66 	String topic = t.link;
67 
68 	// sets toc root string from topic ticle
69 	tocTree.SetRoot(Null, t.title);
70 
71 	// converts QTF to RichTxt, easier to analyze
72 	RichText txt = ParseQTF(t.text);
73 
74 	// extract all paragraphs from RichTxt, skip other objects
75 	for(int iPart = 0; iPart < txt.GetPartCount(); iPart++)
76 	{
77 		String tocLine;
78 		String lineLink;
79 		if(!txt.IsPara(iPart))
80 			continue;
81 		RichPara p = txt.Get(iPart);
82 		for(int iParaPart = 0; iParaPart < p.GetCount(); iParaPart++)
83 		{
84 			RichPara::Part &paraPart = p.part[iParaPart];
85 			if(paraPart.format.link != "")
86 				lineLink = paraPart.format.link;
87 			if(paraPart.NonText())
88 				continue;
89 			tocLine += paraPart.text.ToString();
90 		}
91 		// if part contains no text nor link to topic stuffs, simply skip it
92 		if(tocLine == "" || lineLink == "" || !lineLink.StartsWith("topic:"))
93 			continue;
94 
95 		// now we should count tabls to get TOC indent level
96 		int level = 0;
97 		int j;
98 		while( (j = tocLine.Find(0x09)) >= 0)
99 		{
100 			level++;
101 			tocLine.Remove(j);
102 		}
103 		// allows just SINGLE UPPER level change, i.e., deeper ONE level from current
104 		// for each step; backleveling is possible at any depth
105 		if(level > curLevel+1)
106 			return false;
107 		if(level > curLevel)
108 		{
109 			ids.Add(curId);
110 			curLevel++;
111 		}
112 		else while(curLevel > level)
113 		{
114 			curLevel--;
115 			ids.Pop();
116 		}
117 
118 		// add the line to correct tree node
119 		curId = tocTree.Add(ids.Top(), Null, Value(lineLink), Value(tocLine));
120 	}
121 
122 	// if the tree control is non empty, appends it to the main TOC TreeCtrl
123 	if(tocTree.GetChildCount(0))
124 		AppendTOC(tocTree);
125 
126 
127 	// opens all content tree and display first item
128 	mainTocTree.OpenDeep(0);
129 	mainTocTree.SetCursor(2);
130 
131 	// stores first topic at top of stack
132 	String curLink = mainTocTree.Get();
133 	stack.Clear();
134 	stack.Add(curLink);
135 	tos = 0;
136 
137 	return true;
138 }
139 
140 // go to a selected link
showLink(String const & link)141 void HelpViewer::showLink(String const &link)
142 {
143 	Topic t = GetTopic(link);
144 	if(!IsNull(t.text))
145 	{
146 		Zoom zoom;
147 		zoom.m = 160;
148 		zoom.d = 1000;
149 
150 		String label = t.label;
151 		RichText txt = ParseQTF(t.text);
152 		contentsPane.Pick(pick(txt), zoom);
153 		contentsPane.GotoLabel(label, true);
154 		int tocId = mainTocTree.Find(link);
155 		if(tocId >= 0)
156 		{
157 			// disable tocTree action on selecting cursor
158 			// otherwise we've got a recursive call
159 			mainTocTree.WhenSel.Clear();
160 			mainTocTree.SetCursor(tocId);
161 
162 			// re-enable tocTree action
163 			mainTocTree.WhenSel = THISBACK(tocLinkCb);
164 
165 			// setup window title
166 			Title(mainTocTree.GetValue());
167 		}
168 	}
169 }
170 
171 // reacts on link selection inside content pane
contentLinkCb(String const & link)172 void HelpViewer::contentLinkCb(String const &link)
173 {
174 	// clear forward buffer
175 	stack.Trim(tos+1);
176 
177 	// updates history
178 	int tocId = mainTocTree.GetCursor();
179 	if(tocId > 0)
180 	{
181 		String curLink = mainTocTree.Get();
182 		stack.Add(curLink);
183 		tos++;
184 		toolBar.Set(THISBACK(toolBarCb));
185 
186 	}
187 
188 	// shows link inside main pane
189 	showLink(link);
190 }
191 
192 
tocLinkCb()193 void HelpViewer::tocLinkCb()
194 {
195 	int id = mainTocTree.GetCursor();
196 	if(id < 0)
197 		return;
198 
199 	// gets link
200 	String link = mainTocTree.Get(id);
201 
202 	// follow link
203 	contentLinkCb(link);
204 }
205 
206 // go previous/next
backCb(void)207 void HelpViewer::backCb(void)
208 {
209 	if(--tos >= 0)
210 	{
211 		String link = stack[tos];
212 		showLink(link);
213 	}
214 	toolBar.Set(THISBACK(toolBarCb));
215 }
216 
fwCb(void)217 void HelpViewer::fwCb(void)
218 {
219 	if(tos < stack.GetCount() - 1)
220 	{
221 		String link = stack[++tos];
222 		showLink(link);
223 	}
224 	toolBar.Set(THISBACK(toolBarCb));
225 }
226 
227 // toolbar construction
toolBarCb(Bar & bar)228 void HelpViewer::toolBarCb(Bar &bar)
229 {
230 	bar.Add(tos > 0, "Back", HelpImg::Back(), THISBACK(backCb));
231 	bar.Add(tos < stack.GetCount() - 1, "Forward", HelpImg::Forward(), THISBACK(fwCb));
232 }
233