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
) {