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