changeset 75199:6f528fee9d6d

(HAVE_DIALOGS): Define if TARGET_API_MAC_CARBON. (mac_handle_dialog_event, install_dialog_event_handler) (create_and_show_dialog) [TARGET_API_MAC_CARBON]: New functions. (DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN, DIALOG_RIGHT_MARGIN) (DIALOG_BOTTOM_MARGIN, DIALOG_MIN_INNER_WIDTH) (DIALOG_MAX_INNER_WIDTH, DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE) (DIALOG_BUTTON_BUTTON_VERTICAL_SPACE, DIALOG_BUTTON_MIN_WIDTH) (DIALOG_TEXT_MIN_HEIGHT, DIALOG_TEXT_BUTTONS_VERTICAL_SPACE) (DIALOG_ICON_WIDTH, DIALOG_ICON_HEIGHT, DIALOG_ICON_LEFT_MARGIN) (DIALOG_ICON_TOP_MARGIN) [TARGET_API_MAC_CARBON]: New macros. (mac_dialog) [TARGET_API_MAC_CARBON]: Remove function. (mac_dialog_show) [TARGET_API_MAC_CARBON]: Use create_and_show_dialog.
author YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
date Fri, 12 Jan 2007 09:00:16 +0000
parents 1123e57f97c7
children 659905f1c3a4
files src/macmenu.c
diffstat 1 files changed, 359 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/macmenu.c	Fri Jan 12 00:55:34 2007 +0000
+++ b/src/macmenu.c	Fri Jan 12 09:00:16 2007 +0000
@@ -77,10 +77,11 @@
 
 #define DIALOG_WINDOW_RESOURCE 130
 
+#if TARGET_API_MAC_CARBON
 #define HAVE_DIALOGS 1
+#endif
 
 #undef HAVE_MULTILINGUAL_MENU
-#undef HAVE_DIALOGS /* TODO: Implement native dialogs.  */
 
 /******************************************************************/
 /* Definitions copied from lwlib.h */
@@ -2319,8 +2320,359 @@
 
 
 #ifdef HAVE_DIALOGS
-/* Construct native Mac OS menubar based on widget_value tree.  */
-
+/* Construct native Mac OS dialog based on widget_value tree.  */
+
+#if TARGET_API_MAC_CARBON
+
+static pascal OSStatus
+mac_handle_dialog_event (next_handler, event, data)
+     EventHandlerCallRef next_handler;
+     EventRef event;
+     void *data;
+{
+  OSStatus err;
+  WindowRef window = (WindowRef) data;
+
+  switch (GetEventClass (event))
+    {
+    case kEventClassCommand:
+      {
+	HICommand command;
+
+	err = GetEventParameter (event, kEventParamDirectObject,
+				 typeHICommand, NULL, sizeof (HICommand),
+				 NULL, &command);
+	if (err == noErr)
+	  if ((command.commandID & ~0xffff) == 'Bt\0\0')
+	    {
+	      SetWRefCon (window, command.commandID);
+	      err = QuitAppModalLoopForWindow (window);
+
+	      return err == noErr ? noErr : eventNotHandledErr;
+	    }
+
+	return CallNextEventHandler (next_handler, event);
+      }
+      break;
+
+    case kEventClassKeyboard:
+      {
+	OSStatus result;
+	char char_code;
+
+	result = CallNextEventHandler (next_handler, event);
+	if (result == noErr)
+	  return noErr;
+
+	err = GetEventParameter (event, kEventParamKeyMacCharCodes,
+				 typeChar, NULL, sizeof (char),
+				 NULL, &char_code);
+	if (err == noErr)
+	  switch (char_code)
+	    {
+	    case kEscapeCharCode:
+	      err = QuitAppModalLoopForWindow (window);
+	      break;
+
+	    default:
+	      {
+		UInt32 modifiers, key_code;
+
+		err = GetEventParameter (event, kEventParamKeyModifiers,
+					 typeUInt32, NULL, sizeof (UInt32),
+					 NULL, &modifiers);
+		if (err == noErr)
+		  err = GetEventParameter (event, kEventParamKeyCode,
+					   typeUInt32, NULL, sizeof (UInt32),
+					   NULL, &key_code);
+		if (err == noErr)
+		  if (mac_quit_char_key_p (modifiers, key_code))
+		    err = QuitAppModalLoopForWindow (window);
+		  else
+		    err = eventNotHandledErr;
+	      }
+	      break;
+	    }
+
+	return err == noErr ? noErr : result;
+      }
+      break;
+
+    default:
+      abort ();
+    }
+}
+
+static OSStatus
+install_dialog_event_handler (window)
+     WindowRef window;
+{
+  static const EventTypeSpec specs[] =
+    {{kEventClassCommand, kEventCommandProcess},
+     {kEventClassKeyboard, kEventRawKeyDown}};
+  static EventHandlerUPP handle_dialog_eventUPP = NULL;
+
+  if (handle_dialog_eventUPP == NULL)
+    handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event);
+  return InstallWindowEventHandler (window, handle_dialog_eventUPP,
+				    GetEventTypeCount (specs), specs,
+				    window, NULL);
+}
+
+#define DIALOG_LEFT_MARGIN (112)
+#define DIALOG_TOP_MARGIN (24)
+#define DIALOG_RIGHT_MARGIN (24)
+#define DIALOG_BOTTOM_MARGIN (20)
+#define DIALOG_MIN_INNER_WIDTH (338)
+#define DIALOG_MAX_INNER_WIDTH (564)
+#define DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE (12)
+#define DIALOG_BUTTON_BUTTON_VERTICAL_SPACE (12)
+#define DIALOG_BUTTON_MIN_WIDTH (68)
+#define DIALOG_TEXT_MIN_HEIGHT (50)
+#define DIALOG_TEXT_BUTTONS_VERTICAL_SPACE (10)
+#define DIALOG_ICON_WIDTH (64)
+#define DIALOG_ICON_HEIGHT (64)
+#define DIALOG_ICON_LEFT_MARGIN (24)
+#define DIALOG_ICON_TOP_MARGIN (15)
+
+static int
+create_and_show_dialog (f, first_wv)
+     FRAME_PTR f;
+     widget_value *first_wv;
+{
+  OSStatus err;
+  char *dialog_name, *message;
+  int nb_buttons, first_group_count, i, result = 0;
+  widget_value *wv;
+  short buttons_height, text_height, inner_width, inner_height;
+  Rect empty_rect, *rects;
+  WindowRef window = NULL;
+  ControlRef *buttons, text;
+
+  dialog_name = first_wv->name;
+  nb_buttons = dialog_name[1] - '0';
+  first_group_count = nb_buttons - (dialog_name[4] - '0');
+
+  wv = first_wv->contents;
+  message = wv->value;
+
+  wv = wv->next;
+  SetRect (&empty_rect, 0, 0, 0, 0);
+
+  /* Create dialog window.  */
+  err = CreateNewWindow (kMovableAlertWindowClass,
+			 kWindowStandardHandlerAttribute,
+			 &empty_rect, &window);
+  if (err == noErr)
+    err = SetThemeWindowBackground (window, kThemeBrushAlertBackgroundActive,
+				    true);
+  if (err == noErr)
+    err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q'
+					       ? CFSTR ("Question")
+					       : CFSTR ("Information")));
+
+  /* Create button controls and measure their optimal bounds.  */
+  if (err == noErr)
+    {
+      buttons = alloca (sizeof (ControlRef) * nb_buttons);
+      rects = alloca (sizeof (Rect) * nb_buttons);
+      for (i = 0; i < nb_buttons; i++)
+	{
+	  CFStringRef label = cfstring_create_with_utf8_cstring (wv->value);
+
+	  if (label == NULL)
+	    err = memFullErr;
+	  else
+	    {
+	      err = CreatePushButtonControl (window, &empty_rect,
+					     label, &buttons[i]);
+	      CFRelease (label);
+	    }
+	  if (err == noErr)
+	    {
+	      SInt16 unused;
+
+	      rects[i] = empty_rect;
+	      err = GetBestControlRect (buttons[i], &rects[i], &unused);
+	    }
+	  if (err == noErr)
+	    {
+	      OffsetRect (&rects[i], -rects[i].left, -rects[i].top);
+	      if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH)
+		rects[i].right = DIALOG_BUTTON_MIN_WIDTH;
+	      else if (rects[i].right > DIALOG_MAX_INNER_WIDTH)
+		rects[i].right = DIALOG_MAX_INNER_WIDTH;
+
+	      err = SetControlCommandID (buttons[i],
+					 'Bt\0\0' + (int) wv->call_data);
+	    }
+	  if (err != noErr)
+	    break;
+	  wv = wv->next;
+	}
+    }
+
+  /* Layout buttons.  rects[i] is set relative to the bottom-right
+     corner of the inner box.  */
+  if (err == noErr)
+    {
+      short bottom, right, max_height, left_align_shift;
+
+      inner_width = DIALOG_MIN_INNER_WIDTH;
+      bottom = right = max_height = 0;
+      for (i = 0; i < nb_buttons; i++)
+	{
+	  if (right - rects[i].right < - inner_width)
+	    {
+	      if (i != first_group_count
+		  && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH)
+		inner_width = - (right - rects[i].right);
+	      else
+		{
+		  bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE;
+		  right = max_height = 0;
+		}
+	    }
+	  if (max_height < rects[i].bottom)
+	    max_height = rects[i].bottom;
+	  OffsetRect (&rects[i], right - rects[i].right,
+		      bottom - rects[i].bottom);
+	  right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
+	  if (i == first_group_count - 1)
+	    right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
+	}
+      buttons_height = - (bottom - max_height);
+
+      left_align_shift = - (inner_width + rects[nb_buttons - 1].left);
+      for (i = nb_buttons - 1; i >= first_group_count; i--)
+	{
+	  if (bottom != rects[i].bottom)
+	    {
+	      left_align_shift = - (inner_width + rects[i].left);
+	      bottom = rects[i].bottom;
+	    }
+	  OffsetRect (&rects[i], left_align_shift, 0);
+	}
+    }
+
+  /* Create a static text control and measure its bounds.  */
+  if (err == noErr)
+    {
+      CFStringRef message_string;
+      Rect bounds;
+
+      message_string = cfstring_create_with_utf8_cstring (message);
+      if (message_string == NULL)
+	err = memFullErr;
+      else
+	{
+	  ControlFontStyleRec text_style;
+
+	  text_style.flags = 0;
+	  SetRect (&bounds, 0, 0, inner_width, 0);
+	  err = CreateStaticTextControl (window, &bounds, message_string,
+					 &text_style, &text);
+	  CFRelease (message_string);
+	}
+      if (err == noErr)
+	{
+	  SInt16 unused;
+
+	  bounds = empty_rect;
+	  err = GetBestControlRect (text, &bounds, &unused);
+	}
+      if (err == noErr)
+	{
+	  text_height = bounds.bottom - bounds.top;
+	  if (text_height < DIALOG_TEXT_MIN_HEIGHT)
+	    text_height = DIALOG_TEXT_MIN_HEIGHT;
+	}
+    }
+
+  /* Place buttons. */
+  if (err == noErr)
+    {
+      inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE
+		      + buttons_height);
+
+      for (i = 0; i < nb_buttons; i++)
+	{
+	  OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width,
+		      DIALOG_TOP_MARGIN + inner_height);
+	  SetControlBounds (buttons[i], &rects[i]);
+	}
+    }
+
+  /* Place text.  */
+  if (err == noErr)
+    {
+      Rect bounds;
+
+      SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN,
+	       DIALOG_LEFT_MARGIN + inner_width,
+	       DIALOG_TOP_MARGIN + text_height);
+      SetControlBounds (text, &bounds);
+    }
+
+  /* Create the application icon at the upper-left corner.  */
+  if (err == noErr)
+    {
+      ControlButtonContentInfo button_info;
+      IconRef icon_ref;
+      ControlRef icon;
+
+      button_info.contentType = kControlContentIconRef;
+      err = GetIconRef (kOnAppropriateDisk, MAC_EMACS_CREATOR_CODE,
+			kGenericApplicationIcon, &icon_ref);
+      if (err == noErr)
+	{
+	  Rect bounds;
+
+	  button_info.u.iconRef = icon_ref;
+	  SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN,
+		   DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH,
+		   DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT);
+	  err = CreateIconControl (window, &bounds, &button_info,
+				   true, &icon);
+	  ReleaseIconRef (icon_ref);
+	}
+    }
+
+  /* Show the dialog window and run event loop.  */
+  if (err == noErr)
+    err = SetWindowDefaultButton (window, buttons[0]);
+  if (err == noErr)
+    err = install_dialog_event_handler (window);
+  if (err == noErr)
+    {
+      SizeWindow (window,
+		  DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN,
+		  DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN,
+		  true);
+      err = RepositionWindow (window, FRAME_MAC_WINDOW (f),
+			      kWindowAlertPositionOnParentWindow);
+    }
+  if (err == noErr)
+    {
+      SetWRefCon (window, 0);
+      ShowWindow (window);
+      BringToFront (window);
+      err = RunAppModalLoopForWindow (window);
+    }
+  if (err == noErr)
+    {
+      UInt32 command_id = GetWRefCon (window);
+
+      if ((command_id & ~0xffff) == 'Bt\0\0')
+	result = command_id - 'Bt\0\0';
+    }
+
+  if (window)
+    DisposeWindow (window);
+
+  return result;
+}
+#else  /* not TARGET_API_MAC_CARBON */
 static int
 mac_dialog (widget_value *wv)
 {
@@ -2425,6 +2777,7 @@
 
   return i;
 }
+#endif  /* not TARGET_API_MAC_CARBON */
 
 static char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
@@ -2557,10 +2910,10 @@
   }
 
   /* Actually create the dialog.  */
-#ifdef HAVE_DIALOGS
+#if TARGET_API_MAC_CARBON
+  menu_item_selection = create_and_show_dialog (f, first_wv);
+#else
   menu_item_selection = mac_dialog (first_wv);
-#else
-  menu_item_selection = 0;
 #endif
 
   /* Free the widget_value objects we used to specify the contents.  */