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