[USE_MAC_TSM] (mac_handle_text_input_event):
[emacs.git] / src / mactoolbox.c
blobf470c5175545e674f093c0f22e66a11629d48c0f
1 /* Functions for GUI implemented with (HI)Toolbox on the Mac OS.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
22 #include <config.h>
24 #include <stdio.h>
26 #include "lisp.h"
27 #include "blockinput.h"
29 #include "macterm.h"
31 #if !TARGET_API_MAC_CARBON
32 #include <Quickdraw.h>
33 #include <ToolUtils.h>
34 #include <Sound.h>
35 #include <Events.h>
36 #include <Script.h>
37 #include <Resources.h>
38 #include <Fonts.h>
39 #include <TextUtils.h>
40 #include <LowMem.h>
41 #include <Controls.h>
42 #include <Windows.h>
43 #include <Displays.h>
44 #if defined (__MRC__) || (__MSL__ >= 0x6000)
45 #include <ControlDefinitions.h>
46 #endif
48 #if __profile__
49 #include <profiler.h>
50 #endif
51 #endif /* not TARGET_API_MAC_CARBON */
53 #include "charset.h"
54 #include "coding.h"
55 #include "frame.h"
56 #include "dispextern.h"
57 #include "fontset.h"
58 #include "termhooks.h"
59 #include "buffer.h"
60 #include "window.h"
61 #include "keyboard.h"
63 #include <sys/param.h>
65 #ifndef MAC_OSX
66 #include <alloca.h>
67 #endif
70 /************************************************************************
71 General
72 ************************************************************************/
74 /* The difference in pixels between the top left corner of the
75 Emacs window (including possible window manager decorations)
76 and FRAME_MAC_WINDOW (f). */
77 #define FRAME_OUTER_TO_INNER_DIFF_X(f) ((f)->x_pixels_diff)
78 #define FRAME_OUTER_TO_INNER_DIFF_Y(f) ((f)->y_pixels_diff)
80 #define mac_window_to_frame(wp) (((mac_output *) GetWRefCon (wp))->mFP)
82 void
83 mac_alert_sound_play ()
85 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
86 AlertSoundPlay ();
87 #else
88 SysBeep (1);
89 #endif
93 /************************************************************************
94 Application
95 ************************************************************************/
97 extern struct frame *mac_focus_frame P_ ((struct mac_display_info *));
98 extern void do_keystroke P_ ((EventKind, unsigned char, UInt32, UInt32,
99 unsigned long, struct input_event *));
100 extern UInt32 mac_mapped_modifiers P_ ((UInt32, UInt32));
101 #if TARGET_API_MAC_CARBON
102 extern int mac_to_emacs_modifiers P_ ((UInt32, UInt32));
103 #else
104 extern int mac_to_emacs_modifiers P_ ((EventModifiers, EventModifiers));
105 #endif
107 #if TARGET_API_MAC_CARBON
108 /* Points to the variable `inev' in the function XTread_socket. It is
109 used for passing an input event to the function back from
110 Carbon/Apple event handlers. */
111 static struct input_event *read_socket_inev = NULL;
113 extern const unsigned char keycode_to_xkeysym_table[];
114 extern EMACS_INT extra_keyboard_modifiers;
116 extern Lisp_Object Qhi_command;
117 #if USE_MAC_TSM
118 static TSMDocumentID tsm_document_id;
119 extern Lisp_Object Qtext_input;
120 extern Lisp_Object Qupdate_active_input_area, Qunicode_for_key_event;
121 extern Lisp_Object Vmac_ts_active_input_overlay, Vmac_ts_active_input_buf;
122 extern Lisp_Object Qbefore_string;
123 #endif
125 static int mac_event_to_emacs_modifiers P_ ((EventRef));
126 static OSStatus install_menu_target_item_handler P_ ((void));
127 #ifdef MAC_OSX
128 static OSStatus install_service_handler P_ ((void));
129 #endif
131 extern OSStatus mac_store_event_ref_as_apple_event P_ ((AEEventClass, AEEventID,
132 Lisp_Object,
133 Lisp_Object,
134 EventRef, UInt32,
135 const EventParamName *,
136 const EventParamType *));
137 extern int fast_find_position P_ ((struct window *, int, int *, int *,
138 int *, int *, Lisp_Object));
139 extern struct glyph *x_y_to_hpos_vpos P_ ((struct window *, int, int,
140 int *, int *, int *, int *, int *));
141 extern void mac_ax_selected_text_range P_ ((struct frame *, CFRange *));
142 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
143 extern unsigned int mac_ax_number_of_characters P_ ((struct frame *));
144 #endif
146 #if USE_MAC_TSM
147 extern OSStatus mac_restore_keyboard_input_source P_ ((void));
148 extern void mac_save_keyboard_input_source P_ ((void));
150 static OSStatus
151 mac_tsm_resume ()
153 OSStatus err;
155 err = ActivateTSMDocument (tsm_document_id);
156 if (err == noErr)
157 err = mac_restore_keyboard_input_source ();
159 return err;
162 static OSStatus
163 mac_tsm_suspend ()
165 OSStatus err;
167 mac_save_keyboard_input_source ();
168 err = DeactivateTSMDocument (tsm_document_id);
170 return err;
173 static void
174 init_tsm ()
176 #ifdef MAC_OSX
177 static InterfaceTypeList types = {kUnicodeDocument};
178 #else
179 static InterfaceTypeList types = {kTextService};
180 #endif
182 NewTSMDocument (sizeof (types) / sizeof (types[0]), types,
183 &tsm_document_id, 0);
185 #endif /* USE_MAC_TSM */
187 static pascal OSStatus
188 mac_handle_keyboard_event (next_handler, event, data)
189 EventHandlerCallRef next_handler;
190 EventRef event;
191 void *data;
193 OSStatus err, result = eventNotHandledErr;
194 UInt32 event_kind, key_code, modifiers;
195 unsigned char char_code;
197 event_kind = GetEventKind (event);
198 switch (event_kind)
200 case kEventRawKeyDown:
201 case kEventRawKeyRepeat:
202 case kEventRawKeyUp:
203 /* When using Carbon Events, we need to pass raw keyboard events
204 to the TSM ourselves. If TSM handles it, it will pass back
205 noErr, otherwise it will pass back "eventNotHandledErr" and
206 we can process it normally. */
207 result = CallNextEventHandler (next_handler, event);
208 if (result != eventNotHandledErr)
209 break;
211 if (read_socket_inev == NULL)
212 break;
214 #if USE_MAC_TSM
215 if (read_socket_inev->kind != NO_EVENT)
217 result = noErr;
218 break;
220 #endif
222 if (event_kind == kEventRawKeyUp)
223 break;
225 err = GetEventParameter (event, kEventParamKeyMacCharCodes,
226 typeChar, NULL,
227 sizeof (char), NULL, &char_code);
228 if (err != noErr)
229 break;
231 err = GetEventParameter (event, kEventParamKeyCode,
232 typeUInt32, NULL,
233 sizeof (UInt32), NULL, &key_code);
234 if (err != noErr)
235 break;
237 err = GetEventParameter (event, kEventParamKeyModifiers,
238 typeUInt32, NULL,
239 sizeof (UInt32), NULL, &modifiers);
240 if (err != noErr)
241 break;
243 do_keystroke ((event_kind == kEventRawKeyDown ? keyDown : autoKey),
244 char_code, key_code, modifiers,
245 ((unsigned long)
246 (GetEventTime (event) / kEventDurationMillisecond)),
247 read_socket_inev);
248 result = noErr;
249 break;
251 default:
252 abort ();
255 return result;
258 static pascal OSStatus
259 mac_handle_command_event (next_handler, event, data)
260 EventHandlerCallRef next_handler;
261 EventRef event;
262 void *data;
264 OSStatus err, result = eventNotHandledErr;
265 HICommand command;
266 static const EventParamName names[] =
267 {kEventParamDirectObject, kEventParamKeyModifiers};
268 static const EventParamType types[] =
269 {typeHICommand, typeUInt32};
270 int num_params = sizeof (names) / sizeof (names[0]);
272 err = GetEventParameter (event, kEventParamDirectObject, typeHICommand,
273 NULL, sizeof (HICommand), NULL, &command);
274 if (err != noErr)
275 return eventNotHandledErr;
277 switch (GetEventKind (event))
279 case kEventCommandProcess:
280 result = CallNextEventHandler (next_handler, event);
281 if (result != eventNotHandledErr)
282 break;
284 err = GetEventParameter (event, kEventParamDirectObject,
285 typeHICommand, NULL,
286 sizeof (HICommand), NULL, &command);
288 if (err != noErr || command.commandID == 0)
289 break;
291 /* A HI command event is mapped to an Apple event whose event
292 class symbol is `hi-command' and event ID is its command
293 ID. */
294 err = mac_store_event_ref_as_apple_event (0, command.commandID,
295 Qhi_command, Qnil,
296 event, num_params,
297 names, types);
298 if (err == noErr)
299 result = noErr;
300 break;
302 default:
303 abort ();
306 return result;
309 static pascal OSStatus
310 mac_handle_mouse_event (next_handler, event, data)
311 EventHandlerCallRef next_handler;
312 EventRef event;
313 void *data;
315 OSStatus err, result = eventNotHandledErr;
317 switch (GetEventKind (event))
319 case kEventMouseWheelMoved:
321 WindowRef wp;
322 struct frame *f;
323 EventMouseWheelAxis axis;
324 SInt32 delta;
325 Point point;
327 result = CallNextEventHandler (next_handler, event);
328 if (result != eventNotHandledErr || read_socket_inev == NULL)
329 break;
331 f = mac_focus_frame (&one_mac_display_info);
333 err = GetEventParameter (event, kEventParamWindowRef, typeWindowRef,
334 NULL, sizeof (WindowRef), NULL, &wp);
335 if (err != noErr
336 || wp != FRAME_MAC_WINDOW (f))
337 break;
339 err = GetEventParameter (event, kEventParamMouseWheelAxis,
340 typeMouseWheelAxis, NULL,
341 sizeof (EventMouseWheelAxis), NULL, &axis);
342 if (err != noErr || axis != kEventMouseWheelAxisY)
343 break;
345 err = GetEventParameter (event, kEventParamMouseLocation,
346 typeQDPoint, NULL, sizeof (Point),
347 NULL, &point);
348 if (err != noErr)
349 break;
351 point.h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
352 point.v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
353 if (point.h < 0 || point.v < 0
354 || EQ (window_from_coordinates (f, point.h, point.v, 0, 0, 0, 1),
355 f->tool_bar_window))
356 break;
358 err = GetEventParameter (event, kEventParamMouseWheelDelta,
359 typeSInt32, NULL, sizeof (SInt32),
360 NULL, &delta);
361 if (err != noErr)
362 break;
364 read_socket_inev->kind = WHEEL_EVENT;
365 read_socket_inev->code = 0;
366 read_socket_inev->modifiers =
367 (mac_event_to_emacs_modifiers (event)
368 | ((delta < 0) ? down_modifier : up_modifier));
369 XSETINT (read_socket_inev->x, point.h);
370 XSETINT (read_socket_inev->y, point.v);
371 XSETFRAME (read_socket_inev->frame_or_window, f);
373 result = noErr;
375 break;
377 default:
378 abort ();
381 return result;
384 #if USE_MAC_TSM
385 extern void mac_get_selected_range P_ ((struct window *, CFRange *));
386 extern int mac_store_buffer_text_to_unicode_chars P_ ((struct buffer *,
387 int, int, UniChar *));
389 static pascal OSStatus
390 mac_handle_text_input_event (next_handler, event, data)
391 EventHandlerCallRef next_handler;
392 EventRef event;
393 void *data;
395 OSStatus err, result;
396 Lisp_Object id_key = Qnil;
397 int num_params;
398 const EventParamName *names;
399 const EventParamType *types;
400 static UInt32 seqno_uaia = 0;
401 static const EventParamName names_uaia[] =
402 {kEventParamTextInputSendComponentInstance,
403 kEventParamTextInputSendRefCon,
404 kEventParamTextInputSendSLRec,
405 kEventParamTextInputSendFixLen,
406 kEventParamTextInputSendText,
407 kEventParamTextInputSendUpdateRng,
408 kEventParamTextInputSendHiliteRng,
409 kEventParamTextInputSendClauseRng,
410 kEventParamTextInputSendPinRng,
411 kEventParamTextInputSendTextServiceEncoding,
412 kEventParamTextInputSendTextServiceMacEncoding,
413 EVENT_PARAM_TEXT_INPUT_SEQUENCE_NUMBER};
414 static const EventParamType types_uaia[] =
415 {typeComponentInstance,
416 typeLongInteger,
417 typeIntlWritingCode,
418 typeLongInteger,
419 #ifdef MAC_OSX
420 typeUnicodeText,
421 #else
422 typeChar,
423 #endif
424 typeTextRangeArray,
425 typeTextRangeArray,
426 typeOffsetArray,
427 typeTextRange,
428 typeUInt32,
429 typeUInt32,
430 typeUInt32};
431 static const EventParamName names_ufke[] =
432 {kEventParamTextInputSendComponentInstance,
433 kEventParamTextInputSendRefCon,
434 kEventParamTextInputSendSLRec,
435 kEventParamTextInputSendText};
436 static const EventParamType types_ufke[] =
437 {typeComponentInstance,
438 typeLongInteger,
439 typeIntlWritingCode,
440 typeUnicodeText};
442 result = CallNextEventHandler (next_handler, event);
443 if (result != eventNotHandledErr)
444 return result;
446 switch (GetEventKind (event))
448 case kEventTextInputUpdateActiveInputArea:
449 id_key = Qupdate_active_input_area;
450 num_params = sizeof (names_uaia) / sizeof (names_uaia[0]);
451 names = names_uaia;
452 types = types_uaia;
453 SetEventParameter (event, EVENT_PARAM_TEXT_INPUT_SEQUENCE_NUMBER,
454 typeUInt32, sizeof (UInt32), &seqno_uaia);
455 seqno_uaia++;
456 result = noErr;
457 break;
459 case kEventTextInputUnicodeForKeyEvent:
461 EventRef kbd_event;
462 UInt32 actual_size, modifiers, key_code;
464 err = GetEventParameter (event, kEventParamTextInputSendKeyboardEvent,
465 typeEventRef, NULL, sizeof (EventRef), NULL,
466 &kbd_event);
467 if (err == noErr)
468 err = GetEventParameter (kbd_event, kEventParamKeyModifiers,
469 typeUInt32, NULL,
470 sizeof (UInt32), NULL, &modifiers);
471 if (err == noErr)
472 err = GetEventParameter (kbd_event, kEventParamKeyCode,
473 typeUInt32, NULL, sizeof (UInt32),
474 NULL, &key_code);
475 if (err == noErr && mac_mapped_modifiers (modifiers, key_code))
476 /* There're mapped modifier keys. Process it in
477 do_keystroke. */
478 break;
479 if (err == noErr)
480 err = GetEventParameter (kbd_event, kEventParamKeyUnicodes,
481 typeUnicodeText, NULL, 0, &actual_size,
482 NULL);
483 if (err == noErr && actual_size == sizeof (UniChar))
485 UniChar code;
487 err = GetEventParameter (kbd_event, kEventParamKeyUnicodes,
488 typeUnicodeText, NULL,
489 sizeof (UniChar), NULL, &code);
490 if (err == noErr && code < 0x80)
492 /* ASCII character. Process it in do_keystroke. */
493 if (read_socket_inev && code >= 0x20 && code <= 0x7e
494 && !(key_code <= 0x7f
495 && keycode_to_xkeysym_table [key_code]))
497 struct frame *f = mac_focus_frame (&one_mac_display_info);
499 read_socket_inev->kind = ASCII_KEYSTROKE_EVENT;
500 read_socket_inev->code = code;
501 read_socket_inev->modifiers =
502 mac_to_emacs_modifiers (modifiers, 0);
503 read_socket_inev->modifiers |=
504 (extra_keyboard_modifiers
505 & (meta_modifier | alt_modifier
506 | hyper_modifier | super_modifier));
507 XSETFRAME (read_socket_inev->frame_or_window, f);
509 break;
512 if (err == noErr)
514 /* Non-ASCII keystrokes without mapped modifiers are
515 processed at the Lisp level. */
516 id_key = Qunicode_for_key_event;
517 num_params = sizeof (names_ufke) / sizeof (names_ufke[0]);
518 names = names_ufke;
519 types = types_ufke;
520 result = noErr;
523 break;
525 case kEventTextInputOffsetToPos:
527 long byte_offset;
528 struct frame *f;
529 struct window *w;
530 Point p;
532 err = GetEventParameter (event, kEventParamTextInputSendTextOffset,
533 typeLongInteger, NULL, sizeof (long), NULL,
534 &byte_offset);
535 if (err != noErr)
536 break;
538 if (STRINGP (Vmac_ts_active_input_buf)
539 && SBYTES (Vmac_ts_active_input_buf) != 0)
541 if (!OVERLAYP (Vmac_ts_active_input_overlay))
542 break;
544 /* Strictly speaking, this is not always correct because
545 previous events may change some states about display. */
546 if (!NILP (Foverlay_get (Vmac_ts_active_input_overlay, Qbefore_string)))
548 /* Active input area is displayed around the current point. */
549 f = SELECTED_FRAME ();
550 w = XWINDOW (f->selected_window);
552 else if (WINDOWP (echo_area_window))
554 /* Active input area is displayed in the echo area. */
555 w = XWINDOW (echo_area_window);
556 f = WINDOW_XFRAME (w);
558 else
559 break;
561 p.h = (WINDOW_TO_FRAME_PIXEL_X (w, w->cursor.x)
562 + WINDOW_LEFT_FRINGE_WIDTH (w)
563 + f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f));
564 p.v = (WINDOW_TO_FRAME_PIXEL_Y (w, w->cursor.y)
565 + FONT_BASE (FRAME_FONT (f))
566 + f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f));
568 else
570 #ifndef MAC_OSX
571 break;
572 #else /* MAC_OSX */
573 CFRange sel_range;
574 int charpos;
575 int hpos, vpos, x, y;
576 struct glyph_row *row;
577 struct glyph *glyph;
578 struct face *face;
580 f = mac_focus_frame (&one_mac_display_info);
581 w = XWINDOW (f->selected_window);
582 mac_get_selected_range (w, &sel_range);
583 charpos = (BUF_BEGV (XBUFFER (w->buffer)) + sel_range.location
584 + byte_offset / (long) sizeof (UniChar));
586 if (!fast_find_position (w, charpos, &hpos, &vpos, &x, &y, Qnil))
588 result = errOffsetInvalid;
589 break;
592 row = MATRIX_ROW (w->current_matrix, vpos);
593 glyph = row->glyphs[TEXT_AREA] + hpos;
594 if (glyph->type != CHAR_GLYPH || glyph->glyph_not_available_p)
595 break;
597 p.h = (WINDOW_TEXT_TO_FRAME_PIXEL_X (w, x)
598 + f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f));
599 p.v = (WINDOW_TO_FRAME_PIXEL_Y (w, y)
600 + row->visible_height
601 + f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f));
603 face = FACE_FROM_ID (f, glyph->face_id);
604 if (face && face->font)
606 XFontStruct *font = face->font;
607 Fixed point_size = Long2Fix (font->mac_fontsize);
608 short height = row->visible_height;
609 short ascent = row->ascent;
611 SetEventParameter (event,
612 kEventParamTextInputReplyPointSize,
613 typeFixed, sizeof (Fixed), &point_size);
614 SetEventParameter (event,
615 kEventParamTextInputReplyLineHeight,
616 typeShortInteger, sizeof (short), &height);
617 SetEventParameter (event,
618 kEventParamTextInputReplyLineAscent,
619 typeShortInteger, sizeof (short), &ascent);
620 if (font->mac_fontnum != -1)
622 OSStatus err1;
623 FMFont fm_font;
624 FMFontStyle style;
626 err1 = FMGetFontFromFontFamilyInstance (font->mac_fontnum,
627 font->mac_fontface,
628 &fm_font, &style);
629 if (err1 == noErr)
630 SetEventParameter (event, kEventParamTextInputReplyFMFont,
631 typeUInt32, sizeof (UInt32), &fm_font);
632 else
634 long qd_font = font->mac_fontnum;
636 SetEventParameter (event, kEventParamTextInputReplyFont,
637 typeLongInteger, sizeof (long),
638 &qd_font);
641 else if (font->mac_style)
643 OSStatus err1;
644 ATSUFontID font_id;
646 err1 = ATSUGetAttribute (font->mac_style, kATSUFontTag,
647 sizeof (ATSUFontID), &font_id,
648 NULL);
649 if (err1 == noErr)
650 SetEventParameter (event, kEventParamTextInputReplyFMFont,
651 typeUInt32, sizeof (UInt32), &font_id);
653 else
654 abort ();
656 #endif /* MAC_OSX */
659 err = SetEventParameter (event, kEventParamTextInputReplyPoint,
660 typeQDPoint, sizeof (Point), &p);
661 if (err == noErr)
662 result = noErr;
664 break;
666 #ifdef MAC_OSX
667 case kEventTextInputPosToOffset:
669 Point point;
670 Boolean leading_edge_p = true;
671 struct frame *f;
672 int x, y;
673 Lisp_Object window;
674 enum window_part part;
675 long region_class = kTSMOutsideOfBody, byte_offset = 0;
677 err = GetEventParameter (event, kEventParamTextInputSendCurrentPoint,
678 typeQDPoint, NULL, sizeof (Point), NULL,
679 &point);
680 if (err != noErr)
681 break;
683 GetEventParameter (event, kEventParamTextInputReplyLeadingEdge,
684 typeBoolean, NULL, sizeof (Boolean), NULL,
685 &leading_edge_p);
687 f = mac_focus_frame (&one_mac_display_info);
688 x = point.h - (f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f));
689 y = point.v - (f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f));
690 window = window_from_coordinates (f, x, y, &part, 0, 0, 1);
691 if (WINDOWP (window) && EQ (window, f->selected_window))
693 struct window *w;
694 struct buffer *b;
696 /* Convert to window-relative pixel coordinates. */
697 w = XWINDOW (window);
698 frame_to_window_pixel_xy (w, &x, &y);
700 /* Are we in a window whose display is up to date?
701 And verify the buffer's text has not changed. */
702 b = XBUFFER (w->buffer);
703 if (part == ON_TEXT
704 && EQ (w->window_end_valid, w->buffer)
705 && XINT (w->last_modified) == BUF_MODIFF (b)
706 && XINT (w->last_overlay_modified) == BUF_OVERLAY_MODIFF (b))
708 int hpos, vpos, area;
709 struct glyph *glyph;
711 /* Find the glyph under X/Y. */
712 glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, 0, 0, &area);
714 if (glyph != NULL && area == TEXT_AREA)
716 byte_offset = ((glyph->charpos - BUF_BEGV (b))
717 * sizeof (UniChar));
718 region_class = kTSMInsideOfBody;
723 err = SetEventParameter (event, kEventParamTextInputReplyRegionClass,
724 typeLongInteger, sizeof (long),
725 &region_class);
726 if (err == noErr)
727 err = SetEventParameter (event, kEventParamTextInputReplyTextOffset,
728 typeLongInteger, sizeof (long),
729 &byte_offset);
730 if (err == noErr)
731 result = noErr;
733 break;
735 case kEventTextInputGetSelectedText:
737 struct frame *f = mac_focus_frame (&one_mac_display_info);
738 struct window *w = XWINDOW (f->selected_window);
739 struct buffer *b = XBUFFER (w->buffer);
740 CFRange sel_range;
741 int start, end;
742 UniChar *characters, c;
744 if (poll_suppress_count == 0 && !NILP (Vinhibit_quit))
745 /* Don't try to get buffer contents as the gap might be
746 being altered. */
747 break;
749 mac_get_selected_range (w, &sel_range);
750 if (sel_range.length == 0)
752 Boolean leading_edge_p;
754 err = GetEventParameter (event,
755 kEventParamTextInputReplyLeadingEdge,
756 typeBoolean, NULL, sizeof (Boolean), NULL,
757 &leading_edge_p);
758 if (err != noErr)
759 break;
761 start = BUF_BEGV (b) + sel_range.location;
762 if (!leading_edge_p)
763 start--;
764 end = start + 1;
765 characters = &c;
767 if (start < BUF_BEGV (b) || end > BUF_ZV (b))
768 break;
770 else
772 start = BUF_BEGV (b) + sel_range.location;
773 end = start + sel_range.length;
774 characters = xmalloc (sel_range.length * sizeof (UniChar));
777 if (mac_store_buffer_text_to_unicode_chars (b, start, end, characters))
778 err = SetEventParameter (event, kEventParamTextInputReplyText,
779 typeUnicodeText,
780 sel_range.length * sizeof (UniChar),
781 characters);
782 if (characters != &c)
783 xfree (characters);
785 if (err == noErr)
786 result = noErr;
788 break;
789 #endif /* MAC_OSX */
791 default:
792 abort ();
795 if (!NILP (id_key))
796 err = mac_store_event_ref_as_apple_event (0, 0, Qtext_input, id_key,
797 event, num_params,
798 names, types);
799 return result;
802 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
803 static pascal OSStatus
804 mac_handle_document_access_event (next_handler, event, data)
805 EventHandlerCallRef next_handler;
806 EventRef event;
807 void *data;
809 OSStatus err, result;
810 struct frame *f = mac_focus_frame (&one_mac_display_info);
812 result = CallNextEventHandler (next_handler, event);
813 if (result != eventNotHandledErr)
814 return result;
816 switch (GetEventKind (event))
818 case kEventTSMDocumentAccessGetLength:
820 CFIndex count = mac_ax_number_of_characters (f);
822 err = SetEventParameter (event, kEventParamTSMDocAccessCharacterCount,
823 typeCFIndex, sizeof (CFIndex), &count);
824 if (err == noErr)
825 result = noErr;
827 break;
829 case kEventTSMDocumentAccessGetSelectedRange:
831 CFRange sel_range;
833 mac_ax_selected_text_range (f, &sel_range);
834 err = SetEventParameter (event,
835 kEventParamTSMDocAccessReplyCharacterRange,
836 typeCFRange, sizeof (CFRange), &sel_range);
837 if (err == noErr)
838 result = noErr;
840 break;
842 case kEventTSMDocumentAccessGetCharacters:
844 struct buffer *b = XBUFFER (XWINDOW (f->selected_window)->buffer);
845 CFRange range;
846 Ptr characters;
847 int start, end;
849 if (poll_suppress_count == 0 && !NILP (Vinhibit_quit))
850 /* Don't try to get buffer contents as the gap might be
851 being altered. */
852 break;
854 err = GetEventParameter (event,
855 kEventParamTSMDocAccessSendCharacterRange,
856 typeCFRange, NULL, sizeof (CFRange), NULL,
857 &range);
858 if (err == noErr)
859 err = GetEventParameter (event,
860 kEventParamTSMDocAccessSendCharactersPtr,
861 typePtr, NULL, sizeof (Ptr), NULL,
862 &characters);
863 if (err != noErr)
864 break;
866 start = BUF_BEGV (b) + range.location;
867 end = start + range.length;
868 if (mac_store_buffer_text_to_unicode_chars (b, start, end,
869 (UniChar *) characters))
870 result = noErr;
872 break;
874 default:
875 abort ();
878 return result;
880 #endif
881 #endif
883 OSStatus
884 install_application_handler ()
886 OSStatus err = noErr;
888 if (err == noErr)
890 static const EventTypeSpec specs[] =
891 {{kEventClassKeyboard, kEventRawKeyDown},
892 {kEventClassKeyboard, kEventRawKeyRepeat},
893 {kEventClassKeyboard, kEventRawKeyUp}};
895 err = InstallApplicationEventHandler (NewEventHandlerUPP
896 (mac_handle_keyboard_event),
897 GetEventTypeCount (specs),
898 specs, NULL, NULL);
901 if (err == noErr)
903 static const EventTypeSpec specs[] =
904 {{kEventClassCommand, kEventCommandProcess}};
906 err = InstallApplicationEventHandler (NewEventHandlerUPP
907 (mac_handle_command_event),
908 GetEventTypeCount (specs),
909 specs, NULL, NULL);
912 if (err == noErr)
914 static const EventTypeSpec specs[] =
915 {{kEventClassMouse, kEventMouseWheelMoved}};
917 err = InstallApplicationEventHandler (NewEventHandlerUPP
918 (mac_handle_mouse_event),
919 GetEventTypeCount (specs),
920 specs, NULL, NULL);
923 #if USE_MAC_TSM
924 if (err == noErr)
926 static const EventTypeSpec specs[] =
927 {{kEventClassTextInput, kEventTextInputUpdateActiveInputArea},
928 {kEventClassTextInput, kEventTextInputUnicodeForKeyEvent},
929 {kEventClassTextInput, kEventTextInputOffsetToPos},
930 #ifdef MAC_OSX
931 {kEventClassTextInput, kEventTextInputPosToOffset},
932 {kEventClassTextInput, kEventTextInputGetSelectedText}
933 #endif
936 err = InstallApplicationEventHandler (NewEventHandlerUPP
937 (mac_handle_text_input_event),
938 GetEventTypeCount (specs),
939 specs, NULL, NULL);
942 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
943 if (err == noErr)
945 static const EventTypeSpec specs[] =
946 {{kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength},
947 {kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange},
948 {kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters}};
950 err = InstallApplicationEventHandler (mac_handle_document_access_event,
951 GetEventTypeCount (specs),
952 specs, NULL, NULL);
954 #endif
955 #endif
957 if (err == noErr)
958 err = install_menu_target_item_handler ();
960 #ifdef MAC_OSX
961 if (err == noErr)
962 err = install_service_handler ();
963 #endif
965 return err;
967 #endif /* TARGET_API_MAC_CARBON */
970 /************************************************************************
971 Windows
972 ************************************************************************/
974 #define DEFAULT_NUM_COLS 80
976 #define MIN_DOC_SIZE 64
977 #define MAX_DOC_SIZE 32767
979 /* Drag and Drop */
980 static OSErr install_drag_handler P_ ((WindowRef));
981 static void remove_drag_handler P_ ((WindowRef));
983 #if USE_CG_DRAWING
984 static void mac_prepare_for_quickdraw P_ ((struct frame *));
985 #endif
987 extern void mac_handle_visibility_change P_ ((struct frame *));
988 extern void mac_handle_origin_change P_ ((struct frame *));
989 extern void mac_handle_size_change P_ ((struct frame *, int, int));
991 #if TARGET_API_MAC_CARBON
992 #ifdef MAC_OSX
993 extern Lisp_Object Qwindow;
994 extern Lisp_Object Qtoolbar_switch_mode;
995 #endif
996 #endif
998 static void
999 do_window_update (WindowRef win)
1001 struct frame *f = mac_window_to_frame (win);
1003 BeginUpdate (win);
1005 /* The tooltip has been drawn already. Avoid the SET_FRAME_GARBAGED
1006 below. */
1007 if (win != tip_window)
1009 if (f->async_visible == 0)
1011 /* Update events may occur when a frame gets iconified. */
1012 #if 0
1013 f->async_visible = 1;
1014 f->async_iconified = 0;
1015 SET_FRAME_GARBAGED (f);
1016 #endif
1018 else
1020 Rect r;
1021 #if TARGET_API_MAC_CARBON
1022 RgnHandle region = NewRgn ();
1024 GetPortVisibleRegion (GetWindowPort (win), region);
1025 GetRegionBounds (region, &r);
1026 expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top);
1027 #if USE_CG_DRAWING
1028 mac_prepare_for_quickdraw (f);
1029 #endif
1030 UpdateControls (win, region);
1031 DisposeRgn (region);
1032 #else
1033 r = (*win->visRgn)->rgnBBox;
1034 expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top);
1035 UpdateControls (win, win->visRgn);
1036 #endif
1040 EndUpdate (win);
1043 static int
1044 is_emacs_window (WindowRef win)
1046 Lisp_Object tail, frame;
1048 if (!win)
1049 return 0;
1051 FOR_EACH_FRAME (tail, frame)
1052 if (FRAME_MAC_P (XFRAME (frame)))
1053 if (FRAME_MAC_WINDOW (XFRAME (frame)) == win)
1054 return 1;
1056 return 0;
1059 /* Handle drags in size box. Based on code contributed by Ben
1060 Mesander and IM - Window Manager A. */
1062 static void
1063 do_grow_window (w, e)
1064 WindowRef w;
1065 const EventRecord *e;
1067 Rect limit_rect;
1068 int rows, columns, width, height;
1069 struct frame *f = mac_window_to_frame (w);
1070 XSizeHints *size_hints = FRAME_SIZE_HINTS (f);
1071 int min_width = MIN_DOC_SIZE, min_height = MIN_DOC_SIZE;
1072 #if TARGET_API_MAC_CARBON
1073 Rect new_rect;
1074 #else
1075 long grow_size;
1076 #endif
1078 if (size_hints->flags & PMinSize)
1080 min_width = size_hints->min_width;
1081 min_height = size_hints->min_height;
1083 SetRect (&limit_rect, min_width, min_height, MAX_DOC_SIZE, MAX_DOC_SIZE);
1085 #if TARGET_API_MAC_CARBON
1086 if (!ResizeWindow (w, e->where, &limit_rect, &new_rect))
1087 return;
1088 height = new_rect.bottom - new_rect.top;
1089 width = new_rect.right - new_rect.left;
1090 #else
1091 grow_size = GrowWindow (w, e->where, &limit_rect);
1092 /* see if it really changed size */
1093 if (grow_size == 0)
1094 return;
1095 height = HiWord (grow_size);
1096 width = LoWord (grow_size);
1097 #endif
1099 if (width != FRAME_PIXEL_WIDTH (f)
1100 || height != FRAME_PIXEL_HEIGHT (f))
1102 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height);
1103 columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width);
1105 x_set_window_size (f, 0, columns, rows);
1109 #if TARGET_API_MAC_CARBON
1110 static Point
1111 mac_get_ideal_size (f)
1112 struct frame *f;
1114 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
1115 WindowRef w = FRAME_MAC_WINDOW (f);
1116 Point ideal_size;
1117 Rect standard_rect;
1118 int height, width, columns, rows;
1120 ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS);
1121 ideal_size.v = dpyinfo->height;
1122 IsWindowInStandardState (w, &ideal_size, &standard_rect);
1123 /* Adjust the standard size according to character boundaries. */
1124 width = standard_rect.right - standard_rect.left;
1125 height = standard_rect.bottom - standard_rect.top;
1126 columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width);
1127 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height);
1128 ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, columns);
1129 ideal_size.v = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows);
1131 return ideal_size;
1134 static pascal OSStatus
1135 mac_handle_window_event (next_handler, event, data)
1136 EventHandlerCallRef next_handler;
1137 EventRef event;
1138 void *data;
1140 WindowRef wp;
1141 OSStatus err, result = eventNotHandledErr;
1142 struct frame *f;
1143 UInt32 attributes;
1144 XSizeHints *size_hints;
1146 err = GetEventParameter (event, kEventParamDirectObject, typeWindowRef,
1147 NULL, sizeof (WindowRef), NULL, &wp);
1148 if (err != noErr)
1149 return eventNotHandledErr;
1151 f = mac_window_to_frame (wp);
1152 switch (GetEventKind (event))
1154 /* -- window refresh events -- */
1156 case kEventWindowUpdate:
1157 result = CallNextEventHandler (next_handler, event);
1158 if (result != eventNotHandledErr)
1159 break;
1161 do_window_update (wp);
1162 result = noErr;
1163 break;
1165 /* -- window state change events -- */
1167 case kEventWindowShowing:
1168 size_hints = FRAME_SIZE_HINTS (f);
1169 if (!(size_hints->flags & (USPosition | PPosition)))
1171 struct frame *sf = SELECTED_FRAME ();
1173 if (!(FRAME_MAC_P (sf) && sf->async_visible))
1174 RepositionWindow (wp, NULL, kWindowCenterOnMainScreen);
1175 else
1177 RepositionWindow (wp, FRAME_MAC_WINDOW (sf),
1178 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
1179 kWindowCascadeStartAtParentWindowScreen
1180 #else
1181 kWindowCascadeOnParentWindowScreen
1182 #endif
1184 #if USE_MAC_TOOLBAR
1185 /* This is a workaround. RepositionWindow fails to put
1186 a window at the cascading position when its parent
1187 window has a Carbon HIToolbar. */
1188 if ((f->left_pos == sf->left_pos
1189 && f->top_pos == sf->top_pos)
1190 || (f->left_pos == sf->left_pos + 10 * 2
1191 && f->top_pos == sf->top_pos + 32 * 2))
1192 MoveWindowStructure (wp, sf->left_pos + 10, sf->top_pos + 32);
1193 #endif
1195 result = noErr;
1197 break;
1199 case kEventWindowHiding:
1200 /* Before unmapping the window, update the WM_SIZE_HINTS
1201 property to claim that the current position of the window is
1202 user-specified, rather than program-specified, so that when
1203 the window is mapped again, it will be placed at the same
1204 location, without forcing the user to position it by hand
1205 again (they have already done that once for this window.) */
1206 x_wm_set_size_hint (f, (long) 0, 1);
1207 result = noErr;
1208 break;
1210 case kEventWindowShown:
1211 case kEventWindowHidden:
1212 case kEventWindowCollapsed:
1213 case kEventWindowExpanded:
1214 mac_handle_visibility_change (f);
1215 result = noErr;
1216 break;
1218 case kEventWindowBoundsChanging:
1219 result = CallNextEventHandler (next_handler, event);
1220 if (result != eventNotHandledErr)
1221 break;
1223 err = GetEventParameter (event, kEventParamAttributes, typeUInt32,
1224 NULL, sizeof (UInt32), NULL, &attributes);
1225 if (err != noErr)
1226 break;
1228 size_hints = FRAME_SIZE_HINTS (f);
1229 if ((attributes & kWindowBoundsChangeUserResize)
1230 && ((size_hints->flags & (PResizeInc | PBaseSize | PMinSize))
1231 == (PResizeInc | PBaseSize | PMinSize)))
1233 Rect bounds;
1234 int width, height;
1236 err = GetEventParameter (event, kEventParamCurrentBounds,
1237 typeQDRectangle, NULL, sizeof (Rect),
1238 NULL, &bounds);
1239 if (err != noErr)
1240 break;
1242 width = bounds.right - bounds.left;
1243 height = bounds.bottom - bounds.top;
1245 if (width < size_hints->min_width)
1246 width = size_hints->min_width;
1247 else
1248 width = size_hints->base_width
1249 + (int) ((width - size_hints->base_width)
1250 / (float) size_hints->width_inc + .5)
1251 * size_hints->width_inc;
1253 if (height < size_hints->min_height)
1254 height = size_hints->min_height;
1255 else
1256 height = size_hints->base_height
1257 + (int) ((height - size_hints->base_height)
1258 / (float) size_hints->height_inc + .5)
1259 * size_hints->height_inc;
1261 bounds.right = bounds.left + width;
1262 bounds.bottom = bounds.top + height;
1263 SetEventParameter (event, kEventParamCurrentBounds,
1264 typeQDRectangle, sizeof (Rect), &bounds);
1265 result = noErr;
1267 break;
1269 case kEventWindowBoundsChanged:
1270 err = GetEventParameter (event, kEventParamAttributes, typeUInt32,
1271 NULL, sizeof (UInt32), NULL, &attributes);
1272 if (err != noErr)
1273 break;
1275 if (attributes & kWindowBoundsChangeSizeChanged)
1277 Rect bounds;
1279 err = GetEventParameter (event, kEventParamCurrentBounds,
1280 typeQDRectangle, NULL, sizeof (Rect),
1281 NULL, &bounds);
1282 if (err == noErr)
1284 int width, height;
1286 width = bounds.right - bounds.left;
1287 height = bounds.bottom - bounds.top;
1288 mac_handle_size_change (f, width, height);
1289 mac_wakeup_from_rne ();
1293 if (attributes & kWindowBoundsChangeOriginChanged)
1294 mac_handle_origin_change (f);
1296 result = noErr;
1297 break;
1299 /* -- window action events -- */
1301 case kEventWindowClose:
1303 struct input_event buf;
1305 EVENT_INIT (buf);
1306 buf.kind = DELETE_WINDOW_EVENT;
1307 XSETFRAME (buf.frame_or_window, f);
1308 buf.arg = Qnil;
1309 kbd_buffer_store_event (&buf);
1311 result = noErr;
1312 break;
1314 case kEventWindowGetIdealSize:
1315 result = CallNextEventHandler (next_handler, event);
1316 if (result != eventNotHandledErr)
1317 break;
1320 Point ideal_size = mac_get_ideal_size (f);
1322 err = SetEventParameter (event, kEventParamDimensions,
1323 typeQDPoint, sizeof (Point), &ideal_size);
1324 if (err == noErr)
1325 result = noErr;
1327 break;
1329 #ifdef MAC_OSX
1330 case kEventWindowToolbarSwitchMode:
1332 static const EventParamName names[] = {kEventParamDirectObject,
1333 kEventParamWindowMouseLocation,
1334 kEventParamKeyModifiers,
1335 kEventParamMouseButton,
1336 kEventParamClickCount,
1337 kEventParamMouseChord};
1338 static const EventParamType types[] = {typeWindowRef,
1339 typeQDPoint,
1340 typeUInt32,
1341 typeMouseButton,
1342 typeUInt32,
1343 typeUInt32};
1344 int num_params = sizeof (names) / sizeof (names[0]);
1346 err = mac_store_event_ref_as_apple_event (0, 0,
1347 Qwindow,
1348 Qtoolbar_switch_mode,
1349 event, num_params,
1350 names, types);
1352 if (err == noErr)
1353 result = noErr;
1354 break;
1355 #endif
1357 #if USE_MAC_TSM
1358 /* -- window focus events -- */
1360 case kEventWindowFocusAcquired:
1361 err = mac_tsm_resume ();
1362 if (err == noErr)
1363 result = noErr;
1364 break;
1366 case kEventWindowFocusRelinquish:
1367 err = mac_tsm_suspend ();
1368 if (err == noErr)
1369 result = noErr;
1370 break;
1371 #endif
1373 default:
1374 abort ();
1377 return result;
1379 #endif
1381 /* Handle clicks in zoom box. Calculation of "standard state" based
1382 on code in IM - Window Manager A and code contributed by Ben
1383 Mesander. The standard state of an Emacs window is 80-characters
1384 wide (DEFAULT_NUM_COLS) and as tall as will fit on the screen. */
1386 static void
1387 do_zoom_window (WindowRef w, int zoom_in_or_out)
1389 Rect zoom_rect, port_rect;
1390 int width, height;
1391 struct frame *f = mac_window_to_frame (w);
1392 #if TARGET_API_MAC_CARBON
1393 Point ideal_size = mac_get_ideal_size (f);
1395 GetWindowBounds (w, kWindowContentRgn, &port_rect);
1396 if (IsWindowInStandardState (w, &ideal_size, &zoom_rect)
1397 && port_rect.left == zoom_rect.left
1398 && port_rect.top == zoom_rect.top)
1399 zoom_in_or_out = inZoomIn;
1400 else
1401 zoom_in_or_out = inZoomOut;
1403 #ifdef MAC_OS8
1404 mac_clear_area (f, 0, 0, port_rect.right - port_rect.left,
1405 port_rect.bottom - port_rect.top);
1406 #endif
1407 ZoomWindowIdeal (w, zoom_in_or_out, &ideal_size);
1408 #else /* not TARGET_API_MAC_CARBON */
1409 GrafPtr save_port;
1410 Point top_left;
1411 int w_title_height, rows;
1412 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
1414 GetPort (&save_port);
1416 SetPortWindowPort (w);
1418 /* Clear window to avoid flicker. */
1419 EraseRect (&(w->portRect));
1420 if (zoom_in_or_out == inZoomOut)
1422 SetPt (&top_left, w->portRect.left, w->portRect.top);
1423 LocalToGlobal (&top_left);
1425 /* calculate height of window's title bar */
1426 w_title_height = top_left.v - 1
1427 - (**((WindowPeek) w)->strucRgn).rgnBBox.top + GetMBarHeight ();
1429 /* get maximum height of window into zoom_rect.bottom - zoom_rect.top */
1430 zoom_rect = qd.screenBits.bounds;
1431 zoom_rect.top += w_title_height;
1432 InsetRect (&zoom_rect, 8, 4); /* not too tight */
1434 zoom_rect.right = zoom_rect.left
1435 + FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS);
1437 /* Adjust the standard size according to character boundaries. */
1438 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, zoom_rect.bottom - zoom_rect.top);
1439 zoom_rect.bottom =
1440 zoom_rect.top + FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows);
1442 (**((WStateDataHandle) ((WindowPeek) w)->dataHandle)).stdState
1443 = zoom_rect;
1446 ZoomWindow (w, zoom_in_or_out, f == mac_focus_frame (dpyinfo));
1448 SetPort (save_port);
1449 #endif /* not TARGET_API_MAC_CARBON */
1451 #if !TARGET_API_MAC_CARBON
1452 /* retrieve window size and update application values */
1453 port_rect = w->portRect;
1454 height = port_rect.bottom - port_rect.top;
1455 width = port_rect.right - port_rect.left;
1457 mac_handle_size_change (f, width, height);
1458 mac_handle_origin_change (f);
1459 #endif
1462 static OSStatus
1463 install_window_handler (window)
1464 WindowRef window;
1466 OSStatus err = noErr;
1468 #if TARGET_API_MAC_CARBON
1469 if (err == noErr)
1471 static const EventTypeSpec specs[] =
1473 /* -- window refresh events -- */
1474 {kEventClassWindow, kEventWindowUpdate},
1475 /* -- window state change events -- */
1476 {kEventClassWindow, kEventWindowShowing},
1477 {kEventClassWindow, kEventWindowHiding},
1478 {kEventClassWindow, kEventWindowShown},
1479 {kEventClassWindow, kEventWindowHidden},
1480 {kEventClassWindow, kEventWindowCollapsed},
1481 {kEventClassWindow, kEventWindowExpanded},
1482 {kEventClassWindow, kEventWindowBoundsChanging},
1483 {kEventClassWindow, kEventWindowBoundsChanged},
1484 /* -- window action events -- */
1485 {kEventClassWindow, kEventWindowClose},
1486 {kEventClassWindow, kEventWindowGetIdealSize},
1487 #ifdef MAC_OSX
1488 {kEventClassWindow, kEventWindowToolbarSwitchMode},
1489 #endif
1490 #if USE_MAC_TSM
1491 /* -- window focus events -- */
1492 {kEventClassWindow, kEventWindowFocusAcquired},
1493 {kEventClassWindow, kEventWindowFocusRelinquish},
1494 #endif
1496 static EventHandlerUPP handle_window_eventUPP = NULL;
1498 if (handle_window_eventUPP == NULL)
1499 handle_window_eventUPP = NewEventHandlerUPP (mac_handle_window_event);
1501 err = InstallWindowEventHandler (window, handle_window_eventUPP,
1502 GetEventTypeCount (specs),
1503 specs, NULL, NULL);
1505 #endif
1507 if (err == noErr)
1508 err = install_drag_handler (window);
1510 return err;
1513 static void
1514 remove_window_handler (window)
1515 WindowRef window;
1517 remove_drag_handler (window);
1520 void
1521 mac_get_window_bounds (f, inner, outer)
1522 struct frame *f;
1523 Rect *inner, *outer;
1525 #if TARGET_API_MAC_CARBON
1526 GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowContentRgn, inner);
1527 GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowStructureRgn, outer);
1528 #else /* not TARGET_API_MAC_CARBON */
1529 RgnHandle region = NewRgn ();
1531 GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowContentRgn, region);
1532 *inner = (*region)->rgnBBox;
1533 GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowStructureRgn, region);
1534 *outer = (*region)->rgnBBox;
1535 DisposeRgn (region);
1536 #endif /* not TARGET_API_MAC_CARBON */
1539 Rect *
1540 mac_get_frame_bounds (f, r)
1541 struct frame *f;
1542 Rect *r;
1544 #if TARGET_API_MAC_CARBON
1545 return GetWindowPortBounds (FRAME_MAC_WINDOW (f), r);
1546 #else
1547 *r = FRAME_MAC_WINDOW (f)->portRect;
1549 return r;
1550 #endif
1553 void
1554 mac_get_frame_mouse (f, point)
1555 struct frame *f;
1556 Point *point;
1558 #if TARGET_API_MAC_CARBON
1559 GetGlobalMouse (point);
1560 point->h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1561 point->v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
1562 #else
1563 SetPortWindowPort (FRAME_MAC_WINDOW (f));
1564 GetMouse (point);
1565 #endif
1568 void
1569 mac_convert_frame_point_to_global (f, x, y)
1570 struct frame *f;
1571 int *x, *y;
1573 *x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1574 *y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
1577 #if TARGET_API_MAC_CARBON
1578 void
1579 mac_update_proxy_icon (f)
1580 struct frame *f;
1582 OSStatus err;
1583 Lisp_Object file_name =
1584 XBUFFER (XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer)->filename;
1585 Window w = FRAME_MAC_WINDOW (f);
1586 AliasHandle alias = NULL;
1588 err = GetWindowProxyAlias (w, &alias);
1589 if (err == errWindowDoesNotHaveProxy && !STRINGP (file_name))
1590 return;
1592 if (STRINGP (file_name))
1594 AEDesc desc;
1595 #ifdef MAC_OSX
1596 FSRef fref, fref_proxy;
1597 #else
1598 FSSpec fss, fss_proxy;
1599 #endif
1600 Boolean changed;
1601 Lisp_Object encoded_file_name = ENCODE_FILE (file_name);
1603 #ifdef MAC_OSX
1604 err = AECoercePtr (TYPE_FILE_NAME, SDATA (encoded_file_name),
1605 SBYTES (encoded_file_name), typeFSRef, &desc);
1606 #else
1607 SetPortWindowPort (w);
1608 err = AECoercePtr (TYPE_FILE_NAME, SDATA (encoded_file_name),
1609 SBYTES (encoded_file_name), typeFSS, &desc);
1610 #endif
1611 if (err == noErr)
1613 #ifdef MAC_OSX
1614 err = AEGetDescData (&desc, &fref, sizeof (FSRef));
1615 #else
1616 err = AEGetDescData (&desc, &fss, sizeof (FSSpec));
1617 #endif
1618 AEDisposeDesc (&desc);
1620 if (err == noErr)
1622 if (alias)
1624 /* (FS)ResolveAlias never sets `changed' to true if
1625 `alias' is minimal. */
1626 #ifdef MAC_OSX
1627 err = FSResolveAlias (NULL, alias, &fref_proxy, &changed);
1628 if (err == noErr)
1629 err = FSCompareFSRefs (&fref, &fref_proxy);
1630 #else
1631 err = ResolveAlias (NULL, alias, &fss_proxy, &changed);
1632 if (err == noErr)
1633 err = !(fss.vRefNum == fss_proxy.vRefNum
1634 && fss.parID == fss_proxy.parID
1635 && EqualString (fss.name, fss_proxy.name,
1636 false, true));
1637 #endif
1639 if (err != noErr || alias == NULL)
1641 if (alias)
1642 DisposeHandle ((Handle) alias);
1643 #ifdef MAC_OSX
1644 err = FSNewAliasMinimal (&fref, &alias);
1645 #else
1646 err = NewAliasMinimal (&fss, &alias);
1647 #endif
1648 changed = true;
1651 if (err == noErr)
1652 if (changed)
1653 err = SetWindowProxyAlias (w, alias);
1656 if (alias)
1657 DisposeHandle ((Handle) alias);
1659 if (err != noErr || !STRINGP (file_name))
1660 RemoveWindowProxy (w);
1662 #endif
1664 /* Mac replacement for XSetWindowBackground. */
1666 void
1667 mac_set_frame_window_background (f, color)
1668 struct frame *f;
1669 unsigned long color;
1671 WindowRef w = FRAME_MAC_WINDOW (f);
1672 #if !TARGET_API_MAC_CARBON
1673 AuxWinHandle aw_handle;
1674 CTabHandle ctab_handle;
1675 ColorSpecPtr ct_table;
1676 short ct_size;
1677 #endif
1678 RGBColor bg_color;
1680 bg_color.red = RED16_FROM_ULONG (color);
1681 bg_color.green = GREEN16_FROM_ULONG (color);
1682 bg_color.blue = BLUE16_FROM_ULONG (color);
1684 #if TARGET_API_MAC_CARBON
1685 SetWindowContentColor (w, &bg_color);
1686 #else
1687 if (GetAuxWin (w, &aw_handle))
1689 ctab_handle = (*aw_handle)->awCTable;
1690 HandToHand ((Handle *) &ctab_handle);
1691 ct_table = (*ctab_handle)->ctTable;
1692 ct_size = (*ctab_handle)->ctSize;
1693 while (ct_size > -1)
1695 if (ct_table->value == 0)
1697 ct_table->rgb = bg_color;
1698 CTabChanged (ctab_handle);
1699 SetWinColor (w, (WCTabHandle) ctab_handle);
1701 ct_size--;
1704 #endif
1707 /* Flush display of frame F, or of all frames if F is null. */
1709 void
1710 x_flush (f)
1711 struct frame *f;
1713 #if TARGET_API_MAC_CARBON
1714 BLOCK_INPUT;
1715 #if USE_CG_DRAWING
1716 mac_prepare_for_quickdraw (f);
1717 #endif
1718 if (f)
1719 QDFlushPortBuffer (GetWindowPort (FRAME_MAC_WINDOW (f)), NULL);
1720 else
1721 QDFlushPortBuffer (GetQDGlobalsThePort (), NULL);
1722 UNBLOCK_INPUT;
1723 #endif
1726 #if USE_CG_DRAWING
1727 void
1728 mac_flush_display_optional (f)
1729 struct frame *f;
1731 BLOCK_INPUT;
1732 mac_prepare_for_quickdraw (f);
1733 UNBLOCK_INPUT;
1735 #endif
1737 void
1738 mac_update_begin (f)
1739 struct frame *f;
1741 #if TARGET_API_MAC_CARBON
1742 /* During update of a frame, availability of input events is
1743 periodically checked with ReceiveNextEvent if
1744 redisplay-dont-pause is nil. That normally flushes window buffer
1745 changes for every check, and thus screen update looks waving even
1746 if no input is available. So we disable screen updates during
1747 update of a frame. */
1748 DisableScreenUpdates ();
1749 #endif
1752 void
1753 mac_update_end (f)
1754 struct frame *f;
1756 #if TARGET_API_MAC_CARBON
1757 EnableScreenUpdates ();
1758 #endif
1761 void
1762 mac_frame_up_to_date (f)
1763 struct frame *f;
1765 /* Nothing to do. */
1768 void
1769 mac_create_frame_window (f, tooltip_p)
1770 struct frame *f;
1771 int tooltip_p;
1773 Rect r;
1774 #if TARGET_API_MAC_CARBON
1775 WindowClass window_class;
1776 WindowAttributes attributes;
1777 #else
1778 short proc_id;
1779 WindowRef behind;
1780 Boolean go_away_flag;
1781 #endif
1783 if (!tooltip_p)
1785 SetRect (&r, f->left_pos, f->top_pos,
1786 f->left_pos + FRAME_PIXEL_WIDTH (f),
1787 f->top_pos + FRAME_PIXEL_HEIGHT (f));
1788 #if TARGET_API_MAC_CARBON
1789 window_class = kDocumentWindowClass;
1790 attributes = (kWindowStandardDocumentAttributes
1791 #ifdef MAC_OSX
1792 | kWindowToolbarButtonAttribute
1793 #endif
1795 #else
1796 proc_id = zoomDocProc;
1797 behind = (WindowRef) -1;
1798 go_away_flag = true;
1799 #endif
1801 else
1803 SetRect (&r, 0, 0, 1, 1);
1804 #if TARGET_API_MAC_CARBON
1805 window_class = kHelpWindowClass;
1806 attributes = (kWindowNoUpdatesAttribute
1807 | kWindowNoActivatesAttribute
1808 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
1809 | kWindowIgnoreClicksAttribute
1810 #endif
1812 #else
1813 proc_id = plainDBox;
1814 behind = NULL;
1815 go_away_flag = false;
1816 #endif
1819 #if TARGET_API_MAC_CARBON
1820 CreateNewWindow (window_class, attributes, &r, &FRAME_MAC_WINDOW (f));
1821 if (FRAME_MAC_WINDOW (f))
1823 SetWRefCon (FRAME_MAC_WINDOW (f), (long) f->output_data.mac);
1824 if (!tooltip_p)
1825 if (install_window_handler (FRAME_MAC_WINDOW (f)) != noErr)
1827 DisposeWindow (FRAME_MAC_WINDOW (f));
1828 FRAME_MAC_WINDOW (f) = NULL;
1831 #else /* !TARGET_API_MAC_CARBON */
1832 FRAME_MAC_WINDOW (f)
1833 = NewCWindow (NULL, &r, "\p", false, proc_id, behind, go_away_flag,
1834 (long) f->output_data.mac);
1835 #endif /* !TARGET_API_MAC_CARBON */
1836 /* so that update events can find this mac_output struct */
1837 f->output_data.mac->mFP = f; /* point back to emacs frame */
1839 #ifndef MAC_OSX
1840 if (!tooltip_p)
1841 if (FRAME_MAC_WINDOW (f))
1843 ControlRef root_control;
1845 if (CreateRootControl (FRAME_MAC_WINDOW (f), &root_control) != noErr)
1847 DisposeWindow (FRAME_MAC_WINDOW (f));
1848 FRAME_MAC_WINDOW (f) = NULL;
1851 #endif
1854 /* Dispose of the Mac window of the frame F. */
1856 void
1857 mac_dispose_frame_window (f)
1858 struct frame *f;
1860 WindowRef window = FRAME_MAC_WINDOW (f);
1862 if (window != tip_window)
1863 remove_window_handler (window);
1865 #if USE_CG_DRAWING
1866 mac_prepare_for_quickdraw (f);
1867 #endif
1868 DisposeWindow (window);
1872 /************************************************************************
1873 View and Drawing
1874 ************************************************************************/
1876 #if USE_CG_DRAWING
1877 #define FRAME_CG_CONTEXT(f) ((f)->output_data.mac->cg_context)
1879 CGContextRef
1880 mac_begin_cg_clip (f, gc)
1881 struct frame *f;
1882 GC gc;
1884 CGContextRef context = FRAME_CG_CONTEXT (f);
1886 if (!context)
1888 QDBeginCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)), &context);
1889 FRAME_CG_CONTEXT (f) = context;
1892 CGContextSaveGState (context);
1893 CGContextTranslateCTM (context, 0, FRAME_PIXEL_HEIGHT (f));
1894 CGContextScaleCTM (context, 1, -1);
1895 if (gc && gc->n_clip_rects)
1896 CGContextClipToRects (context, gc->clip_rects, gc->n_clip_rects);
1898 return context;
1901 void
1902 mac_end_cg_clip (f)
1903 struct frame *f;
1905 CGContextRestoreGState (FRAME_CG_CONTEXT (f));
1908 static void
1909 mac_prepare_for_quickdraw (f)
1910 struct frame *f;
1912 if (f == NULL)
1914 Lisp_Object rest, frame;
1915 FOR_EACH_FRAME (rest, frame)
1916 if (FRAME_MAC_P (XFRAME (frame)))
1917 mac_prepare_for_quickdraw (XFRAME (frame));
1919 else
1921 CGContextRef context = FRAME_CG_CONTEXT (f);
1923 if (context)
1925 CGContextSynchronize (context);
1926 QDEndCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)),
1927 &FRAME_CG_CONTEXT (f));
1931 #endif
1933 static RgnHandle saved_port_clip_region = NULL;
1935 void
1936 mac_begin_clip (f, gc)
1937 struct frame *f;
1938 GC gc;
1940 static RgnHandle new_region = NULL;
1942 if (saved_port_clip_region == NULL)
1943 saved_port_clip_region = NewRgn ();
1944 if (new_region == NULL)
1945 new_region = NewRgn ();
1947 #if USE_CG_DRAWING
1948 mac_prepare_for_quickdraw (f);
1949 #endif
1950 SetPortWindowPort (FRAME_MAC_WINDOW (f));
1952 if (gc && gc->n_clip_rects)
1954 GetClip (saved_port_clip_region);
1955 SectRgn (saved_port_clip_region, gc->clip_region, new_region);
1956 SetClip (new_region);
1960 void
1961 mac_end_clip (f, gc)
1962 struct frame *f;
1963 GC gc;
1965 if (gc && gc->n_clip_rects)
1966 SetClip (saved_port_clip_region);
1969 #if TARGET_API_MAC_CARBON
1970 /* Mac replacement for XCopyArea: used only for scrolling. */
1972 void
1973 mac_scroll_area (f, gc, src_x, src_y, width, height, dest_x, dest_y)
1974 struct frame *f;
1975 GC gc;
1976 int src_x, src_y;
1977 unsigned int width, height;
1978 int dest_x, dest_y;
1980 Rect src_r;
1981 RgnHandle dummy = NewRgn (); /* For avoiding update events. */
1983 SetRect (&src_r, src_x, src_y, src_x + width, src_y + height);
1984 #if USE_CG_DRAWING
1985 mac_prepare_for_quickdraw (f);
1986 #endif
1987 ScrollWindowRect (FRAME_MAC_WINDOW (f),
1988 &src_r, dest_x - src_x, dest_y - src_y,
1989 kScrollWindowNoOptions, dummy);
1990 DisposeRgn (dummy);
1992 #endif
1995 /************************************************************************
1996 Scroll bars
1997 ************************************************************************/
1999 extern struct scroll_bar *tracked_scroll_bar;
2000 extern Lisp_Object last_mouse_scroll_bar;
2001 extern Time last_mouse_movement_time;
2003 static void x_scroll_bar_handle_click P_ ((struct scroll_bar *,
2004 ControlPartCode,
2005 const EventRecord *,
2006 struct input_event *));
2007 #ifndef USE_TOOLKIT_SCROLL_BARS
2008 static void x_scroll_bar_note_movement P_ ((struct scroll_bar *, int, Time));
2009 #else /* USE_TOOLKIT_SCROLL_BARS */
2010 static void x_scroll_bar_handle_press P_ ((struct scroll_bar *,
2011 ControlPartCode, Point,
2012 struct input_event *));
2013 static void x_scroll_bar_handle_release P_ ((struct scroll_bar *,
2014 struct input_event *));
2015 static void x_scroll_bar_handle_drag P_ ((WindowRef, struct scroll_bar *,
2016 Point, struct input_event *));
2017 static pascal void scroll_bar_timer_callback P_ ((EventLoopTimerRef, void *));
2018 static OSStatus install_scroll_bar_timer P_ ((void));
2019 static OSStatus set_scroll_bar_timer P_ ((EventTimerInterval));
2020 static int control_part_code_to_scroll_bar_part P_ ((ControlPartCode));
2021 static void construct_scroll_bar_click P_ ((struct scroll_bar *, int,
2022 struct input_event *));
2023 static OSStatus get_control_part_bounds P_ ((ControlRef, ControlPartCode,
2024 Rect *));
2025 static void update_scroll_bar_track_info P_ ((struct scroll_bar *));
2027 /* Last scroll bar part sent in x_scroll_bar_handle_*. */
2029 static int last_scroll_bar_part;
2031 static EventLoopTimerRef scroll_bar_timer;
2033 static int scroll_bar_timer_event_posted_p;
2035 #define SCROLL_BAR_FIRST_DELAY 0.5
2036 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
2038 static pascal void
2039 scroll_bar_timer_callback (timer, data)
2040 EventLoopTimerRef timer;
2041 void *data;
2043 OSStatus err;
2045 err = mac_post_mouse_moved_event ();
2046 if (err == noErr)
2047 scroll_bar_timer_event_posted_p = 1;
2050 static OSStatus
2051 install_scroll_bar_timer ()
2053 static EventLoopTimerUPP scroll_bar_timer_callbackUPP = NULL;
2055 if (scroll_bar_timer_callbackUPP == NULL)
2056 scroll_bar_timer_callbackUPP =
2057 NewEventLoopTimerUPP (scroll_bar_timer_callback);
2059 if (scroll_bar_timer == NULL)
2060 /* Mac OS X and CarbonLib 1.5 and later allow us to specify
2061 kEventDurationForever as delays. */
2062 return
2063 InstallEventLoopTimer (GetCurrentEventLoop (),
2064 kEventDurationForever, kEventDurationForever,
2065 scroll_bar_timer_callbackUPP, NULL,
2066 &scroll_bar_timer);
2069 static OSStatus
2070 set_scroll_bar_timer (delay)
2071 EventTimerInterval delay;
2073 if (scroll_bar_timer == NULL)
2074 install_scroll_bar_timer ();
2076 scroll_bar_timer_event_posted_p = 0;
2078 return SetEventLoopTimerNextFireTime (scroll_bar_timer, delay);
2081 static int
2082 control_part_code_to_scroll_bar_part (part_code)
2083 ControlPartCode part_code;
2085 switch (part_code)
2087 case kControlUpButtonPart: return scroll_bar_up_arrow;
2088 case kControlDownButtonPart: return scroll_bar_down_arrow;
2089 case kControlPageUpPart: return scroll_bar_above_handle;
2090 case kControlPageDownPart: return scroll_bar_below_handle;
2091 case kControlIndicatorPart: return scroll_bar_handle;
2094 return -1;
2097 static void
2098 construct_scroll_bar_click (bar, part, bufp)
2099 struct scroll_bar *bar;
2100 int part;
2101 struct input_event *bufp;
2103 bufp->kind = SCROLL_BAR_CLICK_EVENT;
2104 bufp->frame_or_window = bar->window;
2105 bufp->arg = Qnil;
2106 bufp->part = part;
2107 bufp->code = 0;
2108 XSETINT (bufp->x, 0);
2109 XSETINT (bufp->y, 0);
2110 bufp->modifiers = 0;
2113 static OSStatus
2114 get_control_part_bounds (ch, part_code, rect)
2115 ControlRef ch;
2116 ControlPartCode part_code;
2117 Rect *rect;
2119 RgnHandle region = NewRgn ();
2120 OSStatus err;
2122 err = GetControlRegion (ch, part_code, region);
2123 if (err == noErr)
2124 GetRegionBounds (region, rect);
2125 DisposeRgn (region);
2127 return err;
2130 static void
2131 x_scroll_bar_handle_press (bar, part_code, mouse_pos, bufp)
2132 struct scroll_bar *bar;
2133 ControlPartCode part_code;
2134 Point mouse_pos;
2135 struct input_event *bufp;
2137 int part = control_part_code_to_scroll_bar_part (part_code);
2139 if (part < 0)
2140 return;
2142 if (part != scroll_bar_handle)
2144 construct_scroll_bar_click (bar, part, bufp);
2145 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code);
2146 set_scroll_bar_timer (SCROLL_BAR_FIRST_DELAY);
2147 bar->dragging = Qnil;
2149 else
2151 Rect r;
2153 get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar),
2154 kControlIndicatorPart, &r);
2155 XSETINT (bar->dragging, - (mouse_pos.v - r.top) - 1);
2158 last_scroll_bar_part = part;
2159 tracked_scroll_bar = bar;
2162 static void
2163 x_scroll_bar_handle_release (bar, bufp)
2164 struct scroll_bar *bar;
2165 struct input_event *bufp;
2167 if (last_scroll_bar_part != scroll_bar_handle
2168 || (INTEGERP (bar->dragging) && XINT (bar->dragging) >= 0))
2169 construct_scroll_bar_click (bar, scroll_bar_end_scroll, bufp);
2171 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0);
2172 set_scroll_bar_timer (kEventDurationForever);
2174 last_scroll_bar_part = -1;
2175 bar->dragging = Qnil;
2176 tracked_scroll_bar = NULL;
2179 static void
2180 x_scroll_bar_handle_drag (win, bar, mouse_pos, bufp)
2181 WindowRef win;
2182 struct scroll_bar *bar;
2183 Point mouse_pos;
2184 struct input_event *bufp;
2186 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2188 if (last_scroll_bar_part == scroll_bar_handle)
2190 int top, top_range;
2191 Rect r;
2193 get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar),
2194 kControlIndicatorPart, &r);
2196 if (INTEGERP (bar->dragging) && XINT (bar->dragging) < 0)
2197 XSETINT (bar->dragging, - (XINT (bar->dragging) + 1));
2199 top = mouse_pos.v - XINT (bar->dragging) - XINT (bar->track_top);
2200 top_range = XINT (bar->track_height) - XINT (bar->min_handle);
2202 if (top < 0)
2203 top = 0;
2204 if (top > top_range)
2205 top = top_range;
2207 construct_scroll_bar_click (bar, scroll_bar_handle, bufp);
2208 XSETINT (bufp->x, top);
2209 XSETINT (bufp->y, top_range);
2211 else
2213 ControlPartCode part_code;
2214 int unhilite_p = 0, part;
2216 if (ch != FindControlUnderMouse (mouse_pos, win, &part_code))
2217 unhilite_p = 1;
2218 else
2220 part = control_part_code_to_scroll_bar_part (part_code);
2222 switch (last_scroll_bar_part)
2224 case scroll_bar_above_handle:
2225 case scroll_bar_below_handle:
2226 if (part != scroll_bar_above_handle
2227 && part != scroll_bar_below_handle)
2228 unhilite_p = 1;
2229 break;
2231 case scroll_bar_up_arrow:
2232 case scroll_bar_down_arrow:
2233 if (part != scroll_bar_up_arrow
2234 && part != scroll_bar_down_arrow)
2235 unhilite_p = 1;
2236 break;
2240 if (unhilite_p)
2241 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0);
2242 else if (part != last_scroll_bar_part
2243 || scroll_bar_timer_event_posted_p)
2245 construct_scroll_bar_click (bar, part, bufp);
2246 last_scroll_bar_part = part;
2247 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code);
2248 set_scroll_bar_timer (SCROLL_BAR_CONTINUOUS_DELAY);
2253 /* Update BAR->track_top, BAR->track_height, and BAR->min_handle for
2254 the scroll bar BAR. This function should be called when the bounds
2255 of the scroll bar is changed. */
2257 static void
2258 update_scroll_bar_track_info (bar)
2259 struct scroll_bar *bar;
2261 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2262 Rect r0, r1;
2264 GetControlBounds (ch, &r0);
2266 if (r0.right - r0.left >= r0.bottom - r0.top
2267 #ifdef MAC_OSX
2268 || r0.right - r0.left < MAC_AQUA_SMALL_VERTICAL_SCROLL_BAR_WIDTH
2269 #endif
2272 XSETINT (bar->track_top, 0);
2273 XSETINT (bar->track_height, 0);
2274 XSETINT (bar->min_handle, 0);
2276 else
2278 BLOCK_INPUT;
2280 SetControl32BitMinimum (ch, 0);
2281 SetControl32BitMaximum (ch, 1 << 30);
2282 SetControlViewSize (ch, 1);
2284 /* Move the scroll bar thumb to the top. */
2285 SetControl32BitValue (ch, 0);
2286 get_control_part_bounds (ch, kControlIndicatorPart, &r0);
2288 /* Move the scroll bar thumb to the bottom. */
2289 SetControl32BitValue (ch, 1 << 30);
2290 get_control_part_bounds (ch, kControlIndicatorPart, &r1);
2292 UnionRect (&r0, &r1, &r0);
2293 XSETINT (bar->track_top, r0.top);
2294 XSETINT (bar->track_height, r0.bottom - r0.top);
2295 XSETINT (bar->min_handle, r1.bottom - r1.top);
2297 /* Don't show the scroll bar if its height is not enough to
2298 display the scroll bar thumb. */
2299 if (r0.bottom - r0.top > 0)
2300 ShowControl (ch);
2302 UNBLOCK_INPUT;
2306 /* Set the thumb size and position of scroll bar BAR. We are currently
2307 displaying PORTION out of a whole WHOLE, and our position POSITION. */
2309 void
2310 x_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
2311 struct scroll_bar *bar;
2312 int portion, position, whole;
2314 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2315 int value, viewsize, maximum;
2317 if (XINT (bar->track_height) == 0)
2318 return;
2320 if (whole <= portion)
2321 value = 0, viewsize = 1, maximum = 0;
2322 else
2324 float scale;
2326 maximum = XINT (bar->track_height) - XINT (bar->min_handle);
2327 scale = (float) maximum / (whole - portion);
2328 value = position * scale + 0.5f;
2329 viewsize = (int) (portion * scale + 0.5f) + XINT (bar->min_handle);
2332 BLOCK_INPUT;
2334 if (GetControlViewSize (ch) != viewsize
2335 || GetControl32BitValue (ch) != value
2336 || GetControl32BitMaximum (ch) != maximum)
2338 /* Temporarily hide the scroll bar to avoid multiple redraws. */
2339 SetControlVisibility (ch, false, false);
2341 SetControl32BitMaximum (ch, maximum);
2342 SetControl32BitValue (ch, value);
2343 SetControlViewSize (ch, viewsize);
2345 SetControlVisibility (ch, true, true);
2348 UNBLOCK_INPUT;
2351 #endif /* USE_TOOLKIT_SCROLL_BARS */
2353 /* Create a scroll bar control for BAR. BOUNDS and VISIBLE specifies
2354 the initial geometry and visibility, respectively. The created
2355 control is stored in some members of BAR. */
2357 void
2358 mac_create_scroll_bar (bar, bounds, visible)
2359 struct scroll_bar *bar;
2360 const Rect *bounds;
2361 Boolean visible;
2363 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2364 ControlRef ch;
2366 #if USE_CG_DRAWING
2367 mac_prepare_for_quickdraw (f);
2368 #endif
2369 ch = NewControl (FRAME_MAC_WINDOW (f), bounds, "\p", visible, 0, 0, 0,
2370 #if TARGET_API_MAC_CARBON
2371 kControlScrollBarProc,
2372 #else
2373 scrollBarProc,
2374 #endif
2375 (SInt32) bar);
2376 SET_SCROLL_BAR_CONTROL_REF (bar, ch);
2378 XSETINT (bar->start, 0);
2379 XSETINT (bar->end, 0);
2380 bar->dragging = Qnil;
2382 #ifdef USE_TOOLKIT_SCROLL_BARS
2383 update_scroll_bar_track_info (bar);
2384 #endif
2387 /* Dispose of the scroll bar control stored in some members of
2388 BAR. */
2390 void
2391 mac_dispose_scroll_bar (bar)
2392 struct scroll_bar *bar;
2394 #if USE_CG_DRAWING
2395 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2397 mac_prepare_for_quickdraw (f);
2398 #endif
2399 DisposeControl (SCROLL_BAR_CONTROL_REF (bar));
2402 /* Set bounds of the scroll bar BAR to BOUNDS. */
2404 void
2405 mac_set_scroll_bar_bounds (bar, bounds)
2406 struct scroll_bar *bar;
2407 const Rect *bounds;
2409 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2410 SInt16 width, height;
2411 #if USE_CG_DRAWING
2412 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2414 mac_prepare_for_quickdraw (f);
2415 #endif
2417 width = bounds->right - bounds->left;
2418 height = bounds->bottom - bounds->top;
2419 HideControl (ch);
2420 MoveControl (ch, bounds->left, bounds->top);
2421 SizeControl (ch, width, height);
2422 #ifdef USE_TOOLKIT_SCROLL_BARS
2423 update_scroll_bar_track_info (bar);
2424 #else
2425 if (width < height)
2426 ShowControl (ch);
2427 #endif
2430 /* Draw the scroll bar BAR. */
2432 void
2433 mac_redraw_scroll_bar (bar)
2434 struct scroll_bar *bar;
2436 #if USE_CG_DRAWING
2437 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2439 mac_prepare_for_quickdraw (f);
2440 #endif
2441 Draw1Control (SCROLL_BAR_CONTROL_REF (bar));
2444 /* Handle a mouse click on the scroll bar BAR. If *EMACS_EVENT's kind
2445 is set to something other than NO_EVENT, it is enqueued.
2447 This may be called from a signal handler, so we have to ignore GC
2448 mark bits. */
2450 static void
2451 x_scroll_bar_handle_click (bar, part_code, er, bufp)
2452 struct scroll_bar *bar;
2453 ControlPartCode part_code;
2454 const EventRecord *er;
2455 struct input_event *bufp;
2457 int win_y, top_range;
2459 if (! GC_WINDOWP (bar->window))
2460 abort ();
2462 bufp->kind = SCROLL_BAR_CLICK_EVENT;
2463 bufp->frame_or_window = bar->window;
2464 bufp->arg = Qnil;
2466 bar->dragging = Qnil;
2468 switch (part_code)
2470 case kControlUpButtonPart:
2471 bufp->part = scroll_bar_up_arrow;
2472 break;
2473 case kControlDownButtonPart:
2474 bufp->part = scroll_bar_down_arrow;
2475 break;
2476 case kControlPageUpPart:
2477 bufp->part = scroll_bar_above_handle;
2478 break;
2479 case kControlPageDownPart:
2480 bufp->part = scroll_bar_below_handle;
2481 break;
2482 #if TARGET_API_MAC_CARBON
2483 default:
2484 #else
2485 case kControlIndicatorPart:
2486 #endif
2487 if (er->what == mouseDown)
2488 bar->dragging = make_number (0);
2489 XSETVECTOR (last_mouse_scroll_bar, bar);
2490 bufp->part = scroll_bar_handle;
2491 break;
2494 win_y = XINT (bufp->y) - XINT (bar->top);
2495 top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (0/*dummy*/, XINT (bar->height));
2497 win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER;
2499 win_y -= 24;
2501 if (! NILP (bar->dragging))
2502 win_y -= XINT (bar->dragging);
2504 if (win_y < 0)
2505 win_y = 0;
2506 if (win_y > top_range)
2507 win_y = top_range;
2509 XSETINT (bufp->x, win_y);
2510 XSETINT (bufp->y, top_range);
2513 /* Return information to the user about the current position of the mouse
2514 on the scroll bar. */
2516 void
2517 x_scroll_bar_report_motion (fp, bar_window, part, x, y, time)
2518 FRAME_PTR *fp;
2519 Lisp_Object *bar_window;
2520 enum scroll_bar_part *part;
2521 Lisp_Object *x, *y;
2522 unsigned long *time;
2524 struct scroll_bar *bar = XSCROLL_BAR (last_mouse_scroll_bar);
2525 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2526 #if TARGET_API_MAC_CARBON
2527 WindowRef wp = GetControlOwner (ch);
2528 #else
2529 WindowRef wp = (*ch)->contrlOwner;
2530 #endif
2531 Point mouse_pos;
2532 struct frame *f = mac_window_to_frame (wp);
2533 int win_y, top_range;
2535 #if TARGET_API_MAC_CARBON
2536 GetGlobalMouse (&mouse_pos);
2537 mouse_pos.h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
2538 mouse_pos.v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
2539 #else
2540 SetPortWindowPort (wp);
2541 GetMouse (&mouse_pos);
2542 #endif
2544 win_y = mouse_pos.v - XINT (bar->top);
2545 top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height));
2547 win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER;
2549 win_y -= 24;
2551 if (! NILP (bar->dragging))
2552 win_y -= XINT (bar->dragging);
2554 if (win_y < 0)
2555 win_y = 0;
2556 if (win_y > top_range)
2557 win_y = top_range;
2559 *fp = f;
2560 *bar_window = bar->window;
2562 if (! NILP (bar->dragging))
2563 *part = scroll_bar_handle;
2564 else if (win_y < XINT (bar->start))
2565 *part = scroll_bar_above_handle;
2566 else if (win_y < XINT (bar->end) + VERTICAL_SCROLL_BAR_MIN_HANDLE)
2567 *part = scroll_bar_handle;
2568 else
2569 *part = scroll_bar_below_handle;
2571 XSETINT (*x, win_y);
2572 XSETINT (*y, top_range);
2574 f->mouse_moved = 0;
2575 last_mouse_scroll_bar = Qnil;
2577 *time = last_mouse_movement_time;
2580 #ifndef USE_TOOLKIT_SCROLL_BARS
2581 /* Draw BAR's handle in the proper position.
2583 If the handle is already drawn from START to END, don't bother
2584 redrawing it, unless REBUILD is non-zero; in that case, always
2585 redraw it. (REBUILD is handy for drawing the handle after expose
2586 events.)
2588 Normally, we want to constrain the start and end of the handle to
2589 fit inside its rectangle, but if the user is dragging the scroll
2590 bar handle, we want to let them drag it down all the way, so that
2591 the bar's top is as far down as it goes; otherwise, there's no way
2592 to move to the very end of the buffer. */
2594 void
2595 x_scroll_bar_set_handle (bar, start, end, rebuild)
2596 struct scroll_bar *bar;
2597 int start, end;
2598 int rebuild;
2600 int dragging = ! NILP (bar->dragging);
2601 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2602 FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2603 int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height));
2604 int length = end - start;
2606 /* If the display is already accurate, do nothing. */
2607 if (! rebuild
2608 && start == XINT (bar->start)
2609 && end == XINT (bar->end))
2610 return;
2612 BLOCK_INPUT;
2614 /* Make sure the values are reasonable, and try to preserve the
2615 distance between start and end. */
2616 if (start < 0)
2617 start = 0;
2618 else if (start > top_range)
2619 start = top_range;
2620 end = start + length;
2622 if (end < start)
2623 end = start;
2624 else if (end > top_range && ! dragging)
2625 end = top_range;
2627 /* Store the adjusted setting in the scroll bar. */
2628 XSETINT (bar->start, start);
2629 XSETINT (bar->end, end);
2631 /* Clip the end position, just for display. */
2632 if (end > top_range)
2633 end = top_range;
2635 /* Draw bottom positions VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
2636 top positions, to make sure the handle is always at least that
2637 many pixels tall. */
2638 end += VERTICAL_SCROLL_BAR_MIN_HANDLE;
2640 SetControlMinimum (ch, 0);
2641 /* Don't inadvertently activate deactivated scroll bars */
2642 if (GetControlMaximum (ch) != -1)
2643 SetControlMaximum (ch, top_range + VERTICAL_SCROLL_BAR_MIN_HANDLE
2644 - (end - start));
2645 SetControlValue (ch, start);
2646 #if TARGET_API_MAC_CARBON
2647 SetControlViewSize (ch, end - start);
2648 #endif
2650 UNBLOCK_INPUT;
2653 /* Handle some mouse motion while someone is dragging the scroll bar.
2655 This may be called from a signal handler, so we have to ignore GC
2656 mark bits. */
2658 static void
2659 x_scroll_bar_note_movement (bar, y_pos, t)
2660 struct scroll_bar *bar;
2661 int y_pos;
2662 Time t;
2664 FRAME_PTR f = XFRAME (XWINDOW (bar->window)->frame);
2666 last_mouse_movement_time = t;
2668 f->mouse_moved = 1;
2669 XSETVECTOR (last_mouse_scroll_bar, bar);
2671 /* If we're dragging the bar, display it. */
2672 if (! GC_NILP (bar->dragging))
2674 /* Where should the handle be now? */
2675 int new_start = y_pos - 24;
2677 if (new_start != XINT (bar->start))
2679 int new_end = new_start + (XINT (bar->end) - XINT (bar->start));
2681 x_scroll_bar_set_handle (bar, new_start, new_end, 0);
2685 #endif /* !USE_TOOLKIT_SCROLL_BARS */
2688 /***********************************************************************
2689 Tool-bars
2690 ***********************************************************************/
2692 #if USE_MAC_TOOLBAR
2693 /* In identifiers such as function/variable names, Emacs tool bar is
2694 referred to as `tool_bar', and Carbon HIToolbar as `toolbar'. */
2696 #define TOOLBAR_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar"))
2697 #define TOOLBAR_ICON_ITEM_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar.icon"))
2699 #define TOOLBAR_ITEM_COMMAND_ID_OFFSET 'Tb\0\0'
2700 #define TOOLBAR_ITEM_COMMAND_ID_P(id) \
2701 (((id) & ~0xffff) == TOOLBAR_ITEM_COMMAND_ID_OFFSET)
2702 #define TOOLBAR_ITEM_COMMAND_ID_VALUE(id) \
2703 ((id) - TOOLBAR_ITEM_COMMAND_ID_OFFSET)
2704 #define TOOLBAR_ITEM_MAKE_COMMAND_ID(value) \
2705 ((value) + TOOLBAR_ITEM_COMMAND_ID_OFFSET)
2707 static OSStatus mac_handle_toolbar_command_event P_ ((EventHandlerCallRef,
2708 EventRef, void *));
2710 extern Rect last_mouse_glyph;
2712 extern void mac_move_window_with_gravity P_ ((struct frame *, int,
2713 short, short));
2714 extern void mac_get_window_origin_with_gravity P_ ((struct frame *, int,
2715 short *, short *));
2716 extern CGImageRef mac_image_spec_to_cg_image P_ ((struct frame *,
2717 Lisp_Object));
2719 static OSStatus
2720 mac_handle_toolbar_event (next_handler, event, data)
2721 EventHandlerCallRef next_handler;
2722 EventRef event;
2723 void *data;
2725 OSStatus result = eventNotHandledErr;
2727 switch (GetEventKind (event))
2729 case kEventToolbarGetDefaultIdentifiers:
2730 result = noErr;
2731 break;
2733 case kEventToolbarGetAllowedIdentifiers:
2735 CFMutableArrayRef array;
2737 GetEventParameter (event, kEventParamMutableArray,
2738 typeCFMutableArrayRef, NULL,
2739 sizeof (CFMutableArrayRef), NULL, &array);
2740 CFArrayAppendValue (array, TOOLBAR_ICON_ITEM_IDENTIFIER);
2741 result = noErr;
2743 break;
2745 case kEventToolbarCreateItemWithIdentifier:
2747 CFStringRef identifier;
2748 HIToolbarItemRef item = NULL;
2750 GetEventParameter (event, kEventParamToolbarItemIdentifier,
2751 typeCFStringRef, NULL,
2752 sizeof (CFStringRef), NULL, &identifier);
2754 if (CFStringCompare (identifier, TOOLBAR_ICON_ITEM_IDENTIFIER, 0)
2755 == kCFCompareEqualTo)
2756 HIToolbarItemCreate (identifier,
2757 kHIToolbarItemAllowDuplicates
2758 | kHIToolbarItemCantBeRemoved, &item);
2760 if (item)
2762 SetEventParameter (event, kEventParamToolbarItem,
2763 typeHIToolbarItemRef,
2764 sizeof (HIToolbarItemRef), &item);
2765 result = noErr;
2768 break;
2770 default:
2771 abort ();
2774 return result;
2777 /* Create a tool bar for frame F. */
2779 static OSStatus
2780 mac_create_frame_tool_bar (f)
2781 FRAME_PTR f;
2783 OSStatus err;
2784 HIToolbarRef toolbar;
2786 err = HIToolbarCreate (TOOLBAR_IDENTIFIER, kHIToolbarNoAttributes,
2787 &toolbar);
2788 if (err == noErr)
2790 static const EventTypeSpec specs[] =
2791 {{kEventClassToolbar, kEventToolbarGetDefaultIdentifiers},
2792 {kEventClassToolbar, kEventToolbarGetAllowedIdentifiers},
2793 {kEventClassToolbar, kEventToolbarCreateItemWithIdentifier}};
2795 err = InstallEventHandler (HIObjectGetEventTarget (toolbar),
2796 mac_handle_toolbar_event,
2797 GetEventTypeCount (specs), specs,
2798 f, NULL);
2801 if (err == noErr)
2802 err = HIToolbarSetDisplayMode (toolbar, kHIToolbarDisplayModeIconOnly);
2803 if (err == noErr)
2805 static const EventTypeSpec specs[] =
2806 {{kEventClassCommand, kEventCommandProcess}};
2808 err = InstallWindowEventHandler (FRAME_MAC_WINDOW (f),
2809 mac_handle_toolbar_command_event,
2810 GetEventTypeCount (specs),
2811 specs, f, NULL);
2813 if (err == noErr)
2814 err = SetWindowToolbar (FRAME_MAC_WINDOW (f), toolbar);
2816 if (toolbar)
2817 CFRelease (toolbar);
2819 return err;
2822 /* Update the tool bar for frame F. Add new buttons and remove old. */
2824 void
2825 update_frame_tool_bar (f)
2826 FRAME_PTR f;
2828 HIToolbarRef toolbar = NULL;
2829 short left, top;
2830 CFArrayRef old_items = NULL;
2831 CFIndex old_count;
2832 int i, pos, win_gravity = f->output_data.mac->toolbar_win_gravity;
2833 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
2835 BLOCK_INPUT;
2837 GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar);
2838 if (toolbar == NULL)
2840 mac_create_frame_tool_bar (f);
2841 GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar);
2842 if (toolbar == NULL)
2843 goto out;
2844 if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity)
2845 mac_get_window_origin_with_gravity (f, win_gravity, &left, &top);
2848 HIToolbarCopyItems (toolbar, &old_items);
2849 if (old_items == NULL)
2850 goto out;
2852 old_count = CFArrayGetCount (old_items);
2853 pos = 0;
2854 for (i = 0; i < f->n_tool_bar_items; ++i)
2856 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
2858 int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
2859 int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
2860 int idx;
2861 Lisp_Object image;
2862 CGImageRef cg_image;
2863 CFStringRef label;
2864 HIToolbarItemRef item;
2866 /* If image is a vector, choose the image according to the
2867 button state. */
2868 image = PROP (TOOL_BAR_ITEM_IMAGES);
2869 if (VECTORP (image))
2871 if (enabled_p)
2872 idx = (selected_p
2873 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
2874 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
2875 else
2876 idx = (selected_p
2877 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
2878 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
2880 xassert (ASIZE (image) >= idx);
2881 image = AREF (image, idx);
2883 else
2884 idx = -1;
2886 cg_image = mac_image_spec_to_cg_image (f, image);
2887 /* Ignore invalid image specifications. */
2888 if (cg_image == NULL)
2889 continue;
2891 label = cfstring_create_with_string (PROP (TOOL_BAR_ITEM_CAPTION));
2892 if (label == NULL)
2893 label = CFSTR ("");
2895 if (pos < old_count)
2897 CGImageRef old_cg_image = NULL;
2898 CFStringRef old_label = NULL;
2899 Boolean old_enabled_p;
2901 item = (HIToolbarItemRef) CFArrayGetValueAtIndex (old_items, pos);
2903 HIToolbarItemCopyImage (item, &old_cg_image);
2904 if (cg_image != old_cg_image)
2905 HIToolbarItemSetImage (item, cg_image);
2906 CGImageRelease (old_cg_image);
2908 HIToolbarItemCopyLabel (item, &old_label);
2909 if (CFStringCompare (label, old_label, 0) != kCFCompareEqualTo)
2910 HIToolbarItemSetLabel (item, label);
2911 CFRelease (old_label);
2913 old_enabled_p = HIToolbarItemIsEnabled (item);
2914 if ((enabled_p || idx >= 0) != old_enabled_p)
2915 HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0));
2917 else
2919 item = NULL;
2920 HIToolbarCreateItemWithIdentifier (toolbar,
2921 TOOLBAR_ICON_ITEM_IDENTIFIER,
2922 NULL, &item);
2923 if (item)
2925 HIToolbarItemSetImage (item, cg_image);
2926 HIToolbarItemSetLabel (item, label);
2927 HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0));
2928 HIToolbarAppendItem (toolbar, item);
2929 CFRelease (item);
2933 CFRelease (label);
2934 if (item)
2936 HIToolbarItemSetCommandID (item, TOOLBAR_ITEM_MAKE_COMMAND_ID (i));
2937 pos++;
2941 CFRelease (old_items);
2943 while (pos < old_count)
2944 HIToolbarRemoveItemAtIndex (toolbar, --old_count);
2946 ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), true,
2947 !win_gravity && f == mac_focus_frame (dpyinfo));
2948 /* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events on
2949 toolbar visibility change. */
2950 mac_handle_origin_change (f);
2951 if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity)
2953 mac_move_window_with_gravity (f, win_gravity, left, top);
2954 /* If the title bar is completely outside the screen, adjust the
2955 position. */
2956 ConstrainWindowToScreen (FRAME_MAC_WINDOW (f), kWindowTitleBarRgn,
2957 kWindowConstrainMoveRegardlessOfFit
2958 | kWindowConstrainAllowPartial, NULL, NULL);
2959 f->output_data.mac->toolbar_win_gravity = 0;
2962 out:
2963 UNBLOCK_INPUT;
2966 /* Hide the tool bar on frame F. Unlike the counterpart on GTK+, it
2967 doesn't deallocate the resources. */
2969 void
2970 free_frame_tool_bar (f)
2971 FRAME_PTR f;
2973 if (IsWindowToolbarVisible (FRAME_MAC_WINDOW (f)))
2975 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
2977 BLOCK_INPUT;
2978 ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), false,
2979 (NILP (find_symbol_value
2980 (intern ("frame-notice-user-settings")))
2981 && f == mac_focus_frame (dpyinfo)));
2982 /* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events
2983 on toolbar visibility change. */
2984 mac_handle_origin_change (f);
2985 UNBLOCK_INPUT;
2989 /* Report a mouse movement over toolbar to the mainstream Emacs
2990 code. */
2992 static void
2993 mac_tool_bar_note_mouse_movement (f, event)
2994 struct frame *f;
2995 EventRef event;
2997 OSStatus err;
2998 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
2999 int mouse_down_p;
3000 WindowRef window;
3001 WindowPartCode part_code;
3002 HIViewRef item_view;
3003 UInt32 command_id;
3005 mouse_down_p = (dpyinfo->grabbed
3006 && f == last_mouse_frame
3007 && FRAME_LIVE_P (f));
3008 if (mouse_down_p)
3009 return;
3011 err = GetEventParameter (event, kEventParamWindowRef, typeWindowRef, NULL,
3012 sizeof (WindowRef), NULL, &window);
3013 if (err != noErr || window != FRAME_MAC_WINDOW (f))
3014 return;
3016 err = GetEventParameter (event, kEventParamWindowPartCode,
3017 typeWindowPartCode, NULL,
3018 sizeof (WindowPartCode), NULL, &part_code);
3019 if (err != noErr || part_code != inStructure)
3020 return;
3022 err = HIViewGetViewForMouseEvent (HIViewGetRoot (window), event, &item_view);
3023 /* This doesn't work on Mac OS X 10.2. On Mac OS X 10.3 and 10.4, a
3024 toolbar item view seems to have the same command ID with that of
3025 the toolbar item. */
3026 if (err == noErr)
3027 err = GetControlCommandID (item_view, &command_id);
3028 if (err == noErr && TOOLBAR_ITEM_COMMAND_ID_P (command_id))
3030 int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command_id);
3032 if (i < f->n_tool_bar_items)
3034 HIRect bounds;
3035 HIViewRef content_view;
3037 err = HIViewGetBounds (item_view, &bounds);
3038 if (err == noErr)
3039 err = HIViewFindByID (HIViewGetRoot (window),
3040 kHIViewWindowContentID, &content_view);
3041 if (err == noErr)
3042 err = HIViewConvertRect (&bounds, item_view, content_view);
3043 if (err == noErr)
3044 SetRect (&last_mouse_glyph,
3045 CGRectGetMinX (bounds), CGRectGetMinY (bounds),
3046 CGRectGetMaxX (bounds), CGRectGetMaxY (bounds));
3048 help_echo_object = help_echo_window = Qnil;
3049 help_echo_pos = -1;
3050 help_echo_string = PROP (TOOL_BAR_ITEM_HELP);
3051 if (NILP (help_echo_string))
3052 help_echo_string = PROP (TOOL_BAR_ITEM_CAPTION);
3057 static OSStatus
3058 mac_handle_toolbar_command_event (next_handler, event, data)
3059 EventHandlerCallRef next_handler;
3060 EventRef event;
3061 void *data;
3063 OSStatus err, result = eventNotHandledErr;
3064 struct frame *f = (struct frame *) data;
3065 HICommand command;
3067 err = GetEventParameter (event, kEventParamDirectObject,
3068 typeHICommand, NULL,
3069 sizeof (HICommand), NULL, &command);
3070 if (err != noErr)
3071 return result;
3073 switch (GetEventKind (event))
3075 case kEventCommandProcess:
3076 if (!TOOLBAR_ITEM_COMMAND_ID_P (command.commandID))
3077 result = CallNextEventHandler (next_handler, event);
3078 else
3080 int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command.commandID);
3082 if (i < f->n_tool_bar_items
3083 && !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P)))
3085 Lisp_Object frame;
3086 struct input_event buf;
3088 EVENT_INIT (buf);
3090 XSETFRAME (frame, f);
3091 buf.kind = TOOL_BAR_EVENT;
3092 buf.frame_or_window = frame;
3093 buf.arg = frame;
3094 kbd_buffer_store_event (&buf);
3096 buf.kind = TOOL_BAR_EVENT;
3097 buf.frame_or_window = frame;
3098 buf.arg = PROP (TOOL_BAR_ITEM_KEY);
3099 buf.modifiers = mac_event_to_emacs_modifiers (event);
3100 kbd_buffer_store_event (&buf);
3102 result = noErr;
3105 break;
3107 default:
3108 abort ();
3110 #undef PROP
3112 return result;
3114 #endif /* USE_MAC_TOOLBAR */
3117 /***********************************************************************
3118 Font Panel
3119 ***********************************************************************/
3121 #if USE_MAC_FONT_PANEL
3122 /* Whether Font Panel has been shown before. The first call to font
3123 panel functions (FPIsFontPanelVisible, SetFontInfoForSelection) is
3124 slow. This variable is used for deferring such a call as much as
3125 possible. */
3126 static int font_panel_shown_p = 0;
3128 extern Lisp_Object Qpanel_closed, Qselection;
3129 extern Lisp_Object Qfont;
3131 /* Whether the font panel is currently visible. */
3134 mac_font_panel_visible_p ()
3136 return font_panel_shown_p && FPIsFontPanelVisible ();
3139 static pascal OSStatus
3140 mac_handle_font_event (next_handler, event, data)
3141 EventHandlerCallRef next_handler;
3142 EventRef event;
3143 void *data;
3145 OSStatus result, err;
3146 Lisp_Object id_key;
3147 int num_params;
3148 const EventParamName *names;
3149 const EventParamType *types;
3150 static const EventParamName names_sel[] = {kEventParamATSUFontID,
3151 kEventParamATSUFontSize,
3152 kEventParamFMFontFamily,
3153 kEventParamFMFontStyle,
3154 kEventParamFMFontSize,
3155 kEventParamFontColor};
3156 static const EventParamType types_sel[] = {typeATSUFontID,
3157 typeATSUSize,
3158 typeFMFontFamily,
3159 typeFMFontStyle,
3160 typeFMFontSize,
3161 typeFontColor};
3163 result = CallNextEventHandler (next_handler, event);
3164 if (result != eventNotHandledErr)
3165 return result;
3167 switch (GetEventKind (event))
3169 case kEventFontPanelClosed:
3170 id_key = Qpanel_closed;
3171 num_params = 0;
3172 names = NULL;
3173 types = NULL;
3174 break;
3176 case kEventFontSelection:
3177 id_key = Qselection;
3178 num_params = sizeof (names_sel) / sizeof (names_sel[0]);
3179 names = names_sel;
3180 types = types_sel;
3181 break;
3184 err = mac_store_event_ref_as_apple_event (0, 0, Qfont, id_key,
3185 event, num_params,
3186 names, types);
3187 if (err == noErr)
3188 result = noErr;
3190 return result;
3193 /* Toggle visiblity of the font panel. */
3195 OSStatus
3196 mac_show_hide_font_panel ()
3198 if (!font_panel_shown_p)
3200 OSStatus err;
3202 static const EventTypeSpec specs[] =
3203 {{kEventClassFont, kEventFontPanelClosed},
3204 {kEventClassFont, kEventFontSelection}};
3206 err = InstallApplicationEventHandler (mac_handle_font_event,
3207 GetEventTypeCount (specs),
3208 specs, NULL, NULL);
3209 if (err != noErr)
3210 return err;
3212 font_panel_shown_p = 1;
3215 return FPShowHideFontPanel ();
3218 /* Set the font selected in the font panel to the one corresponding to
3219 the face FACE_ID and the charcacter C in the frame F. */
3221 OSStatus
3222 mac_set_font_info_for_selection (f, face_id, c)
3223 struct frame *f;
3224 int face_id, c;
3226 OSStatus err;
3227 EventTargetRef target = NULL;
3228 XFontStruct *font = NULL;
3230 if (!mac_font_panel_visible_p ())
3231 return noErr;
3233 if (f)
3235 target = GetWindowEventTarget (FRAME_MAC_WINDOW (f));
3237 if (FRAME_FACE_CACHE (f) && CHAR_VALID_P (c, 0))
3239 struct face *face;
3241 face_id = FACE_FOR_CHAR (f, FACE_FROM_ID (f, face_id), c);
3242 face = FACE_FROM_ID (f, face_id);
3243 font = face->font;
3247 if (font == NULL)
3248 err = SetFontInfoForSelection (kFontSelectionATSUIType, 0, NULL, target);
3249 else
3251 if (font->mac_fontnum != -1)
3253 FontSelectionQDStyle qd_style;
3255 qd_style.version = kFontSelectionQDStyleVersionZero;
3256 qd_style.instance.fontFamily = font->mac_fontnum;
3257 qd_style.instance.fontStyle = font->mac_fontface;
3258 qd_style.size = font->mac_fontsize;
3259 qd_style.hasColor = false;
3261 err = SetFontInfoForSelection (kFontSelectionQDType,
3262 1, &qd_style, target);
3264 else
3265 err = SetFontInfoForSelection (kFontSelectionATSUIType,
3266 1, &font->mac_style, target);
3269 return err;
3271 #endif /* USE_MAC_FONT_PANEL */
3274 /************************************************************************
3275 Event Handling
3276 ************************************************************************/
3278 /* Non-zero means that a HELP_EVENT has been generated since Emacs
3279 start. */
3281 static int any_help_event_p;
3283 /* Last window where we saw the mouse. Used by mouse-autoselect-window. */
3284 static Lisp_Object last_window;
3286 static Point saved_menu_event_location;
3288 extern struct frame *pending_autoraise_frame;
3290 extern FRAME_PTR last_mouse_glyph_frame;
3292 #ifdef __STDC__
3293 extern int volatile input_signal_count;
3294 #else
3295 extern int input_signal_count;
3296 #endif
3298 extern int mac_screen_config_changed;
3300 extern Lisp_Object Vmac_emulate_three_button_mouse;
3301 #if TARGET_API_MAC_CARBON
3302 extern int mac_wheel_button_is_mouse_2;
3303 extern int mac_pass_command_to_system;
3304 extern int mac_pass_control_to_system;
3305 #endif /* TARGET_API_MAC_CARBON */
3306 extern int mac_ready_for_apple_events;
3308 extern void mac_focus_changed P_ ((int, struct mac_display_info *,
3309 struct frame *, struct input_event *));
3310 extern int mac_get_emulated_btn P_ ((UInt32));
3311 extern int note_mouse_movement P_ ((FRAME_PTR, Point *));
3312 extern void mac_get_screen_info P_ ((struct mac_display_info *));
3314 /* The focus may have changed. Figure out if it is a real focus change,
3315 by checking both FocusIn/Out and Enter/LeaveNotify events.
3317 Returns FOCUS_IN_EVENT event in *BUFP. */
3319 static void
3320 x_detect_focus_change (dpyinfo, event, bufp)
3321 struct mac_display_info *dpyinfo;
3322 const EventRecord *event;
3323 struct input_event *bufp;
3325 struct frame *frame;
3327 frame = mac_window_to_frame ((WindowRef) event->message);
3328 if (! frame)
3329 return;
3331 /* On Mac, this is only called from focus events, so no switch needed. */
3332 mac_focus_changed ((event->modifiers & activeFlag),
3333 dpyinfo, frame, bufp);
3336 #if TARGET_API_MAC_CARBON
3337 /* Obtains the event modifiers from the event EVENTREF and then calls
3338 mac_to_emacs_modifiers. */
3340 static int
3341 mac_event_to_emacs_modifiers (EventRef eventRef)
3343 UInt32 mods = 0, class;
3345 GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32, NULL,
3346 sizeof (UInt32), NULL, &mods);
3347 class = GetEventClass (eventRef);
3348 if (!NILP (Vmac_emulate_three_button_mouse)
3349 && (class == kEventClassMouse || class == kEventClassCommand))
3351 mods &= ~(optionKey | cmdKey);
3353 return mac_to_emacs_modifiers (mods, 0);
3356 /* Given an event REF, return the code to use for the mouse button
3357 code in the emacs input_event. */
3359 static int
3360 mac_get_mouse_btn (EventRef ref)
3362 EventMouseButton result = kEventMouseButtonPrimary;
3363 GetEventParameter (ref, kEventParamMouseButton, typeMouseButton, NULL,
3364 sizeof (EventMouseButton), NULL, &result);
3365 switch (result)
3367 case kEventMouseButtonPrimary:
3368 if (NILP (Vmac_emulate_three_button_mouse))
3369 return 0;
3370 else {
3371 UInt32 mods = 0;
3372 GetEventParameter (ref, kEventParamKeyModifiers, typeUInt32, NULL,
3373 sizeof (UInt32), NULL, &mods);
3374 return mac_get_emulated_btn(mods);
3376 case kEventMouseButtonSecondary:
3377 return mac_wheel_button_is_mouse_2 ? 2 : 1;
3378 case kEventMouseButtonTertiary:
3379 case 4: /* 4 is the number for the mouse wheel button */
3380 return mac_wheel_button_is_mouse_2 ? 1 : 2;
3381 default:
3382 return 0;
3386 /* Normally, ConvertEventRefToEventRecord will correctly handle all
3387 events. However the click of the mouse wheel is not converted to a
3388 mouseDown or mouseUp event. Likewise for dead key events. This
3389 calls ConvertEventRefToEventRecord, but then checks to see if it is
3390 a mouse up/down, or a dead key Carbon event that has not been
3391 converted, and if so, converts it by hand (to be picked up in the
3392 XTread_socket loop). */
3393 static Boolean mac_convert_event_ref (EventRef eventRef, EventRecord *eventRec)
3395 OSStatus err;
3396 Boolean result = ConvertEventRefToEventRecord (eventRef, eventRec);
3397 EventKind action;
3399 if (result)
3400 return result;
3402 switch (GetEventClass (eventRef))
3404 case kEventClassMouse:
3405 switch (GetEventKind (eventRef))
3407 case kEventMouseDown:
3408 eventRec->what = mouseDown;
3409 result = 1;
3410 break;
3412 case kEventMouseUp:
3413 eventRec->what = mouseUp;
3414 result = 1;
3415 break;
3417 default:
3418 break;
3420 break;
3422 case kEventClassKeyboard:
3423 switch (GetEventKind (eventRef))
3425 case kEventRawKeyDown:
3426 action = keyDown;
3427 goto keystroke_common;
3428 case kEventRawKeyRepeat:
3429 action = autoKey;
3430 goto keystroke_common;
3431 case kEventRawKeyUp:
3432 action = keyUp;
3433 keystroke_common:
3435 unsigned char char_codes;
3436 UInt32 key_code;
3438 err = GetEventParameter (eventRef, kEventParamKeyMacCharCodes,
3439 typeChar, NULL, sizeof (char),
3440 NULL, &char_codes);
3441 if (err == noErr)
3442 err = GetEventParameter (eventRef, kEventParamKeyCode,
3443 typeUInt32, NULL, sizeof (UInt32),
3444 NULL, &key_code);
3445 if (err == noErr)
3447 eventRec->what = action;
3448 eventRec->message = char_codes | ((key_code & 0xff) << 8);
3449 result = 1;
3452 break;
3454 default:
3455 break;
3457 break;
3459 default:
3460 break;
3463 if (result)
3465 /* Need where and when. */
3466 UInt32 mods = 0;
3468 GetEventParameter (eventRef, kEventParamMouseLocation, typeQDPoint,
3469 NULL, sizeof (Point), NULL, &eventRec->where);
3470 /* Use two step process because new event modifiers are 32-bit
3471 and old are 16-bit. Currently, only loss is NumLock & Fn. */
3472 GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32,
3473 NULL, sizeof (UInt32), NULL, &mods);
3474 eventRec->modifiers = mods;
3476 eventRec->when = EventTimeToTicks (GetEventTime (eventRef));
3479 return result;
3481 #endif /* TARGET_API_MAC_CARBON */
3483 #if !TARGET_API_MAC_CARBON
3484 static RgnHandle mouse_region = NULL;
3486 Boolean
3487 mac_wait_next_event (er, sleep_time, dequeue)
3488 EventRecord *er;
3489 UInt32 sleep_time;
3490 Boolean dequeue;
3492 static EventRecord er_buf = {nullEvent};
3493 UInt32 target_tick, current_tick;
3494 EventMask event_mask;
3496 if (mouse_region == NULL)
3497 mouse_region = NewRgn ();
3499 event_mask = everyEvent;
3500 if (!mac_ready_for_apple_events)
3501 event_mask -= highLevelEventMask;
3503 current_tick = TickCount ();
3504 target_tick = current_tick + sleep_time;
3506 if (er_buf.what == nullEvent)
3507 while (!WaitNextEvent (event_mask, &er_buf,
3508 target_tick - current_tick, mouse_region))
3510 current_tick = TickCount ();
3511 if (target_tick <= current_tick)
3512 return false;
3515 *er = er_buf;
3516 if (dequeue)
3517 er_buf.what = nullEvent;
3518 return true;
3520 #endif /* not TARGET_API_MAC_CARBON */
3522 #if TARGET_API_MAC_CARBON
3523 OSStatus
3524 mac_post_mouse_moved_event ()
3526 EventRef event = NULL;
3527 OSStatus err;
3529 err = CreateEvent (NULL, kEventClassMouse, kEventMouseMoved, 0,
3530 kEventAttributeNone, &event);
3531 if (err == noErr)
3533 Point mouse_pos;
3535 GetGlobalMouse (&mouse_pos);
3536 err = SetEventParameter (event, kEventParamMouseLocation, typeQDPoint,
3537 sizeof (Point), &mouse_pos);
3539 if (err == noErr)
3541 UInt32 modifiers = GetCurrentKeyModifiers ();
3543 err = SetEventParameter (event, kEventParamKeyModifiers, typeUInt32,
3544 sizeof (UInt32), &modifiers);
3546 if (err == noErr)
3547 err = PostEventToQueue (GetCurrentEventQueue (), event,
3548 kEventPriorityStandard);
3549 if (event)
3550 ReleaseEvent (event);
3552 return err;
3554 #endif
3556 #ifdef MAC_OSX
3557 /* Run the current run loop in the default mode until some input
3558 happens or TIMEOUT seconds passes unless it is negative. Return
3559 true if timeout occurs first. */
3561 Boolean
3562 mac_run_loop_run_once (timeout)
3563 EventTimeout timeout;
3565 #if USE_CG_DRAWING
3566 mac_prepare_for_quickdraw (NULL);
3567 #endif
3568 return (CFRunLoopRunInMode (kCFRunLoopDefaultMode,
3569 timeout >= 0 ? timeout : 100000, true)
3570 == kCFRunLoopRunTimedOut);
3572 #endif
3574 /* Emacs calls this whenever it wants to read an input event from the
3575 user. */
3578 XTread_socket (sd, expected, hold_quit)
3579 int sd, expected;
3580 struct input_event *hold_quit;
3582 struct input_event inev;
3583 int count = 0;
3584 #if TARGET_API_MAC_CARBON
3585 EventRef eventRef;
3586 EventTargetRef toolbox_dispatcher;
3587 #endif
3588 EventRecord er;
3589 struct mac_display_info *dpyinfo = &one_mac_display_info;
3591 if (interrupt_input_blocked)
3593 interrupt_input_pending = 1;
3594 return -1;
3597 interrupt_input_pending = 0;
3598 BLOCK_INPUT;
3600 /* So people can tell when we have read the available input. */
3601 input_signal_count++;
3603 ++handling_signal;
3605 #if TARGET_API_MAC_CARBON
3606 toolbox_dispatcher = GetEventDispatcherTarget ();
3608 while (
3609 #if USE_CG_DRAWING
3610 mac_prepare_for_quickdraw (NULL),
3611 #endif
3612 !ReceiveNextEvent (0, NULL, kEventDurationNoWait,
3613 kEventRemoveFromQueue, &eventRef))
3614 #else /* !TARGET_API_MAC_CARBON */
3615 while (mac_wait_next_event (&er, 0, true))
3616 #endif /* !TARGET_API_MAC_CARBON */
3618 int do_help = 0;
3619 struct frame *f;
3620 unsigned long timestamp;
3622 EVENT_INIT (inev);
3623 inev.kind = NO_EVENT;
3624 inev.arg = Qnil;
3626 #if TARGET_API_MAC_CARBON
3627 timestamp = GetEventTime (eventRef) / kEventDurationMillisecond;
3629 if (!mac_convert_event_ref (eventRef, &er))
3630 goto OTHER;
3631 #else /* !TARGET_API_MAC_CARBON */
3632 timestamp = er.when * (1000 / 60); /* ticks to milliseconds */
3633 #endif /* !TARGET_API_MAC_CARBON */
3635 switch (er.what)
3637 case mouseDown:
3638 case mouseUp:
3640 WindowRef window_ptr;
3641 ControlPartCode part_code;
3642 int tool_bar_p = 0;
3644 #if TARGET_API_MAC_CARBON
3645 OSStatus err;
3647 /* This is needed to send mouse events like aqua window
3648 buttons to the correct handler. */
3649 read_socket_inev = &inev;
3650 err = SendEventToEventTarget (eventRef, toolbox_dispatcher);
3651 read_socket_inev = NULL;
3652 if (err != eventNotHandledErr)
3653 break;
3654 #endif
3655 last_mouse_glyph_frame = 0;
3657 if (dpyinfo->grabbed && last_mouse_frame
3658 && FRAME_LIVE_P (last_mouse_frame))
3660 window_ptr = FRAME_MAC_WINDOW (last_mouse_frame);
3661 part_code = inContent;
3663 else
3665 part_code = FindWindow (er.where, &window_ptr);
3666 if (tip_window && window_ptr == tip_window)
3668 HideWindow (tip_window);
3669 part_code = FindWindow (er.where, &window_ptr);
3673 if (er.what != mouseDown
3674 && (part_code != inContent || dpyinfo->grabbed == 0))
3675 break;
3677 switch (part_code)
3679 case inMenuBar:
3680 f = mac_focus_frame (dpyinfo);
3681 saved_menu_event_location = er.where;
3682 inev.kind = MENU_BAR_ACTIVATE_EVENT;
3683 XSETFRAME (inev.frame_or_window, f);
3684 break;
3686 case inContent:
3687 if (
3688 #if TARGET_API_MAC_CARBON
3689 FrontNonFloatingWindow ()
3690 #else
3691 FrontWindow ()
3692 #endif
3693 != window_ptr
3694 || (mac_window_to_frame (window_ptr)
3695 != dpyinfo->x_focus_frame))
3696 SelectWindow (window_ptr);
3697 else
3699 ControlPartCode control_part_code;
3700 ControlRef ch;
3701 Point mouse_loc;
3702 #ifdef MAC_OSX
3703 ControlKind control_kind;
3704 #endif
3706 f = mac_window_to_frame (window_ptr);
3707 /* convert to local coordinates of new window */
3708 mouse_loc.h = (er.where.h
3709 - (f->left_pos
3710 + FRAME_OUTER_TO_INNER_DIFF_X (f)));
3711 mouse_loc.v = (er.where.v
3712 - (f->top_pos
3713 + FRAME_OUTER_TO_INNER_DIFF_Y (f)));
3714 #if TARGET_API_MAC_CARBON
3715 ch = FindControlUnderMouse (mouse_loc, window_ptr,
3716 &control_part_code);
3717 #ifdef MAC_OSX
3718 if (ch)
3719 GetControlKind (ch, &control_kind);
3720 #endif
3721 #else
3722 control_part_code = FindControl (mouse_loc, window_ptr,
3723 &ch);
3724 #endif
3726 #if TARGET_API_MAC_CARBON
3727 inev.code = mac_get_mouse_btn (eventRef);
3728 inev.modifiers = mac_event_to_emacs_modifiers (eventRef);
3729 #else
3730 inev.code = mac_get_emulated_btn (er.modifiers);
3731 inev.modifiers = mac_to_emacs_modifiers (er.modifiers, 0);
3732 #endif
3733 XSETINT (inev.x, mouse_loc.h);
3734 XSETINT (inev.y, mouse_loc.v);
3736 if ((dpyinfo->grabbed && tracked_scroll_bar)
3737 || (ch != 0
3738 #ifndef USE_TOOLKIT_SCROLL_BARS
3739 /* control_part_code becomes kControlNoPart if
3740 a progress indicator is clicked. */
3741 && control_part_code != kControlNoPart
3742 #else /* USE_TOOLKIT_SCROLL_BARS */
3743 #ifdef MAC_OSX
3744 && control_kind.kind == kControlKindScrollBar
3745 #endif /* MAC_OSX */
3746 #endif /* USE_TOOLKIT_SCROLL_BARS */
3749 struct scroll_bar *bar;
3751 if (dpyinfo->grabbed && tracked_scroll_bar)
3753 bar = tracked_scroll_bar;
3754 #ifndef USE_TOOLKIT_SCROLL_BARS
3755 control_part_code = kControlIndicatorPart;
3756 #endif
3758 else
3759 bar = (struct scroll_bar *) GetControlReference (ch);
3760 #ifdef USE_TOOLKIT_SCROLL_BARS
3761 /* Make the "Ctrl-Mouse-2 splits window" work
3762 for toolkit scroll bars. */
3763 if (inev.modifiers & ctrl_modifier)
3764 x_scroll_bar_handle_click (bar, control_part_code,
3765 &er, &inev);
3766 else if (er.what == mouseDown)
3767 x_scroll_bar_handle_press (bar, control_part_code,
3768 mouse_loc, &inev);
3769 else
3770 x_scroll_bar_handle_release (bar, &inev);
3771 #else /* not USE_TOOLKIT_SCROLL_BARS */
3772 x_scroll_bar_handle_click (bar, control_part_code,
3773 &er, &inev);
3774 if (er.what == mouseDown
3775 && control_part_code == kControlIndicatorPart)
3776 tracked_scroll_bar = bar;
3777 else
3778 tracked_scroll_bar = NULL;
3779 #endif /* not USE_TOOLKIT_SCROLL_BARS */
3781 else
3783 Lisp_Object window;
3784 int x = mouse_loc.h;
3785 int y = mouse_loc.v;
3787 window = window_from_coordinates (f, x, y, 0, 0, 0, 1);
3788 if (EQ (window, f->tool_bar_window))
3790 if (er.what == mouseDown)
3791 handle_tool_bar_click (f, x, y, 1, 0);
3792 else
3793 handle_tool_bar_click (f, x, y, 0,
3794 inev.modifiers);
3795 tool_bar_p = 1;
3797 else
3799 XSETFRAME (inev.frame_or_window, f);
3800 inev.kind = MOUSE_CLICK_EVENT;
3804 if (er.what == mouseDown)
3806 dpyinfo->grabbed |= (1 << inev.code);
3807 last_mouse_frame = f;
3809 if (!tool_bar_p)
3810 last_tool_bar_item = -1;
3812 else
3814 if ((dpyinfo->grabbed & (1 << inev.code)) == 0)
3815 /* If a button is released though it was not
3816 previously pressed, that would be because
3817 of multi-button emulation. */
3818 dpyinfo->grabbed = 0;
3819 else
3820 dpyinfo->grabbed &= ~(1 << inev.code);
3823 /* Ignore any mouse motion that happened before
3824 this event; any subsequent mouse-movement Emacs
3825 events should reflect only motion after the
3826 ButtonPress. */
3827 if (f != 0)
3828 f->mouse_moved = 0;
3830 #ifdef USE_TOOLKIT_SCROLL_BARS
3831 if (inev.kind == MOUSE_CLICK_EVENT
3832 || (inev.kind == SCROLL_BAR_CLICK_EVENT
3833 && (inev.modifiers & ctrl_modifier)))
3834 #endif
3835 switch (er.what)
3837 case mouseDown:
3838 inev.modifiers |= down_modifier;
3839 break;
3840 case mouseUp:
3841 inev.modifiers |= up_modifier;
3842 break;
3845 break;
3847 case inDrag:
3848 #if TARGET_API_MAC_CARBON
3849 case inProxyIcon:
3850 if (IsWindowPathSelectClick (window_ptr, &er))
3852 WindowPathSelect (window_ptr, NULL, NULL);
3853 break;
3855 if (part_code == inProxyIcon
3856 && (TrackWindowProxyDrag (window_ptr, er.where)
3857 != errUserWantsToDragWindow))
3858 break;
3859 DragWindow (window_ptr, er.where, NULL);
3860 #else /* not TARGET_API_MAC_CARBON */
3861 DragWindow (window_ptr, er.where, &qd.screenBits.bounds);
3862 /* Update the frame parameters. */
3864 struct frame *f = mac_window_to_frame (window_ptr);
3866 if (f && !f->async_iconified)
3867 mac_handle_origin_change (f);
3869 #endif /* not TARGET_API_MAC_CARBON */
3870 break;
3872 case inGoAway:
3873 if (TrackGoAway (window_ptr, er.where))
3875 inev.kind = DELETE_WINDOW_EVENT;
3876 XSETFRAME (inev.frame_or_window,
3877 mac_window_to_frame (window_ptr));
3879 break;
3881 /* window resize handling added --ben */
3882 case inGrow:
3883 do_grow_window (window_ptr, &er);
3884 break;
3886 /* window zoom handling added --ben */
3887 case inZoomIn:
3888 case inZoomOut:
3889 if (TrackBox (window_ptr, er.where, part_code))
3890 do_zoom_window (window_ptr, part_code);
3891 break;
3893 #if USE_MAC_TOOLBAR
3894 case inStructure:
3896 OSStatus err;
3897 HIViewRef ch;
3899 if (FrontNonFloatingWindow () != window_ptr)
3900 SelectWindow (window_ptr);
3902 err = HIViewGetViewForMouseEvent (HIViewGetRoot (window_ptr),
3903 eventRef, &ch);
3904 /* This doesn't work on Mac OS X 10.2. */
3905 if (err == noErr)
3906 HIViewClick (ch, eventRef);
3908 break;
3909 #endif /* USE_MAC_TOOLBAR */
3911 default:
3912 break;
3915 break;
3917 #if !TARGET_API_MAC_CARBON
3918 case updateEvt:
3919 do_window_update ((WindowRef) er.message);
3920 break;
3921 #endif
3923 case osEvt:
3924 #if TARGET_API_MAC_CARBON
3925 if (SendEventToEventTarget (eventRef, toolbox_dispatcher)
3926 != eventNotHandledErr)
3927 break;
3928 #endif
3929 switch ((er.message >> 24) & 0x000000FF)
3931 #if USE_MAC_TSM
3932 case suspendResumeMessage:
3933 if (er.message & resumeFlag)
3934 mac_tsm_resume ();
3935 else
3936 mac_tsm_suspend ();
3937 break;
3938 #endif
3940 case mouseMovedMessage:
3941 #if !TARGET_API_MAC_CARBON
3942 SetRectRgn (mouse_region, er.where.h, er.where.v,
3943 er.where.h + 1, er.where.v + 1);
3944 #endif
3945 previous_help_echo_string = help_echo_string;
3946 help_echo_string = Qnil;
3948 if (dpyinfo->grabbed && last_mouse_frame
3949 && FRAME_LIVE_P (last_mouse_frame))
3950 f = last_mouse_frame;
3951 else
3952 f = dpyinfo->x_focus_frame;
3954 if (dpyinfo->mouse_face_hidden)
3956 dpyinfo->mouse_face_hidden = 0;
3957 clear_mouse_face (dpyinfo);
3960 if (f)
3962 WindowRef wp = FRAME_MAC_WINDOW (f);
3963 Point mouse_pos;
3965 mouse_pos.h = (er.where.h
3966 - (f->left_pos
3967 + FRAME_OUTER_TO_INNER_DIFF_X (f)));
3968 mouse_pos.v = (er.where.v
3969 - (f->top_pos
3970 + FRAME_OUTER_TO_INNER_DIFF_Y (f)));
3971 if (dpyinfo->grabbed && tracked_scroll_bar)
3972 #ifdef USE_TOOLKIT_SCROLL_BARS
3973 x_scroll_bar_handle_drag (wp, tracked_scroll_bar,
3974 mouse_pos, &inev);
3975 #else /* not USE_TOOLKIT_SCROLL_BARS */
3976 x_scroll_bar_note_movement (tracked_scroll_bar,
3977 mouse_pos.v
3978 - XINT (tracked_scroll_bar->top),
3979 er.when * (1000 / 60));
3980 #endif /* not USE_TOOLKIT_SCROLL_BARS */
3981 else
3983 /* Generate SELECT_WINDOW_EVENTs when needed. */
3984 if (!NILP (Vmouse_autoselect_window))
3986 Lisp_Object window;
3988 window = window_from_coordinates (f,
3989 mouse_pos.h,
3990 mouse_pos.v,
3991 0, 0, 0, 0);
3993 /* Window will be selected only when it is
3994 not selected now and last mouse movement
3995 event was not in it. Minibuffer window
3996 will be selected only when it is active. */
3997 if (WINDOWP (window)
3998 && !EQ (window, last_window)
3999 && !EQ (window, selected_window)
4000 /* For click-to-focus window managers
4001 create event iff we don't leave the
4002 selected frame. */
4003 && (focus_follows_mouse
4004 || (EQ (XWINDOW (window)->frame,
4005 XWINDOW (selected_window)->frame))))
4007 inev.kind = SELECT_WINDOW_EVENT;
4008 inev.frame_or_window = window;
4011 last_window=window;
4013 if (!note_mouse_movement (f, &mouse_pos))
4014 help_echo_string = previous_help_echo_string;
4015 #if USE_MAC_TOOLBAR
4016 else
4017 mac_tool_bar_note_mouse_movement (f, eventRef);
4018 #endif
4022 /* If the contents of the global variable
4023 help_echo_string has changed, generate a
4024 HELP_EVENT. */
4025 if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
4026 do_help = 1;
4027 break;
4029 break;
4031 case activateEvt:
4033 WindowRef window_ptr = (WindowRef) er.message;
4034 OSErr err;
4035 ControlRef root_control;
4037 if (window_ptr == tip_window)
4039 HideWindow (tip_window);
4040 break;
4043 if (!is_emacs_window (window_ptr))
4044 goto OTHER;
4046 f = mac_window_to_frame (window_ptr);
4048 if ((er.modifiers & activeFlag) != 0)
4050 /* A window has been activated */
4051 Point mouse_loc;
4053 err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control);
4054 if (err == noErr)
4055 ActivateControl (root_control);
4057 x_detect_focus_change (dpyinfo, &er, &inev);
4059 mouse_loc.h = (er.where.h
4060 - (f->left_pos
4061 + FRAME_OUTER_TO_INNER_DIFF_X (f)));
4062 mouse_loc.v = (er.where.v
4063 - (f->top_pos
4064 + FRAME_OUTER_TO_INNER_DIFF_Y (f)));
4065 /* Window-activated event counts as mouse movement,
4066 so update things that depend on mouse position. */
4067 note_mouse_movement (f, &mouse_loc);
4069 else
4071 /* A window has been deactivated */
4072 err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control);
4073 if (err == noErr)
4074 DeactivateControl (root_control);
4076 #ifdef USE_TOOLKIT_SCROLL_BARS
4077 if (dpyinfo->grabbed && tracked_scroll_bar)
4079 struct input_event event;
4081 EVENT_INIT (event);
4082 event.kind = NO_EVENT;
4083 x_scroll_bar_handle_release (tracked_scroll_bar, &event);
4084 if (event.kind != NO_EVENT)
4086 event.timestamp = timestamp;
4087 kbd_buffer_store_event_hold (&event, hold_quit);
4088 count++;
4091 #endif
4092 dpyinfo->grabbed = 0;
4094 x_detect_focus_change (dpyinfo, &er, &inev);
4096 if (f == dpyinfo->mouse_face_mouse_frame)
4098 /* If we move outside the frame, then we're
4099 certainly no longer on any text in the
4100 frame. */
4101 clear_mouse_face (dpyinfo);
4102 dpyinfo->mouse_face_mouse_frame = 0;
4105 /* Generate a nil HELP_EVENT to cancel a help-echo.
4106 Do it only if there's something to cancel.
4107 Otherwise, the startup message is cleared when the
4108 mouse leaves the frame. */
4109 if (any_help_event_p)
4110 do_help = -1;
4113 break;
4115 case keyDown:
4116 case keyUp:
4117 case autoKey:
4118 ObscureCursor ();
4120 f = mac_focus_frame (dpyinfo);
4121 XSETFRAME (inev.frame_or_window, f);
4123 /* If mouse-highlight is an integer, input clears out mouse
4124 highlighting. */
4125 if (!dpyinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight)
4126 && !EQ (f->tool_bar_window, dpyinfo->mouse_face_window))
4128 clear_mouse_face (dpyinfo);
4129 dpyinfo->mouse_face_hidden = 1;
4133 UInt32 modifiers = er.modifiers, mapped_modifiers;
4134 UInt32 key_code = (er.message & keyCodeMask) >> 8;
4136 #ifdef MAC_OSX
4137 GetEventParameter (eventRef, kEventParamKeyModifiers,
4138 typeUInt32, NULL,
4139 sizeof (UInt32), NULL, &modifiers);
4140 #endif
4141 mapped_modifiers = mac_mapped_modifiers (modifiers, key_code);
4143 #if TARGET_API_MAC_CARBON
4144 if (!(mapped_modifiers
4145 & ~(mac_pass_command_to_system ? cmdKey : 0)
4146 & ~(mac_pass_control_to_system ? controlKey : 0)))
4147 goto OTHER;
4148 else
4149 #endif
4150 if (er.what != keyUp)
4151 do_keystroke (er.what, er.message & charCodeMask,
4152 key_code, modifiers, timestamp, &inev);
4154 break;
4156 case kHighLevelEvent:
4157 AEProcessAppleEvent (&er);
4158 break;
4160 default:
4161 OTHER:
4162 #if TARGET_API_MAC_CARBON
4164 OSStatus err;
4166 read_socket_inev = &inev;
4167 err = SendEventToEventTarget (eventRef, toolbox_dispatcher);
4168 read_socket_inev = NULL;
4170 #endif
4171 break;
4173 #if TARGET_API_MAC_CARBON
4174 ReleaseEvent (eventRef);
4175 #endif
4177 if (inev.kind != NO_EVENT)
4179 inev.timestamp = timestamp;
4180 kbd_buffer_store_event_hold (&inev, hold_quit);
4181 count++;
4184 if (do_help
4185 && !(hold_quit && hold_quit->kind != NO_EVENT))
4187 Lisp_Object frame;
4189 if (f)
4190 XSETFRAME (frame, f);
4191 else
4192 frame = Qnil;
4194 if (do_help > 0)
4196 any_help_event_p = 1;
4197 gen_help_event (help_echo_string, frame, help_echo_window,
4198 help_echo_object, help_echo_pos);
4200 else
4202 help_echo_string = Qnil;
4203 gen_help_event (Qnil, frame, Qnil, Qnil, 0);
4205 count++;
4209 /* If the focus was just given to an autoraising frame,
4210 raise it now. */
4211 /* ??? This ought to be able to handle more than one such frame. */
4212 if (pending_autoraise_frame)
4214 x_raise_frame (pending_autoraise_frame);
4215 pending_autoraise_frame = 0;
4218 if (mac_screen_config_changed)
4220 mac_get_screen_info (dpyinfo);
4221 mac_screen_config_changed = 0;
4224 #if !TARGET_API_MAC_CARBON
4225 /* Check which frames are still visible. We do this here because
4226 there doesn't seem to be any direct notification from the Window
4227 Manager that the visibility of a window has changed (at least,
4228 not in all cases). */
4230 Lisp_Object tail, frame;
4232 FOR_EACH_FRAME (tail, frame)
4234 struct frame *f = XFRAME (frame);
4236 /* The tooltip has been drawn already. Avoid the
4237 SET_FRAME_GARBAGED in mac_handle_visibility_change. */
4238 if (EQ (frame, tip_frame))
4239 continue;
4241 if (FRAME_MAC_P (f))
4242 mac_handle_visibility_change (f);
4245 #endif
4247 --handling_signal;
4248 UNBLOCK_INPUT;
4249 return count;
4253 /***********************************************************************
4254 Busy cursor
4255 ***********************************************************************/
4257 #if TARGET_API_MAC_CARBON
4258 /* Show the spinning progress indicator for the frame F. Create it if
4259 it doesn't exist yet. */
4261 void
4262 mac_show_hourglass (f)
4263 struct frame *f;
4265 #if USE_CG_DRAWING
4266 mac_prepare_for_quickdraw (f);
4267 #endif
4268 if (!f->output_data.mac->hourglass_control)
4270 Window w = FRAME_MAC_WINDOW (f);
4271 Rect r;
4272 ControlRef c;
4274 GetWindowPortBounds (w, &r);
4275 r.left = r.right - HOURGLASS_WIDTH;
4276 r.bottom = r.top + HOURGLASS_HEIGHT;
4277 if (CreateChasingArrowsControl (w, &r, &c) == noErr)
4278 f->output_data.mac->hourglass_control = c;
4281 if (f->output_data.mac->hourglass_control)
4282 ShowControl (f->output_data.mac->hourglass_control);
4285 /* Hide the spinning progress indicator for the frame F. Do nothing
4286 it doesn't exist yet. */
4288 void
4289 mac_hide_hourglass (f)
4290 struct frame *f;
4292 if (f->output_data.mac->hourglass_control)
4294 #if USE_CG_DRAWING
4295 mac_prepare_for_quickdraw (f);
4296 #endif
4297 HideControl (f->output_data.mac->hourglass_control);
4301 /* Reposition the spinning progress indicator for the frame F. Do
4302 nothing it doesn't exist yet. */
4304 void
4305 mac_reposition_hourglass (f)
4306 struct frame *f;
4308 if (f->output_data.mac->hourglass_control)
4310 #if USE_CG_DRAWING
4311 mac_prepare_for_quickdraw (f);
4312 #endif
4313 MoveControl (f->output_data.mac->hourglass_control,
4314 FRAME_PIXEL_WIDTH (f) - HOURGLASS_WIDTH, 0);
4317 #endif /* TARGET_API_MAC_CARBON */
4320 /***********************************************************************
4321 File selection dialog
4322 ***********************************************************************/
4324 #if TARGET_API_MAC_CARBON
4325 extern Lisp_Object Qfile_name_history;
4327 static pascal void mac_nav_event_callback P_ ((NavEventCallbackMessage,
4328 NavCBRecPtr, void *));
4330 /* The actual implementation of Fx_file_dialog. */
4332 Lisp_Object
4333 mac_file_dialog (prompt, dir, default_filename, mustmatch, only_dir_p)
4334 Lisp_Object prompt, dir, default_filename, mustmatch, only_dir_p;
4336 Lisp_Object file = Qnil;
4337 int count = SPECPDL_INDEX ();
4338 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6;
4339 char filename[MAXPATHLEN];
4340 static NavEventUPP mac_nav_event_callbackUPP = NULL;
4342 check_mac ();
4344 GCPRO6 (prompt, dir, default_filename, mustmatch, file, only_dir_p);
4345 CHECK_STRING (prompt);
4346 CHECK_STRING (dir);
4348 /* Create the dialog with PROMPT as title, using DIR as initial
4349 directory and using "*" as pattern. */
4350 dir = Fexpand_file_name (dir, Qnil);
4353 OSStatus status;
4354 NavDialogCreationOptions options;
4355 NavDialogRef dialogRef;
4356 NavTypeListHandle fileTypes = NULL;
4357 NavUserAction userAction;
4358 CFStringRef message=NULL, saveName = NULL;
4360 BLOCK_INPUT;
4361 /* No need for a callback function because we are modal */
4362 NavGetDefaultDialogCreationOptions(&options);
4363 options.modality = kWindowModalityAppModal;
4364 options.location.h = options.location.v = -1;
4365 options.optionFlags = kNavDefaultNavDlogOptions;
4366 options.optionFlags |= kNavAllFilesInPopup; /* All files allowed */
4367 options.optionFlags |= kNavSelectAllReadableItem;
4368 options.optionFlags &= ~kNavAllowMultipleFiles;
4369 if (!NILP(prompt))
4371 message = cfstring_create_with_string (prompt);
4372 options.message = message;
4374 /* Don't set the application, let it use default.
4375 options.clientName = CFSTR ("Emacs");
4378 if (mac_nav_event_callbackUPP == NULL)
4379 mac_nav_event_callbackUPP = NewNavEventUPP (mac_nav_event_callback);
4381 if (!NILP (only_dir_p))
4382 status = NavCreateChooseFolderDialog(&options, mac_nav_event_callbackUPP,
4383 NULL, NULL, &dialogRef);
4384 else if (NILP (mustmatch))
4386 /* This is a save dialog */
4387 options.optionFlags |= kNavDontConfirmReplacement;
4388 options.actionButtonLabel = CFSTR ("Ok");
4389 options.windowTitle = CFSTR ("Enter name");
4391 if (STRINGP (default_filename))
4393 Lisp_Object utf8 = ENCODE_UTF_8 (default_filename);
4394 char *begPtr = SDATA(utf8);
4395 char *filePtr = begPtr + SBYTES(utf8);
4396 while (filePtr != begPtr && !IS_DIRECTORY_SEP(filePtr[-1]))
4397 filePtr--;
4398 saveName = cfstring_create_with_utf8_cstring (filePtr);
4399 options.saveFileName = saveName;
4400 options.optionFlags |= kNavSelectDefaultLocation;
4402 status = NavCreatePutFileDialog(&options,
4403 'TEXT', kNavGenericSignature,
4404 mac_nav_event_callbackUPP, NULL,
4405 &dialogRef);
4407 else
4409 /* This is an open dialog*/
4410 status = NavCreateChooseFileDialog(&options, fileTypes,
4411 mac_nav_event_callbackUPP, NULL,
4412 NULL, NULL, &dialogRef);
4415 /* Set the default location and continue*/
4416 if (status == noErr)
4418 Lisp_Object encoded_dir = ENCODE_FILE (dir);
4419 AEDesc defLocAed;
4421 status = AECreateDesc (TYPE_FILE_NAME, SDATA (encoded_dir),
4422 SBYTES (encoded_dir), &defLocAed);
4423 if (status == noErr)
4425 NavCustomControl(dialogRef, kNavCtlSetLocation, (void*) &defLocAed);
4426 AEDisposeDesc(&defLocAed);
4428 status = NavDialogRun(dialogRef);
4431 if (saveName) CFRelease(saveName);
4432 if (message) CFRelease(message);
4434 if (status == noErr) {
4435 userAction = NavDialogGetUserAction(dialogRef);
4436 switch (userAction)
4438 case kNavUserActionNone:
4439 case kNavUserActionCancel:
4440 break; /* Treat cancel like C-g */
4441 case kNavUserActionOpen:
4442 case kNavUserActionChoose:
4443 case kNavUserActionSaveAs:
4445 NavReplyRecord reply;
4446 Size len;
4448 status = NavDialogGetReply(dialogRef, &reply);
4449 if (status != noErr)
4450 break;
4451 status = AEGetNthPtr (&reply.selection, 1, TYPE_FILE_NAME,
4452 NULL, NULL, filename,
4453 sizeof (filename) - 1, &len);
4454 if (status == noErr)
4456 len = min (len, sizeof (filename) - 1);
4457 filename[len] = '\0';
4458 if (reply.saveFileName)
4460 /* If it was a saved file, we need to add the file name */
4461 if (len && len < sizeof (filename) - 1
4462 && filename[len-1] != '/')
4463 filename[len++] = '/';
4464 CFStringGetCString(reply.saveFileName, filename+len,
4465 sizeof (filename) - len,
4466 #ifdef MAC_OSX
4467 kCFStringEncodingUTF8
4468 #else
4469 CFStringGetSystemEncoding ()
4470 #endif
4473 file = DECODE_FILE (make_unibyte_string (filename,
4474 strlen (filename)));
4476 NavDisposeReply(&reply);
4478 break;
4480 NavDialogDispose(dialogRef);
4481 UNBLOCK_INPUT;
4483 else {
4484 UNBLOCK_INPUT;
4485 /* Fall back on minibuffer if there was a problem */
4486 file = Fcompleting_read (prompt, intern ("read-file-name-internal"),
4487 dir, mustmatch, dir, Qfile_name_history,
4488 default_filename, Qnil);
4492 UNGCPRO;
4494 /* Make "Cancel" equivalent to C-g. */
4495 if (NILP (file))
4496 Fsignal (Qquit, Qnil);
4498 return unbind_to (count, file);
4501 /* Need to register some event callback function for enabling drag and
4502 drop in Navigation Service dialogs. */
4503 static pascal void
4504 mac_nav_event_callback (selector, parms, data)
4505 NavEventCallbackMessage selector;
4506 NavCBRecPtr parms;
4507 void *data;
4510 #endif
4513 /************************************************************************
4514 Menu
4515 ************************************************************************/
4517 #if !TARGET_API_MAC_CARBON
4518 #include <MacTypes.h>
4519 #include <Menus.h>
4520 #include <Quickdraw.h>
4521 #include <ToolUtils.h>
4522 #include <Fonts.h>
4523 #include <Controls.h>
4524 #include <Windows.h>
4525 #include <Events.h>
4526 #if defined (__MRC__) || (__MSL__ >= 0x6000)
4527 #include <ControlDefinitions.h>
4528 #endif
4529 #endif /* not TARGET_API_MAC_CARBON */
4531 extern int menu_item_selection;
4532 extern int popup_activated_flag;
4533 extern int name_is_separator P_ ((const char *));
4534 extern void find_and_call_menu_selection P_ ((FRAME_PTR, int, Lisp_Object,
4535 void *));
4536 extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
4538 enum mac_menu_kind { /* Menu ID range */
4539 MAC_MENU_APPLE, /* 0 (Reserved by Apple) */
4540 MAC_MENU_MENU_BAR, /* 1 .. 233 */
4541 MAC_MENU_M_APPLE, /* 234 (== M_APPLE) */
4542 MAC_MENU_POPUP, /* 235 */
4543 MAC_MENU_DRIVER, /* 236 .. 255 (Reserved) */
4544 MAC_MENU_MENU_BAR_SUB, /* 256 .. 16383 */
4545 MAC_MENU_POPUP_SUB, /* 16384 .. 32767 */
4546 MAC_MENU_END /* 32768 */
4549 static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768};
4551 static int fill_menu P_ ((MenuRef, widget_value *, enum mac_menu_kind, int));
4552 static void dispose_menus P_ ((enum mac_menu_kind, int));
4554 #if !TARGET_API_MAC_CARBON
4555 static void
4556 do_apple_menu (SInt16 menu_item)
4558 Str255 item_name;
4559 SInt16 da_driver_refnum;
4561 if (menu_item == I_ABOUT)
4562 NoteAlert (ABOUT_ALERT_ID, NULL);
4563 else
4565 GetMenuItemText (GetMenuRef (M_APPLE), menu_item, item_name);
4566 da_driver_refnum = OpenDeskAcc (item_name);
4569 #endif /* !TARGET_API_MAC_CARBON */
4571 /* Activate the menu bar of frame F.
4572 This is called from keyboard.c when it gets the
4573 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
4575 To activate the menu bar, we use the button-press event location
4576 that was saved in saved_menu_event_location.
4578 But first we recompute the menu bar contents (the whole tree).
4580 The reason for saving the button event until here, instead of
4581 passing it to the toolkit right away, is that we can safely
4582 execute Lisp code. */
4584 void
4585 x_activate_menubar (f)
4586 FRAME_PTR f;
4588 SInt32 menu_choice;
4589 SInt16 menu_id, menu_item;
4591 set_frame_menubar (f, 0, 1);
4592 BLOCK_INPUT;
4594 popup_activated_flag = 1;
4595 menu_choice = MenuSelect (saved_menu_event_location);
4596 popup_activated_flag = 0;
4597 menu_id = HiWord (menu_choice);
4598 menu_item = LoWord (menu_choice);
4600 #if !TARGET_API_MAC_CARBON
4601 if (menu_id == min_menu_id[MAC_MENU_M_APPLE])
4602 do_apple_menu (menu_item);
4603 else
4604 #endif
4605 if (menu_id)
4607 MenuRef menu = GetMenuRef (menu_id);
4609 if (menu)
4611 UInt32 refcon;
4613 GetMenuItemRefCon (menu, menu_item, &refcon);
4614 find_and_call_menu_selection (f, f->menu_bar_items_used,
4615 f->menu_bar_vector, (void *) refcon);
4619 HiliteMenu (0);
4621 UNBLOCK_INPUT;
4624 #if TARGET_API_MAC_CARBON
4625 extern Lisp_Object Vshow_help_function;
4627 static Lisp_Object
4628 restore_show_help_function (old_show_help_function)
4629 Lisp_Object old_show_help_function;
4631 Vshow_help_function = old_show_help_function;
4633 return Qnil;
4636 static pascal OSStatus
4637 menu_target_item_handler (next_handler, event, data)
4638 EventHandlerCallRef next_handler;
4639 EventRef event;
4640 void *data;
4642 OSStatus err;
4643 MenuRef menu;
4644 MenuItemIndex menu_item;
4645 Lisp_Object help;
4646 GrafPtr port;
4647 int specpdl_count = SPECPDL_INDEX ();
4649 /* Don't be bothered with the overflowed toolbar items menu. */
4650 if (!popup_activated ())
4651 return eventNotHandledErr;
4653 err = GetEventParameter (event, kEventParamDirectObject, typeMenuRef,
4654 NULL, sizeof (MenuRef), NULL, &menu);
4655 if (err == noErr)
4656 err = GetEventParameter (event, kEventParamMenuItemIndex,
4657 typeMenuItemIndex, NULL,
4658 sizeof (MenuItemIndex), NULL, &menu_item);
4659 if (err == noErr)
4660 err = GetMenuItemProperty (menu, menu_item,
4661 MAC_EMACS_CREATOR_CODE, 'help',
4662 sizeof (Lisp_Object), NULL, &help);
4663 if (err != noErr)
4664 help = Qnil;
4666 /* Temporarily bind Vshow_help_function to Qnil because we don't
4667 want tooltips during menu tracking. */
4668 record_unwind_protect (restore_show_help_function, Vshow_help_function);
4669 Vshow_help_function = Qnil;
4670 GetPort (&port);
4671 show_help_echo (help, Qnil, Qnil, Qnil, 1);
4672 SetPort (port);
4673 unbind_to (specpdl_count, Qnil);
4675 return err == noErr ? noErr : eventNotHandledErr;
4678 /* Showing help echo string during menu tracking. */
4680 static OSStatus
4681 install_menu_target_item_handler ()
4683 static const EventTypeSpec specs[] =
4684 {{kEventClassMenu, kEventMenuTargetItem}};
4686 return InstallApplicationEventHandler (NewEventHandlerUPP
4687 (menu_target_item_handler),
4688 GetEventTypeCount (specs),
4689 specs, NULL, NULL);
4691 #endif /* TARGET_API_MAC_CARBON */
4693 /* Event handler function that pops down a menu on C-g. We can only pop
4694 down menus if CancelMenuTracking is present (OSX 10.3 or later). */
4696 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
4697 static pascal OSStatus
4698 menu_quit_handler (nextHandler, theEvent, userData)
4699 EventHandlerCallRef nextHandler;
4700 EventRef theEvent;
4701 void* userData;
4703 OSStatus err;
4704 UInt32 keyCode;
4705 UInt32 keyModifiers;
4707 err = GetEventParameter (theEvent, kEventParamKeyCode,
4708 typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
4710 if (err == noErr)
4711 err = GetEventParameter (theEvent, kEventParamKeyModifiers,
4712 typeUInt32, NULL, sizeof(UInt32),
4713 NULL, &keyModifiers);
4715 if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode))
4717 MenuRef menu = userData != 0
4718 ? (MenuRef)userData : AcquireRootMenu ();
4720 CancelMenuTracking (menu, true, 0);
4721 if (!userData) ReleaseMenu (menu);
4722 return noErr;
4725 return CallNextEventHandler (nextHandler, theEvent);
4727 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
4729 /* Add event handler to all menus that belong to KIND so we can detect
4730 C-g. ROOT_MENU is the root menu of the tracking session to dismiss
4731 when C-g is detected. NULL means the menu bar. If
4732 CancelMenuTracking isn't available, do nothing. */
4734 static void
4735 install_menu_quit_handler (kind, root_menu)
4736 enum mac_menu_kind kind;
4737 MenuRef root_menu;
4739 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
4740 static const EventTypeSpec typesList[] =
4741 {{kEventClassKeyboard, kEventRawKeyDown}};
4742 int id;
4744 #if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
4745 if (CancelMenuTracking == NULL)
4746 return;
4747 #endif
4748 for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++)
4750 MenuRef menu = GetMenuRef (id);
4752 if (menu == NULL)
4753 break;
4754 InstallMenuEventHandler (menu, menu_quit_handler,
4755 GetEventTypeCount (typesList),
4756 typesList, root_menu, NULL);
4758 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
4761 static Lisp_Object
4762 pop_down_menu (arg)
4763 Lisp_Object arg;
4765 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
4766 FRAME_PTR f = p->pointer;
4767 MenuRef menu = GetMenuRef (min_menu_id[MAC_MENU_POPUP]);
4769 BLOCK_INPUT;
4771 /* Must reset this manually because the button release event is not
4772 passed to Emacs event loop. */
4773 FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
4775 /* delete all menus */
4776 dispose_menus (MAC_MENU_POPUP_SUB, 0);
4777 DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
4778 DisposeMenu (menu);
4780 UNBLOCK_INPUT;
4782 return Qnil;
4785 /* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop
4786 until the menu pops down. Return the selection. */
4788 void
4789 create_and_show_popup_menu (f, first_wv, x, y, for_click)
4790 FRAME_PTR f;
4791 widget_value *first_wv;
4792 int x;
4793 int y;
4794 int for_click;
4796 int result = 0;
4797 MenuRef menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p");
4798 int menu_item_choice;
4799 int specpdl_count = SPECPDL_INDEX ();
4801 InsertMenu (menu, -1);
4802 fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB,
4803 min_menu_id[MAC_MENU_POPUP_SUB]);
4805 /* Add event handler so we can detect C-g. */
4806 install_menu_quit_handler (MAC_MENU_POPUP, menu);
4807 install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu);
4809 record_unwind_protect (pop_down_menu, make_save_value (f, 0));
4811 /* Adjust coordinates to be root-window-relative. */
4812 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
4813 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
4815 /* Display the menu. */
4816 popup_activated_flag = 1;
4817 menu_item_choice = PopUpMenuSelect (menu, y, x, 0);
4818 popup_activated_flag = 0;
4820 /* Get the refcon to find the correct item */
4821 if (menu_item_choice)
4823 MenuRef sel_menu = GetMenuRef (HiWord (menu_item_choice));
4825 if (sel_menu)
4826 GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice),
4827 (UInt32 *) &result);
4830 unbind_to (specpdl_count, Qnil);
4832 menu_item_selection = result;
4835 static void
4836 add_menu_item (menu, pos, wv)
4837 MenuRef menu;
4838 int pos;
4839 widget_value *wv;
4841 #if TARGET_API_MAC_CARBON
4842 CFStringRef item_name;
4843 #else
4844 Str255 item_name;
4845 #endif
4847 if (name_is_separator (wv->name))
4848 AppendMenu (menu, "\p-");
4849 else
4851 AppendMenu (menu, "\pX");
4853 #if TARGET_API_MAC_CARBON
4854 item_name = cfstring_create_with_utf8_cstring (wv->name);
4856 if (wv->key != NULL)
4858 CFStringRef name, key;
4860 name = item_name;
4861 key = cfstring_create_with_utf8_cstring (wv->key);
4862 item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"),
4863 name, key);
4864 CFRelease (name);
4865 CFRelease (key);
4868 SetMenuItemTextWithCFString (menu, pos, item_name);
4869 CFRelease (item_name);
4871 if (wv->enabled)
4872 EnableMenuItem (menu, pos);
4873 else
4874 DisableMenuItem (menu, pos);
4876 if (STRINGP (wv->help))
4877 SetMenuItemProperty (menu, pos, MAC_EMACS_CREATOR_CODE, 'help',
4878 sizeof (Lisp_Object), &wv->help);
4879 #else /* ! TARGET_API_MAC_CARBON */
4880 item_name[sizeof (item_name) - 1] = '\0';
4881 strncpy (item_name, wv->name, sizeof (item_name) - 1);
4882 if (wv->key != NULL)
4884 int len = strlen (item_name);
4886 strncpy (item_name + len, " ", sizeof (item_name) - 1 - len);
4887 len = strlen (item_name);
4888 strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len);
4890 c2pstr (item_name);
4891 SetMenuItemText (menu, pos, item_name);
4893 if (wv->enabled)
4894 EnableItem (menu, pos);
4895 else
4896 DisableItem (menu, pos);
4897 #endif /* ! TARGET_API_MAC_CARBON */
4899 /* Draw radio buttons and tickboxes. */
4900 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE
4901 || wv->button_type == BUTTON_TYPE_RADIO))
4902 SetItemMark (menu, pos, checkMark);
4903 else
4904 SetItemMark (menu, pos, noMark);
4906 SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
4910 /* Construct native Mac OS menu based on widget_value tree. */
4912 static int
4913 fill_menu (menu, wv, kind, submenu_id)
4914 MenuRef menu;
4915 widget_value *wv;
4916 enum mac_menu_kind kind;
4917 int submenu_id;
4919 int pos;
4921 for (pos = 1; wv != NULL; wv = wv->next, pos++)
4923 add_menu_item (menu, pos, wv);
4924 if (wv->contents && submenu_id < min_menu_id[kind + 1])
4926 MenuRef submenu = NewMenu (submenu_id, "\pX");
4928 InsertMenu (submenu, -1);
4929 #if TARGET_API_MAC_CARBON
4930 SetMenuItemHierarchicalMenu (menu, pos, submenu);
4931 #else
4932 SetMenuItemHierarchicalID (menu, pos, submenu_id);
4933 #endif
4934 submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
4938 return submenu_id;
4941 /* Fill menu bar with the items defined by WV. If DEEP_P, consider
4942 the entire menu trees we supply, rather than just the menu bar item
4943 names. */
4945 void
4946 mac_fill_menubar (wv, deep_p)
4947 widget_value *wv;
4948 int deep_p;
4950 int id, submenu_id;
4951 #if !TARGET_API_MAC_CARBON
4952 int title_changed_p = 0;
4953 #endif
4955 /* Clean up the menu bar when filled by the entire menu trees. */
4956 if (deep_p)
4958 dispose_menus (MAC_MENU_MENU_BAR, 0);
4959 dispose_menus (MAC_MENU_MENU_BAR_SUB, 0);
4960 #if !TARGET_API_MAC_CARBON
4961 title_changed_p = 1;
4962 #endif
4965 /* Fill menu bar titles and submenus. Reuse the existing menu bar
4966 titles as much as possible to minimize redraw (if !deep_p). */
4967 submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB];
4968 for (id = min_menu_id[MAC_MENU_MENU_BAR];
4969 wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1];
4970 wv = wv->next, id++)
4972 OSStatus err = noErr;
4973 MenuRef menu;
4974 #if TARGET_API_MAC_CARBON
4975 CFStringRef title;
4977 title = CFStringCreateWithCString (NULL, wv->name,
4978 kCFStringEncodingMacRoman);
4979 #else
4980 Str255 title;
4982 strncpy (title, wv->name, 255);
4983 title[255] = '\0';
4984 c2pstr (title);
4985 #endif
4987 menu = GetMenuRef (id);
4988 if (menu)
4990 #if TARGET_API_MAC_CARBON
4991 CFStringRef old_title;
4993 err = CopyMenuTitleAsCFString (menu, &old_title);
4994 if (err == noErr)
4996 if (CFStringCompare (title, old_title, 0) != kCFCompareEqualTo)
4998 #ifdef MAC_OSX
4999 if (id + 1 == min_menu_id[MAC_MENU_MENU_BAR + 1]
5000 || GetMenuRef (id + 1) == NULL)
5002 /* This is a workaround for Mac OS X 10.5 where
5003 just calling SetMenuTitleWithCFString fails
5004 to change the title of the last (Help) menu
5005 in the menu bar. */
5006 DeleteMenu (id);
5007 DisposeMenu (menu);
5008 menu = NULL;
5010 else
5011 #endif /* MAC_OSX */
5012 err = SetMenuTitleWithCFString (menu, title);
5014 CFRelease (old_title);
5016 else
5017 err = SetMenuTitleWithCFString (menu, title);
5018 #else /* !TARGET_API_MAC_CARBON */
5019 if (!EqualString (title, (*menu)->menuData, false, false))
5021 DeleteMenu (id);
5022 DisposeMenu (menu);
5023 menu = NewMenu (id, title);
5024 InsertMenu (menu, GetMenuRef (id + 1) ? id + 1 : 0);
5025 title_changed_p = 1;
5027 #endif /* !TARGET_API_MAC_CARBON */
5030 if (!menu)
5032 #if TARGET_API_MAC_CARBON
5033 err = CreateNewMenu (id, 0, &menu);
5034 if (err == noErr)
5035 err = SetMenuTitleWithCFString (menu, title);
5036 #else
5037 menu = NewMenu (id, title);
5038 #endif
5039 if (err == noErr)
5041 InsertMenu (menu, 0);
5042 #if !TARGET_API_MAC_CARBON
5043 title_changed_p = 1;
5044 #endif
5047 #if TARGET_API_MAC_CARBON
5048 CFRelease (title);
5049 #endif
5051 if (err == noErr)
5052 if (wv->contents)
5053 submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
5054 submenu_id);
5057 if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuRef (id))
5059 dispose_menus (MAC_MENU_MENU_BAR, id);
5060 #if !TARGET_API_MAC_CARBON
5061 title_changed_p = 1;
5062 #endif
5065 #if !TARGET_API_MAC_CARBON
5066 if (title_changed_p)
5067 InvalMenuBar ();
5068 #endif
5070 /* Add event handler so we can detect C-g. */
5071 install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL);
5072 install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL);
5075 /* Dispose of menus that belong to KIND, and remove them from the menu
5076 list. ID is the lower bound of menu IDs that will be processed. */
5078 static void
5079 dispose_menus (kind, id)
5080 enum mac_menu_kind kind;
5081 int id;
5083 for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++)
5085 MenuRef menu = GetMenuRef (id);
5087 if (menu == NULL)
5088 break;
5089 DeleteMenu (id);
5090 DisposeMenu (menu);
5094 static void
5095 init_menu_bar ()
5097 #ifdef MAC_OSX
5098 OSStatus err;
5099 MenuRef menu;
5100 MenuItemIndex menu_index;
5102 err = GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
5103 &menu, &menu_index);
5104 if (err == noErr)
5105 SetMenuItemCommandKey (menu, menu_index, false, 0);
5106 EnableMenuCommand (NULL, kHICommandPreferences);
5107 err = GetIndMenuItemWithCommandID (NULL, kHICommandPreferences, 1,
5108 &menu, &menu_index);
5109 if (err == noErr)
5111 SetMenuItemCommandKey (menu, menu_index, false, 0);
5112 InsertMenuItemTextWithCFString (menu, NULL,
5113 0, kMenuItemAttrSeparator, 0);
5114 InsertMenuItemTextWithCFString (menu, CFSTR ("About Emacs"),
5115 0, 0, kHICommandAbout);
5117 #else /* !MAC_OSX */
5118 #if TARGET_API_MAC_CARBON
5119 SetMenuItemCommandID (GetMenuRef (M_APPLE), I_ABOUT, kHICommandAbout);
5120 #endif
5121 #endif
5125 /***********************************************************************
5126 Popup Dialog
5127 ***********************************************************************/
5129 #if TARGET_API_MAC_CARBON
5130 #define DIALOG_BUTTON_COMMAND_ID_OFFSET 'Bt\0\0'
5131 #define DIALOG_BUTTON_COMMAND_ID_P(id) \
5132 (((id) & ~0xffff) == DIALOG_BUTTON_COMMAND_ID_OFFSET)
5133 #define DIALOG_BUTTON_COMMAND_ID_VALUE(id) \
5134 ((id) - DIALOG_BUTTON_COMMAND_ID_OFFSET)
5135 #define DIALOG_BUTTON_MAKE_COMMAND_ID(value) \
5136 ((value) + DIALOG_BUTTON_COMMAND_ID_OFFSET)
5138 extern EMACS_TIME timer_check P_ ((int));
5139 static int quit_dialog_event_loop;
5141 static pascal OSStatus
5142 mac_handle_dialog_event (next_handler, event, data)
5143 EventHandlerCallRef next_handler;
5144 EventRef event;
5145 void *data;
5147 OSStatus err, result = eventNotHandledErr;
5148 WindowRef window = (WindowRef) data;
5150 switch (GetEventClass (event))
5152 case kEventClassCommand:
5154 HICommand command;
5156 err = GetEventParameter (event, kEventParamDirectObject,
5157 typeHICommand, NULL, sizeof (HICommand),
5158 NULL, &command);
5159 if (err == noErr)
5160 if (DIALOG_BUTTON_COMMAND_ID_P (command.commandID))
5162 SetWRefCon (window, command.commandID);
5163 quit_dialog_event_loop = 1;
5164 break;
5167 result = CallNextEventHandler (next_handler, event);
5169 break;
5171 case kEventClassKeyboard:
5173 OSStatus result;
5174 char char_code;
5176 result = CallNextEventHandler (next_handler, event);
5177 if (result != eventNotHandledErr)
5178 break;
5180 err = GetEventParameter (event, kEventParamKeyMacCharCodes,
5181 typeChar, NULL, sizeof (char),
5182 NULL, &char_code);
5183 if (err == noErr)
5184 switch (char_code)
5186 case kEscapeCharCode:
5187 quit_dialog_event_loop = 1;
5188 break;
5190 default:
5192 UInt32 modifiers, key_code;
5194 err = GetEventParameter (event, kEventParamKeyModifiers,
5195 typeUInt32, NULL, sizeof (UInt32),
5196 NULL, &modifiers);
5197 if (err == noErr)
5198 err = GetEventParameter (event, kEventParamKeyCode,
5199 typeUInt32, NULL, sizeof (UInt32),
5200 NULL, &key_code);
5201 if (err == noErr)
5202 if (mac_quit_char_key_p (modifiers, key_code))
5203 quit_dialog_event_loop = 1;
5205 break;
5208 break;
5210 default:
5211 abort ();
5214 if (quit_dialog_event_loop)
5216 err = QuitEventLoop (GetCurrentEventLoop ());
5217 if (err == noErr)
5218 result = noErr;
5221 return result;
5224 static OSStatus
5225 install_dialog_event_handler (window)
5226 WindowRef window;
5228 static const EventTypeSpec specs[] =
5229 {{kEventClassCommand, kEventCommandProcess},
5230 {kEventClassKeyboard, kEventRawKeyDown}};
5231 static EventHandlerUPP handle_dialog_eventUPP = NULL;
5233 if (handle_dialog_eventUPP == NULL)
5234 handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event);
5235 return InstallWindowEventHandler (window, handle_dialog_eventUPP,
5236 GetEventTypeCount (specs), specs,
5237 window, NULL);
5240 static Lisp_Object
5241 pop_down_dialog (arg)
5242 Lisp_Object arg;
5244 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
5245 WindowRef window = p->pointer;
5247 BLOCK_INPUT;
5249 if (popup_activated_flag)
5250 EndAppModalStateForWindow (window);
5251 DisposeWindow (window);
5252 popup_activated_flag = 0;
5254 UNBLOCK_INPUT;
5256 return Qnil;
5259 /* Pop up the dialog for frame F defined by FIRST_WV and loop until the
5260 dialog pops down.
5261 menu_item_selection will be set to the selection. */
5263 void
5264 create_and_show_dialog (f, first_wv)
5265 FRAME_PTR f;
5266 widget_value *first_wv;
5268 OSStatus err;
5269 char *dialog_name, *message;
5270 int nb_buttons, first_group_count, i, result = 0;
5271 widget_value *wv;
5272 short buttons_height, text_height, inner_width, inner_height;
5273 Rect empty_rect, *rects;
5274 WindowRef window = NULL;
5275 ControlRef *buttons, default_button = NULL, text;
5276 int specpdl_count = SPECPDL_INDEX ();
5278 dialog_name = first_wv->name;
5279 nb_buttons = dialog_name[1] - '0';
5280 first_group_count = nb_buttons - (dialog_name[4] - '0');
5282 wv = first_wv->contents;
5283 message = wv->value;
5285 wv = wv->next;
5286 SetRect (&empty_rect, 0, 0, 0, 0);
5288 /* Create dialog window. */
5289 err = CreateNewWindow (kMovableModalWindowClass,
5290 kWindowStandardHandlerAttribute,
5291 &empty_rect, &window);
5292 if (err == noErr)
5294 record_unwind_protect (pop_down_dialog, make_save_value (window, 0));
5295 err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground,
5296 true);
5298 if (err == noErr)
5299 err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q'
5300 ? CFSTR ("Question")
5301 : CFSTR ("Information")));
5303 /* Create button controls and measure their optimal bounds. */
5304 if (err == noErr)
5306 buttons = alloca (sizeof (ControlRef) * nb_buttons);
5307 rects = alloca (sizeof (Rect) * nb_buttons);
5308 for (i = 0; i < nb_buttons; i++)
5310 CFStringRef label = cfstring_create_with_utf8_cstring (wv->value);
5312 if (label == NULL)
5313 err = memFullErr;
5314 else
5316 err = CreatePushButtonControl (window, &empty_rect,
5317 label, &buttons[i]);
5318 CFRelease (label);
5320 if (err == noErr)
5322 if (!wv->enabled)
5324 #ifdef MAC_OSX
5325 err = DisableControl (buttons[i]);
5326 #else
5327 err = DeactivateControl (buttons[i]);
5328 #endif
5330 else if (default_button == NULL)
5331 default_button = buttons[i];
5333 if (err == noErr)
5335 SInt16 unused;
5337 rects[i] = empty_rect;
5338 err = GetBestControlRect (buttons[i], &rects[i], &unused);
5340 if (err == noErr)
5342 UInt32 command_id;
5344 OffsetRect (&rects[i], -rects[i].left, -rects[i].top);
5345 if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH)
5346 rects[i].right = DIALOG_BUTTON_MIN_WIDTH;
5347 else if (rects[i].right > DIALOG_MAX_INNER_WIDTH)
5348 rects[i].right = DIALOG_MAX_INNER_WIDTH;
5350 command_id = DIALOG_BUTTON_MAKE_COMMAND_ID ((int) wv->call_data);
5351 err = SetControlCommandID (buttons[i], command_id);
5353 if (err != noErr)
5354 break;
5355 wv = wv->next;
5359 /* Layout buttons. rects[i] is set relative to the bottom-right
5360 corner of the inner box. */
5361 if (err == noErr)
5363 short bottom, right, max_height, left_align_shift;
5365 inner_width = DIALOG_MIN_INNER_WIDTH;
5366 bottom = right = max_height = 0;
5367 for (i = 0; i < nb_buttons; i++)
5369 if (right - rects[i].right < - inner_width)
5371 if (i != first_group_count
5372 && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH)
5373 inner_width = - (right - rects[i].right);
5374 else
5376 bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE;
5377 right = max_height = 0;
5380 if (max_height < rects[i].bottom)
5381 max_height = rects[i].bottom;
5382 OffsetRect (&rects[i], right - rects[i].right,
5383 bottom - rects[i].bottom);
5384 right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
5385 if (i == first_group_count - 1)
5386 right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
5388 buttons_height = - (bottom - max_height);
5390 left_align_shift = - (inner_width + rects[nb_buttons - 1].left);
5391 for (i = nb_buttons - 1; i >= first_group_count; i--)
5393 if (bottom != rects[i].bottom)
5395 left_align_shift = - (inner_width + rects[i].left);
5396 bottom = rects[i].bottom;
5398 OffsetRect (&rects[i], left_align_shift, 0);
5402 /* Create a static text control and measure its bounds. */
5403 if (err == noErr)
5405 CFStringRef message_string;
5406 Rect bounds;
5408 message_string = cfstring_create_with_utf8_cstring (message);
5409 if (message_string == NULL)
5410 err = memFullErr;
5411 else
5413 ControlFontStyleRec text_style;
5415 text_style.flags = 0;
5416 SetRect (&bounds, 0, 0, inner_width, 0);
5417 err = CreateStaticTextControl (window, &bounds, message_string,
5418 &text_style, &text);
5419 CFRelease (message_string);
5421 if (err == noErr)
5423 SInt16 unused;
5425 bounds = empty_rect;
5426 err = GetBestControlRect (text, &bounds, &unused);
5428 if (err == noErr)
5430 text_height = bounds.bottom - bounds.top;
5431 if (text_height < DIALOG_TEXT_MIN_HEIGHT)
5432 text_height = DIALOG_TEXT_MIN_HEIGHT;
5436 /* Place buttons. */
5437 if (err == noErr)
5439 inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE
5440 + buttons_height);
5442 for (i = 0; i < nb_buttons; i++)
5444 OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width,
5445 DIALOG_TOP_MARGIN + inner_height);
5446 SetControlBounds (buttons[i], &rects[i]);
5450 /* Place text. */
5451 if (err == noErr)
5453 Rect bounds;
5455 SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN,
5456 DIALOG_LEFT_MARGIN + inner_width,
5457 DIALOG_TOP_MARGIN + text_height);
5458 SetControlBounds (text, &bounds);
5461 /* Create the application icon at the upper-left corner. */
5462 if (err == noErr)
5464 ControlButtonContentInfo content;
5465 ControlRef icon;
5466 static const ProcessSerialNumber psn = {0, kCurrentProcess};
5467 #ifdef MAC_OSX
5468 FSRef app_location;
5469 #else
5470 ProcessInfoRec pinfo;
5471 FSSpec app_spec;
5472 #endif
5473 SInt16 unused;
5475 content.contentType = kControlContentIconRef;
5476 #ifdef MAC_OSX
5477 err = GetProcessBundleLocation (&psn, &app_location);
5478 if (err == noErr)
5479 err = GetIconRefFromFileInfo (&app_location, 0, NULL, 0, NULL,
5480 kIconServicesNormalUsageFlag,
5481 &content.u.iconRef, &unused);
5482 #else
5483 bzero (&pinfo, sizeof (ProcessInfoRec));
5484 pinfo.processInfoLength = sizeof (ProcessInfoRec);
5485 pinfo.processAppSpec = &app_spec;
5486 err = GetProcessInformation (&psn, &pinfo);
5487 if (err == noErr)
5488 err = GetIconRefFromFile (&app_spec, &content.u.iconRef, &unused);
5489 #endif
5490 if (err == noErr)
5492 Rect bounds;
5494 SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN,
5495 DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH,
5496 DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT);
5497 err = CreateIconControl (window, &bounds, &content, true, &icon);
5498 ReleaseIconRef (content.u.iconRef);
5502 /* Show the dialog window and run event loop. */
5503 if (err == noErr)
5504 if (default_button)
5505 err = SetWindowDefaultButton (window, default_button);
5506 if (err == noErr)
5507 err = install_dialog_event_handler (window);
5508 if (err == noErr)
5510 SizeWindow (window,
5511 DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN,
5512 DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN,
5513 true);
5514 err = RepositionWindow (window, FRAME_MAC_WINDOW (f),
5515 kWindowAlertPositionOnParentWindow);
5517 if (err == noErr)
5519 SetWRefCon (window, 0);
5520 ShowWindow (window);
5521 BringToFront (window);
5522 popup_activated_flag = 1;
5523 err = BeginAppModalStateForWindow (window);
5525 if (err == noErr)
5527 EventTargetRef toolbox_dispatcher = GetEventDispatcherTarget ();
5529 quit_dialog_event_loop = 0;
5530 while (1)
5532 EMACS_TIME next_time = timer_check (1);
5533 long secs = EMACS_SECS (next_time);
5534 long usecs = EMACS_USECS (next_time);
5535 EventTimeout timeout;
5536 EventRef event;
5538 if (secs < 0 || (secs == 0 && usecs == 0))
5540 /* Sometimes timer_check returns -1 (no timers) even if
5541 there are timers. So do a timeout anyway. */
5542 secs = 1;
5543 usecs = 0;
5546 timeout = (secs * kEventDurationSecond
5547 + usecs * kEventDurationMicrosecond);
5548 err = ReceiveNextEvent (0, NULL, timeout, kEventRemoveFromQueue,
5549 &event);
5550 if (err == noErr)
5552 SendEventToEventTarget (event, toolbox_dispatcher);
5553 ReleaseEvent (event);
5555 #if 0 /* defined (MAC_OSX) */
5556 else if (err != eventLoopTimedOutErr)
5558 if (err == eventLoopQuitErr)
5559 err = noErr;
5560 break;
5562 #else
5563 /* The return value of ReceiveNextEvent seems to be
5564 unreliable. Use our own global variable instead. */
5565 if (quit_dialog_event_loop)
5567 err = noErr;
5568 break;
5570 #endif
5573 if (err == noErr)
5575 UInt32 command_id = GetWRefCon (window);
5577 if (DIALOG_BUTTON_COMMAND_ID_P (command_id))
5578 result = DIALOG_BUTTON_COMMAND_ID_VALUE (command_id);
5581 unbind_to (specpdl_count, Qnil);
5583 menu_item_selection = result;
5585 #else /* not TARGET_API_MAC_CARBON */
5586 #define DIALOG_WINDOW_RESOURCE 130
5589 mac_dialog (widget_value *wv)
5591 char *dialog_name;
5592 char *prompt;
5593 char **button_labels;
5594 UInt32 *ref_cons;
5595 int nb_buttons;
5596 int left_count;
5597 int i;
5598 int dialog_width;
5599 Rect rect;
5600 WindowRef window_ptr;
5601 ControlRef ch;
5602 int left;
5603 EventRecord event_record;
5604 SInt16 part_code;
5605 int control_part_code;
5606 Point mouse;
5608 dialog_name = wv->name;
5609 nb_buttons = dialog_name[1] - '0';
5610 left_count = nb_buttons - (dialog_name[4] - '0');
5611 button_labels = (char **) alloca (sizeof (char *) * nb_buttons);
5612 ref_cons = (UInt32 *) alloca (sizeof (UInt32) * nb_buttons);
5614 wv = wv->contents;
5615 prompt = (char *) alloca (strlen (wv->value) + 1);
5616 strcpy (prompt, wv->value);
5617 c2pstr (prompt);
5619 wv = wv->next;
5620 for (i = 0; i < nb_buttons; i++)
5622 button_labels[i] = wv->value;
5623 button_labels[i] = (char *) alloca (strlen (wv->value) + 1);
5624 strcpy (button_labels[i], wv->value);
5625 c2pstr (button_labels[i]);
5626 ref_cons[i] = (UInt32) wv->call_data;
5627 wv = wv->next;
5630 window_ptr = GetNewCWindow (DIALOG_WINDOW_RESOURCE, NULL, (WindowRef) -1);
5632 SetPortWindowPort (window_ptr);
5634 TextFont (0);
5635 /* Left and right margins in the dialog are 13 pixels each.*/
5636 dialog_width = 14;
5637 /* Calculate width of dialog box: 8 pixels on each side of the text
5638 label in each button, 12 pixels between buttons. */
5639 for (i = 0; i < nb_buttons; i++)
5640 dialog_width += StringWidth (button_labels[i]) + 16 + 12;
5642 if (left_count != 0 && nb_buttons - left_count != 0)
5643 dialog_width += 12;
5645 dialog_width = max (dialog_width, StringWidth (prompt) + 26);
5647 SizeWindow (window_ptr, dialog_width, 78, 0);
5648 ShowWindow (window_ptr);
5650 SetPortWindowPort (window_ptr);
5652 TextFont (0);
5654 MoveTo (13, 29);
5655 DrawString (prompt);
5657 left = 13;
5658 for (i = 0; i < nb_buttons; i++)
5660 int button_width = StringWidth (button_labels[i]) + 16;
5661 SetRect (&rect, left, 45, left + button_width, 65);
5662 ch = NewControl (window_ptr, &rect, button_labels[i], 1, 0, 0, 0,
5663 kControlPushButtonProc, ref_cons[i]);
5664 left += button_width + 12;
5665 if (i == left_count - 1)
5666 left += 12;
5669 i = 0;
5670 while (!i)
5672 if (WaitNextEvent (mDownMask, &event_record, 10, NULL))
5673 if (event_record.what == mouseDown)
5675 part_code = FindWindow (event_record.where, &window_ptr);
5676 if (part_code == inContent)
5678 mouse = event_record.where;
5679 GlobalToLocal (&mouse);
5680 control_part_code = FindControl (mouse, window_ptr, &ch);
5681 if (control_part_code == kControlButtonPart)
5682 if (TrackControl (ch, mouse, NULL))
5683 i = GetControlReference (ch);
5688 DisposeWindow (window_ptr);
5690 return i;
5692 #endif /* not TARGET_API_MAC_CARBON */
5695 /***********************************************************************
5696 Selection support
5697 ***********************************************************************/
5699 #if !TARGET_API_MAC_CARBON
5700 #include <Scrap.h>
5701 #include <Endian.h>
5702 #endif
5704 extern Lisp_Object Vselection_converter_alist;
5705 extern Lisp_Object Qmac_scrap_name, Qmac_ostype;
5707 static ScrapFlavorType get_flavor_type_from_symbol P_ ((Lisp_Object,
5708 Selection));
5710 /* Get a reference to the selection corresponding to the symbol SYM.
5711 The reference is set to *SEL, and it becomes NULL if there's no
5712 corresponding selection. Clear the selection if CLEAR_P is
5713 non-zero. */
5715 OSStatus
5716 mac_get_selection_from_symbol (sym, clear_p, sel)
5717 Lisp_Object sym;
5718 int clear_p;
5719 Selection *sel;
5721 OSStatus err = noErr;
5722 Lisp_Object str = Fget (sym, Qmac_scrap_name);
5724 if (!STRINGP (str))
5725 *sel = NULL;
5726 else
5728 #if TARGET_API_MAC_CARBON
5729 #ifdef MAC_OSX
5730 CFStringRef scrap_name = cfstring_create_with_string (str);
5731 OptionBits options = (clear_p ? kScrapClearNamedScrap
5732 : kScrapGetNamedScrap);
5734 err = GetScrapByName (scrap_name, options, sel);
5735 CFRelease (scrap_name);
5736 #else /* !MAC_OSX */
5737 if (clear_p)
5738 err = ClearCurrentScrap ();
5739 if (err == noErr)
5740 err = GetCurrentScrap (sel);
5741 #endif /* !MAC_OSX */
5742 #else /* !TARGET_API_MAC_CARBON */
5743 if (clear_p)
5744 err = ZeroScrap ();
5745 if (err == noErr)
5746 *sel = 1;
5747 #endif /* !TARGET_API_MAC_CARBON */
5750 return err;
5753 /* Get a scrap flavor type from the symbol SYM. Return 0 if no
5754 corresponding flavor type. If SEL is non-zero, the return value is
5755 non-zero only when the SEL has the flavor type. */
5757 static ScrapFlavorType
5758 get_flavor_type_from_symbol (sym, sel)
5759 Lisp_Object sym;
5760 Selection sel;
5762 Lisp_Object str = Fget (sym, Qmac_ostype);
5763 ScrapFlavorType flavor_type;
5765 if (STRINGP (str) && SBYTES (str) == 4)
5766 flavor_type = EndianU32_BtoN (*((UInt32 *) SDATA (str)));
5767 else
5768 flavor_type = 0;
5770 if (flavor_type && sel)
5772 #if TARGET_API_MAC_CARBON
5773 OSStatus err;
5774 ScrapFlavorFlags flags;
5776 err = GetScrapFlavorFlags (sel, flavor_type, &flags);
5777 if (err != noErr)
5778 flavor_type = 0;
5779 #else /* !TARGET_API_MAC_CARBON */
5780 SInt32 size, offset;
5782 size = GetScrap (NULL, flavor_type, &offset);
5783 if (size < 0)
5784 flavor_type = 0;
5785 #endif /* !TARGET_API_MAC_CARBON */
5788 return flavor_type;
5791 /* Check if the symbol SYM has a corresponding selection target type. */
5794 mac_valid_selection_target_p (sym)
5795 Lisp_Object sym;
5797 return get_flavor_type_from_symbol (sym, 0) != 0;
5800 /* Clear the selection whose reference is *SEL. */
5802 OSStatus
5803 mac_clear_selection (sel)
5804 Selection *sel;
5806 #if TARGET_API_MAC_CARBON
5807 #ifdef MAC_OSX
5808 return ClearScrap (sel);
5809 #else
5810 OSStatus err;
5812 err = ClearCurrentScrap ();
5813 if (err == noErr)
5814 err = GetCurrentScrap (sel);
5815 return err;
5816 #endif
5817 #else /* !TARGET_API_MAC_CARBON */
5818 return ZeroScrap ();
5819 #endif /* !TARGET_API_MAC_CARBON */
5822 /* Get ownership information for SEL. Emacs can detect a change of
5823 the ownership by comparing saved and current values of the
5824 ownership information. */
5826 Lisp_Object
5827 mac_get_selection_ownership_info (sel)
5828 Selection sel;
5830 #if TARGET_API_MAC_CARBON
5831 return long_to_cons ((unsigned long) sel);
5832 #else /* !TARGET_API_MAC_CARBON */
5833 ScrapStuffPtr scrap_info = InfoScrap ();
5835 return make_number (scrap_info->scrapCount);
5836 #endif /* !TARGET_API_MAC_CARBON */
5839 /* Return non-zero if VALUE is a valid selection value for TARGET. */
5842 mac_valid_selection_value_p (value, target)
5843 Lisp_Object value, target;
5845 return STRINGP (value);
5848 /* Put Lisp object VALUE to the selection SEL. The target type is
5849 specified by TARGET. */
5851 OSStatus
5852 mac_put_selection_value (sel, target, value)
5853 Selection sel;
5854 Lisp_Object target, value;
5856 ScrapFlavorType flavor_type = get_flavor_type_from_symbol (target, 0);
5858 if (flavor_type == 0 || !STRINGP (value))
5859 return noTypeErr;
5861 #if TARGET_API_MAC_CARBON
5862 return PutScrapFlavor (sel, flavor_type, kScrapFlavorMaskNone,
5863 SBYTES (value), SDATA (value));
5864 #else /* !TARGET_API_MAC_CARBON */
5865 return PutScrap (SBYTES (value), flavor_type, SDATA (value));
5866 #endif /* !TARGET_API_MAC_CARBON */
5869 /* Check if data for the target type TARGET is available in SEL. */
5872 mac_selection_has_target_p (sel, target)
5873 Selection sel;
5874 Lisp_Object target;
5876 return get_flavor_type_from_symbol (target, sel) != 0;
5879 /* Get data for the target type TARGET from SEL and create a Lisp
5880 string. Return nil if failed to get data. */
5882 Lisp_Object
5883 mac_get_selection_value (sel, target)
5884 Selection sel;
5885 Lisp_Object target;
5887 OSStatus err;
5888 Lisp_Object result = Qnil;
5889 ScrapFlavorType flavor_type = get_flavor_type_from_symbol (target, sel);
5890 #if TARGET_API_MAC_CARBON
5891 Size size;
5893 if (flavor_type)
5895 err = GetScrapFlavorSize (sel, flavor_type, &size);
5896 if (err == noErr)
5900 result = make_uninit_string (size);
5901 err = GetScrapFlavorData (sel, flavor_type,
5902 &size, SDATA (result));
5903 if (err != noErr)
5904 result = Qnil;
5905 else if (size < SBYTES (result))
5906 result = make_unibyte_string (SDATA (result), size);
5908 while (STRINGP (result) && size > SBYTES (result));
5911 #else
5912 Handle handle;
5913 SInt32 size, offset;
5915 if (flavor_type)
5916 size = GetScrap (NULL, flavor_type, &offset);
5917 if (size >= 0)
5919 handle = NewHandle (size);
5920 HLock (handle);
5921 size = GetScrap (handle, flavor_type, &offset);
5922 if (size >= 0)
5923 result = make_unibyte_string (*handle, size);
5924 DisposeHandle (handle);
5926 #endif
5928 return result;
5931 /* Get the list of target types in SEL. The return value is a list of
5932 target type symbols possibly followed by scrap flavor type
5933 strings. */
5935 Lisp_Object
5936 mac_get_selection_target_list (sel)
5937 Selection sel;
5939 Lisp_Object result = Qnil, rest, target;
5940 #if TARGET_API_MAC_CARBON
5941 OSStatus err;
5942 UInt32 count, i, type;
5943 ScrapFlavorInfo *flavor_info = NULL;
5944 Lisp_Object strings = Qnil;
5946 err = GetScrapFlavorCount (sel, &count);
5947 if (err == noErr)
5948 flavor_info = xmalloc (sizeof (ScrapFlavorInfo) * count);
5949 err = GetScrapFlavorInfoList (sel, &count, flavor_info);
5950 if (err != noErr)
5952 xfree (flavor_info);
5953 flavor_info = NULL;
5955 if (flavor_info == NULL)
5956 count = 0;
5957 #endif
5958 for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest))
5960 ScrapFlavorType flavor_type = 0;
5962 if (CONSP (XCAR (rest))
5963 && (target = XCAR (XCAR (rest)),
5964 SYMBOLP (target))
5965 && (flavor_type = get_flavor_type_from_symbol (target, sel)))
5967 result = Fcons (target, result);
5968 #if TARGET_API_MAC_CARBON
5969 for (i = 0; i < count; i++)
5970 if (flavor_info[i].flavorType == flavor_type)
5972 flavor_info[i].flavorType = 0;
5973 break;
5975 #endif
5978 #if TARGET_API_MAC_CARBON
5979 if (flavor_info)
5981 for (i = 0; i < count; i++)
5982 if (flavor_info[i].flavorType)
5984 type = EndianU32_NtoB (flavor_info[i].flavorType);
5985 strings = Fcons (make_unibyte_string ((char *) &type, 4), strings);
5987 result = nconc2 (result, strings);
5988 xfree (flavor_info);
5990 #endif
5992 return result;
5996 /***********************************************************************
5997 Apple event support
5998 ***********************************************************************/
6000 extern pascal OSErr mac_handle_apple_event P_ ((const AppleEvent *,
6001 AppleEvent *, SInt32));
6002 extern void cleanup_all_suspended_apple_events P_ ((void));
6004 void
6005 init_apple_event_handler ()
6007 OSErr err;
6008 long result;
6010 /* Make sure we have Apple events before starting. */
6011 err = Gestalt (gestaltAppleEventsAttr, &result);
6012 if (err != noErr)
6013 abort ();
6015 if (!(result & (1 << gestaltAppleEventsPresent)))
6016 abort ();
6018 err = AEInstallEventHandler (typeWildCard, typeWildCard,
6019 #if TARGET_API_MAC_CARBON
6020 NewAEEventHandlerUPP (mac_handle_apple_event),
6021 #else
6022 NewAEEventHandlerProc (mac_handle_apple_event),
6023 #endif
6024 0L, false);
6025 if (err != noErr)
6026 abort ();
6028 atexit (cleanup_all_suspended_apple_events);
6032 /***********************************************************************
6033 Drag and drop support
6034 ***********************************************************************/
6036 #if TARGET_API_MAC_CARBON
6037 extern Lisp_Object Vmac_dnd_known_types;
6039 static pascal OSErr mac_do_track_drag P_ ((DragTrackingMessage, WindowRef,
6040 void *, DragRef));
6041 static pascal OSErr mac_do_receive_drag P_ ((WindowRef, void *, DragRef));
6042 static DragTrackingHandlerUPP mac_do_track_dragUPP = NULL;
6043 static DragReceiveHandlerUPP mac_do_receive_dragUPP = NULL;
6045 static OSErr
6046 create_apple_event_from_drag_ref (drag, num_types, types, result)
6047 DragRef drag;
6048 UInt32 num_types;
6049 const FlavorType *types;
6050 AppleEvent *result;
6052 OSErr err;
6053 UInt16 num_items;
6054 AppleEvent items;
6055 long index;
6056 char *buf = NULL;
6058 err = CountDragItems (drag, &num_items);
6059 if (err != noErr)
6060 return err;
6061 err = AECreateList (NULL, 0, false, &items);
6062 if (err != noErr)
6063 return err;
6065 for (index = 1; index <= num_items; index++)
6067 ItemReference item;
6068 DescType desc_type = typeNull;
6069 Size size;
6071 err = GetDragItemReferenceNumber (drag, index, &item);
6072 if (err == noErr)
6074 int i;
6076 for (i = 0; i < num_types; i++)
6078 err = GetFlavorDataSize (drag, item, types[i], &size);
6079 if (err == noErr)
6081 buf = xrealloc (buf, size);
6082 err = GetFlavorData (drag, item, types[i], buf, &size, 0);
6084 if (err == noErr)
6086 desc_type = types[i];
6087 break;
6091 err = AEPutPtr (&items, index, desc_type,
6092 desc_type != typeNull ? buf : NULL,
6093 desc_type != typeNull ? size : 0);
6094 if (err != noErr)
6095 break;
6097 if (buf)
6098 xfree (buf);
6100 if (err == noErr)
6102 err = create_apple_event (0, 0, result); /* Dummy class and ID. */
6103 if (err == noErr)
6104 err = AEPutParamDesc (result, keyDirectObject, &items);
6105 if (err != noErr)
6106 AEDisposeDesc (result);
6109 AEDisposeDesc (&items);
6111 return err;
6114 static void
6115 mac_store_drag_event (window, mouse_pos, modifiers, desc)
6116 WindowRef window;
6117 Point mouse_pos;
6118 SInt16 modifiers;
6119 const AEDesc *desc;
6121 struct input_event buf;
6123 EVENT_INIT (buf);
6125 buf.kind = DRAG_N_DROP_EVENT;
6126 buf.modifiers = mac_to_emacs_modifiers (modifiers, 0);
6127 buf.timestamp = TickCount () * (1000 / 60);
6128 XSETINT (buf.x, mouse_pos.h);
6129 XSETINT (buf.y, mouse_pos.v);
6130 XSETFRAME (buf.frame_or_window, mac_window_to_frame (window));
6131 buf.arg = mac_aedesc_to_lisp (desc);
6132 kbd_buffer_store_event (&buf);
6135 static pascal OSErr
6136 mac_do_track_drag (message, window, refcon, drag)
6137 DragTrackingMessage message;
6138 WindowRef window;
6139 void *refcon;
6140 DragRef drag;
6142 OSErr err = noErr;
6143 static int can_accept;
6144 UInt16 num_items, index;
6146 if (GetFrontWindowOfClass (kMovableModalWindowClass, false))
6147 return dragNotAcceptedErr;
6149 switch (message)
6151 case kDragTrackingEnterHandler:
6152 err = CountDragItems (drag, &num_items);
6153 if (err != noErr)
6154 break;
6155 can_accept = 0;
6156 for (index = 1; index <= num_items; index++)
6158 ItemReference item;
6159 FlavorFlags flags;
6160 Lisp_Object rest;
6162 err = GetDragItemReferenceNumber (drag, index, &item);
6163 if (err != noErr)
6164 continue;
6165 for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest))
6167 Lisp_Object str;
6168 FlavorType type;
6170 str = XCAR (rest);
6171 if (!(STRINGP (str) && SBYTES (str) == 4))
6172 continue;
6173 type = EndianU32_BtoN (*((UInt32 *) SDATA (str)));
6175 err = GetFlavorFlags (drag, item, type, &flags);
6176 if (err == noErr)
6178 can_accept = 1;
6179 break;
6183 break;
6185 case kDragTrackingEnterWindow:
6186 if (can_accept)
6188 RgnHandle hilite_rgn = NewRgn ();
6190 if (hilite_rgn)
6192 Rect r;
6194 GetWindowPortBounds (window, &r);
6195 OffsetRect (&r, -r.left, -r.top);
6196 RectRgn (hilite_rgn, &r);
6197 ShowDragHilite (drag, hilite_rgn, true);
6198 DisposeRgn (hilite_rgn);
6200 SetThemeCursor (kThemeCopyArrowCursor);
6202 break;
6204 case kDragTrackingInWindow:
6205 break;
6207 case kDragTrackingLeaveWindow:
6208 if (can_accept)
6210 HideDragHilite (drag);
6211 SetThemeCursor (kThemeArrowCursor);
6213 break;
6215 case kDragTrackingLeaveHandler:
6216 break;
6219 if (err != noErr)
6220 return dragNotAcceptedErr;
6221 return noErr;
6224 static pascal OSErr
6225 mac_do_receive_drag (window, refcon, drag)
6226 WindowRef window;
6227 void *refcon;
6228 DragRef drag;
6230 OSErr err;
6231 int num_types, i;
6232 Lisp_Object rest, str;
6233 FlavorType *types;
6234 AppleEvent apple_event;
6235 Point mouse_pos;
6236 SInt16 modifiers;
6238 if (GetFrontWindowOfClass (kMovableModalWindowClass, false))
6239 return dragNotAcceptedErr;
6241 num_types = 0;
6242 for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest))
6244 str = XCAR (rest);
6245 if (STRINGP (str) && SBYTES (str) == 4)
6246 num_types++;
6249 types = xmalloc (sizeof (FlavorType) * num_types);
6250 i = 0;
6251 for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest))
6253 str = XCAR (rest);
6254 if (STRINGP (str) && SBYTES (str) == 4)
6255 types[i++] = EndianU32_BtoN (*((UInt32 *) SDATA (str)));
6258 err = create_apple_event_from_drag_ref (drag, num_types, types,
6259 &apple_event);
6260 xfree (types);
6262 if (err == noErr)
6263 err = GetDragMouse (drag, &mouse_pos, NULL);
6264 if (err == noErr)
6266 GlobalToLocal (&mouse_pos);
6267 err = GetDragModifiers (drag, NULL, NULL, &modifiers);
6269 if (err == noErr)
6271 UInt32 key_modifiers = modifiers;
6273 err = AEPutParamPtr (&apple_event, kEventParamKeyModifiers,
6274 typeUInt32, &key_modifiers, sizeof (UInt32));
6277 if (err == noErr)
6279 mac_store_drag_event (window, mouse_pos, 0, &apple_event);
6280 AEDisposeDesc (&apple_event);
6281 mac_wakeup_from_rne ();
6282 return noErr;
6284 else
6285 return dragNotAcceptedErr;
6287 #endif /* TARGET_API_MAC_CARBON */
6289 static OSErr
6290 install_drag_handler (window)
6291 WindowRef window;
6293 OSErr err = noErr;
6295 #if TARGET_API_MAC_CARBON
6296 if (mac_do_track_dragUPP == NULL)
6297 mac_do_track_dragUPP = NewDragTrackingHandlerUPP (mac_do_track_drag);
6298 if (mac_do_receive_dragUPP == NULL)
6299 mac_do_receive_dragUPP = NewDragReceiveHandlerUPP (mac_do_receive_drag);
6301 err = InstallTrackingHandler (mac_do_track_dragUPP, window, NULL);
6302 if (err == noErr)
6303 err = InstallReceiveHandler (mac_do_receive_dragUPP, window, NULL);
6304 #endif
6306 return err;
6309 static void
6310 remove_drag_handler (window)
6311 WindowRef window;
6313 #if TARGET_API_MAC_CARBON
6314 if (mac_do_track_dragUPP)
6315 RemoveTrackingHandler (mac_do_track_dragUPP, window);
6316 if (mac_do_receive_dragUPP)
6317 RemoveReceiveHandler (mac_do_receive_dragUPP, window);
6318 #endif
6321 #if TARGET_API_MAC_CARBON
6322 /* Return default value for mac-dnd-known-types. */
6324 Lisp_Object
6325 mac_dnd_default_known_types ()
6327 Lisp_Object result = list4 (build_string ("hfs "), build_string ("utxt"),
6328 build_string ("TEXT"), build_string ("TIFF"));
6330 #ifdef MAC_OSX
6331 result = Fcons (build_string ("furl"), result);
6332 #endif
6334 return result;
6336 #endif
6339 /***********************************************************************
6340 Services menu support
6341 ***********************************************************************/
6343 #ifdef MAC_OSX
6344 extern Lisp_Object Qservice, Qpaste, Qperform;
6345 extern Lisp_Object Vmac_service_selection;
6347 static OSStatus
6348 mac_store_service_event (event)
6349 EventRef event;
6351 OSStatus err;
6352 Lisp_Object id_key;
6353 int num_params;
6354 const EventParamName *names;
6355 const EventParamType *types;
6356 static const EventParamName names_pfm[] =
6357 {kEventParamServiceMessageName, kEventParamServiceUserData};
6358 static const EventParamType types_pfm[] =
6359 {typeCFStringRef, typeCFStringRef};
6361 switch (GetEventKind (event))
6363 case kEventServicePaste:
6364 id_key = Qpaste;
6365 num_params = 0;
6366 names = NULL;
6367 types = NULL;
6368 break;
6370 case kEventServicePerform:
6371 id_key = Qperform;
6372 num_params = sizeof (names_pfm) / sizeof (names_pfm[0]);
6373 names = names_pfm;
6374 types = types_pfm;
6375 break;
6377 default:
6378 abort ();
6381 err = mac_store_event_ref_as_apple_event (0, 0, Qservice, id_key,
6382 event, num_params,
6383 names, types);
6385 return err;
6388 static OSStatus
6389 copy_scrap_flavor_data (from_scrap, to_scrap, flavor_type)
6390 ScrapRef from_scrap, to_scrap;
6391 ScrapFlavorType flavor_type;
6393 OSStatus err;
6394 Size size, size_allocated;
6395 char *buf = NULL;
6397 err = GetScrapFlavorSize (from_scrap, flavor_type, &size);
6398 if (err == noErr)
6399 buf = xmalloc (size);
6400 while (buf)
6402 size_allocated = size;
6403 err = GetScrapFlavorData (from_scrap, flavor_type, &size, buf);
6404 if (err != noErr)
6406 xfree (buf);
6407 buf = NULL;
6409 else if (size_allocated < size)
6410 buf = xrealloc (buf, size);
6411 else
6412 break;
6414 if (err == noErr)
6416 if (buf == NULL)
6417 err = memFullErr;
6418 else
6420 err = PutScrapFlavor (to_scrap, flavor_type, kScrapFlavorMaskNone,
6421 size, buf);
6422 xfree (buf);
6426 return err;
6429 static OSStatus
6430 mac_handle_service_event (call_ref, event, data)
6431 EventHandlerCallRef call_ref;
6432 EventRef event;
6433 void *data;
6435 OSStatus err = noErr;
6436 ScrapRef cur_scrap, specific_scrap;
6437 UInt32 event_kind = GetEventKind (event);
6438 CFMutableArrayRef copy_types, paste_types;
6439 CFStringRef type;
6440 Lisp_Object rest;
6441 ScrapFlavorType flavor_type;
6443 /* Check if Vmac_service_selection is a valid selection that has a
6444 corresponding scrap. */
6445 if (!SYMBOLP (Vmac_service_selection))
6446 err = eventNotHandledErr;
6447 else
6448 err = mac_get_selection_from_symbol (Vmac_service_selection, 0, &cur_scrap);
6449 if (!(err == noErr && cur_scrap))
6450 return eventNotHandledErr;
6452 switch (event_kind)
6454 case kEventServiceGetTypes:
6455 /* Set paste types. */
6456 err = GetEventParameter (event, kEventParamServicePasteTypes,
6457 typeCFMutableArrayRef, NULL,
6458 sizeof (CFMutableArrayRef), NULL,
6459 &paste_types);
6460 if (err != noErr)
6461 break;
6463 for (rest = Vselection_converter_alist; CONSP (rest);
6464 rest = XCDR (rest))
6465 if (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest)))
6466 && (flavor_type =
6467 get_flavor_type_from_symbol (XCAR (XCAR (rest)), 0)))
6469 type = CreateTypeStringWithOSType (flavor_type);
6470 if (type)
6472 CFArrayAppendValue (paste_types, type);
6473 CFRelease (type);
6477 /* Set copy types. */
6478 err = GetEventParameter (event, kEventParamServiceCopyTypes,
6479 typeCFMutableArrayRef, NULL,
6480 sizeof (CFMutableArrayRef), NULL,
6481 &copy_types);
6482 if (err != noErr)
6483 break;
6485 if (NILP (Fx_selection_owner_p (Vmac_service_selection)))
6486 break;
6487 else
6488 goto copy_all_flavors;
6490 case kEventServiceCopy:
6491 err = GetEventParameter (event, kEventParamScrapRef,
6492 typeScrapRef, NULL,
6493 sizeof (ScrapRef), NULL, &specific_scrap);
6494 if (err != noErr
6495 || NILP (Fx_selection_owner_p (Vmac_service_selection)))
6497 err = eventNotHandledErr;
6498 break;
6501 copy_all_flavors:
6503 UInt32 count, i;
6504 ScrapFlavorInfo *flavor_info = NULL;
6505 ScrapFlavorFlags flags;
6507 err = GetScrapFlavorCount (cur_scrap, &count);
6508 if (err == noErr)
6509 flavor_info = xmalloc (sizeof (ScrapFlavorInfo) * count);
6510 err = GetScrapFlavorInfoList (cur_scrap, &count, flavor_info);
6511 if (err != noErr)
6513 xfree (flavor_info);
6514 flavor_info = NULL;
6516 if (flavor_info == NULL)
6517 break;
6519 for (i = 0; i < count; i++)
6521 flavor_type = flavor_info[i].flavorType;
6522 err = GetScrapFlavorFlags (cur_scrap, flavor_type, &flags);
6523 if (err == noErr && !(flags & kScrapFlavorMaskSenderOnly))
6525 if (event_kind == kEventServiceCopy)
6526 err = copy_scrap_flavor_data (cur_scrap, specific_scrap,
6527 flavor_type);
6528 else /* event_kind == kEventServiceGetTypes */
6530 type = CreateTypeStringWithOSType (flavor_type);
6531 if (type)
6533 CFArrayAppendValue (copy_types, type);
6534 CFRelease (type);
6539 xfree (flavor_info);
6541 break;
6543 case kEventServicePaste:
6544 case kEventServicePerform:
6546 int data_exists_p = 0;
6548 err = GetEventParameter (event, kEventParamScrapRef, typeScrapRef,
6549 NULL, sizeof (ScrapRef), NULL,
6550 &specific_scrap);
6551 if (err == noErr)
6552 err = mac_clear_selection (&cur_scrap);
6553 if (err == noErr)
6554 for (rest = Vselection_converter_alist; CONSP (rest);
6555 rest = XCDR (rest))
6557 if (! (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest)))))
6558 continue;
6559 flavor_type = get_flavor_type_from_symbol (XCAR (XCAR (rest)),
6560 specific_scrap);
6561 if (flavor_type == 0)
6562 continue;
6563 err = copy_scrap_flavor_data (specific_scrap, cur_scrap,
6564 flavor_type);
6565 if (err == noErr)
6566 data_exists_p = 1;
6568 if (!data_exists_p)
6569 err = eventNotHandledErr;
6570 else
6571 err = mac_store_service_event (event);
6573 break;
6576 if (err != noErr)
6577 err = eventNotHandledErr;
6578 return err;
6581 static OSStatus
6582 install_service_handler ()
6584 static const EventTypeSpec specs[] =
6585 {{kEventClassService, kEventServiceGetTypes},
6586 {kEventClassService, kEventServiceCopy},
6587 {kEventClassService, kEventServicePaste},
6588 {kEventClassService, kEventServicePerform}};
6590 return InstallApplicationEventHandler (NewEventHandlerUPP
6591 (mac_handle_service_event),
6592 GetEventTypeCount (specs),
6593 specs, NULL, NULL);
6595 #endif /* MAC_OSX */
6598 /***********************************************************************
6599 Initialization
6600 ***********************************************************************/
6602 void
6603 mac_toolbox_initialize ()
6605 any_help_event_p = 0;
6607 init_menu_bar ();
6609 #ifdef MAC_OSX
6610 init_apple_event_handler ();
6611 #endif
6612 #if USE_MAC_TSM
6613 init_tsm ();
6614 #endif
6617 /* arch-tag: 71a597a8-6e9f-47b0-8b89-5a5ae3e16516
6618 (do not change this comment) */