Optimize %path% parser code
[TortoiseGit.git] / ext / scintilla / src / CallTip.cxx
blob1729d6827f5bb8638d742935d511ae5c8df8a1d0
1 // Scintilla source code edit control
2 /** @file CallTip.cxx
3 ** Code for displaying call tips.
4 **/
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
11 #include "Platform.h"
13 #include "Scintilla.h"
14 #include "CallTip.h"
15 #include <stdio.h>
17 #ifdef SCI_NAMESPACE
18 using namespace Scintilla;
19 #endif
21 static const int insetX = 5; // text inset in x from calltip border
22 static const int widthArrow = 14;
24 CallTip::CallTip() {
25 wCallTip = 0;
26 inCallTipMode = false;
27 posStartCallTip = 0;
28 val = 0;
29 rectUp = PRectangle(0,0,0,0);
30 rectDown = PRectangle(0,0,0,0);
31 lineHeight = 1;
32 offsetMain = 0;
33 startHighlight = 0;
34 endHighlight = 0;
35 tabSize = 0;
36 above = false;
37 useStyleCallTip = false; // for backwards compatibility
39 #ifdef __APPLE__
40 // proper apple colours for the default
41 colourBG = ColourDesired(0xff, 0xff, 0xc6);
42 colourUnSel = ColourDesired(0, 0, 0);
43 #else
44 colourBG = ColourDesired(0xff, 0xff, 0xff);
45 colourUnSel = ColourDesired(0x80, 0x80, 0x80);
46 #endif
47 colourSel = ColourDesired(0, 0, 0x80);
48 colourShade = ColourDesired(0, 0, 0);
49 colourLight = ColourDesired(0xc0, 0xc0, 0xc0);
50 codePage = 0;
51 clickPlace = 0;
54 CallTip::~CallTip() {
55 font.Release();
56 wCallTip.Destroy();
57 delete []val;
58 val = 0;
61 // Although this test includes 0, we should never see a \0 character.
62 static bool IsArrowCharacter(char ch) {
63 return (ch == 0) || (ch == '\001') || (ch == '\002');
66 // We ignore tabs unless a tab width has been set.
67 bool CallTip::IsTabCharacter(char ch) const {
68 return (tabSize > 0) && (ch == '\t');
71 int CallTip::NextTabPos(int x) {
72 if (tabSize > 0) { // paranoia... not called unless this is true
73 x -= insetX; // position relative to text
74 x = (x + tabSize) / tabSize; // tab "number"
75 return tabSize*x + insetX; // position of next tab
76 } else {
77 return x + 1; // arbitrary
81 // Draw a section of the call tip that does not include \n in one colour.
82 // The text may include up to numEnds tabs or arrow characters.
83 void CallTip::DrawChunk(Surface *surface, int &x, const char *s,
84 int posStart, int posEnd, int ytext, PRectangle rcClient,
85 bool highlight, bool draw) {
86 s += posStart;
87 int len = posEnd - posStart;
89 // Divide the text into sections that are all text, or that are
90 // single arrows or single tab characters (if tabSize > 0).
91 int maxEnd = 0;
92 const int numEnds = 10;
93 int ends[numEnds + 2];
94 for (int i=0; i<len; i++) {
95 if ((maxEnd < numEnds) &&
96 (IsArrowCharacter(s[i]) || IsTabCharacter(s[i]))) {
97 if (i > 0)
98 ends[maxEnd++] = i;
99 ends[maxEnd++] = i+1;
102 ends[maxEnd++] = len;
103 int startSeg = 0;
104 int xEnd;
105 for (int seg = 0; seg<maxEnd; seg++) {
106 int endSeg = ends[seg];
107 if (endSeg > startSeg) {
108 if (IsArrowCharacter(s[startSeg])) {
109 bool upArrow = s[startSeg] == '\001';
110 rcClient.left = x;
111 rcClient.right = rcClient.left + widthArrow;
112 if (draw) {
113 const int halfWidth = widthArrow / 2 - 3;
114 const int centreX = rcClient.left + widthArrow / 2 - 1;
115 const int centreY = (rcClient.top + rcClient.bottom) / 2;
116 surface->FillRectangle(rcClient, colourBG);
117 PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1,
118 rcClient.right - 2, rcClient.bottom - 1);
119 surface->FillRectangle(rcClientInner, colourUnSel);
121 if (upArrow) { // Up arrow
122 Point pts[] = {
123 Point(centreX - halfWidth, centreY + halfWidth / 2),
124 Point(centreX + halfWidth, centreY + halfWidth / 2),
125 Point(centreX, centreY - halfWidth + halfWidth / 2),
127 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
128 colourBG, colourBG);
129 } else { // Down arrow
130 Point pts[] = {
131 Point(centreX - halfWidth, centreY - halfWidth / 2),
132 Point(centreX + halfWidth, centreY - halfWidth / 2),
133 Point(centreX, centreY + halfWidth - halfWidth / 2),
135 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
136 colourBG, colourBG);
139 xEnd = rcClient.right;
140 offsetMain = xEnd;
141 if (upArrow) {
142 rectUp = rcClient;
143 } else {
144 rectDown = rcClient;
146 } else if (IsTabCharacter(s[startSeg])) {
147 xEnd = NextTabPos(x);
148 } else {
149 xEnd = x + surface->WidthText(font, s + startSeg, endSeg - startSeg);
150 if (draw) {
151 rcClient.left = x;
152 rcClient.right = xEnd;
153 surface->DrawTextTransparent(rcClient, font, ytext,
154 s+startSeg, endSeg - startSeg,
155 highlight ? colourSel : colourUnSel);
158 x = xEnd;
159 startSeg = endSeg;
164 int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
165 PRectangle rcClientPos = wCallTip.GetClientPosition();
166 PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
167 rcClientPos.bottom - rcClientPos.top);
168 PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
170 // To make a nice small call tip window, it is only sized to fit most normal characters without accents
171 int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);
173 // For each line...
174 // Draw the definition in three parts: before highlight, highlighted, after highlight
175 int ytext = rcClient.top + ascent + 1;
176 rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
177 char *chunkVal = val;
178 bool moreChunks = true;
179 int maxWidth = 0;
181 while (moreChunks) {
182 char *chunkEnd = strchr(chunkVal, '\n');
183 if (chunkEnd == NULL) {
184 chunkEnd = chunkVal + strlen(chunkVal);
185 moreChunks = false;
187 int chunkOffset = chunkVal - val;
188 int chunkLength = chunkEnd - chunkVal;
189 int chunkEndOffset = chunkOffset + chunkLength;
190 int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
191 thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
192 thisStartHighlight -= chunkOffset;
193 int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
194 thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
195 thisEndHighlight -= chunkOffset;
196 rcClient.top = ytext - ascent - 1;
198 int x = insetX; // start each line at this inset
200 DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight,
201 ytext, rcClient, false, draw);
202 DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight,
203 ytext, rcClient, true, draw);
204 DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength,
205 ytext, rcClient, false, draw);
207 chunkVal = chunkEnd + 1;
208 ytext += lineHeight;
209 rcClient.bottom += lineHeight;
210 maxWidth = Platform::Maximum(maxWidth, x);
212 return maxWidth;
215 void CallTip::PaintCT(Surface *surfaceWindow) {
216 if (!val)
217 return;
218 PRectangle rcClientPos = wCallTip.GetClientPosition();
219 PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
220 rcClientPos.bottom - rcClientPos.top);
221 PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
223 surfaceWindow->FillRectangle(rcClient, colourBG);
225 offsetMain = insetX; // initial alignment assuming no arrows
226 PaintContents(surfaceWindow, true);
228 #ifndef __APPLE__
229 // OSX doesn't put borders on "help tags"
230 // Draw a raised border around the edges of the window
231 surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
232 surfaceWindow->PenColour(colourShade);
233 surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
234 surfaceWindow->LineTo(rcClientSize.right - 1, 0);
235 surfaceWindow->PenColour(colourLight);
236 surfaceWindow->LineTo(0, 0);
237 surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
238 #endif
241 void CallTip::MouseClick(Point pt) {
242 clickPlace = 0;
243 if (rectUp.Contains(pt))
244 clickPlace = 1;
245 if (rectDown.Contains(pt))
246 clickPlace = 2;
249 PRectangle CallTip::CallTipStart(int pos, Point pt, int textHeight, const char *defn,
250 const char *faceName, int size,
251 int codePage_, int characterSet,
252 int technology, Window &wParent) {
253 clickPlace = 0;
254 delete []val;
255 val = 0;
256 val = new char[strlen(defn) + 1];
257 strcpy(val, defn);
258 codePage = codePage_;
259 Surface *surfaceMeasure = Surface::Allocate(technology);
260 if (!surfaceMeasure)
261 return PRectangle();
262 surfaceMeasure->Init(wParent.GetID());
263 surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
264 surfaceMeasure->SetDBCSMode(codePage);
265 startHighlight = 0;
266 endHighlight = 0;
267 inCallTipMode = true;
268 posStartCallTip = pos;
269 int deviceHeight = surfaceMeasure->DeviceHeightFont(size);
270 FontParameters fp(faceName, deviceHeight / SC_FONT_SIZE_MULTIPLIER, SC_WEIGHT_NORMAL, false, 0, technology, characterSet);
271 font.Create(fp);
272 // Look for multiple lines in the text
273 // Only support \n here - simply means container must avoid \r!
274 int numLines = 1;
275 const char *newline;
276 const char *look = val;
277 rectUp = PRectangle(0,0,0,0);
278 rectDown = PRectangle(0,0,0,0);
279 offsetMain = insetX; // changed to right edge of any arrows
280 int width = PaintContents(surfaceMeasure, false) + insetX;
281 while ((newline = strchr(look, '\n')) != NULL) {
282 look = newline + 1;
283 numLines++;
285 lineHeight = surfaceMeasure->Height(font);
287 // Extra line for border and an empty line at top and bottom. The returned
288 // rectangle is aligned to the right edge of the last arrow encountered in
289 // the tip text, else to the tip text left edge.
290 int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2;
291 delete surfaceMeasure;
292 if (above) {
293 return PRectangle(pt.x - offsetMain, pt.y - 1 - height, pt.x + width - offsetMain, pt.y - 1);
294 } else {
295 return PRectangle(pt.x - offsetMain, pt.y + 1 + textHeight, pt.x + width - offsetMain, pt.y + 1 + textHeight + height);
299 void CallTip::CallTipCancel() {
300 inCallTipMode = false;
301 if (wCallTip.Created()) {
302 wCallTip.Destroy();
306 void CallTip::SetHighlight(int start, int end) {
307 // Avoid flashing by checking something has really changed
308 if ((start != startHighlight) || (end != endHighlight)) {
309 startHighlight = start;
310 endHighlight = end;
311 if (wCallTip.Created()) {
312 wCallTip.InvalidateAll();
317 // Set the tab size (sizes > 0 enable the use of tabs). This also enables the
318 // use of the STYLE_CALLTIP.
319 void CallTip::SetTabSize(int tabSz) {
320 tabSize = tabSz;
321 useStyleCallTip = true;
324 // Set the calltip position, below the text by default or if above is false
325 // else above the text.
326 void CallTip::SetPosition(bool aboveText) {
327 above = aboveText;
330 // It might be better to have two access functions for this and to use
331 // them for all settings of colours.
332 void CallTip::SetForeBack(const ColourDesired &fore, const ColourDesired &back) {
333 colourBG = back;
334 colourUnSel = fore;