[go: up one dir, main page]

Menu

[ffec3f]: / source / calltips.c  Maximize  Restore  History

Download this file

327 lines (287 with data), 11.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/*******************************************************************************
* *
* calltips.c -- Calltip UI functions (calltip *file* functions are in tags.c) *
* *
* Copyright (C) 2002 Nathaniel Gray *
* *
* This is free software; you can redistribute it and/or modify it under the *
* terms of the GNU General Public License as published by the Free Software *
* Foundation; either version 2 of the License, or (at your option) any later *
* version. In addition, you may distribute version of this program linked to *
* Motif or Open Motif. See README for details. *
* *
* This software is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
* for more details. *
* *
* You should have received a copy of the GNU General Public License along with *
* software; if not, write to the Free Software Foundation, Inc., 59 Temple *
* Place, Suite 330, Boston, MA 02111-1307 USA *
* *
* Nirvana Text Editor *
* April, 1997 *
* *
* Written by Mark Edel *
* *
*******************************************************************************/
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "text.h"
#include "textP.h"
#include "calltips.h"
#include "../util/misc.h"
#include "../util/nedit_malloc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <Xm/Xm.h>
#include <Xm/Label.h>
#include <X11/Shell.h>
#ifdef HAVE_DEBUG_H
#include "../debug.h"
#endif
static char *expandAllTabs( char *text, int tab_width );
/*
** Pop-down a calltip if one exists, else do nothing
*/
void KillCalltip(WindowInfo *window, int calltipID) {
textDisp *textD = ((TextWidget)window->lastFocus)->text.textD;
TextDKillCalltip( textD, calltipID );
}
void TextDKillCalltip(textDisp *textD, int calltipID) {
if( textD->calltip.ID == 0 )
return;
if( calltipID == 0 || calltipID == textD->calltip.ID ) {
XtPopdown( textD->calltipShell );
textD->calltip.ID = 0;
}
}
/*
** Is a calltip displayed? Returns the calltip ID of the currently displayed
** calltip, or 0 if there is no calltip displayed. If called with
** calltipID != 0, returns 0 unless there is a calltip being
** displayed with that calltipID.
*/
int GetCalltipID(WindowInfo *window, int calltipID) {
textDisp *textD = ((TextWidget)window->lastFocus)->text.textD;
if( calltipID == 0 )
return textD->calltip.ID;
else {
if( calltipID == textD->calltip.ID)
return calltipID;
else
return 0;
}
}
#define CALLTIP_EDGE_GUARD 5
static Boolean offscreenV(XWindowAttributes *screenAttr, int top, int height) {
return (top < CALLTIP_EDGE_GUARD ||
top + height >= screenAttr->height - CALLTIP_EDGE_GUARD);
}
/*
** Update the position of the current calltip if one exists, else do nothing
*/
void TextDRedrawCalltip(textDisp *textD, int calltipID) {
int lineHeight = textD->ascent + textD->descent;
Position txtX, txtY, borderWidth, abs_x, abs_y, tipWidth, tipHeight;
XWindowAttributes screenAttr;
int rel_x, rel_y, flip_delta;
if( textD->calltip.ID == 0 )
return;
if( calltipID != 0 && calltipID != textD->calltip.ID )
return;
/* Get the location/dimensions of the text area */
XtVaGetValues(textD->w, XmNx, &txtX, XmNy, &txtY, NULL);
if( textD->calltip.anchored ) {
/* Put it at the anchor position */
if (!TextDPositionToXY(textD, textD->calltip.pos, &rel_x, &rel_y)) {
if (textD->calltip.alignMode == TIP_STRICT)
TextDKillCalltip(textD, textD->calltip.ID);
return;
}
} else {
if (textD->calltip.pos < 0) {
/* First display of tip with cursor offscreen (detected in
ShowCalltip) */
textD->calltip.pos = textD->width/2;
textD->calltip.hAlign = TIP_CENTER;
rel_y = textD->height/3;
} else if (!TextDPositionToXY(textD, textD->cursorPos, &rel_x, &rel_y)){
/* Window has scrolled and tip is now offscreen */
if (textD->calltip.alignMode == TIP_STRICT)
TextDKillCalltip(textD, textD->calltip.ID);
return;
}
rel_x = textD->calltip.pos;
}
XtVaGetValues(textD->calltipShell, XmNwidth, &tipWidth, XmNheight,
&tipHeight, XmNborderWidth, &borderWidth, NULL);
rel_x += borderWidth;
rel_y += lineHeight/2 + borderWidth;
/* Adjust rel_x for horizontal alignment modes */
if (textD->calltip.hAlign == TIP_CENTER)
rel_x -= tipWidth/2;
else if (textD->calltip.hAlign == TIP_RIGHT)
rel_x -= tipWidth;
/* Adjust rel_y for vertical alignment modes */
if (textD->calltip.vAlign == TIP_ABOVE) {
flip_delta = tipHeight + lineHeight + 2*borderWidth;
rel_y -= flip_delta;
} else
flip_delta = -(tipHeight + lineHeight + 2*borderWidth);
XtTranslateCoords(textD->w, rel_x, rel_y, &abs_x, &abs_y);
/* If we're not in strict mode try to keep the tip on-screen */
if (textD->calltip.alignMode == TIP_SLOPPY) {
XGetWindowAttributes(XtDisplay(textD->w),
RootWindowOfScreen(XtScreen(textD->w)), &screenAttr);
/* make sure tip doesn't run off right or left side of screen */
if (abs_x + tipWidth >= screenAttr.width - CALLTIP_EDGE_GUARD)
abs_x = screenAttr.width - tipWidth - CALLTIP_EDGE_GUARD;
if (abs_x < CALLTIP_EDGE_GUARD)
abs_x = CALLTIP_EDGE_GUARD;
/* Try to keep the tip onscreen vertically if possible */
if (screenAttr.height > tipHeight &&
offscreenV(&screenAttr, abs_y, tipHeight)) {
/* Maybe flipping from below to above (or vice-versa) will help */
if (!offscreenV(&screenAttr, abs_y + flip_delta, tipHeight))
abs_y += flip_delta;
/* Make sure the tip doesn't end up *totally* offscreen */
else if (abs_y + tipHeight < 0)
abs_y = CALLTIP_EDGE_GUARD;
else if (abs_y >= screenAttr.height)
abs_y = screenAttr.height - tipHeight - CALLTIP_EDGE_GUARD;
/* If no case applied, just go with the default placement. */
}
}
XtVaSetValues( textD->calltipShell, XmNx, abs_x, XmNy, abs_y, NULL );
}
/*
** Returns a new string with each \t replaced with tab_width spaces or
** a pointer to text if there were no tabs. Returns NULL on malloc failure.
** Note that this is dumb replacement, not smart tab-like behavior! The goal
** is to prevent tabs from turning into squares in calltips, not to get the
** formatting just right.
*/
static char *expandAllTabs( char *text, int tab_width ) {
int i, nTabs=0;
size_t len;
char *c, *cCpy, *textCpy;
/* First count 'em */
for( c = text; *c; ++c )
if( *c == '\t' )
++nTabs;
if( nTabs == 0 )
return text;
/* Allocate the new string */
len = strlen( text ) + ( tab_width - 1 )*nTabs;
textCpy = (char*)NEditMalloc( len + 1 );
if( !textCpy ) {
fprintf(stderr,
"nedit: Out of heap memory in expandAllTabs!\n");
return NULL;
}
/* Now replace 'em */
for( c = text, cCpy = textCpy; *c; ++c, ++cCpy) {
if( *c == '\t' ) {
for( i = 0; i < tab_width; ++i, ++cCpy )
*cCpy = ' ';
--cCpy; /* Will be incremented in outer for loop */
} else
*cCpy = *c;
}
*cCpy = '\0';
return textCpy;
}
/*
** Pop-up a calltip.
** If a calltip is already being displayed it is destroyed and replaced with
** the new calltip. Returns the ID of the calltip or 0 on failure.
*/
int ShowCalltip(WindowInfo *window, char *text, Boolean anchored,
int pos, int hAlign, int vAlign, int alignMode) {
static int StaticCalltipID = 1;
textDisp *textD = ((TextWidget)window->lastFocus)->text.textD;
int rel_x, rel_y;
Position txtX, txtY;
char *textCpy;
XmString str;
/* Destroy any previous calltip */
TextDKillCalltip( textD, 0 );
/* Make sure the text isn't NULL */
if (text == NULL) return 0;
/* Expand any tabs in the calltip and make it an XmString */
textCpy = expandAllTabs( text, BufGetTabDistance(textD->buffer) );
if( textCpy == NULL )
return 0; /* Out of memory */
str = XmStringCreateLtoR(textCpy, XmFONTLIST_DEFAULT_TAG);
if( textCpy != text )
NEditFree( textCpy );
/* Get the location/dimensions of the text area */
XtVaGetValues(textD->w,
XmNx, &txtX,
XmNy, &txtY,
NULL);
/* Create the calltip widget on first request */
if (textD->calltipW == NULL) {
Arg args[10];
int argcnt = 0;
XtSetArg(args[argcnt], XmNsaveUnder, True); argcnt++;
XtSetArg(args[argcnt], XmNallowShellResize, True); argcnt++;
textD->calltipShell = CreatePopupShellWithBestVis("calltipshell",
overrideShellWidgetClass, textD->w, args, argcnt);
/* Might want to make this a read-only XmText eventually so that
users can copy from it */
textD->calltipW = XtVaCreateManagedWidget(
"calltip", xmLabelWidgetClass, textD->calltipShell,
XmNborderWidth, 1, /* Thin borders */
XmNhighlightThickness, 0,
XmNalignment, XmALIGNMENT_BEGINNING,
XmNforeground, textD->calltipFGPixel,
XmNbackground, textD->calltipBGPixel,
NULL );
}
/* Set the text on the label */
XtVaSetValues( textD->calltipW, XmNlabelString, str, NULL );
XmStringFree( str );
/* Figure out where to put the tip */
if (anchored) {
/* Put it at the specified position */
/* If position is not displayed, return 0 */
if (pos < textD->firstChar || pos > textD->lastChar ) {
XBell(TheDisplay, 0);
return 0;
}
textD->calltip.pos = pos;
} else {
/* Put it next to the cursor, or in the center of the window if the
cursor is offscreen and mode != strict */
if (!TextDPositionToXY(textD, textD->cursorPos, &rel_x, &rel_y)) {
if (alignMode == TIP_STRICT) {
XBell(TheDisplay, 0);
return 0;
}
textD->calltip.pos = -1;
} else
/* Store the x-offset for use when redrawing */
textD->calltip.pos = rel_x;
}
/* Should really bounds-check these enumerations... */
textD->calltip.ID = StaticCalltipID;
textD->calltip.anchored = anchored;
textD->calltip.hAlign = hAlign;
textD->calltip.vAlign = vAlign;
textD->calltip.alignMode = alignMode;
/* Increment the static calltip ID. Macro variables can only be int,
not unsigned, so have to work to keep it > 0 on overflow */
if(++StaticCalltipID <= 0)
StaticCalltipID = 1;
/* Realize the calltip's shell so that its width & height are known */
XtRealizeWidget( textD->calltipShell );
/* Move the calltip and pop it up */
TextDRedrawCalltip(textD, 0);
XtPopup( textD->calltipShell, XtGrabNone );
return textD->calltip.ID;
}