1 // Scintilla source code edit control
\r
2 /** @file CallTip.cxx
\r
3 ** Code for displaying call tips.
\r
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
\r
6 // The License.txt file describes the conditions under which this software may be distributed.
\r
11 #include "Platform.h"
\r
13 #include "Scintilla.h"
\r
14 #include "CallTip.h"
\r
17 #ifdef SCI_NAMESPACE
\r
18 using namespace Scintilla;
\r
21 static const int insetX = 5; // text inset in x from calltip border
\r
22 static const int widthArrow = 14;
\r
24 CallTip::CallTip() {
\r
26 inCallTipMode = false;
\r
27 posStartCallTip = 0;
\r
29 rectUp = PRectangle(0,0,0,0);
\r
30 rectDown = PRectangle(0,0,0,0);
\r
35 useStyleCallTip = false; // for backwards compatibility
\r
38 // proper apple colours for the default
\r
39 colourBG.desired = ColourDesired(0xff, 0xff, 0xc6);
\r
40 colourUnSel.desired = ColourDesired(0, 0, 0);
\r
42 colourBG.desired = ColourDesired(0xff, 0xff, 0xff);
\r
43 colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80);
\r
45 colourSel.desired = ColourDesired(0, 0, 0x80);
\r
46 colourShade.desired = ColourDesired(0, 0, 0);
\r
47 colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0);
\r
50 CallTip::~CallTip() {
\r
57 void CallTip::RefreshColourPalette(Palette &pal, bool want) {
\r
58 pal.WantFind(colourBG, want);
\r
59 pal.WantFind(colourUnSel, want);
\r
60 pal.WantFind(colourSel, want);
\r
61 pal.WantFind(colourShade, want);
\r
62 pal.WantFind(colourLight, want);
\r
65 // Although this test includes 0, we should never see a \0 character.
\r
66 static bool IsArrowCharacter(char ch) {
\r
67 return (ch == 0) || (ch == '\001') || (ch == '\002');
\r
70 // We ignore tabs unless a tab width has been set.
\r
71 bool CallTip::IsTabCharacter(char ch) {
\r
72 return (tabSize > 0) && (ch == '\t');
\r
75 int CallTip::NextTabPos(int x) {
\r
76 if (tabSize > 0) { // paranoia... not called unless this is true
\r
77 x -= insetX; // position relative to text
\r
78 x = (x + tabSize) / tabSize; // tab "number"
\r
79 return tabSize*x + insetX; // position of next tab
\r
81 return x + 1; // arbitrary
\r
85 // Draw a section of the call tip that does not include \n in one colour.
\r
86 // The text may include up to numEnds tabs or arrow characters.
\r
87 void CallTip::DrawChunk(Surface *surface, int &x, const char *s,
\r
88 int posStart, int posEnd, int ytext, PRectangle rcClient,
\r
89 bool highlight, bool draw) {
\r
91 int len = posEnd - posStart;
\r
93 // Divide the text into sections that are all text, or that are
\r
94 // single arrows or single tab characters (if tabSize > 0).
\r
96 const int numEnds = 10;
\r
97 int ends[numEnds + 2];
\r
98 for (int i=0;i<len;i++) {
\r
99 if ((maxEnd < numEnds) &&
\r
100 (IsArrowCharacter(s[i]) || IsTabCharacter(s[i])) ) {
\r
102 ends[maxEnd++] = i;
\r
103 ends[maxEnd++] = i+1;
\r
106 ends[maxEnd++] = len;
\r
109 for (int seg = 0; seg<maxEnd; seg++) {
\r
110 int endSeg = ends[seg];
\r
111 if (endSeg > startSeg) {
\r
112 if (IsArrowCharacter(s[startSeg])) {
\r
113 bool upArrow = s[startSeg] == '\001';
\r
115 rcClient.right = rcClient.left + widthArrow;
\r
117 const int halfWidth = widthArrow / 2 - 3;
\r
118 const int centreX = rcClient.left + widthArrow / 2 - 1;
\r
119 const int centreY = (rcClient.top + rcClient.bottom) / 2;
\r
120 surface->FillRectangle(rcClient, colourBG.allocated);
\r
121 PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1,
\r
122 rcClient.right - 2, rcClient.bottom - 1);
\r
123 surface->FillRectangle(rcClientInner, colourUnSel.allocated);
\r
125 if (upArrow) { // Up arrow
\r
127 Point(centreX - halfWidth, centreY + halfWidth / 2),
\r
128 Point(centreX + halfWidth, centreY + halfWidth / 2),
\r
129 Point(centreX, centreY - halfWidth + halfWidth / 2),
\r
131 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
\r
132 colourBG.allocated, colourBG.allocated);
\r
133 } else { // Down arrow
\r
135 Point(centreX - halfWidth, centreY - halfWidth / 2),
\r
136 Point(centreX + halfWidth, centreY - halfWidth / 2),
\r
137 Point(centreX, centreY + halfWidth - halfWidth / 2),
\r
139 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
\r
140 colourBG.allocated, colourBG.allocated);
\r
143 xEnd = rcClient.right;
\r
148 rectDown = rcClient;
\r
150 } else if (IsTabCharacter(s[startSeg])) {
\r
151 xEnd = NextTabPos(x);
\r
153 xEnd = x + surface->WidthText(font, s + startSeg, endSeg - startSeg);
\r
156 rcClient.right = xEnd;
\r
157 surface->DrawTextTransparent(rcClient, font, ytext,
\r
158 s+startSeg, endSeg - startSeg,
\r
159 highlight ? colourSel.allocated : colourUnSel.allocated);
\r
168 int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
\r
169 PRectangle rcClientPos = wCallTip.GetClientPosition();
\r
170 PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
\r
171 rcClientPos.bottom - rcClientPos.top);
\r
172 PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
\r
174 // To make a nice small call tip window, it is only sized to fit most normal characters without accents
\r
175 int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);
\r
177 // For each line...
\r
178 // Draw the definition in three parts: before highlight, highlighted, after highlight
\r
179 int ytext = rcClient.top + ascent + 1;
\r
180 rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
\r
181 char *chunkVal = val;
\r
182 bool moreChunks = true;
\r
185 while (moreChunks) {
\r
186 char *chunkEnd = strchr(chunkVal, '\n');
\r
187 if (chunkEnd == NULL) {
\r
188 chunkEnd = chunkVal + strlen(chunkVal);
\r
189 moreChunks = false;
\r
191 int chunkOffset = chunkVal - val;
\r
192 int chunkLength = chunkEnd - chunkVal;
\r
193 int chunkEndOffset = chunkOffset + chunkLength;
\r
194 int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
\r
195 thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
\r
196 thisStartHighlight -= chunkOffset;
\r
197 int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
\r
198 thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
\r
199 thisEndHighlight -= chunkOffset;
\r
200 rcClient.top = ytext - ascent - 1;
\r
202 int x = insetX; // start each line at this inset
\r
204 DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight,
\r
205 ytext, rcClient, false, draw);
\r
206 DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight,
\r
207 ytext, rcClient, true, draw);
\r
208 DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength,
\r
209 ytext, rcClient, false, draw);
\r
211 chunkVal = chunkEnd + 1;
\r
212 ytext += lineHeight;
\r
213 rcClient.bottom += lineHeight;
\r
214 maxWidth = Platform::Maximum(maxWidth, x);
\r
219 void CallTip::PaintCT(Surface *surfaceWindow) {
\r
222 PRectangle rcClientPos = wCallTip.GetClientPosition();
\r
223 PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
\r
224 rcClientPos.bottom - rcClientPos.top);
\r
225 PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
\r
227 surfaceWindow->FillRectangle(rcClient, colourBG.allocated);
\r
229 offsetMain = insetX; // initial alignment assuming no arrows
\r
230 PaintContents(surfaceWindow, true);
\r
233 // OSX doesn't put borders on "help tags"
\r
234 // Draw a raised border around the edges of the window
\r
235 surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
\r
236 surfaceWindow->PenColour(colourShade.allocated);
\r
237 surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
\r
238 surfaceWindow->LineTo(rcClientSize.right - 1, 0);
\r
239 surfaceWindow->PenColour(colourLight.allocated);
\r
240 surfaceWindow->LineTo(0, 0);
\r
241 surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
\r
245 void CallTip::MouseClick(Point pt) {
\r
247 if (rectUp.Contains(pt))
\r
249 if (rectDown.Contains(pt))
\r
253 PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn,
\r
254 const char *faceName, int size,
\r
255 int codePage_, int characterSet, Window &wParent) {
\r
259 val = new char[strlen(defn) + 1];
\r
261 return PRectangle();
\r
263 codePage = codePage_;
\r
264 Surface *surfaceMeasure = Surface::Allocate();
\r
265 if (!surfaceMeasure)
\r
266 return PRectangle();
\r
267 surfaceMeasure->Init(wParent.GetID());
\r
268 surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
\r
269 surfaceMeasure->SetDBCSMode(codePage);
\r
270 startHighlight = 0;
\r
272 inCallTipMode = true;
\r
273 posStartCallTip = pos;
\r
274 int deviceHeight = surfaceMeasure->DeviceHeightFont(size);
\r
275 font.Create(faceName, characterSet, deviceHeight, false, false);
\r
276 // Look for multiple lines in the text
\r
277 // Only support \n here - simply means container must avoid \r!
\r
279 const char *newline;
\r
280 const char *look = val;
\r
281 rectUp = PRectangle(0,0,0,0);
\r
282 rectDown = PRectangle(0,0,0,0);
\r
283 offsetMain = insetX; // changed to right edge of any arrows
\r
284 int width = PaintContents(surfaceMeasure, false) + insetX;
\r
285 while ((newline = strchr(look, '\n')) != NULL) {
\r
286 look = newline + 1;
\r
289 lineHeight = surfaceMeasure->Height(font);
\r
291 // Extra line for border and an empty line at top and bottom. The returned
\r
292 // rectangle is aligned to the right edge of the last arrow encountered in
\r
293 // the tip text, else to the tip text left edge.
\r
294 int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2;
\r
295 delete surfaceMeasure;
\r
296 return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height);
\r
299 void CallTip::CallTipCancel() {
\r
300 inCallTipMode = false;
\r
301 if (wCallTip.Created()) {
\r
302 wCallTip.Destroy();
\r
306 void CallTip::SetHighlight(int start, int end) {
\r
307 // Avoid flashing by checking something has really changed
\r
308 if ((start != startHighlight) || (end != endHighlight)) {
\r
309 startHighlight = start;
\r
310 endHighlight = end;
\r
311 if (wCallTip.Created()) {
\r
312 wCallTip.InvalidateAll();
\r
317 // Set the tab size (sizes > 0 enable the use of tabs). This also enables the
\r
318 // use of the STYLE_CALLTIP.
\r
319 void CallTip::SetTabSize(int tabSz) {
\r
321 useStyleCallTip = true;
\r
324 // It might be better to have two access functions for this and to use
\r
325 // them for all settings of colours.
\r
326 void CallTip::SetForeBack(const ColourPair &fore, const ColourPair &back) {
\r
328 colourUnSel = fore;
\r