(mac_handle_text_input_event):
[emacs.git] / src / mactoolbox.c
blob58d975742e63a9c17ba30cddc92b1bdd0a9c9dec
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 struct buffer *b;
574 CFRange sel_range;
575 int charpos;
576 int hpos, vpos, x, y;
577 struct glyph_row *row;
578 struct glyph *glyph;
579 struct face *face;
581 f = mac_focus_frame (&one_mac_display_info);
582 w = XWINDOW (f->selected_window);
583 b = XBUFFER (w->buffer);
585 /* Are we in a window whose display is up to date?
586 And verify the buffer's text has not changed. */
587 if (!(EQ (w->window_end_valid, w->buffer)
588 && XINT (w->last_modified) == BUF_MODIFF (b)
589 && XINT (w->last_overlay_modified) == BUF_OVERLAY_MODIFF (b)))
590 break;
592 mac_get_selected_range (w, &sel_range);
593 charpos = (BUF_BEGV (b) + sel_range.location
594 + byte_offset / (long) sizeof (UniChar));
596 if (!fast_find_position (w, charpos, &hpos, &vpos, &x, &y, Qnil))
598 result = errOffsetInvalid;
599 break;
602 row = MATRIX_ROW (w->current_matrix, vpos);
603 glyph = row->glyphs[TEXT_AREA] + hpos;
604 if (glyph->type != CHAR_GLYPH || glyph->glyph_not_available_p)
605 break;
607 p.h = (WINDOW_TEXT_TO_FRAME_PIXEL_X (w, x)
608 + f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f));
609 p.v = (WINDOW_TO_FRAME_PIXEL_Y (w, y)
610 + row->visible_height
611 + f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f));
613 face = FACE_FROM_ID (f, glyph->face_id);
614 if (face && face->font)
616 XFontStruct *font = face->font;
617 Fixed point_size = Long2Fix (font->mac_fontsize);
618 short height = row->visible_height;
619 short ascent = row->ascent;
621 SetEventParameter (event,
622 kEventParamTextInputReplyPointSize,
623 typeFixed, sizeof (Fixed), &point_size);
624 SetEventParameter (event,
625 kEventParamTextInputReplyLineHeight,
626 typeShortInteger, sizeof (short), &height);
627 SetEventParameter (event,
628 kEventParamTextInputReplyLineAscent,
629 typeShortInteger, sizeof (short), &ascent);
630 if (font->mac_fontnum != -1)
632 OSStatus err1;
633 FMFont fm_font;
634 FMFontStyle style;
636 err1 = FMGetFontFromFontFamilyInstance (font->mac_fontnum,
637 font->mac_fontface,
638 &fm_font, &style);
639 if (err1 == noErr)
640 SetEventParameter (event, kEventParamTextInputReplyFMFont,
641 typeUInt32, sizeof (UInt32), &fm_font);
642 else
644 long qd_font = font->mac_fontnum;
646 SetEventParameter (event, kEventParamTextInputReplyFont,
647 typeLongInteger, sizeof (long),
648 &qd_font);
651 else if (font->mac_style)
653 OSStatus err1;
654 ATSUFontID font_id;
656 err1 = ATSUGetAttribute (font->mac_style, kATSUFontTag,
657 sizeof (ATSUFontID), &font_id,
658 NULL);
659 if (err1 == noErr)
660 SetEventParameter (event, kEventParamTextInputReplyFMFont,
661 typeUInt32, sizeof (UInt32), &font_id);
663 else
664 abort ();
666 #endif /* MAC_OSX */
669 err = SetEventParameter (event, kEventParamTextInputReplyPoint,
670 typeQDPoint, sizeof (Point), &p);
671 if (err == noErr)
672 result = noErr;
674 break;
676 #ifdef MAC_OSX
677 case kEventTextInputPosToOffset:
679 Point point;
680 Boolean leading_edge_p = true;
681 struct frame *f;
682 int x, y;
683 Lisp_Object window;
684 enum window_part part;
685 long region_class = kTSMOutsideOfBody, byte_offset = 0;
687 err = GetEventParameter (event, kEventParamTextInputSendCurrentPoint,
688 typeQDPoint, NULL, sizeof (Point), NULL,
689 &point);
690 if (err != noErr)
691 break;
693 GetEventParameter (event, kEventParamTextInputReplyLeadingEdge,
694 typeBoolean, NULL, sizeof (Boolean), NULL,
695 &leading_edge_p);
697 f = mac_focus_frame (&one_mac_display_info);
698 x = point.h - (f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f));
699 y = point.v - (f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f));
700 window = window_from_coordinates (f, x, y, &part, 0, 0, 1);
701 if (WINDOWP (window) && EQ (window, f->selected_window))
703 struct window *w;
704 struct buffer *b;
706 /* Convert to window-relative pixel coordinates. */
707 w = XWINDOW (window);
708 frame_to_window_pixel_xy (w, &x, &y);
710 /* Are we in a window whose display is up to date?
711 And verify the buffer's text has not changed. */
712 b = XBUFFER (w->buffer);
713 if (part == ON_TEXT
714 && EQ (w->window_end_valid, w->buffer)
715 && XINT (w->last_modified) == BUF_MODIFF (b)
716 && XINT (w->last_overlay_modified) == BUF_OVERLAY_MODIFF (b))
718 int hpos, vpos, area;
719 struct glyph *glyph;
721 /* Find the glyph under X/Y. */
722 glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, 0, 0, &area);
724 if (glyph != NULL && area == TEXT_AREA)
726 byte_offset = ((glyph->charpos - BUF_BEGV (b))
727 * sizeof (UniChar));
728 region_class = kTSMInsideOfBody;
733 err = SetEventParameter (event, kEventParamTextInputReplyRegionClass,
734 typeLongInteger, sizeof (long),
735 &region_class);
736 if (err == noErr)
737 err = SetEventParameter (event, kEventParamTextInputReplyTextOffset,
738 typeLongInteger, sizeof (long),
739 &byte_offset);
740 if (err == noErr)
741 result = noErr;
743 break;
745 case kEventTextInputGetSelectedText:
747 struct frame *f = mac_focus_frame (&one_mac_display_info);
748 struct window *w = XWINDOW (f->selected_window);
749 struct buffer *b = XBUFFER (w->buffer);
750 CFRange sel_range;
751 int start, end;
752 UniChar *characters, c;
754 if (poll_suppress_count == 0 && !NILP (Vinhibit_quit))
755 /* Don't try to get buffer contents as the gap might be
756 being altered. */
757 break;
759 mac_get_selected_range (w, &sel_range);
760 if (sel_range.length == 0)
762 Boolean leading_edge_p;
764 err = GetEventParameter (event,
765 kEventParamTextInputReplyLeadingEdge,
766 typeBoolean, NULL, sizeof (Boolean), NULL,
767 &leading_edge_p);
768 if (err != noErr)
769 break;
771 start = BUF_BEGV (b) + sel_range.location;
772 if (!leading_edge_p)
773 start--;
774 end = start + 1;
775 characters = &c;
777 if (start < BUF_BEGV (b) || end > BUF_ZV (b))
778 break;
780 else
782 start = BUF_BEGV (b) + sel_range.location;
783 end = start + sel_range.length;
784 characters = xmalloc (sel_range.length * sizeof (UniChar));
787 if (mac_store_buffer_text_to_unicode_chars (b, start, end, characters))
788 err = SetEventParameter (event, kEventParamTextInputReplyText,
789 typeUnicodeText,
790 sel_range.length * sizeof (UniChar),
791 characters);
792 if (characters != &c)
793 xfree (characters);
795 if (err == noErr)
796 result = noErr;
798 break;
799 #endif /* MAC_OSX */
801 default:
802 abort ();
805 if (!NILP (id_key))
806 err = mac_store_event_ref_as_apple_event (0, 0, Qtext_input, id_key,
807 event, num_params,
808 names, types);
809 return result;
812 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
813 static pascal OSStatus
814 mac_handle_document_access_event (next_handler, event, data)
815 EventHandlerCallRef next_handler;
816 EventRef event;
817 void *data;
819 OSStatus err, result;
820 struct frame *f = mac_focus_frame (&one_mac_display_info);
822 result = CallNextEventHandler (next_handler, event);
823 if (result != eventNotHandledErr)
824 return result;
826 switch (GetEventKind (event))
828 case kEventTSMDocumentAccessGetLength:
830 CFIndex count = mac_ax_number_of_characters (f);
832 err = SetEventParameter (event, kEventParamTSMDocAccessCharacterCount,
833 typeCFIndex, sizeof (CFIndex), &count);
834 if (err == noErr)
835 result = noErr;
837 break;
839 case kEventTSMDocumentAccessGetSelectedRange:
841 CFRange sel_range;
843 mac_ax_selected_text_range (f, &sel_range);
844 err = SetEventParameter (event,
845 kEventParamTSMDocAccessReplyCharacterRange,
846 typeCFRange, sizeof (CFRange), &sel_range);
847 if (err == noErr)
848 result = noErr;
850 break;
852 case kEventTSMDocumentAccessGetCharacters:
854 struct buffer *b = XBUFFER (XWINDOW (f->selected_window)->buffer);
855 CFRange range;
856 Ptr characters;
857 int start, end;
859 if (poll_suppress_count == 0 && !NILP (Vinhibit_quit))
860 /* Don't try to get buffer contents as the gap might be
861 being altered. */
862 break;
864 err = GetEventParameter (event,
865 kEventParamTSMDocAccessSendCharacterRange,
866 typeCFRange, NULL, sizeof (CFRange), NULL,
867 &range);
868 if (err == noErr)
869 err = GetEventParameter (event,
870 kEventParamTSMDocAccessSendCharactersPtr,
871 typePtr, NULL, sizeof (Ptr), NULL,
872 &characters);
873 if (err != noErr)
874 break;
876 start = BUF_BEGV (b) + range.location;
877 end = start + range.length;
878 if (mac_store_buffer_text_to_unicode_chars (b, start, end,
879 (UniChar *) characters))
880 result = noErr;
882 break;
884 default:
885 abort ();
888 return result;
890 #endif
891 #endif
893 OSStatus
894 install_application_handler ()
896 OSStatus err = noErr;
898 if (err == noErr)
900 static const EventTypeSpec specs[] =
901 {{kEventClassKeyboard, kEventRawKeyDown},
902 {kEventClassKeyboard, kEventRawKeyRepeat},
903 {kEventClassKeyboard, kEventRawKeyUp}};
905 err = InstallApplicationEventHandler (NewEventHandlerUPP
906 (mac_handle_keyboard_event),
907 GetEventTypeCount (specs),
908 specs, NULL, NULL);
911 if (err == noErr)
913 static const EventTypeSpec specs[] =
914 {{kEventClassCommand, kEventCommandProcess}};
916 err = InstallApplicationEventHandler (NewEventHandlerUPP
917 (mac_handle_command_event),
918 GetEventTypeCount (specs),
919 specs, NULL, NULL);
922 if (err == noErr)
924 static const EventTypeSpec specs[] =
925 {{kEventClassMouse, kEventMouseWheelMoved}};
927 err = InstallApplicationEventHandler (NewEventHandlerUPP
928 (mac_handle_mouse_event),
929 GetEventTypeCount (specs),
930 specs, NULL, NULL);
933 #if USE_MAC_TSM
934 if (err == noErr)
936 static const EventTypeSpec specs[] =
937 {{kEventClassTextInput, kEventTextInputUpdateActiveInputArea},
938 {kEventClassTextInput, kEventTextInputUnicodeForKeyEvent},
939 {kEventClassTextInput, kEventTextInputOffsetToPos},
940 #ifdef MAC_OSX
941 {kEventClassTextInput, kEventTextInputPosToOffset},
942 {kEventClassTextInput, kEventTextInputGetSelectedText}
943 #endif
946 err = InstallApplicationEventHandler (NewEventHandlerUPP
947 (mac_handle_text_input_event),
948 GetEventTypeCount (specs),
949 specs, NULL, NULL);
952 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
953 if (err == noErr)
955 static const EventTypeSpec specs[] =
956 {{kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength},
957 {kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange},
958 {kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters}};
960 err = InstallApplicationEventHandler (mac_handle_document_access_event,
961 GetEventTypeCount (specs),
962 specs, NULL, NULL);
964 #endif
965 #endif
967 if (err == noErr)
968 err = install_menu_target_item_handler ();
970 #ifdef MAC_OSX
971 if (err == noErr)
972 err = install_service_handler ();
973 #endif
975 return err;
977 #endif /* TARGET_API_MAC_CARBON */
980 /************************************************************************
981 Windows
982 ************************************************************************/
984 #define DEFAULT_NUM_COLS 80
986 #define MIN_DOC_SIZE 64
987 #define MAX_DOC_SIZE 32767
989 /* Drag and Drop */
990 static OSErr install_drag_handler P_ ((WindowRef));
991 static void remove_drag_handler P_ ((WindowRef));
993 #if USE_CG_DRAWING
994 static void mac_prepare_for_quickdraw P_ ((struct frame *));
995 #endif
997 extern void mac_handle_visibility_change P_ ((struct frame *));
998 extern void mac_handle_origin_change P_ ((struct frame *));
999 extern void mac_handle_size_change P_ ((struct frame *, int, int));
1001 #if TARGET_API_MAC_CARBON
1002 #ifdef MAC_OSX
1003 extern Lisp_Object Qwindow;
1004 extern Lisp_Object Qtoolbar_switch_mode;
1005 #endif
1006 #endif
1008 static void
1009 do_window_update (WindowRef win)
1011 struct frame *f = mac_window_to_frame (win);
1013 BeginUpdate (win);
1015 /* The tooltip has been drawn already. Avoid the SET_FRAME_GARBAGED
1016 below. */
1017 if (win != tip_window)
1019 if (f->async_visible == 0)
1021 /* Update events may occur when a frame gets iconified. */
1022 #if 0
1023 f->async_visible = 1;
1024 f->async_iconified = 0;
1025 SET_FRAME_GARBAGED (f);
1026 #endif
1028 else
1030 Rect r;
1031 #if TARGET_API_MAC_CARBON
1032 RgnHandle region = NewRgn ();
1034 GetPortVisibleRegion (GetWindowPort (win), region);
1035 GetRegionBounds (region, &r);
1036 expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top);
1037 #if USE_CG_DRAWING
1038 mac_prepare_for_quickdraw (f);
1039 #endif
1040 UpdateControls (win, region);
1041 DisposeRgn (region);
1042 #else
1043 r = (*win->visRgn)->rgnBBox;
1044 expose_frame (f, r.left, r.top, r.right - r.left, r.bottom - r.top);
1045 UpdateControls (win, win->visRgn);
1046 #endif
1050 EndUpdate (win);
1053 static int
1054 is_emacs_window (WindowRef win)
1056 Lisp_Object tail, frame;
1058 if (!win)
1059 return 0;
1061 FOR_EACH_FRAME (tail, frame)
1062 if (FRAME_MAC_P (XFRAME (frame)))
1063 if (FRAME_MAC_WINDOW (XFRAME (frame)) == win)
1064 return 1;
1066 return 0;
1069 /* Handle drags in size box. Based on code contributed by Ben
1070 Mesander and IM - Window Manager A. */
1072 static void
1073 do_grow_window (w, e)
1074 WindowRef w;
1075 const EventRecord *e;
1077 Rect limit_rect;
1078 int rows, columns, width, height;
1079 struct frame *f = mac_window_to_frame (w);
1080 XSizeHints *size_hints = FRAME_SIZE_HINTS (f);
1081 int min_width = MIN_DOC_SIZE, min_height = MIN_DOC_SIZE;
1082 #if TARGET_API_MAC_CARBON
1083 Rect new_rect;
1084 #else
1085 long grow_size;
1086 #endif
1088 if (size_hints->flags & PMinSize)
1090 min_width = size_hints->min_width;
1091 min_height = size_hints->min_height;
1093 SetRect (&limit_rect, min_width, min_height, MAX_DOC_SIZE, MAX_DOC_SIZE);
1095 #if TARGET_API_MAC_CARBON
1096 if (!ResizeWindow (w, e->where, &limit_rect, &new_rect))
1097 return;
1098 height = new_rect.bottom - new_rect.top;
1099 width = new_rect.right - new_rect.left;
1100 #else
1101 grow_size = GrowWindow (w, e->where, &limit_rect);
1102 /* see if it really changed size */
1103 if (grow_size == 0)
1104 return;
1105 height = HiWord (grow_size);
1106 width = LoWord (grow_size);
1107 #endif
1109 if (width != FRAME_PIXEL_WIDTH (f)
1110 || height != FRAME_PIXEL_HEIGHT (f))
1112 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height);
1113 columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width);
1115 x_set_window_size (f, 0, columns, rows);
1119 #if TARGET_API_MAC_CARBON
1120 static Point
1121 mac_get_ideal_size (f)
1122 struct frame *f;
1124 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
1125 WindowRef w = FRAME_MAC_WINDOW (f);
1126 Point ideal_size;
1127 Rect standard_rect;
1128 int height, width, columns, rows;
1130 ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS);
1131 ideal_size.v = dpyinfo->height;
1132 IsWindowInStandardState (w, &ideal_size, &standard_rect);
1133 /* Adjust the standard size according to character boundaries. */
1134 width = standard_rect.right - standard_rect.left;
1135 height = standard_rect.bottom - standard_rect.top;
1136 columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width);
1137 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height);
1138 ideal_size.h = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, columns);
1139 ideal_size.v = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows);
1141 return ideal_size;
1144 static pascal OSStatus
1145 mac_handle_window_event (next_handler, event, data)
1146 EventHandlerCallRef next_handler;
1147 EventRef event;
1148 void *data;
1150 WindowRef wp;
1151 OSStatus err, result = eventNotHandledErr;
1152 struct frame *f;
1153 UInt32 attributes;
1154 XSizeHints *size_hints;
1156 err = GetEventParameter (event, kEventParamDirectObject, typeWindowRef,
1157 NULL, sizeof (WindowRef), NULL, &wp);
1158 if (err != noErr)
1159 return eventNotHandledErr;
1161 f = mac_window_to_frame (wp);
1162 switch (GetEventKind (event))
1164 /* -- window refresh events -- */
1166 case kEventWindowUpdate:
1167 result = CallNextEventHandler (next_handler, event);
1168 if (result != eventNotHandledErr)
1169 break;
1171 do_window_update (wp);
1172 result = noErr;
1173 break;
1175 /* -- window state change events -- */
1177 case kEventWindowShowing:
1178 size_hints = FRAME_SIZE_HINTS (f);
1179 if (!(size_hints->flags & (USPosition | PPosition)))
1181 struct frame *sf = SELECTED_FRAME ();
1183 if (!(FRAME_MAC_P (sf) && sf->async_visible))
1184 RepositionWindow (wp, NULL, kWindowCenterOnMainScreen);
1185 else
1187 RepositionWindow (wp, FRAME_MAC_WINDOW (sf),
1188 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
1189 kWindowCascadeStartAtParentWindowScreen
1190 #else
1191 kWindowCascadeOnParentWindowScreen
1192 #endif
1194 #if USE_MAC_TOOLBAR
1195 /* This is a workaround. RepositionWindow fails to put
1196 a window at the cascading position when its parent
1197 window has a Carbon HIToolbar. */
1198 if ((f->left_pos == sf->left_pos
1199 && f->top_pos == sf->top_pos)
1200 || (f->left_pos == sf->left_pos + 10 * 2
1201 && f->top_pos == sf->top_pos + 32 * 2))
1202 MoveWindowStructure (wp, sf->left_pos + 10, sf->top_pos + 32);
1203 #endif
1205 result = noErr;
1207 break;
1209 case kEventWindowHiding:
1210 /* Before unmapping the window, update the WM_SIZE_HINTS
1211 property to claim that the current position of the window is
1212 user-specified, rather than program-specified, so that when
1213 the window is mapped again, it will be placed at the same
1214 location, without forcing the user to position it by hand
1215 again (they have already done that once for this window.) */
1216 x_wm_set_size_hint (f, (long) 0, 1);
1217 result = noErr;
1218 break;
1220 case kEventWindowShown:
1221 case kEventWindowHidden:
1222 case kEventWindowCollapsed:
1223 case kEventWindowExpanded:
1224 mac_handle_visibility_change (f);
1225 result = noErr;
1226 break;
1228 case kEventWindowBoundsChanging:
1229 result = CallNextEventHandler (next_handler, event);
1230 if (result != eventNotHandledErr)
1231 break;
1233 err = GetEventParameter (event, kEventParamAttributes, typeUInt32,
1234 NULL, sizeof (UInt32), NULL, &attributes);
1235 if (err != noErr)
1236 break;
1238 size_hints = FRAME_SIZE_HINTS (f);
1239 if ((attributes & kWindowBoundsChangeUserResize)
1240 && ((size_hints->flags & (PResizeInc | PBaseSize | PMinSize))
1241 == (PResizeInc | PBaseSize | PMinSize)))
1243 Rect bounds;
1244 int width, height;
1246 err = GetEventParameter (event, kEventParamCurrentBounds,
1247 typeQDRectangle, NULL, sizeof (Rect),
1248 NULL, &bounds);
1249 if (err != noErr)
1250 break;
1252 width = bounds.right - bounds.left;
1253 height = bounds.bottom - bounds.top;
1255 if (width < size_hints->min_width)
1256 width = size_hints->min_width;
1257 else
1258 width = size_hints->base_width
1259 + (int) ((width - size_hints->base_width)
1260 / (float) size_hints->width_inc + .5)
1261 * size_hints->width_inc;
1263 if (height < size_hints->min_height)
1264 height = size_hints->min_height;
1265 else
1266 height = size_hints->base_height
1267 + (int) ((height - size_hints->base_height)
1268 / (float) size_hints->height_inc + .5)
1269 * size_hints->height_inc;
1271 bounds.right = bounds.left + width;
1272 bounds.bottom = bounds.top + height;
1273 SetEventParameter (event, kEventParamCurrentBounds,
1274 typeQDRectangle, sizeof (Rect), &bounds);
1275 result = noErr;
1277 break;
1279 case kEventWindowBoundsChanged:
1280 err = GetEventParameter (event, kEventParamAttributes, typeUInt32,
1281 NULL, sizeof (UInt32), NULL, &attributes);
1282 if (err != noErr)
1283 break;
1285 if (attributes & kWindowBoundsChangeSizeChanged)
1287 Rect bounds;
1289 err = GetEventParameter (event, kEventParamCurrentBounds,
1290 typeQDRectangle, NULL, sizeof (Rect),
1291 NULL, &bounds);
1292 if (err == noErr)
1294 int width, height;
1296 width = bounds.right - bounds.left;
1297 height = bounds.bottom - bounds.top;
1298 mac_handle_size_change (f, width, height);
1299 mac_wakeup_from_rne ();
1303 if (attributes & kWindowBoundsChangeOriginChanged)
1304 mac_handle_origin_change (f);
1306 result = noErr;
1307 break;
1309 /* -- window action events -- */
1311 case kEventWindowClose:
1313 struct input_event buf;
1315 EVENT_INIT (buf);
1316 buf.kind = DELETE_WINDOW_EVENT;
1317 XSETFRAME (buf.frame_or_window, f);
1318 buf.arg = Qnil;
1319 kbd_buffer_store_event (&buf);
1321 result = noErr;
1322 break;
1324 case kEventWindowGetIdealSize:
1325 result = CallNextEventHandler (next_handler, event);
1326 if (result != eventNotHandledErr)
1327 break;
1330 Point ideal_size = mac_get_ideal_size (f);
1332 err = SetEventParameter (event, kEventParamDimensions,
1333 typeQDPoint, sizeof (Point), &ideal_size);
1334 if (err == noErr)
1335 result = noErr;
1337 break;
1339 #ifdef MAC_OSX
1340 case kEventWindowToolbarSwitchMode:
1342 static const EventParamName names[] = {kEventParamDirectObject,
1343 kEventParamWindowMouseLocation,
1344 kEventParamKeyModifiers,
1345 kEventParamMouseButton,
1346 kEventParamClickCount,
1347 kEventParamMouseChord};
1348 static const EventParamType types[] = {typeWindowRef,
1349 typeQDPoint,
1350 typeUInt32,
1351 typeMouseButton,
1352 typeUInt32,
1353 typeUInt32};
1354 int num_params = sizeof (names) / sizeof (names[0]);
1356 err = mac_store_event_ref_as_apple_event (0, 0,
1357 Qwindow,
1358 Qtoolbar_switch_mode,
1359 event, num_params,
1360 names, types);
1362 if (err == noErr)
1363 result = noErr;
1364 break;
1365 #endif
1367 #if USE_MAC_TSM
1368 /* -- window focus events -- */
1370 case kEventWindowFocusAcquired:
1371 err = mac_tsm_resume ();
1372 if (err == noErr)
1373 result = noErr;
1374 break;
1376 case kEventWindowFocusRelinquish:
1377 err = mac_tsm_suspend ();
1378 if (err == noErr)
1379 result = noErr;
1380 break;
1381 #endif
1383 default:
1384 abort ();
1387 return result;
1389 #endif
1391 /* Handle clicks in zoom box. Calculation of "standard state" based
1392 on code in IM - Window Manager A and code contributed by Ben
1393 Mesander. The standard state of an Emacs window is 80-characters
1394 wide (DEFAULT_NUM_COLS) and as tall as will fit on the screen. */
1396 static void
1397 do_zoom_window (WindowRef w, int zoom_in_or_out)
1399 Rect zoom_rect, port_rect;
1400 int width, height;
1401 struct frame *f = mac_window_to_frame (w);
1402 #if TARGET_API_MAC_CARBON
1403 Point ideal_size = mac_get_ideal_size (f);
1405 GetWindowBounds (w, kWindowContentRgn, &port_rect);
1406 if (IsWindowInStandardState (w, &ideal_size, &zoom_rect)
1407 && port_rect.left == zoom_rect.left
1408 && port_rect.top == zoom_rect.top)
1409 zoom_in_or_out = inZoomIn;
1410 else
1411 zoom_in_or_out = inZoomOut;
1413 #ifdef MAC_OS8
1414 mac_clear_area (f, 0, 0, port_rect.right - port_rect.left,
1415 port_rect.bottom - port_rect.top);
1416 #endif
1417 ZoomWindowIdeal (w, zoom_in_or_out, &ideal_size);
1418 #else /* not TARGET_API_MAC_CARBON */
1419 GrafPtr save_port;
1420 Point top_left;
1421 int w_title_height, rows;
1422 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
1424 GetPort (&save_port);
1426 SetPortWindowPort (w);
1428 /* Clear window to avoid flicker. */
1429 EraseRect (&(w->portRect));
1430 if (zoom_in_or_out == inZoomOut)
1432 SetPt (&top_left, w->portRect.left, w->portRect.top);
1433 LocalToGlobal (&top_left);
1435 /* calculate height of window's title bar */
1436 w_title_height = top_left.v - 1
1437 - (**((WindowPeek) w)->strucRgn).rgnBBox.top + GetMBarHeight ();
1439 /* get maximum height of window into zoom_rect.bottom - zoom_rect.top */
1440 zoom_rect = qd.screenBits.bounds;
1441 zoom_rect.top += w_title_height;
1442 InsetRect (&zoom_rect, 8, 4); /* not too tight */
1444 zoom_rect.right = zoom_rect.left
1445 + FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, DEFAULT_NUM_COLS);
1447 /* Adjust the standard size according to character boundaries. */
1448 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, zoom_rect.bottom - zoom_rect.top);
1449 zoom_rect.bottom =
1450 zoom_rect.top + FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows);
1452 (**((WStateDataHandle) ((WindowPeek) w)->dataHandle)).stdState
1453 = zoom_rect;
1456 ZoomWindow (w, zoom_in_or_out, f == mac_focus_frame (dpyinfo));
1458 SetPort (save_port);
1459 #endif /* not TARGET_API_MAC_CARBON */
1461 #if !TARGET_API_MAC_CARBON
1462 /* retrieve window size and update application values */
1463 port_rect = w->portRect;
1464 height = port_rect.bottom - port_rect.top;
1465 width = port_rect.right - port_rect.left;
1467 mac_handle_size_change (f, width, height);
1468 mac_handle_origin_change (f);
1469 #endif
1472 static OSStatus
1473 install_window_handler (window)
1474 WindowRef window;
1476 OSStatus err = noErr;
1478 #if TARGET_API_MAC_CARBON
1479 if (err == noErr)
1481 static const EventTypeSpec specs[] =
1483 /* -- window refresh events -- */
1484 {kEventClassWindow, kEventWindowUpdate},
1485 /* -- window state change events -- */
1486 {kEventClassWindow, kEventWindowShowing},
1487 {kEventClassWindow, kEventWindowHiding},
1488 {kEventClassWindow, kEventWindowShown},
1489 {kEventClassWindow, kEventWindowHidden},
1490 {kEventClassWindow, kEventWindowCollapsed},
1491 {kEventClassWindow, kEventWindowExpanded},
1492 {kEventClassWindow, kEventWindowBoundsChanging},
1493 {kEventClassWindow, kEventWindowBoundsChanged},
1494 /* -- window action events -- */
1495 {kEventClassWindow, kEventWindowClose},
1496 {kEventClassWindow, kEventWindowGetIdealSize},
1497 #ifdef MAC_OSX
1498 {kEventClassWindow, kEventWindowToolbarSwitchMode},
1499 #endif
1500 #if USE_MAC_TSM
1501 /* -- window focus events -- */
1502 {kEventClassWindow, kEventWindowFocusAcquired},
1503 {kEventClassWindow, kEventWindowFocusRelinquish},
1504 #endif
1506 static EventHandlerUPP handle_window_eventUPP = NULL;
1508 if (handle_window_eventUPP == NULL)
1509 handle_window_eventUPP = NewEventHandlerUPP (mac_handle_window_event);
1511 err = InstallWindowEventHandler (window, handle_window_eventUPP,
1512 GetEventTypeCount (specs),
1513 specs, NULL, NULL);
1515 #endif
1517 if (err == noErr)
1518 err = install_drag_handler (window);
1520 return err;
1523 static void
1524 remove_window_handler (window)
1525 WindowRef window;
1527 remove_drag_handler (window);
1530 void
1531 mac_get_window_bounds (f, inner, outer)
1532 struct frame *f;
1533 Rect *inner, *outer;
1535 #if TARGET_API_MAC_CARBON
1536 GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowContentRgn, inner);
1537 GetWindowBounds (FRAME_MAC_WINDOW (f), kWindowStructureRgn, outer);
1538 #else /* not TARGET_API_MAC_CARBON */
1539 RgnHandle region = NewRgn ();
1541 GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowContentRgn, region);
1542 *inner = (*region)->rgnBBox;
1543 GetWindowRegion (FRAME_MAC_WINDOW (f), kWindowStructureRgn, region);
1544 *outer = (*region)->rgnBBox;
1545 DisposeRgn (region);
1546 #endif /* not TARGET_API_MAC_CARBON */
1549 Rect *
1550 mac_get_frame_bounds (f, r)
1551 struct frame *f;
1552 Rect *r;
1554 #if TARGET_API_MAC_CARBON
1555 return GetWindowPortBounds (FRAME_MAC_WINDOW (f), r);
1556 #else
1557 *r = FRAME_MAC_WINDOW (f)->portRect;
1559 return r;
1560 #endif
1563 void
1564 mac_get_frame_mouse (f, point)
1565 struct frame *f;
1566 Point *point;
1568 #if TARGET_API_MAC_CARBON
1569 GetGlobalMouse (point);
1570 point->h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1571 point->v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
1572 #else
1573 SetPortWindowPort (FRAME_MAC_WINDOW (f));
1574 GetMouse (point);
1575 #endif
1578 void
1579 mac_convert_frame_point_to_global (f, x, y)
1580 struct frame *f;
1581 int *x, *y;
1583 *x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1584 *y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
1587 #if TARGET_API_MAC_CARBON
1588 void
1589 mac_update_proxy_icon (f)
1590 struct frame *f;
1592 OSStatus err;
1593 Lisp_Object file_name =
1594 XBUFFER (XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer)->filename;
1595 Window w = FRAME_MAC_WINDOW (f);
1596 AliasHandle alias = NULL;
1598 err = GetWindowProxyAlias (w, &alias);
1599 if (err == errWindowDoesNotHaveProxy && !STRINGP (file_name))
1600 return;
1602 if (STRINGP (file_name))
1604 AEDesc desc;
1605 #ifdef MAC_OSX
1606 FSRef fref, fref_proxy;
1607 #else
1608 FSSpec fss, fss_proxy;
1609 #endif
1610 Boolean changed;
1611 Lisp_Object encoded_file_name = ENCODE_FILE (file_name);
1613 #ifdef MAC_OSX
1614 err = AECoercePtr (TYPE_FILE_NAME, SDATA (encoded_file_name),
1615 SBYTES (encoded_file_name), typeFSRef, &desc);
1616 #else
1617 SetPortWindowPort (w);
1618 err = AECoercePtr (TYPE_FILE_NAME, SDATA (encoded_file_name),
1619 SBYTES (encoded_file_name), typeFSS, &desc);
1620 #endif
1621 if (err == noErr)
1623 #ifdef MAC_OSX
1624 err = AEGetDescData (&desc, &fref, sizeof (FSRef));
1625 #else
1626 err = AEGetDescData (&desc, &fss, sizeof (FSSpec));
1627 #endif
1628 AEDisposeDesc (&desc);
1630 if (err == noErr)
1632 if (alias)
1634 /* (FS)ResolveAlias never sets `changed' to true if
1635 `alias' is minimal. */
1636 #ifdef MAC_OSX
1637 err = FSResolveAlias (NULL, alias, &fref_proxy, &changed);
1638 if (err == noErr)
1639 err = FSCompareFSRefs (&fref, &fref_proxy);
1640 #else
1641 err = ResolveAlias (NULL, alias, &fss_proxy, &changed);
1642 if (err == noErr)
1643 err = !(fss.vRefNum == fss_proxy.vRefNum
1644 && fss.parID == fss_proxy.parID
1645 && EqualString (fss.name, fss_proxy.name,
1646 false, true));
1647 #endif
1649 if (err != noErr || alias == NULL)
1651 if (alias)
1652 DisposeHandle ((Handle) alias);
1653 #ifdef MAC_OSX
1654 err = FSNewAliasMinimal (&fref, &alias);
1655 #else
1656 err = NewAliasMinimal (&fss, &alias);
1657 #endif
1658 changed = true;
1661 if (err == noErr)
1662 if (changed)
1663 err = SetWindowProxyAlias (w, alias);
1666 if (alias)
1667 DisposeHandle ((Handle) alias);
1669 if (err != noErr || !STRINGP (file_name))
1670 RemoveWindowProxy (w);
1672 #endif
1674 /* Mac replacement for XSetWindowBackground. */
1676 void
1677 mac_set_frame_window_background (f, color)
1678 struct frame *f;
1679 unsigned long color;
1681 WindowRef w = FRAME_MAC_WINDOW (f);
1682 #if !TARGET_API_MAC_CARBON
1683 AuxWinHandle aw_handle;
1684 CTabHandle ctab_handle;
1685 ColorSpecPtr ct_table;
1686 short ct_size;
1687 #endif
1688 RGBColor bg_color;
1690 bg_color.red = RED16_FROM_ULONG (color);
1691 bg_color.green = GREEN16_FROM_ULONG (color);
1692 bg_color.blue = BLUE16_FROM_ULONG (color);
1694 #if TARGET_API_MAC_CARBON
1695 SetWindowContentColor (w, &bg_color);
1696 #else
1697 if (GetAuxWin (w, &aw_handle))
1699 ctab_handle = (*aw_handle)->awCTable;
1700 HandToHand ((Handle *) &ctab_handle);
1701 ct_table = (*ctab_handle)->ctTable;
1702 ct_size = (*ctab_handle)->ctSize;
1703 while (ct_size > -1)
1705 if (ct_table->value == 0)
1707 ct_table->rgb = bg_color;
1708 CTabChanged (ctab_handle);
1709 SetWinColor (w, (WCTabHandle) ctab_handle);
1711 ct_size--;
1714 #endif
1717 /* Flush display of frame F, or of all frames if F is null. */
1719 void
1720 x_flush (f)
1721 struct frame *f;
1723 #if TARGET_API_MAC_CARBON
1724 BLOCK_INPUT;
1725 #if USE_CG_DRAWING
1726 mac_prepare_for_quickdraw (f);
1727 #endif
1728 if (f)
1729 QDFlushPortBuffer (GetWindowPort (FRAME_MAC_WINDOW (f)), NULL);
1730 else
1731 QDFlushPortBuffer (GetQDGlobalsThePort (), NULL);
1732 UNBLOCK_INPUT;
1733 #endif
1736 #if USE_CG_DRAWING
1737 void
1738 mac_flush_display_optional (f)
1739 struct frame *f;
1741 BLOCK_INPUT;
1742 mac_prepare_for_quickdraw (f);
1743 UNBLOCK_INPUT;
1745 #endif
1747 void
1748 mac_update_begin (f)
1749 struct frame *f;
1751 #if TARGET_API_MAC_CARBON
1752 /* During update of a frame, availability of input events is
1753 periodically checked with ReceiveNextEvent if
1754 redisplay-dont-pause is nil. That normally flushes window buffer
1755 changes for every check, and thus screen update looks waving even
1756 if no input is available. So we disable screen updates during
1757 update of a frame. */
1758 DisableScreenUpdates ();
1759 #endif
1762 void
1763 mac_update_end (f)
1764 struct frame *f;
1766 #if TARGET_API_MAC_CARBON
1767 EnableScreenUpdates ();
1768 #endif
1771 void
1772 mac_frame_up_to_date (f)
1773 struct frame *f;
1775 /* Nothing to do. */
1778 void
1779 mac_create_frame_window (f, tooltip_p)
1780 struct frame *f;
1781 int tooltip_p;
1783 Rect r;
1784 #if TARGET_API_MAC_CARBON
1785 WindowClass window_class;
1786 WindowAttributes attributes;
1787 #else
1788 short proc_id;
1789 WindowRef behind;
1790 Boolean go_away_flag;
1791 #endif
1793 if (!tooltip_p)
1795 SetRect (&r, f->left_pos, f->top_pos,
1796 f->left_pos + FRAME_PIXEL_WIDTH (f),
1797 f->top_pos + FRAME_PIXEL_HEIGHT (f));
1798 #if TARGET_API_MAC_CARBON
1799 window_class = kDocumentWindowClass;
1800 attributes = (kWindowStandardDocumentAttributes
1801 #ifdef MAC_OSX
1802 | kWindowToolbarButtonAttribute
1803 #endif
1805 #else
1806 proc_id = zoomDocProc;
1807 behind = (WindowRef) -1;
1808 go_away_flag = true;
1809 #endif
1811 else
1813 SetRect (&r, 0, 0, 1, 1);
1814 #if TARGET_API_MAC_CARBON
1815 window_class = kHelpWindowClass;
1816 attributes = (kWindowNoUpdatesAttribute
1817 | kWindowNoActivatesAttribute
1818 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
1819 | kWindowIgnoreClicksAttribute
1820 #endif
1822 #else
1823 proc_id = plainDBox;
1824 behind = NULL;
1825 go_away_flag = false;
1826 #endif
1829 #if TARGET_API_MAC_CARBON
1830 CreateNewWindow (window_class, attributes, &r, &FRAME_MAC_WINDOW (f));
1831 if (FRAME_MAC_WINDOW (f))
1833 SetWRefCon (FRAME_MAC_WINDOW (f), (long) f->output_data.mac);
1834 if (!tooltip_p)
1835 if (install_window_handler (FRAME_MAC_WINDOW (f)) != noErr)
1837 DisposeWindow (FRAME_MAC_WINDOW (f));
1838 FRAME_MAC_WINDOW (f) = NULL;
1841 #else /* !TARGET_API_MAC_CARBON */
1842 FRAME_MAC_WINDOW (f)
1843 = NewCWindow (NULL, &r, "\p", false, proc_id, behind, go_away_flag,
1844 (long) f->output_data.mac);
1845 #endif /* !TARGET_API_MAC_CARBON */
1846 /* so that update events can find this mac_output struct */
1847 f->output_data.mac->mFP = f; /* point back to emacs frame */
1849 #ifndef MAC_OSX
1850 if (!tooltip_p)
1851 if (FRAME_MAC_WINDOW (f))
1853 ControlRef root_control;
1855 if (CreateRootControl (FRAME_MAC_WINDOW (f), &root_control) != noErr)
1857 DisposeWindow (FRAME_MAC_WINDOW (f));
1858 FRAME_MAC_WINDOW (f) = NULL;
1861 #endif
1864 /* Dispose of the Mac window of the frame F. */
1866 void
1867 mac_dispose_frame_window (f)
1868 struct frame *f;
1870 WindowRef window = FRAME_MAC_WINDOW (f);
1872 if (window != tip_window)
1873 remove_window_handler (window);
1875 #if USE_CG_DRAWING
1876 mac_prepare_for_quickdraw (f);
1877 #endif
1878 DisposeWindow (window);
1882 /************************************************************************
1883 View and Drawing
1884 ************************************************************************/
1886 #if USE_CG_DRAWING
1887 #define FRAME_CG_CONTEXT(f) ((f)->output_data.mac->cg_context)
1889 CGContextRef
1890 mac_begin_cg_clip (f, gc)
1891 struct frame *f;
1892 GC gc;
1894 CGContextRef context = FRAME_CG_CONTEXT (f);
1896 if (!context)
1898 QDBeginCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)), &context);
1899 FRAME_CG_CONTEXT (f) = context;
1902 CGContextSaveGState (context);
1903 CGContextTranslateCTM (context, 0, FRAME_PIXEL_HEIGHT (f));
1904 CGContextScaleCTM (context, 1, -1);
1905 if (gc && gc->n_clip_rects)
1906 CGContextClipToRects (context, gc->clip_rects, gc->n_clip_rects);
1908 return context;
1911 void
1912 mac_end_cg_clip (f)
1913 struct frame *f;
1915 CGContextRestoreGState (FRAME_CG_CONTEXT (f));
1918 static void
1919 mac_prepare_for_quickdraw (f)
1920 struct frame *f;
1922 if (f == NULL)
1924 Lisp_Object rest, frame;
1925 FOR_EACH_FRAME (rest, frame)
1926 if (FRAME_MAC_P (XFRAME (frame)))
1927 mac_prepare_for_quickdraw (XFRAME (frame));
1929 else
1931 CGContextRef context = FRAME_CG_CONTEXT (f);
1933 if (context)
1935 CGContextSynchronize (context);
1936 QDEndCGContext (GetWindowPort (FRAME_MAC_WINDOW (f)),
1937 &FRAME_CG_CONTEXT (f));
1941 #endif
1943 static RgnHandle saved_port_clip_region = NULL;
1945 void
1946 mac_begin_clip (f, gc)
1947 struct frame *f;
1948 GC gc;
1950 static RgnHandle new_region = NULL;
1952 if (saved_port_clip_region == NULL)
1953 saved_port_clip_region = NewRgn ();
1954 if (new_region == NULL)
1955 new_region = NewRgn ();
1957 #if USE_CG_DRAWING
1958 mac_prepare_for_quickdraw (f);
1959 #endif
1960 SetPortWindowPort (FRAME_MAC_WINDOW (f));
1962 if (gc && gc->n_clip_rects)
1964 GetClip (saved_port_clip_region);
1965 SectRgn (saved_port_clip_region, gc->clip_region, new_region);
1966 SetClip (new_region);
1970 void
1971 mac_end_clip (f, gc)
1972 struct frame *f;
1973 GC gc;
1975 if (gc && gc->n_clip_rects)
1976 SetClip (saved_port_clip_region);
1979 #if TARGET_API_MAC_CARBON
1980 /* Mac replacement for XCopyArea: used only for scrolling. */
1982 void
1983 mac_scroll_area (f, gc, src_x, src_y, width, height, dest_x, dest_y)
1984 struct frame *f;
1985 GC gc;
1986 int src_x, src_y;
1987 unsigned int width, height;
1988 int dest_x, dest_y;
1990 Rect src_r;
1991 RgnHandle dummy = NewRgn (); /* For avoiding update events. */
1993 SetRect (&src_r, src_x, src_y, src_x + width, src_y + height);
1994 #if USE_CG_DRAWING
1995 mac_prepare_for_quickdraw (f);
1996 #endif
1997 ScrollWindowRect (FRAME_MAC_WINDOW (f),
1998 &src_r, dest_x - src_x, dest_y - src_y,
1999 kScrollWindowNoOptions, dummy);
2000 DisposeRgn (dummy);
2002 #endif
2005 /************************************************************************
2006 Scroll bars
2007 ************************************************************************/
2009 extern struct scroll_bar *tracked_scroll_bar;
2010 extern Lisp_Object last_mouse_scroll_bar;
2011 extern Time last_mouse_movement_time;
2013 static void x_scroll_bar_handle_click P_ ((struct scroll_bar *,
2014 ControlPartCode,
2015 const EventRecord *,
2016 struct input_event *));
2017 #ifndef USE_TOOLKIT_SCROLL_BARS
2018 static void x_scroll_bar_note_movement P_ ((struct scroll_bar *, int, Time));
2019 #else /* USE_TOOLKIT_SCROLL_BARS */
2020 static void x_scroll_bar_handle_press P_ ((struct scroll_bar *,
2021 ControlPartCode, Point,
2022 struct input_event *));
2023 static void x_scroll_bar_handle_release P_ ((struct scroll_bar *,
2024 struct input_event *));
2025 static void x_scroll_bar_handle_drag P_ ((WindowRef, struct scroll_bar *,
2026 Point, struct input_event *));
2027 static pascal void scroll_bar_timer_callback P_ ((EventLoopTimerRef, void *));
2028 static OSStatus install_scroll_bar_timer P_ ((void));
2029 static OSStatus set_scroll_bar_timer P_ ((EventTimerInterval));
2030 static int control_part_code_to_scroll_bar_part P_ ((ControlPartCode));
2031 static void construct_scroll_bar_click P_ ((struct scroll_bar *, int,
2032 struct input_event *));
2033 static OSStatus get_control_part_bounds P_ ((ControlRef, ControlPartCode,
2034 Rect *));
2035 static void update_scroll_bar_track_info P_ ((struct scroll_bar *));
2037 /* Last scroll bar part sent in x_scroll_bar_handle_*. */
2039 static int last_scroll_bar_part;
2041 static EventLoopTimerRef scroll_bar_timer;
2043 static int scroll_bar_timer_event_posted_p;
2045 #define SCROLL_BAR_FIRST_DELAY 0.5
2046 #define SCROLL_BAR_CONTINUOUS_DELAY (1.0 / 15)
2048 static pascal void
2049 scroll_bar_timer_callback (timer, data)
2050 EventLoopTimerRef timer;
2051 void *data;
2053 OSStatus err;
2055 err = mac_post_mouse_moved_event ();
2056 if (err == noErr)
2057 scroll_bar_timer_event_posted_p = 1;
2060 static OSStatus
2061 install_scroll_bar_timer ()
2063 static EventLoopTimerUPP scroll_bar_timer_callbackUPP = NULL;
2065 if (scroll_bar_timer_callbackUPP == NULL)
2066 scroll_bar_timer_callbackUPP =
2067 NewEventLoopTimerUPP (scroll_bar_timer_callback);
2069 if (scroll_bar_timer == NULL)
2070 /* Mac OS X and CarbonLib 1.5 and later allow us to specify
2071 kEventDurationForever as delays. */
2072 return
2073 InstallEventLoopTimer (GetCurrentEventLoop (),
2074 kEventDurationForever, kEventDurationForever,
2075 scroll_bar_timer_callbackUPP, NULL,
2076 &scroll_bar_timer);
2079 static OSStatus
2080 set_scroll_bar_timer (delay)
2081 EventTimerInterval delay;
2083 if (scroll_bar_timer == NULL)
2084 install_scroll_bar_timer ();
2086 scroll_bar_timer_event_posted_p = 0;
2088 return SetEventLoopTimerNextFireTime (scroll_bar_timer, delay);
2091 static int
2092 control_part_code_to_scroll_bar_part (part_code)
2093 ControlPartCode part_code;
2095 switch (part_code)
2097 case kControlUpButtonPart: return scroll_bar_up_arrow;
2098 case kControlDownButtonPart: return scroll_bar_down_arrow;
2099 case kControlPageUpPart: return scroll_bar_above_handle;
2100 case kControlPageDownPart: return scroll_bar_below_handle;
2101 case kControlIndicatorPart: return scroll_bar_handle;
2104 return -1;
2107 static void
2108 construct_scroll_bar_click (bar, part, bufp)
2109 struct scroll_bar *bar;
2110 int part;
2111 struct input_event *bufp;
2113 bufp->kind = SCROLL_BAR_CLICK_EVENT;
2114 bufp->frame_or_window = bar->window;
2115 bufp->arg = Qnil;
2116 bufp->part = part;
2117 bufp->code = 0;
2118 XSETINT (bufp->x, 0);
2119 XSETINT (bufp->y, 0);
2120 bufp->modifiers = 0;
2123 static OSStatus
2124 get_control_part_bounds (ch, part_code, rect)
2125 ControlRef ch;
2126 ControlPartCode part_code;
2127 Rect *rect;
2129 RgnHandle region = NewRgn ();
2130 OSStatus err;
2132 err = GetControlRegion (ch, part_code, region);
2133 if (err == noErr)
2134 GetRegionBounds (region, rect);
2135 DisposeRgn (region);
2137 return err;
2140 static void
2141 x_scroll_bar_handle_press (bar, part_code, mouse_pos, bufp)
2142 struct scroll_bar *bar;
2143 ControlPartCode part_code;
2144 Point mouse_pos;
2145 struct input_event *bufp;
2147 int part = control_part_code_to_scroll_bar_part (part_code);
2149 if (part < 0)
2150 return;
2152 if (part != scroll_bar_handle)
2154 construct_scroll_bar_click (bar, part, bufp);
2155 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code);
2156 set_scroll_bar_timer (SCROLL_BAR_FIRST_DELAY);
2157 bar->dragging = Qnil;
2159 else
2161 Rect r;
2163 get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar),
2164 kControlIndicatorPart, &r);
2165 XSETINT (bar->dragging, - (mouse_pos.v - r.top) - 1);
2168 last_scroll_bar_part = part;
2169 tracked_scroll_bar = bar;
2172 static void
2173 x_scroll_bar_handle_release (bar, bufp)
2174 struct scroll_bar *bar;
2175 struct input_event *bufp;
2177 if (last_scroll_bar_part != scroll_bar_handle
2178 || (INTEGERP (bar->dragging) && XINT (bar->dragging) >= 0))
2179 construct_scroll_bar_click (bar, scroll_bar_end_scroll, bufp);
2181 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0);
2182 set_scroll_bar_timer (kEventDurationForever);
2184 last_scroll_bar_part = -1;
2185 bar->dragging = Qnil;
2186 tracked_scroll_bar = NULL;
2189 static void
2190 x_scroll_bar_handle_drag (win, bar, mouse_pos, bufp)
2191 WindowRef win;
2192 struct scroll_bar *bar;
2193 Point mouse_pos;
2194 struct input_event *bufp;
2196 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2198 if (last_scroll_bar_part == scroll_bar_handle)
2200 int top, top_range;
2201 Rect r;
2203 get_control_part_bounds (SCROLL_BAR_CONTROL_REF (bar),
2204 kControlIndicatorPart, &r);
2206 if (INTEGERP (bar->dragging) && XINT (bar->dragging) < 0)
2207 XSETINT (bar->dragging, - (XINT (bar->dragging) + 1));
2209 top = mouse_pos.v - XINT (bar->dragging) - XINT (bar->track_top);
2210 top_range = XINT (bar->track_height) - XINT (bar->min_handle);
2212 if (top < 0)
2213 top = 0;
2214 if (top > top_range)
2215 top = top_range;
2217 construct_scroll_bar_click (bar, scroll_bar_handle, bufp);
2218 XSETINT (bufp->x, top);
2219 XSETINT (bufp->y, top_range);
2221 else
2223 ControlPartCode part_code;
2224 int unhilite_p = 0, part;
2226 if (ch != FindControlUnderMouse (mouse_pos, win, &part_code))
2227 unhilite_p = 1;
2228 else
2230 part = control_part_code_to_scroll_bar_part (part_code);
2232 switch (last_scroll_bar_part)
2234 case scroll_bar_above_handle:
2235 case scroll_bar_below_handle:
2236 if (part != scroll_bar_above_handle
2237 && part != scroll_bar_below_handle)
2238 unhilite_p = 1;
2239 break;
2241 case scroll_bar_up_arrow:
2242 case scroll_bar_down_arrow:
2243 if (part != scroll_bar_up_arrow
2244 && part != scroll_bar_down_arrow)
2245 unhilite_p = 1;
2246 break;
2250 if (unhilite_p)
2251 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), 0);
2252 else if (part != last_scroll_bar_part
2253 || scroll_bar_timer_event_posted_p)
2255 construct_scroll_bar_click (bar, part, bufp);
2256 last_scroll_bar_part = part;
2257 HiliteControl (SCROLL_BAR_CONTROL_REF (bar), part_code);
2258 set_scroll_bar_timer (SCROLL_BAR_CONTINUOUS_DELAY);
2263 /* Update BAR->track_top, BAR->track_height, and BAR->min_handle for
2264 the scroll bar BAR. This function should be called when the bounds
2265 of the scroll bar is changed. */
2267 static void
2268 update_scroll_bar_track_info (bar)
2269 struct scroll_bar *bar;
2271 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2272 Rect r0, r1;
2274 GetControlBounds (ch, &r0);
2276 if (r0.right - r0.left >= r0.bottom - r0.top
2277 #ifdef MAC_OSX
2278 || r0.right - r0.left < MAC_AQUA_SMALL_VERTICAL_SCROLL_BAR_WIDTH
2279 #endif
2282 XSETINT (bar->track_top, 0);
2283 XSETINT (bar->track_height, 0);
2284 XSETINT (bar->min_handle, 0);
2286 else
2288 BLOCK_INPUT;
2290 SetControl32BitMinimum (ch, 0);
2291 SetControl32BitMaximum (ch, 1 << 30);
2292 SetControlViewSize (ch, 1);
2294 /* Move the scroll bar thumb to the top. */
2295 SetControl32BitValue (ch, 0);
2296 get_control_part_bounds (ch, kControlIndicatorPart, &r0);
2298 /* Move the scroll bar thumb to the bottom. */
2299 SetControl32BitValue (ch, 1 << 30);
2300 get_control_part_bounds (ch, kControlIndicatorPart, &r1);
2302 UnionRect (&r0, &r1, &r0);
2303 XSETINT (bar->track_top, r0.top);
2304 XSETINT (bar->track_height, r0.bottom - r0.top);
2305 XSETINT (bar->min_handle, r1.bottom - r1.top);
2307 /* Don't show the scroll bar if its height is not enough to
2308 display the scroll bar thumb. */
2309 if (r0.bottom - r0.top > 0)
2310 ShowControl (ch);
2312 UNBLOCK_INPUT;
2316 /* Set the thumb size and position of scroll bar BAR. We are currently
2317 displaying PORTION out of a whole WHOLE, and our position POSITION. */
2319 void
2320 x_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
2321 struct scroll_bar *bar;
2322 int portion, position, whole;
2324 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2325 int value, viewsize, maximum;
2327 if (XINT (bar->track_height) == 0)
2328 return;
2330 if (whole <= portion)
2331 value = 0, viewsize = 1, maximum = 0;
2332 else
2334 float scale;
2336 maximum = XINT (bar->track_height) - XINT (bar->min_handle);
2337 scale = (float) maximum / (whole - portion);
2338 value = position * scale + 0.5f;
2339 viewsize = (int) (portion * scale + 0.5f) + XINT (bar->min_handle);
2342 BLOCK_INPUT;
2344 if (GetControlViewSize (ch) != viewsize
2345 || GetControl32BitValue (ch) != value
2346 || GetControl32BitMaximum (ch) != maximum)
2348 /* Temporarily hide the scroll bar to avoid multiple redraws. */
2349 SetControlVisibility (ch, false, false);
2351 SetControl32BitMaximum (ch, maximum);
2352 SetControl32BitValue (ch, value);
2353 SetControlViewSize (ch, viewsize);
2355 SetControlVisibility (ch, true, true);
2358 UNBLOCK_INPUT;
2361 #endif /* USE_TOOLKIT_SCROLL_BARS */
2363 /* Create a scroll bar control for BAR. BOUNDS and VISIBLE specifies
2364 the initial geometry and visibility, respectively. The created
2365 control is stored in some members of BAR. */
2367 void
2368 mac_create_scroll_bar (bar, bounds, visible)
2369 struct scroll_bar *bar;
2370 const Rect *bounds;
2371 Boolean visible;
2373 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2374 ControlRef ch;
2376 #if USE_CG_DRAWING
2377 mac_prepare_for_quickdraw (f);
2378 #endif
2379 ch = NewControl (FRAME_MAC_WINDOW (f), bounds, "\p", visible, 0, 0, 0,
2380 #if TARGET_API_MAC_CARBON
2381 kControlScrollBarProc,
2382 #else
2383 scrollBarProc,
2384 #endif
2385 (SInt32) bar);
2386 SET_SCROLL_BAR_CONTROL_REF (bar, ch);
2388 XSETINT (bar->start, 0);
2389 XSETINT (bar->end, 0);
2390 bar->dragging = Qnil;
2392 #ifdef USE_TOOLKIT_SCROLL_BARS
2393 update_scroll_bar_track_info (bar);
2394 #endif
2397 /* Dispose of the scroll bar control stored in some members of
2398 BAR. */
2400 void
2401 mac_dispose_scroll_bar (bar)
2402 struct scroll_bar *bar;
2404 #if USE_CG_DRAWING
2405 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2407 mac_prepare_for_quickdraw (f);
2408 #endif
2409 DisposeControl (SCROLL_BAR_CONTROL_REF (bar));
2412 /* Set bounds of the scroll bar BAR to BOUNDS. */
2414 void
2415 mac_set_scroll_bar_bounds (bar, bounds)
2416 struct scroll_bar *bar;
2417 const Rect *bounds;
2419 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2420 SInt16 width, height;
2421 #if USE_CG_DRAWING
2422 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2424 mac_prepare_for_quickdraw (f);
2425 #endif
2427 width = bounds->right - bounds->left;
2428 height = bounds->bottom - bounds->top;
2429 HideControl (ch);
2430 MoveControl (ch, bounds->left, bounds->top);
2431 SizeControl (ch, width, height);
2432 #ifdef USE_TOOLKIT_SCROLL_BARS
2433 update_scroll_bar_track_info (bar);
2434 #else
2435 if (width < height)
2436 ShowControl (ch);
2437 #endif
2440 /* Draw the scroll bar BAR. */
2442 void
2443 mac_redraw_scroll_bar (bar)
2444 struct scroll_bar *bar;
2446 #if USE_CG_DRAWING
2447 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2449 mac_prepare_for_quickdraw (f);
2450 #endif
2451 Draw1Control (SCROLL_BAR_CONTROL_REF (bar));
2454 /* Handle a mouse click on the scroll bar BAR. If *EMACS_EVENT's kind
2455 is set to something other than NO_EVENT, it is enqueued.
2457 This may be called from a signal handler, so we have to ignore GC
2458 mark bits. */
2460 static void
2461 x_scroll_bar_handle_click (bar, part_code, er, bufp)
2462 struct scroll_bar *bar;
2463 ControlPartCode part_code;
2464 const EventRecord *er;
2465 struct input_event *bufp;
2467 int win_y, top_range;
2469 if (! GC_WINDOWP (bar->window))
2470 abort ();
2472 bufp->kind = SCROLL_BAR_CLICK_EVENT;
2473 bufp->frame_or_window = bar->window;
2474 bufp->arg = Qnil;
2476 bar->dragging = Qnil;
2478 switch (part_code)
2480 case kControlUpButtonPart:
2481 bufp->part = scroll_bar_up_arrow;
2482 break;
2483 case kControlDownButtonPart:
2484 bufp->part = scroll_bar_down_arrow;
2485 break;
2486 case kControlPageUpPart:
2487 bufp->part = scroll_bar_above_handle;
2488 break;
2489 case kControlPageDownPart:
2490 bufp->part = scroll_bar_below_handle;
2491 break;
2492 #if TARGET_API_MAC_CARBON
2493 default:
2494 #else
2495 case kControlIndicatorPart:
2496 #endif
2497 if (er->what == mouseDown)
2498 bar->dragging = make_number (0);
2499 XSETVECTOR (last_mouse_scroll_bar, bar);
2500 bufp->part = scroll_bar_handle;
2501 break;
2504 win_y = XINT (bufp->y) - XINT (bar->top);
2505 top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (0/*dummy*/, XINT (bar->height));
2507 win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER;
2509 win_y -= 24;
2511 if (! NILP (bar->dragging))
2512 win_y -= XINT (bar->dragging);
2514 if (win_y < 0)
2515 win_y = 0;
2516 if (win_y > top_range)
2517 win_y = top_range;
2519 XSETINT (bufp->x, win_y);
2520 XSETINT (bufp->y, top_range);
2523 /* Return information to the user about the current position of the mouse
2524 on the scroll bar. */
2526 void
2527 x_scroll_bar_report_motion (fp, bar_window, part, x, y, time)
2528 FRAME_PTR *fp;
2529 Lisp_Object *bar_window;
2530 enum scroll_bar_part *part;
2531 Lisp_Object *x, *y;
2532 unsigned long *time;
2534 struct scroll_bar *bar = XSCROLL_BAR (last_mouse_scroll_bar);
2535 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2536 #if TARGET_API_MAC_CARBON
2537 WindowRef wp = GetControlOwner (ch);
2538 #else
2539 WindowRef wp = (*ch)->contrlOwner;
2540 #endif
2541 Point mouse_pos;
2542 struct frame *f = mac_window_to_frame (wp);
2543 int win_y, top_range;
2545 #if TARGET_API_MAC_CARBON
2546 GetGlobalMouse (&mouse_pos);
2547 mouse_pos.h -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
2548 mouse_pos.v -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
2549 #else
2550 SetPortWindowPort (wp);
2551 GetMouse (&mouse_pos);
2552 #endif
2554 win_y = mouse_pos.v - XINT (bar->top);
2555 top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height));
2557 win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER;
2559 win_y -= 24;
2561 if (! NILP (bar->dragging))
2562 win_y -= XINT (bar->dragging);
2564 if (win_y < 0)
2565 win_y = 0;
2566 if (win_y > top_range)
2567 win_y = top_range;
2569 *fp = f;
2570 *bar_window = bar->window;
2572 if (! NILP (bar->dragging))
2573 *part = scroll_bar_handle;
2574 else if (win_y < XINT (bar->start))
2575 *part = scroll_bar_above_handle;
2576 else if (win_y < XINT (bar->end) + VERTICAL_SCROLL_BAR_MIN_HANDLE)
2577 *part = scroll_bar_handle;
2578 else
2579 *part = scroll_bar_below_handle;
2581 XSETINT (*x, win_y);
2582 XSETINT (*y, top_range);
2584 f->mouse_moved = 0;
2585 last_mouse_scroll_bar = Qnil;
2587 *time = last_mouse_movement_time;
2590 #ifndef USE_TOOLKIT_SCROLL_BARS
2591 /* Draw BAR's handle in the proper position.
2593 If the handle is already drawn from START to END, don't bother
2594 redrawing it, unless REBUILD is non-zero; in that case, always
2595 redraw it. (REBUILD is handy for drawing the handle after expose
2596 events.)
2598 Normally, we want to constrain the start and end of the handle to
2599 fit inside its rectangle, but if the user is dragging the scroll
2600 bar handle, we want to let them drag it down all the way, so that
2601 the bar's top is as far down as it goes; otherwise, there's no way
2602 to move to the very end of the buffer. */
2604 void
2605 x_scroll_bar_set_handle (bar, start, end, rebuild)
2606 struct scroll_bar *bar;
2607 int start, end;
2608 int rebuild;
2610 int dragging = ! NILP (bar->dragging);
2611 ControlRef ch = SCROLL_BAR_CONTROL_REF (bar);
2612 FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2613 int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, XINT (bar->height));
2614 int length = end - start;
2616 /* If the display is already accurate, do nothing. */
2617 if (! rebuild
2618 && start == XINT (bar->start)
2619 && end == XINT (bar->end))
2620 return;
2622 BLOCK_INPUT;
2624 /* Make sure the values are reasonable, and try to preserve the
2625 distance between start and end. */
2626 if (start < 0)
2627 start = 0;
2628 else if (start > top_range)
2629 start = top_range;
2630 end = start + length;
2632 if (end < start)
2633 end = start;
2634 else if (end > top_range && ! dragging)
2635 end = top_range;
2637 /* Store the adjusted setting in the scroll bar. */
2638 XSETINT (bar->start, start);
2639 XSETINT (bar->end, end);
2641 /* Clip the end position, just for display. */
2642 if (end > top_range)
2643 end = top_range;
2645 /* Draw bottom positions VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
2646 top positions, to make sure the handle is always at least that
2647 many pixels tall. */
2648 end += VERTICAL_SCROLL_BAR_MIN_HANDLE;
2650 SetControlMinimum (ch, 0);
2651 /* Don't inadvertently activate deactivated scroll bars */
2652 if (GetControlMaximum (ch) != -1)
2653 SetControlMaximum (ch, top_range + VERTICAL_SCROLL_BAR_MIN_HANDLE
2654 - (end - start));
2655 SetControlValue (ch, start);
2656 #if TARGET_API_MAC_CARBON
2657 SetControlViewSize (ch, end - start);
2658 #endif
2660 UNBLOCK_INPUT;
2663 /* Handle some mouse motion while someone is dragging the scroll bar.
2665 This may be called from a signal handler, so we have to ignore GC
2666 mark bits. */
2668 static void
2669 x_scroll_bar_note_movement (bar, y_pos, t)
2670 struct scroll_bar *bar;
2671 int y_pos;
2672 Time t;
2674 FRAME_PTR f = XFRAME (XWINDOW (bar->window)->frame);
2676 last_mouse_movement_time = t;
2678 f->mouse_moved = 1;
2679 XSETVECTOR (last_mouse_scroll_bar, bar);
2681 /* If we're dragging the bar, display it. */
2682 if (! GC_NILP (bar->dragging))
2684 /* Where should the handle be now? */
2685 int new_start = y_pos - 24;
2687 if (new_start != XINT (bar->start))
2689 int new_end = new_start + (XINT (bar->end) - XINT (bar->start));
2691 x_scroll_bar_set_handle (bar, new_start, new_end, 0);
2695 #endif /* !USE_TOOLKIT_SCROLL_BARS */
2698 /***********************************************************************
2699 Tool-bars
2700 ***********************************************************************/
2702 #if USE_MAC_TOOLBAR
2703 /* In identifiers such as function/variable names, Emacs tool bar is
2704 referred to as `tool_bar', and Carbon HIToolbar as `toolbar'. */
2706 #define TOOLBAR_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar"))
2707 #define TOOLBAR_ICON_ITEM_IDENTIFIER (CFSTR ("org.gnu.Emacs.toolbar.icon"))
2709 #define TOOLBAR_ITEM_COMMAND_ID_OFFSET 'Tb\0\0'
2710 #define TOOLBAR_ITEM_COMMAND_ID_P(id) \
2711 (((id) & ~0xffff) == TOOLBAR_ITEM_COMMAND_ID_OFFSET)
2712 #define TOOLBAR_ITEM_COMMAND_ID_VALUE(id) \
2713 ((id) - TOOLBAR_ITEM_COMMAND_ID_OFFSET)
2714 #define TOOLBAR_ITEM_MAKE_COMMAND_ID(value) \
2715 ((value) + TOOLBAR_ITEM_COMMAND_ID_OFFSET)
2717 static OSStatus mac_handle_toolbar_command_event P_ ((EventHandlerCallRef,
2718 EventRef, void *));
2720 extern Rect last_mouse_glyph;
2722 extern void mac_move_window_with_gravity P_ ((struct frame *, int,
2723 short, short));
2724 extern void mac_get_window_origin_with_gravity P_ ((struct frame *, int,
2725 short *, short *));
2726 extern CGImageRef mac_image_spec_to_cg_image P_ ((struct frame *,
2727 Lisp_Object));
2729 static OSStatus
2730 mac_handle_toolbar_event (next_handler, event, data)
2731 EventHandlerCallRef next_handler;
2732 EventRef event;
2733 void *data;
2735 OSStatus result = eventNotHandledErr;
2737 switch (GetEventKind (event))
2739 case kEventToolbarGetDefaultIdentifiers:
2740 result = noErr;
2741 break;
2743 case kEventToolbarGetAllowedIdentifiers:
2745 CFMutableArrayRef array;
2747 GetEventParameter (event, kEventParamMutableArray,
2748 typeCFMutableArrayRef, NULL,
2749 sizeof (CFMutableArrayRef), NULL, &array);
2750 CFArrayAppendValue (array, TOOLBAR_ICON_ITEM_IDENTIFIER);
2751 result = noErr;
2753 break;
2755 case kEventToolbarCreateItemWithIdentifier:
2757 CFStringRef identifier;
2758 HIToolbarItemRef item = NULL;
2760 GetEventParameter (event, kEventParamToolbarItemIdentifier,
2761 typeCFStringRef, NULL,
2762 sizeof (CFStringRef), NULL, &identifier);
2764 if (CFStringCompare (identifier, TOOLBAR_ICON_ITEM_IDENTIFIER, 0)
2765 == kCFCompareEqualTo)
2766 HIToolbarItemCreate (identifier,
2767 kHIToolbarItemAllowDuplicates
2768 | kHIToolbarItemCantBeRemoved, &item);
2770 if (item)
2772 SetEventParameter (event, kEventParamToolbarItem,
2773 typeHIToolbarItemRef,
2774 sizeof (HIToolbarItemRef), &item);
2775 result = noErr;
2778 break;
2780 default:
2781 abort ();
2784 return result;
2787 /* Create a tool bar for frame F. */
2789 static OSStatus
2790 mac_create_frame_tool_bar (f)
2791 FRAME_PTR f;
2793 OSStatus err;
2794 HIToolbarRef toolbar;
2796 err = HIToolbarCreate (TOOLBAR_IDENTIFIER, kHIToolbarNoAttributes,
2797 &toolbar);
2798 if (err == noErr)
2800 static const EventTypeSpec specs[] =
2801 {{kEventClassToolbar, kEventToolbarGetDefaultIdentifiers},
2802 {kEventClassToolbar, kEventToolbarGetAllowedIdentifiers},
2803 {kEventClassToolbar, kEventToolbarCreateItemWithIdentifier}};
2805 err = InstallEventHandler (HIObjectGetEventTarget (toolbar),
2806 mac_handle_toolbar_event,
2807 GetEventTypeCount (specs), specs,
2808 f, NULL);
2811 if (err == noErr)
2812 err = HIToolbarSetDisplayMode (toolbar, kHIToolbarDisplayModeIconOnly);
2813 if (err == noErr)
2815 static const EventTypeSpec specs[] =
2816 {{kEventClassCommand, kEventCommandProcess}};
2818 err = InstallWindowEventHandler (FRAME_MAC_WINDOW (f),
2819 mac_handle_toolbar_command_event,
2820 GetEventTypeCount (specs),
2821 specs, f, NULL);
2823 if (err == noErr)
2824 err = SetWindowToolbar (FRAME_MAC_WINDOW (f), toolbar);
2826 if (toolbar)
2827 CFRelease (toolbar);
2829 return err;
2832 /* Update the tool bar for frame F. Add new buttons and remove old. */
2834 void
2835 update_frame_tool_bar (f)
2836 FRAME_PTR f;
2838 HIToolbarRef toolbar = NULL;
2839 short left, top;
2840 CFArrayRef old_items = NULL;
2841 CFIndex old_count;
2842 int i, pos, win_gravity = f->output_data.mac->toolbar_win_gravity;
2843 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
2845 BLOCK_INPUT;
2847 GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar);
2848 if (toolbar == NULL)
2850 mac_create_frame_tool_bar (f);
2851 GetWindowToolbar (FRAME_MAC_WINDOW (f), &toolbar);
2852 if (toolbar == NULL)
2853 goto out;
2854 if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity)
2855 mac_get_window_origin_with_gravity (f, win_gravity, &left, &top);
2858 HIToolbarCopyItems (toolbar, &old_items);
2859 if (old_items == NULL)
2860 goto out;
2862 old_count = CFArrayGetCount (old_items);
2863 pos = 0;
2864 for (i = 0; i < f->n_tool_bar_items; ++i)
2866 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
2868 int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
2869 int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
2870 int idx;
2871 Lisp_Object image;
2872 CGImageRef cg_image;
2873 CFStringRef label;
2874 HIToolbarItemRef item;
2876 /* If image is a vector, choose the image according to the
2877 button state. */
2878 image = PROP (TOOL_BAR_ITEM_IMAGES);
2879 if (VECTORP (image))
2881 if (enabled_p)
2882 idx = (selected_p
2883 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
2884 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
2885 else
2886 idx = (selected_p
2887 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
2888 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
2890 xassert (ASIZE (image) >= idx);
2891 image = AREF (image, idx);
2893 else
2894 idx = -1;
2896 cg_image = mac_image_spec_to_cg_image (f, image);
2897 /* Ignore invalid image specifications. */
2898 if (cg_image == NULL)
2899 continue;
2901 label = cfstring_create_with_string (PROP (TOOL_BAR_ITEM_CAPTION));
2902 if (label == NULL)
2903 label = CFSTR ("");
2905 if (pos < old_count)
2907 CGImageRef old_cg_image = NULL;
2908 CFStringRef old_label = NULL;
2909 Boolean old_enabled_p;
2911 item = (HIToolbarItemRef) CFArrayGetValueAtIndex (old_items, pos);
2913 HIToolbarItemCopyImage (item, &old_cg_image);
2914 if (cg_image != old_cg_image)
2915 HIToolbarItemSetImage (item, cg_image);
2916 CGImageRelease (old_cg_image);
2918 HIToolbarItemCopyLabel (item, &old_label);
2919 if (CFStringCompare (label, old_label, 0) != kCFCompareEqualTo)
2920 HIToolbarItemSetLabel (item, label);
2921 CFRelease (old_label);
2923 old_enabled_p = HIToolbarItemIsEnabled (item);
2924 if ((enabled_p || idx >= 0) != old_enabled_p)
2925 HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0));
2927 else
2929 item = NULL;
2930 HIToolbarCreateItemWithIdentifier (toolbar,
2931 TOOLBAR_ICON_ITEM_IDENTIFIER,
2932 NULL, &item);
2933 if (item)
2935 HIToolbarItemSetImage (item, cg_image);
2936 HIToolbarItemSetLabel (item, label);
2937 HIToolbarItemSetEnabled (item, (enabled_p || idx >= 0));
2938 HIToolbarAppendItem (toolbar, item);
2939 CFRelease (item);
2943 CFRelease (label);
2944 if (item)
2946 HIToolbarItemSetCommandID (item, TOOLBAR_ITEM_MAKE_COMMAND_ID (i));
2947 pos++;
2951 CFRelease (old_items);
2953 while (pos < old_count)
2954 HIToolbarRemoveItemAtIndex (toolbar, --old_count);
2956 ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), true,
2957 !win_gravity && f == mac_focus_frame (dpyinfo));
2958 /* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events on
2959 toolbar visibility change. */
2960 mac_handle_origin_change (f);
2961 if (win_gravity >= NorthWestGravity && win_gravity <= SouthEastGravity)
2963 mac_move_window_with_gravity (f, win_gravity, left, top);
2964 /* If the title bar is completely outside the screen, adjust the
2965 position. */
2966 ConstrainWindowToScreen (FRAME_MAC_WINDOW (f), kWindowTitleBarRgn,
2967 kWindowConstrainMoveRegardlessOfFit
2968 | kWindowConstrainAllowPartial, NULL, NULL);
2969 f->output_data.mac->toolbar_win_gravity = 0;
2972 out:
2973 UNBLOCK_INPUT;
2976 /* Hide the tool bar on frame F. Unlike the counterpart on GTK+, it
2977 doesn't deallocate the resources. */
2979 void
2980 free_frame_tool_bar (f)
2981 FRAME_PTR f;
2983 if (IsWindowToolbarVisible (FRAME_MAC_WINDOW (f)))
2985 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
2987 BLOCK_INPUT;
2988 ShowHideWindowToolbar (FRAME_MAC_WINDOW (f), false,
2989 (NILP (find_symbol_value
2990 (intern ("frame-notice-user-settings")))
2991 && f == mac_focus_frame (dpyinfo)));
2992 /* Mac OS X 10.3 does not issue kEventWindowBoundsChanged events
2993 on toolbar visibility change. */
2994 mac_handle_origin_change (f);
2995 UNBLOCK_INPUT;
2999 /* Report a mouse movement over toolbar to the mainstream Emacs
3000 code. */
3002 static void
3003 mac_tool_bar_note_mouse_movement (f, event)
3004 struct frame *f;
3005 EventRef event;
3007 OSStatus err;
3008 struct mac_display_info *dpyinfo = FRAME_MAC_DISPLAY_INFO (f);
3009 int mouse_down_p;
3010 WindowRef window;
3011 WindowPartCode part_code;
3012 HIViewRef item_view;
3013 UInt32 command_id;
3015 mouse_down_p = (dpyinfo->grabbed
3016 && f == last_mouse_frame
3017 && FRAME_LIVE_P (f));
3018 if (mouse_down_p)
3019 return;
3021 err = GetEventParameter (event, kEventParamWindowRef, typeWindowRef, NULL,
3022 sizeof (WindowRef), NULL, &window);
3023 if (err != noErr || window != FRAME_MAC_WINDOW (f))
3024 return;
3026 err = GetEventParameter (event, kEventParamWindowPartCode,
3027 typeWindowPartCode, NULL,
3028 sizeof (WindowPartCode), NULL, &part_code);
3029 if (err != noErr || part_code != inStructure)
3030 return;
3032 err = HIViewGetViewForMouseEvent (HIViewGetRoot (window), event, &item_view);
3033 /* This doesn't work on Mac OS X 10.2. On Mac OS X 10.3 and 10.4, a
3034 toolbar item view seems to have the same command ID with that of
3035 the toolbar item. */
3036 if (err == noErr)
3037 err = GetControlCommandID (item_view, &command_id);
3038 if (err == noErr && TOOLBAR_ITEM_COMMAND_ID_P (command_id))
3040 int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command_id);
3042 if (i < f->n_tool_bar_items)
3044 HIRect bounds;
3045 HIViewRef content_view;
3047 err = HIViewGetBounds (item_view, &bounds);
3048 if (err == noErr)
3049 err = HIViewFindByID (HIViewGetRoot (window),
3050 kHIViewWindowContentID, &content_view);
3051 if (err == noErr)
3052 err = HIViewConvertRect (&bounds, item_view, content_view);
3053 if (err == noErr)
3054 SetRect (&last_mouse_glyph,
3055 CGRectGetMinX (bounds), CGRectGetMinY (bounds),
3056 CGRectGetMaxX (bounds), CGRectGetMaxY (bounds));
3058 help_echo_object = help_echo_window = Qnil;
3059 help_echo_pos = -1;
3060 help_echo_string = PROP (TOOL_BAR_ITEM_HELP);
3061 if (NILP (help_echo_string))
3062 help_echo_string = PROP (TOOL_BAR_ITEM_CAPTION);
3067 static OSStatus
3068 mac_handle_toolbar_command_event (next_handler, event, data)
3069 EventHandlerCallRef next_handler;
3070 EventRef event;
3071 void *data;
3073 OSStatus err, result = eventNotHandledErr;
3074 struct frame *f = (struct frame *) data;
3075 HICommand command;
3077 err = GetEventParameter (event, kEventParamDirectObject,
3078 typeHICommand, NULL,
3079 sizeof (HICommand), NULL, &command);
3080 if (err != noErr)
3081 return result;
3083 switch (GetEventKind (event))
3085 case kEventCommandProcess:
3086 if (!TOOLBAR_ITEM_COMMAND_ID_P (command.commandID))
3087 result = CallNextEventHandler (next_handler, event);
3088 else
3090 int i = TOOLBAR_ITEM_COMMAND_ID_VALUE (command.commandID);
3092 if (i < f->n_tool_bar_items
3093 && !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P)))
3095 Lisp_Object frame;
3096 struct input_event buf;
3098 EVENT_INIT (buf);
3100 XSETFRAME (frame, f);
3101 buf.kind = TOOL_BAR_EVENT;
3102 buf.frame_or_window = frame;
3103 buf.arg = frame;
3104 kbd_buffer_store_event (&buf);
3106 buf.kind = TOOL_BAR_EVENT;
3107 buf.frame_or_window = frame;
3108 buf.arg = PROP (TOOL_BAR_ITEM_KEY);
3109 buf.modifiers = mac_event_to_emacs_modifiers (event);
3110 kbd_buffer_store_event (&buf);
3112 result = noErr;
3115 break;
3117 default:
3118 abort ();
3120 #undef PROP
3122 return result;
3124 #endif /* USE_MAC_TOOLBAR */
3127 /***********************************************************************
3128 Font Panel
3129 ***********************************************************************/
3131 #if USE_MAC_FONT_PANEL
3132 /* Whether Font Panel has been shown before. The first call to font
3133 panel functions (FPIsFontPanelVisible, SetFontInfoForSelection) is
3134 slow. This variable is used for deferring such a call as much as
3135 possible. */
3136 static int font_panel_shown_p = 0;
3138 extern Lisp_Object Qpanel_closed, Qselection;
3139 extern Lisp_Object Qfont;
3141 /* Whether the font panel is currently visible. */
3144 mac_font_panel_visible_p ()
3146 return font_panel_shown_p && FPIsFontPanelVisible ();
3149 static pascal OSStatus
3150 mac_handle_font_event (next_handler, event, data)
3151 EventHandlerCallRef next_handler;
3152 EventRef event;
3153 void *data;
3155 OSStatus result, err;
3156 Lisp_Object id_key;
3157 int num_params;
3158 const EventParamName *names;
3159 const EventParamType *types;
3160 static const EventParamName names_sel[] = {kEventParamATSUFontID,
3161 kEventParamATSUFontSize,
3162 kEventParamFMFontFamily,
3163 kEventParamFMFontStyle,
3164 kEventParamFMFontSize,
3165 kEventParamFontColor};
3166 static const EventParamType types_sel[] = {typeATSUFontID,
3167 typeATSUSize,
3168 typeFMFontFamily,
3169 typeFMFontStyle,
3170 typeFMFontSize,
3171 typeFontColor};
3173 result = CallNextEventHandler (next_handler, event);
3174 if (result != eventNotHandledErr)
3175 return result;
3177 switch (GetEventKind (event))
3179 case kEventFontPanelClosed:
3180 id_key = Qpanel_closed;
3181 num_params = 0;
3182 names = NULL;
3183 types = NULL;
3184 break;
3186 case kEventFontSelection:
3187 id_key = Qselection;
3188 num_params = sizeof (names_sel) / sizeof (names_sel[0]);
3189 names = names_sel;
3190 types = types_sel;
3191 break;
3194 err = mac_store_event_ref_as_apple_event (0, 0, Qfont, id_key,
3195 event, num_params,
3196 names, types);
3197 if (err == noErr)
3198 result = noErr;
3200 return result;
3203 /* Toggle visiblity of the font panel. */
3205 OSStatus
3206 mac_show_hide_font_panel ()
3208 if (!font_panel_shown_p)
3210 OSStatus err;
3212 static const EventTypeSpec specs[] =
3213 {{kEventClassFont, kEventFontPanelClosed},
3214 {kEventClassFont, kEventFontSelection}};
3216 err = InstallApplicationEventHandler (mac_handle_font_event,
3217 GetEventTypeCount (specs),
3218 specs, NULL, NULL);
3219 if (err != noErr)
3220 return err;
3222 font_panel_shown_p = 1;
3225 return FPShowHideFontPanel ();
3228 /* Set the font selected in the font panel to the one corresponding to
3229 the face FACE_ID and the charcacter C in the frame F. */
3231 OSStatus
3232 mac_set_font_info_for_selection (f, face_id, c)
3233 struct frame *f;
3234 int face_id, c;
3236 OSStatus err;
3237 EventTargetRef target = NULL;
3238 XFontStruct *font = NULL;
3240 if (!mac_font_panel_visible_p ())
3241 return noErr;
3243 if (f)
3245 target = GetWindowEventTarget (FRAME_MAC_WINDOW (f));
3247 if (FRAME_FACE_CACHE (f) && CHAR_VALID_P (c, 0))
3249 struct face *face;
3251 face_id = FACE_FOR_CHAR (f, FACE_FROM_ID (f, face_id), c);
3252 face = FACE_FROM_ID (f, face_id);
3253 font = face->font;
3257 if (font == NULL)
3258 err = SetFontInfoForSelection (kFontSelectionATSUIType, 0, NULL, target);
3259 else
3261 if (font->mac_fontnum != -1)
3263 FontSelectionQDStyle qd_style;
3265 qd_style.version = kFontSelectionQDStyleVersionZero;
3266 qd_style.instance.fontFamily = font->mac_fontnum;
3267 qd_style.instance.fontStyle = font->mac_fontface;
3268 qd_style.size = font->mac_fontsize;
3269 qd_style.hasColor = false;
3271 err = SetFontInfoForSelection (kFontSelectionQDType,
3272 1, &qd_style, target);
3274 else
3275 err = SetFontInfoForSelection (kFontSelectionATSUIType,
3276 1, &font->mac_style, target);
3279 return err;
3281 #endif /* USE_MAC_FONT_PANEL */
3284 /************************************************************************
3285 Event Handling
3286 ************************************************************************/
3288 /* Non-zero means that a HELP_EVENT has been generated since Emacs
3289 start. */
3291 static int any_help_event_p;
3293 /* Last window where we saw the mouse. Used by mouse-autoselect-window. */
3294 static Lisp_Object last_window;
3296 static Point saved_menu_event_location;
3298 extern struct frame *pending_autoraise_frame;
3300 extern FRAME_PTR last_mouse_glyph_frame;
3302 #ifdef __STDC__
3303 extern int volatile input_signal_count;
3304 #else
3305 extern int input_signal_count;
3306 #endif
3308 extern int mac_screen_config_changed;
3310 extern Lisp_Object Vmac_emulate_three_button_mouse;
3311 #if TARGET_API_MAC_CARBON
3312 extern int mac_wheel_button_is_mouse_2;
3313 extern int mac_pass_command_to_system;
3314 extern int mac_pass_control_to_system;
3315 #endif /* TARGET_API_MAC_CARBON */
3316 extern int mac_ready_for_apple_events;
3318 extern void mac_focus_changed P_ ((int, struct mac_display_info *,
3319 struct frame *, struct input_event *));
3320 extern int mac_get_emulated_btn P_ ((UInt32));
3321 extern int note_mouse_movement P_ ((FRAME_PTR, Point *));
3322 extern void mac_get_screen_info P_ ((struct mac_display_info *));
3324 /* The focus may have changed. Figure out if it is a real focus change,
3325 by checking both FocusIn/Out and Enter/LeaveNotify events.
3327 Returns FOCUS_IN_EVENT event in *BUFP. */
3329 static void
3330 x_detect_focus_change (dpyinfo, event, bufp)
3331 struct mac_display_info *dpyinfo;
3332 const EventRecord *event;
3333 struct input_event *bufp;
3335 struct frame *frame;
3337 frame = mac_window_to_frame ((WindowRef) event->message);
3338 if (! frame)
3339 return;
3341 /* On Mac, this is only called from focus events, so no switch needed. */
3342 mac_focus_changed ((event->modifiers & activeFlag),
3343 dpyinfo, frame, bufp);
3346 #if TARGET_API_MAC_CARBON
3347 /* Obtains the event modifiers from the event EVENTREF and then calls
3348 mac_to_emacs_modifiers. */
3350 static int
3351 mac_event_to_emacs_modifiers (EventRef eventRef)
3353 UInt32 mods = 0, class;
3355 GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32, NULL,
3356 sizeof (UInt32), NULL, &mods);
3357 class = GetEventClass (eventRef);
3358 if (!NILP (Vmac_emulate_three_button_mouse)
3359 && (class == kEventClassMouse || class == kEventClassCommand))
3361 mods &= ~(optionKey | cmdKey);
3363 return mac_to_emacs_modifiers (mods, 0);
3366 /* Given an event REF, return the code to use for the mouse button
3367 code in the emacs input_event. */
3369 static int
3370 mac_get_mouse_btn (EventRef ref)
3372 EventMouseButton result = kEventMouseButtonPrimary;
3373 GetEventParameter (ref, kEventParamMouseButton, typeMouseButton, NULL,
3374 sizeof (EventMouseButton), NULL, &result);
3375 switch (result)
3377 case kEventMouseButtonPrimary:
3378 if (NILP (Vmac_emulate_three_button_mouse))
3379 return 0;
3380 else {
3381 UInt32 mods = 0;
3382 GetEventParameter (ref, kEventParamKeyModifiers, typeUInt32, NULL,
3383 sizeof (UInt32), NULL, &mods);
3384 return mac_get_emulated_btn(mods);
3386 case kEventMouseButtonSecondary:
3387 return mac_wheel_button_is_mouse_2 ? 2 : 1;
3388 case kEventMouseButtonTertiary:
3389 case 4: /* 4 is the number for the mouse wheel button */
3390 return mac_wheel_button_is_mouse_2 ? 1 : 2;
3391 default:
3392 return 0;
3396 /* Normally, ConvertEventRefToEventRecord will correctly handle all
3397 events. However the click of the mouse wheel is not converted to a
3398 mouseDown or mouseUp event. Likewise for dead key events. This
3399 calls ConvertEventRefToEventRecord, but then checks to see if it is
3400 a mouse up/down, or a dead key Carbon event that has not been
3401 converted, and if so, converts it by hand (to be picked up in the
3402 XTread_socket loop). */
3403 static Boolean mac_convert_event_ref (EventRef eventRef, EventRecord *eventRec)
3405 OSStatus err;
3406 Boolean result = ConvertEventRefToEventRecord (eventRef, eventRec);
3407 EventKind action;
3409 if (result)
3410 return result;
3412 switch (GetEventClass (eventRef))
3414 case kEventClassMouse:
3415 switch (GetEventKind (eventRef))
3417 case kEventMouseDown:
3418 eventRec->what = mouseDown;
3419 result = 1;
3420 break;
3422 case kEventMouseUp:
3423 eventRec->what = mouseUp;
3424 result = 1;
3425 break;
3427 default:
3428 break;
3430 break;
3432 case kEventClassKeyboard:
3433 switch (GetEventKind (eventRef))
3435 case kEventRawKeyDown:
3436 action = keyDown;
3437 goto keystroke_common;
3438 case kEventRawKeyRepeat:
3439 action = autoKey;
3440 goto keystroke_common;
3441 case kEventRawKeyUp:
3442 action = keyUp;
3443 keystroke_common:
3445 unsigned char char_codes;
3446 UInt32 key_code;
3448 err = GetEventParameter (eventRef, kEventParamKeyMacCharCodes,
3449 typeChar, NULL, sizeof (char),
3450 NULL, &char_codes);
3451 if (err == noErr)
3452 err = GetEventParameter (eventRef, kEventParamKeyCode,
3453 typeUInt32, NULL, sizeof (UInt32),
3454 NULL, &key_code);
3455 if (err == noErr)
3457 eventRec->what = action;
3458 eventRec->message = char_codes | ((key_code & 0xff) << 8);
3459 result = 1;
3462 break;
3464 default:
3465 break;
3467 break;
3469 default:
3470 break;
3473 if (result)
3475 /* Need where and when. */
3476 UInt32 mods = 0;
3478 GetEventParameter (eventRef, kEventParamMouseLocation, typeQDPoint,
3479 NULL, sizeof (Point), NULL, &eventRec->where);
3480 /* Use two step process because new event modifiers are 32-bit
3481 and old are 16-bit. Currently, only loss is NumLock & Fn. */
3482 GetEventParameter (eventRef, kEventParamKeyModifiers, typeUInt32,
3483 NULL, sizeof (UInt32), NULL, &mods);
3484 eventRec->modifiers = mods;
3486 eventRec->when = EventTimeToTicks (GetEventTime (eventRef));
3489 return result;
3491 #endif /* TARGET_API_MAC_CARBON */
3493 #if !TARGET_API_MAC_CARBON
3494 static RgnHandle mouse_region = NULL;
3496 Boolean
3497 mac_wait_next_event (er, sleep_time, dequeue)
3498 EventRecord *er;
3499 UInt32 sleep_time;
3500 Boolean dequeue;
3502 static EventRecord er_buf = {nullEvent};
3503 UInt32 target_tick, current_tick;
3504 EventMask event_mask;
3506 if (mouse_region == NULL)
3507 mouse_region = NewRgn ();
3509 event_mask = everyEvent;
3510 if (!mac_ready_for_apple_events)
3511 event_mask -= highLevelEventMask;
3513 current_tick = TickCount ();
3514 target_tick = current_tick + sleep_time;
3516 if (er_buf.what == nullEvent)
3517 while (!WaitNextEvent (event_mask, &er_buf,
3518 target_tick - current_tick, mouse_region))
3520 current_tick = TickCount ();
3521 if (target_tick <= current_tick)
3522 return false;
3525 *er = er_buf;
3526 if (dequeue)
3527 er_buf.what = nullEvent;
3528 return true;
3530 #endif /* not TARGET_API_MAC_CARBON */
3532 #if TARGET_API_MAC_CARBON
3533 OSStatus
3534 mac_post_mouse_moved_event ()
3536 EventRef event = NULL;
3537 OSStatus err;
3539 err = CreateEvent (NULL, kEventClassMouse, kEventMouseMoved, 0,
3540 kEventAttributeNone, &event);
3541 if (err == noErr)
3543 Point mouse_pos;
3545 GetGlobalMouse (&mouse_pos);
3546 err = SetEventParameter (event, kEventParamMouseLocation, typeQDPoint,
3547 sizeof (Point), &mouse_pos);
3549 if (err == noErr)
3551 UInt32 modifiers = GetCurrentKeyModifiers ();
3553 err = SetEventParameter (event, kEventParamKeyModifiers, typeUInt32,
3554 sizeof (UInt32), &modifiers);
3556 if (err == noErr)
3557 err = PostEventToQueue (GetCurrentEventQueue (), event,
3558 kEventPriorityStandard);
3559 if (event)
3560 ReleaseEvent (event);
3562 return err;
3564 #endif
3566 #ifdef MAC_OSX
3567 /* Run the current run loop in the default mode until some input
3568 happens or TIMEOUT seconds passes unless it is negative. Return
3569 true if timeout occurs first. */
3571 Boolean
3572 mac_run_loop_run_once (timeout)
3573 EventTimeout timeout;
3575 #if USE_CG_DRAWING
3576 mac_prepare_for_quickdraw (NULL);
3577 #endif
3578 return (CFRunLoopRunInMode (kCFRunLoopDefaultMode,
3579 timeout >= 0 ? timeout : 100000, true)
3580 == kCFRunLoopRunTimedOut);
3582 #endif
3584 /* Emacs calls this whenever it wants to read an input event from the
3585 user. */
3588 XTread_socket (sd, expected, hold_quit)
3589 int sd, expected;
3590 struct input_event *hold_quit;
3592 struct input_event inev;
3593 int count = 0;
3594 #if TARGET_API_MAC_CARBON
3595 EventRef eventRef;
3596 EventTargetRef toolbox_dispatcher;
3597 #endif
3598 EventRecord er;
3599 struct mac_display_info *dpyinfo = &one_mac_display_info;
3601 if (interrupt_input_blocked)
3603 interrupt_input_pending = 1;
3604 return -1;
3607 interrupt_input_pending = 0;
3608 BLOCK_INPUT;
3610 /* So people can tell when we have read the available input. */
3611 input_signal_count++;
3613 ++handling_signal;
3615 #if TARGET_API_MAC_CARBON
3616 toolbox_dispatcher = GetEventDispatcherTarget ();
3618 while (
3619 #if USE_CG_DRAWING
3620 mac_prepare_for_quickdraw (NULL),
3621 #endif
3622 !ReceiveNextEvent (0, NULL, kEventDurationNoWait,
3623 kEventRemoveFromQueue, &eventRef))
3624 #else /* !TARGET_API_MAC_CARBON */
3625 while (mac_wait_next_event (&er, 0, true))
3626 #endif /* !TARGET_API_MAC_CARBON */
3628 int do_help = 0;
3629 struct frame *f;
3630 unsigned long timestamp;
3632 EVENT_INIT (inev);
3633 inev.kind = NO_EVENT;
3634 inev.arg = Qnil;
3636 #if TARGET_API_MAC_CARBON
3637 timestamp = GetEventTime (eventRef) / kEventDurationMillisecond;
3639 if (!mac_convert_event_ref (eventRef, &er))
3640 goto OTHER;
3641 #else /* !TARGET_API_MAC_CARBON */
3642 timestamp = er.when * (1000 / 60); /* ticks to milliseconds */
3643 #endif /* !TARGET_API_MAC_CARBON */
3645 switch (er.what)
3647 case mouseDown:
3648 case mouseUp:
3650 WindowRef window_ptr;
3651 ControlPartCode part_code;
3652 int tool_bar_p = 0;
3654 #if TARGET_API_MAC_CARBON
3655 OSStatus err;
3657 /* This is needed to send mouse events like aqua window
3658 buttons to the correct handler. */
3659 read_socket_inev = &inev;
3660 err = SendEventToEventTarget (eventRef, toolbox_dispatcher);
3661 read_socket_inev = NULL;
3662 if (err != eventNotHandledErr)
3663 break;
3664 #endif
3665 last_mouse_glyph_frame = 0;
3667 if (dpyinfo->grabbed && last_mouse_frame
3668 && FRAME_LIVE_P (last_mouse_frame))
3670 window_ptr = FRAME_MAC_WINDOW (last_mouse_frame);
3671 part_code = inContent;
3673 else
3675 part_code = FindWindow (er.where, &window_ptr);
3676 if (tip_window && window_ptr == tip_window)
3678 HideWindow (tip_window);
3679 part_code = FindWindow (er.where, &window_ptr);
3683 if (er.what != mouseDown
3684 && (part_code != inContent || dpyinfo->grabbed == 0))
3685 break;
3687 switch (part_code)
3689 case inMenuBar:
3690 f = mac_focus_frame (dpyinfo);
3691 saved_menu_event_location = er.where;
3692 inev.kind = MENU_BAR_ACTIVATE_EVENT;
3693 XSETFRAME (inev.frame_or_window, f);
3694 break;
3696 case inContent:
3697 if (
3698 #if TARGET_API_MAC_CARBON
3699 FrontNonFloatingWindow ()
3700 #else
3701 FrontWindow ()
3702 #endif
3703 != window_ptr
3704 || (mac_window_to_frame (window_ptr)
3705 != dpyinfo->x_focus_frame))
3706 SelectWindow (window_ptr);
3707 else
3709 ControlPartCode control_part_code;
3710 ControlRef ch;
3711 Point mouse_loc;
3712 #ifdef MAC_OSX
3713 ControlKind control_kind;
3714 #endif
3716 f = mac_window_to_frame (window_ptr);
3717 /* convert to local coordinates of new window */
3718 mouse_loc.h = (er.where.h
3719 - (f->left_pos
3720 + FRAME_OUTER_TO_INNER_DIFF_X (f)));
3721 mouse_loc.v = (er.where.v
3722 - (f->top_pos
3723 + FRAME_OUTER_TO_INNER_DIFF_Y (f)));
3724 #if TARGET_API_MAC_CARBON
3725 ch = FindControlUnderMouse (mouse_loc, window_ptr,
3726 &control_part_code);
3727 #ifdef MAC_OSX
3728 if (ch)
3729 GetControlKind (ch, &control_kind);
3730 #endif
3731 #else
3732 control_part_code = FindControl (mouse_loc, window_ptr,
3733 &ch);
3734 #endif
3736 #if TARGET_API_MAC_CARBON
3737 inev.code = mac_get_mouse_btn (eventRef);
3738 inev.modifiers = mac_event_to_emacs_modifiers (eventRef);
3739 #else
3740 inev.code = mac_get_emulated_btn (er.modifiers);
3741 inev.modifiers = mac_to_emacs_modifiers (er.modifiers, 0);
3742 #endif
3743 XSETINT (inev.x, mouse_loc.h);
3744 XSETINT (inev.y, mouse_loc.v);
3746 if ((dpyinfo->grabbed && tracked_scroll_bar)
3747 || (ch != 0
3748 #ifndef USE_TOOLKIT_SCROLL_BARS
3749 /* control_part_code becomes kControlNoPart if
3750 a progress indicator is clicked. */
3751 && control_part_code != kControlNoPart
3752 #else /* USE_TOOLKIT_SCROLL_BARS */
3753 #ifdef MAC_OSX
3754 && control_kind.kind == kControlKindScrollBar
3755 #endif /* MAC_OSX */
3756 #endif /* USE_TOOLKIT_SCROLL_BARS */
3759 struct scroll_bar *bar;
3761 if (dpyinfo->grabbed && tracked_scroll_bar)
3763 bar = tracked_scroll_bar;
3764 #ifndef USE_TOOLKIT_SCROLL_BARS
3765 control_part_code = kControlIndicatorPart;
3766 #endif
3768 else
3769 bar = (struct scroll_bar *) GetControlReference (ch);
3770 #ifdef USE_TOOLKIT_SCROLL_BARS
3771 /* Make the "Ctrl-Mouse-2 splits window" work
3772 for toolkit scroll bars. */
3773 if (inev.modifiers & ctrl_modifier)
3774 x_scroll_bar_handle_click (bar, control_part_code,
3775 &er, &inev);
3776 else if (er.what == mouseDown)
3777 x_scroll_bar_handle_press (bar, control_part_code,
3778 mouse_loc, &inev);
3779 else
3780 x_scroll_bar_handle_release (bar, &inev);
3781 #else /* not USE_TOOLKIT_SCROLL_BARS */
3782 x_scroll_bar_handle_click (bar, control_part_code,
3783 &er, &inev);
3784 if (er.what == mouseDown
3785 && control_part_code == kControlIndicatorPart)
3786 tracked_scroll_bar = bar;
3787 else
3788 tracked_scroll_bar = NULL;
3789 #endif /* not USE_TOOLKIT_SCROLL_BARS */
3791 else
3793 Lisp_Object window;
3794 int x = mouse_loc.h;
3795 int y = mouse_loc.v;
3797 window = window_from_coordinates (f, x, y, 0, 0, 0, 1);
3798 if (EQ (window, f->tool_bar_window))
3800 if (er.what == mouseDown)
3801 handle_tool_bar_click (f, x, y, 1, 0);
3802 else
3803 handle_tool_bar_click (f, x, y, 0,
3804 inev.modifiers);
3805 tool_bar_p = 1;
3807 else
3809 XSETFRAME (inev.frame_or_window, f);
3810 inev.kind = MOUSE_CLICK_EVENT;
3814 if (er.what == mouseDown)
3816 dpyinfo->grabbed |= (1 << inev.code);
3817 last_mouse_frame = f;
3819 if (!tool_bar_p)
3820 last_tool_bar_item = -1;
3822 else
3824 if ((dpyinfo->grabbed & (1 << inev.code)) == 0)
3825 /* If a button is released though it was not
3826 previously pressed, that would be because
3827 of multi-button emulation. */
3828 dpyinfo->grabbed = 0;
3829 else
3830 dpyinfo->grabbed &= ~(1 << inev.code);
3833 /* Ignore any mouse motion that happened before
3834 this event; any subsequent mouse-movement Emacs
3835 events should reflect only motion after the
3836 ButtonPress. */
3837 if (f != 0)
3838 f->mouse_moved = 0;
3840 #ifdef USE_TOOLKIT_SCROLL_BARS
3841 if (inev.kind == MOUSE_CLICK_EVENT
3842 || (inev.kind == SCROLL_BAR_CLICK_EVENT
3843 && (inev.modifiers & ctrl_modifier)))
3844 #endif
3845 switch (er.what)
3847 case mouseDown:
3848 inev.modifiers |= down_modifier;
3849 break;
3850 case mouseUp:
3851 inev.modifiers |= up_modifier;
3852 break;
3855 break;
3857 case inDrag:
3858 #if TARGET_API_MAC_CARBON
3859 case inProxyIcon:
3860 if (IsWindowPathSelectClick (window_ptr, &er))
3862 WindowPathSelect (window_ptr, NULL, NULL);
3863 break;
3865 if (part_code == inProxyIcon
3866 && (TrackWindowProxyDrag (window_ptr, er.where)
3867 != errUserWantsToDragWindow))
3868 break;
3869 DragWindow (window_ptr, er.where, NULL);
3870 #else /* not TARGET_API_MAC_CARBON */
3871 DragWindow (window_ptr, er.where, &qd.screenBits.bounds);
3872 /* Update the frame parameters. */
3874 struct frame *f = mac_window_to_frame (window_ptr);
3876 if (f && !f->async_iconified)
3877 mac_handle_origin_change (f);
3879 #endif /* not TARGET_API_MAC_CARBON */
3880 break;
3882 case inGoAway:
3883 if (TrackGoAway (window_ptr, er.where))
3885 inev.kind = DELETE_WINDOW_EVENT;
3886 XSETFRAME (inev.frame_or_window,
3887 mac_window_to_frame (window_ptr));
3889 break;
3891 /* window resize handling added --ben */
3892 case inGrow:
3893 do_grow_window (window_ptr, &er);
3894 break;
3896 /* window zoom handling added --ben */
3897 case inZoomIn:
3898 case inZoomOut:
3899 if (TrackBox (window_ptr, er.where, part_code))
3900 do_zoom_window (window_ptr, part_code);
3901 break;
3903 #if USE_MAC_TOOLBAR
3904 case inStructure:
3906 OSStatus err;
3907 HIViewRef ch;
3909 if (FrontNonFloatingWindow () != window_ptr)
3910 SelectWindow (window_ptr);
3912 err = HIViewGetViewForMouseEvent (HIViewGetRoot (window_ptr),
3913 eventRef, &ch);
3914 /* This doesn't work on Mac OS X 10.2. */
3915 if (err == noErr)
3916 HIViewClick (ch, eventRef);
3918 break;
3919 #endif /* USE_MAC_TOOLBAR */
3921 default:
3922 break;
3925 break;
3927 #if !TARGET_API_MAC_CARBON
3928 case updateEvt:
3929 do_window_update ((WindowRef) er.message);
3930 break;
3931 #endif
3933 case osEvt:
3934 #if TARGET_API_MAC_CARBON
3935 if (SendEventToEventTarget (eventRef, toolbox_dispatcher)
3936 != eventNotHandledErr)
3937 break;
3938 #endif
3939 switch ((er.message >> 24) & 0x000000FF)
3941 #if USE_MAC_TSM
3942 case suspendResumeMessage:
3943 if (er.message & resumeFlag)
3944 mac_tsm_resume ();
3945 else
3946 mac_tsm_suspend ();
3947 break;
3948 #endif
3950 case mouseMovedMessage:
3951 #if !TARGET_API_MAC_CARBON
3952 SetRectRgn (mouse_region, er.where.h, er.where.v,
3953 er.where.h + 1, er.where.v + 1);
3954 #endif
3955 previous_help_echo_string = help_echo_string;
3956 help_echo_string = Qnil;
3958 if (dpyinfo->grabbed && last_mouse_frame
3959 && FRAME_LIVE_P (last_mouse_frame))
3960 f = last_mouse_frame;
3961 else
3962 f = dpyinfo->x_focus_frame;
3964 if (dpyinfo->mouse_face_hidden)
3966 dpyinfo->mouse_face_hidden = 0;
3967 clear_mouse_face (dpyinfo);
3970 if (f)
3972 WindowRef wp = FRAME_MAC_WINDOW (f);
3973 Point mouse_pos;
3975 mouse_pos.h = (er.where.h
3976 - (f->left_pos
3977 + FRAME_OUTER_TO_INNER_DIFF_X (f)));
3978 mouse_pos.v = (er.where.v
3979 - (f->top_pos
3980 + FRAME_OUTER_TO_INNER_DIFF_Y (f)));
3981 if (dpyinfo->grabbed && tracked_scroll_bar)
3982 #ifdef USE_TOOLKIT_SCROLL_BARS
3983 x_scroll_bar_handle_drag (wp, tracked_scroll_bar,
3984 mouse_pos, &inev);
3985 #else /* not USE_TOOLKIT_SCROLL_BARS */
3986 x_scroll_bar_note_movement (tracked_scroll_bar,
3987 mouse_pos.v
3988 - XINT (tracked_scroll_bar->top),
3989 er.when * (1000 / 60));
3990 #endif /* not USE_TOOLKIT_SCROLL_BARS */
3991 else
3993 /* Generate SELECT_WINDOW_EVENTs when needed. */
3994 if (!NILP (Vmouse_autoselect_window))
3996 Lisp_Object window;
3998 window = window_from_coordinates (f,
3999 mouse_pos.h,
4000 mouse_pos.v,
4001 0, 0, 0, 0);
4003 /* Window will be selected only when it is
4004 not selected now and last mouse movement
4005 event was not in it. Minibuffer window
4006 will be selected only when it is active. */
4007 if (WINDOWP (window)
4008 && !EQ (window, last_window)
4009 && !EQ (window, selected_window)
4010 /* For click-to-focus window managers
4011 create event iff we don't leave the
4012 selected frame. */
4013 && (focus_follows_mouse
4014 || (EQ (XWINDOW (window)->frame,
4015 XWINDOW (selected_window)->frame))))
4017 inev.kind = SELECT_WINDOW_EVENT;
4018 inev.frame_or_window = window;
4021 last_window=window;
4023 if (!note_mouse_movement (f, &mouse_pos))
4024 help_echo_string = previous_help_echo_string;
4025 #if USE_MAC_TOOLBAR
4026 else
4027 mac_tool_bar_note_mouse_movement (f, eventRef);
4028 #endif
4032 /* If the contents of the global variable
4033 help_echo_string has changed, generate a
4034 HELP_EVENT. */
4035 if (!NILP (help_echo_string) || !NILP (previous_help_echo_string))
4036 do_help = 1;
4037 break;
4039 break;
4041 case activateEvt:
4043 WindowRef window_ptr = (WindowRef) er.message;
4044 OSErr err;
4045 ControlRef root_control;
4047 if (window_ptr == tip_window)
4049 HideWindow (tip_window);
4050 break;
4053 if (!is_emacs_window (window_ptr))
4054 goto OTHER;
4056 f = mac_window_to_frame (window_ptr);
4058 if ((er.modifiers & activeFlag) != 0)
4060 /* A window has been activated */
4061 Point mouse_loc;
4063 err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control);
4064 if (err == noErr)
4065 ActivateControl (root_control);
4067 x_detect_focus_change (dpyinfo, &er, &inev);
4069 mouse_loc.h = (er.where.h
4070 - (f->left_pos
4071 + FRAME_OUTER_TO_INNER_DIFF_X (f)));
4072 mouse_loc.v = (er.where.v
4073 - (f->top_pos
4074 + FRAME_OUTER_TO_INNER_DIFF_Y (f)));
4075 /* Window-activated event counts as mouse movement,
4076 so update things that depend on mouse position. */
4077 note_mouse_movement (f, &mouse_loc);
4079 else
4081 /* A window has been deactivated */
4082 err = GetRootControl (FRAME_MAC_WINDOW (f), &root_control);
4083 if (err == noErr)
4084 DeactivateControl (root_control);
4086 #ifdef USE_TOOLKIT_SCROLL_BARS
4087 if (dpyinfo->grabbed && tracked_scroll_bar)
4089 struct input_event event;
4091 EVENT_INIT (event);
4092 event.kind = NO_EVENT;
4093 x_scroll_bar_handle_release (tracked_scroll_bar, &event);
4094 if (event.kind != NO_EVENT)
4096 event.timestamp = timestamp;
4097 kbd_buffer_store_event_hold (&event, hold_quit);
4098 count++;
4101 #endif
4102 dpyinfo->grabbed = 0;
4104 x_detect_focus_change (dpyinfo, &er, &inev);
4106 if (f == dpyinfo->mouse_face_mouse_frame)
4108 /* If we move outside the frame, then we're
4109 certainly no longer on any text in the
4110 frame. */
4111 clear_mouse_face (dpyinfo);
4112 dpyinfo->mouse_face_mouse_frame = 0;
4115 /* Generate a nil HELP_EVENT to cancel a help-echo.
4116 Do it only if there's something to cancel.
4117 Otherwise, the startup message is cleared when the
4118 mouse leaves the frame. */
4119 if (any_help_event_p)
4120 do_help = -1;
4123 break;
4125 case keyDown:
4126 case keyUp:
4127 case autoKey:
4128 ObscureCursor ();
4130 f = mac_focus_frame (dpyinfo);
4131 XSETFRAME (inev.frame_or_window, f);
4133 /* If mouse-highlight is an integer, input clears out mouse
4134 highlighting. */
4135 if (!dpyinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight)
4136 && !EQ (f->tool_bar_window, dpyinfo->mouse_face_window))
4138 clear_mouse_face (dpyinfo);
4139 dpyinfo->mouse_face_hidden = 1;
4143 UInt32 modifiers = er.modifiers, mapped_modifiers;
4144 UInt32 key_code = (er.message & keyCodeMask) >> 8;
4146 #ifdef MAC_OSX
4147 GetEventParameter (eventRef, kEventParamKeyModifiers,
4148 typeUInt32, NULL,
4149 sizeof (UInt32), NULL, &modifiers);
4150 #endif
4151 mapped_modifiers = mac_mapped_modifiers (modifiers, key_code);
4153 #if TARGET_API_MAC_CARBON
4154 if (!(mapped_modifiers
4155 & ~(mac_pass_command_to_system ? cmdKey : 0)
4156 & ~(mac_pass_control_to_system ? controlKey : 0)))
4157 goto OTHER;
4158 else
4159 #endif
4160 if (er.what != keyUp)
4161 do_keystroke (er.what, er.message & charCodeMask,
4162 key_code, modifiers, timestamp, &inev);
4164 break;
4166 case kHighLevelEvent:
4167 AEProcessAppleEvent (&er);
4168 break;
4170 default:
4171 OTHER:
4172 #if TARGET_API_MAC_CARBON
4174 OSStatus err;
4176 read_socket_inev = &inev;
4177 err = SendEventToEventTarget (eventRef, toolbox_dispatcher);
4178 read_socket_inev = NULL;
4180 #endif
4181 break;
4183 #if TARGET_API_MAC_CARBON
4184 ReleaseEvent (eventRef);
4185 #endif
4187 if (inev.kind != NO_EVENT)
4189 inev.timestamp = timestamp;
4190 kbd_buffer_store_event_hold (&inev, hold_quit);
4191 count++;
4194 if (do_help
4195 && !(hold_quit && hold_quit->kind != NO_EVENT))
4197 Lisp_Object frame;
4199 if (f)
4200 XSETFRAME (frame, f);
4201 else
4202 frame = Qnil;
4204 if (do_help > 0)
4206 any_help_event_p = 1;
4207 gen_help_event (help_echo_string, frame, help_echo_window,
4208 help_echo_object, help_echo_pos);
4210 else
4212 help_echo_string = Qnil;
4213 gen_help_event (Qnil, frame, Qnil, Qnil, 0);
4215 count++;
4219 /* If the focus was just given to an autoraising frame,
4220 raise it now. */
4221 /* ??? This ought to be able to handle more than one such frame. */
4222 if (pending_autoraise_frame)
4224 x_raise_frame (pending_autoraise_frame);
4225 pending_autoraise_frame = 0;
4228 if (mac_screen_config_changed)
4230 mac_get_screen_info (dpyinfo);
4231 mac_screen_config_changed = 0;
4234 #if !TARGET_API_MAC_CARBON
4235 /* Check which frames are still visible. We do this here because
4236 there doesn't seem to be any direct notification from the Window
4237 Manager that the visibility of a window has changed (at least,
4238 not in all cases). */
4240 Lisp_Object tail, frame;
4242 FOR_EACH_FRAME (tail, frame)
4244 struct frame *f = XFRAME (frame);
4246 /* The tooltip has been drawn already. Avoid the
4247 SET_FRAME_GARBAGED in mac_handle_visibility_change. */
4248 if (EQ (frame, tip_frame))
4249 continue;
4251 if (FRAME_MAC_P (f))
4252 mac_handle_visibility_change (f);
4255 #endif
4257 --handling_signal;
4258 UNBLOCK_INPUT;
4259 return count;
4263 /***********************************************************************
4264 Busy cursor
4265 ***********************************************************************/
4267 #if TARGET_API_MAC_CARBON
4268 /* Show the spinning progress indicator for the frame F. Create it if
4269 it doesn't exist yet. */
4271 void
4272 mac_show_hourglass (f)
4273 struct frame *f;
4275 #if USE_CG_DRAWING
4276 mac_prepare_for_quickdraw (f);
4277 #endif
4278 if (!f->output_data.mac->hourglass_control)
4280 Window w = FRAME_MAC_WINDOW (f);
4281 Rect r;
4282 ControlRef c;
4284 GetWindowPortBounds (w, &r);
4285 r.left = r.right - HOURGLASS_WIDTH;
4286 r.bottom = r.top + HOURGLASS_HEIGHT;
4287 if (CreateChasingArrowsControl (w, &r, &c) == noErr)
4288 f->output_data.mac->hourglass_control = c;
4291 if (f->output_data.mac->hourglass_control)
4292 ShowControl (f->output_data.mac->hourglass_control);
4295 /* Hide the spinning progress indicator for the frame F. Do nothing
4296 it doesn't exist yet. */
4298 void
4299 mac_hide_hourglass (f)
4300 struct frame *f;
4302 if (f->output_data.mac->hourglass_control)
4304 #if USE_CG_DRAWING
4305 mac_prepare_for_quickdraw (f);
4306 #endif
4307 HideControl (f->output_data.mac->hourglass_control);
4311 /* Reposition the spinning progress indicator for the frame F. Do
4312 nothing it doesn't exist yet. */
4314 void
4315 mac_reposition_hourglass (f)
4316 struct frame *f;
4318 if (f->output_data.mac->hourglass_control)
4320 #if USE_CG_DRAWING
4321 mac_prepare_for_quickdraw (f);
4322 #endif
4323 MoveControl (f->output_data.mac->hourglass_control,
4324 FRAME_PIXEL_WIDTH (f) - HOURGLASS_WIDTH, 0);
4327 #endif /* TARGET_API_MAC_CARBON */
4330 /***********************************************************************
4331 File selection dialog
4332 ***********************************************************************/
4334 #if TARGET_API_MAC_CARBON
4335 extern Lisp_Object Qfile_name_history;
4337 static pascal void mac_nav_event_callback P_ ((NavEventCallbackMessage,
4338 NavCBRecPtr, void *));
4340 /* The actual implementation of Fx_file_dialog. */
4342 Lisp_Object
4343 mac_file_dialog (prompt, dir, default_filename, mustmatch, only_dir_p)
4344 Lisp_Object prompt, dir, default_filename, mustmatch, only_dir_p;
4346 Lisp_Object file = Qnil;
4347 int count = SPECPDL_INDEX ();
4348 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6;
4349 char filename[MAXPATHLEN];
4350 static NavEventUPP mac_nav_event_callbackUPP = NULL;
4352 check_mac ();
4354 GCPRO6 (prompt, dir, default_filename, mustmatch, file, only_dir_p);
4355 CHECK_STRING (prompt);
4356 CHECK_STRING (dir);
4358 /* Create the dialog with PROMPT as title, using DIR as initial
4359 directory and using "*" as pattern. */
4360 dir = Fexpand_file_name (dir, Qnil);
4363 OSStatus status;
4364 NavDialogCreationOptions options;
4365 NavDialogRef dialogRef;
4366 NavTypeListHandle fileTypes = NULL;
4367 NavUserAction userAction;
4368 CFStringRef message=NULL, saveName = NULL;
4370 BLOCK_INPUT;
4371 /* No need for a callback function because we are modal */
4372 NavGetDefaultDialogCreationOptions(&options);
4373 options.modality = kWindowModalityAppModal;
4374 options.location.h = options.location.v = -1;
4375 options.optionFlags = kNavDefaultNavDlogOptions;
4376 options.optionFlags |= kNavAllFilesInPopup; /* All files allowed */
4377 options.optionFlags |= kNavSelectAllReadableItem;
4378 options.optionFlags &= ~kNavAllowMultipleFiles;
4379 if (!NILP(prompt))
4381 message = cfstring_create_with_string (prompt);
4382 options.message = message;
4384 /* Don't set the application, let it use default.
4385 options.clientName = CFSTR ("Emacs");
4388 if (mac_nav_event_callbackUPP == NULL)
4389 mac_nav_event_callbackUPP = NewNavEventUPP (mac_nav_event_callback);
4391 if (!NILP (only_dir_p))
4392 status = NavCreateChooseFolderDialog(&options, mac_nav_event_callbackUPP,
4393 NULL, NULL, &dialogRef);
4394 else if (NILP (mustmatch))
4396 /* This is a save dialog */
4397 options.optionFlags |= kNavDontConfirmReplacement;
4398 options.actionButtonLabel = CFSTR ("Ok");
4399 options.windowTitle = CFSTR ("Enter name");
4401 if (STRINGP (default_filename))
4403 Lisp_Object utf8 = ENCODE_UTF_8 (default_filename);
4404 char *begPtr = SDATA(utf8);
4405 char *filePtr = begPtr + SBYTES(utf8);
4406 while (filePtr != begPtr && !IS_DIRECTORY_SEP(filePtr[-1]))
4407 filePtr--;
4408 saveName = cfstring_create_with_utf8_cstring (filePtr);
4409 options.saveFileName = saveName;
4410 options.optionFlags |= kNavSelectDefaultLocation;
4412 status = NavCreatePutFileDialog(&options,
4413 'TEXT', kNavGenericSignature,
4414 mac_nav_event_callbackUPP, NULL,
4415 &dialogRef);
4417 else
4419 /* This is an open dialog*/
4420 status = NavCreateChooseFileDialog(&options, fileTypes,
4421 mac_nav_event_callbackUPP, NULL,
4422 NULL, NULL, &dialogRef);
4425 /* Set the default location and continue*/
4426 if (status == noErr)
4428 Lisp_Object encoded_dir = ENCODE_FILE (dir);
4429 AEDesc defLocAed;
4431 status = AECreateDesc (TYPE_FILE_NAME, SDATA (encoded_dir),
4432 SBYTES (encoded_dir), &defLocAed);
4433 if (status == noErr)
4435 NavCustomControl(dialogRef, kNavCtlSetLocation, (void*) &defLocAed);
4436 AEDisposeDesc(&defLocAed);
4438 status = NavDialogRun(dialogRef);
4441 if (saveName) CFRelease(saveName);
4442 if (message) CFRelease(message);
4444 if (status == noErr) {
4445 userAction = NavDialogGetUserAction(dialogRef);
4446 switch (userAction)
4448 case kNavUserActionNone:
4449 case kNavUserActionCancel:
4450 break; /* Treat cancel like C-g */
4451 case kNavUserActionOpen:
4452 case kNavUserActionChoose:
4453 case kNavUserActionSaveAs:
4455 NavReplyRecord reply;
4456 Size len;
4458 status = NavDialogGetReply(dialogRef, &reply);
4459 if (status != noErr)
4460 break;
4461 status = AEGetNthPtr (&reply.selection, 1, TYPE_FILE_NAME,
4462 NULL, NULL, filename,
4463 sizeof (filename) - 1, &len);
4464 if (status == noErr)
4466 len = min (len, sizeof (filename) - 1);
4467 filename[len] = '\0';
4468 if (reply.saveFileName)
4470 /* If it was a saved file, we need to add the file name */
4471 if (len && len < sizeof (filename) - 1
4472 && filename[len-1] != '/')
4473 filename[len++] = '/';
4474 CFStringGetCString(reply.saveFileName, filename+len,
4475 sizeof (filename) - len,
4476 #ifdef MAC_OSX
4477 kCFStringEncodingUTF8
4478 #else
4479 CFStringGetSystemEncoding ()
4480 #endif
4483 file = DECODE_FILE (make_unibyte_string (filename,
4484 strlen (filename)));
4486 NavDisposeReply(&reply);
4488 break;
4490 NavDialogDispose(dialogRef);
4491 UNBLOCK_INPUT;
4493 else {
4494 UNBLOCK_INPUT;
4495 /* Fall back on minibuffer if there was a problem */
4496 file = Fcompleting_read (prompt, intern ("read-file-name-internal"),
4497 dir, mustmatch, dir, Qfile_name_history,
4498 default_filename, Qnil);
4502 UNGCPRO;
4504 /* Make "Cancel" equivalent to C-g. */
4505 if (NILP (file))
4506 Fsignal (Qquit, Qnil);
4508 return unbind_to (count, file);
4511 /* Need to register some event callback function for enabling drag and
4512 drop in Navigation Service dialogs. */
4513 static pascal void
4514 mac_nav_event_callback (selector, parms, data)
4515 NavEventCallbackMessage selector;
4516 NavCBRecPtr parms;
4517 void *data;
4520 #endif
4523 /************************************************************************
4524 Menu
4525 ************************************************************************/
4527 #if !TARGET_API_MAC_CARBON
4528 #include <MacTypes.h>
4529 #include <Menus.h>
4530 #include <Quickdraw.h>
4531 #include <ToolUtils.h>
4532 #include <Fonts.h>
4533 #include <Controls.h>
4534 #include <Windows.h>
4535 #include <Events.h>
4536 #if defined (__MRC__) || (__MSL__ >= 0x6000)
4537 #include <ControlDefinitions.h>
4538 #endif
4539 #endif /* not TARGET_API_MAC_CARBON */
4541 extern int menu_item_selection;
4542 extern int popup_activated_flag;
4543 extern int name_is_separator P_ ((const char *));
4544 extern void find_and_call_menu_selection P_ ((FRAME_PTR, int, Lisp_Object,
4545 void *));
4546 extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
4548 enum mac_menu_kind { /* Menu ID range */
4549 MAC_MENU_APPLE, /* 0 (Reserved by Apple) */
4550 MAC_MENU_MENU_BAR, /* 1 .. 233 */
4551 MAC_MENU_M_APPLE, /* 234 (== M_APPLE) */
4552 MAC_MENU_POPUP, /* 235 */
4553 MAC_MENU_DRIVER, /* 236 .. 255 (Reserved) */
4554 MAC_MENU_MENU_BAR_SUB, /* 256 .. 16383 */
4555 MAC_MENU_POPUP_SUB, /* 16384 .. 32767 */
4556 MAC_MENU_END /* 32768 */
4559 static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768};
4561 static int fill_menu P_ ((MenuRef, widget_value *, enum mac_menu_kind, int));
4562 static void dispose_menus P_ ((enum mac_menu_kind, int));
4564 #if !TARGET_API_MAC_CARBON
4565 static void
4566 do_apple_menu (SInt16 menu_item)
4568 Str255 item_name;
4569 SInt16 da_driver_refnum;
4571 if (menu_item == I_ABOUT)
4572 NoteAlert (ABOUT_ALERT_ID, NULL);
4573 else
4575 GetMenuItemText (GetMenuRef (M_APPLE), menu_item, item_name);
4576 da_driver_refnum = OpenDeskAcc (item_name);
4579 #endif /* !TARGET_API_MAC_CARBON */
4581 /* Activate the menu bar of frame F.
4582 This is called from keyboard.c when it gets the
4583 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
4585 To activate the menu bar, we use the button-press event location
4586 that was saved in saved_menu_event_location.
4588 But first we recompute the menu bar contents (the whole tree).
4590 The reason for saving the button event until here, instead of
4591 passing it to the toolkit right away, is that we can safely
4592 execute Lisp code. */
4594 void
4595 x_activate_menubar (f)
4596 FRAME_PTR f;
4598 SInt32 menu_choice;
4599 SInt16 menu_id, menu_item;
4601 set_frame_menubar (f, 0, 1);
4602 BLOCK_INPUT;
4604 popup_activated_flag = 1;
4605 menu_choice = MenuSelect (saved_menu_event_location);
4606 popup_activated_flag = 0;
4607 menu_id = HiWord (menu_choice);
4608 menu_item = LoWord (menu_choice);
4610 #if !TARGET_API_MAC_CARBON
4611 if (menu_id == min_menu_id[MAC_MENU_M_APPLE])
4612 do_apple_menu (menu_item);
4613 else
4614 #endif
4615 if (menu_id)
4617 MenuRef menu = GetMenuRef (menu_id);
4619 if (menu)
4621 UInt32 refcon;
4623 GetMenuItemRefCon (menu, menu_item, &refcon);
4624 find_and_call_menu_selection (f, f->menu_bar_items_used,
4625 f->menu_bar_vector, (void *) refcon);
4629 HiliteMenu (0);
4631 UNBLOCK_INPUT;
4634 #if TARGET_API_MAC_CARBON
4635 extern Lisp_Object Vshow_help_function;
4637 static Lisp_Object
4638 restore_show_help_function (old_show_help_function)
4639 Lisp_Object old_show_help_function;
4641 Vshow_help_function = old_show_help_function;
4643 return Qnil;
4646 static pascal OSStatus
4647 menu_target_item_handler (next_handler, event, data)
4648 EventHandlerCallRef next_handler;
4649 EventRef event;
4650 void *data;
4652 OSStatus err;
4653 MenuRef menu;
4654 MenuItemIndex menu_item;
4655 Lisp_Object help;
4656 GrafPtr port;
4657 int specpdl_count = SPECPDL_INDEX ();
4659 /* Don't be bothered with the overflowed toolbar items menu. */
4660 if (!popup_activated ())
4661 return eventNotHandledErr;
4663 err = GetEventParameter (event, kEventParamDirectObject, typeMenuRef,
4664 NULL, sizeof (MenuRef), NULL, &menu);
4665 if (err == noErr)
4666 err = GetEventParameter (event, kEventParamMenuItemIndex,
4667 typeMenuItemIndex, NULL,
4668 sizeof (MenuItemIndex), NULL, &menu_item);
4669 if (err == noErr)
4670 err = GetMenuItemProperty (menu, menu_item,
4671 MAC_EMACS_CREATOR_CODE, 'help',
4672 sizeof (Lisp_Object), NULL, &help);
4673 if (err != noErr)
4674 help = Qnil;
4676 /* Temporarily bind Vshow_help_function to Qnil because we don't
4677 want tooltips during menu tracking. */
4678 record_unwind_protect (restore_show_help_function, Vshow_help_function);
4679 Vshow_help_function = Qnil;
4680 GetPort (&port);
4681 show_help_echo (help, Qnil, Qnil, Qnil, 1);
4682 SetPort (port);
4683 unbind_to (specpdl_count, Qnil);
4685 return err == noErr ? noErr : eventNotHandledErr;
4688 /* Showing help echo string during menu tracking. */
4690 static OSStatus
4691 install_menu_target_item_handler ()
4693 static const EventTypeSpec specs[] =
4694 {{kEventClassMenu, kEventMenuTargetItem}};
4696 return InstallApplicationEventHandler (NewEventHandlerUPP
4697 (menu_target_item_handler),
4698 GetEventTypeCount (specs),
4699 specs, NULL, NULL);
4701 #endif /* TARGET_API_MAC_CARBON */
4703 /* Event handler function that pops down a menu on C-g. We can only pop
4704 down menus if CancelMenuTracking is present (OSX 10.3 or later). */
4706 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
4707 static pascal OSStatus
4708 menu_quit_handler (nextHandler, theEvent, userData)
4709 EventHandlerCallRef nextHandler;
4710 EventRef theEvent;
4711 void* userData;
4713 OSStatus err;
4714 UInt32 keyCode;
4715 UInt32 keyModifiers;
4717 err = GetEventParameter (theEvent, kEventParamKeyCode,
4718 typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
4720 if (err == noErr)
4721 err = GetEventParameter (theEvent, kEventParamKeyModifiers,
4722 typeUInt32, NULL, sizeof(UInt32),
4723 NULL, &keyModifiers);
4725 if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode))
4727 MenuRef menu = userData != 0
4728 ? (MenuRef)userData : AcquireRootMenu ();
4730 CancelMenuTracking (menu, true, 0);
4731 if (!userData) ReleaseMenu (menu);
4732 return noErr;
4735 return CallNextEventHandler (nextHandler, theEvent);
4737 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
4739 /* Add event handler to all menus that belong to KIND so we can detect
4740 C-g. ROOT_MENU is the root menu of the tracking session to dismiss
4741 when C-g is detected. NULL means the menu bar. If
4742 CancelMenuTracking isn't available, do nothing. */
4744 static void
4745 install_menu_quit_handler (kind, root_menu)
4746 enum mac_menu_kind kind;
4747 MenuRef root_menu;
4749 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
4750 static const EventTypeSpec typesList[] =
4751 {{kEventClassKeyboard, kEventRawKeyDown}};
4752 int id;
4754 #if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
4755 if (CancelMenuTracking == NULL)
4756 return;
4757 #endif
4758 for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++)
4760 MenuRef menu = GetMenuRef (id);
4762 if (menu == NULL)
4763 break;
4764 InstallMenuEventHandler (menu, menu_quit_handler,
4765 GetEventTypeCount (typesList),
4766 typesList, root_menu, NULL);
4768 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
4771 static Lisp_Object
4772 pop_down_menu (arg)
4773 Lisp_Object arg;
4775 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
4776 FRAME_PTR f = p->pointer;
4777 MenuRef menu = GetMenuRef (min_menu_id[MAC_MENU_POPUP]);
4779 BLOCK_INPUT;
4781 /* Must reset this manually because the button release event is not
4782 passed to Emacs event loop. */
4783 FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
4785 /* delete all menus */
4786 dispose_menus (MAC_MENU_POPUP_SUB, 0);
4787 DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
4788 DisposeMenu (menu);
4790 UNBLOCK_INPUT;
4792 return Qnil;
4795 /* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop
4796 until the menu pops down. Return the selection. */
4798 void
4799 create_and_show_popup_menu (f, first_wv, x, y, for_click)
4800 FRAME_PTR f;
4801 widget_value *first_wv;
4802 int x;
4803 int y;
4804 int for_click;
4806 int result = 0;
4807 MenuRef menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p");
4808 int menu_item_choice;
4809 int specpdl_count = SPECPDL_INDEX ();
4811 InsertMenu (menu, -1);
4812 fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB,
4813 min_menu_id[MAC_MENU_POPUP_SUB]);
4815 /* Add event handler so we can detect C-g. */
4816 install_menu_quit_handler (MAC_MENU_POPUP, menu);
4817 install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu);
4819 record_unwind_protect (pop_down_menu, make_save_value (f, 0));
4821 /* Adjust coordinates to be root-window-relative. */
4822 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
4823 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
4825 /* Display the menu. */
4826 popup_activated_flag = 1;
4827 menu_item_choice = PopUpMenuSelect (menu, y, x, 0);
4828 popup_activated_flag = 0;
4830 /* Get the refcon to find the correct item */
4831 if (menu_item_choice)
4833 MenuRef sel_menu = GetMenuRef (HiWord (menu_item_choice));
4835 if (sel_menu)
4836 GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice),
4837 (UInt32 *) &result);
4840 unbind_to (specpdl_count, Qnil);
4842 menu_item_selection = result;
4845 static void
4846 add_menu_item (menu, pos, wv)
4847 MenuRef menu;
4848 int pos;
4849 widget_value *wv;
4851 #if TARGET_API_MAC_CARBON
4852 CFStringRef item_name;
4853 #else
4854 Str255 item_name;
4855 #endif
4857 if (name_is_separator (wv->name))
4858 AppendMenu (menu, "\p-");
4859 else
4861 AppendMenu (menu, "\pX");
4863 #if TARGET_API_MAC_CARBON
4864 item_name = cfstring_create_with_utf8_cstring (wv->name);
4866 if (wv->key != NULL)
4868 CFStringRef name, key;
4870 name = item_name;
4871 key = cfstring_create_with_utf8_cstring (wv->key);
4872 item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"),
4873 name, key);
4874 CFRelease (name);
4875 CFRelease (key);
4878 SetMenuItemTextWithCFString (menu, pos, item_name);
4879 CFRelease (item_name);
4881 if (wv->enabled)
4882 EnableMenuItem (menu, pos);
4883 else
4884 DisableMenuItem (menu, pos);
4886 if (STRINGP (wv->help))
4887 SetMenuItemProperty (menu, pos, MAC_EMACS_CREATOR_CODE, 'help',
4888 sizeof (Lisp_Object), &wv->help);
4889 #else /* ! TARGET_API_MAC_CARBON */
4890 item_name[sizeof (item_name) - 1] = '\0';
4891 strncpy (item_name, wv->name, sizeof (item_name) - 1);
4892 if (wv->key != NULL)
4894 int len = strlen (item_name);
4896 strncpy (item_name + len, " ", sizeof (item_name) - 1 - len);
4897 len = strlen (item_name);
4898 strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len);
4900 c2pstr (item_name);
4901 SetMenuItemText (menu, pos, item_name);
4903 if (wv->enabled)
4904 EnableItem (menu, pos);
4905 else
4906 DisableItem (menu, pos);
4907 #endif /* ! TARGET_API_MAC_CARBON */
4909 /* Draw radio buttons and tickboxes. */
4910 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE
4911 || wv->button_type == BUTTON_TYPE_RADIO))
4912 SetItemMark (menu, pos, checkMark);
4913 else
4914 SetItemMark (menu, pos, noMark);
4916 SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
4920 /* Construct native Mac OS menu based on widget_value tree. */
4922 static int
4923 fill_menu (menu, wv, kind, submenu_id)
4924 MenuRef menu;
4925 widget_value *wv;
4926 enum mac_menu_kind kind;
4927 int submenu_id;
4929 int pos;
4931 for (pos = 1; wv != NULL; wv = wv->next, pos++)
4933 add_menu_item (menu, pos, wv);
4934 if (wv->contents && submenu_id < min_menu_id[kind + 1])
4936 MenuRef submenu = NewMenu (submenu_id, "\pX");
4938 InsertMenu (submenu, -1);
4939 #if TARGET_API_MAC_CARBON
4940 SetMenuItemHierarchicalMenu (menu, pos, submenu);
4941 #else
4942 SetMenuItemHierarchicalID (menu, pos, submenu_id);
4943 #endif
4944 submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
4948 return submenu_id;
4951 /* Fill menu bar with the items defined by WV. If DEEP_P, consider
4952 the entire menu trees we supply, rather than just the menu bar item
4953 names. */
4955 void
4956 mac_fill_menubar (wv, deep_p)
4957 widget_value *wv;
4958 int deep_p;
4960 int id, submenu_id;
4961 #if !TARGET_API_MAC_CARBON
4962 int title_changed_p = 0;
4963 #endif
4965 /* Clean up the menu bar when filled by the entire menu trees. */
4966 if (deep_p)
4968 dispose_menus (MAC_MENU_MENU_BAR, 0);
4969 dispose_menus (MAC_MENU_MENU_BAR_SUB, 0);
4970 #if !TARGET_API_MAC_CARBON
4971 title_changed_p = 1;
4972 #endif
4975 /* Fill menu bar titles and submenus. Reuse the existing menu bar
4976 titles as much as possible to minimize redraw (if !deep_p). */
4977 submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB];
4978 for (id = min_menu_id[MAC_MENU_MENU_BAR];
4979 wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1];
4980 wv = wv->next, id++)
4982 OSStatus err = noErr;
4983 MenuRef menu;
4984 #if TARGET_API_MAC_CARBON
4985 CFStringRef title;
4987 title = CFStringCreateWithCString (NULL, wv->name,
4988 kCFStringEncodingMacRoman);
4989 #else
4990 Str255 title;
4992 strncpy (title, wv->name, 255);
4993 title[255] = '\0';
4994 c2pstr (title);
4995 #endif
4997 menu = GetMenuRef (id);
4998 if (menu)
5000 #if TARGET_API_MAC_CARBON
5001 CFStringRef old_title;
5003 err = CopyMenuTitleAsCFString (menu, &old_title);
5004 if (err == noErr)
5006 if (CFStringCompare (title, old_title, 0) != kCFCompareEqualTo)
5008 #ifdef MAC_OSX
5009 if (id + 1 == min_menu_id[MAC_MENU_MENU_BAR + 1]
5010 || GetMenuRef (id + 1) == NULL)
5012 /* This is a workaround for Mac OS X 10.5 where
5013 just calling SetMenuTitleWithCFString fails
5014 to change the title of the last (Help) menu
5015 in the menu bar. */
5016 DeleteMenu (id);
5017 DisposeMenu (menu);
5018 menu = NULL;
5020 else
5021 #endif /* MAC_OSX */
5022 err = SetMenuTitleWithCFString (menu, title);
5024 CFRelease (old_title);
5026 else
5027 err = SetMenuTitleWithCFString (menu, title);
5028 #else /* !TARGET_API_MAC_CARBON */
5029 if (!EqualString (title, (*menu)->menuData, false, false))
5031 DeleteMenu (id);
5032 DisposeMenu (menu);
5033 menu = NewMenu (id, title);
5034 InsertMenu (menu, GetMenuRef (id + 1) ? id + 1 : 0);
5035 title_changed_p = 1;
5037 #endif /* !TARGET_API_MAC_CARBON */
5040 if (!menu)
5042 #if TARGET_API_MAC_CARBON
5043 err = CreateNewMenu (id, 0, &menu);
5044 if (err == noErr)
5045 err = SetMenuTitleWithCFString (menu, title);
5046 #else
5047 menu = NewMenu (id, title);
5048 #endif
5049 if (err == noErr)
5051 InsertMenu (menu, 0);
5052 #if !TARGET_API_MAC_CARBON
5053 title_changed_p = 1;
5054 #endif
5057 #if TARGET_API_MAC_CARBON
5058 CFRelease (title);
5059 #endif
5061 if (err == noErr)
5062 if (wv->contents)
5063 submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
5064 submenu_id);
5067 if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuRef (id))
5069 dispose_menus (MAC_MENU_MENU_BAR, id);
5070 #if !TARGET_API_MAC_CARBON
5071 title_changed_p = 1;
5072 #endif
5075 #if !TARGET_API_MAC_CARBON
5076 if (title_changed_p)
5077 InvalMenuBar ();
5078 #endif
5080 /* Add event handler so we can detect C-g. */
5081 install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL);
5082 install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL);
5085 /* Dispose of menus that belong to KIND, and remove them from the menu
5086 list. ID is the lower bound of menu IDs that will be processed. */
5088 static void
5089 dispose_menus (kind, id)
5090 enum mac_menu_kind kind;
5091 int id;
5093 for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++)
5095 MenuRef menu = GetMenuRef (id);
5097 if (menu == NULL)
5098 break;
5099 DeleteMenu (id);
5100 DisposeMenu (menu);
5104 static void
5105 init_menu_bar ()
5107 #ifdef MAC_OSX
5108 OSStatus err;
5109 MenuRef menu;
5110 MenuItemIndex menu_index;
5112 err = GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1,
5113 &menu, &menu_index);
5114 if (err == noErr)
5115 SetMenuItemCommandKey (menu, menu_index, false, 0);
5116 EnableMenuCommand (NULL, kHICommandPreferences);
5117 err = GetIndMenuItemWithCommandID (NULL, kHICommandPreferences, 1,
5118 &menu, &menu_index);
5119 if (err == noErr)
5121 SetMenuItemCommandKey (menu, menu_index, false, 0);
5122 InsertMenuItemTextWithCFString (menu, NULL,
5123 0, kMenuItemAttrSeparator, 0);
5124 InsertMenuItemTextWithCFString (menu, CFSTR ("About Emacs"),
5125 0, 0, kHICommandAbout);
5127 #else /* !MAC_OSX */
5128 #if TARGET_API_MAC_CARBON
5129 SetMenuItemCommandID (GetMenuRef (M_APPLE), I_ABOUT, kHICommandAbout);
5130 #endif
5131 #endif
5135 /***********************************************************************
5136 Popup Dialog
5137 ***********************************************************************/
5139 #if TARGET_API_MAC_CARBON
5140 #define DIALOG_BUTTON_COMMAND_ID_OFFSET 'Bt\0\0'
5141 #define DIALOG_BUTTON_COMMAND_ID_P(id) \
5142 (((id) & ~0xffff) == DIALOG_BUTTON_COMMAND_ID_OFFSET)
5143 #define DIALOG_BUTTON_COMMAND_ID_VALUE(id) \
5144 ((id) - DIALOG_BUTTON_COMMAND_ID_OFFSET)
5145 #define DIALOG_BUTTON_MAKE_COMMAND_ID(value) \
5146 ((value) + DIALOG_BUTTON_COMMAND_ID_OFFSET)
5148 extern EMACS_TIME timer_check P_ ((int));
5149 static int quit_dialog_event_loop;
5151 static pascal OSStatus
5152 mac_handle_dialog_event (next_handler, event, data)
5153 EventHandlerCallRef next_handler;
5154 EventRef event;
5155 void *data;
5157 OSStatus err, result = eventNotHandledErr;
5158 WindowRef window = (WindowRef) data;
5160 switch (GetEventClass (event))
5162 case kEventClassCommand:
5164 HICommand command;
5166 err = GetEventParameter (event, kEventParamDirectObject,
5167 typeHICommand, NULL, sizeof (HICommand),
5168 NULL, &command);
5169 if (err == noErr)
5170 if (DIALOG_BUTTON_COMMAND_ID_P (command.commandID))
5172 SetWRefCon (window, command.commandID);
5173 quit_dialog_event_loop = 1;
5174 break;
5177 result = CallNextEventHandler (next_handler, event);
5179 break;
5181 case kEventClassKeyboard:
5183 OSStatus result;
5184 char char_code;
5186 result = CallNextEventHandler (next_handler, event);
5187 if (result != eventNotHandledErr)
5188 break;
5190 err = GetEventParameter (event, kEventParamKeyMacCharCodes,
5191 typeChar, NULL, sizeof (char),
5192 NULL, &char_code);
5193 if (err == noErr)
5194 switch (char_code)
5196 case kEscapeCharCode:
5197 quit_dialog_event_loop = 1;
5198 break;
5200 default:
5202 UInt32 modifiers, key_code;
5204 err = GetEventParameter (event, kEventParamKeyModifiers,
5205 typeUInt32, NULL, sizeof (UInt32),
5206 NULL, &modifiers);
5207 if (err == noErr)
5208 err = GetEventParameter (event, kEventParamKeyCode,
5209 typeUInt32, NULL, sizeof (UInt32),
5210 NULL, &key_code);
5211 if (err == noErr)
5212 if (mac_quit_char_key_p (modifiers, key_code))
5213 quit_dialog_event_loop = 1;
5215 break;
5218 break;
5220 default:
5221 abort ();
5224 if (quit_dialog_event_loop)
5226 err = QuitEventLoop (GetCurrentEventLoop ());
5227 if (err == noErr)
5228 result = noErr;
5231 return result;
5234 static OSStatus
5235 install_dialog_event_handler (window)
5236 WindowRef window;
5238 static const EventTypeSpec specs[] =
5239 {{kEventClassCommand, kEventCommandProcess},
5240 {kEventClassKeyboard, kEventRawKeyDown}};
5241 static EventHandlerUPP handle_dialog_eventUPP = NULL;
5243 if (handle_dialog_eventUPP == NULL)
5244 handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event);
5245 return InstallWindowEventHandler (window, handle_dialog_eventUPP,
5246 GetEventTypeCount (specs), specs,
5247 window, NULL);
5250 static Lisp_Object
5251 pop_down_dialog (arg)
5252 Lisp_Object arg;
5254 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
5255 WindowRef window = p->pointer;
5257 BLOCK_INPUT;
5259 if (popup_activated_flag)
5260 EndAppModalStateForWindow (window);
5261 DisposeWindow (window);
5262 popup_activated_flag = 0;
5264 UNBLOCK_INPUT;
5266 return Qnil;
5269 /* Pop up the dialog for frame F defined by FIRST_WV and loop until the
5270 dialog pops down.
5271 menu_item_selection will be set to the selection. */
5273 void
5274 create_and_show_dialog (f, first_wv)
5275 FRAME_PTR f;
5276 widget_value *first_wv;
5278 OSStatus err;
5279 char *dialog_name, *message;
5280 int nb_buttons, first_group_count, i, result = 0;
5281 widget_value *wv;
5282 short buttons_height, text_height, inner_width, inner_height;
5283 Rect empty_rect, *rects;
5284 WindowRef window = NULL;
5285 ControlRef *buttons, default_button = NULL, text;
5286 int specpdl_count = SPECPDL_INDEX ();
5288 dialog_name = first_wv->name;
5289 nb_buttons = dialog_name[1] - '0';
5290 first_group_count = nb_buttons - (dialog_name[4] - '0');
5292 wv = first_wv->contents;
5293 message = wv->value;
5295 wv = wv->next;
5296 SetRect (&empty_rect, 0, 0, 0, 0);
5298 /* Create dialog window. */
5299 err = CreateNewWindow (kMovableModalWindowClass,
5300 kWindowStandardHandlerAttribute,
5301 &empty_rect, &window);
5302 if (err == noErr)
5304 record_unwind_protect (pop_down_dialog, make_save_value (window, 0));
5305 err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground,
5306 true);
5308 if (err == noErr)
5309 err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q'
5310 ? CFSTR ("Question")
5311 : CFSTR ("Information")));
5313 /* Create button controls and measure their optimal bounds. */
5314 if (err == noErr)
5316 buttons = alloca (sizeof (ControlRef) * nb_buttons);
5317 rects = alloca (sizeof (Rect) * nb_buttons);
5318 for (i = 0; i < nb_buttons; i++)
5320 CFStringRef label = cfstring_create_with_utf8_cstring (wv->value);
5322 if (label == NULL)
5323 err = memFullErr;
5324 else
5326 err = CreatePushButtonControl (window, &empty_rect,
5327 label, &buttons[i]);
5328 CFRelease (label);
5330 if (err == noErr)
5332 if (!wv->enabled)
5334 #ifdef MAC_OSX
5335 err = DisableControl (buttons[i]);
5336 #else
5337 err = DeactivateControl (buttons[i]);
5338 #endif
5340 else if (default_button == NULL)
5341 default_button = buttons[i];
5343 if (err == noErr)
5345 SInt16 unused;
5347 rects[i] = empty_rect;
5348 err = GetBestControlRect (buttons[i], &rects[i], &unused);
5350 if (err == noErr)
5352 UInt32 command_id;
5354 OffsetRect (&rects[i], -rects[i].left, -rects[i].top);
5355 if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH)
5356 rects[i].right = DIALOG_BUTTON_MIN_WIDTH;
5357 else if (rects[i].right > DIALOG_MAX_INNER_WIDTH)
5358 rects[i].right = DIALOG_MAX_INNER_WIDTH;
5360 command_id = DIALOG_BUTTON_MAKE_COMMAND_ID ((int) wv->call_data);
5361 err = SetControlCommandID (buttons[i], command_id);
5363 if (err != noErr)
5364 break;
5365 wv = wv->next;
5369 /* Layout buttons. rects[i] is set relative to the bottom-right
5370 corner of the inner box. */
5371 if (err == noErr)
5373 short bottom, right, max_height, left_align_shift;
5375 inner_width = DIALOG_MIN_INNER_WIDTH;
5376 bottom = right = max_height = 0;
5377 for (i = 0; i < nb_buttons; i++)
5379 if (right - rects[i].right < - inner_width)
5381 if (i != first_group_count
5382 && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH)
5383 inner_width = - (right - rects[i].right);
5384 else
5386 bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE;
5387 right = max_height = 0;
5390 if (max_height < rects[i].bottom)
5391 max_height = rects[i].bottom;
5392 OffsetRect (&rects[i], right - rects[i].right,
5393 bottom - rects[i].bottom);
5394 right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
5395 if (i == first_group_count - 1)
5396 right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
5398 buttons_height = - (bottom - max_height);
5400 left_align_shift = - (inner_width + rects[nb_buttons - 1].left);
5401 for (i = nb_buttons - 1; i >= first_group_count; i--)
5403 if (bottom != rects[i].bottom)
5405 left_align_shift = - (inner_width + rects[i].left);
5406 bottom = rects[i].bottom;
5408 OffsetRect (&rects[i], left_align_shift, 0);
5412 /* Create a static text control and measure its bounds. */
5413 if (err == noErr)
5415 CFStringRef message_string;
5416 Rect bounds;
5418 message_string = cfstring_create_with_utf8_cstring (message);
5419 if (message_string == NULL)
5420 err = memFullErr;
5421 else
5423 ControlFontStyleRec text_style;
5425 text_style.flags = 0;
5426 SetRect (&bounds, 0, 0, inner_width, 0);
5427 err = CreateStaticTextControl (window, &bounds, message_string,
5428 &text_style, &text);
5429 CFRelease (message_string);
5431 if (err == noErr)
5433 SInt16 unused;
5435 bounds = empty_rect;
5436 err = GetBestControlRect (text, &bounds, &unused);
5438 if (err == noErr)
5440 text_height = bounds.bottom - bounds.top;
5441 if (text_height < DIALOG_TEXT_MIN_HEIGHT)
5442 text_height = DIALOG_TEXT_MIN_HEIGHT;
5446 /* Place buttons. */
5447 if (err == noErr)
5449 inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE
5450 + buttons_height);
5452 for (i = 0; i < nb_buttons; i++)
5454 OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width,
5455 DIALOG_TOP_MARGIN + inner_height);
5456 SetControlBounds (buttons[i], &rects[i]);
5460 /* Place text. */
5461 if (err == noErr)
5463 Rect bounds;
5465 SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN,
5466 DIALOG_LEFT_MARGIN + inner_width,
5467 DIALOG_TOP_MARGIN + text_height);
5468 SetControlBounds (text, &bounds);
5471 /* Create the application icon at the upper-left corner. */
5472 if (err == noErr)
5474 ControlButtonContentInfo content;
5475 ControlRef icon;
5476 static const ProcessSerialNumber psn = {0, kCurrentProcess};
5477 #ifdef MAC_OSX
5478 FSRef app_location;
5479 #else
5480 ProcessInfoRec pinfo;
5481 FSSpec app_spec;
5482 #endif
5483 SInt16 unused;
5485 content.contentType = kControlContentIconRef;
5486 #ifdef MAC_OSX
5487 err = GetProcessBundleLocation (&psn, &app_location);
5488 if (err == noErr)
5489 err = GetIconRefFromFileInfo (&app_location, 0, NULL, 0, NULL,
5490 kIconServicesNormalUsageFlag,
5491 &content.u.iconRef, &unused);
5492 #else
5493 bzero (&pinfo, sizeof (ProcessInfoRec));
5494 pinfo.processInfoLength = sizeof (ProcessInfoRec);
5495 pinfo.processAppSpec = &app_spec;
5496 err = GetProcessInformation (&psn, &pinfo);
5497 if (err == noErr)
5498 err = GetIconRefFromFile (&app_spec, &content.u.iconRef, &unused);
5499 #endif
5500 if (err == noErr)
5502 Rect bounds;
5504 SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN,
5505 DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH,
5506 DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT);
5507 err = CreateIconControl (window, &bounds, &content, true, &icon);
5508 ReleaseIconRef (content.u.iconRef);
5512 /* Show the dialog window and run event loop. */
5513 if (err == noErr)
5514 if (default_button)
5515 err = SetWindowDefaultButton (window, default_button);
5516 if (err == noErr)
5517 err = install_dialog_event_handler (window);
5518 if (err == noErr)
5520 SizeWindow (window,
5521 DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN,
5522 DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN,
5523 true);
5524 err = RepositionWindow (window, FRAME_MAC_WINDOW (f),
5525 kWindowAlertPositionOnParentWindow);
5527 if (err == noErr)
5529 SetWRefCon (window, 0);
5530 ShowWindow (window);
5531 BringToFront (window);
5532 popup_activated_flag = 1;
5533 err = BeginAppModalStateForWindow (window);
5535 if (err == noErr)
5537 EventTargetRef toolbox_dispatcher = GetEventDispatcherTarget ();
5539 quit_dialog_event_loop = 0;
5540 while (1)
5542 EMACS_TIME next_time = timer_check (1);
5543 long secs = EMACS_SECS (next_time);
5544 long usecs = EMACS_USECS (next_time);
5545 EventTimeout timeout;
5546 EventRef event;
5548 if (secs < 0 || (secs == 0 && usecs == 0))
5550 /* Sometimes timer_check returns -1 (no timers) even if
5551 there are timers. So do a timeout anyway. */
5552 secs = 1;
5553 usecs = 0;
5556 timeout = (secs * kEventDurationSecond
5557 + usecs * kEventDurationMicrosecond);
5558 err = ReceiveNextEvent (0, NULL, timeout, kEventRemoveFromQueue,
5559 &event);
5560 if (err == noErr)
5562 SendEventToEventTarget (event, toolbox_dispatcher);
5563 ReleaseEvent (event);
5565 #if 0 /* defined (MAC_OSX) */
5566 else if (err != eventLoopTimedOutErr)
5568 if (err == eventLoopQuitErr)
5569 err = noErr;
5570 break;
5572 #else
5573 /* The return value of ReceiveNextEvent seems to be
5574 unreliable. Use our own global variable instead. */
5575 if (quit_dialog_event_loop)
5577 err = noErr;
5578 break;
5580 #endif
5583 if (err == noErr)
5585 UInt32 command_id = GetWRefCon (window);
5587 if (DIALOG_BUTTON_COMMAND_ID_P (command_id))
5588 result = DIALOG_BUTTON_COMMAND_ID_VALUE (command_id);
5591 unbind_to (specpdl_count, Qnil);
5593 menu_item_selection = result;
5595 #else /* not TARGET_API_MAC_CARBON */
5596 #define DIALOG_WINDOW_RESOURCE 130
5599 mac_dialog (widget_value *wv)
5601 char *dialog_name;
5602 char *prompt;
5603 char **button_labels;
5604 UInt32 *ref_cons;
5605 int nb_buttons;
5606 int left_count;
5607 int i;
5608 int dialog_width;
5609 Rect rect;
5610 WindowRef window_ptr;
5611 ControlRef ch;
5612 int left;
5613 EventRecord event_record;
5614 SInt16 part_code;
5615 int control_part_code;
5616 Point mouse;
5618 dialog_name = wv->name;
5619 nb_buttons = dialog_name[1] - '0';
5620 left_count = nb_buttons - (dialog_name[4] - '0');
5621 button_labels = (char **) alloca (sizeof (char *) * nb_buttons);
5622 ref_cons = (UInt32 *) alloca (sizeof (UInt32) * nb_buttons);
5624 wv = wv->contents;
5625 prompt = (char *) alloca (strlen (wv->value) + 1);
5626 strcpy (prompt, wv->value);
5627 c2pstr (prompt);
5629 wv = wv->next;
5630 for (i = 0; i < nb_buttons; i++)
5632 button_labels[i] = wv->value;
5633 button_labels[i] = (char *) alloca (strlen (wv->value) + 1);
5634 strcpy (button_labels[i], wv->value);
5635 c2pstr (button_labels[i]);
5636 ref_cons[i] = (UInt32) wv->call_data;
5637 wv = wv->next;
5640 window_ptr = GetNewCWindow (DIALOG_WINDOW_RESOURCE, NULL, (WindowRef) -1);
5642 SetPortWindowPort (window_ptr);
5644 TextFont (0);
5645 /* Left and right margins in the dialog are 13 pixels each.*/
5646 dialog_width = 14;
5647 /* Calculate width of dialog box: 8 pixels on each side of the text
5648 label in each button, 12 pixels between buttons. */
5649 for (i = 0; i < nb_buttons; i++)
5650 dialog_width += StringWidth (button_labels[i]) + 16 + 12;
5652 if (left_count != 0 && nb_buttons - left_count != 0)
5653 dialog_width += 12;
5655 dialog_width = max (dialog_width, StringWidth (prompt) + 26);
5657 SizeWindow (window_ptr, dialog_width, 78, 0);
5658 ShowWindow (window_ptr);
5660 SetPortWindowPort (window_ptr);
5662 TextFont (0);
5664 MoveTo (13, 29);
5665 DrawString (prompt);
5667 left = 13;
5668 for (i = 0; i < nb_buttons; i++)
5670 int button_width = StringWidth (button_labels[i]) + 16;
5671 SetRect (&rect, left, 45, left + button_width, 65);
5672 ch = NewControl (window_ptr, &rect, button_labels[i], 1, 0, 0, 0,
5673 kControlPushButtonProc, ref_cons[i]);
5674 left += button_width + 12;
5675 if (i == left_count - 1)
5676 left += 12;
5679 i = 0;
5680 while (!i)
5682 if (WaitNextEvent (mDownMask, &event_record, 10, NULL))
5683 if (event_record.what == mouseDown)
5685 part_code = FindWindow (event_record.where, &window_ptr);
5686 if (part_code == inContent)
5688 mouse = event_record.where;
5689 GlobalToLocal (&mouse);
5690 control_part_code = FindControl (mouse, window_ptr, &ch);
5691 if (control_part_code == kControlButtonPart)
5692 if (TrackControl (ch, mouse, NULL))
5693 i = GetControlReference (ch);
5698 DisposeWindow (window_ptr);
5700 return i;
5702 #endif /* not TARGET_API_MAC_CARBON */
5705 /***********************************************************************
5706 Selection support
5707 ***********************************************************************/
5709 #if !TARGET_API_MAC_CARBON
5710 #include <Scrap.h>
5711 #include <Endian.h>
5712 #endif
5714 extern Lisp_Object Vselection_converter_alist;
5715 extern Lisp_Object Qmac_scrap_name, Qmac_ostype;
5717 static ScrapFlavorType get_flavor_type_from_symbol P_ ((Lisp_Object,
5718 Selection));
5720 /* Get a reference to the selection corresponding to the symbol SYM.
5721 The reference is set to *SEL, and it becomes NULL if there's no
5722 corresponding selection. Clear the selection if CLEAR_P is
5723 non-zero. */
5725 OSStatus
5726 mac_get_selection_from_symbol (sym, clear_p, sel)
5727 Lisp_Object sym;
5728 int clear_p;
5729 Selection *sel;
5731 OSStatus err = noErr;
5732 Lisp_Object str = Fget (sym, Qmac_scrap_name);
5734 if (!STRINGP (str))
5735 *sel = NULL;
5736 else
5738 #if TARGET_API_MAC_CARBON
5739 #ifdef MAC_OSX
5740 CFStringRef scrap_name = cfstring_create_with_string (str);
5741 OptionBits options = (clear_p ? kScrapClearNamedScrap
5742 : kScrapGetNamedScrap);
5744 err = GetScrapByName (scrap_name, options, sel);
5745 CFRelease (scrap_name);
5746 #else /* !MAC_OSX */
5747 if (clear_p)
5748 err = ClearCurrentScrap ();
5749 if (err == noErr)
5750 err = GetCurrentScrap (sel);
5751 #endif /* !MAC_OSX */
5752 #else /* !TARGET_API_MAC_CARBON */
5753 if (clear_p)
5754 err = ZeroScrap ();
5755 if (err == noErr)
5756 *sel = 1;
5757 #endif /* !TARGET_API_MAC_CARBON */
5760 return err;
5763 /* Get a scrap flavor type from the symbol SYM. Return 0 if no
5764 corresponding flavor type. If SEL is non-zero, the return value is
5765 non-zero only when the SEL has the flavor type. */
5767 static ScrapFlavorType
5768 get_flavor_type_from_symbol (sym, sel)
5769 Lisp_Object sym;
5770 Selection sel;
5772 Lisp_Object str = Fget (sym, Qmac_ostype);
5773 ScrapFlavorType flavor_type;
5775 if (STRINGP (str) && SBYTES (str) == 4)
5776 flavor_type = EndianU32_BtoN (*((UInt32 *) SDATA (str)));
5777 else
5778 flavor_type = 0;
5780 if (flavor_type && sel)
5782 #if TARGET_API_MAC_CARBON
5783 OSStatus err;
5784 ScrapFlavorFlags flags;
5786 err = GetScrapFlavorFlags (sel, flavor_type, &flags);
5787 if (err != noErr)
5788 flavor_type = 0;
5789 #else /* !TARGET_API_MAC_CARBON */
5790 SInt32 size, offset;
5792 size = GetScrap (NULL, flavor_type, &offset);
5793 if (size < 0)
5794 flavor_type = 0;
5795 #endif /* !TARGET_API_MAC_CARBON */
5798 return flavor_type;
5801 /* Check if the symbol SYM has a corresponding selection target type. */
5804 mac_valid_selection_target_p (sym)
5805 Lisp_Object sym;
5807 return get_flavor_type_from_symbol (sym, 0) != 0;
5810 /* Clear the selection whose reference is *SEL. */
5812 OSStatus
5813 mac_clear_selection (sel)
5814 Selection *sel;
5816 #if TARGET_API_MAC_CARBON
5817 #ifdef MAC_OSX
5818 return ClearScrap (sel);
5819 #else
5820 OSStatus err;
5822 err = ClearCurrentScrap ();
5823 if (err == noErr)
5824 err = GetCurrentScrap (sel);
5825 return err;
5826 #endif
5827 #else /* !TARGET_API_MAC_CARBON */
5828 return ZeroScrap ();
5829 #endif /* !TARGET_API_MAC_CARBON */
5832 /* Get ownership information for SEL. Emacs can detect a change of
5833 the ownership by comparing saved and current values of the
5834 ownership information. */
5836 Lisp_Object
5837 mac_get_selection_ownership_info (sel)
5838 Selection sel;
5840 #if TARGET_API_MAC_CARBON
5841 return long_to_cons ((unsigned long) sel);
5842 #else /* !TARGET_API_MAC_CARBON */
5843 ScrapStuffPtr scrap_info = InfoScrap ();
5845 return make_number (scrap_info->scrapCount);
5846 #endif /* !TARGET_API_MAC_CARBON */
5849 /* Return non-zero if VALUE is a valid selection value for TARGET. */
5852 mac_valid_selection_value_p (value, target)
5853 Lisp_Object value, target;
5855 return STRINGP (value);
5858 /* Put Lisp object VALUE to the selection SEL. The target type is
5859 specified by TARGET. */
5861 OSStatus
5862 mac_put_selection_value (sel, target, value)
5863 Selection sel;
5864 Lisp_Object target, value;
5866 ScrapFlavorType flavor_type = get_flavor_type_from_symbol (target, 0);
5868 if (flavor_type == 0 || !STRINGP (value))
5869 return noTypeErr;
5871 #if TARGET_API_MAC_CARBON
5872 return PutScrapFlavor (sel, flavor_type, kScrapFlavorMaskNone,
5873 SBYTES (value), SDATA (value));
5874 #else /* !TARGET_API_MAC_CARBON */
5875 return PutScrap (SBYTES (value), flavor_type, SDATA (value));
5876 #endif /* !TARGET_API_MAC_CARBON */
5879 /* Check if data for the target type TARGET is available in SEL. */
5882 mac_selection_has_target_p (sel, target)
5883 Selection sel;
5884 Lisp_Object target;
5886 return get_flavor_type_from_symbol (target, sel) != 0;
5889 /* Get data for the target type TARGET from SEL and create a Lisp
5890 string. Return nil if failed to get data. */
5892 Lisp_Object
5893 mac_get_selection_value (sel, target)
5894 Selection sel;
5895 Lisp_Object target;
5897 OSStatus err;
5898 Lisp_Object result = Qnil;
5899 ScrapFlavorType flavor_type = get_flavor_type_from_symbol (target, sel);
5900 #if TARGET_API_MAC_CARBON
5901 Size size;
5903 if (flavor_type)
5905 err = GetScrapFlavorSize (sel, flavor_type, &size);
5906 if (err == noErr)
5910 result = make_uninit_string (size);
5911 err = GetScrapFlavorData (sel, flavor_type,
5912 &size, SDATA (result));
5913 if (err != noErr)
5914 result = Qnil;
5915 else if (size < SBYTES (result))
5916 result = make_unibyte_string (SDATA (result), size);
5918 while (STRINGP (result) && size > SBYTES (result));
5921 #else
5922 Handle handle;
5923 SInt32 size, offset;
5925 if (flavor_type)
5926 size = GetScrap (NULL, flavor_type, &offset);
5927 if (size >= 0)
5929 handle = NewHandle (size);
5930 HLock (handle);
5931 size = GetScrap (handle, flavor_type, &offset);
5932 if (size >= 0)
5933 result = make_unibyte_string (*handle, size);
5934 DisposeHandle (handle);
5936 #endif
5938 return result;
5941 /* Get the list of target types in SEL. The return value is a list of
5942 target type symbols possibly followed by scrap flavor type
5943 strings. */
5945 Lisp_Object
5946 mac_get_selection_target_list (sel)
5947 Selection sel;
5949 Lisp_Object result = Qnil, rest, target;
5950 #if TARGET_API_MAC_CARBON
5951 OSStatus err;
5952 UInt32 count, i, type;
5953 ScrapFlavorInfo *flavor_info = NULL;
5954 Lisp_Object strings = Qnil;
5956 err = GetScrapFlavorCount (sel, &count);
5957 if (err == noErr)
5958 flavor_info = xmalloc (sizeof (ScrapFlavorInfo) * count);
5959 err = GetScrapFlavorInfoList (sel, &count, flavor_info);
5960 if (err != noErr)
5962 xfree (flavor_info);
5963 flavor_info = NULL;
5965 if (flavor_info == NULL)
5966 count = 0;
5967 #endif
5968 for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest))
5970 ScrapFlavorType flavor_type = 0;
5972 if (CONSP (XCAR (rest))
5973 && (target = XCAR (XCAR (rest)),
5974 SYMBOLP (target))
5975 && (flavor_type = get_flavor_type_from_symbol (target, sel)))
5977 result = Fcons (target, result);
5978 #if TARGET_API_MAC_CARBON
5979 for (i = 0; i < count; i++)
5980 if (flavor_info[i].flavorType == flavor_type)
5982 flavor_info[i].flavorType = 0;
5983 break;
5985 #endif
5988 #if TARGET_API_MAC_CARBON
5989 if (flavor_info)
5991 for (i = 0; i < count; i++)
5992 if (flavor_info[i].flavorType)
5994 type = EndianU32_NtoB (flavor_info[i].flavorType);
5995 strings = Fcons (make_unibyte_string ((char *) &type, 4), strings);
5997 result = nconc2 (result, strings);
5998 xfree (flavor_info);
6000 #endif
6002 return result;
6006 /***********************************************************************
6007 Apple event support
6008 ***********************************************************************/
6010 extern pascal OSErr mac_handle_apple_event P_ ((const AppleEvent *,
6011 AppleEvent *, SInt32));
6012 extern void cleanup_all_suspended_apple_events P_ ((void));
6014 void
6015 init_apple_event_handler ()
6017 OSErr err;
6018 long result;
6020 /* Make sure we have Apple events before starting. */
6021 err = Gestalt (gestaltAppleEventsAttr, &result);
6022 if (err != noErr)
6023 abort ();
6025 if (!(result & (1 << gestaltAppleEventsPresent)))
6026 abort ();
6028 err = AEInstallEventHandler (typeWildCard, typeWildCard,
6029 #if TARGET_API_MAC_CARBON
6030 NewAEEventHandlerUPP (mac_handle_apple_event),
6031 #else
6032 NewAEEventHandlerProc (mac_handle_apple_event),
6033 #endif
6034 0L, false);
6035 if (err != noErr)
6036 abort ();
6038 atexit (cleanup_all_suspended_apple_events);
6042 /***********************************************************************
6043 Drag and drop support
6044 ***********************************************************************/
6046 #if TARGET_API_MAC_CARBON
6047 extern Lisp_Object Vmac_dnd_known_types;
6049 static pascal OSErr mac_do_track_drag P_ ((DragTrackingMessage, WindowRef,
6050 void *, DragRef));
6051 static pascal OSErr mac_do_receive_drag P_ ((WindowRef, void *, DragRef));
6052 static DragTrackingHandlerUPP mac_do_track_dragUPP = NULL;
6053 static DragReceiveHandlerUPP mac_do_receive_dragUPP = NULL;
6055 static OSErr
6056 create_apple_event_from_drag_ref (drag, num_types, types, result)
6057 DragRef drag;
6058 UInt32 num_types;
6059 const FlavorType *types;
6060 AppleEvent *result;
6062 OSErr err;
6063 UInt16 num_items;
6064 AppleEvent items;
6065 long index;
6066 char *buf = NULL;
6068 err = CountDragItems (drag, &num_items);
6069 if (err != noErr)
6070 return err;
6071 err = AECreateList (NULL, 0, false, &items);
6072 if (err != noErr)
6073 return err;
6075 for (index = 1; index <= num_items; index++)
6077 ItemReference item;
6078 DescType desc_type = typeNull;
6079 Size size;
6081 err = GetDragItemReferenceNumber (drag, index, &item);
6082 if (err == noErr)
6084 int i;
6086 for (i = 0; i < num_types; i++)
6088 err = GetFlavorDataSize (drag, item, types[i], &size);
6089 if (err == noErr)
6091 buf = xrealloc (buf, size);
6092 err = GetFlavorData (drag, item, types[i], buf, &size, 0);
6094 if (err == noErr)
6096 desc_type = types[i];
6097 break;
6101 err = AEPutPtr (&items, index, desc_type,
6102 desc_type != typeNull ? buf : NULL,
6103 desc_type != typeNull ? size : 0);
6104 if (err != noErr)
6105 break;
6107 if (buf)
6108 xfree (buf);
6110 if (err == noErr)
6112 err = create_apple_event (0, 0, result); /* Dummy class and ID. */
6113 if (err == noErr)
6114 err = AEPutParamDesc (result, keyDirectObject, &items);
6115 if (err != noErr)
6116 AEDisposeDesc (result);
6119 AEDisposeDesc (&items);
6121 return err;
6124 static void
6125 mac_store_drag_event (window, mouse_pos, modifiers, desc)
6126 WindowRef window;
6127 Point mouse_pos;
6128 SInt16 modifiers;
6129 const AEDesc *desc;
6131 struct input_event buf;
6133 EVENT_INIT (buf);
6135 buf.kind = DRAG_N_DROP_EVENT;
6136 buf.modifiers = mac_to_emacs_modifiers (modifiers, 0);
6137 buf.timestamp = TickCount () * (1000 / 60);
6138 XSETINT (buf.x, mouse_pos.h);
6139 XSETINT (buf.y, mouse_pos.v);
6140 XSETFRAME (buf.frame_or_window, mac_window_to_frame (window));
6141 buf.arg = mac_aedesc_to_lisp (desc);
6142 kbd_buffer_store_event (&buf);
6145 static pascal OSErr
6146 mac_do_track_drag (message, window, refcon, drag)
6147 DragTrackingMessage message;
6148 WindowRef window;
6149 void *refcon;
6150 DragRef drag;
6152 OSErr err = noErr;
6153 static int can_accept;
6154 UInt16 num_items, index;
6156 if (GetFrontWindowOfClass (kMovableModalWindowClass, false))
6157 return dragNotAcceptedErr;
6159 switch (message)
6161 case kDragTrackingEnterHandler:
6162 err = CountDragItems (drag, &num_items);
6163 if (err != noErr)
6164 break;
6165 can_accept = 0;
6166 for (index = 1; index <= num_items; index++)
6168 ItemReference item;
6169 FlavorFlags flags;
6170 Lisp_Object rest;
6172 err = GetDragItemReferenceNumber (drag, index, &item);
6173 if (err != noErr)
6174 continue;
6175 for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest))
6177 Lisp_Object str;
6178 FlavorType type;
6180 str = XCAR (rest);
6181 if (!(STRINGP (str) && SBYTES (str) == 4))
6182 continue;
6183 type = EndianU32_BtoN (*((UInt32 *) SDATA (str)));
6185 err = GetFlavorFlags (drag, item, type, &flags);
6186 if (err == noErr)
6188 can_accept = 1;
6189 break;
6193 break;
6195 case kDragTrackingEnterWindow:
6196 if (can_accept)
6198 RgnHandle hilite_rgn = NewRgn ();
6200 if (hilite_rgn)
6202 Rect r;
6204 GetWindowPortBounds (window, &r);
6205 OffsetRect (&r, -r.left, -r.top);
6206 RectRgn (hilite_rgn, &r);
6207 ShowDragHilite (drag, hilite_rgn, true);
6208 DisposeRgn (hilite_rgn);
6210 SetThemeCursor (kThemeCopyArrowCursor);
6212 break;
6214 case kDragTrackingInWindow:
6215 break;
6217 case kDragTrackingLeaveWindow:
6218 if (can_accept)
6220 HideDragHilite (drag);
6221 SetThemeCursor (kThemeArrowCursor);
6223 break;
6225 case kDragTrackingLeaveHandler:
6226 break;
6229 if (err != noErr)
6230 return dragNotAcceptedErr;
6231 return noErr;
6234 static pascal OSErr
6235 mac_do_receive_drag (window, refcon, drag)
6236 WindowRef window;
6237 void *refcon;
6238 DragRef drag;
6240 OSErr err;
6241 int num_types, i;
6242 Lisp_Object rest, str;
6243 FlavorType *types;
6244 AppleEvent apple_event;
6245 Point mouse_pos;
6246 SInt16 modifiers;
6248 if (GetFrontWindowOfClass (kMovableModalWindowClass, false))
6249 return dragNotAcceptedErr;
6251 num_types = 0;
6252 for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest))
6254 str = XCAR (rest);
6255 if (STRINGP (str) && SBYTES (str) == 4)
6256 num_types++;
6259 types = xmalloc (sizeof (FlavorType) * num_types);
6260 i = 0;
6261 for (rest = Vmac_dnd_known_types; CONSP (rest); rest = XCDR (rest))
6263 str = XCAR (rest);
6264 if (STRINGP (str) && SBYTES (str) == 4)
6265 types[i++] = EndianU32_BtoN (*((UInt32 *) SDATA (str)));
6268 err = create_apple_event_from_drag_ref (drag, num_types, types,
6269 &apple_event);
6270 xfree (types);
6272 if (err == noErr)
6273 err = GetDragMouse (drag, &mouse_pos, NULL);
6274 if (err == noErr)
6276 GlobalToLocal (&mouse_pos);
6277 err = GetDragModifiers (drag, NULL, NULL, &modifiers);
6279 if (err == noErr)
6281 UInt32 key_modifiers = modifiers;
6283 err = AEPutParamPtr (&apple_event, kEventParamKeyModifiers,
6284 typeUInt32, &key_modifiers, sizeof (UInt32));
6287 if (err == noErr)
6289 mac_store_drag_event (window, mouse_pos, 0, &apple_event);
6290 AEDisposeDesc (&apple_event);
6291 mac_wakeup_from_rne ();
6292 return noErr;
6294 else
6295 return dragNotAcceptedErr;
6297 #endif /* TARGET_API_MAC_CARBON */
6299 static OSErr
6300 install_drag_handler (window)
6301 WindowRef window;
6303 OSErr err = noErr;
6305 #if TARGET_API_MAC_CARBON
6306 if (mac_do_track_dragUPP == NULL)
6307 mac_do_track_dragUPP = NewDragTrackingHandlerUPP (mac_do_track_drag);
6308 if (mac_do_receive_dragUPP == NULL)
6309 mac_do_receive_dragUPP = NewDragReceiveHandlerUPP (mac_do_receive_drag);
6311 err = InstallTrackingHandler (mac_do_track_dragUPP, window, NULL);
6312 if (err == noErr)
6313 err = InstallReceiveHandler (mac_do_receive_dragUPP, window, NULL);
6314 #endif
6316 return err;
6319 static void
6320 remove_drag_handler (window)
6321 WindowRef window;
6323 #if TARGET_API_MAC_CARBON
6324 if (mac_do_track_dragUPP)
6325 RemoveTrackingHandler (mac_do_track_dragUPP, window);
6326 if (mac_do_receive_dragUPP)
6327 RemoveReceiveHandler (mac_do_receive_dragUPP, window);
6328 #endif
6331 #if TARGET_API_MAC_CARBON
6332 /* Return default value for mac-dnd-known-types. */
6334 Lisp_Object
6335 mac_dnd_default_known_types ()
6337 Lisp_Object result = list4 (build_string ("hfs "), build_string ("utxt"),
6338 build_string ("TEXT"), build_string ("TIFF"));
6340 #ifdef MAC_OSX
6341 result = Fcons (build_string ("furl"), result);
6342 #endif
6344 return result;
6346 #endif
6349 /***********************************************************************
6350 Services menu support
6351 ***********************************************************************/
6353 #ifdef MAC_OSX
6354 extern Lisp_Object Qservice, Qpaste, Qperform;
6355 extern Lisp_Object Vmac_service_selection;
6357 static OSStatus
6358 mac_store_service_event (event)
6359 EventRef event;
6361 OSStatus err;
6362 Lisp_Object id_key;
6363 int num_params;
6364 const EventParamName *names;
6365 const EventParamType *types;
6366 static const EventParamName names_pfm[] =
6367 {kEventParamServiceMessageName, kEventParamServiceUserData};
6368 static const EventParamType types_pfm[] =
6369 {typeCFStringRef, typeCFStringRef};
6371 switch (GetEventKind (event))
6373 case kEventServicePaste:
6374 id_key = Qpaste;
6375 num_params = 0;
6376 names = NULL;
6377 types = NULL;
6378 break;
6380 case kEventServicePerform:
6381 id_key = Qperform;
6382 num_params = sizeof (names_pfm) / sizeof (names_pfm[0]);
6383 names = names_pfm;
6384 types = types_pfm;
6385 break;
6387 default:
6388 abort ();
6391 err = mac_store_event_ref_as_apple_event (0, 0, Qservice, id_key,
6392 event, num_params,
6393 names, types);
6395 return err;
6398 static OSStatus
6399 copy_scrap_flavor_data (from_scrap, to_scrap, flavor_type)
6400 ScrapRef from_scrap, to_scrap;
6401 ScrapFlavorType flavor_type;
6403 OSStatus err;
6404 Size size, size_allocated;
6405 char *buf = NULL;
6407 err = GetScrapFlavorSize (from_scrap, flavor_type, &size);
6408 if (err == noErr)
6409 buf = xmalloc (size);
6410 while (buf)
6412 size_allocated = size;
6413 err = GetScrapFlavorData (from_scrap, flavor_type, &size, buf);
6414 if (err != noErr)
6416 xfree (buf);
6417 buf = NULL;
6419 else if (size_allocated < size)
6420 buf = xrealloc (buf, size);
6421 else
6422 break;
6424 if (err == noErr)
6426 if (buf == NULL)
6427 err = memFullErr;
6428 else
6430 err = PutScrapFlavor (to_scrap, flavor_type, kScrapFlavorMaskNone,
6431 size, buf);
6432 xfree (buf);
6436 return err;
6439 static OSStatus
6440 mac_handle_service_event (call_ref, event, data)
6441 EventHandlerCallRef call_ref;
6442 EventRef event;
6443 void *data;
6445 OSStatus err = noErr;
6446 ScrapRef cur_scrap, specific_scrap;
6447 UInt32 event_kind = GetEventKind (event);
6448 CFMutableArrayRef copy_types, paste_types;
6449 CFStringRef type;
6450 Lisp_Object rest;
6451 ScrapFlavorType flavor_type;
6453 /* Check if Vmac_service_selection is a valid selection that has a
6454 corresponding scrap. */
6455 if (!SYMBOLP (Vmac_service_selection))
6456 err = eventNotHandledErr;
6457 else
6458 err = mac_get_selection_from_symbol (Vmac_service_selection, 0, &cur_scrap);
6459 if (!(err == noErr && cur_scrap))
6460 return eventNotHandledErr;
6462 switch (event_kind)
6464 case kEventServiceGetTypes:
6465 /* Set paste types. */
6466 err = GetEventParameter (event, kEventParamServicePasteTypes,
6467 typeCFMutableArrayRef, NULL,
6468 sizeof (CFMutableArrayRef), NULL,
6469 &paste_types);
6470 if (err != noErr)
6471 break;
6473 for (rest = Vselection_converter_alist; CONSP (rest);
6474 rest = XCDR (rest))
6475 if (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest)))
6476 && (flavor_type =
6477 get_flavor_type_from_symbol (XCAR (XCAR (rest)), 0)))
6479 type = CreateTypeStringWithOSType (flavor_type);
6480 if (type)
6482 CFArrayAppendValue (paste_types, type);
6483 CFRelease (type);
6487 /* Set copy types. */
6488 err = GetEventParameter (event, kEventParamServiceCopyTypes,
6489 typeCFMutableArrayRef, NULL,
6490 sizeof (CFMutableArrayRef), NULL,
6491 &copy_types);
6492 if (err != noErr)
6493 break;
6495 if (NILP (Fx_selection_owner_p (Vmac_service_selection)))
6496 break;
6497 else
6498 goto copy_all_flavors;
6500 case kEventServiceCopy:
6501 err = GetEventParameter (event, kEventParamScrapRef,
6502 typeScrapRef, NULL,
6503 sizeof (ScrapRef), NULL, &specific_scrap);
6504 if (err != noErr
6505 || NILP (Fx_selection_owner_p (Vmac_service_selection)))
6507 err = eventNotHandledErr;
6508 break;
6511 copy_all_flavors:
6513 UInt32 count, i;
6514 ScrapFlavorInfo *flavor_info = NULL;
6515 ScrapFlavorFlags flags;
6517 err = GetScrapFlavorCount (cur_scrap, &count);
6518 if (err == noErr)
6519 flavor_info = xmalloc (sizeof (ScrapFlavorInfo) * count);
6520 err = GetScrapFlavorInfoList (cur_scrap, &count, flavor_info);
6521 if (err != noErr)
6523 xfree (flavor_info);
6524 flavor_info = NULL;
6526 if (flavor_info == NULL)
6527 break;
6529 for (i = 0; i < count; i++)
6531 flavor_type = flavor_info[i].flavorType;
6532 err = GetScrapFlavorFlags (cur_scrap, flavor_type, &flags);
6533 if (err == noErr && !(flags & kScrapFlavorMaskSenderOnly))
6535 if (event_kind == kEventServiceCopy)
6536 err = copy_scrap_flavor_data (cur_scrap, specific_scrap,
6537 flavor_type);
6538 else /* event_kind == kEventServiceGetTypes */
6540 type = CreateTypeStringWithOSType (flavor_type);
6541 if (type)
6543 CFArrayAppendValue (copy_types, type);
6544 CFRelease (type);
6549 xfree (flavor_info);
6551 break;
6553 case kEventServicePaste:
6554 case kEventServicePerform:
6556 int data_exists_p = 0;
6558 err = GetEventParameter (event, kEventParamScrapRef, typeScrapRef,
6559 NULL, sizeof (ScrapRef), NULL,
6560 &specific_scrap);
6561 if (err == noErr)
6562 err = mac_clear_selection (&cur_scrap);
6563 if (err == noErr)
6564 for (rest = Vselection_converter_alist; CONSP (rest);
6565 rest = XCDR (rest))
6567 if (! (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest)))))
6568 continue;
6569 flavor_type = get_flavor_type_from_symbol (XCAR (XCAR (rest)),
6570 specific_scrap);
6571 if (flavor_type == 0)
6572 continue;
6573 err = copy_scrap_flavor_data (specific_scrap, cur_scrap,
6574 flavor_type);
6575 if (err == noErr)
6576 data_exists_p = 1;
6578 if (!data_exists_p)
6579 err = eventNotHandledErr;
6580 else
6581 err = mac_store_service_event (event);
6583 break;
6586 if (err != noErr)
6587 err = eventNotHandledErr;
6588 return err;
6591 static OSStatus
6592 install_service_handler ()
6594 static const EventTypeSpec specs[] =
6595 {{kEventClassService, kEventServiceGetTypes},
6596 {kEventClassService, kEventServiceCopy},
6597 {kEventClassService, kEventServicePaste},
6598 {kEventClassService, kEventServicePerform}};
6600 return InstallApplicationEventHandler (NewEventHandlerUPP
6601 (mac_handle_service_event),
6602 GetEventTypeCount (specs),
6603 specs, NULL, NULL);
6605 #endif /* MAC_OSX */
6608 /***********************************************************************
6609 Initialization
6610 ***********************************************************************/
6612 void
6613 mac_toolbox_initialize ()
6615 any_help_event_p = 0;
6617 init_menu_bar ();
6619 #ifdef MAC_OSX
6620 init_apple_event_handler ();
6621 #endif
6622 #if USE_MAC_TSM
6623 init_tsm ();
6624 #endif
6627 /* arch-tag: 71a597a8-6e9f-47b0-8b89-5a5ae3e16516
6628 (do not change this comment) */