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