Mercurial > ~dholland > hg > ag > index.cgi
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/anagram/vaclgui/helpview.cpp Sat Dec 22 17:52:45 2007 -0500 @@ -0,0 +1,1772 @@ +/* + * AnaGram, A System for Syntax Directed Programming + * Copyright 1997-2002 Parsifal Software. All Rights Reserved. + * See the file COPYING for license and usage terms. + * + * helpview.cpp + */ + +#include <icoordsy.hpp> +#include <windows.h> + +#include "agcstack.h" +#include "agstack.h" +#include "agstring.h" +#include "ctrlpanel.hpp" +#include "dspar.hpp" +#include "dview.hpp" +#include "file.h" +#include "help.h" +#include "helpview.hpp" +#include "minmax.h" +#include "stacks.h" +#include "vaclgui-res.h" +#include "vaclgui.hpp" + +//#define INCLUDE_LOGGING +#include "log.h" + + +const int AgHelpView::defaultWindowHeight = 15; +AgBalancedTree<AgString> DrawingArea::traversedLinks; +AgBalancedTree<DrawingArea *> DrawingArea::activeViews; + + +DigSetter::Style DrawingArea::displayStyle[4] = { + DigSetter::Style(FontSpec::help, ColorSpec::helpText), + DigSetter::Style(FontSpec::help, ColorSpec::helpLink), + DigSetter::Style(FontSpec::help, ColorSpec::helpUsedLink), + DigSetter::Style(FontSpec::helpTitle, ColorSpec::helpText) +}; + +static int leading(IFont &f) { + return f.maxSize().height() + f.externalLeading(); +} + +HelpWord::HelpWord(char *text, int nChar, cint where, Style index) + : DigSetter::Dig(text, nChar, where, index) + , linktopic("") + , indent(0) + , bullet(0) + , forceBreak(0) + , noBreak(0) + , linkTraversed(0) + , highlight(0) +{ + //LOGSECTION("HelpWord::HelpWord"); + //LOGV(AgString(text, nChar)); + //LOGV(nChar); +} + +HelpWord::HelpWord(const HelpWord &word) + : DigSetter::Dig(word) + , linktopic(word.linktopic) + , indent(word.indent) + , bullet(word.bullet) + , forceBreak(word.forceBreak) + , noBreak(word.noBreak) + , linkTraversed(word.linkTraversed) + , highlight(word.highlight) +{ + //LOGSECTION("HelpWord::HelpWord(const HelpWord &)"); +} + +IRectangle HelpWord::rect(DigSetter::Style *style) { + IFont &font = style[styleIndex].font; + int above = font.maxAscender(); + int below = font.maxDescender(); + IPoint location(where.x, where.y - above); + ISize size(width, above+below); + return IRectangle(location,size); +} + +DrawingArea::DrawingArea(IWindow *owner_, AgString topic_) + : IStaticText(nextChildId(), owner_, owner_) + , windowFont(FontSpec::help) + , margin(windowFont.charWidth('M')) + , paraLeading((windowFont.maxSize().height() + + windowFont.externalLeading())/2) + , topic(topic_) + , setter(this, displayStyle) + , painting(0) + , popUpMenu(this, nextChildId()) + , refreshAction(*this, refreshAllLinks) + , frameWindow(0) + , highlightText(0) + , nHighlight(0) + , fingerCursorSet(0) + , fingerCursor(IResourceLibrary().loadPointer(IDI_FINGER_CUR)) + , helpShowing(0) + , rightButtonState(buttonIdle) +{ + LOGSECTION("DrawingArea::DrawingArea"); + IWindow *f; + for (f = parent(); !f->isFrameWindow(); f=f->parent()) { + /* nothing */ + } + frameWindow = (AgFrame *) f; + //LOGV(int(f)); + + int i; + for (i = 0; i < 256; i++) { + charWidth[i] = windowFont.charWidth(i); + } + activeViews.insert(this); + LOGV((int) this); +#ifdef INCLUDE_LOGGING + for (i = 0; i < activeViews.size(); i++) { + //LOGV((int) activeViews.sortedItem(i)); + //LOGV(activeViews.sortedItem(i)->topic); + LOGV((int) activeViews[i]) LCV(activeViews[i]->topic); + } +#endif + //paraIndent = windowFont.charWidth('M') + margin; + paraIndent = margin; + helpText = getHelpText(topic.pointer()); + links = findLinks(helpText); + LOGV(links.size()) LCV(activeViews.size()) LCV(topic); + sortedLinks = sortLinks(links); + setMinimumSize(calcMinimumSize()); + LOGV(minimumSize().asString()); + int minWidth = minimumSize().width() + 2*margin;; + int maxWidth = IWindow::desktopWindow()->size().width(); + measure = 56*avgCharWidth() + 2*margin; + if (measure < minWidth) { + measure = minWidth; + } + if (measure > maxWidth) { + measure = maxWidth; + } + LOGV(measure); + measureText(); + preferredSize = ISize(longestLine, nLines); + + initPopUp(); + + ICommandHandler ::handleEventsFor(this); + IMenuHandler ::handleEventsFor(this); + //IMenuHandler ::handleEventsFor(frameWindow); + IMouseHandler ::handleEventsFor(this); + IPaintHandler ::handleEventsFor(this); + //AgHelpHandler ::handleEventsFor(&popUpMenu); + AgHelpHandler ::handleEventsFor(frameWindow); + //LOGS("Help handler attached"); +} + + +DrawingArea::~DrawingArea() { + LOGSECTION("DrawingArea::~DrawingArea"); + LOGV((int) this); +#ifdef INCLUDE_LOGGING + for (int i = 0; i < activeViews.size(); i++) { + //LOGV((int) activeViews.sortedItem(i)); + //LOGV(activeViews.sortedItem(i)->topic); + LOGV((int) activeViews[i]) LCV(activeViews[i]->topic); + } +#endif + int flag = activeViews.remove(this); + assert(flag); +#ifdef INCLUDE_LOGGING + for (i = 0; i < activeViews.size(); i++) { + //LOGV((int) activeViews.sortedItem(i)); + //LOGV(activeViews.sortedItem(i)->topic); + LOGV((int)activeViews[i]) LCV(activeViews[i]->topic); + } +#endif + LOGV(topic) LCV(activeViews.size()); + ICommandHandler ::stopHandlingEventsFor(this); + IMenuHandler ::stopHandlingEventsFor(this); + //IMenuHandler ::stopHandlingEventsFor(frameWindow); + IMouseHandler ::stopHandlingEventsFor(this); + IPaintHandler ::stopHandlingEventsFor(this); + //AgHelpHandler ::stopHandlingEventsFor(&popUpMenu); + AgHelpHandler ::stopHandlingEventsFor(frameWindow); +} + +void DrawingArea::reset() { + LOGSECTION("DrawingArea::reset"); + disableUpdate(); + windowFont = FontSpec::help; + setFont(FontSpec::help); + for (int i = 0; i < 256; i++) { + charWidth[i] = windowFont.charWidth(i); + } + margin = windowFont.charWidth('M'); + //paraIndent = windowFont.charWidth('M') + margin; + paraIndent = margin; + paraLeading = (windowFont.maxSize().height() + + windowFont.externalLeading())/2; + LOGV(nWords); + LOGV(word.size()); + int minWidth = minimumSize().width() + 2*margin;; + int maxWidth = IWindow::desktopWindow()->size().width(); + measure = 56*avgCharWidth() + 2*margin; + if (measure < minWidth) { + measure = minWidth; + } + if (measure > maxWidth) { + measure = maxWidth; + } +/* + for (int j = 0; j < nWords; j++) { + setter.measureDig(word[j]); + } + remeasureText(); +*/ + helpText = getHelpText(topic.pointer()); + measureText(); + //remeasureText(); + enableUpdate(); + show(); +} + +DrawingArea &DrawingArea::copyTo(IClipboard &clipboard) { + LOGSECTION("DrawingArea::copyTo"); + int i; + AgCharStack charStack; + LOGV(nWords); + for (i = 0; i < nWords; i++) { + if (word[i].indent) { + charStack.push(" "); + } + charStack.push(word[i].text, word[i].textLength); + if (word[i].forceBreak) { + charStack.push("\r\n\r\n "); + } + else { + charStack.push(' '); + } + } + clipboard.setText(charStack.popString().pointer()); + return *this; +} + +void AgHelpView::onFontChange() { + LOGSECTION("AgHelpView::onFontChange"); + disableUpdate(); + dataArea.reset(); + doLayout(); + enableUpdate(); + show(); +} + +void DrawingArea::refreshAllLinks() { + LOGSECTION("DrawingArea::refreshAllLinks"); + int i; + LOGV(topic); + LOGV(activeViews.size()); + for (i = 0; i < activeViews.size(); i++) { + //LOGV(activeViews.sortedItem(i)->topic); + LOGV(activeViews[i]->topic); + //activeViews.sortedItem(i)->refreshLinks(); + activeViews[i]->refreshLinks(); + } +} + +DrawingArea &DrawingArea::refreshLinks() { + LOGSECTION("DrawingArea::refreshLinks"); + AgStack<AgString> tLinks; + int n = links.size(); + LOGV(topic); + LOGV(links.size()); + int i; + for (i = 0; i < n; i++) if (traversedLinks.includes(links[i])) { + tLinks.push(links[i]); + } + n = tLinks.size(); + LOGV(tLinks.size()); + if (n == 0) { + return *this; + } + for (i = 0; i < n; i++) { + int j; + AgString link = tLinks[i]; + int nLinkedWords = linkedWords.size(); + for (j = 0; j < nLinkedWords; j++) { + int k = linkedWords[j]; + if (word[k].linktopic != link) { + continue; + } + if (word[k].linkTraversed) { + continue; + } + word[k].linkTraversed = 1; + word[k].styleIndex = HelpWord::usedStyle; + setter.refresh(word[k]); + } + } + return *this; +} + +Boolean DrawingArea::paintWindow(IPaintEvent &event) { + //LOGSECTION("DrawingArea::paintWindow"); + if (painting) { + return true; + } + painting++; + IPresSpaceHandle handle = event.presSpaceHandle(); //cookie + SetBkMode(handle, TRANSPARENT); + IRectangle invalidRect( + ICoordinateSystem::isConversionNeeded() + ? ICoordinateSystem::convertToApplication(event.rect(),size()) + : event.rect() + ); + //LOGV(invalidRect.asString()); + IColor bgColor(ColorSpec::helpText.bg()); + setter.setEvent(event); + setter.clear(invalidRect); + int top = invalidRect.minY(); + int bot = invalidRect.maxY(); + IFont &font = FontSpec::help; + int descender = font.maxDescender(); + int ascender = font.maxAscender(); + //LOGV(ascender) LCV(descender); + int first = 0; + int last = nWords - 1; + //LOGV(top) LCV(bot); + while (first < last) { + int middle = (first + last) /2; + //LOGV(first) LCV(middle) LCV(last); + //LOGV(word[middle].where.y + descender); + if (setter.bottom(word[middle]) < top) { + first = middle + 1; + } + else { + last = middle - 1; + } + } + //LOGS("found first dig"); + char *endHighlight = highlightText + nHighlight; + + while (first < nWords && setter.top(word[first]) <= bot) { + HelpWord &aWord = word[first]; + if (endHighlight && aWord.text < endHighlight + && aWord.text + aWord.textLength >= highlightText) + { + HelpWord highlightWord = aWord; + int n = highlightText - highlightWord.text; + if (n > 0) { + DigSetter::Dig dig = highlightWord; + dig.textLength = n; + setter.measureDig(dig); + setter.setDig(dig); + highlightWord.where.x += dig.width; + highlightWord.text += n; + highlightWord.textLength -= n; + } + n = highlightWord.text + highlightWord.textLength - + (highlightText + nHighlight); + if (n > 0) { + highlightWord.textLength -= n; + } + setter.measureDig(highlightWord); + setter.setDig(highlightWord, 1); + if (n > 0) { + DigSetter::Dig dig = highlightWord; + dig.textLength = n; + dig.text += highlightWord.textLength; + setter.measureDig(highlightWord); + dig.where.x += highlightWord.width; + setter.setDig(dig); + } + //LOGV(aWord.where) LCV(aWord.width) LCV(aWord.forceBreak); + if (first+1 < word.size()) { + HelpWord &bWord = word[first+1]; + //LOGV(bWord.where) LCV(bWord.width) LCV(bWord.forceBreak); + if (aWord.where.y == bWord.where.y && + aWord.text + aWord.textLength < endHighlight) { + //LOGSECTION("BridgeHighlights"); + cint where = aWord.where; + where.x += aWord.width; + where.y -= ascender; + int bWidth = bWord.where.x - where.x; + int aWidth = bWidth/2; + bWidth -= aWidth; + int height = ascender+descender; + //LOGV(aWidth) LCV(bWidth) LCV(height); + //LOGV(where); + DigSetter::Hole firstHalf(where, cint(aWidth, height), + aWord.styleIndex); + where.x += aWidth; + //LOGV(where); + DigSetter::Hole secondHalf(where, cint(bWidth, height), + bWord.styleIndex); + setter.reverse(firstHalf); + setter.reverse(secondHalf); + } + } + } + else { + setter.setDig(aWord); + } + first++; + //LOGV(first); + } + //LOGS("loop done"); + setter.closeEvent(); + painting = 0; + return true; +} + +DrawingArea &DrawingArea::refreshWords(char *p, int n) { + LOGSECTION("DrawingArea::refreshWords"); + LOGV((int) p) LCV(n); + if (p == 0 || n == 0) { + return *this; + } + int first = 1; + int middle; + int last = nWords - 1; + while (first < last) { + middle = (first + last) /2; + if (p < word[middle].text) { + last = middle - 1; + } + else if (p < word[middle].text + word[middle].textLength) { + first = middle; + break; + } + else { + first = middle + 1; + } + } + char *end = p + nHighlight; + last = first; + DigSetter::Hole hole = setter.makeHole(word[first]); + while (end > word[last].text + word[last].textLength) { + last++; + if (word[last].where.y != word[first].where.y) { + setter.refresh(hole); + hole = setter.makeHole(word[last]); + LOGV(hole.where) LCV(hole.size); + } + else { + hole.size.x = word[last].width + word[last].where.x - hole.where.x; + } + } + setter.refresh(hole); + LOGV(hole.where) LCV(hole.size); + LOGV(first) LCV(middle) LCV(last); + LOGV((int) word[first].text) LCV(word[first].textLength); + LOGV((int) word[middle].text) LCV(word[middle].textLength); + LOGV((int) word[last].text) LCV(word[last].textLength); + return *this; +} + +AgHelpView &AgHelpView::positionWords(char *p, int n) { + LOGSECTION("AgHelpView::positionWords"); + if (p == 0 || n == 0) { + return *this; + } + int first = 1; + int middle; + int last = dataArea.nWords - 1; + while (first < last) { + middle = (first + last) /2; + if (p < dataArea.word[middle].text) { + last = middle - 1; + } + else if (p < + dataArea.word[middle].text + dataArea.word[middle].textLength) { + first = middle; + break; + } + else { + first = middle + 1; + } + } + char *end = p + dataArea.nHighlight; + last = first; + while (end > dataArea.word[last].text + dataArea.word[last].textLength) { + last++; + } + LOGV(first) LCV(middle) LCV(last); + HelpWord &firstWord = dataArea.word[first]; + IFLOG(HelpWord &middleWord = ) dataArea.word[middle]; + HelpWord &lastWord = dataArea.word[last]; + LOGV((int) firstWord.text) LCV(firstWord.textLength); + LOGV((int) middleWord.text) LCV(middleWord.textLength); + LOGV((int) lastWord.text) LCV(lastWord.textLength); + IFont &font = dataArea.displayStyle[firstWord.styleIndex].font; + int descender = font.maxDescender(); + int ascender = font.maxAscender(); + int bottom = verticalScrollBar.scrollBoxPosition() + size().height(); + int top = verticalScrollBar.scrollBoxPosition(); + LOGV(top) LCV(bottom); + LOGV(firstWord.where.y - ascender); + LOGV(lastWord.where.y + descender); + if (lastWord.where.y + descender > bottom) { + int yPos = lastWord.where.y + descender - size().height(); + verticalScrollBar.moveScrollBoxTo(yPos); + repositionWindow(); + } + else if (firstWord.where.y - ascender < top) { + int yPos = firstWord.where.y - ascender; + verticalScrollBar.moveScrollBoxTo(yPos); + repositionWindow(); + } + return *this; +} + +Boolean AgHelpView::findNext(AgString s) { + LOGSECTION("AgHelpView::findNext"); + LOGV(s); + searchProcess.setKey(s); + + char *start = dataArea.highlightText; + if (start == 0) { + start = dataArea.helpText.pointer(); + } + else if (s.size() == dataArea.nHighlight + && strnicmp(s.pointer(), dataArea.highlightText, s.size()) == 0) { + start++; + } + + int length = dataArea.myTextLength - (start - dataArea.helpText.pointer()); + LOGV((int) start) LCV(length); + if (length <= 0) { + return 0; + } + char *p = searchProcess.scanForward(start, length); + if (p == 0) { + return 0; + } + char *oldHighlight = dataArea.highlightText; + int oldCount = dataArea.nHighlight; + dataArea.highlightText = p; + dataArea.nHighlight = s.size(); + dataArea.refreshWords(oldHighlight, oldCount); + dataArea.refreshWords(dataArea.highlightText, dataArea.nHighlight); + positionWords(dataArea.highlightText, dataArea.nHighlight); + return 1; +} + +Boolean AgHelpView::findPrev(AgString s) { + LOGSECTION("AgHelpView::findPrev"); + LOGV(s); + searchProcess.setKey(s); + + char *start = dataArea.helpText.pointer(); + int length = dataArea.helpText.size(); + if (dataArea.highlightText) { + length = dataArea.highlightText + dataArea.nHighlight - start; + } + + if (dataArea.nHighlight) { + if (s.size() == dataArea.nHighlight && + !strnicmp(s.pointer(), dataArea.highlightText, s.size())) { + length--; + } + } + char *p = searchProcess.scanReverse(start, length); + if (p == 0) { + return 0; + } + char *oldHighlight = dataArea.highlightText; + int oldCount = dataArea.nHighlight; + dataArea.highlightText = p; + dataArea.nHighlight = s.size(); + dataArea.refreshWords(oldHighlight, oldCount); + dataArea.refreshWords(dataArea.highlightText, dataArea.nHighlight); + positionWords(dataArea.highlightText, dataArea.nHighlight); + return 1; +} + +Boolean DrawingArea::mouseMoved(IMouseEvent &event) { + //LOGSECTION("DrawingArea::mouseMoved"); + IPoint pWhere(event.mousePosition()); + cint where(pWhere.x(), pWhere.y()); + //LOGV(where); + //IFont &font = FontSpec::help; + int first = 0; + int last = nWords - 1; + while (first < last) { + int middle = (first + last) /2; + if (setter.bottom(word[middle]) < where.y) { + first = middle + 1; + } + else if (setter.top(word[middle]) > where.y) { + last = middle - 1; + } + else if (word[middle].where.x + word[middle].width < where.x) { + first = middle + 1; + } + else if (word[middle].where.x > where.x) { + last = middle - 1; + } + else { + first = middle; + break; + } + } + //LOGV(first); + int haslink = 0; + if (first < nWords && word[first].linktopic != "") { + haslink = 1; + } + if (where.x < word[first].where.x + && (first <= 0 + || setter.bottom(word[first-1]) < where.y)) { + haslink = 0; + } + if (where.x > word[first].where.x + word[first].width + && (first+1 >= nWords + || setter.top(word[first+1]) > where.y)) { + haslink = 0; + } + //LOGV(haslink); + //IMousePointerEvent pointerEvent(event); + if (haslink && !fingerCursorSet) { + //LOGS("Set fingerCursor"); + frameWindow->setMousePointer(fingerCursor); + //pointerEvent.setMousePointer(fingerCursor); + //SetCursor(fingerCursor); + fingerCursorSet = true; + } + else if (!haslink && fingerCursorSet) { + //LOGS("Reset activeCursor"); + frameWindow->setMousePointer(ControlPanel::activeCursor); + //pointerEvent.setMousePointer(ControlPanel::activeCursor); + //SetCursor(ControlPanel::activeCursor); + fingerCursorSet = false; + } + return true; +} + +Boolean DrawingArea::mouseClicked(IMouseClickEvent &event) { + LOGSECTION("DrawingArea::mouseClicked"); + if (event.mouseButton() == IMouseClickEvent::button2) { + if (event.mouseAction() == IMouseClickEvent::down) { + rightButtonState = buttonDown; + } + else if (event.mouseAction() == IMouseClickEvent::up) { + rightButtonState = waitingForClick; + } + else if (event.mouseAction() == IMouseClickEvent::click) { + rightButtonState = buttonIdle; + } + return false; + } + if (event.mouseButton() != IMouseClickEvent::button1) { + return false; + } + if (ControlPanel::helpCursorSet) { + if (event.mouseAction() == IMouseClickEvent::down) { + ControlPanel::helpCursorSet = 0; + ControlPanel::resetCursor(); + } + } + switch (event.mouseAction()) { + case IMouseClickEvent::doubleClick: + case IMouseClickEvent::click: { + if (event.windowUnderPointer() != handle()) { + return false; + } + IPoint pWhere(event.mousePosition()); + cint where(pWhere.x(), pWhere.y()); + //LOGV(where); + //IFont &font = FontSpec::help; + int first = 0; + int last = nWords - 1; + while (first < last) { + int middle = (first + last) /2; + //LOGV(first) LCV(middle) LCV(last); + //LOGV(word[middle].where.x) LCV(word[middle].where.y); + //LOGV(setter.top(word[middle])) LCV(setter.bottom(word[middle])); + if (setter.bottom(word[middle]) < where.y) { + first = middle + 1; + } + else if (setter.top(word[middle]) > where.y) { + last = middle - 1; + } + else if (word[middle].where.x + word[middle].width < where.x) { + first = middle + 1; + } + else if (word[middle].where.x > where.x) { + last = middle - 1; + } + else { + first = middle; + break; + } + } + AgString linktopic = ""; + //LOGV(first); + for (;first < nWords; first++) { + if (setter.top(word[first]) > where.y) { + return false; + } + //LOGV(word[first].where) LCV(word[first].link); + int xr = word[first].where.x + word[first].width; + //LOGV(word[first].width) LCV(xr); + if (xr < where.x) { + linktopic = word[first].linktopic; + continue; + } + int xl = word[first].where.x; + //LOGS("contact") LV(xr) LCV(xl) LCV(link) LCV(word[first].link); + if ( where.x < xl && linktopic != word[first].linktopic) { + return false; + } + if (word[first].linktopic == "") { + return false; + } + break; + } + if (first >= nWords) { + return false; + } + //LOGV(first) LCV(word[first].linktopic); + + word[first].linkTraversed = 1; + linktopic = word[first].linktopic; + traversedLinks.insert(linktopic); + + int i; + + int iMin = first, iMax = first; + for (i = first; i >= 0 && word[i].linktopic == linktopic; i--) { + word[i].styleIndex = HelpWord::usedStyle; + iMin = i; + } + for (i = first + 1 ; i < nWords && word[i].linktopic == linktopic; i++) { + iMax = i; + word[i].styleIndex = HelpWord::usedStyle; + } + HelpWord &wmn = word[iMin]; + HelpWord &wmx = word[iMax]; + + int minX = min(wmn.where.x,wmx.where.x); + int maxX = max(wmn.where.x + wmn.width, wmx.where.x + wmx.width); + int minY = min(setter.top(wmn), setter.top(wmx)); + int maxY = max(setter.bottom(wmn), setter.bottom(wmx)); + + IRectangle r(IPoint(minX, minY), IPoint(maxX, maxY)); + //LOGV(r.asString()); + refresh(r); + + const char *newTopic = word[first].linktopic.pointer(); + //LOGV(newTopic); + AgString title = AgString::format("Help - %s", newTopic); + IFrameWindow *helpWindow = AgFrame::windowRegistry.find(title); + if (helpWindow == 0) { + helpWindow = new AgHelpWindow(newTopic); + helpWindow->setAutoDeleteObject(); + } + helpWindow->show().setFocus(); + + refreshAction.performDeferred(); + return true; + } + } + return false; +} + +ISize DrawingArea::calcMinimumSize() const { + LOGSECTION("DrawingArea::calcMinimumSize"); + unsigned char *p = (unsigned char *) helpText.pointer(); + IFont &font = FontSpec::help; + int enWidth = font.avgCharWidth(); + int minWidth = font.minTextWidth((char *)p); + LOGV(minWidth); + LOGV(margin); + LOGV(enWidth); + + IFont &titleFont = FontSpec::helpTitle; + int width = titleFont.textWidth(topic.pointer()); + if (width > minWidth) { + minWidth = width; + } + while (*p) { + if (*p == ' ') { + unsigned char *q = p; + int k = 0; + while (*q == ' ') { + k++; + q++; + } + p = q; + int width = 0; + while (*q && *q != '\n') { + width += charWidth[*q++]; + } + width += k*enWidth; + if (width > minWidth) { + minWidth = width; + } + p = ++q; + continue; + } + while (*p && *p != '\n') { + p++; + } + if (*p) { + p++; + } + } + minWidth += 2*margin; + int height = windowFont.maxCharHeight() + windowFont.externalLeading(); + return ISize(minWidth, 5*height); +} + +#define BULLET 7 + +DrawingArea &DrawingArea::measureText() { + LOGSECTION("DrawingArea::measureText"); + IFont &textFont = FontSpec::help; + IFont &titleFont = FontSpec::helpTitle; + AgString auxText(helpText.pointer()); + + + AgStack<int> linkedWordStack; + + unsigned char *p = (unsigned char *) helpText.pointer(); + unsigned char *aux = (unsigned char *) auxText.pointer(); + *aux = 0; + + int lineHeight = textFont.maxCharHeight() + textFont.externalLeading(); + + int spaceWidth = charWidth[' ']; + int whereX = paraIndent; + int whereY = leading(titleFont); + nWords = 0; + word.discardData(); + + //LOGFont(windowFont); + //LOGV(margin); + //LOGV(paraIndent); + + int titleWidth = titleFont.textWidth(topic.pointer()); + //LOGV((char *) titleFont.name()) LCV(titleFont.pointSize()); + //LOGV(titleWidth); + + whereX = (measure - titleWidth)/2; + LOGV(whereX) LCV(whereY); + + word.push(HelpWord(topic.pointer(), strlen(topic.pointer()), + cint(whereX, whereY), HelpWord::titleStyle)); + word[0].width = titleWidth; + nWords = 1; + whereY += titleFont.maxDescender(); + whereY += paraLeading; + word[0].forceBreak = 1; + + whereX = paraIndent; + + int indentedLine = 0; + int tabbedLine = 0; + + int linkIndex = 0; + + int highlightBegin = 0; + int highlightEnd = 0; + longestLine = 0; + int enWidth = avgCharWidth(); + int emWidth = font().charWidth('M'); + LOGV(enWidth) LCV(emWidth); + + LOGV(whereY); + //int indentLevel = 0; + int forceBreak = 0; + int bulletedLine = 0; + while (*p) { + if (*p == ' ') { + int k = 0; + while (*p == ' ') { + p++; + k++; + } + whereX = k*enWidth + margin; + word[nWords-1].forceBreak = 1; + indentedLine = 1; + //indentLevel = k; + } + else if (*p == '\t') { + p++; + whereX = 2*emWidth + margin; + word[nWords-1].forceBreak = 1; + tabbedLine = 1; + LOGS("tabbed Line"); + } + else if (*p == BULLET) { + whereX = emWidth + margin; + if (nWords > 0) { + word[nWords-1].forceBreak = 1; + } + bulletedLine = 1; + LOGS("bulleted Line") LCV(whereX); + } + int wordLength = 0; + unsigned char *beginWord = aux; + unsigned char *q = beginWord; + int dotFlag = 0; + while (*p && *p != ' ' && *p != '\t' && *p != '\n') { + if (*p == (unsigned char) 169) { + //LOGV(nWords) LCV(whereX) LCV(whereY); + highlightBegin++; + p++; + continue; + } + if (*p == (unsigned char) 170) { + //LOGV(nWords) LCV(whereX) LCV(whereY); + highlightEnd++; + p++; + continue; + } + //*q++ = *p++; + + *q++ = *p; + if (*p == 0 || *p++ != '.') { + continue; + } + if (strchr(" \r\n\t", *p) == 0) { + dotFlag = 1; + } + break; + } + + int nChars = q-beginWord; + *q = 0; + wordLength = windowFont.textWidth((char *) beginWord); + LOGV(nWords) LCV(wordLength) LCV(nChars) LCV(beginWord); + *q++ = ' '; + aux = q; + while (*p == ' ' || (*p == '\t' && !tabbedLine)) { + p++; + } + //forceBreak = (indentedLine || tabbedLine) && *p == '\n'; + //forceBreak = (bulletedLine || tabbedLine) && *p == '\n'; + forceBreak = tabbedLine && *p == '\n'; + int endLine = *p == '\n'; + if (endLine) { + p++; + } + if (*p == '\n') { // More than one newline? + forceBreak = 1; // Yes + while (*p == '\n') { + p++; + } + } + //LOGV(forceBreak) LCV(indentLevel) LCV(indentedLine) LCV(dotFlag); + LOGV(whereX); + if (!dotFlag && + !tabbedLine && + !indentedLine && + whereX + wordLength > measure - margin) { + if (whereX > longestLine) longestLine = whereX; + LOGV(longestLine); + whereX = margin; + whereY += lineHeight; + } + AgString temp((char *) beginWord, 10); + LOGV(word.size()) LCV(temp.pointer()) LCV(nChars) LCV(whereX) LCV(whereY); + HelpWord thisWord((char *) beginWord, nChars, cint(whereX, whereY)); + if (*beginWord == BULLET) { + //whereX = margin + 2*emWidth; + *beginWord = 183; + LOGS("replaced bullet") LCV(whereX) LCV(emWidth); + } + else { + whereX += wordLength; + if (!dotFlag) { + whereX += spaceWidth; + } + LOGV(whereX); + if (tabbedLine && !endLine && *p == '\t') { + p++; + int newX = margin; + while (newX < whereX) { + newX += 2*emWidth; + } + LOGV(newX); + whereX = newX; + while (*p == '\t') { + whereX += 2*emWidth; + p++; + } + } + } + LOGV(whereX); + thisWord.indent = indentedLine || tabbedLine; + thisWord.bullet = !thisWord.indent && bulletedLine != 0; + thisWord.forceBreak = forceBreak; + thisWord.noBreak = dotFlag; + thisWord.width = wordLength; + LOGV(thisWord.bullet) LCV(thisWord.forceBreak) LCV(thisWord.indent); + if (highlightBegin) { + //LOGS("set up links"); + //LOGV(linkIndex); + LOGV(links.size()) LCV(topic); + thisWord.linktopic = links[linkIndex]; + //LOGV(thisWord.link); + if (traversedLinks.includes(thisWord.linktopic)) { + thisWord.linkTraversed = 1; + } + //LOGV(dict_str(help_dict, thisWord.link)); + //LOGV(thisWord.where); + if (thisWord.linkTraversed) { + thisWord.styleIndex = HelpWord::usedStyle; + } + else { + thisWord.styleIndex = HelpWord::linkStyle; + } + } + if (highlightEnd) { + highlightBegin = highlightEnd = 0; + linkIndex++; + } + if (forceBreak) { + LOGS("forced break"); + if ((tabbedLine || indentedLine ) && whereX > longestLine) { + longestLine = whereX; + LOGV(longestLine); + } + whereX = indentedLine || tabbedLine ? margin : paraIndent; + if ((tabbedLine || bulletedLine) && *p == ' ') { + whereX = paraIndent; + while (*p == ' ') p++; + } + //whereX = paraIndent; + whereY += lineHeight; + if (!indentedLine && !tabbedLine) { + whereY += paraLeading; + } + } + word.push(thisWord); + linkedWordStack.push(nWords); + nWords++; + if (forceBreak) { + indentedLine = tabbedLine = bulletedLine = 0; + } + //LOGV(nWords); + } + whereY += 2*lineHeight; + longestLine += margin; + LOGV(longestLine); + nLines = whereY/lineHeight; + //LOGV(nLines); + //LOGV(nWords); + spaceRequired = measure * whereY; + linkedWords = AgArray<int>(linkedWordStack); + *aux = 0; + helpText = auxText; + myTextLength = helpText.size(); + return *this; +} + +DrawingArea &DrawingArea::remeasureText() { + LOGSECTION("DrawingArea::remeasureText"); + //LOGV(measure); + IFont &textFont = FontSpec::help; + IFont &titleFont = FontSpec::helpTitle; + int lineHeight = textFont.maxCharHeight() + textFont.externalLeading(); + //int emWidth = textFont.maxSize().width(); + int emWidth = textFont.charWidth('M'); + + LOGV(emWidth); + //int paraIndent = margin + emWidth; + int paraIndent = margin; + + int spaceWidth = charWidth[' ']; + //LOGV(spaceWidth); + cint where(paraIndent, paraLeading); + int forceBreak = 0; + int k = 0; + HelpWord &title = word[0]; + int titleWidth = titleFont.textWidth(topic.pointer()); + //LOGV((char *) titleFont.name()) LCV(titleFont.pointSize()); + //LOGV(titleWidth); + word[0].width = titleWidth; + title.where.x = (measure - titleWidth)/2; + title.where.y = leading(titleFont); + { + //IFont &f = displayStyle[title.styleIndex].font; + //LOGV(f.name()) LCV(f.pointSize()); + } + where.y += 3*leading(titleFont)/2; + where.y += titleFont.maxDescender(); + word[1].where.y = where.y; + int indentedLine = 0; + LOGV(paraLeading) LCV(lineHeight); + for (k = 1; k < nWords; k++) { + HelpWord &textWord = word[k]; + int width = word[k].width; + //int i = k; + //while (i < nWords-1 && word[i++].noBreak) { + // width += word[i].width; + //} + char buf[100]; + strncpy(buf, textWord.text, textWord.textLength); + buf[textWord.textLength] = 0; + LOGV(k) LCV(textWord.indent) LCV(textWord.where) + LCV(*textWord.text) LCV(buf); + LOGV(textWord.bullet); + int rightIndent = textWord.bullet ? 2*emWidth : 0; + if (forceBreak) { + where.y += lineHeight; + where.x = textWord.where.x; + if (textWord.bullet) { + where.x = margin + 2*emWidth; + } + //if (!textWord.indent && where.x > margin) { + // where.y += paraLeading; + //} + if (!textWord.indent) { + where.y += paraLeading; + } + indentedLine = textWord.indent; + } + else if (!indentedLine && + where.x + width > measure - margin - rightIndent) { + where.x = margin; + where.y += lineHeight; + if (textWord.bullet) { + where.x += 2*emWidth; + } + } + textWord.where.y = where.y; + + //if (!forceBreak || !textWord.indent) + if (!forceBreak && !textWord.indent) { + textWord.where.x = where.x; + } + + LOGV(where) LCV(forceBreak) LCV(indentedLine); + if (textWord.linktopic != "") { + //LOGV(textWord.linktopic); + //LOGV(k) LV(where); + //LOGV(where.x + textWord.width); + } + if (*textWord.text == (char) 183) { + //where.x += emWidth; //bullet + } + else { + where.x += textWord.width + spaceWidth; + } + forceBreak = textWord.forceBreak; + AgString temp((char *)textWord.text, 10); + LOGV(k) LCV(temp) LCV(textWord.where); + } + where.y += 2*lineHeight; + + nLines = where.y/lineHeight; + //LOGV(nLines); + return *this; +} + +AgString DrawingArea::getHelpText(const char *topic) { + LOGSECTION("getHelpText"); + const HelpTopic *ht = help_topic(topic); + LOGV((int)ht); + AgString ret = helptopic_gettext(ht); + LOGV(ret); + return ret; +} + +AgArray<AgString> DrawingArea::findLinks(AgString msg) { + char *t = msg.pointer(); + LOGSECTION("DrawingArea::findLinks"); + LOGV(topic); + AgStack<AgString> stack; + while(1) { + t = strchr(t, 169); + if (t != NULL && t[1] == '\'') { + t = strchr(t+1, 169); + } + if (t == NULL) { + break; + } + AgCharStack charStack; + t++; + while (1) { + unsigned char c = *t++; + switch (c) { + //case '.': + case '\n': + case '\r': + case ' ' : + case '\t': { + charStack.push(' '); + while (1) { + switch (*t) { + case '\n': + case '\r': + case ' ' : + case '\t': { + t++; + continue; + } + default: break; + } + break; + } + continue; + } + case 0: + case 170: + break; + default: + charStack.push(c); + continue; + } + break; + } + if (charStack.size() == 0) { + continue; + } + AgString word = charStack.popString(); + LOGV(word.pointer()); + + if (!help_topicexists(word.pointer())) { + int n = word.size(); + int k = word[n-1]; + if (tolower(k) == 's') { + word[n-1] = 0; + } + } + if (!help_topicexists(word.pointer())) { + LOGS("Dangling help link: ") LV(word); + } + + // Canonicalize the name of the topic. + const HelpTopic *ht = help_topic(word.pointer()); + word = helptopic_gettitle(ht); + + stack.push(word); + LOGV(AgString(t, 10).pointer()); + } + stack.push("Using Help"); + LOGV(stack.size()); + return AgArray<AgString>(stack); +} + + +static int OPTLINK linksortfunc(const void *av, const void *bv) { + const AgString &as = *(const AgString *)av; + const AgString &bs = *(const AgString *)bv; + + int r = stricmp(as.pointer(), bs.pointer()); + if (r==0) { + r = strcmp(as.pointer(), bs.pointer()); + } + return r; +} + +AgArray<AgString> DrawingArea::sortLinks(AgArray<AgString> links) { + LOGSECTION("DrawingArea::sortLinks"); + + // This used to sort and uniq by inserting into an AgBalancedTree. + // The problem (apart from overelaboration) is that this requires a + // custom wrapper class to insert the desired sort function, and + // thus its own template instantiation. Which is a hassle, and + // fairly silly... + + int i, n = links.size(); + LOGV(n); + AgArray<AgString> tmp(n); + for (i=0; i<n; i++) { + tmp[i] = links[i]; + } + qsort(tmp.pointer(), tmp.size(), sizeof(AgString), linksortfunc); + + // Now uniq. Since AgArray isn't expandable, count first. + int j, ct; + + for (i=ct=0; i<n; i++) { + if (i==0 || tmp[i] != tmp[i-1]) { + ct++; + } + } + + AgArray<AgString> result(ct); + for (i=j=0; i<n; i++) { + if (i==0 || tmp[i] != tmp[i-1]) { + result[j++] = tmp[i]; + } + } + + return result; +} + +AgHelpView &AgHelpView::layout() { + LOGSECTION("AgHelpView::layout"); + if (size().width() == 0 || layoutActive) { + return *this; + } + layoutActive++; + doLayout(); + setLayoutDistorted(0, IWindow::layoutChanged); + layoutActive--; + return *this; +} + +AgHelpView::AgHelpView(IWindow *ownerWindow_, + AgString topic) + : ICanvas(nextChildId(), ownerWindow_, ownerWindow_) + , verticalScrollBar(nextChildId(), this, this, IRectangle(), + IScrollBar::vertical | IWindow::visible) + , dataArea(this, topic) + , vsbShowing(false) + , layoutActive(0) + , colorChange(this, onColorChange) + , fontChange(this, onFontChange) + , highlightIndex(0) +{ + LOGSECTION("AgHelpView::AgHelpView"); + windowId = id(); + LOGV(id()); + + colorChange.attach(&ColorSpec::helpText); + colorChange.attach(&ColorSpec::helpLink); + colorChange.attach(&ColorSpec::helpUsedLink); + + fontChange.attach(&FontSpec::help); + fontChange.attach(&FontSpec::helpTitle); + + + setMinimumSize(calcMinimumSize()); + verticalScrollBar.setScrollableRange(IRange(0,100)); + verticalScrollBar.moveScrollBoxTo(0); + + IKeyboardHandler::handleEventsFor(this); + IResizeHandler ::handleEventsFor(this); + IScrollHandler ::handleEventsFor(this); + + dataArea.show(); + show().setFocus(); +} + +AgHelpView::~AgHelpView() { + IKeyboardHandler::stopHandlingEventsFor(this); + IResizeHandler ::stopHandlingEventsFor(this); + IScrollHandler ::stopHandlingEventsFor(this); +} + +Boolean AgHelpView::windowResize(IResizeEvent &event){ + setLayoutDistorted(IWindow::layoutChanged, 0); + return false; +} + +ISize AgHelpView::calcMinimumSize() const { + int width = dataArea.minimumSize().width() + + verticalScrollBar.minimumSize().width(); + return ISize(width, dataArea.minimumSize().height()); +} + +ISize AgHelpView::suggestSize() { + LOGSECTION("AgHelpView::suggestSize"); + ISize preferredSize = dataArea.preferredSize; + int width = preferredSize.width(); + int nLines = preferredSize.height(); + if (nLines > defaultWindowHeight) { + nLines = defaultWindowHeight; + width += verticalScrollBar.minimumSize().width(); + } + return ISize(width, nLines * lineHeight()); +} + +AgHelpView &AgHelpView::repositionWindow() { + int y = -verticalScrollBar.scrollBoxPosition(); + dataArea.moveTo(IPoint(0,y)); + return *this; +} + + +/* + * + * Layout considerations: + * + * The data view consists of up to four parts: two scroll bars, a heading + * and a data area. + * + * The presence or absence of the scroll bars depends on the relative size + * of the data area and the size of the table to be displayed. + */ + +AgHelpView &AgHelpView::doLayout() { + LOGSECTION("AgHelpView::doLayout"); + LOGV(id()); + + ISize canvasSize = size(); + int canvasWidth = canvasSize.width(); + int canvasHeight = canvasSize.height(); + + LOGV(canvasWidth); + LOGV(canvasHeight); + + int dataWidth = canvasWidth; + int dataHeight = canvasHeight; + //int dataY = 0; + + LOGV(dataWidth); + LOGV(dataHeight); + + + + int vsbWidth = verticalScrollBar.minimumSize().width(); + + int verticalPosition = verticalScrollBar.scrollBoxPosition(); + + Boolean vsb = dataWidth * dataHeight < dataArea.spaceRequired; + + dataArea.measure = dataWidth; + if (vsb) { + dataArea.measure -= vsbWidth+1; + } + dataArea.remeasureText(); + dataHeight = dataArea.nLines*lineHeight(); + if (dataHeight <= canvasHeight && vsb) { + vsb = 0; + dataArea.measure = dataWidth; + dataArea.remeasureText(); + dataHeight = canvasHeight; + } + else if (dataHeight > canvasHeight && !vsb) { + vsb = 1; + dataArea.measure = dataWidth - vsbWidth+1; + dataArea.remeasureText(); + dataHeight = dataArea.nLines*lineHeight(); + } + else if (dataHeight <= canvasHeight) { + dataHeight = canvasHeight; + } + dataArea.sizeTo(ISize(dataArea.measure, dataHeight)); + + if (vsb && !vsbShowing) { + verticalScrollBar.moveScrollBoxTo(0); + verticalScrollBar.show(); + vsbShowing = true; + } + if (!vsb && vsbShowing) { + verticalScrollBar.hide(); + vsbShowing = false; + verticalScrollBar.moveScrollBoxTo(0); + } + + if (vsbShowing) { + dataWidth -= vsbWidth + 1; + verticalScrollBar.moveTo(IPoint(dataWidth+1, 0)); + verticalScrollBar.sizeTo(ISize(vsbWidth, canvasHeight)); + } + + LOGV(dataArea.size().asString()); + LOGV(dataArea.parentSize().asString()); + + LOGV(rect().asString()); + LOGV(dataArea.rect().asString()); + + + LOGV(dataArea.size().asString()); + + verticalScrollBar.setScrollableRange(IRange(0, dataHeight-1)); + verticalScrollBar.setVisibleCount(canvasHeight); + verticalPosition = min((int) verticalScrollBar.scrollBoxRange().upperBound(), + verticalPosition); + + verticalScrollBar.setMinScrollIncrement(lineHeight()); + + verticalScrollBar.moveScrollBoxTo(verticalPosition); + LOGV(verticalPosition); + repositionWindow(); + return *this; +} + +Boolean AgHelpView::lineDown(IScrollEvent &event) { + if (event.scrollBarWindow() != &verticalScrollBar) { + return false; + } + moveScrollBox(event); + repositionWindow(); + return true; +} + +Boolean AgHelpView::lineUp(IScrollEvent &event) { + if (event.scrollBarWindow() != &verticalScrollBar) { + return false; + } + moveScrollBox(event); + repositionWindow(); + return true; +} + +Boolean AgHelpView::pageDown(IScrollEvent &event) { + if (event.scrollBarWindow() != &verticalScrollBar) { + return false; + } + moveScrollBox(event); + repositionWindow(); + return true; +} + +Boolean AgHelpView::pageUp(IScrollEvent &event) { + if (event.scrollBarWindow() != &verticalScrollBar) { + return false; + } + moveScrollBox(event); + repositionWindow(); + return true; +} + +Boolean AgHelpView::scrollBoxTrack(IScrollEvent &event) { + moveScrollBox(event); + repositionWindow(); + return true; +} + +Boolean AgHelpView::virtualKeyPress(IKeyboardEvent &event) { + LOGSECTION("AgHelpView::Virtualkeypress"); + switch (event.virtualKey()) { + case IKeyboardEvent::up: { + int topLine = verticalScrollBar.scrollBoxPosition() + - verticalScrollBar.minScrollIncrement(); + verticalScrollBar.moveScrollBoxTo(topLine); + repositionWindow(); + return true; + } + case IKeyboardEvent::down: { + LOGSECTION("Cursor down one line"); + int topLine = verticalScrollBar.scrollBoxPosition() + + verticalScrollBar.minScrollIncrement(); + verticalScrollBar.moveScrollBoxTo(topLine); + repositionWindow(); + return true; + } + case IKeyboardEvent::home: { + if (!event.isCtrlDown()) { + messageBeep(); + return true; + } + } + case IKeyboardEvent::pageUp: { + int topLine = verticalScrollBar.scrollBoxPosition(); + topLine -= verticalScrollBar.visibleCount(); + if (event.isCtrlDown()) { + topLine = 0; + } + verticalScrollBar.moveScrollBoxTo(topLine); + repositionWindow(); + return true; + } + case IKeyboardEvent::end: { + if (!event.isCtrlDown()) { + messageBeep(); + return true; + } + } + case IKeyboardEvent::pageDown: { + int topLine = verticalScrollBar.scrollBoxPosition(); + if (event.isCtrlDown()) { + topLine = verticalScrollBar.scrollBoxRange().upperBound(); + } + topLine += verticalScrollBar.visibleCount(); + verticalScrollBar.moveScrollBoxTo(topLine); + repositionWindow(); + return true; + } + } + return false; +} + +Boolean DrawingArea::command(ICommandEvent &event) { + LOGSECTION("DrawingArea::command"); + unsigned index = event.commandId(); + if (index >= sortedLinks.size()) { + LOGS("bad index"); + messageBeep(); + return true; + } + char *topic = sortedLinks[index].pointer(); + LOGV(topic); + AgString title = AgString::format("Help - %s", topic); + IFrameWindow *helpWindow = AgFrame::windowRegistry.find(title); + if (helpWindow) { + helpWindow->setFocus(); + return true; + } + helpWindow = new AgHelpWindow(topic); + helpWindow->setAutoDeleteObject(); + return true; +} + +DrawingArea &DrawingArea::initPopUp() { + LOGSECTION("DrawingArea::initPopUp"); + LOGV((int) &popUpMenu); + int n = sortedLinks.size(); + int i; + for (i = 0; i < n; i++) { + AgString topic = sortedLinks[i]; + LOGV(i) LCV(topic); + popUpMenu.addText(i, topic.pointer()); + } + return *this; +} + +Boolean DrawingArea::makePopUpMenu(IMenuEvent &event ) { + LOGSECTION("DrawingArea::makePopUpMenu called"); + //if (helpShowing) return true; + //AgHelpHandler::handleEventsFor(frameWindow); + //AgHelpHandler::handleEventsFor(&popUpMenu); + //helpShowing = true; + IPoint where(0,0); + AgHelpWindow *frame = (AgHelpWindow *) frameWindow; + where.setY(frame->helpView.verticalScrollBar.scrollBoxPosition()); + if (rightButtonState == waitingForClick) { + LOGV(event.mousePosition().asString()); + where = event.mousePosition(); + } + //popUpMenu.show(event.mousePosition()); + popUpMenu.show(where); + //AgHelpHandler::stopHandlingEventsFor(frameWindow); + //AgHelpHandler::stopHandlingEventsFor(&popUpMenu); + //helpShowing = false; + + return true; +} + +Boolean DrawingArea::showHelp(IEvent &event) { + LOGSECTION("DrawingArea::showHelp"); + AgString topic; + LOGV((int) frameWindow); + LOGV((int) this); + LOGV((int) event.controlWindow()); + LOGV((int) event.dispatchingWindow()); + + if (event.controlWindow() == frameWindow) { + if (helpShowing) { + return true; + } + helpShowing = true; + //IPoint where = rect().minXMinY(); + IPoint where(0, 0); + AgHelpWindow *frame = (AgHelpWindow *) frameWindow; + where.setY(frame->helpView.verticalScrollBar.scrollBoxPosition()); + LOGV(where.asString()); + popUpMenu.show(where); + helpShowing = false; + return true; + } + return false; +} + + +AgHelpWindow::AgHelpWindow(AgString topic) + : AgFrame(IFrameWindow::systemMenu + | IFrameWindow::maximizeButton + | IFrameWindow::sizingBorder) + , helpView(this, topic) +{ + LOGSECTION("AgHelpWindow::AgHelpWindow"); + AgString titleString = + AgString::format("AnaGram Help - %s", topic.pointer()); + AgString simpleTitle = + AgString::format("Help - %s", topic.pointer()); + + windowTitle.setObjectText(titleString.pointer()); + + registerTitle(simpleTitle.pointer()); + copyTitleText = simpleTitle; + + LOGV(simpleTitle.pointer()); + setClient(&helpView); + + + ISize minSize = frameRectFor(helpView.minimumSize()).size(); + setMinimumSize(minSize); + + LOGV(helpView.minimumSize().asString()); + LOGV(minSize.asString()); + ISize tableSize = helpView.suggestSize(); + + int width = 56*font().avgCharWidth(); + int testWidth = tableSize.width(); + + int titleWidth + = windowTitle.displaySize(windowTitle.text()).width() + + windowTitle.minimumSize().width(); + + LOGV(windowTitle.text()); + LOGV(titleWidth); + LOGV(testWidth); + + if (testWidth < titleWidth) { + testWidth = titleWidth; + } + LOGV(width); + LOGV(testWidth); + + if (testWidth < width/2) { + width = width/2; + } + else if (testWidth > 2*width) { + width = 2*width; + } + else { + width = testWidth; + } + + ISize clientSize(width, tableSize.height()); + IRectangle frameRect(frameRectFor(clientSize)); + LOGV(clientSize.asString()); + LOGV(frameRect.size().asString()); + sizeTo(frameRect.size()); + + positionFrame(); + show(); +} + +AgHelpWindow::~AgHelpWindow() {} + +Boolean AgHelpWindow::showHelp(AgString topic) { + LOGSECTION("AgHelpWindow::showHelp"); + LOGV(topic); + + AgString windowTitle; + + if (topic.exists()) { + windowTitle = AgString::format("Help - %s", topic.pointer()); + } + if (!windowTitle.exists()) { + return false; + } + + LOGV(windowTitle); + + IFrameWindow *helpWindow = AgFrame::windowRegistry.find(windowTitle); + LOGV((int) helpWindow); + if (helpWindow == 0) { + helpWindow = new AgHelpWindow(topic); + helpWindow->setAutoDeleteObject(); + } + helpWindow->show(); + helpWindow->setFocus(); + //BringWindowToTop(helpWindow->handle()); + return true; +} + +Boolean AgHelpWindow::showHelpCentered(AgString topic) { + LOGSECTION("AgHelpWindow::showHelp"); + AgString windowTitle; + + if (topic.exists()) { + windowTitle = AgString::format("Help - %s", topic.pointer()); + } + if (!windowTitle.exists()) { + return false; + } + + LOGV(topic.pointer()); + + IFrameWindow *helpWindow = AgFrame::windowRegistry.find(windowTitle); + LOGV((int) helpWindow); + if (helpWindow == 0) { + helpWindow = new AgHelpWindow(topic); + helpWindow->setAutoDeleteObject(); + } + IPoint where = (IPair) place(IWindow::desktopWindow()->size(), + helpWindow->size(), 11); + helpWindow->moveTo(where); + helpWindow->show().setFocus(); + return true; +} + +