/* Functions for GUI implemented with (HI)Toolbox on the Mac OS. Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs 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 3 of the License, or (at your option) any later version. GNU Emacs 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 GNU Emacs. If not, see . */ #include #include #include "lisp.h" #include "blockinput.h" #include "macterm.h" #if !TARGET_API_MAC_CARBON #include #include #include #include #include #include #include #include #include #include #include #include #if defined (__MRC__) || (__MSL__ >= 0x6000) #include #endif #if __profile__ #include #endif #endif /* not TARGET_API_MAC_CARBON */ #include "charset.h" #include "coding.h" #include "frame.h" #include "dispextern.h" #include "fontset.h" #include "termhooks.h" #include "buffer.h" #include "window.h" #include "keyboard.h" #include #ifndef MAC_OSX #include #endif /************************************************************************ General ************************************************************************/ /* The difference in pixels between the top left corner of the Emacs window (including possible window manager decorations) and FRAME_MAC_WINDOW (f). */ #define FRAME_OUTER_TO_INNER_DIFF_X(f) ((f)->x_pixels_diff) #define FRAME_OUTER_TO_INNER_DIFF_Y(f) ((f)->y_pixels_diff) #define mac_window_to_frame(wp) (((mac_output *) GetWRefCon (wp))->mFP) void mac_alert_sound_play () { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020 AlertSoundPlay (); #else SysBeep (1); #endif } /************************************************************************ Application ************************************************************************/ extern struct frame *mac_focus_frame P_ ((struct mac_display_info *)); extern void do_keystroke P_ ((EventKind, unsigned char, UInt32, UInt32, unsigned long, struct input_event *)); extern UInt32 mac_mapped_modifiers P_ ((UInt32, UInt32)); #if TARGET_API_MAC_CARBON extern int mac_to_emacs_modifiers P_ ((UInt32, UInt32)); #else extern int mac_to_emacs_modifiers P_ ((EventModifiers, EventModifiers)); #endif #if TARGET_API_MAC_CARBON /* Points to the variable `inev' in the function XTread_socket. It is used for passing an input event to the function back from Carbon/Apple event handlers. */ static struct input_event *read_socket_inev = NULL; extern const unsigned char keycode_to_xkeysym_table[]; extern EMACS_INT extra_keyboard_modifiers; extern Lisp_Object Qhi_command; #if USE_MAC_TSM static TSMDocumentID tsm_document_id; extern Lisp_Object Qtext_input; extern Lisp_Object Qupdate_active_input_area, Qunicode_for_key_event; extern Lisp_Object Vmac_ts_active_input_overlay, Vmac_ts_active_input_buf; extern Lisp_Object Qbefore_string; #endif static int mac_event_to_emacs_modifiers P_ ((EventRef)); static OSStatus install_menu_target_item_handler P_ ((void)); #ifdef MAC_OSX static OSStatus install_service_handler P_ ((void)); #endif extern OSStatus mac_store_event_ref_as_apple_event P_ ((AEEventClass, AEEventID, Lisp_Object, Lisp_Object, EventRef, UInt32, const EventParamName *, const EventParamType *)); extern int fast_find_position P_ ((struct window *, int, int *, int *, int *, int *, Lisp_Object)); extern struct glyph *x_y_to_hpos_vpos P_ ((struct window *, int, int, int *, int *, int *, int *, int *)); extern void mac_ax_selected_text_range P_ ((struct frame *, CFRange *)); #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 extern unsigned int mac_ax_number_of_characters P_ ((struct frame *)); #endif #if USE_MAC_TSM extern OSStatus mac_restore_keyboard_input_source P_ ((void)); extern void mac_save_keyboard_input_source P_ ((void)); static OSStatus mac_tsm_resume () { OSStatus err; err = ActivateTSMDocument (tsm_document_id); if (err == noErr) err = mac_restore_keyboard_input_source (); return err; } static OSStatus mac_tsm_suspend () { OSStatus err; mac_save_keyboard_input_source (); err = DeactivateTSMDocument (tsm_document_id); return err; } static void init_tsm () { #ifdef MAC_OSX static InterfaceTypeList types = {kUnicodeDocument}; #else static InterfaceTypeList types = {kTextService}; #endif NewTSMDocument (sizeof (types) / sizeof (types[0]), types, &tsm_document_id, 0); } #endif /* USE_MAC_TSM */ static pascal OSStatus mac_handle_keyboard_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err, result = eventNotHandledErr; UInt32 event_kind, key_code, modifiers; unsigned char char_code; event_kind = GetEventKind (event); switch (event_kind) { case kEventRawKeyDown: case kEventRawKeyRepeat: case kEventRawKeyUp: /* When using Carbon Events, we need to pass raw keyboard events to the TSM ourselves. If TSM handles it, it will pass back noErr, otherwise it will pass back "eventNotHandledErr" and we can process it normally. */ result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) break; if (read_socket_inev == NULL) break; #if USE_MAC_TSM if (read_socket_inev->kind != NO_EVENT) { result = noErr; break; } #endif if (event_kind == kEventRawKeyUp) break; err = GetEventParameter (event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &char_code); if (err != noErr) break; err = GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &key_code); if (err != noErr) break; err = GetEventParameter (event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); if (err != noErr) break; do_keystroke ((event_kind == kEventRawKeyDown ? keyDown : autoKey), char_code, key_code, modifiers, ((unsigned long) (GetEventTime (event) / kEventDurationMillisecond)), read_socket_inev); result = noErr; break; default: abort (); } return result; } static pascal OSStatus mac_handle_command_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err, result = eventNotHandledErr; HICommand command; static const EventParamName names[] = {kEventParamDirectObject, kEventParamKeyModifiers}; static const EventParamType types[] = {typeHICommand, typeUInt32}; int num_params = sizeof (names) / sizeof (names[0]); err = GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof (HICommand), NULL, &command); if (err != noErr) return eventNotHandledErr; switch (GetEventKind (event)) { case kEventCommandProcess: result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) break; err = GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof (HICommand), NULL, &command); if (err != noErr || command.commandID == 0) break; /* A HI command event is mapped to an Apple event whose event class symbol is `hi-command' and event ID is its command ID. */ err = mac_store_event_ref_as_apple_event (0, command.commandID, Qhi_command, Qnil, event, num_params, names, types); if (err == noErr) result = noErr; break; default: abort (); } return result; } static pascal OSStatus mac_handle_mouse_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err, result = eventNotHandledErr; switch (GetEventKind (event)) { case kEventMouseWheelMoved: { WindowRef wp; struct frame *f; EventMouseWheelAxis axis; SInt32 delta; Point point; result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr || read_socket_inev == NULL) break; f = mac_focus_frame (&one_mac_display_info); err = GetEventParameter (event, kEventParamWindowRef, typeWindowRef, NULL, sizeof (WindowRef), NULL, &wp); if (err != noErr || wp != FRAME_MAC_WINDOW (f)) break; err = GetEventParameter (event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof (EventMouseWheelAxis), NULL, &axis); if (err != noErr || axis != kEventMouseWheelAxisY) break; err = GetEventParameter (event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof (Point), NULL, &point); if (err != noErr) break; point.h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f); point.v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f); if (point.h < 0 || point.v < 0 || EQ (window_from_coordinates (f, point.h, point.v, 0, 0, 0, 1), f->tool_bar_window)) break; err = GetEventParameter (event, kEventParamMouseWheelDelta, typeSInt32, NULL, sizeof (SInt32), NULL, &delta); if (err != noErr) break; read_socket_inev->kind = WHEEL_EVENT; read_socket_inev->code = 0; read_socket_inev->modifiers = (mac_event_to_emacs_modifiers (event) | ((delta < 0) ? down_modifier : up_modifier)); XSETINT (read_socket_inev->x, point.h); XSETINT (read_socket_inev->y, point.v); XSETFRAME (read_socket_inev->frame_or_window, f); result = noErr; } break; default: abort (); } return result; } #if USE_MAC_TSM extern void mac_get_selected_range P_ ((struct window *, CFRange *)); extern int mac_store_buffer_text_to_unicode_chars P_ ((struct buffer *, int, int, UniChar *)); static pascal OSStatus mac_handle_text_input_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err, result; Lisp_Object id_key = Qnil; int num_params; const EventParamName *names; const EventParamType *types; static UInt32 seqno_uaia = 0; static const EventParamName names_uaia[] = {kEventParamTextInputSendComponentInstance, kEventParamTextInputSendRefCon, kEventParamTextInputSendSLRec, kEventParamTextInputSendFixLen, kEventParamTextInputSendText, kEventParamTextInputSendUpdateRng, kEventParamTextInputSendHiliteRng, kEventParamTextInputSendClauseRng, kEventParamTextInputSendPinRng, kEventParamTextInputSendTextServiceEncoding, kEventParamTextInputSendTextServiceMacEncoding, EVENT_PARAM_TEXT_INPUT_SEQUENCE_NUMBER}; static const EventParamType types_uaia[] = {typeComponentInstance, typeLongInteger, typeIntlWritingCode, typeLongInteger, #ifdef MAC_OSX typeUnicodeText, #else typeChar, #endif typeTextRangeArray, typeTextRangeArray, typeOffsetArray, typeTextRange, typeUInt32, typeUInt32, typeUInt32}; static const EventParamName names_ufke[] = {kEventParamTextInputSendComponentInstance, kEventParamTextInputSendRefCon, kEventParamTextInputSendSLRec, kEventParamTextInputSendText}; static const EventParamType types_ufke[] = {typeComponentInstance, typeLongInteger, typeIntlWritingCode, typeUnicodeText}; result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) return result; switch (GetEventKind (event)) { case kEventTextInputUpdateActiveInputArea: id_key = Qupdate_active_input_area; num_params = sizeof (names_uaia) / sizeof (names_uaia[0]); names = names_uaia; types = types_uaia; SetEventParameter (event, EVENT_PARAM_TEXT_INPUT_SEQUENCE_NUMBER, typeUInt32, sizeof (UInt32), &seqno_uaia); seqno_uaia++; result = noErr; break; case kEventTextInputUnicodeForKeyEvent: { EventRef kbd_event; UInt32 actual_size, modifiers, key_code; err = GetEventParameter (event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof (EventRef), NULL, &kbd_event); if (err == noErr) err = GetEventParameter (kbd_event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); if (err == noErr) err = GetEventParameter (kbd_event, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &key_code); if (err == noErr && mac_mapped_modifiers (modifiers, key_code)) /* There're mapped modifier keys. Process it in do_keystroke. */ break; if (err == noErr) err = GetEventParameter (kbd_event, kEventParamKeyUnicodes, typeUnicodeText, NULL, 0, &actual_size, NULL); if (err == noErr && actual_size == sizeof (UniChar)) { UniChar code; err = GetEventParameter (kbd_event, kEventParamKeyUnicodes, typeUnicodeText, NULL, sizeof (UniChar), NULL, &code); if (err == noErr && code < 0x80) { /* ASCII character. Process it in do_keystroke. */ if (read_socket_inev && code >= 0x20 && code <= 0x7e && !(key_code <= 0x7f && keycode_to_xkeysym_table [key_code])) { struct frame *f = mac_focus_frame (&one_mac_display_info); read_socket_inev->kind = ASCII_KEYSTROKE_EVENT; read_socket_inev->code = code; read_socket_inev->modifiers = mac_to_emacs_modifiers (modifiers, 0); read_socket_inev->modifiers |= (extra_keyboard_modifiers & (meta_modifier | alt_modifier | hyper_modifier | super_modifier)); XSETFRAME (read_socket_inev->frame_or_window, f); } break; } } if (err == noErr) { /* Non-ASCII keystrokes without mapped modifiers are processed at the Lisp level. */ id_key = Qunicode_for_key_event; num_params = sizeof (names_ufke) / sizeof (names_ufke[0]); names = names_ufke; types = types_ufke; result = noErr; } } break; case kEventTextInputOffsetToPos: { long byte_offset; struct frame *f; struct window *w; Point p; err = GetEventParameter (event, kEventParamTextInputSendTextOffset, typeLongInteger, NULL, sizeof (long), NULL, &byte_offset); if (err != noErr) break; if (STRINGP (Vmac_ts_active_input_buf) && SBYTES (Vmac_ts_active_input_buf) != 0) { if (!OVERLAYP (Vmac_ts_active_input_overlay)) break; /* Strictly speaking, this is not always correct because previous events may change some states about display. */ if (!NILP (Foverlay_get (Vmac_ts_active_input_overlay, Qbefore_string))) { /* Active input area is displayed around the current point. */ f = SELECTED_FRAME (); w = XWINDOW (f->selected_window); } else if (WINDOWP (echo_area_window)) { /* Active input area is displayed in the echo area. */ w = XWINDOW (echo_area_window); f = WINDOW_XFRAME (w); } else break; p.h = (WINDOW_TO_FRAME_PIXEL_X (w, w->cursor.x) + WINDOW_LEFT_FRINGE_WIDTH (w) + f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f)); p.v = (WINDOW_TO_FRAME_PIXEL_Y (w, w->cursor.y) + FONT_BASE (FRAME_FONT (f)) + f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f)); } else { #ifndef MAC_OSX break; #else /* MAC_OSX */ CFRange sel_range; int charpos; int hpos, vpos, x, y; struct glyph_row *row; struct glyph *glyph; XFontStruct *font; f = mac_focus_frame (&one_mac_display_info); w = XWINDOW (f->selected_window); mac_get_selected_range (w, &sel_range); charpos = (BUF_BEGV (XBUFFER (w->buffer)) + sel_range.location + byte_offset / (long) sizeof (UniChar)); if (!fast_find_position (w, charpos, &hpos, &vpos, &x, &y, Qnil)) { result = errOffsetInvalid; break; } row = MATRIX_ROW (w->current_matrix, vpos); glyph = row->glyphs[TEXT_AREA] + hpos; if (glyph->type != CHAR_GLYPH || glyph->glyph_not_available_p) break; p.h = (WINDOW_TEXT_TO_FRAME_PIXEL_X (w, x) + f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f)); p.v = (WINDOW_TO_FRAME_PIXEL_Y (w, y) + row->visible_height + f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f)); font = FACE_FROM_ID (f, glyph->face_id)->font; if (font) { Fixed point_size = Long2Fix (font->mac_fontsize); short height = row->visible_height; short ascent = row->ascent; SetEventParameter (event, kEventParamTextInputReplyPointSize, typeFixed, sizeof (Fixed), &point_size); SetEventParameter (event, kEventParamTextInputReplyLineHeight, typeShortInteger, sizeof (short), &height); SetEventParameter (event, kEventParamTextInputReplyLineAscent, typeShortInteger, sizeof (short), &ascent); if (font->mac_fontnum != -1) { OSStatus err1; FMFont fm_font; FMFontStyle style; err1 = FMGetFontFromFontFamilyInstance (font->mac_fontnum, font->mac_fontface, &fm_font, &style); if (err1 == noErr) SetEventParameter (event, kEventParamTextInputReplyFMFont, typeUInt32, sizeof (UInt32), &fm_font); else { long qd_font = font->mac_fontnum; SetEventParameter (event, kEventParamTextInputReplyFont, typeLongInteger, sizeof (long), &qd_font); } } else if (font->mac_style) { OSStatus err1; ATSUFontID font_id; err1 = ATSUGetAttribute (font->mac_style, kATSUFontTag, sizeof (ATSUFontID), &font_id, NULL); if (err1 == noErr) SetEventParameter (event, kEventParamTextInputReplyFMFont, typeUInt32, sizeof (UInt32), &font_id); } else abort (); } #endif /* MAC_OSX */ } err = SetEventParameter (event, kEventParamTextInputReplyPoint, typeQDPoint, sizeof (Point), &p); if (err == noErr) result = noErr; } break; #ifdef MAC_OSX case kEventTextInputPosToOffset: { Point point; Boolean leading_edge_p = true; struct frame *f; int x, y; Lisp_Object window; enum window_part part; long region_class = kTSMOutsideOfBody, byte_offset = 0; err = GetEventParameter (event, kEventParamTextInputSendCurrentPoint, typeQDPoint, NULL, sizeof (Point), NULL, &point); if (err != noErr) break; GetEventParameter (event, kEventParamTextInputReplyLeadingEdge, typeBoolean, NULL, sizeof (Boolean), NULL, &leading_edge_p); f = mac_focus_frame (&one_mac_display_info); x = point.h - (f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f)); y = point.v - (f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f)); window = window_from_coordinates (f, x, y, &part, 0, 0, 1); if (WINDOWP (window) && EQ (window, f->selected_window)) { struct window *w; struct buffer *b; /* Convert to window-relative pixel coordinates. */ w = XWINDOW (window); frame_to_window_pixel_xy (w, &x, &y); /* Are we in a window whose display is up to date? And verify the buffer's text has not changed. */ b = XBUFFER (w->buffer); if (part == ON_TEXT && EQ (w->window_end_valid, w->buffer) && XINT (w->last_modified) == BUF_MODIFF (b) && XINT (w->last_overlay_modified) == BUF_OVERLAY_MODIFF (b)) { int hpos, vpos, area; struct glyph *glyph; /* Find the glyph under X/Y. */ glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, 0, 0, &area); if (glyph != NULL && area == TEXT_AREA) { byte_offset = ((glyph->charpos - BUF_BEGV (b)) * sizeof (UniChar)); region_class = kTSMInsideOfBody; } } } err = SetEventParameter (event, kEventParamTextInputReplyRegionClass, typeLongInteger, sizeof (long), ®ion_class); if (err == noErr) err = SetEventParameter (event, kEventParamTextInputReplyTextOffset, typeLongInteger, sizeof (long), &byte_offset); if (err == noErr) result = noErr; } break; case kEventTextInputGetSelectedText: { struct frame *f = mac_focus_frame (&one_mac_display_info); struct window *w = XWINDOW (f->selected_window); struct buffer *b = XBUFFER (w->buffer); CFRange sel_range; int start, end; UniChar *characters, c; if (poll_suppress_count == 0 && !NILP (Vinhibit_quit)) /* Don't try to get buffer contents as the gap might be being altered. */ break; mac_get_selected_range (w, &sel_range); if (sel_range.length == 0) { Boolean leading_edge_p; err = GetEventParameter (event, kEventParamTextInputReplyLeadingEdge, typeBoolean, NULL, sizeof (Boolean), NULL, &leading_edge_p); if (err != noErr) break; start = BUF_BEGV (b) + sel_range.location; if (!leading_edge_p) start--; end = start + 1; characters = &c; if (start < BUF_BEGV (b) || end > BUF_ZV (b)) break; } else { start = BUF_BEGV (b) + sel_range.location; end = start + sel_range.length; characters = xmalloc (sel_range.length * sizeof (UniChar)); } if (mac_store_buffer_text_to_unicode_chars (b, start, end, characters)) err = SetEventParameter (event, kEventParamTextInputReplyText, typeUnicodeText, sel_range.length * sizeof (UniChar), characters); if (characters != &c) xfree (characters); if (err == noErr) result = noErr; } break; #endif /* MAC_OSX */ default: abort (); } if (!NILP (id_key)) err = mac_store_event_ref_as_apple_event (0, 0, Qtext_input, id_key, event, num_params, names, types); return result; } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 static pascal OSStatus mac_handle_document_access_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err, result; struct frame *f = mac_focus_frame (&one_mac_display_info); result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) return result; switch (GetEventKind (event)) { case kEventTSMDocumentAccessGetLength: { CFIndex count = mac_ax_number_of_characters (f); err = SetEventParameter (event, kEventParamTSMDocAccessCharacterCount, typeCFIndex, sizeof (CFIndex), &count); if (err == noErr) result = noErr; } break; case kEventTSMDocumentAccessGetSelectedRange: { CFRange sel_range; mac_ax_selected_text_range (f, &sel_range); err = SetEventParameter (event, kEventParamTSMDocAccessReplyCharacterRange, typeCFRange, sizeof (CFRange), &sel_range); if (err == noErr) result = noErr; } break; case kEventTSMDocumentAccessGetCharacters: { struct buffer *b = XBUFFER (XWINDOW (f->selected_window)->buffer); CFRange range; Ptr characters; int start, end; if (poll_suppress_count == 0 && !NILP (Vinhibit_quit)) /* Don't try to get buffer contents as the gap might be being altered. */ break; err = GetEventParameter (event, kEventParamTSMDocAccessSendCharacterRange, typeCFRange, NULL, sizeof (CFRange), NULL, &range); if (err == noErr) err = GetEventParameter (event, kEventParamTSMDocAccessSendCharactersPtr, typePtr, NULL, sizeof (Ptr), NULL, &characters); if (err != noErr) break; start = BUF_BEGV (b) + range.location; end = start + range.length; if (mac_store_buffer_text_to_unicode_chars (b, start, end, (UniChar *) characters)) result = noErr; } break; default: abort (); } return result; } #endif #endif OSStatus install_application_handler () { OSStatus err = noErr; if (err == noErr) { static const EventTypeSpec specs[] = {{kEventClassKeyboard, kEventRawKeyDown}, {kEventClassKeyboard, kEventRawKeyRepeat}, {kEventClassKeyboard, kEventRawKeyUp}}; err = InstallApplicationEventHandler (NewEventHandlerUPP (mac_handle_keyboard_event), GetEventTypeCount (specs), specs, NULL, NULL); } if (err == noErr) { static const EventTypeSpec specs[] = {{kEventClassCommand, kEventCommandProcess}}; err = InstallApplicationEventHandler (NewEventHandlerUPP (mac_handle_command_event), GetEventTypeCount (specs), specs, NULL, NULL); } if (err == noErr) { static const EventTypeSpec specs[] = {{kEventClassMouse, kEventMouseWheelMoved}}; err = InstallApplicationEventHandler (NewEventHandlerUPP (mac_handle_mouse_event), GetEventTypeCount (specs), specs, NULL, NULL); } #if USE_MAC_TSM if (err == noErr) { static const EventTypeSpec specs[] = {{kEventClassTextInput, kEventTextInputUpdateActiveInputArea}, {kEventClassTextInput, kEventTextInputUnicodeForKeyEvent}, {kEventClassTextInput, kEventTextInputOffsetToPos}, #ifdef MAC_OSX {kEventClassTextInput, kEventTextInputPosToOffset}, {kEventClassTextInput, kEventTextInputGetSelectedText} #endif }; err = InstallApplicationEventHandler (NewEventHandlerUPP (mac_handle_text_input_event), GetEventTypeCount (specs), specs, NULL, NULL); } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 if (err == noErr) { static const EventTypeSpec specs[] = {{kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength}, {kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange}, {kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters}}; err = InstallApplicationEventHandler (mac_handle_document_access_event, GetEventTypeCount (specs), specs, NULL, NULL); } #endif #endif if (err == noErr) err = install_menu_target_item_handler (); #ifdef MAC_OSX if (err == noErr) err = install_service_handler (); #endif return err; } #endif /* TARGET_API_MAC_CARBON */ /************************************************************************ Windows ************************************************************************/ #define DEFAULT_NUM_COLS 80 #define MIN_DOC_SIZE 64 #define MAX_DOC_SIZE 32767 /* Drag and Drop */ static OSErr install_drag_handler P_ ((WindowRef)); static void remove_drag_handler P_ ((WindowRef)); #if USE_CG_DRAWING static void mac_prepare_for_quickdraw P_ ((struct frame *)); #endif extern void mac_handle_visibility_change P_ ((struct frame *)); extern void mac_handle_origin_change P_ ((struct frame *)); extern void mac_handle_size_change P_ ((struct frame *, int, int)); #if TARGET_API_MAC_CARBON #ifdef MAC_OSX extern Lisp_Object Qwindow; extern Lisp_Object Qtoolbar_switch_mode; #endif #endif static void do_window_update (WindowRef win) { struct frame *f = mac_window_to_frame (win); BeginUpdate (win); /* The tooltip has been drawn already. Avoid the SET_FRAME_GARBAGED below. */ if (win != tip_window) { if (f->async_visible == 0) { /* Update events may occur when a frame gets iconified. */ #if 0 f->async_visible = 1; f->async_iconified = 0; SET_FRAME_GARBAGED (f); #endif } else { Rect r; #if TARGET_API_MAC_CARBON RgnHandle region = NewRgn (); GetPortVisibleRegion (GetWindowPort (win), region); GetRegionBounds (region, &r); expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top); #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif UpdateControls (win, region); DisposeRgn (region); #else r = (*win->visRgn)->rgnBBox; expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top); UpdateControls (win, win->visRgn); #endif } } EndUpdate (win); } static int is_emacs_window (WindowRef win) { Lisp_Object tail, frame; if (!win) return 0; FOR_EACH_FRAME (tail, frame) if (FRAME_MAC_P (XFRAME (frame))) if (FRAME_MAC_WINDOW (XFRAME (frame)) == win) return 1; return 0; } /* Handle drags in size box. Based on code contributed by Ben Mesander and IM - Window Manager A. */ static void do_grow_window (w, e) WindowRef w; const EventRecord *e; { Rect limit_rect; int rows, columns, width, height; struct frame *f = mac_window_to_frame (w); XSizeHints *size_hints = FRAME_SIZE_HINTS (f); int min_width = MIN_DOC_SIZE, min_height = MIN_DOC_SIZE; #if TARGET_API_MAC_CARBON Rect new_rect; #else long grow_size; #endif if (size_hints->flags & PMinSize) { min_width = size_hints->min_width; min_height = size_hints->min_height; } SetRect (&limit_rect, min_width, min_height, MAX_DOC_SIZE, MAX_DOC_SIZE); #if TARGET_API_MAC_CARBON if (!ResizeWindow (w, e->where, &limit_rect, &new_rect)) return; height = new_rect.bottom - new_rect.top; width = new_rect.right - new_rect.left; #else grow_size = GrowWindow (w, e->where, &limit_rect); /* see if it really changed size */ if (grow_size == 0) return; height = HiWord (grow_size); width = LoWord (grow_size); #endif if (width != FRAME_PIXEL_WIDTH (f) || height != FRAME_PIXEL_HEIGHT (f)) { rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height); columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width); x_set_window_size (f, 0, columns, rows); } } #if TARGET_API_MAC_CARBON static Point mac_get_ideal_size (f) struct frame *f; { struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f); WindowRef w = FRAME_MAC_WINDOW (f); Point ideal_size; Rect standard_rect; int height, width, columns, rows; ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS); ideal_size.v = dpyinfo->height; IsWindowInStandardState (w, &ideal_size, &standard_rect); /* Adjust the standard size according to character boundaries. */ width = standard_rect.right - standard_rect.left; height = standard_rect.bottom - standard_rect.top; columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width); rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height); ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, columns); ideal_size.v = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows); return ideal_size; } static pascal OSStatus mac_handle_window_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { WindowRef wp; OSStatus err, result = eventNotHandledErr; struct frame *f; UInt32 attributes; XSizeHints *size_hints; err = GetEventParameter (event, kEventParamDirectObject, typeWindowRef, NULL, sizeof (WindowRef), NULL, &wp); if (err != noErr) return eventNotHandledErr; f = mac_window_to_frame (wp); switch (GetEventKind (event)) { /* -- window refresh events -- */ case kEventWindowUpdate: result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) break; do_window_update (wp); result = noErr; break; /* -- window state change events -- */ case kEventWindowShowing: size_hints = FRAME_SIZE_HINTS (f); if (!(size_hints->flags & (USPosition | PPosition))) { struct frame *sf = SELECTED_FRAME (); if (!(FRAME_MAC_P (sf) && sf->async_visible)) RepositionWindow (wp, NULL, kWindowCenterOnMainScreen); else { RepositionWindow (wp, FRAME_MAC_WINDOW (sf), #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020 kWindowCascadeStartAtParentWindowScreen #else kWindowCascadeOnParentWindowScreen #endif ); #if USE_MAC_TOOLBAR /* This is a workaround. RepositionWindow fails to put a window at the cascading position when its parent window has a Carbon HIToolbar. */ if ((f->left_pos == sf->left_pos && f->top_pos == sf->top_pos) || (f->left_pos == sf->left_pos + 10 * 2 && f->top_pos == sf->top_pos + 32 * 2)) MoveWindowStructure (wp, sf->left_pos + 10, sf->top_pos + 32); #endif } result = noErr; } break; case kEventWindowHiding: /* Before unmapping the window, update the WM_SIZE_HINTS property to claim that the current position of the window is user-specified, rather than program-specified, so that when the window is mapped again, it will be placed at the same location, without forcing the user to position it by hand again (they have already done that once for this window.) */ x_wm_set_size_hint (f, (long) 0, 1); result = noErr; break; case kEventWindowShown: case kEventWindowHidden: case kEventWindowCollapsed: case kEventWindowExpanded: mac_handle_visibility_change (f); result = noErr; break; case kEventWindowBoundsChanging: result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) break; err = GetEventParameter (event, kEventParamAttributes, typeUInt32, NULL, sizeof (UInt32), NULL, &attributes); if (err != noErr) break; size_hints = FRAME_SIZE_HINTS (f); if ((attributes & kWindowBoundsChangeUserResize) && ((size_hints->flags & (PResizeInc | PBaseSize | PMinSize)) == (PResizeInc | PBaseSize | PMinSize))) { Rect bounds; int width, height; err = GetEventParameter (event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof (Rect), NULL, &bounds); if (err != noErr) break; width = bounds.right - bounds.left; height = bounds.bottom - bounds.top; if (width < size_hints->min_width) width = size_hints->min_width; else width = size_hints->base_width + (int) ((width - size_hints->base_width) / (float) size_hints->width_inc + .5) * size_hints->width_inc; if (height < size_hints->min_height) height = size_hints->min_height; else height = size_hints->base_height + (int) ((height - size_hints->base_height) / (float) size_hints->height_inc + .5) * size_hints->height_inc; bounds.right = bounds.left + width; bounds.bottom = bounds.top + height; SetEventParameter (event, kEventParamCurrentBounds, typeQDRectangle, sizeof (Rect), &bounds); result = noErr; } break; case kEventWindowBoundsChanged: err = GetEventParameter (event, kEventParamAttributes, typeUInt32, NULL, sizeof (UInt32), NULL, &attributes); if (err != noErr) break; if (attributes & kWindowBoundsChangeSizeChanged) { Rect bounds; err = GetEventParameter (event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof (Rect), NULL, &bounds); if (err == noErr) { int width, height; width = bounds.right - bounds.left; height = bounds.bottom - bounds.top; mac_handle_size_change (f, width, height); mac_wakeup_from_rne (); } } if (attributes & kWindowBoundsChangeOriginChanged) mac_handle_origin_change (f); result = noErr; break; /* -- window action events -- */ case kEventWindowClose: { struct input_event buf; EVENT_INIT (buf); buf.kind = DELETE_WINDOW_EVENT; XSETFRAME (buf.frame_or_window, f); buf.arg = Qnil; kbd_buffer_store_event (&buf); } result = noErr; break; case kEventWindowGetIdealSize: result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) break; { Point ideal_size = mac_get_ideal_size (f); err = SetEventParameter (event, kEventParamDimensions, typeQDPoint, sizeof (Point), &ideal_size); if (err == noErr) result = noErr; } break; #ifdef MAC_OSX case kEventWindowToolbarSwitchMode: { static const EventParamName names[] = {kEventParamDirectObject, kEventParamWindowMouseLocation, kEventParamKeyModifiers, kEventParamMouseButton, kEventParamClickCount, kEventParamMouseChord}; static const EventParamType types[] = {typeWindowRef, typeQDPoint, typeUInt32, typeMouseButton, typeUInt32, typeUInt32}; int num_params = sizeof (names) / sizeof (names[0]); err = mac_store_event_ref_as_apple_event (0, 0, Qwindow, Qtoolbar_switch_mode, event, num_params, names, types); } if (err == noErr) result = noErr; break; #endif #if USE_MAC_TSM /* -- window focus events -- */ case kEventWindowFocusAcquired: err = mac_tsm_resume (); if (err == noErr) result = noErr; break; case kEventWindowFocusRelinquish: err = mac_tsm_suspend (); if (err == noErr) result = noErr; break; #endif default: abort (); } return result; } #endif /* Handle clicks in zoom box. Calculation of "standard state" based on code in IM - Window Manager A and code contributed by Ben Mesander. The standard state of an Emacs window is 80-characters wide (DEFAULT_NUM_COLS) and as tall as will fit on the screen. */ static void do_zoom_window (WindowRef w, int zoom_in_or_out) { Rect zoom_rect, port_rect; int width, height; struct frame *f = mac_window_to_frame (w); #if TARGET_API_MAC_CARBON Point ideal_size = mac_get_ideal_size (f); GetWindowBounds (w, kWindowContentRgn, &port_rect); if (IsWindowInStandardState (w, &ideal_size, &zoom_rect) && port_rect.left == zoom_rect.left && port_rect.top == zoom_rect.top) zoom_in_or_out = inZoomIn; else zoom_in_or_out = inZoomOut; #ifdef MAC_OS8 mac_clear_area (f, 0, 0, port_rect.right - port_rect.left, port_rect.bottom - port_rect.top); #endif ZoomWindowIdeal (w, zoom_in_or_out, &ideal_size); #else /* not TARGET_API_MAC_CARBON */ GrafPtr save_port; Point top_left; int w_title_height, rows; struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f); GetPort (&save_port); SetPortWindowPort (w); /* Clear window to avoid flicker. */ EraseRect (&(w->portRect)); if (zoom_in_or_out == inZoomOut) { SetPt (&top_left, w->portRect.left, w->portRect.top); LocalToGlobal (&top_left); /* calculate height of window's title bar */ w_title_height = top_left.v - 1 - (**((WindowPeek) w)->strucRgn).rgnBBox.top + GetMBarHeight (); /* get maximum height of window into zoom_rect.bottom - zoom_rect.top */ zoom_rect = qd.screenBits.bounds; zoom_rect.top += w_title_height; InsetRect (&zoom_rect, 8, 4); /* not too tight */ zoom_rect.right = zoom_rect.left + FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS); /* Adjust the standard size according to character boundaries. */ rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, zoom_rect.bottom - zoom_rect.top); zoom_rect.bottom = zoom_rect.top + FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows); (**((WStateDataHandle) ((WindowPeek) w)->dataHandle)).stdState = zoom_rect; } ZoomWindow (w, zoom_in_or_out, f == mac_focus_frame (dpyinfo)); SetPort (save_port); #endif /* not TARGET_API_MAC_CARBON */ #if !TARGET_API_MAC_CARBON /* retrieve window size and update application values */ port_rect = w->portRect; height = port_rect.bottom - port_rect.top; width = port_rect.right - port_rect.left; mac_handle_size_change (f, width, height); mac_handle_origin_change (f); #endif } static OSStatus install_window_handler (window) WindowRef window; { OSStatus err = noErr; #if TARGET_API_MAC_CARBON if (err == noErr) { static const EventTypeSpec specs[] = { /* -- window refresh events -- */ {kEventClassWindow, kEventWindowUpdate}, /* -- window state change events -- */ {kEventClassWindow, kEventWindowShowing}, {kEventClassWindow, kEventWindowHiding}, {kEventClassWindow, kEventWindowShown}, {kEventClassWindow, kEventWindowHidden}, {kEventClassWindow, kEventWindowCollapsed}, {kEventClassWindow, kEventWindowExpanded}, {kEventClassWindow, kEventWindowBoundsChanging}, {kEventClassWindow, kEventWindowBoundsChanged}, /* -- window action events -- */ {kEventClassWindow, kEventWindowClose}, {kEventClassWindow, kEventWindowGetIdealSize}, #ifdef MAC_OSX {kEventClassWindow, kEventWindowToolbarSwitchMode}, #endif #if USE_MAC_TSM /* -- window focus events -- */ {kEventClassWindow, kEventWindowFocusAcquired}, {kEventClassWindow, kEventWindowFocusRelinquish}, #endif }; static EventHandlerUPP handle_window_eventUPP = NULL; if (handle_window_eventUPP == NULL) handle_window_eventUPP = NewEventHandlerUPP (mac_handle_window_event); err = InstallWindowEventHandler (window, handle_window_eventUPP, GetEventTypeCount (specs), specs, NULL, NULL); } #endif if (err == noErr) err = install_drag_handler (window); return err; } static void remove_window_handler (window) WindowRef window; { remove_drag_handler (window); } void mac_get_window_bounds (f, inner, outer) struct frame *f; Rect *inner, *outer; { #if TARGET_API_MAC_CARBON GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowContentRgn, inner); GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowStructureRgn, outer); #else /* not TARGET_API_MAC_CARBON */ RgnHandle region = NewRgn (); GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowContentRgn, region); *inner = (*region)->rgnBBox; GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowStructureRgn, region); *outer = (*region)->rgnBBox; DisposeRgn (region); #endif /* not TARGET_API_MAC_CARBON */ } Rect * mac_get_frame_bounds (f, r) struct frame *f; Rect *r; { #if TARGET_API_MAC_CARBON return GetWindowPortBounds (FRAME_MAC_WINDOW (f), r); #else *r = FRAME_MAC_WINDOW (f)->portRect; return r; #endif } void mac_get_frame_mouse (f, point) struct frame *f; Point *point; { #if TARGET_API_MAC_CARBON GetGlobalMouse (point); point->h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f); point->v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f); #else SetPortWindowPort (FRAME_MAC_WINDOW (f)); GetMouse (point); #endif } void mac_convert_frame_point_to_global (f, x, y) struct frame *f; int *x, *y; { *x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f); *y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f); } #if TARGET_API_MAC_CARBON void mac_update_proxy_icon (f) struct frame *f; { OSStatus err; Lisp_Object file_name = XBUFFER (XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer)->filename; Window w = FRAME_MAC_WINDOW (f); AliasHandle alias = NULL; err = GetWindowProxyAlias (w, &alias); if (err == errWindowDoesNotHaveProxy && !STRINGP (file_name)) return; if (STRINGP (file_name)) { AEDesc desc; #ifdef MAC_OSX FSRef fref, fref_proxy; #else FSSpec fss, fss_proxy; #endif Boolean changed; Lisp_Object encoded_file_name = ENCODE_FILE (file_name); #ifdef MAC_OSX err = AECoercePtr (TYPE_FILE_NAME, SDATA (encoded_file_name), SBYTES (encoded_file_name), typeFSRef, &desc); #else SetPortWindowPort (w); err = AECoercePtr (TYPE_FILE_NAME, SDATA (encoded_file_name), SBYTES (encoded_file_name), typeFSS, &desc); #endif if (err == noErr) { #ifdef MAC_OSX err = AEGetDescData (&desc, &fref, sizeof (FSRef)); #else err = AEGetDescData (&desc, &fss, sizeof (FSSpec)); #endif AEDisposeDesc (&desc); } if (err == noErr) { if (alias) { /* (FS)ResolveAlias never sets `changed' to true if `alias' is minimal. */ #ifdef MAC_OSX err = FSResolveAlias (NULL, alias, &fref_proxy, &changed); if (err == noErr) err = FSCompareFSRefs (&fref, &fref_proxy); #else err = ResolveAlias (NULL, alias, &fss_proxy, &changed); if (err == noErr) err = !(fss.vRefNum == fss_proxy.vRefNum && fss.parID == fss_proxy.parID && EqualString (fss.name, fss_proxy.name, false, true)); #endif } if (err != noErr || alias == NULL) { if (alias) DisposeHandle ((Handle) alias); #ifdef MAC_OSX err = FSNewAliasMinimal (&fref, &alias); #else err = NewAliasMinimal (&fss, &alias); #endif changed = true; } } if (err == noErr) if (changed) err = SetWindowProxyAlias (w, alias); } if (alias) DisposeHandle ((Handle) alias); if (err != noErr || !STRINGP (file_name)) RemoveWindowProxy (w); } #endif /* Mac replacement for XSetWindowBackground. */ void mac_set_frame_window_background (f, color) struct frame *f; unsigned long color; { WindowRef w = FRAME_MAC_WINDOW (f); #if !TARGET_API_MAC_CARBON AuxWinHandle aw_handle; CTabHandle ctab_handle; ColorSpecPtr ct_table; short ct_size; #endif RGBColor bg_color; bg_color.red = RED16_FROM_ULONG (color); bg_color.green = GREEN16_FROM_ULONG (color); bg_color.blue = BLUE16_FROM_ULONG (color); #if TARGET_API_MAC_CARBON SetWindowContentColor (w, &bg_color); #else if (GetAuxWin (w, &aw_handle)) { ctab_handle = (*aw_handle)->awCTable; HandToHand ((Handle *) &ctab_handle); ct_table = (*ctab_handle)->ctTable; ct_size = (*ctab_handle)->ctSize; while (ct_size > -1) { if (ct_table->value == 0) { ct_table->rgb = bg_color; CTabChanged (ctab_handle); SetWinColor (w, (WCTabHandle) ctab_handle); } ct_size--; } } #endif } /* Flush display of frame F, or of all frames if F is null. */ void x_flush (f) struct frame *f; { #if TARGET_API_MAC_CARBON BLOCK_INPUT; #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif if (f) QDFlushPortBuffer (GetWindowPort (FRAME_MAC_WINDOW (f)), NULL); else QDFlushPortBuffer (GetQDGlobalsThePort (), NULL); UNBLOCK_INPUT; #endif } #if USE_CG_DRAWING void mac_flush_display_optional (f) struct frame *f; { BLOCK_INPUT; mac_prepare_for_quickdraw (f); UNBLOCK_INPUT; } #endif void mac_update_begin (f) struct frame *f; { #if TARGET_API_MAC_CARBON /* During update of a frame, availability of input events is periodically checked with ReceiveNextEvent if redisplay-dont-pause is nil. That normally flushes window buffer changes for every check, and thus screen update looks waving even if no input is available. So we disable screen updates during update of a frame. */ DisableScreenUpdates (); #endif } void mac_update_end (f) struct frame *f; { #if TARGET_API_MAC_CARBON EnableScreenUpdates (); #endif } void mac_frame_up_to_date (f) struct frame *f; { /* Nothing to do. */ } void mac_create_frame_window (f, tooltip_p) struct frame *f; int tooltip_p; { Rect r; #if TARGET_API_MAC_CARBON WindowClass window_class; WindowAttributes attributes; #else short proc_id; WindowRef behind; Boolean go_away_flag; #endif if (!tooltip_p) { SetRect (&r, f->left_pos, f->top_pos, f->left_pos + FRAME_PIXEL_WIDTH (f), f->top_pos + FRAME_PIXEL_HEIGHT (f)); #if TARGET_API_MAC_CARBON window_class = kDocumentWindowClass; attributes = (kWindowStandardDocumentAttributes #ifdef MAC_OSX | kWindowToolbarButtonAttribute #endif ); #else proc_id = zoomDocProc; behind = (WindowRef) -1; go_away_flag = true; #endif } else { SetRect (&r, 0, 0, 1, 1); #if TARGET_API_MAC_CARBON window_class = kHelpWindowClass; attributes = (kWindowNoUpdatesAttribute | kWindowNoActivatesAttribute #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020 | kWindowIgnoreClicksAttribute #endif ); #else proc_id = plainDBox; behind = NULL; go_away_flag = false; #endif } #if TARGET_API_MAC_CARBON CreateNewWindow (window_class, attributes, &r, &FRAME_MAC_WINDOW (f)); if (FRAME_MAC_WINDOW (f)) { SetWRefCon (FRAME_MAC_WINDOW (f), (long) f->output_data.mac); if (!tooltip_p) if (install_window_handler (FRAME_MAC_WINDOW (f)) != noErr) { DisposeWindow (FRAME_MAC_WINDOW (f)); FRAME_MAC_WINDOW (f) = NULL; } } #else /* !TARGET_API_MAC_CARBON */ FRAME_MAC_WINDOW (f) = NewCWindow (NULL, &r, "\p", false, proc_id, behind, go_away_flag, (long) f->output_data.mac); #endif /* !TARGET_API_MAC_CARBON */ /* so that update events can find this mac_output struct */ f->output_data.mac->mFP = f; /* point back to emacs frame */ #ifndef MAC_OSX if (!tooltip_p) if (FRAME_MAC_WINDOW (f)) { ControlRef root_control; if (CreateRootControl (FRAME_MAC_WINDOW (f), &root_control) != noErr) { DisposeWindow (FRAME_MAC_WINDOW (f)); FRAME_MAC_WINDOW (f) = NULL; } } #endif } /* Dispose of the Mac window of the frame F. */ void mac_dispose_frame_window (f) struct frame *f; { WindowRef window = FRAME_MAC_WINDOW (f); if (window != tip_window) remove_window_handler (window); #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif DisposeWindow (window); } /************************************************************************ View and Drawing ************************************************************************/ #if USE_CG_DRAWING #define FRAME_CG_CONTEXT(f) ((f)->output_data.mac->cg_context) CGContextRef mac_begin_cg_clip (f, gc) struct frame *f; GC gc; { CGContextRef context = FRAME_CG_CONTEXT (f); if (!context) { QDBeginCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)), &context); FRAME_CG_CONTEXT (f) = context; } CGContextSaveGState (context); CGContextTranslateCTM (context, 0, FRAME_PIXEL_HEIGHT (f)); CGContextScaleCTM (context, 1, -1); if (gc && gc->n_clip_rects) CGContextClipToRects (context, gc->clip_rects, gc->n_clip_rects); return context; } void mac_end_cg_clip (f) struct frame *f; { CGContextRestoreGState (FRAME_CG_CONTEXT (f)); } static void mac_prepare_for_quickdraw (f) struct frame *f; { if (f == NULL) { Lisp_Object rest, frame; FOR_EACH_FRAME (rest, frame) if (FRAME_MAC_P (XFRAME (frame))) mac_prepare_for_quickdraw (XFRAME (frame)); } else { CGContextRef context = FRAME_CG_CONTEXT (f); if (context) { CGContextSynchronize (context); QDEndCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)), &FRAME_CG_CONTEXT (f)); } } } #endif static RgnHandle saved_port_clip_region = NULL; void mac_begin_clip (f, gc) struct frame *f; GC gc; { static RgnHandle new_region = NULL; if (saved_port_clip_region == NULL) saved_port_clip_region = NewRgn (); if (new_region == NULL) new_region = NewRgn (); #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif SetPortWindowPort (FRAME_MAC_WINDOW (f)); if (gc && gc->n_clip_rects) { GetClip (saved_port_clip_region); SectRgn (saved_port_clip_region, gc->clip_region, new_region); SetClip (new_region); } } void mac_end_clip (f, gc) struct frame *f; GC gc; { if (gc && gc->n_clip_rects) SetClip (saved_port_clip_region); } #if TARGET_API_MAC_CARBON /* Mac replacement for XCopyArea: used only for scrolling. */ void mac_scroll_area (f, gc, src_x, src_y, width, height, dest_x, dest_y) struct frame *f; GC gc; int src_x, src_y; unsigned int width, height; int dest_x, dest_y; { Rect src_r; RgnHandle dummy = NewRgn (); /* For avoiding update events. */ SetRect (&src_r, src_x, src_y, src_x + width, src_y + height); #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif ScrollWindowRect (FRAME_MAC_WINDOW (f), &src_r, dest_x - src_x, dest_y - src_y, kScrollWindowNoOptions, dummy); DisposeRgn (dummy); } #endif /************************************************************************ Scroll bars ************************************************************************/ extern struct scroll_bar *tracked_scroll_bar; extern Lisp_Object last_mouse_scroll_bar; extern Time last_mouse_movement_time; static void x_scroll_bar_handle_click P_ ((struct scroll_bar *, ControlPartCode, const EventRecord *, struct input_event *)); #ifndef USE_TOOLKIT_SCROLL_BARS static void x_scroll_bar_note_movement P_ ((struct scroll_bar *, int, Time)); #else /* USE_TOOLKIT_SCROLL_BARS */ static void x_scroll_bar_handle_press P_ ((struct scroll_bar *, ControlPartCode, Point, struct input_event *)); static void x_scroll_bar_handle_release P_ ((struct scroll_bar *, struct input_event *)); static void x_scroll_bar_handle_drag P_ ((WindowRef, struct scroll_bar *, Point, struct input_event *)); static pascal void scroll_bar_timer_callback P_ ((EventLoopTimerRef, void *)); static OSStatus install_scroll_bar_timer P_ ((void)); static OSStatus set_scroll_bar_timer P_ ((EventTimerInterval)); static int control_part_code_to_scroll_bar_part P_ ((ControlPartCode)); static void construct_scroll_bar_click P_ ((struct scroll_bar *, int, struct input_event *)); static OSStatus get_control_part_bounds P_ ((ControlRef, ControlPartCode, Rect *)); static void update_scroll_bar_track_info P_ ((struct scroll_bar *)); /* Last scroll bar part sent in x_scroll_bar_handle_*. */ static int last_scroll_bar_part; static EventLoopTimerRef scroll_bar_timer; static int scroll_bar_timer_event_posted_p; #define SCROLL_BAR_FIRST_DELAY 0.5 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15) static pascal void scroll_bar_timer_callback (timer, data) EventLoopTimerRef timer; void *data; { OSStatus err; err = mac_post_mouse_moved_event (); if (err == noErr) scroll_bar_timer_event_posted_p = 1; } static OSStatus install_scroll_bar_timer () { static EventLoopTimerUPP scroll_bar_timer_callbackUPP = NULL; if (scroll_bar_timer_callbackUPP == NULL) scroll_bar_timer_callbackUPP = NewEventLoopTimerUPP (scroll_bar_timer_callback); if (scroll_bar_timer == NULL) /* Mac OS X and CarbonLib 1.5 and later allow us to specify kEventDurationForever as delays. */ return InstallEventLoopTimer (GetCurrentEventLoop (), kEventDurationForever, kEventDurationForever, scroll_bar_timer_callbackUPP, NULL, &scroll_bar_timer); } static OSStatus set_scroll_bar_timer (delay) EventTimerInterval delay; { if (scroll_bar_timer == NULL) install_scroll_bar_timer (); scroll_bar_timer_event_posted_p = 0; return SetEventLoopTimerNextFireTime (scroll_bar_timer, delay); } static int control_part_code_to_scroll_bar_part (part_code) ControlPartCode part_code; { switch (part_code) { case kControlUpButtonPart: return scroll_bar_up_arrow; case kControlDownButtonPart: return scroll_bar_down_arrow; case kControlPageUpPart: return scroll_bar_above_handle; case kControlPageDownPart: return scroll_bar_below_handle; case kControlIndicatorPart: return scroll_bar_handle; } return -1; } static void construct_scroll_bar_click (bar, part, bufp) struct scroll_bar *bar; int part; struct input_event *bufp; { bufp->kind = SCROLL_BAR_CLICK_EVENT; bufp->frame_or_window = bar->window; bufp->arg = Qnil; bufp->part = part; bufp->code = 0; XSETINT (bufp->x, 0); XSETINT (bufp->y, 0); bufp->modifiers = 0; } static OSStatus get_control_part_bounds (ch, part_code, rect) ControlRef ch; ControlPartCode part_code; Rect *rect; { RgnHandle region = NewRgn (); OSStatus err; err = GetControlRegion (ch, part_code, region); if (err == noErr) GetRegionBounds (region, rect); DisposeRgn (region); return err; } static void x_scroll_bar_handle_press (bar, part_code, mouse_pos, bufp) struct scroll_bar *bar; ControlPartCode part_code; Point mouse_pos; struct input_event *bufp; { int part = control_part_code_to_scroll_bar_part (part_code); if (part < 0) return; if (part != scroll_bar_handle) { construct_scroll_bar_click (bar, part, bufp); HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code); set_scroll_bar_timer (SCROLL_BAR_FIRST_DELAY); bar->dragging = Qnil; } else { Rect r; get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar), kControlIndicatorPart, &r); XSETINT (bar->dragging, - (mouse_pos.v - r.top) - 1); } last_scroll_bar_part = part; tracked_scroll_bar = bar; } static void x_scroll_bar_handle_release (bar, bufp) struct scroll_bar *bar; struct input_event *bufp; { if (last_scroll_bar_part != scroll_bar_handle || (INTEGERP (bar->dragging) && XINT (bar->dragging) >= 0)) construct_scroll_bar_click (bar, scroll_bar_end_scroll, bufp); HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0); set_scroll_bar_timer (kEventDurationForever); last_scroll_bar_part = -1; bar->dragging = Qnil; tracked_scroll_bar = NULL; } static void x_scroll_bar_handle_drag (win, bar, mouse_pos, bufp) WindowRef win; struct scroll_bar *bar; Point mouse_pos; struct input_event *bufp; { ControlRef ch = SCROLL_BAR_CONTROL_REF (bar); if (last_scroll_bar_part == scroll_bar_handle) { int top, top_range; Rect r; get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar), kControlIndicatorPart, &r); if (INTEGERP (bar->dragging) && XINT (bar->dragging) < 0) XSETINT (bar->dragging, - (XINT (bar->dragging) + 1)); top = mouse_pos.v - XINT (bar->dragging) - XINT (bar->track_top); top_range = XINT (bar->track_height) - XINT (bar->min_handle); if (top < 0) top = 0; if (top > top_range) top = top_range; construct_scroll_bar_click (bar, scroll_bar_handle, bufp); XSETINT (bufp->x, top); XSETINT (bufp->y, top_range); } else { ControlPartCode part_code; int unhilite_p = 0, part; if (ch != FindControlUnderMouse (mouse_pos, win, &part_code)) unhilite_p = 1; else { part = control_part_code_to_scroll_bar_part (part_code); switch (last_scroll_bar_part) { case scroll_bar_above_handle: case scroll_bar_below_handle: if (part != scroll_bar_above_handle && part != scroll_bar_below_handle) unhilite_p = 1; break; case scroll_bar_up_arrow: case scroll_bar_down_arrow: if (part != scroll_bar_up_arrow && part != scroll_bar_down_arrow) unhilite_p = 1; break; } } if (unhilite_p) HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0); else if (part != last_scroll_bar_part || scroll_bar_timer_event_posted_p) { construct_scroll_bar_click (bar, part, bufp); last_scroll_bar_part = part; HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code); set_scroll_bar_timer (SCROLL_BAR_CONTINUOUS_DELAY); } } } /* Update BAR->track_top, BAR->track_height, and BAR->min_handle for the scroll bar BAR. This function should be called when the bounds of the scroll bar is changed. */ static void update_scroll_bar_track_info (bar) struct scroll_bar *bar; { ControlRef ch = SCROLL_BAR_CONTROL_REF (bar); Rect r0, r1; GetControlBounds (ch, &r0); if (r0.right - r0.left >= r0.bottom - r0.top #ifdef MAC_OSX || r0.right - r0.left < MAC_AQUA_SMALL_VERTICAL_SCROLL_BAR_WIDTH #endif ) { XSETINT (bar->track_top, 0); XSETINT (bar->track_height, 0); XSETINT (bar->min_handle, 0); } else { BLOCK_INPUT; SetControl32BitMinimum (ch, 0); SetControl32BitMaximum (ch, 1 << 30); SetControlViewSize (ch, 1); /* Move the scroll bar thumb to the top. */ SetControl32BitValue (ch, 0); get_control_part_bounds (ch, kControlIndicatorPart, &r0); /* Move the scroll bar thumb to the bottom. */ SetControl32BitValue (ch, 1 << 30); get_control_part_bounds (ch, kControlIndicatorPart, &r1); UnionRect (&r0, &r1, &r0); XSETINT (bar->track_top, r0.top); XSETINT (bar->track_height, r0.bottom - r0.top); XSETINT (bar->min_handle, r1.bottom - r1.top); /* Don't show the scroll bar if its height is not enough to display the scroll bar thumb. */ if (r0.bottom - r0.top > 0) ShowControl (ch); UNBLOCK_INPUT; } } /* Set the thumb size and position of scroll bar BAR. We are currently displaying PORTION out of a whole WHOLE, and our position POSITION. */ void x_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) struct scroll_bar *bar; int portion, position, whole; { ControlRef ch = SCROLL_BAR_CONTROL_REF (bar); int value, viewsize, maximum; if (XINT (bar->track_height) == 0) return; if (whole <= portion) value = 0, viewsize = 1, maximum = 0; else { float scale; maximum = XINT (bar->track_height) - XINT (bar->min_handle); scale = (float) maximum / (whole - portion); value = position * scale + 0.5f; viewsize = (int) (portion * scale + 0.5f) + XINT (bar->min_handle); } BLOCK_INPUT; if (GetControlViewSize (ch) != viewsize || GetControl32BitValue (ch) != value || GetControl32BitMaximum (ch) != maximum) { /* Temporarily hide the scroll bar to avoid multiple redraws. */ SetControlVisibility (ch, false, false); SetControl32BitMaximum (ch, maximum); SetControl32BitValue (ch, value); SetControlViewSize (ch, viewsize); SetControlVisibility (ch, true, true); } UNBLOCK_INPUT; } #endif /* USE_TOOLKIT_SCROLL_BARS */ /* Create a scroll bar control for BAR. BOUNDS and VISIBLE specifies the initial geometry and visibility, respectively. The created control is stored in some members of BAR. */ void mac_create_scroll_bar (bar, bounds, visible) struct scroll_bar *bar; const Rect *bounds; Boolean visible; { struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); ControlRef ch; #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif ch = NewControl (FRAME_MAC_WINDOW (f), bounds, "\p", visible, 0, 0, 0, #if TARGET_API_MAC_CARBON kControlScrollBarProc, #else scrollBarProc, #endif (SInt32) bar); SET_SCROLL_BAR_CONTROL_REF (bar, ch); XSETINT (bar->start, 0); XSETINT (bar->end, 0); bar->dragging = Qnil; #ifdef USE_TOOLKIT_SCROLL_BARS update_scroll_bar_track_info (bar); #endif } /* Dispose of the scroll bar control stored in some members of BAR. */ void mac_dispose_scroll_bar (bar) struct scroll_bar *bar; { #if USE_CG_DRAWING struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); mac_prepare_for_quickdraw (f); #endif DisposeControl (SCROLL_BAR_CONTROL_REF (bar)); } /* Set bounds of the scroll bar BAR to BOUNDS. */ void mac_set_scroll_bar_bounds (bar, bounds) struct scroll_bar *bar; const Rect *bounds; { ControlRef ch = SCROLL_BAR_CONTROL_REF (bar); SInt16 width, height; #if USE_CG_DRAWING struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); mac_prepare_for_quickdraw (f); #endif width = bounds->right - bounds->left; height = bounds->bottom - bounds->top; HideControl (ch); MoveControl (ch, bounds->left, bounds->top); SizeControl (ch, width, height); #ifdef USE_TOOLKIT_SCROLL_BARS update_scroll_bar_track_info (bar); #else if (width < height) ShowControl (ch); #endif } /* Draw the scroll bar BAR. */ void mac_redraw_scroll_bar (bar) struct scroll_bar *bar; { #if USE_CG_DRAWING struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); mac_prepare_for_quickdraw (f); #endif Draw1Control (SCROLL_BAR_CONTROL_REF (bar)); } /* Handle a mouse click on the scroll bar BAR. If *EMACS_EVENT's kind is set to something other than NO_EVENT, it is enqueued. This may be called from a signal handler, so we have to ignore GC mark bits. */ static void x_scroll_bar_handle_click (bar, part_code, er, bufp) struct scroll_bar *bar; ControlPartCode part_code; const EventRecord *er; struct input_event *bufp; { int win_y, top_range; if (! GC_WINDOWP (bar->window)) abort (); bufp->kind = SCROLL_BAR_CLICK_EVENT; bufp->frame_or_window = bar->window; bufp->arg = Qnil; bar->dragging = Qnil; switch (part_code) { case kControlUpButtonPart: bufp->part = scroll_bar_up_arrow; break; case kControlDownButtonPart: bufp->part = scroll_bar_down_arrow; break; case kControlPageUpPart: bufp->part = scroll_bar_above_handle; break; case kControlPageDownPart: bufp->part = scroll_bar_below_handle; break; #if TARGET_API_MAC_CARBON default: #else case kControlIndicatorPart: #endif if (er->what == mouseDown) bar->dragging = make_number (0); XSETVECTOR (last_mouse_scroll_bar, bar); bufp->part = scroll_bar_handle; break; } win_y = XINT (bufp->y) - XINT (bar->top); top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (0/*dummy*/, XINT (bar->height)); win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER; win_y -= 24; if (! NILP (bar->dragging)) win_y -= XINT (bar->dragging); if (win_y < 0) win_y = 0; if (win_y > top_range) win_y = top_range; XSETINT (bufp->x, win_y); XSETINT (bufp->y, top_range); } /* Return information to the user about the current position of the mouse on the scroll bar. */ void x_scroll_bar_report_motion (fp, bar_window, part, x, y, time) FRAME_PTR *fp; Lisp_Object *bar_window; enum scroll_bar_part *part; Lisp_Object *x, *y; unsigned long *time; { struct scroll_bar *bar = XSCROLL_BAR (last_mouse_scroll_bar); ControlRef ch = SCROLL_BAR_CONTROL_REF (bar); #if TARGET_API_MAC_CARBON WindowRef wp = GetControlOwner (ch); #else WindowRef wp = (*ch)->contrlOwner; #endif Point mouse_pos; struct frame *f = mac_window_to_frame (wp); int win_y, top_range; #if TARGET_API_MAC_CARBON GetGlobalMouse (&mouse_pos); mouse_pos.h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f); mouse_pos.v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f); #else SetPortWindowPort (wp); GetMouse (&mouse_pos); #endif win_y = mouse_pos.v - XINT (bar->top); top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height)); win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER; win_y -= 24; if (! NILP (bar->dragging)) win_y -= XINT (bar->dragging); if (win_y < 0) win_y = 0; if (win_y > top_range) win_y = top_range; *fp = f; *bar_window = bar->window; if (! NILP (bar->dragging)) *part = scroll_bar_handle; else if (win_y < XINT (bar->start)) *part = scroll_bar_above_handle; else if (win_y < XINT (bar->end) + VERTICAL_SCROLL_BAR_MIN_HANDLE) *part = scroll_bar_handle; else *part = scroll_bar_below_handle; XSETINT (*x, win_y); XSETINT (*y, top_range); f->mouse_moved = 0; last_mouse_scroll_bar = Qnil; *time = last_mouse_movement_time; } #ifndef USE_TOOLKIT_SCROLL_BARS /* Draw BAR's handle in the proper position. If the handle is already drawn from START to END, don't bother redrawing it, unless REBUILD is non-zero; in that case, always redraw it. (REBUILD is handy for drawing the handle after expose events.) Normally, we want to constrain the start and end of the handle to fit inside its rectangle, but if the user is dragging the scroll bar handle, we want to let them drag it down all the way, so that the bar's top is as far down as it goes; otherwise, there's no way to move to the very end of the buffer. */ void x_scroll_bar_set_handle (bar, start, end, rebuild) struct scroll_bar *bar; int start, end; int rebuild; { int dragging = ! NILP (bar->dragging); ControlRef ch = SCROLL_BAR_CONTROL_REF (bar); FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height)); int length = end - start; /* If the display is already accurate, do nothing. */ if (! rebuild && start == XINT (bar->start) && end == XINT (bar->end)) return; BLOCK_INPUT; /* Make sure the values are reasonable, and try to preserve the distance between start and end. */ if (start < 0) start = 0; else if (start > top_range) start = top_range; end = start + length; if (end < start) end = start; else if (end > top_range && ! dragging) end = top_range; /* Store the adjusted setting in the scroll bar. */ XSETINT (bar->start, start); XSETINT (bar->end, end); /* Clip the end position, just for display. */ if (end > top_range) end = top_range; /* Draw bottom positions VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below top positions, to make sure the handle is always at least that many pixels tall. */ end += VERTICAL_SCROLL_BAR_MIN_HANDLE; SetControlMinimum (ch, 0); /* Don't inadvertently activate deactivated scroll bars */ if (GetControlMaximum (ch) != -1) SetControlMaximum (ch, top_range + VERTICAL_SCROLL_BAR_MIN_HANDLE - (end - start)); SetControlValue (ch, start); #if TARGET_API_MAC_CARBON SetControlViewSize (ch, end - start); #endif UNBLOCK_INPUT; } /* Handle some mouse motion while someone is dragging the scroll bar. This may be called from a signal handler, so we have to ignore GC mark bits. */ static void x_scroll_bar_note_movement (bar, y_pos, t) struct scroll_bar *bar; int y_pos; Time t; { FRAME_PTR f = XFRAME (XWINDOW (bar->window)->frame); last_mouse_movement_time = t; f->mouse_moved = 1; XSETVECTOR (last_mouse_scroll_bar, bar); /* If we're dragging the bar, display it. */ if (! GC_NILP (bar->dragging)) { /* Where should the handle be now? */ int new_start = y_pos - 24; if (new_start != XINT (bar->start)) { int new_end = new_start + (XINT (bar->end) - XINT (bar->start)); x_scroll_bar_set_handle (bar, new_start, new_end, 0); } } } #endif /* !USE_TOOLKIT_SCROLL_BARS */ /*********************************************************************** Tool-bars ***********************************************************************/ #if USE_MAC_TOOLBAR /* In identifiers such as function/variable names, Emacs tool bar is referred to as `tool_bar', and Carbon HIToolbar as `toolbar'. */ #define TOOLBAR_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar")) #define TOOLBAR_ICON_ITEM_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar.icon")) #define TOOLBAR_ITEM_COMMAND_ID_OFFSET 'Tb\0\0' #define TOOLBAR_ITEM_COMMAND_ID_P(id) \ (((id) & ~0xffff) == TOOLBAR_ITEM_COMMAND_ID_OFFSET) #define TOOLBAR_ITEM_COMMAND_ID_VALUE(id) \ ((id) - TOOLBAR_ITEM_COMMAND_ID_OFFSET) #define TOOLBAR_ITEM_MAKE_COMMAND_ID(value) \ ((value) + TOOLBAR_ITEM_COMMAND_ID_OFFSET) static OSStatus mac_handle_toolbar_command_event P_ ((EventHandlerCallRef, EventRef, void *)); extern Rect last_mouse_glyph; extern void mac_move_window_with_gravity P_ ((struct frame *, int, short, short)); extern void mac_get_window_origin_with_gravity P_ ((struct frame *, int, short *, short *)); extern CGImageRef mac_image_spec_to_cg_image P_ ((struct frame *, Lisp_Object)); static OSStatus mac_handle_toolbar_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus result = eventNotHandledErr; switch (GetEventKind (event)) { case kEventToolbarGetDefaultIdentifiers: result = noErr; break; case kEventToolbarGetAllowedIdentifiers: { CFMutableArrayRef array; GetEventParameter (event, kEventParamMutableArray, typeCFMutableArrayRef, NULL, sizeof (CFMutableArrayRef), NULL, &array); CFArrayAppendValue (array, TOOLBAR_ICON_ITEM_IDENTIFIER); result = noErr; } break; case kEventToolbarCreateItemWithIdentifier: { CFStringRef identifier; HIToolbarItemRef item = NULL; GetEventParameter (event, kEventParamToolbarItemIdentifier, typeCFStringRef, NULL, sizeof (CFStringRef), NULL, &identifier); if (CFStringCompare (identifier, TOOLBAR_ICON_ITEM_IDENTIFIER, 0) == kCFCompareEqualTo) HIToolbarItemCreate (identifier, kHIToolbarItemAllowDuplicates | kHIToolbarItemCantBeRemoved, &item); if (item) { SetEventParameter (event, kEventParamToolbarItem, typeHIToolbarItemRef, sizeof (HIToolbarItemRef), &item); result = noErr; } } break; default: abort (); } return result; } /* Create a tool bar for frame F. */ static OSStatus mac_create_frame_tool_bar (f) FRAME_PTR f; { OSStatus err; HIToolbarRef toolbar; err = HIToolbarCreate (TOOLBAR_IDENTIFIER, kHIToolbarNoAttributes, &toolbar); if (err == noErr) { static const EventTypeSpec specs[] = {{kEventClassToolbar, kEventToolbarGetDefaultIdentifiers}, {kEventClassToolbar, kEventToolbarGetAllowedIdentifiers}, {kEventClassToolbar, kEventToolbarCreateItemWithIdentifier}}; err = InstallEventHandler (HIObjectGetEventTarget (toolbar), mac_handle_toolbar_event, GetEventTypeCount (specs), specs, f, NULL); } if (err == noErr) err = HIToolbarSetDisplayMode (toolbar, kHIToolbarDisplayModeIconOnly); if (err == noErr) { static const EventTypeSpec specs[] = {{kEventClassCommand, kEventCommandProcess}}; err = InstallWindowEventHandler (FRAME_MAC_WINDOW (f), mac_handle_toolbar_command_event, GetEventTypeCount (specs), specs, f, NULL); } if (err == noErr) err = SetWindowToolbar (FRAME_MAC_WINDOW (f), toolbar); if (toolbar) CFRelease (toolbar); return err; } /* Update the tool bar for frame F. Add new buttons and remove old. */ void update_frame_tool_bar (f) FRAME_PTR f; { HIToolbarRef toolbar = NULL; short left, top; CFArrayRef old_items = NULL; CFIndex old_count; int i, pos, win_gravity = f->output_data.mac->toolbar_win_gravity; struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f); BLOCK_INPUT; GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar); if (toolbar == NULL) { mac_create_frame_tool_bar (f); GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar); if (toolbar == NULL) goto out; if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity) mac_get_window_origin_with_gravity (f, win_gravity, &left, &top); } HIToolbarCopyItems (toolbar, &old_items); if (old_items == NULL) goto out; old_count = CFArrayGetCount (old_items); pos = 0; for (i = 0; i < f->n_tool_bar_items; ++i) { #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX)) int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P)); int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P)); int idx; Lisp_Object image; CGImageRef cg_image; CFStringRef label; HIToolbarItemRef item; /* If image is a vector, choose the image according to the button state. */ image = PROP (TOOL_BAR_ITEM_IMAGES); if (VECTORP (image)) { if (enabled_p) idx = (selected_p ? TOOL_BAR_IMAGE_ENABLED_SELECTED : TOOL_BAR_IMAGE_ENABLED_DESELECTED); else idx = (selected_p ? TOOL_BAR_IMAGE_DISABLED_SELECTED : TOOL_BAR_IMAGE_DISABLED_DESELECTED); xassert (ASIZE (image) >= idx); image = AREF (image, idx); } else idx = -1; cg_image = mac_image_spec_to_cg_image (f, image); /* Ignore invalid image specifications. */ if (cg_image == NULL) continue; label = cfstring_create_with_string (PROP (TOOL_BAR_ITEM_CAPTION)); if (label == NULL) label = CFSTR (""); if (pos < old_count) { CGImageRef old_cg_image = NULL; CFStringRef old_label = NULL; Boolean old_enabled_p; item = (HIToolbarItemRef) CFArrayGetValueAtIndex (old_items, pos); HIToolbarItemCopyImage (item, &old_cg_image); if (cg_image != old_cg_image) HIToolbarItemSetImage (item, cg_image); CGImageRelease (old_cg_image); HIToolbarItemCopyLabel (item, &old_label); if (CFStringCompare (label, old_label, 0) != kCFCompareEqualTo) HIToolbarItemSetLabel (item, label); CFRelease (old_label); old_enabled_p = HIToolbarItemIsEnabled (item); if ((enabled_p || idx >= 0) != old_enabled_p) HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0)); } else { item = NULL; HIToolbarCreateItemWithIdentifier (toolbar, TOOLBAR_ICON_ITEM_IDENTIFIER, NULL, &item); if (item) { HIToolbarItemSetImage (item, cg_image); HIToolbarItemSetLabel (item, label); HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0)); HIToolbarAppendItem (toolbar, item); CFRelease (item); } } CFRelease (label); if (item) { HIToolbarItemSetCommandID (item, TOOLBAR_ITEM_MAKE_COMMAND_ID (i)); pos++; } } CFRelease (old_items); while (pos < old_count) HIToolbarRemoveItemAtIndex (toolbar, --old_count); ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), true, !win_gravity && f == mac_focus_frame (dpyinfo)); /* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events on toolbar visibility change. */ mac_handle_origin_change (f); if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity) { mac_move_window_with_gravity (f, win_gravity, left, top); /* If the title bar is completely outside the screen, adjust the position. */ ConstrainWindowToScreen (FRAME_MAC_WINDOW (f), kWindowTitleBarRgn, kWindowConstrainMoveRegardlessOfFit | kWindowConstrainAllowPartial, NULL, NULL); f->output_data.mac->toolbar_win_gravity = 0; } out: UNBLOCK_INPUT; } /* Hide the tool bar on frame F. Unlike the counterpart on GTK+, it doesn't deallocate the resources. */ void free_frame_tool_bar (f) FRAME_PTR f; { if (IsWindowToolbarVisible (FRAME_MAC_WINDOW (f))) { struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f); BLOCK_INPUT; ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), false, (NILP (find_symbol_value (intern ("frame-notice-user-settings"))) && f == mac_focus_frame (dpyinfo))); /* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events on toolbar visibility change. */ mac_handle_origin_change (f); UNBLOCK_INPUT; } } /* Report a mouse movement over toolbar to the mainstream Emacs code. */ static void mac_tool_bar_note_mouse_movement (f, event) struct frame *f; EventRef event; { OSStatus err; struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f); int mouse_down_p; WindowRef window; WindowPartCode part_code; HIViewRef item_view; UInt32 command_id; mouse_down_p = (dpyinfo->grabbed && f == last_mouse_frame && FRAME_LIVE_P (f)); if (mouse_down_p) return; err = GetEventParameter (event, kEventParamWindowRef, typeWindowRef, NULL, sizeof (WindowRef), NULL, &window); if (err != noErr || window != FRAME_MAC_WINDOW (f)) return; err = GetEventParameter (event, kEventParamWindowPartCode, typeWindowPartCode, NULL, sizeof (WindowPartCode), NULL, &part_code); if (err != noErr || part_code != inStructure) return; err = HIViewGetViewForMouseEvent (HIViewGetRoot (window), event, &item_view); /* This doesn't work on Mac OS X 10.2. On Mac OS X 10.3 and 10.4, a toolbar item view seems to have the same command ID with that of the toolbar item. */ if (err == noErr) err = GetControlCommandID (item_view, &command_id); if (err == noErr && TOOLBAR_ITEM_COMMAND_ID_P (command_id)) { int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command_id); if (i < f->n_tool_bar_items) { HIRect bounds; HIViewRef content_view; err = HIViewGetBounds (item_view, &bounds); if (err == noErr) err = HIViewFindByID (HIViewGetRoot (window), kHIViewWindowContentID, &content_view); if (err == noErr) err = HIViewConvertRect (&bounds, item_view, content_view); if (err == noErr) SetRect (&last_mouse_glyph, CGRectGetMinX (bounds), CGRectGetMinY (bounds), CGRectGetMaxX (bounds), CGRectGetMaxY (bounds)); help_echo_object = help_echo_window = Qnil; help_echo_pos = -1; help_echo_string = PROP (TOOL_BAR_ITEM_HELP); if (NILP (help_echo_string)) help_echo_string = PROP (TOOL_BAR_ITEM_CAPTION); } } } static OSStatus mac_handle_toolbar_command_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err, result = eventNotHandledErr; struct frame *f = (struct frame *) data; HICommand command; err = GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof (HICommand), NULL, &command); if (err != noErr) return result; switch (GetEventKind (event)) { case kEventCommandProcess: if (!TOOLBAR_ITEM_COMMAND_ID_P (command.commandID)) result = CallNextEventHandler (next_handler, event); else { int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command.commandID); if (i < f->n_tool_bar_items && !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P))) { Lisp_Object frame; struct input_event buf; EVENT_INIT (buf); XSETFRAME (frame, f); buf.kind = TOOL_BAR_EVENT; buf.frame_or_window = frame; buf.arg = frame; kbd_buffer_store_event (&buf); buf.kind = TOOL_BAR_EVENT; buf.frame_or_window = frame; buf.arg = PROP (TOOL_BAR_ITEM_KEY); buf.modifiers = mac_event_to_emacs_modifiers (event); kbd_buffer_store_event (&buf); result = noErr; } } break; default: abort (); } #undef PROP return result; } #endif /* USE_MAC_TOOLBAR */ /*********************************************************************** Font Panel ***********************************************************************/ #if USE_MAC_FONT_PANEL /* Whether Font Panel has been shown before. The first call to font panel functions (FPIsFontPanelVisible, SetFontInfoForSelection) is slow. This variable is used for deferring such a call as much as possible. */ static int font_panel_shown_p = 0; extern Lisp_Object Qpanel_closed, Qselection; extern Lisp_Object Qfont; /* Whether the font panel is currently visible. */ int mac_font_panel_visible_p () { return font_panel_shown_p && FPIsFontPanelVisible (); } static pascal OSStatus mac_handle_font_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus result, err; Lisp_Object id_key; int num_params; const EventParamName *names; const EventParamType *types; static const EventParamName names_sel[] = {kEventParamATSUFontID, kEventParamATSUFontSize, kEventParamFMFontFamily, kEventParamFMFontStyle, kEventParamFMFontSize, kEventParamFontColor}; static const EventParamType types_sel[] = {typeATSUFontID, typeATSUSize, typeFMFontFamily, typeFMFontStyle, typeFMFontSize, typeFontColor}; result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) return result; switch (GetEventKind (event)) { case kEventFontPanelClosed: id_key = Qpanel_closed; num_params = 0; names = NULL; types = NULL; break; case kEventFontSelection: id_key = Qselection; num_params = sizeof (names_sel) / sizeof (names_sel[0]); names = names_sel; types = types_sel; break; } err = mac_store_event_ref_as_apple_event (0, 0, Qfont, id_key, event, num_params, names, types); if (err == noErr) result = noErr; return result; } /* Toggle visiblity of the font panel. */ OSStatus mac_show_hide_font_panel () { if (!font_panel_shown_p) { OSStatus err; static const EventTypeSpec specs[] = {{kEventClassFont, kEventFontPanelClosed}, {kEventClassFont, kEventFontSelection}}; err = InstallApplicationEventHandler (mac_handle_font_event, GetEventTypeCount (specs), specs, NULL, NULL); if (err != noErr) return err; font_panel_shown_p = 1; } return FPShowHideFontPanel (); } /* Set the font selected in the font panel to the one corresponding to the face FACE_ID and the charcacter C in the frame F. */ OSStatus mac_set_font_info_for_selection (f, face_id, c) struct frame *f; int face_id, c; { OSStatus err; EventTargetRef target = NULL; XFontStruct *font = NULL; if (!mac_font_panel_visible_p ()) return noErr; if (f) { target = GetWindowEventTarget (FRAME_MAC_WINDOW (f)); if (FRAME_FACE_CACHE (f) && CHAR_VALID_P (c, 0)) { struct face *face; face_id = FACE_FOR_CHAR (f, FACE_FROM_ID (f, face_id), c); face = FACE_FROM_ID (f, face_id); font = face->font; } } if (font == NULL) err = SetFontInfoForSelection (kFontSelectionATSUIType, 0, NULL, target); else { if (font->mac_fontnum != -1) { FontSelectionQDStyle qd_style; qd_style.version = kFontSelectionQDStyleVersionZero; qd_style.instance.fontFamily = font->mac_fontnum; qd_style.instance.fontStyle = font->mac_fontface; qd_style.size = font->mac_fontsize; qd_style.hasColor = false; err = SetFontInfoForSelection (kFontSelectionQDType, 1, &qd_style, target); } else err = SetFontInfoForSelection (kFontSelectionATSUIType, 1, &font->mac_style, target); } return err; } #endif /* USE_MAC_FONT_PANEL */ /************************************************************************ Event Handling ************************************************************************/ /* Non-zero means that a HELP_EVENT has been generated since Emacs start. */ static int any_help_event_p; /* Last window where we saw the mouse. Used by mouse-autoselect-window. */ static Lisp_Object last_window; static Point saved_menu_event_location; extern struct frame *pending_autoraise_frame; extern FRAME_PTR last_mouse_glyph_frame; #ifdef __STDC__ extern int volatile input_signal_count; #else extern int input_signal_count; #endif extern int mac_screen_config_changed; extern Lisp_Object Vmac_emulate_three_button_mouse; #if TARGET_API_MAC_CARBON extern int mac_wheel_button_is_mouse_2; extern int mac_pass_command_to_system; extern int mac_pass_control_to_system; #endif /* TARGET_API_MAC_CARBON */ extern int mac_ready_for_apple_events; extern void mac_focus_changed P_ ((int, struct mac_display_info *, struct frame *, struct input_event *)); extern int mac_get_emulated_btn P_ ((UInt32)); extern int note_mouse_movement P_ ((FRAME_PTR, Point *)); extern void mac_get_screen_info P_ ((struct mac_display_info *)); /* The focus may have changed. Figure out if it is a real focus change, by checking both FocusIn/Out and Enter/LeaveNotify events. Returns FOCUS_IN_EVENT event in *BUFP. */ static void x_detect_focus_change (dpyinfo, event, bufp) struct mac_display_info *dpyinfo; const EventRecord *event; struct input_event *bufp; { struct frame *frame; frame = mac_window_to_frame ((WindowRef) event->message); if (! frame) return; /* On Mac, this is only called from focus events, so no switch needed. */ mac_focus_changed ((event->modifiers & activeFlag), dpyinfo, frame, bufp); } #if TARGET_API_MAC_CARBON /* Obtains the event modifiers from the event EVENTREF and then calls mac_to_emacs_modifiers. */ static int mac_event_to_emacs_modifiers (EventRef eventRef) { UInt32 mods = 0, class; GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &mods); class = GetEventClass (eventRef); if (!NILP (Vmac_emulate_three_button_mouse) && (class == kEventClassMouse || class == kEventClassCommand)) { mods &= ~(optionKey | cmdKey); } return mac_to_emacs_modifiers (mods, 0); } /* Given an event REF, return the code to use for the mouse button code in the emacs input_event. */ static int mac_get_mouse_btn (EventRef ref) { EventMouseButton result = kEventMouseButtonPrimary; GetEventParameter (ref, kEventParamMouseButton, typeMouseButton, NULL, sizeof (EventMouseButton), NULL, &result); switch (result) { case kEventMouseButtonPrimary: if (NILP (Vmac_emulate_three_button_mouse)) return 0; else { UInt32 mods = 0; GetEventParameter (ref, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &mods); return mac_get_emulated_btn(mods); } case kEventMouseButtonSecondary: return mac_wheel_button_is_mouse_2 ? 2 : 1; case kEventMouseButtonTertiary: case 4: /* 4 is the number for the mouse wheel button */ return mac_wheel_button_is_mouse_2 ? 1 : 2; default: return 0; } } /* Normally, ConvertEventRefToEventRecord will correctly handle all events. However the click of the mouse wheel is not converted to a mouseDown or mouseUp event. Likewise for dead key events. This calls ConvertEventRefToEventRecord, but then checks to see if it is a mouse up/down, or a dead key Carbon event that has not been converted, and if so, converts it by hand (to be picked up in the XTread_socket loop). */ static Boolean mac_convert_event_ref (EventRef eventRef, EventRecord *eventRec) { OSStatus err; Boolean result = ConvertEventRefToEventRecord (eventRef, eventRec); EventKind action; if (result) return result; switch (GetEventClass (eventRef)) { case kEventClassMouse: switch (GetEventKind (eventRef)) { case kEventMouseDown: eventRec->what = mouseDown; result = 1; break; case kEventMouseUp: eventRec->what = mouseUp; result = 1; break; default: break; } break; case kEventClassKeyboard: switch (GetEventKind (eventRef)) { case kEventRawKeyDown: action = keyDown; goto keystroke_common; case kEventRawKeyRepeat: action = autoKey; goto keystroke_common; case kEventRawKeyUp: action = keyUp; keystroke_common: { unsigned char char_codes; UInt32 key_code; err = GetEventParameter (eventRef, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &char_codes); if (err == noErr) err = GetEventParameter (eventRef, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &key_code); if (err == noErr) { eventRec->what = action; eventRec->message = char_codes | ((key_code & 0xff) << 8); result = 1; } } break; default: break; } break; default: break; } if (result) { /* Need where and when. */ UInt32 mods = 0; GetEventParameter (eventRef, kEventParamMouseLocation, typeQDPoint, NULL, sizeof (Point), NULL, &eventRec->where); /* Use two step process because new event modifiers are 32-bit and old are 16-bit. Currently, only loss is NumLock & Fn. */ GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &mods); eventRec->modifiers = mods; eventRec->when = EventTimeToTicks (GetEventTime (eventRef)); } return result; } #endif /* TARGET_API_MAC_CARBON */ #if !TARGET_API_MAC_CARBON static RgnHandle mouse_region = NULL; Boolean mac_wait_next_event (er, sleep_time, dequeue) EventRecord *er; UInt32 sleep_time; Boolean dequeue; { static EventRecord er_buf = {nullEvent}; UInt32 target_tick, current_tick; EventMask event_mask; if (mouse_region == NULL) mouse_region = NewRgn (); event_mask = everyEvent; if (!mac_ready_for_apple_events) event_mask -= highLevelEventMask; current_tick = TickCount (); target_tick = current_tick + sleep_time; if (er_buf.what == nullEvent) while (!WaitNextEvent (event_mask, &er_buf, target_tick - current_tick, mouse_region)) { current_tick = TickCount (); if (target_tick <= current_tick) return false; } *er = er_buf; if (dequeue) er_buf.what = nullEvent; return true; } #endif /* not TARGET_API_MAC_CARBON */ #if TARGET_API_MAC_CARBON OSStatus mac_post_mouse_moved_event () { EventRef event = NULL; OSStatus err; err = CreateEvent (NULL, kEventClassMouse, kEventMouseMoved, 0, kEventAttributeNone, &event); if (err == noErr) { Point mouse_pos; GetGlobalMouse (&mouse_pos); err = SetEventParameter (event, kEventParamMouseLocation, typeQDPoint, sizeof (Point), &mouse_pos); } if (err == noErr) { UInt32 modifiers = GetCurrentKeyModifiers (); err = SetEventParameter (event, kEventParamKeyModifiers, typeUInt32, sizeof (UInt32), &modifiers); } if (err == noErr) err = PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard); if (event) ReleaseEvent (event); return err; } #endif #ifdef MAC_OSX /* Run the current run loop in the default mode until some input happens or TIMEOUT seconds passes unless it is negative. Return true if timeout occurs first. */ Boolean mac_run_loop_run_once (timeout) EventTimeout timeout; { #if USE_CG_DRAWING mac_prepare_for_quickdraw (NULL); #endif return (CFRunLoopRunInMode (kCFRunLoopDefaultMode, timeout >= 0 ? timeout : 100000, true) == kCFRunLoopRunTimedOut); } #endif /* Emacs calls this whenever it wants to read an input event from the user. */ int XTread_socket (sd, expected, hold_quit) int sd, expected; struct input_event *hold_quit; { struct input_event inev; int count = 0; #if TARGET_API_MAC_CARBON EventRef eventRef; EventTargetRef toolbox_dispatcher; #endif EventRecord er; struct mac_display_info *dpyinfo = &one_mac_display_info; if (interrupt_input_blocked) { interrupt_input_pending = 1; return -1; } interrupt_input_pending = 0; BLOCK_INPUT; /* So people can tell when we have read the available input. */ input_signal_count++; ++handling_signal; #if TARGET_API_MAC_CARBON toolbox_dispatcher = GetEventDispatcherTarget (); while ( #if USE_CG_DRAWING mac_prepare_for_quickdraw (NULL), #endif !ReceiveNextEvent (0, NULL, kEventDurationNoWait, kEventRemoveFromQueue, &eventRef)) #else /* !TARGET_API_MAC_CARBON */ while (mac_wait_next_event (&er, 0, true)) #endif /* !TARGET_API_MAC_CARBON */ { int do_help = 0; struct frame *f; unsigned long timestamp; EVENT_INIT (inev); inev.kind = NO_EVENT; inev.arg = Qnil; #if TARGET_API_MAC_CARBON timestamp = GetEventTime (eventRef) / kEventDurationMillisecond; if (!mac_convert_event_ref (eventRef, &er)) goto OTHER; #else /* !TARGET_API_MAC_CARBON */ timestamp = er.when * (1000 / 60); /* ticks to milliseconds */ #endif /* !TARGET_API_MAC_CARBON */ switch (er.what) { case mouseDown: case mouseUp: { WindowRef window_ptr; ControlPartCode part_code; int tool_bar_p = 0; #if TARGET_API_MAC_CARBON OSStatus err; /* This is needed to send mouse events like aqua window buttons to the correct handler. */ read_socket_inev = &inev; err = SendEventToEventTarget (eventRef, toolbox_dispatcher); read_socket_inev = NULL; if (err != eventNotHandledErr) break; #endif last_mouse_glyph_frame = 0; if (dpyinfo->grabbed && last_mouse_frame && FRAME_LIVE_P (last_mouse_frame)) { window_ptr = FRAME_MAC_WINDOW (last_mouse_frame); part_code = inContent; } else { part_code = FindWindow (er.where, &window_ptr); if (tip_window && window_ptr == tip_window) { HideWindow (tip_window); part_code = FindWindow (er.where, &window_ptr); } } if (er.what != mouseDown && (part_code != inContent || dpyinfo->grabbed == 0)) break; switch (part_code) { case inMenuBar: f = mac_focus_frame (dpyinfo); saved_menu_event_location = er.where; inev.kind = MENU_BAR_ACTIVATE_EVENT; XSETFRAME (inev.frame_or_window, f); break; case inContent: if ( #if TARGET_API_MAC_CARBON FrontNonFloatingWindow () #else FrontWindow () #endif != window_ptr || (mac_window_to_frame (window_ptr) != dpyinfo->x_focus_frame)) SelectWindow (window_ptr); else { ControlPartCode control_part_code; ControlRef ch; Point mouse_loc; #ifdef MAC_OSX ControlKind control_kind; #endif f = mac_window_to_frame (window_ptr); /* convert to local coordinates of new window */ mouse_loc.h = (er.where.h - (f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f))); mouse_loc.v = (er.where.v - (f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f))); #if TARGET_API_MAC_CARBON ch = FindControlUnderMouse (mouse_loc, window_ptr, &control_part_code); #ifdef MAC_OSX if (ch) GetControlKind (ch, &control_kind); #endif #else control_part_code = FindControl (mouse_loc, window_ptr, &ch); #endif #if TARGET_API_MAC_CARBON inev.code = mac_get_mouse_btn (eventRef); inev.modifiers = mac_event_to_emacs_modifiers (eventRef); #else inev.code = mac_get_emulated_btn (er.modifiers); inev.modifiers = mac_to_emacs_modifiers (er.modifiers, 0); #endif XSETINT (inev.x, mouse_loc.h); XSETINT (inev.y, mouse_loc.v); if ((dpyinfo->grabbed && tracked_scroll_bar) || (ch != 0 #ifndef USE_TOOLKIT_SCROLL_BARS /* control_part_code becomes kControlNoPart if a progress indicator is clicked. */ && control_part_code != kControlNoPart #else /* USE_TOOLKIT_SCROLL_BARS */ #ifdef MAC_OSX && control_kind.kind == kControlKindScrollBar #endif /* MAC_OSX */ #endif /* USE_TOOLKIT_SCROLL_BARS */ )) { struct scroll_bar *bar; if (dpyinfo->grabbed && tracked_scroll_bar) { bar = tracked_scroll_bar; #ifndef USE_TOOLKIT_SCROLL_BARS control_part_code = kControlIndicatorPart; #endif } else bar = (struct scroll_bar *) GetControlReference (ch); #ifdef USE_TOOLKIT_SCROLL_BARS /* Make the "Ctrl-Mouse-2 splits window" work for toolkit scroll bars. */ if (inev.modifiers & ctrl_modifier) x_scroll_bar_handle_click (bar, control_part_code, &er, &inev); else if (er.what == mouseDown) x_scroll_bar_handle_press (bar, control_part_code, mouse_loc, &inev); else x_scroll_bar_handle_release (bar, &inev); #else /* not USE_TOOLKIT_SCROLL_BARS */ x_scroll_bar_handle_click (bar, control_part_code, &er, &inev); if (er.what == mouseDown && control_part_code == kControlIndicatorPart) tracked_scroll_bar = bar; else tracked_scroll_bar = NULL; #endif /* not USE_TOOLKIT_SCROLL_BARS */ } else { Lisp_Object window; int x = mouse_loc.h; int y = mouse_loc.v; window = window_from_coordinates (f, x, y, 0, 0, 0, 1); if (EQ (window, f->tool_bar_window)) { if (er.what == mouseDown) handle_tool_bar_click (f, x, y, 1, 0); else handle_tool_bar_click (f, x, y, 0, inev.modifiers); tool_bar_p = 1; } else { XSETFRAME (inev.frame_or_window, f); inev.kind = MOUSE_CLICK_EVENT; } } if (er.what == mouseDown) { dpyinfo->grabbed |= (1 << inev.code); last_mouse_frame = f; if (!tool_bar_p) last_tool_bar_item = -1; } else { if ((dpyinfo->grabbed & (1 << inev.code)) == 0) /* If a button is released though it was not previously pressed, that would be because of multi-button emulation. */ dpyinfo->grabbed = 0; else dpyinfo->grabbed &= ~(1 << inev.code); } /* Ignore any mouse motion that happened before this event; any subsequent mouse-movement Emacs events should reflect only motion after the ButtonPress. */ if (f != 0) f->mouse_moved = 0; #ifdef USE_TOOLKIT_SCROLL_BARS if (inev.kind == MOUSE_CLICK_EVENT || (inev.kind == SCROLL_BAR_CLICK_EVENT && (inev.modifiers & ctrl_modifier))) #endif switch (er.what) { case mouseDown: inev.modifiers |= down_modifier; break; case mouseUp: inev.modifiers |= up_modifier; break; } } break; case inDrag: #if TARGET_API_MAC_CARBON case inProxyIcon: if (IsWindowPathSelectClick (window_ptr, &er)) { WindowPathSelect (window_ptr, NULL, NULL); break; } if (part_code == inProxyIcon && (TrackWindowProxyDrag (window_ptr, er.where) != errUserWantsToDragWindow)) break; DragWindow (window_ptr, er.where, NULL); #else /* not TARGET_API_MAC_CARBON */ DragWindow (window_ptr, er.where, &qd.screenBits.bounds); /* Update the frame parameters. */ { struct frame *f = mac_window_to_frame (window_ptr); if (f && !f->async_iconified) mac_handle_origin_change (f); } #endif /* not TARGET_API_MAC_CARBON */ break; case inGoAway: if (TrackGoAway (window_ptr, er.where)) { inev.kind = DELETE_WINDOW_EVENT; XSETFRAME (inev.frame_or_window, mac_window_to_frame (window_ptr)); } break; /* window resize handling added --ben */ case inGrow: do_grow_window (window_ptr, &er); break; /* window zoom handling added --ben */ case inZoomIn: case inZoomOut: if (TrackBox (window_ptr, er.where, part_code)) do_zoom_window (window_ptr, part_code); break; #if USE_MAC_TOOLBAR case inStructure: { OSStatus err; HIViewRef ch; if (FrontNonFloatingWindow () != window_ptr) SelectWindow (window_ptr); err = HIViewGetViewForMouseEvent (HIViewGetRoot (window_ptr), eventRef, &ch); /* This doesn't work on Mac OS X 10.2. */ if (err == noErr) HIViewClick (ch, eventRef); } break; #endif /* USE_MAC_TOOLBAR */ default: break; } } break; #if !TARGET_API_MAC_CARBON case updateEvt: do_window_update ((WindowRef) er.message); break; #endif case osEvt: #if TARGET_API_MAC_CARBON if (SendEventToEventTarget (eventRef, toolbox_dispatcher) != eventNotHandledErr) break; #endif switch ((er.message >> 24) & 0x000000FF) { #if USE_MAC_TSM case suspendResumeMessage: if (er.message & resumeFlag) mac_tsm_resume (); else mac_tsm_suspend (); break; #endif case mouseMovedMessage: #if !TARGET_API_MAC_CARBON SetRectRgn (mouse_region, er.where.h, er.where.v, er.where.h + 1, er.where.v + 1); #endif previous_help_echo_string = help_echo_string; help_echo_string = Qnil; if (dpyinfo->grabbed && last_mouse_frame && FRAME_LIVE_P (last_mouse_frame)) f = last_mouse_frame; else f = dpyinfo->x_focus_frame; if (dpyinfo->mouse_face_hidden) { dpyinfo->mouse_face_hidden = 0; clear_mouse_face (dpyinfo); } if (f) { WindowRef wp = FRAME_MAC_WINDOW (f); Point mouse_pos; mouse_pos.h = (er.where.h - (f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f))); mouse_pos.v = (er.where.v - (f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f))); if (dpyinfo->grabbed && tracked_scroll_bar) #ifdef USE_TOOLKIT_SCROLL_BARS x_scroll_bar_handle_drag (wp, tracked_scroll_bar, mouse_pos, &inev); #else /* not USE_TOOLKIT_SCROLL_BARS */ x_scroll_bar_note_movement (tracked_scroll_bar, mouse_pos.v - XINT (tracked_scroll_bar->top), er.when * (1000 / 60)); #endif /* not USE_TOOLKIT_SCROLL_BARS */ else { /* Generate SELECT_WINDOW_EVENTs when needed. */ if (!NILP (Vmouse_autoselect_window)) { Lisp_Object window; window = window_from_coordinates (f, mouse_pos.h, mouse_pos.v, 0, 0, 0, 0); /* Window will be selected only when it is not selected now and last mouse movement event was not in it. Minibuffer window will be selected only when it is active. */ if (WINDOWP (window) && !EQ (window, last_window) && !EQ (window, selected_window) /* For click-to-focus window managers create event iff we don't leave the selected frame. */ && (focus_follows_mouse || (EQ (XWINDOW (window)->frame, XWINDOW (selected_window)->frame)))) { inev.kind = SELECT_WINDOW_EVENT; inev.frame_or_window = window; } last_window=window; } if (!note_mouse_movement (f, &mouse_pos)) help_echo_string = previous_help_echo_string; #if USE_MAC_TOOLBAR else mac_tool_bar_note_mouse_movement (f, eventRef); #endif } } /* If the contents of the global variable help_echo_string has changed, generate a HELP_EVENT. */ if (!NILP (help_echo_string) || !NILP (previous_help_echo_string)) do_help = 1; break; } break; case activateEvt: { WindowRef window_ptr = (WindowRef) er.message; OSErr err; ControlRef root_control; if (window_ptr == tip_window) { HideWindow (tip_window); break; } if (!is_emacs_window (window_ptr)) goto OTHER; f = mac_window_to_frame (window_ptr); if ((er.modifiers & activeFlag) != 0) { /* A window has been activated */ Point mouse_loc; err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control); if (err == noErr) ActivateControl (root_control); x_detect_focus_change (dpyinfo, &er, &inev); mouse_loc.h = (er.where.h - (f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f))); mouse_loc.v = (er.where.v - (f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f))); /* Window-activated event counts as mouse movement, so update things that depend on mouse position. */ note_mouse_movement (f, &mouse_loc); } else { /* A window has been deactivated */ err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control); if (err == noErr) DeactivateControl (root_control); #ifdef USE_TOOLKIT_SCROLL_BARS if (dpyinfo->grabbed && tracked_scroll_bar) { struct input_event event; EVENT_INIT (event); event.kind = NO_EVENT; x_scroll_bar_handle_release (tracked_scroll_bar, &event); if (event.kind != NO_EVENT) { event.timestamp = timestamp; kbd_buffer_store_event_hold (&event, hold_quit); count++; } } #endif dpyinfo->grabbed = 0; x_detect_focus_change (dpyinfo, &er, &inev); if (f == dpyinfo->mouse_face_mouse_frame) { /* If we move outside the frame, then we're certainly no longer on any text in the frame. */ clear_mouse_face (dpyinfo); dpyinfo->mouse_face_mouse_frame = 0; } /* Generate a nil HELP_EVENT to cancel a help-echo. Do it only if there's something to cancel. Otherwise, the startup message is cleared when the mouse leaves the frame. */ if (any_help_event_p) do_help = -1; } } break; case keyDown: case keyUp: case autoKey: ObscureCursor (); f = mac_focus_frame (dpyinfo); XSETFRAME (inev.frame_or_window, f); /* If mouse-highlight is an integer, input clears out mouse highlighting. */ if (!dpyinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight) && !EQ (f->tool_bar_window, dpyinfo->mouse_face_window)) { clear_mouse_face (dpyinfo); dpyinfo->mouse_face_hidden = 1; } { UInt32 modifiers = er.modifiers, mapped_modifiers; UInt32 key_code = (er.message & keyCodeMask) >> 8; #ifdef MAC_OSX GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); #endif mapped_modifiers = mac_mapped_modifiers (modifiers, key_code); #if TARGET_API_MAC_CARBON if (!(mapped_modifiers & ~(mac_pass_command_to_system ? cmdKey : 0) & ~(mac_pass_control_to_system ? controlKey : 0))) goto OTHER; else #endif if (er.what != keyUp) do_keystroke (er.what, er.message & charCodeMask, key_code, modifiers, timestamp, &inev); } break; case kHighLevelEvent: AEProcessAppleEvent (&er); break; default: OTHER: #if TARGET_API_MAC_CARBON { OSStatus err; read_socket_inev = &inev; err = SendEventToEventTarget (eventRef, toolbox_dispatcher); read_socket_inev = NULL; } #endif break; } #if TARGET_API_MAC_CARBON ReleaseEvent (eventRef); #endif if (inev.kind != NO_EVENT) { inev.timestamp = timestamp; kbd_buffer_store_event_hold (&inev, hold_quit); count++; } if (do_help && !(hold_quit && hold_quit->kind != NO_EVENT)) { Lisp_Object frame; if (f) XSETFRAME (frame, f); else frame = Qnil; if (do_help > 0) { any_help_event_p = 1; gen_help_event (help_echo_string, frame, help_echo_window, help_echo_object, help_echo_pos); } else { help_echo_string = Qnil; gen_help_event (Qnil, frame, Qnil, Qnil, 0); } count++; } } /* If the focus was just given to an autoraising frame, raise it now. */ /* ??? This ought to be able to handle more than one such frame. */ if (pending_autoraise_frame) { x_raise_frame (pending_autoraise_frame); pending_autoraise_frame = 0; } if (mac_screen_config_changed) { mac_get_screen_info (dpyinfo); mac_screen_config_changed = 0; } #if !TARGET_API_MAC_CARBON /* Check which frames are still visible. We do this here because there doesn't seem to be any direct notification from the Window Manager that the visibility of a window has changed (at least, not in all cases). */ { Lisp_Object tail, frame; FOR_EACH_FRAME (tail, frame) { struct frame *f = XFRAME (frame); /* The tooltip has been drawn already. Avoid the SET_FRAME_GARBAGED in mac_handle_visibility_change. */ if (EQ (frame, tip_frame)) continue; if (FRAME_MAC_P (f)) mac_handle_visibility_change (f); } } #endif --handling_signal; UNBLOCK_INPUT; return count; } /*********************************************************************** Busy cursor ***********************************************************************/ #if TARGET_API_MAC_CARBON /* Show the spinning progress indicator for the frame F. Create it if it doesn't exist yet. */ void mac_show_hourglass (f) struct frame *f; { #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif if (!f->output_data.mac->hourglass_control) { Window w = FRAME_MAC_WINDOW (f); Rect r; ControlRef c; GetWindowPortBounds (w, &r); r.left = r.right - HOURGLASS_WIDTH; r.bottom = r.top + HOURGLASS_HEIGHT; if (CreateChasingArrowsControl (w, &r, &c) == noErr) f->output_data.mac->hourglass_control = c; } if (f->output_data.mac->hourglass_control) ShowControl (f->output_data.mac->hourglass_control); } /* Hide the spinning progress indicator for the frame F. Do nothing it doesn't exist yet. */ void mac_hide_hourglass (f) struct frame *f; { if (f->output_data.mac->hourglass_control) { #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif HideControl (f->output_data.mac->hourglass_control); } } /* Reposition the spinning progress indicator for the frame F. Do nothing it doesn't exist yet. */ void mac_reposition_hourglass (f) struct frame *f; { if (f->output_data.mac->hourglass_control) { #if USE_CG_DRAWING mac_prepare_for_quickdraw (f); #endif MoveControl (f->output_data.mac->hourglass_control, FRAME_PIXEL_WIDTH (f) - HOURGLASS_WIDTH, 0); } } #endif /* TARGET_API_MAC_CARBON */ /*********************************************************************** File selection dialog ***********************************************************************/ #if TARGET_API_MAC_CARBON extern Lisp_Object Qfile_name_history; static pascal void mac_nav_event_callback P_ ((NavEventCallbackMessage, NavCBRecPtr, void *)); /* The actual implementation of Fx_file_dialog. */ Lisp_Object mac_file_dialog (prompt, dir, default_filename, mustmatch, only_dir_p) Lisp_Object prompt, dir, default_filename, mustmatch, only_dir_p; { Lisp_Object file = Qnil; int count = SPECPDL_INDEX (); struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6; char filename[MAXPATHLEN]; static NavEventUPP mac_nav_event_callbackUPP = NULL; check_mac (); GCPRO6 (prompt, dir, default_filename, mustmatch, file, only_dir_p); CHECK_STRING (prompt); CHECK_STRING (dir); /* Create the dialog with PROMPT as title, using DIR as initial directory and using "*" as pattern. */ dir = Fexpand_file_name (dir, Qnil); { OSStatus status; NavDialogCreationOptions options; NavDialogRef dialogRef; NavTypeListHandle fileTypes = NULL; NavUserAction userAction; CFStringRef message=NULL, saveName = NULL; BLOCK_INPUT; /* No need for a callback function because we are modal */ NavGetDefaultDialogCreationOptions(&options); options.modality = kWindowModalityAppModal; options.location.h = options.location.v = -1; options.optionFlags = kNavDefaultNavDlogOptions; options.optionFlags |= kNavAllFilesInPopup; /* All files allowed */ options.optionFlags |= kNavSelectAllReadableItem; options.optionFlags &= ~kNavAllowMultipleFiles; if (!NILP(prompt)) { message = cfstring_create_with_string (prompt); options.message = message; } /* Don't set the application, let it use default. options.clientName = CFSTR ("Emacs"); */ if (mac_nav_event_callbackUPP == NULL) mac_nav_event_callbackUPP = NewNavEventUPP (mac_nav_event_callback); if (!NILP (only_dir_p)) status = NavCreateChooseFolderDialog(&options, mac_nav_event_callbackUPP, NULL, NULL, &dialogRef); else if (NILP (mustmatch)) { /* This is a save dialog */ options.optionFlags |= kNavDontConfirmReplacement; options.actionButtonLabel = CFSTR ("Ok"); options.windowTitle = CFSTR ("Enter name"); if (STRINGP (default_filename)) { Lisp_Object utf8 = ENCODE_UTF_8 (default_filename); char *begPtr = SDATA(utf8); char *filePtr = begPtr + SBYTES(utf8); while (filePtr != begPtr && !IS_DIRECTORY_SEP(filePtr[-1])) filePtr--; saveName = cfstring_create_with_utf8_cstring (filePtr); options.saveFileName = saveName; options.optionFlags |= kNavSelectDefaultLocation; } status = NavCreatePutFileDialog(&options, 'TEXT', kNavGenericSignature, mac_nav_event_callbackUPP, NULL, &dialogRef); } else { /* This is an open dialog*/ status = NavCreateChooseFileDialog(&options, fileTypes, mac_nav_event_callbackUPP, NULL, NULL, NULL, &dialogRef); } /* Set the default location and continue*/ if (status == noErr) { Lisp_Object encoded_dir = ENCODE_FILE (dir); AEDesc defLocAed; status = AECreateDesc (TYPE_FILE_NAME, SDATA (encoded_dir), SBYTES (encoded_dir), &defLocAed); if (status == noErr) { NavCustomControl(dialogRef, kNavCtlSetLocation, (void*) &defLocAed); AEDisposeDesc(&defLocAed); } status = NavDialogRun(dialogRef); } if (saveName) CFRelease(saveName); if (message) CFRelease(message); if (status == noErr) { userAction = NavDialogGetUserAction(dialogRef); switch (userAction) { case kNavUserActionNone: case kNavUserActionCancel: break; /* Treat cancel like C-g */ case kNavUserActionOpen: case kNavUserActionChoose: case kNavUserActionSaveAs: { NavReplyRecord reply; Size len; status = NavDialogGetReply(dialogRef, &reply); if (status != noErr) break; status = AEGetNthPtr (&reply.selection, 1, TYPE_FILE_NAME, NULL, NULL, filename, sizeof (filename) - 1, &len); if (status == noErr) { len = min (len, sizeof (filename) - 1); filename[len] = '\0'; if (reply.saveFileName) { /* If it was a saved file, we need to add the file name */ if (len && len < sizeof (filename) - 1 && filename[len-1] != '/') filename[len++] = '/'; CFStringGetCString(reply.saveFileName, filename+len, sizeof (filename) - len, #ifdef MAC_OSX kCFStringEncodingUTF8 #else CFStringGetSystemEncoding () #endif ); } file = DECODE_FILE (make_unibyte_string (filename, strlen (filename))); } NavDisposeReply(&reply); } break; } NavDialogDispose(dialogRef); UNBLOCK_INPUT; } else { UNBLOCK_INPUT; /* Fall back on minibuffer if there was a problem */ file = Fcompleting_read (prompt, intern ("read-file-name-internal"), dir, mustmatch, dir, Qfile_name_history, default_filename, Qnil); } } UNGCPRO; /* Make "Cancel" equivalent to C-g. */ if (NILP (file)) Fsignal (Qquit, Qnil); return unbind_to (count, file); } /* Need to register some event callback function for enabling drag and drop in Navigation Service dialogs. */ static pascal void mac_nav_event_callback (selector, parms, data) NavEventCallbackMessage selector; NavCBRecPtr parms; void *data; { } #endif /************************************************************************ Menu ************************************************************************/ #if !TARGET_API_MAC_CARBON #include #include #include #include #include #include #include #include #if defined (__MRC__) || (__MSL__ >= 0x6000) #include #endif #endif /* not TARGET_API_MAC_CARBON */ extern int menu_item_selection; extern int popup_activated_flag; extern int name_is_separator P_ ((const char *)); extern void find_and_call_menu_selection P_ ((FRAME_PTR, int, Lisp_Object, void *)); extern void set_frame_menubar P_ ((FRAME_PTR, int, int)); enum mac_menu_kind { /* Menu ID range */ MAC_MENU_APPLE, /* 0 (Reserved by Apple) */ MAC_MENU_MENU_BAR, /* 1 .. 233 */ MAC_MENU_M_APPLE, /* 234 (== M_APPLE) */ MAC_MENU_POPUP, /* 235 */ MAC_MENU_DRIVER, /* 236 .. 255 (Reserved) */ MAC_MENU_MENU_BAR_SUB, /* 256 .. 16383 */ MAC_MENU_POPUP_SUB, /* 16384 .. 32767 */ MAC_MENU_END /* 32768 */ }; static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768}; static int fill_menu P_ ((MenuRef, widget_value *, enum mac_menu_kind, int)); static void dispose_menus P_ ((enum mac_menu_kind, int)); #if !TARGET_API_MAC_CARBON static void do_apple_menu (SInt16 menu_item) { Str255 item_name; SInt16 da_driver_refnum; if (menu_item == I_ABOUT) NoteAlert (ABOUT_ALERT_ID, NULL); else { GetMenuItemText (GetMenuRef (M_APPLE), menu_item, item_name); da_driver_refnum = OpenDeskAcc (item_name); } } #endif /* !TARGET_API_MAC_CARBON */ /* Activate the menu bar of frame F. This is called from keyboard.c when it gets the MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue. To activate the menu bar, we use the button-press event location that was saved in saved_menu_event_location. But first we recompute the menu bar contents (the whole tree). The reason for saving the button event until here, instead of passing it to the toolkit right away, is that we can safely execute Lisp code. */ void x_activate_menubar (f) FRAME_PTR f; { SInt32 menu_choice; SInt16 menu_id, menu_item; set_frame_menubar (f, 0, 1); BLOCK_INPUT; popup_activated_flag = 1; menu_choice = MenuSelect (saved_menu_event_location); popup_activated_flag = 0; menu_id = HiWord (menu_choice); menu_item = LoWord (menu_choice); #if !TARGET_API_MAC_CARBON if (menu_id == min_menu_id[MAC_MENU_M_APPLE]) do_apple_menu (menu_item); else #endif if (menu_id) { MenuRef menu = GetMenuRef (menu_id); if (menu) { UInt32 refcon; GetMenuItemRefCon (menu, menu_item, &refcon); find_and_call_menu_selection (f, f->menu_bar_items_used, f->menu_bar_vector, (void *) refcon); } } HiliteMenu (0); UNBLOCK_INPUT; } #if TARGET_API_MAC_CARBON extern Lisp_Object Vshow_help_function; static Lisp_Object restore_show_help_function (old_show_help_function) Lisp_Object old_show_help_function; { Vshow_help_function = old_show_help_function; return Qnil; } static pascal OSStatus menu_target_item_handler (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err; MenuRef menu; MenuItemIndex menu_item; Lisp_Object help; GrafPtr port; int specpdl_count = SPECPDL_INDEX (); /* Don't be bothered with the overflowed toolbar items menu. */ if (!popup_activated ()) return eventNotHandledErr; err = GetEventParameter (event, kEventParamDirectObject, typeMenuRef, NULL, sizeof (MenuRef), NULL, &menu); if (err == noErr) err = GetEventParameter (event, kEventParamMenuItemIndex, typeMenuItemIndex, NULL, sizeof (MenuItemIndex), NULL, &menu_item); if (err == noErr) err = GetMenuItemProperty (menu, menu_item, MAC_EMACS_CREATOR_CODE, 'help', sizeof (Lisp_Object), NULL, &help); if (err != noErr) help = Qnil; /* Temporarily bind Vshow_help_function to Qnil because we don't want tooltips during menu tracking. */ record_unwind_protect (restore_show_help_function, Vshow_help_function); Vshow_help_function = Qnil; GetPort (&port); show_help_echo (help, Qnil, Qnil, Qnil, 1); SetPort (port); unbind_to (specpdl_count, Qnil); return err == noErr ? noErr : eventNotHandledErr; } /* Showing help echo string during menu tracking. */ static OSStatus install_menu_target_item_handler () { static const EventTypeSpec specs[] = {{kEventClassMenu, kEventMenuTargetItem}}; return InstallApplicationEventHandler (NewEventHandlerUPP (menu_target_item_handler), GetEventTypeCount (specs), specs, NULL, NULL); } #endif /* TARGET_API_MAC_CARBON */ /* Event handler function that pops down a menu on C-g. We can only pop down menus if CancelMenuTracking is present (OSX 10.3 or later). */ #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 static pascal OSStatus menu_quit_handler (nextHandler, theEvent, userData) EventHandlerCallRef nextHandler; EventRef theEvent; void* userData; { OSStatus err; UInt32 keyCode; UInt32 keyModifiers; err = GetEventParameter (theEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode); if (err == noErr) err = GetEventParameter (theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &keyModifiers); if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode)) { MenuRef menu = userData != 0 ? (MenuRef)userData : AcquireRootMenu (); CancelMenuTracking (menu, true, 0); if (!userData) ReleaseMenu (menu); return noErr; } return CallNextEventHandler (nextHandler, theEvent); } #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */ /* Add event handler to all menus that belong to KIND so we can detect C-g. ROOT_MENU is the root menu of the tracking session to dismiss when C-g is detected. NULL means the menu bar. If CancelMenuTracking isn't available, do nothing. */ static void install_menu_quit_handler (kind, root_menu) enum mac_menu_kind kind; MenuRef root_menu; { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 static const EventTypeSpec typesList[] = {{kEventClassKeyboard, kEventRawKeyDown}}; int id; #if MAC_OS_X_VERSION_MIN_REQUIRED == 1020 if (CancelMenuTracking == NULL) return; #endif for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++) { MenuRef menu = GetMenuRef (id); if (menu == NULL) break; InstallMenuEventHandler (menu, menu_quit_handler, GetEventTypeCount (typesList), typesList, root_menu, NULL); } #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */ } static Lisp_Object pop_down_menu (arg) Lisp_Object arg; { struct Lisp_Save_Value *p = XSAVE_VALUE (arg); FRAME_PTR f = p->pointer; MenuRef menu = GetMenuRef (min_menu_id[MAC_MENU_POPUP]); BLOCK_INPUT; /* Must reset this manually because the button release event is not passed to Emacs event loop. */ FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0; /* delete all menus */ dispose_menus (MAC_MENU_POPUP_SUB, 0); DeleteMenu (min_menu_id[MAC_MENU_POPUP]); DisposeMenu (menu); UNBLOCK_INPUT; return Qnil; } /* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the menu pops down. Return the selection. */ void create_and_show_popup_menu (f, first_wv, x, y, for_click) FRAME_PTR f; widget_value *first_wv; int x; int y; int for_click; { int result = 0; MenuRef menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p"); int menu_item_choice; int specpdl_count = SPECPDL_INDEX (); InsertMenu (menu, -1); fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB, min_menu_id[MAC_MENU_POPUP_SUB]); /* Add event handler so we can detect C-g. */ install_menu_quit_handler (MAC_MENU_POPUP, menu); install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu); record_unwind_protect (pop_down_menu, make_save_value (f, 0)); /* Adjust coordinates to be root-window-relative. */ x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f); y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f); /* Display the menu. */ popup_activated_flag = 1; menu_item_choice = PopUpMenuSelect (menu, y, x, 0); popup_activated_flag = 0; /* Get the refcon to find the correct item */ if (menu_item_choice) { MenuRef sel_menu = GetMenuRef (HiWord (menu_item_choice)); if (sel_menu) GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice), (UInt32 *) &result); } unbind_to (specpdl_count, Qnil); menu_item_selection = result; } static void add_menu_item (menu, pos, wv) MenuRef menu; int pos; widget_value *wv; { #if TARGET_API_MAC_CARBON CFStringRef item_name; #else Str255 item_name; #endif if (name_is_separator (wv->name)) AppendMenu (menu, "\p-"); else { AppendMenu (menu, "\pX"); #if TARGET_API_MAC_CARBON item_name = cfstring_create_with_utf8_cstring (wv->name); if (wv->key != NULL) { CFStringRef name, key; name = item_name; key = cfstring_create_with_utf8_cstring (wv->key); item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"), name, key); CFRelease (name); CFRelease (key); } SetMenuItemTextWithCFString (menu, pos, item_name); CFRelease (item_name); if (wv->enabled) EnableMenuItem (menu, pos); else DisableMenuItem (menu, pos); if (STRINGP (wv->help)) SetMenuItemProperty (menu, pos, MAC_EMACS_CREATOR_CODE, 'help', sizeof (Lisp_Object), &wv->help); #else /* ! TARGET_API_MAC_CARBON */ item_name[sizeof (item_name) - 1] = '\0'; strncpy (item_name, wv->name, sizeof (item_name) - 1); if (wv->key != NULL) { int len = strlen (item_name); strncpy (item_name + len, " ", sizeof (item_name) - 1 - len); len = strlen (item_name); strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len); } c2pstr (item_name); SetMenuItemText (menu, pos, item_name); if (wv->enabled) EnableItem (menu, pos); else DisableItem (menu, pos); #endif /* ! TARGET_API_MAC_CARBON */ /* Draw radio buttons and tickboxes. */ if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE || wv->button_type == BUTTON_TYPE_RADIO)) SetItemMark (menu, pos, checkMark); else SetItemMark (menu, pos, noMark); SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data); } } /* Construct native Mac OS menu based on widget_value tree. */ static int fill_menu (menu, wv, kind, submenu_id) MenuRef menu; widget_value *wv; enum mac_menu_kind kind; int submenu_id; { int pos; for (pos = 1; wv != NULL; wv = wv->next, pos++) { add_menu_item (menu, pos, wv); if (wv->contents && submenu_id < min_menu_id[kind + 1]) { MenuRef submenu = NewMenu (submenu_id, "\pX"); InsertMenu (submenu, -1); #if TARGET_API_MAC_CARBON SetMenuItemHierarchicalMenu (menu, pos, submenu); #else SetMenuItemHierarchicalID (menu, pos, submenu_id); #endif submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1); } } return submenu_id; } /* Fill menu bar with the items defined by WV. If DEEP_P, consider the entire menu trees we supply, rather than just the menu bar item names. */ void mac_fill_menubar (wv, deep_p) widget_value *wv; int deep_p; { int id, submenu_id; #if !TARGET_API_MAC_CARBON int title_changed_p = 0; #endif /* Clean up the menu bar when filled by the entire menu trees. */ if (deep_p) { dispose_menus (MAC_MENU_MENU_BAR, 0); dispose_menus (MAC_MENU_MENU_BAR_SUB, 0); #if !TARGET_API_MAC_CARBON title_changed_p = 1; #endif } /* Fill menu bar titles and submenus. Reuse the existing menu bar titles as much as possible to minimize redraw (if !deep_p). */ submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB]; for (id = min_menu_id[MAC_MENU_MENU_BAR]; wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1]; wv = wv->next, id++) { OSStatus err = noErr; MenuRef menu; #if TARGET_API_MAC_CARBON CFStringRef title; title = CFStringCreateWithCString (NULL, wv->name, kCFStringEncodingMacRoman); #else Str255 title; strncpy (title, wv->name, 255); title[255] = '\0'; c2pstr (title); #endif menu = GetMenuRef (id); if (menu) { #if TARGET_API_MAC_CARBON CFStringRef old_title; err = CopyMenuTitleAsCFString (menu, &old_title); if (err == noErr) { if (CFStringCompare (title, old_title, 0) != kCFCompareEqualTo) { #ifdef MAC_OSX if (id + 1 == min_menu_id[MAC_MENU_MENU_BAR + 1] || GetMenuRef (id + 1) == NULL) { /* This is a workaround for Mac OS X 10.5 where just calling SetMenuTitleWithCFString fails to change the title of the last (Help) menu in the menu bar. */ DeleteMenu (id); DisposeMenu (menu); menu = NULL; } else #endif /* MAC_OSX */ err = SetMenuTitleWithCFString (menu, title); } CFRelease (old_title); } else err = SetMenuTitleWithCFString (menu, title); #else /* !TARGET_API_MAC_CARBON */ if (!EqualString (title, (*menu)->menuData, false, false)) { DeleteMenu (id); DisposeMenu (menu); menu = NewMenu (id, title); InsertMenu (menu, GetMenuRef (id + 1) ? id + 1 : 0); title_changed_p = 1; } #endif /* !TARGET_API_MAC_CARBON */ } if (!menu) { #if TARGET_API_MAC_CARBON err = CreateNewMenu (id, 0, &menu); if (err == noErr) err = SetMenuTitleWithCFString (menu, title); #else menu = NewMenu (id, title); #endif if (err == noErr) { InsertMenu (menu, 0); #if !TARGET_API_MAC_CARBON title_changed_p = 1; #endif } } #if TARGET_API_MAC_CARBON CFRelease (title); #endif if (err == noErr) if (wv->contents) submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB, submenu_id); } if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuRef (id)) { dispose_menus (MAC_MENU_MENU_BAR, id); #if !TARGET_API_MAC_CARBON title_changed_p = 1; #endif } #if !TARGET_API_MAC_CARBON if (title_changed_p) InvalMenuBar (); #endif /* Add event handler so we can detect C-g. */ install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL); install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL); } /* Dispose of menus that belong to KIND, and remove them from the menu list. ID is the lower bound of menu IDs that will be processed. */ static void dispose_menus (kind, id) enum mac_menu_kind kind; int id; { for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++) { MenuRef menu = GetMenuRef (id); if (menu == NULL) break; DeleteMenu (id); DisposeMenu (menu); } } static void init_menu_bar () { #ifdef MAC_OSX OSStatus err; MenuRef menu; MenuItemIndex menu_index; err = GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1, &menu, &menu_index); if (err == noErr) SetMenuItemCommandKey (menu, menu_index, false, 0); EnableMenuCommand (NULL, kHICommandPreferences); err = GetIndMenuItemWithCommandID (NULL, kHICommandPreferences, 1, &menu, &menu_index); if (err == noErr) { SetMenuItemCommandKey (menu, menu_index, false, 0); InsertMenuItemTextWithCFString (menu, NULL, 0, kMenuItemAttrSeparator, 0); InsertMenuItemTextWithCFString (menu, CFSTR ("About Emacs"), 0, 0, kHICommandAbout); } #else /* !MAC_OSX */ #if TARGET_API_MAC_CARBON SetMenuItemCommandID (GetMenuRef (M_APPLE), I_ABOUT, kHICommandAbout); #endif #endif } /*********************************************************************** Popup Dialog ***********************************************************************/ #if TARGET_API_MAC_CARBON #define DIALOG_BUTTON_COMMAND_ID_OFFSET 'Bt\0\0' #define DIALOG_BUTTON_COMMAND_ID_P(id) \ (((id) & ~0xffff) == DIALOG_BUTTON_COMMAND_ID_OFFSET) #define DIALOG_BUTTON_COMMAND_ID_VALUE(id) \ ((id) - DIALOG_BUTTON_COMMAND_ID_OFFSET) #define DIALOG_BUTTON_MAKE_COMMAND_ID(value) \ ((value) + DIALOG_BUTTON_COMMAND_ID_OFFSET) extern EMACS_TIME timer_check P_ ((int)); static int quit_dialog_event_loop; static pascal OSStatus mac_handle_dialog_event (next_handler, event, data) EventHandlerCallRef next_handler; EventRef event; void *data; { OSStatus err, result = eventNotHandledErr; WindowRef window = (WindowRef) data; switch (GetEventClass (event)) { case kEventClassCommand: { HICommand command; err = GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof (HICommand), NULL, &command); if (err == noErr) if (DIALOG_BUTTON_COMMAND_ID_P (command.commandID)) { SetWRefCon (window, command.commandID); quit_dialog_event_loop = 1; break; } result = CallNextEventHandler (next_handler, event); } break; case kEventClassKeyboard: { OSStatus result; char char_code; result = CallNextEventHandler (next_handler, event); if (result != eventNotHandledErr) break; err = GetEventParameter (event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &char_code); if (err == noErr) switch (char_code) { case kEscapeCharCode: quit_dialog_event_loop = 1; break; default: { UInt32 modifiers, key_code; err = GetEventParameter (event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); if (err == noErr) err = GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &key_code); if (err == noErr) if (mac_quit_char_key_p (modifiers, key_code)) quit_dialog_event_loop = 1; } break; } } break; default: abort (); } if (quit_dialog_event_loop) { err = QuitEventLoop (GetCurrentEventLoop ()); if (err == noErr) result = noErr; } return result; } static OSStatus install_dialog_event_handler (window) WindowRef window; { static const EventTypeSpec specs[] = {{kEventClassCommand, kEventCommandProcess}, {kEventClassKeyboard, kEventRawKeyDown}}; static EventHandlerUPP handle_dialog_eventUPP = NULL; if (handle_dialog_eventUPP == NULL) handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event); return InstallWindowEventHandler (window, handle_dialog_eventUPP, GetEventTypeCount (specs), specs, window, NULL); } static Lisp_Object pop_down_dialog (arg) Lisp_Object arg; { struct Lisp_Save_Value *p = XSAVE_VALUE (arg); WindowRef window = p->pointer; BLOCK_INPUT; if (popup_activated_flag) EndAppModalStateForWindow (window); DisposeWindow (window); popup_activated_flag = 0; UNBLOCK_INPUT; return Qnil; } /* Pop up the dialog for frame F defined by FIRST_WV and loop until the dialog pops down. menu_item_selection will be set to the selection. */ void create_and_show_dialog (f, first_wv) FRAME_PTR f; widget_value *first_wv; { OSStatus err; char *dialog_name, *message; int nb_buttons, first_group_count, i, result = 0; widget_value *wv; short buttons_height, text_height, inner_width, inner_height; Rect empty_rect, *rects; WindowRef window = NULL; ControlRef *buttons, default_button = NULL, text; int specpdl_count = SPECPDL_INDEX (); dialog_name = first_wv->name; nb_buttons = dialog_name[1] - '0'; first_group_count = nb_buttons - (dialog_name[4] - '0'); wv = first_wv->contents; message = wv->value; wv = wv->next; SetRect (&empty_rect, 0, 0, 0, 0); /* Create dialog window. */ err = CreateNewWindow (kMovableModalWindowClass, kWindowStandardHandlerAttribute, &empty_rect, &window); if (err == noErr) { record_unwind_protect (pop_down_dialog, make_save_value (window, 0)); err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground, true); } if (err == noErr) err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q' ? CFSTR ("Question") : CFSTR ("Information"))); /* Create button controls and measure their optimal bounds. */ if (err == noErr) { buttons = alloca (sizeof (ControlRef) * nb_buttons); rects = alloca (sizeof (Rect) * nb_buttons); for (i = 0; i < nb_buttons; i++) { CFStringRef label = cfstring_create_with_utf8_cstring (wv->value); if (label == NULL) err = memFullErr; else { err = CreatePushButtonControl (window, &empty_rect, label, &buttons[i]); CFRelease (label); } if (err == noErr) { if (!wv->enabled) { #ifdef MAC_OSX err = DisableControl (buttons[i]); #else err = DeactivateControl (buttons[i]); #endif } else if (default_button == NULL) default_button = buttons[i]; } if (err == noErr) { SInt16 unused; rects[i] = empty_rect; err = GetBestControlRect (buttons[i], &rects[i], &unused); } if (err == noErr) { UInt32 command_id; OffsetRect (&rects[i], -rects[i].left, -rects[i].top); if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH) rects[i].right = DIALOG_BUTTON_MIN_WIDTH; else if (rects[i].right > DIALOG_MAX_INNER_WIDTH) rects[i].right = DIALOG_MAX_INNER_WIDTH; command_id = DIALOG_BUTTON_MAKE_COMMAND_ID ((int) wv->call_data); err = SetControlCommandID (buttons[i], command_id); } if (err != noErr) break; wv = wv->next; } } /* Layout buttons. rects[i] is set relative to the bottom-right corner of the inner box. */ if (err == noErr) { short bottom, right, max_height, left_align_shift; inner_width = DIALOG_MIN_INNER_WIDTH; bottom = right = max_height = 0; for (i = 0; i < nb_buttons; i++) { if (right - rects[i].right < - inner_width) { if (i != first_group_count && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH) inner_width = - (right - rects[i].right); else { bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE; right = max_height = 0; } } if (max_height < rects[i].bottom) max_height = rects[i].bottom; OffsetRect (&rects[i], right - rects[i].right, bottom - rects[i].bottom); right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE; if (i == first_group_count - 1) right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE; } buttons_height = - (bottom - max_height); left_align_shift = - (inner_width + rects[nb_buttons - 1].left); for (i = nb_buttons - 1; i >= first_group_count; i--) { if (bottom != rects[i].bottom) { left_align_shift = - (inner_width + rects[i].left); bottom = rects[i].bottom; } OffsetRect (&rects[i], left_align_shift, 0); } } /* Create a static text control and measure its bounds. */ if (err == noErr) { CFStringRef message_string; Rect bounds; message_string = cfstring_create_with_utf8_cstring (message); if (message_string == NULL) err = memFullErr; else { ControlFontStyleRec text_style; text_style.flags = 0; SetRect (&bounds, 0, 0, inner_width, 0); err = CreateStaticTextControl (window, &bounds, message_string, &text_style, &text); CFRelease (message_string); } if (err == noErr) { SInt16 unused; bounds = empty_rect; err = GetBestControlRect (text, &bounds, &unused); } if (err == noErr) { text_height = bounds.bottom - bounds.top; if (text_height < DIALOG_TEXT_MIN_HEIGHT) text_height = DIALOG_TEXT_MIN_HEIGHT; } } /* Place buttons. */ if (err == noErr) { inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE + buttons_height); for (i = 0; i < nb_buttons; i++) { OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width, DIALOG_TOP_MARGIN + inner_height); SetControlBounds (buttons[i], &rects[i]); } } /* Place text. */ if (err == noErr) { Rect bounds; SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN, DIALOG_LEFT_MARGIN + inner_width, DIALOG_TOP_MARGIN + text_height); SetControlBounds (text, &bounds); } /* Create the application icon at the upper-left corner. */ if (err == noErr) { ControlButtonContentInfo content; ControlRef icon; static const ProcessSerialNumber psn = {0, kCurrentProcess}; #ifdef MAC_OSX FSRef app_location; #else ProcessInfoRec pinfo; FSSpec app_spec; #endif SInt16 unused; content.contentType = kControlContentIconRef; #ifdef MAC_OSX err = GetProcessBundleLocation (&psn, &app_location); if (err == noErr) err = GetIconRefFromFileInfo (&app_location, 0, NULL, 0, NULL, kIconServicesNormalUsageFlag, &content.u.iconRef, &unused); #else bzero (&pinfo, sizeof (ProcessInfoRec)); pinfo.processInfoLength = sizeof (ProcessInfoRec); pinfo.processAppSpec = &app_spec; err = GetProcessInformation (&psn, &pinfo); if (err == noErr) err = GetIconRefFromFile (&app_spec, &content.u.iconRef, &unused); #endif if (err == noErr) { Rect bounds; SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN, DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH, DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT); err = CreateIconControl (window, &bounds, &content, true, &icon); ReleaseIconRef (content.u.iconRef); } } /* Show the dialog window and run event loop. */ if (err == noErr) if (default_button) err = SetWindowDefaultButton (window, default_button); if (err == noErr) err = install_dialog_event_handler (window); if (err == noErr) { SizeWindow (window, DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN, DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN, true); err = RepositionWindow (window, FRAME_MAC_WINDOW (f), kWindowAlertPositionOnParentWindow); } if (err == noErr) { SetWRefCon (window, 0); ShowWindow (window); BringToFront (window); popup_activated_flag = 1; err = BeginAppModalStateForWindow (window); } if (err == noErr) { EventTargetRef toolbox_dispatcher = GetEventDispatcherTarget (); quit_dialog_event_loop = 0; while (1) { EMACS_TIME next_time = timer_check (1); long secs = EMACS_SECS (next_time); long usecs = EMACS_USECS (next_time); EventTimeout timeout; EventRef event; if (secs < 0 || (secs == 0 && usecs == 0)) { /* Sometimes timer_check returns -1 (no timers) even if there are timers. So do a timeout anyway. */ secs = 1; usecs = 0; } timeout = (secs * kEventDurationSecond + usecs * kEventDurationMicrosecond); err = ReceiveNextEvent (0, NULL, timeout, kEventRemoveFromQueue, &event); if (err == noErr) { SendEventToEventTarget (event, toolbox_dispatcher); ReleaseEvent (event); } #if 0 /* defined (MAC_OSX) */ else if (err != eventLoopTimedOutErr) { if (err == eventLoopQuitErr) err = noErr; break; } #else /* The return value of ReceiveNextEvent seems to be unreliable. Use our own global variable instead. */ if (quit_dialog_event_loop) { err = noErr; break; } #endif } } if (err == noErr) { UInt32 command_id = GetWRefCon (window); if (DIALOG_BUTTON_COMMAND_ID_P (command_id)) result = DIALOG_BUTTON_COMMAND_ID_VALUE (command_id); } unbind_to (specpdl_count, Qnil); menu_item_selection = result; } #else /* not TARGET_API_MAC_CARBON */ #define DIALOG_WINDOW_RESOURCE 130 int mac_dialog (widget_value *wv) { char *dialog_name; char *prompt; char **button_labels; UInt32 *ref_cons; int nb_buttons; int left_count; int i; int dialog_width; Rect rect; WindowRef window_ptr; ControlRef ch; int left; EventRecord event_record; SInt16 part_code; int control_part_code; Point mouse; dialog_name = wv->name; nb_buttons = dialog_name[1] - '0'; left_count = nb_buttons - (dialog_name[4] - '0'); button_labels = (char **) alloca (sizeof (char *) * nb_buttons); ref_cons = (UInt32 *) alloca (sizeof (UInt32) * nb_buttons); wv = wv->contents; prompt = (char *) alloca (strlen (wv->value) + 1); strcpy (prompt, wv->value); c2pstr (prompt); wv = wv->next; for (i = 0; i < nb_buttons; i++) { button_labels[i] = wv->value; button_labels[i] = (char *) alloca (strlen (wv->value) + 1); strcpy (button_labels[i], wv->value); c2pstr (button_labels[i]); ref_cons[i] = (UInt32) wv->call_data; wv = wv->next; } window_ptr = GetNewCWindow (DIALOG_WINDOW_RESOURCE, NULL, (WindowRef) -1); SetPortWindowPort (window_ptr); TextFont (0); /* Left and right margins in the dialog are 13 pixels each.*/ dialog_width = 14; /* Calculate width of dialog box: 8 pixels on each side of the text label in each button, 12 pixels between buttons. */ for (i = 0; i < nb_buttons; i++) dialog_width += StringWidth (button_labels[i]) + 16 + 12; if (left_count != 0 && nb_buttons - left_count != 0) dialog_width += 12; dialog_width = max (dialog_width, StringWidth (prompt) + 26); SizeWindow (window_ptr, dialog_width, 78, 0); ShowWindow (window_ptr); SetPortWindowPort (window_ptr); TextFont (0); MoveTo (13, 29); DrawString (prompt); left = 13; for (i = 0; i < nb_buttons; i++) { int button_width = StringWidth (button_labels[i]) + 16; SetRect (&rect, left, 45, left + button_width, 65); ch = NewControl (window_ptr, &rect, button_labels[i], 1, 0, 0, 0, kControlPushButtonProc, ref_cons[i]); left += button_width + 12; if (i == left_count - 1) left += 12; } i = 0; while (!i) { if (WaitNextEvent (mDownMask, &event_record, 10, NULL)) if (event_record.what == mouseDown) { part_code = FindWindow (event_record.where, &window_ptr); if (part_code == inContent) { mouse = event_record.where; GlobalToLocal (&mouse); control_part_code = FindControl (mouse, window_ptr, &ch); if (control_part_code == kControlButtonPart) if (TrackControl (ch, mouse, NULL)) i = GetControlReference (ch); } } } DisposeWindow (window_ptr); return i; } #endif /* not TARGET_API_MAC_CARBON */ /*********************************************************************** Selection support ***********************************************************************/ #if !TARGET_API_MAC_CARBON #include #include #endif extern Lisp_Object Vselection_converter_alist; extern Lisp_Object Qmac_scrap_name, Qmac_ostype; static ScrapFlavorType get_flavor_type_from_symbol P_ ((Lisp_Object, Selection)); /* Get a reference to the selection corresponding to the symbol SYM. The reference is set to *SEL, and it becomes NULL if there's no corresponding selection. Clear the selection if CLEAR_P is non-zero. */ OSStatus mac_get_selection_from_symbol (sym, clear_p, sel) Lisp_Object sym; int clear_p; Selection *sel; { OSStatus err = noErr; Lisp_Object str = Fget (sym, Qmac_scrap_name); if (!STRINGP (str)) *sel = NULL; else { #if TARGET_API_MAC_CARBON #ifdef MAC_OSX CFStringRef scrap_name = cfstring_create_with_string (str); OptionBits options = (clear_p ? kScrapClearNamedScrap : kScrapGetNamedScrap); err = GetScrapByName (scrap_name, options, sel); CFRelease (scrap_name); #else /* !MAC_OSX */ if (clear_p) err = ClearCurrentScrap (); if (err == noErr) err = GetCurrentScrap (sel); #endif /* !MAC_OSX */ #else /* !TARGET_API_MAC_CARBON */ if (clear_p) err = ZeroScrap (); if (err == noErr) *sel = 1; #endif /* !TARGET_API_MAC_CARBON */ } return err; } /* Get a scrap flavor type from the symbol SYM. Return 0 if no corresponding flavor type. If SEL is non-zero, the return value is non-zero only when the SEL has the flavor type. */ static ScrapFlavorType get_flavor_type_from_symbol (sym, sel) Lisp_Object sym; Selection sel; { Lisp_Object str = Fget (sym, Qmac_ostype); ScrapFlavorType flavor_type; if (STRINGP (str) && SBYTES (str) == 4) flavor_type = EndianU32_BtoN (*((UInt32 *) SDATA (str))); else flavor_type = 0; if (flavor_type && sel) { #if TARGET_API_MAC_CARBON OSStatus err; ScrapFlavorFlags flags; err = GetScrapFlavorFlags (sel, flavor_type, &flags); if (err != noErr) flavor_type = 0; #else /* !TARGET_API_MAC_CARBON */ SInt32 size, offset; size = GetScrap (NULL, flavor_type, &offset); if (size < 0) flavor_type = 0; #endif /* !TARGET_API_MAC_CARBON */ } return flavor_type; } /* Check if the symbol SYM has a corresponding selection target type. */ int mac_valid_selection_target_p (sym) Lisp_Object sym; { return get_flavor_type_from_symbol (sym, 0) != 0; } /* Clear the selection whose reference is *SEL. */ OSStatus mac_clear_selection (sel) Selection *sel; { #if TARGET_API_MAC_CARBON #ifdef MAC_OSX return ClearScrap (sel); #else OSStatus err; err = ClearCurrentScrap (); if (err == noErr) err = GetCurrentScrap (sel); return err; #endif #else /* !TARGET_API_MAC_CARBON */ return ZeroScrap (); #endif /* !TARGET_API_MAC_CARBON */ } /* Get ownership information for SEL. Emacs can detect a change of the ownership by comparing saved and current values of the ownership information. */ Lisp_Object mac_get_selection_ownership_info (sel) Selection sel; { #if TARGET_API_MAC_CARBON return long_to_cons ((unsigned long) sel); #else /* !TARGET_API_MAC_CARBON */ ScrapStuffPtr scrap_info = InfoScrap (); return make_number (scrap_info->scrapCount); #endif /* !TARGET_API_MAC_CARBON */ } /* Return non-zero if VALUE is a valid selection value for TARGET. */ int mac_valid_selection_value_p (value, target) Lisp_Object value, target; { return STRINGP (value); } /* Put Lisp object VALUE to the selection SEL. The target type is specified by TARGET. */ OSStatus mac_put_selection_value (sel, target, value) Selection sel; Lisp_Object target, value; { ScrapFlavorType flavor_type = get_flavor_type_from_symbol (target, 0); if (flavor_type == 0 || !STRINGP (value)) return noTypeErr; #if TARGET_API_MAC_CARBON return PutScrapFlavor (sel, flavor_type, kScrapFlavorMaskNone, SBYTES (value), SDATA (value)); #else /* !TARGET_API_MAC_CARBON */ return PutScrap (SBYTES (value), flavor_type, SDATA (value)); #endif /* !TARGET_API_MAC_CARBON */ } /* Check if data for the target type TARGET is available in SEL. */ int mac_selection_has_target_p (sel, target) Selection sel; Lisp_Object target; { return get_flavor_type_from_symbol (target, sel) != 0; } /* Get data for the target type TARGET from SEL and create a Lisp string. Return nil if failed to get data. */ Lisp_Object mac_get_selection_value (sel, target) Selection sel; Lisp_Object target; { OSStatus err; Lisp_Object result = Qnil; ScrapFlavorType flavor_type = get_flavor_type_from_symbol (target, sel); #if TARGET_API_MAC_CARBON Size size; if (flavor_type) { err = GetScrapFlavorSize (sel, flavor_type, &size); if (err == noErr) { do { result = make_uninit_string (size); err = GetScrapFlavorData (sel, flavor_type, &size, SDATA (result)); if (err != noErr) result = Qnil; else if (size < SBYTES (result)) result = make_unibyte_string (SDATA (result), size); } while (STRINGP (result) && size > SBYTES (result)); } } #else Handle handle; SInt32 size, offset; if (flavor_type) size = GetScrap (NULL, flavor_type, &offset); if (size >= 0) { handle = NewHandle (size); HLock (handle); size = GetScrap (handle, flavor_type, &offset); if (size >= 0) result = make_unibyte_string (*handle, size); DisposeHandle (handle); } #endif return result; } /* Get the list of target types in SEL. The return value is a list of target type symbols possibly followed by scrap flavor type strings. */ Lisp_Object mac_get_selection_target_list (sel) Selection sel; { Lisp_Object result = Qnil, rest, target; #if TARGET_API_MAC_CARBON OSStatus err; UInt32 count, i, type; ScrapFlavorInfo *flavor_info = NULL; Lisp_Object strings = Qnil; err = GetScrapFlavorCount (sel, &count); if (err == noErr) flavor_info = xmalloc (sizeof (ScrapFlavorInfo) * count); err = GetScrapFlavorInfoList (sel, &count, flavor_info); if (err != noErr) { xfree (flavor_info); flavor_info = NULL; } if (flavor_info == NULL) count = 0; #endif for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest)) { ScrapFlavorType flavor_type = 0; if (CONSP (XCAR (rest)) && (target = XCAR (XCAR (rest)), SYMBOLP (target)) && (flavor_type = get_flavor_type_from_symbol (target, sel))) { result = Fcons (target, result); #if TARGET_API_MAC_CARBON for (i = 0; i < count; i++) if (flavor_info[i].flavorType == flavor_type) { flavor_info[i].flavorType = 0; break; } #endif } } #if TARGET_API_MAC_CARBON if (flavor_info) { for (i = 0; i < count; i++) if (flavor_info[i].flavorType) { type = EndianU32_NtoB (flavor_info[i].flavorType); strings = Fcons (make_unibyte_string ((char *) &type, 4), strings); } result = nconc2 (result, strings); xfree (flavor_info); } #endif return result; } /*********************************************************************** Apple event support ***********************************************************************/ extern pascal OSErr mac_handle_apple_event P_ ((const AppleEvent *, AppleEvent *, SInt32)); extern void cleanup_all_suspended_apple_events P_ ((void)); void init_apple_event_handler () { OSErr err; long result; /* Make sure we have Apple events before starting. */ err = Gestalt (gestaltAppleEventsAttr, &result); if (err != noErr) abort (); if (!(result & (1 << gestaltAppleEventsPresent))) abort (); err = AEInstallEventHandler (typeWildCard, typeWildCard, #if TARGET_API_MAC_CARBON NewAEEventHandlerUPP (mac_handle_apple_event), #else NewAEEventHandlerProc (mac_handle_apple_event), #endif 0L, false); if (err != noErr) abort (); atexit (cleanup_all_suspended_apple_events); } /*********************************************************************** Drag and drop support ***********************************************************************/ #if TARGET_API_MAC_CARBON extern Lisp_Object Vmac_dnd_known_types; static pascal OSErr mac_do_track_drag P_ ((DragTrackingMessage, WindowRef, void *, DragRef)); static pascal OSErr mac_do_receive_drag P_ ((WindowRef, void *, DragRef)); static DragTrackingHandlerUPP mac_do_track_dragUPP = NULL; static DragReceiveHandlerUPP mac_do_receive_dragUPP = NULL; static OSErr create_apple_event_from_drag_ref (drag, num_types, types, result) DragRef drag; UInt32 num_types; const FlavorType *types; AppleEvent *result; { OSErr err; UInt16 num_items; AppleEvent items; long index; char *buf = NULL; err = CountDragItems (drag, &num_items); if (err != noErr) return err; err = AECreateList (NULL, 0, false, &items); if (err != noErr) return err; for (index = 1; index <= num_items; index++) { ItemReference item; DescType desc_type = typeNull; Size size; err = GetDragItemReferenceNumber (drag, index, &item); if (err == noErr) { int i; for (i = 0; i < num_types; i++) { err = GetFlavorDataSize (drag, item, types[i], &size); if (err == noErr) { buf = xrealloc (buf, size); err = GetFlavorData (drag, item, types[i], buf, &size, 0); } if (err == noErr) { desc_type = types[i]; break; } } } err = AEPutPtr (&items, index, desc_type, desc_type != typeNull ? buf : NULL, desc_type != typeNull ? size : 0); if (err != noErr) break; } xfree (buf); if (err == noErr) { err = create_apple_event (0, 0, result); /* Dummy class and ID. */ if (err == noErr) err = AEPutParamDesc (result, keyDirectObject, &items); if (err != noErr) AEDisposeDesc (result); } AEDisposeDesc (&items); return err; } static void mac_store_drag_event (window, mouse_pos, modifiers, desc) WindowRef window; Point mouse_pos; SInt16 modifiers; const AEDesc *desc; { struct input_event buf; EVENT_INIT (buf); buf.kind = DRAG_N_DROP_EVENT; buf.modifiers = mac_to_emacs_modifiers (modifiers, 0); buf.timestamp = TickCount () * (1000 / 60); XSETINT (buf.x, mouse_pos.h); XSETINT (buf.y, mouse_pos.v); XSETFRAME (buf.frame_or_window, mac_window_to_frame (window)); buf.arg = mac_aedesc_to_lisp (desc); kbd_buffer_store_event (&buf); } static pascal OSErr mac_do_track_drag (message, window, refcon, drag) DragTrackingMessage message; WindowRef window; void *refcon; DragRef drag; { OSErr err = noErr; static int can_accept; UInt16 num_items, index; if (GetFrontWindowOfClass (kMovableModalWindowClass, false)) return dragNotAcceptedErr; switch (message) { case kDragTrackingEnterHandler: err = CountDragItems (drag, &num_items); if (err != noErr) break; can_accept = 0; for (index = 1; index <= num_items; index++) { ItemReference item; FlavorFlags flags; Lisp_Object rest; err = GetDragItemReferenceNumber (drag, index, &item); if (err != noErr) continue; for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest)) { Lisp_Object str; FlavorType type; str = XCAR (rest); if (!(STRINGP (str) && SBYTES (str) == 4)) continue; type = EndianU32_BtoN (*((UInt32 *) SDATA (str))); err = GetFlavorFlags (drag, item, type, &flags); if (err == noErr) { can_accept = 1; break; } } } break; case kDragTrackingEnterWindow: if (can_accept) { RgnHandle hilite_rgn = NewRgn (); if (hilite_rgn) { Rect r; GetWindowPortBounds (window, &r); OffsetRect (&r, -r.left, -r.top); RectRgn (hilite_rgn, &r); ShowDragHilite (drag, hilite_rgn, true); DisposeRgn (hilite_rgn); } SetThemeCursor (kThemeCopyArrowCursor); } break; case kDragTrackingInWindow: break; case kDragTrackingLeaveWindow: if (can_accept) { HideDragHilite (drag); SetThemeCursor (kThemeArrowCursor); } break; case kDragTrackingLeaveHandler: break; } if (err != noErr) return dragNotAcceptedErr; return noErr; } static pascal OSErr mac_do_receive_drag (window, refcon, drag) WindowRef window; void *refcon; DragRef drag; { OSErr err; int num_types, i; Lisp_Object rest, str; FlavorType *types; AppleEvent apple_event; Point mouse_pos; SInt16 modifiers; if (GetFrontWindowOfClass (kMovableModalWindowClass, false)) return dragNotAcceptedErr; num_types = 0; for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest)) { str = XCAR (rest); if (STRINGP (str) && SBYTES (str) == 4) num_types++; } types = xmalloc (sizeof (FlavorType) * num_types); i = 0; for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest)) { str = XCAR (rest); if (STRINGP (str) && SBYTES (str) == 4) types[i++] = EndianU32_BtoN (*((UInt32 *) SDATA (str))); } err = create_apple_event_from_drag_ref (drag, num_types, types, &apple_event); xfree (types); if (err == noErr) err = GetDragMouse (drag, &mouse_pos, NULL); if (err == noErr) { GlobalToLocal (&mouse_pos); err = GetDragModifiers (drag, NULL, NULL, &modifiers); } if (err == noErr) { UInt32 key_modifiers = modifiers; err = AEPutParamPtr (&apple_event, kEventParamKeyModifiers, typeUInt32, &key_modifiers, sizeof (UInt32)); } if (err == noErr) { mac_store_drag_event (window, mouse_pos, 0, &apple_event); AEDisposeDesc (&apple_event); mac_wakeup_from_rne (); return noErr; } else return dragNotAcceptedErr; } #endif /* TARGET_API_MAC_CARBON */ static OSErr install_drag_handler (window) WindowRef window; { OSErr err = noErr; #if TARGET_API_MAC_CARBON if (mac_do_track_dragUPP == NULL) mac_do_track_dragUPP = NewDragTrackingHandlerUPP (mac_do_track_drag); if (mac_do_receive_dragUPP == NULL) mac_do_receive_dragUPP = NewDragReceiveHandlerUPP (mac_do_receive_drag); err = InstallTrackingHandler (mac_do_track_dragUPP, window, NULL); if (err == noErr) err = InstallReceiveHandler (mac_do_receive_dragUPP, window, NULL); #endif return err; } static void remove_drag_handler (window) WindowRef window; { #if TARGET_API_MAC_CARBON if (mac_do_track_dragUPP) RemoveTrackingHandler (mac_do_track_dragUPP, window); if (mac_do_receive_dragUPP) RemoveReceiveHandler (mac_do_receive_dragUPP, window); #endif } #if TARGET_API_MAC_CARBON /* Return default value for mac-dnd-known-types. */ Lisp_Object mac_dnd_default_known_types () { Lisp_Object result = list4 (build_string ("hfs "), build_string ("utxt"), build_string ("TEXT"), build_string ("TIFF")); #ifdef MAC_OSX result = Fcons (build_string ("furl"), result); #endif return result; } #endif /*********************************************************************** Services menu support ***********************************************************************/ #ifdef MAC_OSX extern Lisp_Object Qservice, Qpaste, Qperform; extern Lisp_Object Vmac_service_selection; static OSStatus mac_store_service_event (event) EventRef event; { OSStatus err; Lisp_Object id_key; int num_params; const EventParamName *names; const EventParamType *types; static const EventParamName names_pfm[] = {kEventParamServiceMessageName, kEventParamServiceUserData}; static const EventParamType types_pfm[] = {typeCFStringRef, typeCFStringRef}; switch (GetEventKind (event)) { case kEventServicePaste: id_key = Qpaste; num_params = 0; names = NULL; types = NULL; break; case kEventServicePerform: id_key = Qperform; num_params = sizeof (names_pfm) / sizeof (names_pfm[0]); names = names_pfm; types = types_pfm; break; default: abort (); } err = mac_store_event_ref_as_apple_event (0, 0, Qservice, id_key, event, num_params, names, types); return err; } static OSStatus copy_scrap_flavor_data (from_scrap, to_scrap, flavor_type) ScrapRef from_scrap, to_scrap; ScrapFlavorType flavor_type; { OSStatus err; Size size, size_allocated; char *buf = NULL; err = GetScrapFlavorSize (from_scrap, flavor_type, &size); if (err == noErr) buf = xmalloc (size); while (buf) { size_allocated = size; err = GetScrapFlavorData (from_scrap, flavor_type, &size, buf); if (err != noErr) { xfree (buf); buf = NULL; } else if (size_allocated < size) buf = xrealloc (buf, size); else break; } if (err == noErr) { if (buf == NULL) err = memFullErr; else { err = PutScrapFlavor (to_scrap, flavor_type, kScrapFlavorMaskNone, size, buf); xfree (buf); } } return err; } static OSStatus mac_handle_service_event (call_ref, event, data) EventHandlerCallRef call_ref; EventRef event; void *data; { OSStatus err = noErr; ScrapRef cur_scrap, specific_scrap; UInt32 event_kind = GetEventKind (event); CFMutableArrayRef copy_types, paste_types; CFStringRef type; Lisp_Object rest; ScrapFlavorType flavor_type; /* Check if Vmac_service_selection is a valid selection that has a corresponding scrap. */ if (!SYMBOLP (Vmac_service_selection)) err = eventNotHandledErr; else err = mac_get_selection_from_symbol (Vmac_service_selection, 0, &cur_scrap); if (!(err == noErr && cur_scrap)) return eventNotHandledErr; switch (event_kind) { case kEventServiceGetTypes: /* Set paste types. */ err = GetEventParameter (event, kEventParamServicePasteTypes, typeCFMutableArrayRef, NULL, sizeof (CFMutableArrayRef), NULL, &paste_types); if (err != noErr) break; for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest)) if (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest))) && (flavor_type = get_flavor_type_from_symbol (XCAR (XCAR (rest)), 0))) { type = CreateTypeStringWithOSType (flavor_type); if (type) { CFArrayAppendValue (paste_types, type); CFRelease (type); } } /* Set copy types. */ err = GetEventParameter (event, kEventParamServiceCopyTypes, typeCFMutableArrayRef, NULL, sizeof (CFMutableArrayRef), NULL, ©_types); if (err != noErr) break; if (NILP (Fx_selection_owner_p (Vmac_service_selection))) break; else goto copy_all_flavors; case kEventServiceCopy: err = GetEventParameter (event, kEventParamScrapRef, typeScrapRef, NULL, sizeof (ScrapRef), NULL, &specific_scrap); if (err != noErr || NILP (Fx_selection_owner_p (Vmac_service_selection))) { err = eventNotHandledErr; break; } copy_all_flavors: { UInt32 count, i; ScrapFlavorInfo *flavor_info = NULL; ScrapFlavorFlags flags; err = GetScrapFlavorCount (cur_scrap, &count); if (err == noErr) flavor_info = xmalloc (sizeof (ScrapFlavorInfo) * count); err = GetScrapFlavorInfoList (cur_scrap, &count, flavor_info); if (err != noErr) { xfree (flavor_info); flavor_info = NULL; } if (flavor_info == NULL) break; for (i = 0; i < count; i++) { flavor_type = flavor_info[i].flavorType; err = GetScrapFlavorFlags (cur_scrap, flavor_type, &flags); if (err == noErr && !(flags & kScrapFlavorMaskSenderOnly)) { if (event_kind == kEventServiceCopy) err = copy_scrap_flavor_data (cur_scrap, specific_scrap, flavor_type); else /* event_kind == kEventServiceGetTypes */ { type = CreateTypeStringWithOSType (flavor_type); if (type) { CFArrayAppendValue (copy_types, type); CFRelease (type); } } } } xfree (flavor_info); } break; case kEventServicePaste: case kEventServicePerform: { int data_exists_p = 0; err = GetEventParameter (event, kEventParamScrapRef, typeScrapRef, NULL, sizeof (ScrapRef), NULL, &specific_scrap); if (err == noErr) err = mac_clear_selection (&cur_scrap); if (err == noErr) for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest)) { if (! (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest))))) continue; flavor_type = get_flavor_type_from_symbol (XCAR (XCAR (rest)), specific_scrap); if (flavor_type == 0) continue; err = copy_scrap_flavor_data (specific_scrap, cur_scrap, flavor_type); if (err == noErr) data_exists_p = 1; } if (!data_exists_p) err = eventNotHandledErr; else err = mac_store_service_event (event); } break; } if (err != noErr) err = eventNotHandledErr; return err; } static OSStatus install_service_handler () { static const EventTypeSpec specs[] = {{kEventClassService, kEventServiceGetTypes}, {kEventClassService, kEventServiceCopy}, {kEventClassService, kEventServicePaste}, {kEventClassService, kEventServicePerform}}; return InstallApplicationEventHandler (NewEventHandlerUPP (mac_handle_service_event), GetEventTypeCount (specs), specs, NULL, NULL); } #endif /* MAC_OSX */ /*********************************************************************** Initialization ***********************************************************************/ void mac_toolbox_initialize () { any_help_event_p = 0; init_menu_bar (); #ifdef MAC_OSX init_apple_event_handler (); #endif #if USE_MAC_TSM init_tsm (); #endif } /* arch-tag: 71a597a8-6e9f-47b0-8b89-5a5ae3e16516 (do not change this comment) */