comparison anagram/vaclgui/helpview.cpp @ 0:13d2b8934445

Import AnaGram (near-)release tree into Mercurial.
author David A. Holland
date Sat, 22 Dec 2007 17:52:45 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:13d2b8934445
1 /*
2 * AnaGram, A System for Syntax Directed Programming
3 * Copyright 1997-2002 Parsifal Software. All Rights Reserved.
4 * See the file COPYING for license and usage terms.
5 *
6 * helpview.cpp
7 */
8
9 #include <icoordsy.hpp>
10 #include <windows.h>
11
12 #include "agcstack.h"
13 #include "agstack.h"
14 #include "agstring.h"
15 #include "ctrlpanel.hpp"
16 #include "dspar.hpp"
17 #include "dview.hpp"
18 #include "file.h"
19 #include "help.h"
20 #include "helpview.hpp"
21 #include "minmax.h"
22 #include "stacks.h"
23 #include "vaclgui-res.h"
24 #include "vaclgui.hpp"
25
26 //#define INCLUDE_LOGGING
27 #include "log.h"
28
29
30 const int AgHelpView::defaultWindowHeight = 15;
31 AgBalancedTree<AgString> DrawingArea::traversedLinks;
32 AgBalancedTree<DrawingArea *> DrawingArea::activeViews;
33
34
35 DigSetter::Style DrawingArea::displayStyle[4] = {
36 DigSetter::Style(FontSpec::help, ColorSpec::helpText),
37 DigSetter::Style(FontSpec::help, ColorSpec::helpLink),
38 DigSetter::Style(FontSpec::help, ColorSpec::helpUsedLink),
39 DigSetter::Style(FontSpec::helpTitle, ColorSpec::helpText)
40 };
41
42 static int leading(IFont &f) {
43 return f.maxSize().height() + f.externalLeading();
44 }
45
46 HelpWord::HelpWord(char *text, int nChar, cint where, Style index)
47 : DigSetter::Dig(text, nChar, where, index)
48 , linktopic("")
49 , indent(0)
50 , bullet(0)
51 , forceBreak(0)
52 , noBreak(0)
53 , linkTraversed(0)
54 , highlight(0)
55 {
56 //LOGSECTION("HelpWord::HelpWord");
57 //LOGV(AgString(text, nChar));
58 //LOGV(nChar);
59 }
60
61 HelpWord::HelpWord(const HelpWord &word)
62 : DigSetter::Dig(word)
63 , linktopic(word.linktopic)
64 , indent(word.indent)
65 , bullet(word.bullet)
66 , forceBreak(word.forceBreak)
67 , noBreak(word.noBreak)
68 , linkTraversed(word.linkTraversed)
69 , highlight(word.highlight)
70 {
71 //LOGSECTION("HelpWord::HelpWord(const HelpWord &)");
72 }
73
74 IRectangle HelpWord::rect(DigSetter::Style *style) {
75 IFont &font = style[styleIndex].font;
76 int above = font.maxAscender();
77 int below = font.maxDescender();
78 IPoint location(where.x, where.y - above);
79 ISize size(width, above+below);
80 return IRectangle(location,size);
81 }
82
83 DrawingArea::DrawingArea(IWindow *owner_, AgString topic_)
84 : IStaticText(nextChildId(), owner_, owner_)
85 , windowFont(FontSpec::help)
86 , margin(windowFont.charWidth('M'))
87 , paraLeading((windowFont.maxSize().height() +
88 windowFont.externalLeading())/2)
89 , topic(topic_)
90 , setter(this, displayStyle)
91 , painting(0)
92 , popUpMenu(this, nextChildId())
93 , refreshAction(*this, refreshAllLinks)
94 , frameWindow(0)
95 , highlightText(0)
96 , nHighlight(0)
97 , fingerCursorSet(0)
98 , fingerCursor(IResourceLibrary().loadPointer(IDI_FINGER_CUR))
99 , helpShowing(0)
100 , rightButtonState(buttonIdle)
101 {
102 LOGSECTION("DrawingArea::DrawingArea");
103 IWindow *f;
104 for (f = parent(); !f->isFrameWindow(); f=f->parent()) {
105 /* nothing */
106 }
107 frameWindow = (AgFrame *) f;
108 //LOGV(int(f));
109
110 int i;
111 for (i = 0; i < 256; i++) {
112 charWidth[i] = windowFont.charWidth(i);
113 }
114 activeViews.insert(this);
115 LOGV((int) this);
116 #ifdef INCLUDE_LOGGING
117 for (i = 0; i < activeViews.size(); i++) {
118 //LOGV((int) activeViews.sortedItem(i));
119 //LOGV(activeViews.sortedItem(i)->topic);
120 LOGV((int) activeViews[i]) LCV(activeViews[i]->topic);
121 }
122 #endif
123 //paraIndent = windowFont.charWidth('M') + margin;
124 paraIndent = margin;
125 helpText = getHelpText(topic.pointer());
126 links = findLinks(helpText);
127 LOGV(links.size()) LCV(activeViews.size()) LCV(topic);
128 sortedLinks = sortLinks(links);
129 setMinimumSize(calcMinimumSize());
130 LOGV(minimumSize().asString());
131 int minWidth = minimumSize().width() + 2*margin;;
132 int maxWidth = IWindow::desktopWindow()->size().width();
133 measure = 56*avgCharWidth() + 2*margin;
134 if (measure < minWidth) {
135 measure = minWidth;
136 }
137 if (measure > maxWidth) {
138 measure = maxWidth;
139 }
140 LOGV(measure);
141 measureText();
142 preferredSize = ISize(longestLine, nLines);
143
144 initPopUp();
145
146 ICommandHandler ::handleEventsFor(this);
147 IMenuHandler ::handleEventsFor(this);
148 //IMenuHandler ::handleEventsFor(frameWindow);
149 IMouseHandler ::handleEventsFor(this);
150 IPaintHandler ::handleEventsFor(this);
151 //AgHelpHandler ::handleEventsFor(&popUpMenu);
152 AgHelpHandler ::handleEventsFor(frameWindow);
153 //LOGS("Help handler attached");
154 }
155
156
157 DrawingArea::~DrawingArea() {
158 LOGSECTION("DrawingArea::~DrawingArea");
159 LOGV((int) this);
160 #ifdef INCLUDE_LOGGING
161 for (int i = 0; i < activeViews.size(); i++) {
162 //LOGV((int) activeViews.sortedItem(i));
163 //LOGV(activeViews.sortedItem(i)->topic);
164 LOGV((int) activeViews[i]) LCV(activeViews[i]->topic);
165 }
166 #endif
167 int flag = activeViews.remove(this);
168 assert(flag);
169 #ifdef INCLUDE_LOGGING
170 for (i = 0; i < activeViews.size(); i++) {
171 //LOGV((int) activeViews.sortedItem(i));
172 //LOGV(activeViews.sortedItem(i)->topic);
173 LOGV((int)activeViews[i]) LCV(activeViews[i]->topic);
174 }
175 #endif
176 LOGV(topic) LCV(activeViews.size());
177 ICommandHandler ::stopHandlingEventsFor(this);
178 IMenuHandler ::stopHandlingEventsFor(this);
179 //IMenuHandler ::stopHandlingEventsFor(frameWindow);
180 IMouseHandler ::stopHandlingEventsFor(this);
181 IPaintHandler ::stopHandlingEventsFor(this);
182 //AgHelpHandler ::stopHandlingEventsFor(&popUpMenu);
183 AgHelpHandler ::stopHandlingEventsFor(frameWindow);
184 }
185
186 void DrawingArea::reset() {
187 LOGSECTION("DrawingArea::reset");
188 disableUpdate();
189 windowFont = FontSpec::help;
190 setFont(FontSpec::help);
191 for (int i = 0; i < 256; i++) {
192 charWidth[i] = windowFont.charWidth(i);
193 }
194 margin = windowFont.charWidth('M');
195 //paraIndent = windowFont.charWidth('M') + margin;
196 paraIndent = margin;
197 paraLeading = (windowFont.maxSize().height() +
198 windowFont.externalLeading())/2;
199 LOGV(nWords);
200 LOGV(word.size());
201 int minWidth = minimumSize().width() + 2*margin;;
202 int maxWidth = IWindow::desktopWindow()->size().width();
203 measure = 56*avgCharWidth() + 2*margin;
204 if (measure < minWidth) {
205 measure = minWidth;
206 }
207 if (measure > maxWidth) {
208 measure = maxWidth;
209 }
210 /*
211 for (int j = 0; j < nWords; j++) {
212 setter.measureDig(word[j]);
213 }
214 remeasureText();
215 */
216 helpText = getHelpText(topic.pointer());
217 measureText();
218 //remeasureText();
219 enableUpdate();
220 show();
221 }
222
223 DrawingArea &DrawingArea::copyTo(IClipboard &clipboard) {
224 LOGSECTION("DrawingArea::copyTo");
225 int i;
226 AgCharStack charStack;
227 LOGV(nWords);
228 for (i = 0; i < nWords; i++) {
229 if (word[i].indent) {
230 charStack.push(" ");
231 }
232 charStack.push(word[i].text, word[i].textLength);
233 if (word[i].forceBreak) {
234 charStack.push("\r\n\r\n ");
235 }
236 else {
237 charStack.push(' ');
238 }
239 }
240 clipboard.setText(charStack.popString().pointer());
241 return *this;
242 }
243
244 void AgHelpView::onFontChange() {
245 LOGSECTION("AgHelpView::onFontChange");
246 disableUpdate();
247 dataArea.reset();
248 doLayout();
249 enableUpdate();
250 show();
251 }
252
253 void DrawingArea::refreshAllLinks() {
254 LOGSECTION("DrawingArea::refreshAllLinks");
255 int i;
256 LOGV(topic);
257 LOGV(activeViews.size());
258 for (i = 0; i < activeViews.size(); i++) {
259 //LOGV(activeViews.sortedItem(i)->topic);
260 LOGV(activeViews[i]->topic);
261 //activeViews.sortedItem(i)->refreshLinks();
262 activeViews[i]->refreshLinks();
263 }
264 }
265
266 DrawingArea &DrawingArea::refreshLinks() {
267 LOGSECTION("DrawingArea::refreshLinks");
268 AgStack<AgString> tLinks;
269 int n = links.size();
270 LOGV(topic);
271 LOGV(links.size());
272 int i;
273 for (i = 0; i < n; i++) if (traversedLinks.includes(links[i])) {
274 tLinks.push(links[i]);
275 }
276 n = tLinks.size();
277 LOGV(tLinks.size());
278 if (n == 0) {
279 return *this;
280 }
281 for (i = 0; i < n; i++) {
282 int j;
283 AgString link = tLinks[i];
284 int nLinkedWords = linkedWords.size();
285 for (j = 0; j < nLinkedWords; j++) {
286 int k = linkedWords[j];
287 if (word[k].linktopic != link) {
288 continue;
289 }
290 if (word[k].linkTraversed) {
291 continue;
292 }
293 word[k].linkTraversed = 1;
294 word[k].styleIndex = HelpWord::usedStyle;
295 setter.refresh(word[k]);
296 }
297 }
298 return *this;
299 }
300
301 Boolean DrawingArea::paintWindow(IPaintEvent &event) {
302 //LOGSECTION("DrawingArea::paintWindow");
303 if (painting) {
304 return true;
305 }
306 painting++;
307 IPresSpaceHandle handle = event.presSpaceHandle(); //cookie
308 SetBkMode(handle, TRANSPARENT);
309 IRectangle invalidRect(
310 ICoordinateSystem::isConversionNeeded()
311 ? ICoordinateSystem::convertToApplication(event.rect(),size())
312 : event.rect()
313 );
314 //LOGV(invalidRect.asString());
315 IColor bgColor(ColorSpec::helpText.bg());
316 setter.setEvent(event);
317 setter.clear(invalidRect);
318 int top = invalidRect.minY();
319 int bot = invalidRect.maxY();
320 IFont &font = FontSpec::help;
321 int descender = font.maxDescender();
322 int ascender = font.maxAscender();
323 //LOGV(ascender) LCV(descender);
324 int first = 0;
325 int last = nWords - 1;
326 //LOGV(top) LCV(bot);
327 while (first < last) {
328 int middle = (first + last) /2;
329 //LOGV(first) LCV(middle) LCV(last);
330 //LOGV(word[middle].where.y + descender);
331 if (setter.bottom(word[middle]) < top) {
332 first = middle + 1;
333 }
334 else {
335 last = middle - 1;
336 }
337 }
338 //LOGS("found first dig");
339 char *endHighlight = highlightText + nHighlight;
340
341 while (first < nWords && setter.top(word[first]) <= bot) {
342 HelpWord &aWord = word[first];
343 if (endHighlight && aWord.text < endHighlight
344 && aWord.text + aWord.textLength >= highlightText)
345 {
346 HelpWord highlightWord = aWord;
347 int n = highlightText - highlightWord.text;
348 if (n > 0) {
349 DigSetter::Dig dig = highlightWord;
350 dig.textLength = n;
351 setter.measureDig(dig);
352 setter.setDig(dig);
353 highlightWord.where.x += dig.width;
354 highlightWord.text += n;
355 highlightWord.textLength -= n;
356 }
357 n = highlightWord.text + highlightWord.textLength -
358 (highlightText + nHighlight);
359 if (n > 0) {
360 highlightWord.textLength -= n;
361 }
362 setter.measureDig(highlightWord);
363 setter.setDig(highlightWord, 1);
364 if (n > 0) {
365 DigSetter::Dig dig = highlightWord;
366 dig.textLength = n;
367 dig.text += highlightWord.textLength;
368 setter.measureDig(highlightWord);
369 dig.where.x += highlightWord.width;
370 setter.setDig(dig);
371 }
372 //LOGV(aWord.where) LCV(aWord.width) LCV(aWord.forceBreak);
373 if (first+1 < word.size()) {
374 HelpWord &bWord = word[first+1];
375 //LOGV(bWord.where) LCV(bWord.width) LCV(bWord.forceBreak);
376 if (aWord.where.y == bWord.where.y &&
377 aWord.text + aWord.textLength < endHighlight) {
378 //LOGSECTION("BridgeHighlights");
379 cint where = aWord.where;
380 where.x += aWord.width;
381 where.y -= ascender;
382 int bWidth = bWord.where.x - where.x;
383 int aWidth = bWidth/2;
384 bWidth -= aWidth;
385 int height = ascender+descender;
386 //LOGV(aWidth) LCV(bWidth) LCV(height);
387 //LOGV(where);
388 DigSetter::Hole firstHalf(where, cint(aWidth, height),
389 aWord.styleIndex);
390 where.x += aWidth;
391 //LOGV(where);
392 DigSetter::Hole secondHalf(where, cint(bWidth, height),
393 bWord.styleIndex);
394 setter.reverse(firstHalf);
395 setter.reverse(secondHalf);
396 }
397 }
398 }
399 else {
400 setter.setDig(aWord);
401 }
402 first++;
403 //LOGV(first);
404 }
405 //LOGS("loop done");
406 setter.closeEvent();
407 painting = 0;
408 return true;
409 }
410
411 DrawingArea &DrawingArea::refreshWords(char *p, int n) {
412 LOGSECTION("DrawingArea::refreshWords");
413 LOGV((int) p) LCV(n);
414 if (p == 0 || n == 0) {
415 return *this;
416 }
417 int first = 1;
418 int middle;
419 int last = nWords - 1;
420 while (first < last) {
421 middle = (first + last) /2;
422 if (p < word[middle].text) {
423 last = middle - 1;
424 }
425 else if (p < word[middle].text + word[middle].textLength) {
426 first = middle;
427 break;
428 }
429 else {
430 first = middle + 1;
431 }
432 }
433 char *end = p + nHighlight;
434 last = first;
435 DigSetter::Hole hole = setter.makeHole(word[first]);
436 while (end > word[last].text + word[last].textLength) {
437 last++;
438 if (word[last].where.y != word[first].where.y) {
439 setter.refresh(hole);
440 hole = setter.makeHole(word[last]);
441 LOGV(hole.where) LCV(hole.size);
442 }
443 else {
444 hole.size.x = word[last].width + word[last].where.x - hole.where.x;
445 }
446 }
447 setter.refresh(hole);
448 LOGV(hole.where) LCV(hole.size);
449 LOGV(first) LCV(middle) LCV(last);
450 LOGV((int) word[first].text) LCV(word[first].textLength);
451 LOGV((int) word[middle].text) LCV(word[middle].textLength);
452 LOGV((int) word[last].text) LCV(word[last].textLength);
453 return *this;
454 }
455
456 AgHelpView &AgHelpView::positionWords(char *p, int n) {
457 LOGSECTION("AgHelpView::positionWords");
458 if (p == 0 || n == 0) {
459 return *this;
460 }
461 int first = 1;
462 int middle;
463 int last = dataArea.nWords - 1;
464 while (first < last) {
465 middle = (first + last) /2;
466 if (p < dataArea.word[middle].text) {
467 last = middle - 1;
468 }
469 else if (p <
470 dataArea.word[middle].text + dataArea.word[middle].textLength) {
471 first = middle;
472 break;
473 }
474 else {
475 first = middle + 1;
476 }
477 }
478 char *end = p + dataArea.nHighlight;
479 last = first;
480 while (end > dataArea.word[last].text + dataArea.word[last].textLength) {
481 last++;
482 }
483 LOGV(first) LCV(middle) LCV(last);
484 HelpWord &firstWord = dataArea.word[first];
485 IFLOG(HelpWord &middleWord = ) dataArea.word[middle];
486 HelpWord &lastWord = dataArea.word[last];
487 LOGV((int) firstWord.text) LCV(firstWord.textLength);
488 LOGV((int) middleWord.text) LCV(middleWord.textLength);
489 LOGV((int) lastWord.text) LCV(lastWord.textLength);
490 IFont &font = dataArea.displayStyle[firstWord.styleIndex].font;
491 int descender = font.maxDescender();
492 int ascender = font.maxAscender();
493 int bottom = verticalScrollBar.scrollBoxPosition() + size().height();
494 int top = verticalScrollBar.scrollBoxPosition();
495 LOGV(top) LCV(bottom);
496 LOGV(firstWord.where.y - ascender);
497 LOGV(lastWord.where.y + descender);
498 if (lastWord.where.y + descender > bottom) {
499 int yPos = lastWord.where.y + descender - size().height();
500 verticalScrollBar.moveScrollBoxTo(yPos);
501 repositionWindow();
502 }
503 else if (firstWord.where.y - ascender < top) {
504 int yPos = firstWord.where.y - ascender;
505 verticalScrollBar.moveScrollBoxTo(yPos);
506 repositionWindow();
507 }
508 return *this;
509 }
510
511 Boolean AgHelpView::findNext(AgString s) {
512 LOGSECTION("AgHelpView::findNext");
513 LOGV(s);
514 searchProcess.setKey(s);
515
516 char *start = dataArea.highlightText;
517 if (start == 0) {
518 start = dataArea.helpText.pointer();
519 }
520 else if (s.size() == dataArea.nHighlight
521 && strnicmp(s.pointer(), dataArea.highlightText, s.size()) == 0) {
522 start++;
523 }
524
525 int length = dataArea.myTextLength - (start - dataArea.helpText.pointer());
526 LOGV((int) start) LCV(length);
527 if (length <= 0) {
528 return 0;
529 }
530 char *p = searchProcess.scanForward(start, length);
531 if (p == 0) {
532 return 0;
533 }
534 char *oldHighlight = dataArea.highlightText;
535 int oldCount = dataArea.nHighlight;
536 dataArea.highlightText = p;
537 dataArea.nHighlight = s.size();
538 dataArea.refreshWords(oldHighlight, oldCount);
539 dataArea.refreshWords(dataArea.highlightText, dataArea.nHighlight);
540 positionWords(dataArea.highlightText, dataArea.nHighlight);
541 return 1;
542 }
543
544 Boolean AgHelpView::findPrev(AgString s) {
545 LOGSECTION("AgHelpView::findPrev");
546 LOGV(s);
547 searchProcess.setKey(s);
548
549 char *start = dataArea.helpText.pointer();
550 int length = dataArea.helpText.size();
551 if (dataArea.highlightText) {
552 length = dataArea.highlightText + dataArea.nHighlight - start;
553 }
554
555 if (dataArea.nHighlight) {
556 if (s.size() == dataArea.nHighlight &&
557 !strnicmp(s.pointer(), dataArea.highlightText, s.size())) {
558 length--;
559 }
560 }
561 char *p = searchProcess.scanReverse(start, length);
562 if (p == 0) {
563 return 0;
564 }
565 char *oldHighlight = dataArea.highlightText;
566 int oldCount = dataArea.nHighlight;
567 dataArea.highlightText = p;
568 dataArea.nHighlight = s.size();
569 dataArea.refreshWords(oldHighlight, oldCount);
570 dataArea.refreshWords(dataArea.highlightText, dataArea.nHighlight);
571 positionWords(dataArea.highlightText, dataArea.nHighlight);
572 return 1;
573 }
574
575 Boolean DrawingArea::mouseMoved(IMouseEvent &event) {
576 //LOGSECTION("DrawingArea::mouseMoved");
577 IPoint pWhere(event.mousePosition());
578 cint where(pWhere.x(), pWhere.y());
579 //LOGV(where);
580 //IFont &font = FontSpec::help;
581 int first = 0;
582 int last = nWords - 1;
583 while (first < last) {
584 int middle = (first + last) /2;
585 if (setter.bottom(word[middle]) < where.y) {
586 first = middle + 1;
587 }
588 else if (setter.top(word[middle]) > where.y) {
589 last = middle - 1;
590 }
591 else if (word[middle].where.x + word[middle].width < where.x) {
592 first = middle + 1;
593 }
594 else if (word[middle].where.x > where.x) {
595 last = middle - 1;
596 }
597 else {
598 first = middle;
599 break;
600 }
601 }
602 //LOGV(first);
603 int haslink = 0;
604 if (first < nWords && word[first].linktopic != "") {
605 haslink = 1;
606 }
607 if (where.x < word[first].where.x
608 && (first <= 0
609 || setter.bottom(word[first-1]) < where.y)) {
610 haslink = 0;
611 }
612 if (where.x > word[first].where.x + word[first].width
613 && (first+1 >= nWords
614 || setter.top(word[first+1]) > where.y)) {
615 haslink = 0;
616 }
617 //LOGV(haslink);
618 //IMousePointerEvent pointerEvent(event);
619 if (haslink && !fingerCursorSet) {
620 //LOGS("Set fingerCursor");
621 frameWindow->setMousePointer(fingerCursor);
622 //pointerEvent.setMousePointer(fingerCursor);
623 //SetCursor(fingerCursor);
624 fingerCursorSet = true;
625 }
626 else if (!haslink && fingerCursorSet) {
627 //LOGS("Reset activeCursor");
628 frameWindow->setMousePointer(ControlPanel::activeCursor);
629 //pointerEvent.setMousePointer(ControlPanel::activeCursor);
630 //SetCursor(ControlPanel::activeCursor);
631 fingerCursorSet = false;
632 }
633 return true;
634 }
635
636 Boolean DrawingArea::mouseClicked(IMouseClickEvent &event) {
637 LOGSECTION("DrawingArea::mouseClicked");
638 if (event.mouseButton() == IMouseClickEvent::button2) {
639 if (event.mouseAction() == IMouseClickEvent::down) {
640 rightButtonState = buttonDown;
641 }
642 else if (event.mouseAction() == IMouseClickEvent::up) {
643 rightButtonState = waitingForClick;
644 }
645 else if (event.mouseAction() == IMouseClickEvent::click) {
646 rightButtonState = buttonIdle;
647 }
648 return false;
649 }
650 if (event.mouseButton() != IMouseClickEvent::button1) {
651 return false;
652 }
653 if (ControlPanel::helpCursorSet) {
654 if (event.mouseAction() == IMouseClickEvent::down) {
655 ControlPanel::helpCursorSet = 0;
656 ControlPanel::resetCursor();
657 }
658 }
659 switch (event.mouseAction()) {
660 case IMouseClickEvent::doubleClick:
661 case IMouseClickEvent::click: {
662 if (event.windowUnderPointer() != handle()) {
663 return false;
664 }
665 IPoint pWhere(event.mousePosition());
666 cint where(pWhere.x(), pWhere.y());
667 //LOGV(where);
668 //IFont &font = FontSpec::help;
669 int first = 0;
670 int last = nWords - 1;
671 while (first < last) {
672 int middle = (first + last) /2;
673 //LOGV(first) LCV(middle) LCV(last);
674 //LOGV(word[middle].where.x) LCV(word[middle].where.y);
675 //LOGV(setter.top(word[middle])) LCV(setter.bottom(word[middle]));
676 if (setter.bottom(word[middle]) < where.y) {
677 first = middle + 1;
678 }
679 else if (setter.top(word[middle]) > where.y) {
680 last = middle - 1;
681 }
682 else if (word[middle].where.x + word[middle].width < where.x) {
683 first = middle + 1;
684 }
685 else if (word[middle].where.x > where.x) {
686 last = middle - 1;
687 }
688 else {
689 first = middle;
690 break;
691 }
692 }
693 AgString linktopic = "";
694 //LOGV(first);
695 for (;first < nWords; first++) {
696 if (setter.top(word[first]) > where.y) {
697 return false;
698 }
699 //LOGV(word[first].where) LCV(word[first].link);
700 int xr = word[first].where.x + word[first].width;
701 //LOGV(word[first].width) LCV(xr);
702 if (xr < where.x) {
703 linktopic = word[first].linktopic;
704 continue;
705 }
706 int xl = word[first].where.x;
707 //LOGS("contact") LV(xr) LCV(xl) LCV(link) LCV(word[first].link);
708 if ( where.x < xl && linktopic != word[first].linktopic) {
709 return false;
710 }
711 if (word[first].linktopic == "") {
712 return false;
713 }
714 break;
715 }
716 if (first >= nWords) {
717 return false;
718 }
719 //LOGV(first) LCV(word[first].linktopic);
720
721 word[first].linkTraversed = 1;
722 linktopic = word[first].linktopic;
723 traversedLinks.insert(linktopic);
724
725 int i;
726
727 int iMin = first, iMax = first;
728 for (i = first; i >= 0 && word[i].linktopic == linktopic; i--) {
729 word[i].styleIndex = HelpWord::usedStyle;
730 iMin = i;
731 }
732 for (i = first + 1 ; i < nWords && word[i].linktopic == linktopic; i++) {
733 iMax = i;
734 word[i].styleIndex = HelpWord::usedStyle;
735 }
736 HelpWord &wmn = word[iMin];
737 HelpWord &wmx = word[iMax];
738
739 int minX = min(wmn.where.x,wmx.where.x);
740 int maxX = max(wmn.where.x + wmn.width, wmx.where.x + wmx.width);
741 int minY = min(setter.top(wmn), setter.top(wmx));
742 int maxY = max(setter.bottom(wmn), setter.bottom(wmx));
743
744 IRectangle r(IPoint(minX, minY), IPoint(maxX, maxY));
745 //LOGV(r.asString());
746 refresh(r);
747
748 const char *newTopic = word[first].linktopic.pointer();
749 //LOGV(newTopic);
750 AgString title = AgString::format("Help - %s", newTopic);
751 IFrameWindow *helpWindow = AgFrame::windowRegistry.find(title);
752 if (helpWindow == 0) {
753 helpWindow = new AgHelpWindow(newTopic);
754 helpWindow->setAutoDeleteObject();
755 }
756 helpWindow->show().setFocus();
757
758 refreshAction.performDeferred();
759 return true;
760 }
761 }
762 return false;
763 }
764
765 ISize DrawingArea::calcMinimumSize() const {
766 LOGSECTION("DrawingArea::calcMinimumSize");
767 unsigned char *p = (unsigned char *) helpText.pointer();
768 IFont &font = FontSpec::help;
769 int enWidth = font.avgCharWidth();
770 int minWidth = font.minTextWidth((char *)p);
771 LOGV(minWidth);
772 LOGV(margin);
773 LOGV(enWidth);
774
775 IFont &titleFont = FontSpec::helpTitle;
776 int width = titleFont.textWidth(topic.pointer());
777 if (width > minWidth) {
778 minWidth = width;
779 }
780 while (*p) {
781 if (*p == ' ') {
782 unsigned char *q = p;
783 int k = 0;
784 while (*q == ' ') {
785 k++;
786 q++;
787 }
788 p = q;
789 int width = 0;
790 while (*q && *q != '\n') {
791 width += charWidth[*q++];
792 }
793 width += k*enWidth;
794 if (width > minWidth) {
795 minWidth = width;
796 }
797 p = ++q;
798 continue;
799 }
800 while (*p && *p != '\n') {
801 p++;
802 }
803 if (*p) {
804 p++;
805 }
806 }
807 minWidth += 2*margin;
808 int height = windowFont.maxCharHeight() + windowFont.externalLeading();
809 return ISize(minWidth, 5*height);
810 }
811
812 #define BULLET 7
813
814 DrawingArea &DrawingArea::measureText() {
815 LOGSECTION("DrawingArea::measureText");
816 IFont &textFont = FontSpec::help;
817 IFont &titleFont = FontSpec::helpTitle;
818 AgString auxText(helpText.pointer());
819
820
821 AgStack<int> linkedWordStack;
822
823 unsigned char *p = (unsigned char *) helpText.pointer();
824 unsigned char *aux = (unsigned char *) auxText.pointer();
825 *aux = 0;
826
827 int lineHeight = textFont.maxCharHeight() + textFont.externalLeading();
828
829 int spaceWidth = charWidth[' '];
830 int whereX = paraIndent;
831 int whereY = leading(titleFont);
832 nWords = 0;
833 word.discardData();
834
835 //LOGFont(windowFont);
836 //LOGV(margin);
837 //LOGV(paraIndent);
838
839 int titleWidth = titleFont.textWidth(topic.pointer());
840 //LOGV((char *) titleFont.name()) LCV(titleFont.pointSize());
841 //LOGV(titleWidth);
842
843 whereX = (measure - titleWidth)/2;
844 LOGV(whereX) LCV(whereY);
845
846 word.push(HelpWord(topic.pointer(), strlen(topic.pointer()),
847 cint(whereX, whereY), HelpWord::titleStyle));
848 word[0].width = titleWidth;
849 nWords = 1;
850 whereY += titleFont.maxDescender();
851 whereY += paraLeading;
852 word[0].forceBreak = 1;
853
854 whereX = paraIndent;
855
856 int indentedLine = 0;
857 int tabbedLine = 0;
858
859 int linkIndex = 0;
860
861 int highlightBegin = 0;
862 int highlightEnd = 0;
863 longestLine = 0;
864 int enWidth = avgCharWidth();
865 int emWidth = font().charWidth('M');
866 LOGV(enWidth) LCV(emWidth);
867
868 LOGV(whereY);
869 //int indentLevel = 0;
870 int forceBreak = 0;
871 int bulletedLine = 0;
872 while (*p) {
873 if (*p == ' ') {
874 int k = 0;
875 while (*p == ' ') {
876 p++;
877 k++;
878 }
879 whereX = k*enWidth + margin;
880 word[nWords-1].forceBreak = 1;
881 indentedLine = 1;
882 //indentLevel = k;
883 }
884 else if (*p == '\t') {
885 p++;
886 whereX = 2*emWidth + margin;
887 word[nWords-1].forceBreak = 1;
888 tabbedLine = 1;
889 LOGS("tabbed Line");
890 }
891 else if (*p == BULLET) {
892 whereX = emWidth + margin;
893 if (nWords > 0) {
894 word[nWords-1].forceBreak = 1;
895 }
896 bulletedLine = 1;
897 LOGS("bulleted Line") LCV(whereX);
898 }
899 int wordLength = 0;
900 unsigned char *beginWord = aux;
901 unsigned char *q = beginWord;
902 int dotFlag = 0;
903 while (*p && *p != ' ' && *p != '\t' && *p != '\n') {
904 if (*p == (unsigned char) 169) {
905 //LOGV(nWords) LCV(whereX) LCV(whereY);
906 highlightBegin++;
907 p++;
908 continue;
909 }
910 if (*p == (unsigned char) 170) {
911 //LOGV(nWords) LCV(whereX) LCV(whereY);
912 highlightEnd++;
913 p++;
914 continue;
915 }
916 //*q++ = *p++;
917
918 *q++ = *p;
919 if (*p == 0 || *p++ != '.') {
920 continue;
921 }
922 if (strchr(" \r\n\t", *p) == 0) {
923 dotFlag = 1;
924 }
925 break;
926 }
927
928 int nChars = q-beginWord;
929 *q = 0;
930 wordLength = windowFont.textWidth((char *) beginWord);
931 LOGV(nWords) LCV(wordLength) LCV(nChars) LCV(beginWord);
932 *q++ = ' ';
933 aux = q;
934 while (*p == ' ' || (*p == '\t' && !tabbedLine)) {
935 p++;
936 }
937 //forceBreak = (indentedLine || tabbedLine) && *p == '\n';
938 //forceBreak = (bulletedLine || tabbedLine) && *p == '\n';
939 forceBreak = tabbedLine && *p == '\n';
940 int endLine = *p == '\n';
941 if (endLine) {
942 p++;
943 }
944 if (*p == '\n') { // More than one newline?
945 forceBreak = 1; // Yes
946 while (*p == '\n') {
947 p++;
948 }
949 }
950 //LOGV(forceBreak) LCV(indentLevel) LCV(indentedLine) LCV(dotFlag);
951 LOGV(whereX);
952 if (!dotFlag &&
953 !tabbedLine &&
954 !indentedLine &&
955 whereX + wordLength > measure - margin) {
956 if (whereX > longestLine) longestLine = whereX;
957 LOGV(longestLine);
958 whereX = margin;
959 whereY += lineHeight;
960 }
961 AgString temp((char *) beginWord, 10);
962 LOGV(word.size()) LCV(temp.pointer()) LCV(nChars) LCV(whereX) LCV(whereY);
963 HelpWord thisWord((char *) beginWord, nChars, cint(whereX, whereY));
964 if (*beginWord == BULLET) {
965 //whereX = margin + 2*emWidth;
966 *beginWord = 183;
967 LOGS("replaced bullet") LCV(whereX) LCV(emWidth);
968 }
969 else {
970 whereX += wordLength;
971 if (!dotFlag) {
972 whereX += spaceWidth;
973 }
974 LOGV(whereX);
975 if (tabbedLine && !endLine && *p == '\t') {
976 p++;
977 int newX = margin;
978 while (newX < whereX) {
979 newX += 2*emWidth;
980 }
981 LOGV(newX);
982 whereX = newX;
983 while (*p == '\t') {
984 whereX += 2*emWidth;
985 p++;
986 }
987 }
988 }
989 LOGV(whereX);
990 thisWord.indent = indentedLine || tabbedLine;
991 thisWord.bullet = !thisWord.indent && bulletedLine != 0;
992 thisWord.forceBreak = forceBreak;
993 thisWord.noBreak = dotFlag;
994 thisWord.width = wordLength;
995 LOGV(thisWord.bullet) LCV(thisWord.forceBreak) LCV(thisWord.indent);
996 if (highlightBegin) {
997 //LOGS("set up links");
998 //LOGV(linkIndex);
999 LOGV(links.size()) LCV(topic);
1000 thisWord.linktopic = links[linkIndex];
1001 //LOGV(thisWord.link);
1002 if (traversedLinks.includes(thisWord.linktopic)) {
1003 thisWord.linkTraversed = 1;
1004 }
1005 //LOGV(dict_str(help_dict, thisWord.link));
1006 //LOGV(thisWord.where);
1007 if (thisWord.linkTraversed) {
1008 thisWord.styleIndex = HelpWord::usedStyle;
1009 }
1010 else {
1011 thisWord.styleIndex = HelpWord::linkStyle;
1012 }
1013 }
1014 if (highlightEnd) {
1015 highlightBegin = highlightEnd = 0;
1016 linkIndex++;
1017 }
1018 if (forceBreak) {
1019 LOGS("forced break");
1020 if ((tabbedLine || indentedLine ) && whereX > longestLine) {
1021 longestLine = whereX;
1022 LOGV(longestLine);
1023 }
1024 whereX = indentedLine || tabbedLine ? margin : paraIndent;
1025 if ((tabbedLine || bulletedLine) && *p == ' ') {
1026 whereX = paraIndent;
1027 while (*p == ' ') p++;
1028 }
1029 //whereX = paraIndent;
1030 whereY += lineHeight;
1031 if (!indentedLine && !tabbedLine) {
1032 whereY += paraLeading;
1033 }
1034 }
1035 word.push(thisWord);
1036 linkedWordStack.push(nWords);
1037 nWords++;
1038 if (forceBreak) {
1039 indentedLine = tabbedLine = bulletedLine = 0;
1040 }
1041 //LOGV(nWords);
1042 }
1043 whereY += 2*lineHeight;
1044 longestLine += margin;
1045 LOGV(longestLine);
1046 nLines = whereY/lineHeight;
1047 //LOGV(nLines);
1048 //LOGV(nWords);
1049 spaceRequired = measure * whereY;
1050 linkedWords = AgArray<int>(linkedWordStack);
1051 *aux = 0;
1052 helpText = auxText;
1053 myTextLength = helpText.size();
1054 return *this;
1055 }
1056
1057 DrawingArea &DrawingArea::remeasureText() {
1058 LOGSECTION("DrawingArea::remeasureText");
1059 //LOGV(measure);
1060 IFont &textFont = FontSpec::help;
1061 IFont &titleFont = FontSpec::helpTitle;
1062 int lineHeight = textFont.maxCharHeight() + textFont.externalLeading();
1063 //int emWidth = textFont.maxSize().width();
1064 int emWidth = textFont.charWidth('M');
1065
1066 LOGV(emWidth);
1067 //int paraIndent = margin + emWidth;
1068 int paraIndent = margin;
1069
1070 int spaceWidth = charWidth[' '];
1071 //LOGV(spaceWidth);
1072 cint where(paraIndent, paraLeading);
1073 int forceBreak = 0;
1074 int k = 0;
1075 HelpWord &title = word[0];
1076 int titleWidth = titleFont.textWidth(topic.pointer());
1077 //LOGV((char *) titleFont.name()) LCV(titleFont.pointSize());
1078 //LOGV(titleWidth);
1079 word[0].width = titleWidth;
1080 title.where.x = (measure - titleWidth)/2;
1081 title.where.y = leading(titleFont);
1082 {
1083 //IFont &f = displayStyle[title.styleIndex].font;
1084 //LOGV(f.name()) LCV(f.pointSize());
1085 }
1086 where.y += 3*leading(titleFont)/2;
1087 where.y += titleFont.maxDescender();
1088 word[1].where.y = where.y;
1089 int indentedLine = 0;
1090 LOGV(paraLeading) LCV(lineHeight);
1091 for (k = 1; k < nWords; k++) {
1092 HelpWord &textWord = word[k];
1093 int width = word[k].width;
1094 //int i = k;
1095 //while (i < nWords-1 && word[i++].noBreak) {
1096 // width += word[i].width;
1097 //}
1098 char buf[100];
1099 strncpy(buf, textWord.text, textWord.textLength);
1100 buf[textWord.textLength] = 0;
1101 LOGV(k) LCV(textWord.indent) LCV(textWord.where)
1102 LCV(*textWord.text) LCV(buf);
1103 LOGV(textWord.bullet);
1104 int rightIndent = textWord.bullet ? 2*emWidth : 0;
1105 if (forceBreak) {
1106 where.y += lineHeight;
1107 where.x = textWord.where.x;
1108 if (textWord.bullet) {
1109 where.x = margin + 2*emWidth;
1110 }
1111 //if (!textWord.indent && where.x > margin) {
1112 // where.y += paraLeading;
1113 //}
1114 if (!textWord.indent) {
1115 where.y += paraLeading;
1116 }
1117 indentedLine = textWord.indent;
1118 }
1119 else if (!indentedLine &&
1120 where.x + width > measure - margin - rightIndent) {
1121 where.x = margin;
1122 where.y += lineHeight;
1123 if (textWord.bullet) {
1124 where.x += 2*emWidth;
1125 }
1126 }
1127 textWord.where.y = where.y;
1128
1129 //if (!forceBreak || !textWord.indent)
1130 if (!forceBreak && !textWord.indent) {
1131 textWord.where.x = where.x;
1132 }
1133
1134 LOGV(where) LCV(forceBreak) LCV(indentedLine);
1135 if (textWord.linktopic != "") {
1136 //LOGV(textWord.linktopic);
1137 //LOGV(k) LV(where);
1138 //LOGV(where.x + textWord.width);
1139 }
1140 if (*textWord.text == (char) 183) {
1141 //where.x += emWidth; //bullet
1142 }
1143 else {
1144 where.x += textWord.width + spaceWidth;
1145 }
1146 forceBreak = textWord.forceBreak;
1147 AgString temp((char *)textWord.text, 10);
1148 LOGV(k) LCV(temp) LCV(textWord.where);
1149 }
1150 where.y += 2*lineHeight;
1151
1152 nLines = where.y/lineHeight;
1153 //LOGV(nLines);
1154 return *this;
1155 }
1156
1157 AgString DrawingArea::getHelpText(const char *topic) {
1158 LOGSECTION("getHelpText");
1159 const HelpTopic *ht = help_topic(topic);
1160 LOGV((int)ht);
1161 AgString ret = helptopic_gettext(ht);
1162 LOGV(ret);
1163 return ret;
1164 }
1165
1166 AgArray<AgString> DrawingArea::findLinks(AgString msg) {
1167 char *t = msg.pointer();
1168 LOGSECTION("DrawingArea::findLinks");
1169 LOGV(topic);
1170 AgStack<AgString> stack;
1171 while(1) {
1172 t = strchr(t, 169);
1173 if (t != NULL && t[1] == '\'') {
1174 t = strchr(t+1, 169);
1175 }
1176 if (t == NULL) {
1177 break;
1178 }
1179 AgCharStack charStack;
1180 t++;
1181 while (1) {
1182 unsigned char c = *t++;
1183 switch (c) {
1184 //case '.':
1185 case '\n':
1186 case '\r':
1187 case ' ' :
1188 case '\t': {
1189 charStack.push(' ');
1190 while (1) {
1191 switch (*t) {
1192 case '\n':
1193 case '\r':
1194 case ' ' :
1195 case '\t': {
1196 t++;
1197 continue;
1198 }
1199 default: break;
1200 }
1201 break;
1202 }
1203 continue;
1204 }
1205 case 0:
1206 case 170:
1207 break;
1208 default:
1209 charStack.push(c);
1210 continue;
1211 }
1212 break;
1213 }
1214 if (charStack.size() == 0) {
1215 continue;
1216 }
1217 AgString word = charStack.popString();
1218 LOGV(word.pointer());
1219
1220 if (!help_topicexists(word.pointer())) {
1221 int n = word.size();
1222 int k = word[n-1];
1223 if (tolower(k) == 's') {
1224 word[n-1] = 0;
1225 }
1226 }
1227 if (!help_topicexists(word.pointer())) {
1228 LOGS("Dangling help link: ") LV(word);
1229 }
1230
1231 // Canonicalize the name of the topic.
1232 const HelpTopic *ht = help_topic(word.pointer());
1233 word = helptopic_gettitle(ht);
1234
1235 stack.push(word);
1236 LOGV(AgString(t, 10).pointer());
1237 }
1238 stack.push("Using Help");
1239 LOGV(stack.size());
1240 return AgArray<AgString>(stack);
1241 }
1242
1243
1244 static int OPTLINK linksortfunc(const void *av, const void *bv) {
1245 const AgString &as = *(const AgString *)av;
1246 const AgString &bs = *(const AgString *)bv;
1247
1248 int r = stricmp(as.pointer(), bs.pointer());
1249 if (r==0) {
1250 r = strcmp(as.pointer(), bs.pointer());
1251 }
1252 return r;
1253 }
1254
1255 AgArray<AgString> DrawingArea::sortLinks(AgArray<AgString> links) {
1256 LOGSECTION("DrawingArea::sortLinks");
1257
1258 // This used to sort and uniq by inserting into an AgBalancedTree.
1259 // The problem (apart from overelaboration) is that this requires a
1260 // custom wrapper class to insert the desired sort function, and
1261 // thus its own template instantiation. Which is a hassle, and
1262 // fairly silly...
1263
1264 int i, n = links.size();
1265 LOGV(n);
1266 AgArray<AgString> tmp(n);
1267 for (i=0; i<n; i++) {
1268 tmp[i] = links[i];
1269 }
1270 qsort(tmp.pointer(), tmp.size(), sizeof(AgString), linksortfunc);
1271
1272 // Now uniq. Since AgArray isn't expandable, count first.
1273 int j, ct;
1274
1275 for (i=ct=0; i<n; i++) {
1276 if (i==0 || tmp[i] != tmp[i-1]) {
1277 ct++;
1278 }
1279 }
1280
1281 AgArray<AgString> result(ct);
1282 for (i=j=0; i<n; i++) {
1283 if (i==0 || tmp[i] != tmp[i-1]) {
1284 result[j++] = tmp[i];
1285 }
1286 }
1287
1288 return result;
1289 }
1290
1291 AgHelpView &AgHelpView::layout() {
1292 LOGSECTION("AgHelpView::layout");
1293 if (size().width() == 0 || layoutActive) {
1294 return *this;
1295 }
1296 layoutActive++;
1297 doLayout();
1298 setLayoutDistorted(0, IWindow::layoutChanged);
1299 layoutActive--;
1300 return *this;
1301 }
1302
1303 AgHelpView::AgHelpView(IWindow *ownerWindow_,
1304 AgString topic)
1305 : ICanvas(nextChildId(), ownerWindow_, ownerWindow_)
1306 , verticalScrollBar(nextChildId(), this, this, IRectangle(),
1307 IScrollBar::vertical | IWindow::visible)
1308 , dataArea(this, topic)
1309 , vsbShowing(false)
1310 , layoutActive(0)
1311 , colorChange(this, onColorChange)
1312 , fontChange(this, onFontChange)
1313 , highlightIndex(0)
1314 {
1315 LOGSECTION("AgHelpView::AgHelpView");
1316 windowId = id();
1317 LOGV(id());
1318
1319 colorChange.attach(&ColorSpec::helpText);
1320 colorChange.attach(&ColorSpec::helpLink);
1321 colorChange.attach(&ColorSpec::helpUsedLink);
1322
1323 fontChange.attach(&FontSpec::help);
1324 fontChange.attach(&FontSpec::helpTitle);
1325
1326
1327 setMinimumSize(calcMinimumSize());
1328 verticalScrollBar.setScrollableRange(IRange(0,100));
1329 verticalScrollBar.moveScrollBoxTo(0);
1330
1331 IKeyboardHandler::handleEventsFor(this);
1332 IResizeHandler ::handleEventsFor(this);
1333 IScrollHandler ::handleEventsFor(this);
1334
1335 dataArea.show();
1336 show().setFocus();
1337 }
1338
1339 AgHelpView::~AgHelpView() {
1340 IKeyboardHandler::stopHandlingEventsFor(this);
1341 IResizeHandler ::stopHandlingEventsFor(this);
1342 IScrollHandler ::stopHandlingEventsFor(this);
1343 }
1344
1345 Boolean AgHelpView::windowResize(IResizeEvent &event){
1346 setLayoutDistorted(IWindow::layoutChanged, 0);
1347 return false;
1348 }
1349
1350 ISize AgHelpView::calcMinimumSize() const {
1351 int width = dataArea.minimumSize().width()
1352 + verticalScrollBar.minimumSize().width();
1353 return ISize(width, dataArea.minimumSize().height());
1354 }
1355
1356 ISize AgHelpView::suggestSize() {
1357 LOGSECTION("AgHelpView::suggestSize");
1358 ISize preferredSize = dataArea.preferredSize;
1359 int width = preferredSize.width();
1360 int nLines = preferredSize.height();
1361 if (nLines > defaultWindowHeight) {
1362 nLines = defaultWindowHeight;
1363 width += verticalScrollBar.minimumSize().width();
1364 }
1365 return ISize(width, nLines * lineHeight());
1366 }
1367
1368 AgHelpView &AgHelpView::repositionWindow() {
1369 int y = -verticalScrollBar.scrollBoxPosition();
1370 dataArea.moveTo(IPoint(0,y));
1371 return *this;
1372 }
1373
1374
1375 /*
1376 *
1377 * Layout considerations:
1378 *
1379 * The data view consists of up to four parts: two scroll bars, a heading
1380 * and a data area.
1381 *
1382 * The presence or absence of the scroll bars depends on the relative size
1383 * of the data area and the size of the table to be displayed.
1384 */
1385
1386 AgHelpView &AgHelpView::doLayout() {
1387 LOGSECTION("AgHelpView::doLayout");
1388 LOGV(id());
1389
1390 ISize canvasSize = size();
1391 int canvasWidth = canvasSize.width();
1392 int canvasHeight = canvasSize.height();
1393
1394 LOGV(canvasWidth);
1395 LOGV(canvasHeight);
1396
1397 int dataWidth = canvasWidth;
1398 int dataHeight = canvasHeight;
1399 //int dataY = 0;
1400
1401 LOGV(dataWidth);
1402 LOGV(dataHeight);
1403
1404
1405
1406 int vsbWidth = verticalScrollBar.minimumSize().width();
1407
1408 int verticalPosition = verticalScrollBar.scrollBoxPosition();
1409
1410 Boolean vsb = dataWidth * dataHeight < dataArea.spaceRequired;
1411
1412 dataArea.measure = dataWidth;
1413 if (vsb) {
1414 dataArea.measure -= vsbWidth+1;
1415 }
1416 dataArea.remeasureText();
1417 dataHeight = dataArea.nLines*lineHeight();
1418 if (dataHeight <= canvasHeight && vsb) {
1419 vsb = 0;
1420 dataArea.measure = dataWidth;
1421 dataArea.remeasureText();
1422 dataHeight = canvasHeight;
1423 }
1424 else if (dataHeight > canvasHeight && !vsb) {
1425 vsb = 1;
1426 dataArea.measure = dataWidth - vsbWidth+1;
1427 dataArea.remeasureText();
1428 dataHeight = dataArea.nLines*lineHeight();
1429 }
1430 else if (dataHeight <= canvasHeight) {
1431 dataHeight = canvasHeight;
1432 }
1433 dataArea.sizeTo(ISize(dataArea.measure, dataHeight));
1434
1435 if (vsb && !vsbShowing) {
1436 verticalScrollBar.moveScrollBoxTo(0);
1437 verticalScrollBar.show();
1438 vsbShowing = true;
1439 }
1440 if (!vsb && vsbShowing) {
1441 verticalScrollBar.hide();
1442 vsbShowing = false;
1443 verticalScrollBar.moveScrollBoxTo(0);
1444 }
1445
1446 if (vsbShowing) {
1447 dataWidth -= vsbWidth + 1;
1448 verticalScrollBar.moveTo(IPoint(dataWidth+1, 0));
1449 verticalScrollBar.sizeTo(ISize(vsbWidth, canvasHeight));
1450 }
1451
1452 LOGV(dataArea.size().asString());
1453 LOGV(dataArea.parentSize().asString());
1454
1455 LOGV(rect().asString());
1456 LOGV(dataArea.rect().asString());
1457
1458
1459 LOGV(dataArea.size().asString());
1460
1461 verticalScrollBar.setScrollableRange(IRange(0, dataHeight-1));
1462 verticalScrollBar.setVisibleCount(canvasHeight);
1463 verticalPosition = min((int) verticalScrollBar.scrollBoxRange().upperBound(),
1464 verticalPosition);
1465
1466 verticalScrollBar.setMinScrollIncrement(lineHeight());
1467
1468 verticalScrollBar.moveScrollBoxTo(verticalPosition);
1469 LOGV(verticalPosition);
1470 repositionWindow();
1471 return *this;
1472 }
1473
1474 Boolean AgHelpView::lineDown(IScrollEvent &event) {
1475 if (event.scrollBarWindow() != &verticalScrollBar) {
1476 return false;
1477 }
1478 moveScrollBox(event);
1479 repositionWindow();
1480 return true;
1481 }
1482
1483 Boolean AgHelpView::lineUp(IScrollEvent &event) {
1484 if (event.scrollBarWindow() != &verticalScrollBar) {
1485 return false;
1486 }
1487 moveScrollBox(event);
1488 repositionWindow();
1489 return true;
1490 }
1491
1492 Boolean AgHelpView::pageDown(IScrollEvent &event) {
1493 if (event.scrollBarWindow() != &verticalScrollBar) {
1494 return false;
1495 }
1496 moveScrollBox(event);
1497 repositionWindow();
1498 return true;
1499 }
1500
1501 Boolean AgHelpView::pageUp(IScrollEvent &event) {
1502 if (event.scrollBarWindow() != &verticalScrollBar) {
1503 return false;
1504 }
1505 moveScrollBox(event);
1506 repositionWindow();
1507 return true;
1508 }
1509
1510 Boolean AgHelpView::scrollBoxTrack(IScrollEvent &event) {
1511 moveScrollBox(event);
1512 repositionWindow();
1513 return true;
1514 }
1515
1516 Boolean AgHelpView::virtualKeyPress(IKeyboardEvent &event) {
1517 LOGSECTION("AgHelpView::Virtualkeypress");
1518 switch (event.virtualKey()) {
1519 case IKeyboardEvent::up: {
1520 int topLine = verticalScrollBar.scrollBoxPosition()
1521 - verticalScrollBar.minScrollIncrement();
1522 verticalScrollBar.moveScrollBoxTo(topLine);
1523 repositionWindow();
1524 return true;
1525 }
1526 case IKeyboardEvent::down: {
1527 LOGSECTION("Cursor down one line");
1528 int topLine = verticalScrollBar.scrollBoxPosition()
1529 + verticalScrollBar.minScrollIncrement();
1530 verticalScrollBar.moveScrollBoxTo(topLine);
1531 repositionWindow();
1532 return true;
1533 }
1534 case IKeyboardEvent::home: {
1535 if (!event.isCtrlDown()) {
1536 messageBeep();
1537 return true;
1538 }
1539 }
1540 case IKeyboardEvent::pageUp: {
1541 int topLine = verticalScrollBar.scrollBoxPosition();
1542 topLine -= verticalScrollBar.visibleCount();
1543 if (event.isCtrlDown()) {
1544 topLine = 0;
1545 }
1546 verticalScrollBar.moveScrollBoxTo(topLine);
1547 repositionWindow();
1548 return true;
1549 }
1550 case IKeyboardEvent::end: {
1551 if (!event.isCtrlDown()) {
1552 messageBeep();
1553 return true;
1554 }
1555 }
1556 case IKeyboardEvent::pageDown: {
1557 int topLine = verticalScrollBar.scrollBoxPosition();
1558 if (event.isCtrlDown()) {
1559 topLine = verticalScrollBar.scrollBoxRange().upperBound();
1560 }
1561 topLine += verticalScrollBar.visibleCount();
1562 verticalScrollBar.moveScrollBoxTo(topLine);
1563 repositionWindow();
1564 return true;
1565 }
1566 }
1567 return false;
1568 }
1569
1570 Boolean DrawingArea::command(ICommandEvent &event) {
1571 LOGSECTION("DrawingArea::command");
1572 unsigned index = event.commandId();
1573 if (index >= sortedLinks.size()) {
1574 LOGS("bad index");
1575 messageBeep();
1576 return true;
1577 }
1578 char *topic = sortedLinks[index].pointer();
1579 LOGV(topic);
1580 AgString title = AgString::format("Help - %s", topic);
1581 IFrameWindow *helpWindow = AgFrame::windowRegistry.find(title);
1582 if (helpWindow) {
1583 helpWindow->setFocus();
1584 return true;
1585 }
1586 helpWindow = new AgHelpWindow(topic);
1587 helpWindow->setAutoDeleteObject();
1588 return true;
1589 }
1590
1591 DrawingArea &DrawingArea::initPopUp() {
1592 LOGSECTION("DrawingArea::initPopUp");
1593 LOGV((int) &popUpMenu);
1594 int n = sortedLinks.size();
1595 int i;
1596 for (i = 0; i < n; i++) {
1597 AgString topic = sortedLinks[i];
1598 LOGV(i) LCV(topic);
1599 popUpMenu.addText(i, topic.pointer());
1600 }
1601 return *this;
1602 }
1603
1604 Boolean DrawingArea::makePopUpMenu(IMenuEvent &event ) {
1605 LOGSECTION("DrawingArea::makePopUpMenu called");
1606 //if (helpShowing) return true;
1607 //AgHelpHandler::handleEventsFor(frameWindow);
1608 //AgHelpHandler::handleEventsFor(&popUpMenu);
1609 //helpShowing = true;
1610 IPoint where(0,0);
1611 AgHelpWindow *frame = (AgHelpWindow *) frameWindow;
1612 where.setY(frame->helpView.verticalScrollBar.scrollBoxPosition());
1613 if (rightButtonState == waitingForClick) {
1614 LOGV(event.mousePosition().asString());
1615 where = event.mousePosition();
1616 }
1617 //popUpMenu.show(event.mousePosition());
1618 popUpMenu.show(where);
1619 //AgHelpHandler::stopHandlingEventsFor(frameWindow);
1620 //AgHelpHandler::stopHandlingEventsFor(&popUpMenu);
1621 //helpShowing = false;
1622
1623 return true;
1624 }
1625
1626 Boolean DrawingArea::showHelp(IEvent &event) {
1627 LOGSECTION("DrawingArea::showHelp");
1628 AgString topic;
1629 LOGV((int) frameWindow);
1630 LOGV((int) this);
1631 LOGV((int) event.controlWindow());
1632 LOGV((int) event.dispatchingWindow());
1633
1634 if (event.controlWindow() == frameWindow) {
1635 if (helpShowing) {
1636 return true;
1637 }
1638 helpShowing = true;
1639 //IPoint where = rect().minXMinY();
1640 IPoint where(0, 0);
1641 AgHelpWindow *frame = (AgHelpWindow *) frameWindow;
1642 where.setY(frame->helpView.verticalScrollBar.scrollBoxPosition());
1643 LOGV(where.asString());
1644 popUpMenu.show(where);
1645 helpShowing = false;
1646 return true;
1647 }
1648 return false;
1649 }
1650
1651
1652 AgHelpWindow::AgHelpWindow(AgString topic)
1653 : AgFrame(IFrameWindow::systemMenu
1654 | IFrameWindow::maximizeButton
1655 | IFrameWindow::sizingBorder)
1656 , helpView(this, topic)
1657 {
1658 LOGSECTION("AgHelpWindow::AgHelpWindow");
1659 AgString titleString =
1660 AgString::format("AnaGram Help - %s", topic.pointer());
1661 AgString simpleTitle =
1662 AgString::format("Help - %s", topic.pointer());
1663
1664 windowTitle.setObjectText(titleString.pointer());
1665
1666 registerTitle(simpleTitle.pointer());
1667 copyTitleText = simpleTitle;
1668
1669 LOGV(simpleTitle.pointer());
1670 setClient(&helpView);
1671
1672
1673 ISize minSize = frameRectFor(helpView.minimumSize()).size();
1674 setMinimumSize(minSize);
1675
1676 LOGV(helpView.minimumSize().asString());
1677 LOGV(minSize.asString());
1678 ISize tableSize = helpView.suggestSize();
1679
1680 int width = 56*font().avgCharWidth();
1681 int testWidth = tableSize.width();
1682
1683 int titleWidth
1684 = windowTitle.displaySize(windowTitle.text()).width()
1685 + windowTitle.minimumSize().width();
1686
1687 LOGV(windowTitle.text());
1688 LOGV(titleWidth);
1689 LOGV(testWidth);
1690
1691 if (testWidth < titleWidth) {
1692 testWidth = titleWidth;
1693 }
1694 LOGV(width);
1695 LOGV(testWidth);
1696
1697 if (testWidth < width/2) {
1698 width = width/2;
1699 }
1700 else if (testWidth > 2*width) {
1701 width = 2*width;
1702 }
1703 else {
1704 width = testWidth;
1705 }
1706
1707 ISize clientSize(width, tableSize.height());
1708 IRectangle frameRect(frameRectFor(clientSize));
1709 LOGV(clientSize.asString());
1710 LOGV(frameRect.size().asString());
1711 sizeTo(frameRect.size());
1712
1713 positionFrame();
1714 show();
1715 }
1716
1717 AgHelpWindow::~AgHelpWindow() {}
1718
1719 Boolean AgHelpWindow::showHelp(AgString topic) {
1720 LOGSECTION("AgHelpWindow::showHelp");
1721 LOGV(topic);
1722
1723 AgString windowTitle;
1724
1725 if (topic.exists()) {
1726 windowTitle = AgString::format("Help - %s", topic.pointer());
1727 }
1728 if (!windowTitle.exists()) {
1729 return false;
1730 }
1731
1732 LOGV(windowTitle);
1733
1734 IFrameWindow *helpWindow = AgFrame::windowRegistry.find(windowTitle);
1735 LOGV((int) helpWindow);
1736 if (helpWindow == 0) {
1737 helpWindow = new AgHelpWindow(topic);
1738 helpWindow->setAutoDeleteObject();
1739 }
1740 helpWindow->show();
1741 helpWindow->setFocus();
1742 //BringWindowToTop(helpWindow->handle());
1743 return true;
1744 }
1745
1746 Boolean AgHelpWindow::showHelpCentered(AgString topic) {
1747 LOGSECTION("AgHelpWindow::showHelp");
1748 AgString windowTitle;
1749
1750 if (topic.exists()) {
1751 windowTitle = AgString::format("Help - %s", topic.pointer());
1752 }
1753 if (!windowTitle.exists()) {
1754 return false;
1755 }
1756
1757 LOGV(topic.pointer());
1758
1759 IFrameWindow *helpWindow = AgFrame::windowRegistry.find(windowTitle);
1760 LOGV((int) helpWindow);
1761 if (helpWindow == 0) {
1762 helpWindow = new AgHelpWindow(topic);
1763 helpWindow->setAutoDeleteObject();
1764 }
1765 IPoint where = (IPair) place(IWindow::desktopWindow()->size(),
1766 helpWindow->size(), 11);
1767 helpWindow->moveTo(where);
1768 helpWindow->show().setFocus();
1769 return true;
1770 }
1771
1772