diff options
| author | Eli Zaretskii | 2012-06-02 17:57:51 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2012-06-02 17:57:51 +0300 |
| commit | b5e9cbb6fdce4b7e8c5cd6ad1addf6e4af35da67 (patch) | |
| tree | c6a1b2060e5d56677f6785c2241311527fa917fa /src/term.c | |
| parent | f51b6486fc8b0e3fa7fd08cbf83b27ef0d5efe1a (diff) | |
| download | emacs-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.c | 801 |
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. */ | ||
| 2866 | static 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. */ | ||
| 2869 | static int menu_help_paneno, menu_help_itemno; | ||
| 2870 | |||
| 2871 | typedef 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 | |||
| 2885 | static tty_menu * | ||
| 2886 | tty_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 | |||
| 2898 | static void | ||
| 2899 | tty_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 | |||
| 2925 | static tty_menu * | ||
| 2926 | tty_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 | |||
| 2944 | static void | ||
| 2945 | tty_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 | |||
| 2975 | static void | ||
| 2976 | tty_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 | |||
| 3045 | int | ||
| 3046 | have_menus_p (void) { return 1; } | ||
| 3047 | |||
| 3048 | /* Create a new pane and place it on the outer-most level. */ | ||
| 3049 | |||
| 3050 | int | ||
| 3051 | tty_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 | |||
| 3081 | int | ||
| 3082 | tty_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 | |||
| 3116 | void | ||
| 3117 | tty_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 | |||
| 3126 | struct 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 | |||
| 3137 | int | ||
| 3138 | tty_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 | |||
| 3360 | void | ||
| 3361 | tty_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 | |||
| 3378 | Lisp_Object | ||
| 3379 | tty_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 |