]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/CallTip.cxx
f4bc5f83c42e0c93e00db9c0ea4ddd4f16e7e788
[wxWidgets.git] / src / stc / scintilla / src / CallTip.cxx
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.
7
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "Platform.h"
12
13 #include "Scintilla.h"
14 #include "CallTip.h"
15
16 static const int insetX = 5; // text inset in x from calltip border
17 static const int widthArrow = 14;
18
19 CallTip::CallTip() {
20 wCallTip = 0;
21 inCallTipMode = false;
22 posStartCallTip = 0;
23 val = 0;
24 rectUp = PRectangle(0,0,0,0);
25 rectDown = PRectangle(0,0,0,0);
26 lineHeight = 1;
27 startHighlight = 0;
28 endHighlight = 0;
29 tabSize = 0;
30 useStyleCallTip = false; // for backwards compatibility
31
32 colourBG.desired = ColourDesired(0xff, 0xff, 0xff);
33 colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80);
34 colourSel.desired = ColourDesired(0, 0, 0x80);
35 colourShade.desired = ColourDesired(0, 0, 0);
36 colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0);
37 }
38
39 CallTip::~CallTip() {
40 font.Release();
41 wCallTip.Destroy();
42 delete []val;
43 val = 0;
44 }
45
46 void CallTip::RefreshColourPalette(Palette &pal, bool want) {
47 pal.WantFind(colourBG, want);
48 pal.WantFind(colourUnSel, want);
49 pal.WantFind(colourSel, want);
50 pal.WantFind(colourShade, want);
51 pal.WantFind(colourLight, want);
52 }
53
54 // Although this test includes 0, we should never see a \0 character.
55 static bool IsArrowCharacter(char ch) {
56 return (ch == 0) || (ch == '\001') || (ch == '\002');
57 }
58
59 // We ignore tabs unless a tab width has been set.
60 bool CallTip::IsTabCharacter(char ch) {
61 return (tabSize > 0) && (ch == '\t');
62 }
63
64 int CallTip::NextTabPos(int x) {
65 if (tabSize > 0) { // paranoia... not called unless this is true
66 x -= insetX; // position relative to text
67 x = (x + tabSize) / tabSize; // tab "number"
68 return tabSize*x + insetX; // position of next tab
69 } else {
70 return x + 1; // arbitrary
71 }
72 }
73
74 // Draw a section of the call tip that does not include \n in one colour.
75 // The text may include up to numEnds tabs or arrow characters.
76 void CallTip::DrawChunk(Surface *surface, int &x, const char *s,
77 int posStart, int posEnd, int ytext, PRectangle rcClient,
78 bool highlight, bool draw) {
79 s += posStart;
80 int len = posEnd - posStart;
81
82 // Divide the text into sections that are all text, or that are
83 // single arrows or single tab characters (if tabSize > 0).
84 int maxEnd = 0;
85 const int numEnds = 10;
86 int ends[numEnds + 2];
87 for (int i=0;i<len;i++) {
88 if ((maxEnd < numEnds) &&
89 (IsArrowCharacter(s[i]) || IsTabCharacter(s[i])) ) {
90 if (i > 0)
91 ends[maxEnd++] = i;
92 ends[maxEnd++] = i+1;
93 }
94 }
95 ends[maxEnd++] = len;
96 int startSeg = 0;
97 int xEnd;
98 for (int seg = 0; seg<maxEnd; seg++) {
99 int endSeg = ends[seg];
100 if (endSeg > startSeg) {
101 if (IsArrowCharacter(s[startSeg])) {
102 bool upArrow = s[startSeg] == '\001';
103 rcClient.left = x;
104 rcClient.right = rcClient.left + widthArrow;
105 if (draw) {
106 const int halfWidth = widthArrow / 2 - 3;
107 const int centreX = rcClient.left + widthArrow / 2 - 1;
108 const int centreY = (rcClient.top + rcClient.bottom) / 2;
109 surface->FillRectangle(rcClient, colourBG.allocated);
110 PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1,
111 rcClient.right - 2, rcClient.bottom - 1);
112 surface->FillRectangle(rcClientInner, colourUnSel.allocated);
113
114 if (upArrow) { // Up arrow
115 Point pts[] = {
116 Point(centreX - halfWidth, centreY + halfWidth / 2),
117 Point(centreX + halfWidth, centreY + halfWidth / 2),
118 Point(centreX, centreY - halfWidth + halfWidth / 2),
119 };
120 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
121 colourBG.allocated, colourBG.allocated);
122 } else { // Down arrow
123 Point pts[] = {
124 Point(centreX - halfWidth, centreY - halfWidth / 2),
125 Point(centreX + halfWidth, centreY - halfWidth / 2),
126 Point(centreX, centreY + halfWidth - halfWidth / 2),
127 };
128 surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
129 colourBG.allocated, colourBG.allocated);
130 }
131 }
132 xEnd = rcClient.right;
133 offsetMain = xEnd;
134 if (upArrow) {
135 rectUp = rcClient;
136 } else {
137 rectDown = rcClient;
138 }
139 } else if (IsTabCharacter(s[startSeg])) {
140 xEnd = NextTabPos(x);
141 } else {
142 xEnd = x + surface->WidthText(font, s + startSeg, endSeg - startSeg);
143 if (draw) {
144 rcClient.left = x;
145 rcClient.right = xEnd;
146 surface->DrawTextTransparent(rcClient, font, ytext,
147 s+startSeg, endSeg - startSeg,
148 highlight ? colourSel.allocated : colourUnSel.allocated);
149 }
150 }
151 x = xEnd;
152 startSeg = endSeg;
153 }
154 }
155 }
156
157 int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
158 PRectangle rcClientPos = wCallTip.GetClientPosition();
159 PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
160 rcClientPos.bottom - rcClientPos.top);
161 PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
162
163 // To make a nice small call tip window, it is only sized to fit most normal characters without accents
164 int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);
165
166 // For each line...
167 // Draw the definition in three parts: before highlight, highlighted, after highlight
168 int ytext = rcClient.top + ascent + 1;
169 rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
170 char *chunkVal = val;
171 bool moreChunks = true;
172 int maxWidth = 0;
173 while (moreChunks) {
174 char *chunkEnd = strchr(chunkVal, '\n');
175 if (chunkEnd == NULL) {
176 chunkEnd = chunkVal + strlen(chunkVal);
177 moreChunks = false;
178 }
179 int chunkOffset = chunkVal - val;
180 int chunkLength = chunkEnd - chunkVal;
181 int chunkEndOffset = chunkOffset + chunkLength;
182 int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
183 thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
184 thisStartHighlight -= chunkOffset;
185 int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
186 thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
187 thisEndHighlight -= chunkOffset;
188 rcClient.top = ytext - ascent - 1;
189
190 int x = insetX; // start each line at this inset
191
192 DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight,
193 ytext, rcClient, false, draw);
194 DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight,
195 ytext, rcClient, true, draw);
196 DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength,
197 ytext, rcClient, false, draw);
198
199 chunkVal = chunkEnd + 1;
200 ytext += lineHeight;
201 rcClient.bottom += lineHeight;
202 maxWidth = Platform::Maximum(maxWidth, x);
203 }
204 return maxWidth;
205 }
206
207 void CallTip::PaintCT(Surface *surfaceWindow) {
208 if (!val)
209 return;
210 PRectangle rcClientPos = wCallTip.GetClientPosition();
211 PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
212 rcClientPos.bottom - rcClientPos.top);
213 PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
214
215 surfaceWindow->FillRectangle(rcClient, colourBG.allocated);
216
217 offsetMain = insetX; // initial alignment assuming no arrows
218 PaintContents(surfaceWindow, true);
219
220 // Draw a raised border around the edges of the window
221 surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
222 surfaceWindow->PenColour(colourShade.allocated);
223 surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
224 surfaceWindow->LineTo(rcClientSize.right - 1, 0);
225 surfaceWindow->PenColour(colourLight.allocated);
226 surfaceWindow->LineTo(0, 0);
227 surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
228 }
229
230 void CallTip::MouseClick(Point pt) {
231 clickPlace = 0;
232 if (rectUp.Contains(pt))
233 clickPlace = 1;
234 if (rectDown.Contains(pt))
235 clickPlace = 2;
236 }
237
238 PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn,
239 const char *faceName, int size,
240 int codePage_, int characterSet, Window &wParent) {
241 clickPlace = 0;
242 if (val)
243 delete []val;
244 val = new char[strlen(defn) + 1];
245 if (!val)
246 return PRectangle();
247 strcpy(val, defn);
248 codePage = codePage_;
249 Surface *surfaceMeasure = Surface::Allocate();
250 if (!surfaceMeasure)
251 return PRectangle();
252 surfaceMeasure->Init(wParent.GetID());
253 surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
254 surfaceMeasure->SetDBCSMode(codePage);
255 startHighlight = 0;
256 endHighlight = 0;
257 inCallTipMode = true;
258 posStartCallTip = pos;
259 int deviceHeight = surfaceMeasure->DeviceHeightFont(size);
260 font.Create(faceName, characterSet, deviceHeight, false, false);
261 // Look for multiple lines in the text
262 // Only support \n here - simply means container must avoid \r!
263 int numLines = 1;
264 const char *newline;
265 const char *look = val;
266 rectUp = PRectangle(0,0,0,0);
267 rectDown = PRectangle(0,0,0,0);
268 offsetMain = insetX; // changed to right edge of any arrows
269 int width = PaintContents(surfaceMeasure, false) + insetX;
270 while ((newline = strchr(look, '\n')) != NULL) {
271 look = newline + 1;
272 numLines++;
273 }
274 lineHeight = surfaceMeasure->Height(font);
275
276 // Extra line for border and an empty line at top and bottom. The returned
277 // rectangle is aligned to the right edge of the last arrow encountered in
278 // the tip text, else to the tip text left edge.
279 int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2;
280 delete surfaceMeasure;
281 return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height);
282 }
283
284 void CallTip::CallTipCancel() {
285 inCallTipMode = false;
286 if (wCallTip.Created()) {
287 wCallTip.Destroy();
288 }
289 }
290
291 void CallTip::SetHighlight(int start, int end) {
292 // Avoid flashing by checking something has really changed
293 if ((start != startHighlight) || (end != endHighlight)) {
294 startHighlight = start;
295 endHighlight = end;
296 if (wCallTip.Created()) {
297 wCallTip.InvalidateAll();
298 }
299 }
300 }
301
302 // Set the tab size (sizes > 0 enable the use of tabs). This also enables the
303 // use of the STYLE_CALLTIP.
304 void CallTip::SetTabSize(int tabSz) {
305 tabSize = tabSz;
306 useStyleCallTip = true;
307 }
308
309 // It might be better to have two access functions for this and to use
310 // them for all settings of colours.
311 void CallTip::SetForeBack(const ColourPair &fore, const ColourPair &back) {
312 colourBG = back;
313 colourUnSel = fore;
314 }