1
// Scintilla source code edit control
3
** Code for displaying call tips.
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.
13
#include "Scintilla.h"
18
using namespace Scintilla;
21
static const int insetX = 5; // text inset in x from calltip border
22
static const int widthArrow = 14;
26
inCallTipMode = false;
29
rectUp = PRectangle(0,0,0,0);
30
rectDown = PRectangle(0,0,0,0);
36
useStyleCallTip = false; // for backwards compatibility
39
// proper apple colours for the default
40
colourBG.desired = ColourDesired(0xff, 0xff, 0xc6);
41
colourUnSel.desired = ColourDesired(0, 0, 0);
43
colourBG.desired = ColourDesired(0xff, 0xff, 0xff);
44
colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80);
46
colourSel.desired = ColourDesired(0, 0, 0x80);
47
colourShade.desired = ColourDesired(0, 0, 0);
48
colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0);
60
void CallTip::RefreshColourPalette(Palette &pal, bool want) {
61
pal.WantFind(colourBG, want);
62
pal.WantFind(colourUnSel, want);
63
pal.WantFind(colourSel, want);
64
pal.WantFind(colourShade, want);
65
pal.WantFind(colourLight, want);
68
// Although this test includes 0, we should never see a \0 character.
69
static bool IsArrowCharacter(char ch) {
70
return (ch == 0) || (ch == '\001') || (ch == '\002');
73
// We ignore tabs unless a tab width has been set.
74
bool CallTip::IsTabCharacter(char ch) const {
75
return (tabSize > 0) && (ch == '\t');
78
int CallTip::NextTabPos(int x) {
79
if (tabSize > 0) { // paranoia... not called unless this is true
80
x -= insetX; // position relative to text
81
x = (x + tabSize) / tabSize; // tab "number"
82
return tabSize*x + insetX; // position of next tab
84
return x + 1; // arbitrary
88
// Draw a section of the call tip that does not include \n in one colour.
89
// The text may include up to numEnds tabs or arrow characters.
90
void CallTip::DrawChunk(Surface *surface, int &x, const char *s,
91
int posStart, int posEnd, int ytext, PRectangle rcClient,
92
bool highlight, bool draw) {
94
int len = posEnd - posStart;
96
// Divide the text into sections that are all text, or that are
97
// single arrows or single tab characters (if tabSize > 0).
99
const int numEnds = 10;
100
int ends[numEnds + 2];
101
for (int i=0; i<len; i++) {
102
if ((maxEnd < numEnds) &&
103
(IsArrowCharacter(s[i]) || IsTabCharacter(s[i])) ) {
106
ends[maxEnd++] = i+1;
109
ends[maxEnd++] = len;
112
for (int seg = 0; seg<maxEnd; seg++) {
113
int endSeg = ends[seg];
114
if (endSeg > startSeg) {
115
if (IsArrowCharacter(s[startSeg])) {
116
bool upArrow = s[startSeg] == '\001';
118
rcClient.right = rcClient.left + widthArrow;
120
const int halfWidth = widthArrow / 2 - 3;
121
const int centreX = rcClient.left + widthArrow / 2 - 1;
122
const int centreY = (rcClient.top + rcClient.bottom) / 2;
123
surface->FillRectangle(rcClient, colourBG.allocated);
124
PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1,
125
rcClient.right - 2, rcClient.bottom - 1);
126
surface->FillRectangle(rcClientInner, colourUnSel.allocated);
128
if (upArrow) { // Up arrow
130
Point(centreX - halfWidth, centreY + halfWidth / 2),
131
Point(centreX + halfWidth, centreY + halfWidth / 2),
132
Point(centreX, centreY - halfWidth + halfWidth / 2),
134
surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
135
colourBG.allocated, colourBG.allocated);
136
} else { // Down arrow
138
Point(centreX - halfWidth, centreY - halfWidth / 2),
139
Point(centreX + halfWidth, centreY - halfWidth / 2),
140
Point(centreX, centreY + halfWidth - halfWidth / 2),
142
surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
143
colourBG.allocated, colourBG.allocated);
146
xEnd = rcClient.right;
153
} else if (IsTabCharacter(s[startSeg])) {
154
xEnd = NextTabPos(x);
156
xEnd = x + surface->WidthText(font, s + startSeg, endSeg - startSeg);
159
rcClient.right = xEnd;
160
surface->DrawTextTransparent(rcClient, font, ytext,
161
s+startSeg, endSeg - startSeg,
162
highlight ? colourSel.allocated : colourUnSel.allocated);
171
int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
172
PRectangle rcClientPos = wCallTip.GetClientPosition();
173
PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
174
rcClientPos.bottom - rcClientPos.top);
175
PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
177
// To make a nice small call tip window, it is only sized to fit most normal characters without accents
178
int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);
181
// Draw the definition in three parts: before highlight, highlighted, after highlight
182
int ytext = rcClient.top + ascent + 1;
183
rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
184
char *chunkVal = val;
185
bool moreChunks = true;
189
char *chunkEnd = strchr(chunkVal, '\n');
190
if (chunkEnd == NULL) {
191
chunkEnd = chunkVal + strlen(chunkVal);
194
int chunkOffset = chunkVal - val;
195
int chunkLength = chunkEnd - chunkVal;
196
int chunkEndOffset = chunkOffset + chunkLength;
197
int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
198
thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
199
thisStartHighlight -= chunkOffset;
200
int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
201
thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
202
thisEndHighlight -= chunkOffset;
203
rcClient.top = ytext - ascent - 1;
205
int x = insetX; // start each line at this inset
207
DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight,
208
ytext, rcClient, false, draw);
209
DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight,
210
ytext, rcClient, true, draw);
211
DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength,
212
ytext, rcClient, false, draw);
214
chunkVal = chunkEnd + 1;
216
rcClient.bottom += lineHeight;
217
maxWidth = Platform::Maximum(maxWidth, x);
222
void CallTip::PaintCT(Surface *surfaceWindow) {
225
PRectangle rcClientPos = wCallTip.GetClientPosition();
226
PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
227
rcClientPos.bottom - rcClientPos.top);
228
PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
230
surfaceWindow->FillRectangle(rcClient, colourBG.allocated);
232
offsetMain = insetX; // initial alignment assuming no arrows
233
PaintContents(surfaceWindow, true);
236
// OSX doesn't put borders on "help tags"
237
// Draw a raised border around the edges of the window
238
surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
239
surfaceWindow->PenColour(colourShade.allocated);
240
surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
241
surfaceWindow->LineTo(rcClientSize.right - 1, 0);
242
surfaceWindow->PenColour(colourLight.allocated);
243
surfaceWindow->LineTo(0, 0);
244
surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
248
void CallTip::MouseClick(Point pt) {
250
if (rectUp.Contains(pt))
252
if (rectDown.Contains(pt))
256
PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn,
257
const char *faceName, int size,
258
int codePage_, int characterSet, Window &wParent) {
262
val = new char[strlen(defn) + 1];
264
codePage = codePage_;
265
Surface *surfaceMeasure = Surface::Allocate();
268
surfaceMeasure->Init(wParent.GetID());
269
surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
270
surfaceMeasure->SetDBCSMode(codePage);
273
inCallTipMode = true;
274
posStartCallTip = pos;
275
int deviceHeight = surfaceMeasure->DeviceHeightFont(size);
276
font.Create(faceName, characterSet, deviceHeight, false, false);
277
// Look for multiple lines in the text
278
// Only support \n here - simply means container must avoid \r!
281
const char *look = val;
282
rectUp = PRectangle(0,0,0,0);
283
rectDown = PRectangle(0,0,0,0);
284
offsetMain = insetX; // changed to right edge of any arrows
285
int width = PaintContents(surfaceMeasure, false) + insetX;
286
while ((newline = strchr(look, '\n')) != NULL) {
290
lineHeight = surfaceMeasure->Height(font);
292
// Extra line for border and an empty line at top and bottom. The returned
293
// rectangle is aligned to the right edge of the last arrow encountered in
294
// the tip text, else to the tip text left edge.
295
int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2;
296
delete surfaceMeasure;
297
return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height);
300
void CallTip::CallTipCancel() {
301
inCallTipMode = false;
302
if (wCallTip.Created()) {
307
void CallTip::SetHighlight(int start, int end) {
308
// Avoid flashing by checking something has really changed
309
if ((start != startHighlight) || (end != endHighlight)) {
310
startHighlight = start;
312
if (wCallTip.Created()) {
313
wCallTip.InvalidateAll();
318
// Set the tab size (sizes > 0 enable the use of tabs). This also enables the
319
// use of the STYLE_CALLTIP.
320
void CallTip::SetTabSize(int tabSz) {
322
useStyleCallTip = true;
325
// It might be better to have two access functions for this and to use
326
// them for all settings of colours.
327
void CallTip::SetForeBack(const ColourPair &fore, const ColourPair &back) {