diff options
Diffstat (limited to 'src/macmenu.c')
| -rw-r--r-- | src/macmenu.c | 396 |
1 files changed, 390 insertions, 6 deletions
diff --git a/src/macmenu.c b/src/macmenu.c index a70a80d32ed..3ea09412650 100644 --- a/src/macmenu.c +++ b/src/macmenu.c | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | /* Menu support for GNU Emacs on Mac OS. | 1 | /* Menu support for GNU Emacs on Mac OS. |
| 2 | Copyright (C) 2000, 2001, 2002, 2003, 2004, | 2 | Copyright (C) 2000, 2001, 2002, 2003, 2004, |
| 3 | 2005, 2006 Free Software Foundation, Inc. | 3 | 2005, 2006, 2007 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | This file is part of GNU Emacs. | 5 | This file is part of GNU Emacs. |
| 6 | 6 | ||
| @@ -77,10 +77,11 @@ static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768}; | |||
| 77 | 77 | ||
| 78 | #define DIALOG_WINDOW_RESOURCE 130 | 78 | #define DIALOG_WINDOW_RESOURCE 130 |
| 79 | 79 | ||
| 80 | #if TARGET_API_MAC_CARBON | ||
| 80 | #define HAVE_DIALOGS 1 | 81 | #define HAVE_DIALOGS 1 |
| 82 | #endif | ||
| 81 | 83 | ||
| 82 | #undef HAVE_MULTILINGUAL_MENU | 84 | #undef HAVE_MULTILINGUAL_MENU |
| 83 | #undef HAVE_DIALOGS /* TODO: Implement native dialogs. */ | ||
| 84 | 85 | ||
| 85 | /******************************************************************/ | 86 | /******************************************************************/ |
| 86 | /* Definitions copied from lwlib.h */ | 87 | /* Definitions copied from lwlib.h */ |
| @@ -2319,8 +2320,390 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) | |||
| 2319 | 2320 | ||
| 2320 | 2321 | ||
| 2321 | #ifdef HAVE_DIALOGS | 2322 | #ifdef HAVE_DIALOGS |
| 2322 | /* Construct native Mac OS menubar based on widget_value tree. */ | 2323 | /* Construct native Mac OS dialog based on widget_value tree. */ |
| 2324 | |||
| 2325 | #if TARGET_API_MAC_CARBON | ||
| 2326 | |||
| 2327 | static pascal OSStatus | ||
| 2328 | mac_handle_dialog_event (next_handler, event, data) | ||
| 2329 | EventHandlerCallRef next_handler; | ||
| 2330 | EventRef event; | ||
| 2331 | void *data; | ||
| 2332 | { | ||
| 2333 | OSStatus err; | ||
| 2334 | WindowRef window = (WindowRef) data; | ||
| 2335 | |||
| 2336 | switch (GetEventClass (event)) | ||
| 2337 | { | ||
| 2338 | case kEventClassCommand: | ||
| 2339 | { | ||
| 2340 | HICommand command; | ||
| 2341 | |||
| 2342 | err = GetEventParameter (event, kEventParamDirectObject, | ||
| 2343 | typeHICommand, NULL, sizeof (HICommand), | ||
| 2344 | NULL, &command); | ||
| 2345 | if (err == noErr) | ||
| 2346 | if ((command.commandID & ~0xffff) == 'Bt\0\0') | ||
| 2347 | { | ||
| 2348 | SetWRefCon (window, command.commandID); | ||
| 2349 | err = QuitAppModalLoopForWindow (window); | ||
| 2350 | |||
| 2351 | return err == noErr ? noErr : eventNotHandledErr; | ||
| 2352 | } | ||
| 2353 | |||
| 2354 | return CallNextEventHandler (next_handler, event); | ||
| 2355 | } | ||
| 2356 | break; | ||
| 2357 | |||
| 2358 | case kEventClassKeyboard: | ||
| 2359 | { | ||
| 2360 | OSStatus result; | ||
| 2361 | char char_code; | ||
| 2362 | |||
| 2363 | result = CallNextEventHandler (next_handler, event); | ||
| 2364 | if (result == noErr) | ||
| 2365 | return noErr; | ||
| 2366 | |||
| 2367 | err = GetEventParameter (event, kEventParamKeyMacCharCodes, | ||
| 2368 | typeChar, NULL, sizeof (char), | ||
| 2369 | NULL, &char_code); | ||
| 2370 | if (err == noErr) | ||
| 2371 | switch (char_code) | ||
| 2372 | { | ||
| 2373 | case kEscapeCharCode: | ||
| 2374 | err = QuitAppModalLoopForWindow (window); | ||
| 2375 | break; | ||
| 2376 | |||
| 2377 | default: | ||
| 2378 | { | ||
| 2379 | UInt32 modifiers, key_code; | ||
| 2380 | |||
| 2381 | err = GetEventParameter (event, kEventParamKeyModifiers, | ||
| 2382 | typeUInt32, NULL, sizeof (UInt32), | ||
| 2383 | NULL, &modifiers); | ||
| 2384 | if (err == noErr) | ||
| 2385 | err = GetEventParameter (event, kEventParamKeyCode, | ||
| 2386 | typeUInt32, NULL, sizeof (UInt32), | ||
| 2387 | NULL, &key_code); | ||
| 2388 | if (err == noErr) | ||
| 2389 | if (mac_quit_char_key_p (modifiers, key_code)) | ||
| 2390 | err = QuitAppModalLoopForWindow (window); | ||
| 2391 | else | ||
| 2392 | err = eventNotHandledErr; | ||
| 2393 | } | ||
| 2394 | break; | ||
| 2395 | } | ||
| 2396 | |||
| 2397 | return err == noErr ? noErr : result; | ||
| 2398 | } | ||
| 2399 | break; | ||
| 2400 | |||
| 2401 | default: | ||
| 2402 | abort (); | ||
| 2403 | } | ||
| 2404 | } | ||
| 2405 | |||
| 2406 | static OSStatus | ||
| 2407 | install_dialog_event_handler (window) | ||
| 2408 | WindowRef window; | ||
| 2409 | { | ||
| 2410 | static const EventTypeSpec specs[] = | ||
| 2411 | {{kEventClassCommand, kEventCommandProcess}, | ||
| 2412 | {kEventClassKeyboard, kEventRawKeyDown}}; | ||
| 2413 | static EventHandlerUPP handle_dialog_eventUPP = NULL; | ||
| 2414 | |||
| 2415 | if (handle_dialog_eventUPP == NULL) | ||
| 2416 | handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event); | ||
| 2417 | return InstallWindowEventHandler (window, handle_dialog_eventUPP, | ||
| 2418 | GetEventTypeCount (specs), specs, | ||
| 2419 | window, NULL); | ||
| 2420 | } | ||
| 2421 | |||
| 2422 | #define DIALOG_LEFT_MARGIN (112) | ||
| 2423 | #define DIALOG_TOP_MARGIN (24) | ||
| 2424 | #define DIALOG_RIGHT_MARGIN (24) | ||
| 2425 | #define DIALOG_BOTTOM_MARGIN (20) | ||
| 2426 | #define DIALOG_MIN_INNER_WIDTH (338) | ||
| 2427 | #define DIALOG_MAX_INNER_WIDTH (564) | ||
| 2428 | #define DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE (12) | ||
| 2429 | #define DIALOG_BUTTON_BUTTON_VERTICAL_SPACE (12) | ||
| 2430 | #define DIALOG_BUTTON_MIN_WIDTH (68) | ||
| 2431 | #define DIALOG_TEXT_MIN_HEIGHT (50) | ||
| 2432 | #define DIALOG_TEXT_BUTTONS_VERTICAL_SPACE (10) | ||
| 2433 | #define DIALOG_ICON_WIDTH (64) | ||
| 2434 | #define DIALOG_ICON_HEIGHT (64) | ||
| 2435 | #define DIALOG_ICON_LEFT_MARGIN (24) | ||
| 2436 | #define DIALOG_ICON_TOP_MARGIN (15) | ||
| 2437 | |||
| 2438 | static int | ||
| 2439 | create_and_show_dialog (f, first_wv) | ||
| 2440 | FRAME_PTR f; | ||
| 2441 | widget_value *first_wv; | ||
| 2442 | { | ||
| 2443 | OSStatus err; | ||
| 2444 | char *dialog_name, *message; | ||
| 2445 | int nb_buttons, first_group_count, i, result = 0; | ||
| 2446 | widget_value *wv; | ||
| 2447 | short buttons_height, text_height, inner_width, inner_height; | ||
| 2448 | Rect empty_rect, *rects; | ||
| 2449 | WindowRef window = NULL; | ||
| 2450 | ControlRef *buttons, default_button = NULL, text; | ||
| 2451 | |||
| 2452 | dialog_name = first_wv->name; | ||
| 2453 | nb_buttons = dialog_name[1] - '0'; | ||
| 2454 | first_group_count = nb_buttons - (dialog_name[4] - '0'); | ||
| 2455 | |||
| 2456 | wv = first_wv->contents; | ||
| 2457 | message = wv->value; | ||
| 2458 | |||
| 2459 | wv = wv->next; | ||
| 2460 | SetRect (&empty_rect, 0, 0, 0, 0); | ||
| 2461 | |||
| 2462 | /* Create dialog window. */ | ||
| 2463 | err = CreateNewWindow (kMovableModalWindowClass, | ||
| 2464 | kWindowStandardHandlerAttribute, | ||
| 2465 | &empty_rect, &window); | ||
| 2466 | if (err == noErr) | ||
| 2467 | err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground, | ||
| 2468 | true); | ||
| 2469 | if (err == noErr) | ||
| 2470 | err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q' | ||
| 2471 | ? CFSTR ("Question") | ||
| 2472 | : CFSTR ("Information"))); | ||
| 2473 | |||
| 2474 | /* Create button controls and measure their optimal bounds. */ | ||
| 2475 | if (err == noErr) | ||
| 2476 | { | ||
| 2477 | buttons = alloca (sizeof (ControlRef) * nb_buttons); | ||
| 2478 | rects = alloca (sizeof (Rect) * nb_buttons); | ||
| 2479 | for (i = 0; i < nb_buttons; i++) | ||
| 2480 | { | ||
| 2481 | CFStringRef label = cfstring_create_with_utf8_cstring (wv->value); | ||
| 2482 | |||
| 2483 | if (label == NULL) | ||
| 2484 | err = memFullErr; | ||
| 2485 | else | ||
| 2486 | { | ||
| 2487 | err = CreatePushButtonControl (window, &empty_rect, | ||
| 2488 | label, &buttons[i]); | ||
| 2489 | CFRelease (label); | ||
| 2490 | } | ||
| 2491 | if (err == noErr) | ||
| 2492 | { | ||
| 2493 | if (!wv->enabled) | ||
| 2494 | { | ||
| 2495 | #ifdef MAC_OSX | ||
| 2496 | err = DisableControl (buttons[i]); | ||
| 2497 | #else | ||
| 2498 | err = DeactivateControl (buttons[i]); | ||
| 2499 | #endif | ||
| 2500 | } | ||
| 2501 | else if (default_button == NULL) | ||
| 2502 | default_button = buttons[i]; | ||
| 2503 | } | ||
| 2504 | if (err == noErr) | ||
| 2505 | { | ||
| 2506 | SInt16 unused; | ||
| 2507 | |||
| 2508 | rects[i] = empty_rect; | ||
| 2509 | err = GetBestControlRect (buttons[i], &rects[i], &unused); | ||
| 2510 | } | ||
| 2511 | if (err == noErr) | ||
| 2512 | { | ||
| 2513 | OffsetRect (&rects[i], -rects[i].left, -rects[i].top); | ||
| 2514 | if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH) | ||
| 2515 | rects[i].right = DIALOG_BUTTON_MIN_WIDTH; | ||
| 2516 | else if (rects[i].right > DIALOG_MAX_INNER_WIDTH) | ||
| 2517 | rects[i].right = DIALOG_MAX_INNER_WIDTH; | ||
| 2518 | |||
| 2519 | err = SetControlCommandID (buttons[i], | ||
| 2520 | 'Bt\0\0' + (int) wv->call_data); | ||
| 2521 | } | ||
| 2522 | if (err != noErr) | ||
| 2523 | break; | ||
| 2524 | wv = wv->next; | ||
| 2525 | } | ||
| 2526 | } | ||
| 2527 | |||
| 2528 | /* Layout buttons. rects[i] is set relative to the bottom-right | ||
| 2529 | corner of the inner box. */ | ||
| 2530 | if (err == noErr) | ||
| 2531 | { | ||
| 2532 | short bottom, right, max_height, left_align_shift; | ||
| 2533 | |||
| 2534 | inner_width = DIALOG_MIN_INNER_WIDTH; | ||
| 2535 | bottom = right = max_height = 0; | ||
| 2536 | for (i = 0; i < nb_buttons; i++) | ||
| 2537 | { | ||
| 2538 | if (right - rects[i].right < - inner_width) | ||
| 2539 | { | ||
| 2540 | if (i != first_group_count | ||
| 2541 | && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH) | ||
| 2542 | inner_width = - (right - rects[i].right); | ||
| 2543 | else | ||
| 2544 | { | ||
| 2545 | bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE; | ||
| 2546 | right = max_height = 0; | ||
| 2547 | } | ||
| 2548 | } | ||
| 2549 | if (max_height < rects[i].bottom) | ||
| 2550 | max_height = rects[i].bottom; | ||
| 2551 | OffsetRect (&rects[i], right - rects[i].right, | ||
| 2552 | bottom - rects[i].bottom); | ||
| 2553 | right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE; | ||
| 2554 | if (i == first_group_count - 1) | ||
| 2555 | right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE; | ||
| 2556 | } | ||
| 2557 | buttons_height = - (bottom - max_height); | ||
| 2558 | |||
| 2559 | left_align_shift = - (inner_width + rects[nb_buttons - 1].left); | ||
| 2560 | for (i = nb_buttons - 1; i >= first_group_count; i--) | ||
| 2561 | { | ||
| 2562 | if (bottom != rects[i].bottom) | ||
| 2563 | { | ||
| 2564 | left_align_shift = - (inner_width + rects[i].left); | ||
| 2565 | bottom = rects[i].bottom; | ||
| 2566 | } | ||
| 2567 | OffsetRect (&rects[i], left_align_shift, 0); | ||
| 2568 | } | ||
| 2569 | } | ||
| 2570 | |||
| 2571 | /* Create a static text control and measure its bounds. */ | ||
| 2572 | if (err == noErr) | ||
| 2573 | { | ||
| 2574 | CFStringRef message_string; | ||
| 2575 | Rect bounds; | ||
| 2576 | |||
| 2577 | message_string = cfstring_create_with_utf8_cstring (message); | ||
| 2578 | if (message_string == NULL) | ||
| 2579 | err = memFullErr; | ||
| 2580 | else | ||
| 2581 | { | ||
| 2582 | ControlFontStyleRec text_style; | ||
| 2583 | |||
| 2584 | text_style.flags = 0; | ||
| 2585 | SetRect (&bounds, 0, 0, inner_width, 0); | ||
| 2586 | err = CreateStaticTextControl (window, &bounds, message_string, | ||
| 2587 | &text_style, &text); | ||
| 2588 | CFRelease (message_string); | ||
| 2589 | } | ||
| 2590 | if (err == noErr) | ||
| 2591 | { | ||
| 2592 | SInt16 unused; | ||
| 2593 | |||
| 2594 | bounds = empty_rect; | ||
| 2595 | err = GetBestControlRect (text, &bounds, &unused); | ||
| 2596 | } | ||
| 2597 | if (err == noErr) | ||
| 2598 | { | ||
| 2599 | text_height = bounds.bottom - bounds.top; | ||
| 2600 | if (text_height < DIALOG_TEXT_MIN_HEIGHT) | ||
| 2601 | text_height = DIALOG_TEXT_MIN_HEIGHT; | ||
| 2602 | } | ||
| 2603 | } | ||
| 2604 | |||
| 2605 | /* Place buttons. */ | ||
| 2606 | if (err == noErr) | ||
| 2607 | { | ||
| 2608 | inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE | ||
| 2609 | + buttons_height); | ||
| 2610 | |||
| 2611 | for (i = 0; i < nb_buttons; i++) | ||
| 2612 | { | ||
| 2613 | OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width, | ||
| 2614 | DIALOG_TOP_MARGIN + inner_height); | ||
| 2615 | SetControlBounds (buttons[i], &rects[i]); | ||
| 2616 | } | ||
| 2617 | } | ||
| 2323 | 2618 | ||
| 2619 | /* Place text. */ | ||
| 2620 | if (err == noErr) | ||
| 2621 | { | ||
| 2622 | Rect bounds; | ||
| 2623 | |||
| 2624 | SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN, | ||
| 2625 | DIALOG_LEFT_MARGIN + inner_width, | ||
| 2626 | DIALOG_TOP_MARGIN + text_height); | ||
| 2627 | SetControlBounds (text, &bounds); | ||
| 2628 | } | ||
| 2629 | |||
| 2630 | /* Create the application icon at the upper-left corner. */ | ||
| 2631 | if (err == noErr) | ||
| 2632 | { | ||
| 2633 | ControlButtonContentInfo content; | ||
| 2634 | ControlRef icon; | ||
| 2635 | static const ProcessSerialNumber psn = {0, kCurrentProcess}; | ||
| 2636 | #ifdef MAC_OSX | ||
| 2637 | FSRef app_location; | ||
| 2638 | #else | ||
| 2639 | ProcessInfoRec pinfo; | ||
| 2640 | FSSpec app_spec; | ||
| 2641 | #endif | ||
| 2642 | SInt16 unused; | ||
| 2643 | |||
| 2644 | content.contentType = kControlContentIconRef; | ||
| 2645 | #ifdef MAC_OSX | ||
| 2646 | err = GetProcessBundleLocation (&psn, &app_location); | ||
| 2647 | if (err == noErr) | ||
| 2648 | err = GetIconRefFromFileInfo (&app_location, 0, NULL, 0, NULL, | ||
| 2649 | kIconServicesNormalUsageFlag, | ||
| 2650 | &content.u.iconRef, &unused); | ||
| 2651 | #else | ||
| 2652 | bzero (&pinfo, sizeof (ProcessInfoRec)); | ||
| 2653 | pinfo.processInfoLength = sizeof (ProcessInfoRec); | ||
| 2654 | pinfo.processAppSpec = &app_spec; | ||
| 2655 | err = GetProcessInformation (&psn, &pinfo); | ||
| 2656 | if (err == noErr) | ||
| 2657 | err = GetIconRefFromFile (&app_spec, &content.u.iconRef, &unused); | ||
| 2658 | #endif | ||
| 2659 | if (err == noErr) | ||
| 2660 | { | ||
| 2661 | Rect bounds; | ||
| 2662 | |||
| 2663 | SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN, | ||
| 2664 | DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH, | ||
| 2665 | DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT); | ||
| 2666 | err = CreateIconControl (window, &bounds, &content, true, &icon); | ||
| 2667 | ReleaseIconRef (content.u.iconRef); | ||
| 2668 | } | ||
| 2669 | } | ||
| 2670 | |||
| 2671 | /* Show the dialog window and run event loop. */ | ||
| 2672 | if (err == noErr) | ||
| 2673 | if (default_button) | ||
| 2674 | err = SetWindowDefaultButton (window, default_button); | ||
| 2675 | if (err == noErr) | ||
| 2676 | err = install_dialog_event_handler (window); | ||
| 2677 | if (err == noErr) | ||
| 2678 | { | ||
| 2679 | SizeWindow (window, | ||
| 2680 | DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN, | ||
| 2681 | DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN, | ||
| 2682 | true); | ||
| 2683 | err = RepositionWindow (window, FRAME_MAC_WINDOW (f), | ||
| 2684 | kWindowAlertPositionOnParentWindow); | ||
| 2685 | } | ||
| 2686 | if (err == noErr) | ||
| 2687 | { | ||
| 2688 | SetWRefCon (window, 0); | ||
| 2689 | ShowWindow (window); | ||
| 2690 | BringToFront (window); | ||
| 2691 | err = RunAppModalLoopForWindow (window); | ||
| 2692 | } | ||
| 2693 | if (err == noErr) | ||
| 2694 | { | ||
| 2695 | UInt32 command_id = GetWRefCon (window); | ||
| 2696 | |||
| 2697 | if ((command_id & ~0xffff) == 'Bt\0\0') | ||
| 2698 | result = command_id - 'Bt\0\0'; | ||
| 2699 | } | ||
| 2700 | |||
| 2701 | if (window) | ||
| 2702 | DisposeWindow (window); | ||
| 2703 | |||
| 2704 | return result; | ||
| 2705 | } | ||
| 2706 | #else /* not TARGET_API_MAC_CARBON */ | ||
| 2324 | static int | 2707 | static int |
| 2325 | mac_dialog (widget_value *wv) | 2708 | mac_dialog (widget_value *wv) |
| 2326 | { | 2709 | { |
| @@ -2425,6 +2808,7 @@ mac_dialog (widget_value *wv) | |||
| 2425 | 2808 | ||
| 2426 | return i; | 2809 | return i; |
| 2427 | } | 2810 | } |
| 2811 | #endif /* not TARGET_API_MAC_CARBON */ | ||
| 2428 | 2812 | ||
| 2429 | static char * button_names [] = { | 2813 | static char * button_names [] = { |
| 2430 | "button1", "button2", "button3", "button4", "button5", | 2814 | "button1", "button2", "button3", "button4", "button5", |
| @@ -2557,10 +2941,10 @@ mac_dialog_show (f, keymaps, title, header, error_name) | |||
| 2557 | } | 2941 | } |
| 2558 | 2942 | ||
| 2559 | /* Actually create the dialog. */ | 2943 | /* Actually create the dialog. */ |
| 2560 | #ifdef HAVE_DIALOGS | 2944 | #if TARGET_API_MAC_CARBON |
| 2561 | menu_item_selection = mac_dialog (first_wv); | 2945 | menu_item_selection = create_and_show_dialog (f, first_wv); |
| 2562 | #else | 2946 | #else |
| 2563 | menu_item_selection = 0; | 2947 | menu_item_selection = mac_dialog (first_wv); |
| 2564 | #endif | 2948 | #endif |
| 2565 | 2949 | ||
| 2566 | /* Free the widget_value objects we used to specify the contents. */ | 2950 | /* Free the widget_value objects we used to specify the contents. */ |