aboutsummaryrefslogtreecommitdiffstats
path: root/src/term.c
diff options
context:
space:
mode:
authorEli Zaretskii2012-06-02 17:57:51 +0300
committerEli Zaretskii2012-06-02 17:57:51 +0300
commitb5e9cbb6fdce4b7e8c5cd6ad1addf6e4af35da67 (patch)
treec6a1b2060e5d56677f6785c2241311527fa917fa /src/term.c
parentf51b6486fc8b0e3fa7fd08cbf83b27ef0d5efe1a (diff)
downloademacs-b5e9cbb6fdce4b7e8c5cd6ad1addf6e4af35da67.tar.gz
emacs-b5e9cbb6fdce4b7e8c5cd6ad1addf6e4af35da67.zip
Initial incomplete version of tty menus. tty_menu_activate not done yet.
Diffstat (limited to 'src/term.c')
-rw-r--r--src/term.c801
1 files changed, 801 insertions, 0 deletions
diff --git a/src/term.c b/src/term.c
index 3d7a677374c..188c476961b 100644
--- a/src/term.c
+++ b/src/term.c
@@ -58,6 +58,10 @@ static int been_here = -1;
58#include "xterm.h" 58#include "xterm.h"
59#endif 59#endif
60 60
61#ifdef HAVE_MENUS
62#include "menu.h"
63#endif
64
61#ifndef O_RDWR 65#ifndef O_RDWR
62#define O_RDWR 2 66#define O_RDWR 2
63#endif 67#endif
@@ -2834,6 +2838,803 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
2834#endif /* HAVE_GPM */ 2838#endif /* HAVE_GPM */
2835 2839
2836 2840
2841/***********************************************************************
2842 Menus
2843 ***********************************************************************/
2844
2845#if defined (HAVE_MENUS) && !defined (MSDOS)
2846
2847/* TTY menu implementation and main ideas are borrowed from msdos.c.
2848
2849 However, unlike on MSDOS, where the menu text is drawn directly to
2850 the screen, on a TTY we use display_string (see xdisp.c) to put the
2851 glyphs produced from the menu items into the desired_matrix glyph
2852 matrix, and then call update_frame to deliver the results to the
2853 glass. The previous contents of the screen, in the form of the
2854 current_matrix, is stashed away, and used to restore screen
2855 contents when the menu selection changes or when the final
2856 selection is made and the menu should be popped down.
2857
2858 The idea of this implementation was suggested by Gerd Moellmann. */
2859
2860#define TTYM_FAILURE -1
2861#define TTYM_SUCCESS 1
2862#define TTYM_NO_SELECT 2
2863#define TTYM_IA_SELECT 3
2864
2865/* These hold text of the current and the previous menu help messages. */
2866static const char *menu_help_message, *prev_menu_help_message;
2867/* Pane number and item number of the menu item which generated the
2868 last menu help message. */
2869static int menu_help_paneno, menu_help_itemno;
2870
2871typedef struct tty_menu_struct
2872{
2873 int count;
2874 char **text;
2875 struct tty_menu_struct **submenu;
2876 int *panenumber; /* Also used as enable. */
2877 int allocated;
2878 int panecount;
2879 int width;
2880 const char **help_text;
2881} tty_menu;
2882
2883/* Create a brand new menu structure. */
2884
2885static tty_menu *
2886tty_menu_create (void)
2887{
2888 tty_menu *menu;
2889
2890 menu = (tty_menu *) xmalloc (sizeof (tty_menu));
2891 menu->allocated = menu->count = menu->panecount = menu->width = 0;
2892 return menu;
2893}
2894
2895/* Allocate some (more) memory for MENU ensuring that there is room for one
2896 for item. */
2897
2898static void
2899tty_menu_make_room (tty_menu *menu)
2900{
2901 if (menu->allocated == 0)
2902 {
2903 int count = menu->allocated = 10;
2904 menu->text = (char **) xmalloc (count * sizeof (char *));
2905 menu->submenu = (tty_menu **) xmalloc (count * sizeof (tty_menu *));
2906 menu->panenumber = (int *) xmalloc (count * sizeof (int));
2907 menu->help_text = (const char **) xmalloc (count * sizeof (char *));
2908 }
2909 else if (menu->allocated == menu->count)
2910 {
2911 int count = menu->allocated = menu->allocated + 10;
2912 menu->text
2913 = (char **) xrealloc (menu->text, count * sizeof (char *));
2914 menu->submenu
2915 = (tty_menu **) xrealloc (menu->submenu, count * sizeof (tty_menu *));
2916 menu->panenumber
2917 = (int *) xrealloc (menu->panenumber, count * sizeof (int));
2918 menu->help_text
2919 = (const char **) xrealloc (menu->help_text, count * sizeof (char *));
2920 }
2921}
2922
2923/* Search the given menu structure for a given pane number. */
2924
2925static tty_menu *
2926tty_menu_search_pane (tty_menu *menu, int pane)
2927{
2928 int i;
2929 tty_menu *try;
2930
2931 for (i = 0; i < menu->count; i++)
2932 if (menu->submenu[i])
2933 {
2934 if (pane == menu->panenumber[i])
2935 return menu->submenu[i];
2936 if ((try = tty_menu_search_pane (menu->submenu[i], pane)))
2937 return try;
2938 }
2939 return (tty_menu *) 0;
2940}
2941
2942/* Determine how much screen space a given menu needs. */
2943
2944static void
2945tty_menu_calc_size (tty_menu *menu, int *width, int *height)
2946{
2947 int i, h2, w2, maxsubwidth, maxheight;
2948
2949 maxsubwidth = 0;
2950 maxheight = menu->count;
2951 for (i = 0; i < menu->count; i++)
2952 {
2953 if (menu->submenu[i])
2954 {
2955 tty_menu_calc_size (menu->submenu[i], &w2, &h2);
2956 if (w2 > maxsubwidth) maxsubwidth = w2;
2957 if (i + h2 > maxheight) maxheight = i + h2;
2958 }
2959 }
2960 *width = menu->width + maxsubwidth;
2961 *height = maxheight;
2962}
2963
2964/* Display MENU at (X,Y) using FACES. */
2965
2966#define BUILD_CHAR_GLYPH(GLYPH, CODE, FACE_ID, PADDING_P) \
2967 do \
2968 { \
2969 (GLYPH).type = CHAR_GLYPH; \
2970 SET_CHAR_GLYPH ((GLYPH), CODE, FACE_ID, PADDING_P); \
2971 (GLYPH).charpos = -1; \
2972 } \
2973 while (0)
2974
2975static void
2976tty_menu_display (tty_menu *menu, int y, int x, int pn, int *faces,
2977 int disp_help)
2978{
2979 int i, face, width, mx = -1, my = -1, enabled, mousehere, row, col;
2980 struct frame *sf = SELECTED_FRAME ();
2981 struct tty_display_info *tty = FRAME_TTY (sf);
2982#if defined (HAVE_MOUSE) || defined (HAVE_GPM)
2983 Lisp_Object lmx, lmy, lisp_dummy;
2984 enum scroll_bar_part part_dummy;
2985 Time time_dummy;
2986
2987 if (FRAME_TERMINAL (sf)->mouse_position_hook)
2988 (*FRAME_TERMINAL (sf)->mouse_position_hook) (&sf, -1,
2989 &lispy_dummy, &party_dummy,
2990 &lmx, &lmy,
2991 &time_dummy);
2992 if (!NILP (lmx))
2993 {
2994 mx = XINT (lmx);
2995 my = XINT (lmy);
2996 }
2997 else
2998 {
2999 mx = x;
3000 my = y;
3001 }
3002#else
3003 /* FIXME: need to set mx and my from cursor movement commands. */
3004 mx = x;
3005 my = y;
3006#endif
3007
3008 menu_help_message = NULL;
3009
3010 width = menu->width;
3011 col = curX (tty);
3012 row = curY (tty);
3013#if 0
3014 IT_update_begin (sf); /* FIXME: do we need an update_begin_hook? */
3015#endif
3016 for (i = 0; i < menu->count; i++)
3017 {
3018 int max_width = width + 2;
3019
3020 cursor_to (sf, y + i, x);
3021 enabled
3022 = (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
3023 mousehere = (y + i == my && x <= mx && mx < x + max_width);
3024 face = faces[enabled + mousehere * 2];
3025 /* Display the menu help string for the i-th menu item even if
3026 the menu item is currently disabled. That's what the GUI
3027 code does. */
3028 if (disp_help && enabled + mousehere * 2 >= 2)
3029 {
3030 menu_help_message = menu->help_text[i];
3031 menu_help_paneno = pn - 1;
3032 menu_help_itemno = i;
3033 }
3034 display_tty_menu_item (menu->text[i], face, y + i, x,
3035 menu->submenu[i] != NULL);
3036 }
3037 update_frame_with_menu (sf);
3038 cursor_to (sf, row, col);
3039}
3040
3041/* --------------------------- X Menu emulation ---------------------- */
3042
3043/* Report availability of menus. */
3044
3045int
3046have_menus_p (void) { return 1; }
3047
3048/* Create a new pane and place it on the outer-most level. */
3049
3050int
3051tty_menu_add_pane (Display *foo, tty_menu *menu, const char *txt)
3052{
3053 int len;
3054 const char *p;
3055
3056 tty_menu_make_room (menu);
3057 menu->submenu[menu->count] = tty_menu_create ();
3058 menu->text[menu->count] = (char *)txt;
3059 menu->panenumber[menu->count] = ++menu->panecount;
3060 menu->help_text[menu->count] = NULL;
3061 menu->count++;
3062
3063 /* Update the menu width, if necessary. */
3064 for (len = 0, p = txt; *p; )
3065 {
3066 int ch_len;
3067 int ch = STRING_CHAR_AND_LENGTH (p, ch_len);
3068
3069 len += CHAR_WIDTH (ch);
3070 p += ch_len;
3071 }
3072
3073 if (len > menu->width)
3074 menu->width = len;
3075
3076 return menu->panecount;
3077}
3078
3079/* Create a new item in a menu pane. */
3080
3081int
3082tty_menu_add_selection (tty_menu *menu, int pane,
3083 char *txt, int enable, char const *help_text)
3084{
3085 int len;
3086 char *p;
3087
3088 if (pane)
3089 if (!(menu = tty_menu_search_pane (menu, pane)))
3090 return TTYM_FAILURE;
3091 tty_menu_make_room (menu);
3092 menu->submenu[menu->count] = (tty_menu *) 0;
3093 menu->text[menu->count] = txt;
3094 menu->panenumber[menu->count] = enable;
3095 menu->help_text[menu->count] = help_text;
3096 menu->count++;
3097
3098 /* Update the menu width, if necessary. */
3099 for (len = 0, p = txt; *p; )
3100 {
3101 int ch_len;
3102 int ch = STRING_CHAR_AND_LENGTH (p, ch_len);
3103
3104 len += CHAR_WIDTH (ch);
3105 p += ch_len;
3106 }
3107
3108 if (len > menu->width)
3109 menu->width = len;
3110
3111 return TTYM_SUCCESS;
3112}
3113
3114/* Decide where the menu would be placed if requested at (X,Y). */
3115
3116void
3117tty_menu_locate (tty_menu *menu, int x, int y,
3118 int *ulx, int *uly, int *width, int *height)
3119{
3120 tty_menu_calc_size (menu, width, height);
3121 *ulx = x + 1;
3122 *uly = y;
3123 *width += 2;
3124}
3125
3126struct tty_menu_state
3127{
3128 void *screen_behind;
3129 tty_menu *menu;
3130 int pane;
3131 int x, y;
3132};
3133
3134
3135/* Display menu, wait for user's response, and return that response. */
3136
3137int
3138tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
3139 int x0, int y0, char **txt,
3140 void (*help_callback)(char const *, int, int))
3141{
3142 struct tty_menu_state *state;
3143 int statecount, x, y, i, b, screensize, leave, result, onepane;
3144 int title_faces[4]; /* face to display the menu title */
3145 int faces[4], buffers_num_deleted = 0;
3146 struct frame *sf = SELECTED_FRAME ();
3147 Lisp_Object saved_echo_area_message, selectface;
3148
3149 /* Don't allow non-positive x0 and y0, lest the menu will wrap
3150 around the display. */
3151 if (x0 <= 0)
3152 x0 = 1;
3153 if (y0 <= 0)
3154 y0 = 1;
3155
3156 /* We will process all the mouse events directly, so we had
3157 better prevent dos_rawgetc from stealing them from us. */
3158 mouse_preempted++;
3159
3160 state = alloca (menu->panecount * sizeof (struct tty_menu_state));
3161 screensize = screen_size * 2;
3162 faces[0]
3163 = lookup_derived_face (sf, intern ("tty-menu-disabled-face"),
3164 DEFAULT_FACE_ID, 1);
3165 faces[1]
3166 = lookup_derived_face (sf, intern ("tty-menu-enabled-face"),
3167 DEFAULT_FACE_ID, 1);
3168 selectface = intern ("tty-menu-selected-face");
3169 faces[2] = lookup_derived_face (sf, selectface,
3170 faces[0], 1);
3171 faces[3] = lookup_derived_face (sf, selectface,
3172 faces[1], 1);
3173
3174 /* Make sure the menu title is always displayed with
3175 `msdos-menu-active-face', no matter where the mouse pointer is. */
3176 for (i = 0; i < 4; i++)
3177 title_faces[i] = faces[3];
3178
3179 statecount = 1;
3180
3181 /* Don't let the title for the "Buffers" popup menu include a
3182 digit (which is ugly).
3183
3184 This is a terrible kludge, but I think the "Buffers" case is
3185 the only one where the title includes a number, so it doesn't
3186 seem to be necessary to make this more general. */
3187 if (strncmp (menu->text[0], "Buffers 1", 9) == 0)
3188 {
3189 menu->text[0][7] = '\0';
3190 buffers_num_deleted = 1;
3191 }
3192
3193#if 0
3194 /* We need to save the current echo area message, so that we could
3195 restore it below, before we exit. See the commentary below,
3196 before the call to message_with_string. */
3197 saved_echo_area_message = Fcurrent_message ();
3198#endif
3199 state[0].menu = menu;
3200 mouse_off (); /* FIXME */
3201 ScreenRetrieve (state[0].screen_behind = xmalloc (screensize)); /* FIXME */
3202
3203 /* Turn off the cursor. Otherwise it shows through the menu
3204 panes, which is ugly. */
3205 show_cursor (0); /* FIXME: need a new hook. */
3206
3207 /* Display the menu title. */
3208 tty_menu_display (menu, y0 - 1, x0 - 1, 1, title_faces, 0);
3209 if (buffers_num_deleted)
3210 menu->text[0][7] = ' ';
3211 if ((onepane = menu->count == 1 && menu->submenu[0]))
3212 {
3213 menu->width = menu->submenu[0]->width;
3214 state[0].menu = menu->submenu[0];
3215 }
3216 else
3217 {
3218 state[0].menu = menu;
3219 }
3220 state[0].x = x0 - 1;
3221 state[0].y = y0;
3222 state[0].pane = onepane;
3223
3224 mouse_last_x = -1; /* A hack that forces display. */
3225 leave = 0;
3226 while (!leave)
3227 {
3228 if (!mouse_visible) mouse_on ();
3229 mouse_check_moved ();
3230 if (sf->mouse_moved)
3231 {
3232 sf->mouse_moved = 0;
3233 result = TTYM_IA_SELECT;
3234 mouse_get_xy (&x, &y);
3235 for (i = 0; i < statecount; i++)
3236 if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
3237 {
3238 int dy = y - state[i].y;
3239 if (0 <= dy && dy < state[i].menu->count)
3240 {
3241 if (!state[i].menu->submenu[dy])
3242 {
3243 if (state[i].menu->panenumber[dy])
3244 result = TTYM_SUCCESS;
3245 else
3246 result = TTYM_IA_SELECT;
3247 }
3248 *pane = state[i].pane - 1;
3249 *selidx = dy;
3250 /* We hit some part of a menu, so drop extra menus that
3251 have been opened. That does not include an open and
3252 active submenu. */
3253 if (i != statecount - 2
3254 || state[i].menu->submenu[dy] != state[i+1].menu)
3255 while (i != statecount - 1)
3256 {
3257 statecount--;
3258 mouse_off ();
3259 ScreenUpdate (state[statecount].screen_behind);
3260 xfree (state[statecount].screen_behind);
3261 }
3262 if (i == statecount - 1 && state[i].menu->submenu[dy])
3263 {
3264 tty_menu_display (state[i].menu,
3265 state[i].y,
3266 state[i].x,
3267 state[i].pane,
3268 faces, 1);
3269 state[statecount].menu = state[i].menu->submenu[dy];
3270 state[statecount].pane = state[i].menu->panenumber[dy];
3271 mouse_off ();
3272 ScreenRetrieve (state[statecount].screen_behind
3273 = xmalloc (screensize));
3274 state[statecount].x
3275 = state[i].x + state[i].menu->width + 2;
3276 state[statecount].y = y;
3277 statecount++;
3278 }
3279 }
3280 }
3281 tty_menu_display (state[statecount - 1].menu,
3282 state[statecount - 1].y,
3283 state[statecount - 1].x,
3284 state[statecount - 1].pane,
3285 faces, 1);
3286 }
3287 else
3288 {
3289 if ((menu_help_message || prev_menu_help_message)
3290 && menu_help_message != prev_menu_help_message)
3291 {
3292 help_callback (menu_help_message,
3293 menu_help_paneno, menu_help_itemno);
3294 show_cursor (0);
3295 prev_menu_help_message = menu_help_message;
3296 }
3297 /* We are busy-waiting for the mouse to move, so let's be nice
3298 to other Windows applications by releasing our time slice. */
3299 __dpmi_yield ();
3300 }
3301 for (b = 0; b < mouse_button_count && !leave; b++)
3302 {
3303 /* Only leave if user both pressed and released the mouse, and in
3304 that order. This avoids popping down the menu pane unless
3305 the user is really done with it. */
3306 if (mouse_pressed (b, &x, &y))
3307 {
3308 while (mouse_button_depressed (b, &x, &y))
3309 __dpmi_yield ();
3310 leave = 1;
3311 }
3312 (void) mouse_released (b, &x, &y);
3313 }
3314 }
3315
3316 mouse_off ();
3317 ScreenUpdate (state[0].screen_behind);
3318
3319#if 0
3320 /* We have a situation here. ScreenUpdate has just restored the
3321 screen contents as it was before we started drawing this menu.
3322 That includes any echo area message that could have been
3323 displayed back then. (In reality, that echo area message will
3324 almost always be the ``keystroke echo'' that echoes the sequence
3325 of menu items chosen by the user.) However, if the menu had some
3326 help messages, then displaying those messages caused Emacs to
3327 forget about the original echo area message. So when
3328 ScreenUpdate restored it, it created a discrepancy between the
3329 actual screen contents and what Emacs internal data structures
3330 know about it.
3331
3332 To avoid this conflict, we force Emacs to restore the original
3333 echo area message as we found it when we entered this function.
3334 The irony of this is that we then erase the restored message
3335 right away, so the only purpose of restoring it is so that
3336 erasing it works correctly... */
3337 if (! NILP (saved_echo_area_message))
3338 message_with_string ("%s", saved_echo_area_message, 0);
3339 message (0);
3340#endif
3341 while (statecount--)
3342 xfree (state[statecount].screen_behind);
3343 show_cursor (1); /* turn cursor back on */
3344 /* Clean up any mouse events that are waiting inside Emacs event queue.
3345 These events are likely to be generated before the menu was even
3346 displayed, probably because the user pressed and released the button
3347 (which invoked the menu) too quickly. If we don't remove these events,
3348 Emacs will process them after we return and surprise the user. */
3349 discard_mouse_events ();
3350 mouse_clear_clicks ();
3351 if (!kbd_buffer_events_waiting (1))
3352 clear_input_pending ();
3353 /* Allow mouse events generation by dos_rawgetc. */
3354 mouse_preempted--;
3355 return result;
3356}
3357
3358/* Dispose of a menu. */
3359
3360void
3361tty_menu_destroy (tty_menu *menu)
3362{
3363 int i;
3364 if (menu->allocated)
3365 {
3366 for (i = 0; i < menu->count; i++)
3367 if (menu->submenu[i])
3368 tty_menu_destroy (menu->submenu[i]);
3369 xfree (menu->text);
3370 xfree (menu->submenu);
3371 xfree (menu->panenumber);
3372 xfree (menu->help_text);
3373 }
3374 xfree (menu);
3375 menu_help_message = prev_menu_help_message = NULL;
3376}
3377
3378Lisp_Object
3379tty_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
3380 Lisp_Object title, const char **error_name)
3381{
3382 tty_menu *menu;
3383 int pane, selidx, lpane, status;
3384 Lisp_Object entry, pane_prefix;
3385 char *datap;
3386 int ulx, uly, width, height;
3387 int dispwidth, dispheight;
3388 int i, j, lines, maxlines;
3389 int maxwidth;
3390 int dummy_int;
3391 unsigned int dummy_uint;
3392 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
3393
3394 if (! FRAME_TERMCAP_P (f))
3395 abort ();
3396
3397 *error_name = 0;
3398 if (menu_items_n_panes == 0)
3399 return Qnil;
3400
3401 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
3402 {
3403 *error_name = "Empty menu";
3404 return Qnil;
3405 }
3406
3407 /* Make the menu on that window. */
3408 menu = tty_menu_create ();
3409 if (menu == NULL)
3410 {
3411 *error_name = "Can't create menu";
3412 return Qnil;
3413 }
3414
3415 /* Don't GC while we prepare and show the menu, because we give the
3416 menu functions pointers to the contents of strings. */
3417 inhibit_garbage_collection ();
3418
3419 /* Adjust coordinates to be root-window-relative. */
3420 x += f->left_pos;
3421 y += f->top_pos;
3422
3423 /* Create all the necessary panes and their items. */
3424 maxwidth = maxlines = lines = i = 0;
3425 lpane = TTYM_FAILURE;
3426 while (i < menu_items_used)
3427 {
3428 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
3429 {
3430 /* Create a new pane. */
3431 Lisp_Object pane_name, prefix;
3432 const char *pane_string;
3433
3434 maxlines = max (maxlines, lines);
3435 lines = 0;
3436 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
3437 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3438 pane_string = (NILP (pane_name)
3439 ? "" : SSDATA (pane_name));
3440 if (keymaps && !NILP (prefix))
3441 pane_string++;
3442
3443 lpane = tty_menu_add_pane (menu, pane_string);
3444 if (lpane == TTYM_FAILURE)
3445 {
3446 tty_menu_destroy (menu);
3447 *error_name = "Can't create pane";
3448 return Qnil;
3449 }
3450 i += MENU_ITEMS_PANE_LENGTH;
3451
3452 /* Find the width of the widest item in this pane. */
3453 j = i;
3454 while (j < menu_items_used)
3455 {
3456 Lisp_Object item;
3457 item = XVECTOR (menu_items)->contents[j];
3458 if (EQ (item, Qt))
3459 break;
3460 if (NILP (item))
3461 {
3462 j++;
3463 continue;
3464 }
3465 width = SBYTES (item);
3466 if (width > maxwidth)
3467 maxwidth = width;
3468
3469 j += MENU_ITEMS_ITEM_LENGTH;
3470 }
3471 }
3472 /* Ignore a nil in the item list.
3473 It's meaningful only for dialog boxes. */
3474 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
3475 i += 1;
3476 else
3477 {
3478 /* Create a new item within current pane. */
3479 Lisp_Object item_name, enable, descrip, help;
3480 char *item_data;
3481 char const *help_string;
3482
3483 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
3484 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
3485 descrip
3486 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
3487 help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
3488 help_string = STRINGP (help) ? SSDATA (help) : NULL;
3489
3490 if (!NILP (descrip))
3491 {
3492 /* if alloca is fast, use that to make the space,
3493 to reduce gc needs. */
3494 item_data = (char *) alloca (maxwidth + SBYTES (descrip) + 1);
3495 memcpy (item_data, SSDATA (item_name), SBYTES (item_name));
3496 for (j = SCHARS (item_name); j < maxwidth; j++)
3497 item_data[j] = ' ';
3498 memcpy (item_data + j, SSDATA (descrip), SBYTES (descrip));
3499 item_data[j + SBYTES (descrip)] = 0;
3500 }
3501 else
3502 item_data = SSDATA (item_name);
3503
3504 if (lpane == TTYM_FAILURE
3505 || (tty_menu_add_selection (menu, lpane, item_data,
3506 !NILP (enable), help_string)
3507 == TTYM_FAILURE))
3508 {
3509 tty_menu_destroy (menu);
3510 *error_name = "Can't add selection to menu";
3511 return Qnil;
3512 }
3513 i += MENU_ITEMS_ITEM_LENGTH;
3514 lines++;
3515 }
3516 }
3517
3518 maxlines = max (maxlines, lines);
3519
3520 /* All set and ready to fly. */
3521 dispwidth = f->text_cols;
3522 dispheight = f->text_lines;
3523 x = min (x, dispwidth);
3524 y = min (y, dispheight);
3525 x = max (x, 1);
3526 y = max (y, 1);
3527 tty_menu_locate (menu, x, y, &ulx, &uly, &width, &height);
3528 if (ulx+width > dispwidth)
3529 {
3530 x -= (ulx + width) - dispwidth;
3531 ulx = dispwidth - width;
3532 }
3533 if (uly+height > dispheight)
3534 {
3535 y -= (uly + height) - dispheight;
3536 uly = dispheight - height;
3537 }
3538
3539 if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
3540 {
3541 /* Move the menu away of the echo area, to avoid overwriting the
3542 menu with help echo messages or vice versa. */
3543 if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window))
3544 {
3545 y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
3546 uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
3547 }
3548 else
3549 {
3550 y--;
3551 uly--;
3552 }
3553 }
3554
3555 if (ulx < 0) x -= ulx;
3556 if (uly < 0) y -= uly;
3557
3558 if (! for_click)
3559 {
3560 /* If position was not given by a mouse click, adjust so upper left
3561 corner of the menu as a whole ends up at given coordinates. This
3562 is what x-popup-menu says in its documentation. */
3563 x += width/2;
3564 y += 1.5*height/(maxlines+2);
3565 }
3566
3567 pane = selidx = 0;
3568
3569 record_unwind_protect (pop_down_menu,
3570 Fcons (make_save_value (f, 0),
3571 make_save_value (menu, 0)));
3572
3573 /* Help display under X won't work because XMenuActivate contains
3574 a loop that doesn't give Emacs a chance to process it. */
3575 menu_help_frame = f;
3576 status = tty_menu_activate (menu, &pane, &selidx, x, y, &datap,
3577 menu_help_callback);
3578 entry = pane_prefix = Qnil;
3579
3580 switch (status)
3581 {
3582 case TTYM_SUCCESS:
3583 /* Find the item number SELIDX in pane number PANE. */
3584 i = 0;
3585 while (i < menu_items_used)
3586 {
3587 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
3588 {
3589 if (pane == 0)
3590 pane_prefix
3591 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3592 pane--;
3593 i += MENU_ITEMS_PANE_LENGTH;
3594 }
3595 else
3596 {
3597 if (pane == -1)
3598 {
3599 if (selidx == 0)
3600 {
3601 entry
3602 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
3603 if (keymaps != 0)
3604 {
3605 entry = Fcons (entry, Qnil);
3606 if (!NILP (pane_prefix))
3607 entry = Fcons (pane_prefix, entry);
3608 }
3609 break;
3610 }
3611 selidx--;
3612 }
3613 i += MENU_ITEMS_ITEM_LENGTH;
3614 }
3615 }
3616 break;
3617
3618 case TTYM_FAILURE:
3619 *error_name = "Can't activate menu";
3620 case TTYM_IA_SELECT:
3621 break;
3622 case TTYM_NO_SELECT:
3623 /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means
3624 the menu was invoked with a mouse event as POSITION). */
3625 if (! for_click)
3626 Fsignal (Qquit, Qnil);
3627 break;
3628 }
3629
3630 unbind_to (specpdl_count, Qnil);
3631
3632 return entry;
3633}
3634
3635#endif /* HAVE_MENUS && !MSDOS */
3636
3637
2837#ifndef MSDOS 3638#ifndef MSDOS
2838/*********************************************************************** 3639/***********************************************************************
2839 Initialization 3640 Initialization