Mercurial > ~dholland > hg > ag > index.cgi
view anagram/vaclgui/helpview.cpp @ 15:f5acaf0c8a29
Don't cast through "volatile int". Causes a gcc warning nowadays.
XXX: should put something else back here to frighten the optimizer
author | David A. Holland |
---|---|
date | Tue, 31 May 2022 01:00:55 -0400 |
parents | 13d2b8934445 |
children |
line wrap: on
line source
/* * 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; }