diff options
Diffstat (limited to 'src/CallTip.cpp')
-rwxr-xr-x | src/CallTip.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/CallTip.cpp b/src/CallTip.cpp new file mode 100755 index 0000000..f4bc5f8 --- /dev/null +++ b/src/CallTip.cpp @@ -0,0 +1,314 @@ +// Scintilla source code edit control +/** @file CallTip.cxx + ** Code for displaying call tips. + **/ +// Copyright 1998-2001 by Neil Hodgson <[email protected]> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> + +#include "Platform.h" + +#include "Scintilla.h" +#include "CallTip.h" + +static const int insetX = 5; // text inset in x from calltip border +static const int widthArrow = 14; + +CallTip::CallTip() { + wCallTip = 0; + inCallTipMode = false; + posStartCallTip = 0; + val = 0; + rectUp = PRectangle(0,0,0,0); + rectDown = PRectangle(0,0,0,0); + lineHeight = 1; + startHighlight = 0; + endHighlight = 0; + tabSize = 0; + useStyleCallTip = false; // for backwards compatibility + + colourBG.desired = ColourDesired(0xff, 0xff, 0xff); + colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80); + colourSel.desired = ColourDesired(0, 0, 0x80); + colourShade.desired = ColourDesired(0, 0, 0); + colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0); +} + +CallTip::~CallTip() { + font.Release(); + wCallTip.Destroy(); + delete []val; + val = 0; +} + +void CallTip::RefreshColourPalette(Palette &pal, bool want) { + pal.WantFind(colourBG, want); + pal.WantFind(colourUnSel, want); + pal.WantFind(colourSel, want); + pal.WantFind(colourShade, want); + pal.WantFind(colourLight, want); +} + +// Although this test includes 0, we should never see a \0 character. +static bool IsArrowCharacter(char ch) { + return (ch == 0) || (ch == '\001') || (ch == '\002'); +} + +// We ignore tabs unless a tab width has been set. +bool CallTip::IsTabCharacter(char ch) { + return (tabSize > 0) && (ch == '\t'); +} + +int CallTip::NextTabPos(int x) { + if (tabSize > 0) { // paranoia... not called unless this is true + x -= insetX; // position relative to text + x = (x + tabSize) / tabSize; // tab "number" + return tabSize*x + insetX; // position of next tab + } else { + return x + 1; // arbitrary + } +} + +// Draw a section of the call tip that does not include \n in one colour. +// The text may include up to numEnds tabs or arrow characters. +void CallTip::DrawChunk(Surface *surface, int &x, const char *s, + int posStart, int posEnd, int ytext, PRectangle rcClient, + bool highlight, bool draw) { + s += posStart; + int len = posEnd - posStart; + + // Divide the text into sections that are all text, or that are + // single arrows or single tab characters (if tabSize > 0). + int maxEnd = 0; + const int numEnds = 10; + int ends[numEnds + 2]; + for (int i=0;i<len;i++) { + if ((maxEnd < numEnds) && + (IsArrowCharacter(s[i]) || IsTabCharacter(s[i])) ) { + if (i > 0) + ends[maxEnd++] = i; + ends[maxEnd++] = i+1; + } + } + ends[maxEnd++] = len; + int startSeg = 0; + int xEnd; + for (int seg = 0; seg<maxEnd; seg++) { + int endSeg = ends[seg]; + if (endSeg > startSeg) { + if (IsArrowCharacter(s[startSeg])) { + bool upArrow = s[startSeg] == '\001'; + rcClient.left = x; + rcClient.right = rcClient.left + widthArrow; + if (draw) { + const int halfWidth = widthArrow / 2 - 3; + const int centreX = rcClient.left + widthArrow / 2 - 1; + const int centreY = (rcClient.top + rcClient.bottom) / 2; + surface->FillRectangle(rcClient, colourBG.allocated); + PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1, + rcClient.right - 2, rcClient.bottom - 1); + surface->FillRectangle(rcClientInner, colourUnSel.allocated); + + if (upArrow) { // Up arrow + Point pts[] = { + Point(centreX - halfWidth, centreY + halfWidth / 2), + Point(centreX + halfWidth, centreY + halfWidth / 2), + Point(centreX, centreY - halfWidth + halfWidth / 2), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + colourBG.allocated, colourBG.allocated); + } else { // Down arrow + Point pts[] = { + Point(centreX - halfWidth, centreY - halfWidth / 2), + Point(centreX + halfWidth, centreY - halfWidth / 2), + Point(centreX, centreY + halfWidth - halfWidth / 2), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + colourBG.allocated, colourBG.allocated); + } + } + xEnd = rcClient.right; + offsetMain = xEnd; + if (upArrow) { + rectUp = rcClient; + } else { + rectDown = rcClient; + } + } else if (IsTabCharacter(s[startSeg])) { + xEnd = NextTabPos(x); + } else { + xEnd = x + surface->WidthText(font, s + startSeg, endSeg - startSeg); + if (draw) { + rcClient.left = x; + rcClient.right = xEnd; + surface->DrawTextTransparent(rcClient, font, ytext, + s+startSeg, endSeg - startSeg, + highlight ? colourSel.allocated : colourUnSel.allocated); + } + } + x = xEnd; + startSeg = endSeg; + } + } +} + +int CallTip::PaintContents(Surface *surfaceWindow, bool draw) { + PRectangle rcClientPos = wCallTip.GetClientPosition(); + PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left, + rcClientPos.bottom - rcClientPos.top); + PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1); + + // To make a nice small call tip window, it is only sized to fit most normal characters without accents + int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font); + + // For each line... + // Draw the definition in three parts: before highlight, highlighted, after highlight + int ytext = rcClient.top + ascent + 1; + rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1; + char *chunkVal = val; + bool moreChunks = true; + int maxWidth = 0; + while (moreChunks) { + char *chunkEnd = strchr(chunkVal, '\n'); + if (chunkEnd == NULL) { + chunkEnd = chunkVal + strlen(chunkVal); + moreChunks = false; + } + int chunkOffset = chunkVal - val; + int chunkLength = chunkEnd - chunkVal; + int chunkEndOffset = chunkOffset + chunkLength; + int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset); + thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset); + thisStartHighlight -= chunkOffset; + int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset); + thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset); + thisEndHighlight -= chunkOffset; + rcClient.top = ytext - ascent - 1; + + int x = insetX; // start each line at this inset + + DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight, + ytext, rcClient, false, draw); + DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight, + ytext, rcClient, true, draw); + DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength, + ytext, rcClient, false, draw); + + chunkVal = chunkEnd + 1; + ytext += lineHeight; + rcClient.bottom += lineHeight; + maxWidth = Platform::Maximum(maxWidth, x); + } + return maxWidth; +} + +void CallTip::PaintCT(Surface *surfaceWindow) { + if (!val) + return; + PRectangle rcClientPos = wCallTip.GetClientPosition(); + PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left, + rcClientPos.bottom - rcClientPos.top); + PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1); + + surfaceWindow->FillRectangle(rcClient, colourBG.allocated); + + offsetMain = insetX; // initial alignment assuming no arrows + PaintContents(surfaceWindow, true); + + // Draw a raised border around the edges of the window + surfaceWindow->MoveTo(0, rcClientSize.bottom - 1); + surfaceWindow->PenColour(colourShade.allocated); + surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1); + surfaceWindow->LineTo(rcClientSize.right - 1, 0); + surfaceWindow->PenColour(colourLight.allocated); + surfaceWindow->LineTo(0, 0); + surfaceWindow->LineTo(0, rcClientSize.bottom - 1); +} + +void CallTip::MouseClick(Point pt) { + clickPlace = 0; + if (rectUp.Contains(pt)) + clickPlace = 1; + if (rectDown.Contains(pt)) + clickPlace = 2; +} + +PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn, + const char *faceName, int size, + int codePage_, int characterSet, Window &wParent) { + clickPlace = 0; + if (val) + delete []val; + val = new char[strlen(defn) + 1]; + if (!val) + return PRectangle(); + strcpy(val, defn); + codePage = codePage_; + Surface *surfaceMeasure = Surface::Allocate(); + if (!surfaceMeasure) + return PRectangle(); + surfaceMeasure->Init(wParent.GetID()); + surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage); + surfaceMeasure->SetDBCSMode(codePage); + startHighlight = 0; + endHighlight = 0; + inCallTipMode = true; + posStartCallTip = pos; + int deviceHeight = surfaceMeasure->DeviceHeightFont(size); + font.Create(faceName, characterSet, deviceHeight, false, false); + // Look for multiple lines in the text + // Only support \n here - simply means container must avoid \r! + int numLines = 1; + const char *newline; + const char *look = val; + rectUp = PRectangle(0,0,0,0); + rectDown = PRectangle(0,0,0,0); + offsetMain = insetX; // changed to right edge of any arrows + int width = PaintContents(surfaceMeasure, false) + insetX; + while ((newline = strchr(look, '\n')) != NULL) { + look = newline + 1; + numLines++; + } + lineHeight = surfaceMeasure->Height(font); + + // Extra line for border and an empty line at top and bottom. The returned + // rectangle is aligned to the right edge of the last arrow encountered in + // the tip text, else to the tip text left edge. + int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2; + delete surfaceMeasure; + return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height); +} + +void CallTip::CallTipCancel() { + inCallTipMode = false; + if (wCallTip.Created()) { + wCallTip.Destroy(); + } +} + +void CallTip::SetHighlight(int start, int end) { + // Avoid flashing by checking something has really changed + if ((start != startHighlight) || (end != endHighlight)) { + startHighlight = start; + endHighlight = end; + if (wCallTip.Created()) { + wCallTip.InvalidateAll(); + } + } +} + +// Set the tab size (sizes > 0 enable the use of tabs). This also enables the +// use of the STYLE_CALLTIP. +void CallTip::SetTabSize(int tabSz) { + tabSize = tabSz; + useStyleCallTip = true; +} + +// It might be better to have two access functions for this and to use +// them for all settings of colours. +void CallTip::SetForeBack(const ColourPair &fore, const ColourPair &back) { + colourBG = back; + colourUnSel = fore; +} |