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.
16 #include "Scintilla.h"
18 #include "StringCopy.h"
22 using namespace Scintilla
;
27 inCallTipMode
= false;
29 rectUp
= PRectangle(0,0,0,0);
30 rectDown
= PRectangle(0,0,0,0);
37 useStyleCallTip
= false; // for backwards compatibility
41 borderHeight
= 2; // Extra line for border and an empty line at top and bottom.
45 // proper apple colours for the default
46 colourBG
= ColourDesired(0xff, 0xff, 0xc6);
47 colourUnSel
= ColourDesired(0, 0, 0);
49 colourBG
= ColourDesired(0xff, 0xff, 0xff);
50 colourUnSel
= ColourDesired(0x80, 0x80, 0x80);
52 colourSel
= ColourDesired(0, 0, 0x80);
53 colourShade
= ColourDesired(0, 0, 0);
54 colourLight
= ColourDesired(0xc0, 0xc0, 0xc0);
64 // Although this test includes 0, we should never see a \0 character.
65 static bool IsArrowCharacter(char ch
) {
66 return (ch
== 0) || (ch
== '\001') || (ch
== '\002');
69 // We ignore tabs unless a tab width has been set.
70 bool CallTip::IsTabCharacter(char ch
) const {
71 return (tabSize
> 0) && (ch
== '\t');
74 int CallTip::NextTabPos(int x
) const {
75 if (tabSize
> 0) { // paranoia... not called unless this is true
76 x
-= insetX
; // position relative to text
77 x
= (x
+ tabSize
) / tabSize
; // tab "number"
78 return tabSize
*x
+ insetX
; // position of next tab
80 return x
+ 1; // arbitrary
84 // Draw a section of the call tip that does not include \n in one colour.
85 // The text may include up to numEnds tabs or arrow characters.
86 void CallTip::DrawChunk(Surface
*surface
, int &x
, const char *s
,
87 int posStart
, int posEnd
, int ytext
, PRectangle rcClient
,
88 bool highlight
, bool draw
) {
90 int len
= posEnd
- posStart
;
92 // Divide the text into sections that are all text, or that are
93 // single arrows or single tab characters (if tabSize > 0).
95 const int numEnds
= 10;
96 int ends
[numEnds
+ 2];
97 for (int i
=0; i
<len
; i
++) {
98 if ((maxEnd
< numEnds
) &&
99 (IsArrowCharacter(s
[i
]) || IsTabCharacter(s
[i
]))) {
102 ends
[maxEnd
++] = i
+1;
105 ends
[maxEnd
++] = len
;
108 for (int seg
= 0; seg
<maxEnd
; seg
++) {
109 int endSeg
= ends
[seg
];
110 if (endSeg
> startSeg
) {
111 if (IsArrowCharacter(s
[startSeg
])) {
112 xEnd
= x
+ widthArrow
;
113 bool upArrow
= s
[startSeg
] == '\001';
114 rcClient
.left
= static_cast<XYPOSITION
>(x
);
115 rcClient
.right
= static_cast<XYPOSITION
>(xEnd
);
117 const int halfWidth
= widthArrow
/ 2 - 3;
118 const int quarterWidth
= halfWidth
/ 2;
119 const int centreX
= x
+ widthArrow
/ 2 - 1;
120 const int centreY
= static_cast<int>(rcClient
.top
+ rcClient
.bottom
) / 2;
121 surface
->FillRectangle(rcClient
, colourBG
);
122 PRectangle
rcClientInner(rcClient
.left
+ 1, rcClient
.top
+ 1,
123 rcClient
.right
- 2, rcClient
.bottom
- 1);
124 surface
->FillRectangle(rcClientInner
, colourUnSel
);
126 if (upArrow
) { // Up arrow
128 Point::FromInts(centreX
- halfWidth
, centreY
+ quarterWidth
),
129 Point::FromInts(centreX
+ halfWidth
, centreY
+ quarterWidth
),
130 Point::FromInts(centreX
, centreY
- halfWidth
+ quarterWidth
),
132 surface
->Polygon(pts
, ELEMENTS(pts
), colourBG
, colourBG
);
133 } else { // Down arrow
135 Point::FromInts(centreX
- halfWidth
, centreY
- quarterWidth
),
136 Point::FromInts(centreX
+ halfWidth
, centreY
- quarterWidth
),
137 Point::FromInts(centreX
, centreY
+ halfWidth
- quarterWidth
),
139 surface
->Polygon(pts
, ELEMENTS(pts
), colourBG
, colourBG
);
148 } else if (IsTabCharacter(s
[startSeg
])) {
149 xEnd
= NextTabPos(x
);
151 xEnd
= x
+ RoundXYPosition(surface
->WidthText(font
, s
+ startSeg
, endSeg
- startSeg
));
153 rcClient
.left
= static_cast<XYPOSITION
>(x
);
154 rcClient
.right
= static_cast<XYPOSITION
>(xEnd
);
155 surface
->DrawTextTransparent(rcClient
, font
, static_cast<XYPOSITION
>(ytext
),
156 s
+startSeg
, endSeg
- startSeg
,
157 highlight
? colourSel
: colourUnSel
);
166 int CallTip::PaintContents(Surface
*surfaceWindow
, bool draw
) {
167 PRectangle rcClientPos
= wCallTip
.GetClientPosition();
168 PRectangle
rcClientSize(0.0f
, 0.0f
, rcClientPos
.right
- rcClientPos
.left
,
169 rcClientPos
.bottom
- rcClientPos
.top
);
170 PRectangle
rcClient(1.0f
, 1.0f
, 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
= RoundXYPosition(surfaceWindow
->Ascent(font
) - surfaceWindow
->InternalLeading(font
));
176 // Draw the definition in three parts: before highlight, highlighted, after highlight
177 int ytext
= static_cast<int>(rcClient
.top
) + ascent
+ 1;
178 rcClient
.bottom
= ytext
+ surfaceWindow
->Descent(font
) + 1;
179 const char *chunkVal
= val
.c_str();
180 bool moreChunks
= true;
184 const char *chunkEnd
= strchr(chunkVal
, '\n');
185 if (chunkEnd
== NULL
) {
186 chunkEnd
= chunkVal
+ strlen(chunkVal
);
189 int chunkOffset
= static_cast<int>(chunkVal
- val
.c_str());
190 int chunkLength
= static_cast<int>(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
= static_cast<XYPOSITION
>(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;
211 rcClient
.bottom
+= lineHeight
;
212 maxWidth
= Platform::Maximum(maxWidth
, x
);
217 void CallTip::PaintCT(Surface
*surfaceWindow
) {
220 PRectangle rcClientPos
= wCallTip
.GetClientPosition();
221 PRectangle
rcClientSize(0.0f
, 0.0f
, rcClientPos
.right
- rcClientPos
.left
,
222 rcClientPos
.bottom
- rcClientPos
.top
);
223 PRectangle
rcClient(1.0f
, 1.0f
, rcClientSize
.right
- 1, rcClientSize
.bottom
- 1);
225 surfaceWindow
->FillRectangle(rcClient
, colourBG
);
227 offsetMain
= insetX
; // initial alignment assuming no arrows
228 PaintContents(surfaceWindow
, true);
231 // OSX doesn't put borders on "help tags"
232 // Draw a raised border around the edges of the window
233 surfaceWindow
->MoveTo(0, static_cast<int>(rcClientSize
.bottom
) - 1);
234 surfaceWindow
->PenColour(colourShade
);
235 surfaceWindow
->LineTo(static_cast<int>(rcClientSize
.right
) - 1, static_cast<int>(rcClientSize
.bottom
) - 1);
236 surfaceWindow
->LineTo(static_cast<int>(rcClientSize
.right
) - 1, 0);
237 surfaceWindow
->PenColour(colourLight
);
238 surfaceWindow
->LineTo(0, 0);
239 surfaceWindow
->LineTo(0, static_cast<int>(rcClientSize
.bottom
) - 1);
243 void CallTip::MouseClick(Point pt
) {
245 if (rectUp
.Contains(pt
))
247 if (rectDown
.Contains(pt
))
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
) {
257 codePage
= codePage_
;
258 Surface
*surfaceMeasure
= Surface::Allocate(technology
);
261 surfaceMeasure
->Init(wParent
.GetID());
262 surfaceMeasure
->SetUnicodeMode(SC_CP_UTF8
== codePage
);
263 surfaceMeasure
->SetDBCSMode(codePage
);
266 inCallTipMode
= true;
267 posStartCallTip
= pos
;
268 XYPOSITION deviceHeight
= static_cast<XYPOSITION
>(surfaceMeasure
->DeviceHeightFont(size
));
269 FontParameters
fp(faceName
, deviceHeight
/ SC_FONT_SIZE_MULTIPLIER
, SC_WEIGHT_NORMAL
, false, 0, technology
, characterSet
);
271 // Look for multiple lines in the text
272 // Only support \n here - simply means container must avoid \r!
275 const char *look
= val
.c_str();
276 rectUp
= PRectangle(0,0,0,0);
277 rectDown
= PRectangle(0,0,0,0);
278 offsetMain
= insetX
; // changed to right edge of any arrows
279 int width
= PaintContents(surfaceMeasure
, false) + insetX
;
280 while ((newline
= strchr(look
, '\n')) != NULL
) {
284 lineHeight
= RoundXYPosition(surfaceMeasure
->Height(font
));
287 // rectangle is aligned to the right edge of the last arrow encountered in
288 // the tip text, else to the tip text left edge.
289 int height
= lineHeight
* numLines
- static_cast<int>(surfaceMeasure
->InternalLeading(font
)) + borderHeight
* 2;
290 delete surfaceMeasure
;
292 return PRectangle(pt
.x
- offsetMain
, pt
.y
- verticalOffset
- height
, pt
.x
+ width
- offsetMain
, pt
.y
- verticalOffset
);
294 return PRectangle(pt
.x
- offsetMain
, pt
.y
+ verticalOffset
+ textHeight
, pt
.x
+ width
- offsetMain
, pt
.y
+ verticalOffset
+ textHeight
+ height
);
298 void CallTip::CallTipCancel() {
299 inCallTipMode
= false;
300 if (wCallTip
.Created()) {
305 void CallTip::SetHighlight(int start
, int end
) {
306 // Avoid flashing by checking something has really changed
307 if ((start
!= startHighlight
) || (end
!= endHighlight
)) {
308 startHighlight
= start
;
309 endHighlight
= (end
> start
) ? end
: start
;
310 if (wCallTip
.Created()) {
311 wCallTip
.InvalidateAll();
316 // Set the tab size (sizes > 0 enable the use of tabs). This also enables the
317 // use of the STYLE_CALLTIP.
318 void CallTip::SetTabSize(int tabSz
) {
320 useStyleCallTip
= true;
323 // Set the calltip position, below the text by default or if above is false
324 // else above the text.
325 void CallTip::SetPosition(bool aboveText
) {
329 // It might be better to have two access functions for this and to use
330 // them for all settings of colours.
331 void CallTip::SetForeBack(const ColourDesired
&fore
, const ColourDesired
&back
) {