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