Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/metacity/ChangeLog,v
retrieving revision 1.473
diff -u -p -u -r1.473 ChangeLog
--- ChangeLog	3 Oct 2002 22:02:17 -0000	1.473
+++ ChangeLog	3 Oct 2002 22:20:46 -0000
@@ -1,4 +1,23 @@
 2002-10-03  Havoc Pennington  <hp@redhat.com>
+	
+	Half-finished patch based on patch from John McCutchan to make
+	Alt+mouse actions and double-click titlebar action be
+	configurable. This probably breaks some things, the code is
+	significantly more complex/harder to verify now. You can't
+	actually set the preference yet.
+
+	* src/prefs.c: add some of the code to read mouse button action 
+	preferences, but not all.
+
+	* src/frames.c (do_button_press_action): factor out mouse button
+	press handling, and make it adapt to dynamic configuration
+
+	* src/display.c (do_button_press_action): factor out mouse button
+	press code, and make it adapt to dynamic configuration
+
+	* src/common.h (enum): add MetaMouseButtonAction
+
+2002-10-03  Havoc Pennington  <hp@redhat.com>
 
 	* configure.in: 2.4.2
 
Index: src/common.h
===================================================================
RCS file: /cvs/gnome/metacity/src/common.h,v
retrieving revision 1.26
diff -u -p -u -r1.26 common.h
--- src/common.h	9 Aug 2002 04:27:22 -0000	1.26
+++ src/common.h	3 Oct 2002 22:20:46 -0000
@@ -169,6 +169,21 @@ typedef enum
   META_VIRTUAL_MOD5_MASK     = 1 << 14
 } MetaVirtualModifier;
 
+typedef enum
+{
+  META_MOUSE_BUTTON_ACTION_NOTHING,
+  META_MOUSE_BUTTON_ACTION_ACTIVATE,
+  META_MOUSE_BUTTON_ACTION_FOCUS,
+  META_MOUSE_BUTTON_ACTION_MOVE,
+  META_MOUSE_BUTTON_ACTION_RESIZE,
+  META_MOUSE_BUTTON_ACTION_MENU,
+  META_MOUSE_BUTTON_ACTION_TOGGLE_MAXIMIZE,
+  META_MOUSE_BUTTON_ACTION_TOGGLE_SHADE,
+  META_MOUSE_BUTTON_ACTION_LOWER,
+  META_MOUSE_BUTTON_ACTION_RAISE,
+  META_MOUSE_BUTTON_ACTION_LAST
+} MetaMouseButtonAction;
+
 /* should investigate changing these to whatever most apps use */
 #define META_ICON_WIDTH 32
 #define META_ICON_HEIGHT 32
Index: src/display.c
===================================================================
RCS file: /cvs/gnome/metacity/src/display.c,v
retrieving revision 1.149
diff -u -p -u -r1.149 display.c
--- src/display.c	2 Oct 2002 00:47:28 -0000	1.149
+++ src/display.c	3 Oct 2002 22:20:46 -0000
@@ -936,6 +936,217 @@ window_raise_with_delay_callback (void *
   return FALSE;
 }
 
+static void
+do_button_press_action (MetaDisplay *display,
+                        MetaWindow  *window,
+                        XEvent      *event,
+                        gboolean     frame_was_receiver)
+{
+  MetaMouseButtonAction action;
+  gboolean begin_move;
+  guint grab_mask;
+  gboolean unmodified;
+  gboolean was_focus_click;
+  
+  action = META_MOUSE_BUTTON_ACTION_LAST;
+
+  grab_mask = Mod1Mask;
+  if (g_getenv ("METACITY_DEBUG_BUTTON_GRABS"))
+    grab_mask |= ControlMask;
+  
+  /* Two possible sources of an unmodified event; one is a
+   * client that's letting button presses pass through to the
+   * frame, the other is our focus_window_grab on unmodified
+   * buttons 1-3.  So for all such events we activate the window.
+   */
+  unmodified = (event->xbutton.state & grab_mask) == 0;
+
+  if (unmodified)
+    action = META_MOUSE_BUTTON_ACTION_ACTIVATE;
+  else if (event->xbutton.button == 1)
+    action = meta_prefs_get_mouse_button_action (META_MOUSE_ACTION_PREF_MODIFIED_LEFT_CLICK);
+  else if (event->xbutton.button == 2)
+    action = meta_prefs_get_mouse_button_action (META_MOUSE_ACTION_PREF_MODIFIED_MIDDLE_CLICK);
+  else if (event->xbutton.button == 3)
+    action = meta_prefs_get_mouse_button_action (META_MOUSE_ACTION_PREF_MODIFIED_RIGHT_CLICK);
+
+  meta_verbose ("Mouse button action is %d\n", action);
+  
+  begin_move = FALSE;
+  was_focus_click = FALSE;
+  
+  switch (action)
+    {
+    case META_MOUSE_BUTTON_ACTION_ACTIVATE:
+      {
+        if (!frame_was_receiver)
+          {
+            /* don't focus if frame received, will be
+             * done in frames.c if the click wasn't on
+             * the minimize/close button.
+             */
+            meta_window_raise (window);
+                  
+            meta_topic (META_DEBUG_FOCUS,
+                        "Focusing %s due to button %d press modified = %d (display.c)\n",
+                        window->desc, event->xbutton.button, !unmodified);
+            meta_window_focus (window, event->xbutton.time);
+          }
+
+        was_focus_click = TRUE;
+        
+        /* you can move on alt-click but not on
+         * the click-to-focus
+         */
+        if (!unmodified)
+          begin_move = TRUE;
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_RESIZE:
+      {
+        if (window->has_resize_func)
+          {
+            gboolean north;
+            gboolean west;
+            int root_x, root_y;
+            MetaGrabOp op;
+
+            meta_window_get_position (window, &root_x, &root_y);
+
+            west = event->xbutton.x_root < (root_x + window->rect.width / 2);
+            north = event->xbutton.y_root < (root_y + window->rect.height / 2);
+
+            if (west && north)
+              op = META_GRAB_OP_RESIZING_NW;
+            else if (west)
+              op = META_GRAB_OP_RESIZING_SW;
+            else if (north)
+              op = META_GRAB_OP_RESIZING_NE;
+            else
+              op = META_GRAB_OP_RESIZING_SE;
+                  
+            meta_display_begin_grab_op (display,
+                                        window->screen,
+                                        window,
+                                        op,
+                                        TRUE,
+                                        event->xbutton.button,
+                                        0,
+                                        event->xbutton.time,
+                                        event->xbutton.x_root,
+                                        event->xbutton.y_root);
+          }
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_MENU:
+      {
+        meta_window_show_menu (window,
+                               event->xbutton.x_root,
+                               event->xbutton.y_root,
+                               event->xbutton.button,
+                               event->xbutton.time);
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_FOCUS:
+      {
+        if (!frame_was_receiver)
+          {
+            /* don't focus if frame received, will be
+             * done in frames.c if the click wasn't on
+             * the minimize/close button.
+             */
+            meta_topic (META_DEBUG_FOCUS,
+                        "Focusing %s due to button %d press modified = %d (display.c)\n",
+                        window->desc, event->xbutton.button, !unmodified);
+            meta_window_focus (window, event->xbutton.time);
+          }
+        
+        was_focus_click = TRUE;
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_MOVE:
+      begin_move = TRUE;
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_TOGGLE_MAXIMIZE:
+      if (window->maximized)
+        meta_window_unmaximize (window);
+      else if (window->has_maximize_func)
+        meta_window_maximize (window);
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_TOGGLE_SHADE:
+      if (window->shaded)
+        meta_window_unshade (window);
+      else if (window->has_shade_func)
+        meta_window_shade (window);
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_LOWER:
+      meta_window_lower (window);
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_RAISE:
+      meta_window_raise (window);
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_NOTHING:
+    case META_MOUSE_BUTTON_ACTION_LAST:
+      /* Pass the click through if required */
+      if (!frame_was_receiver && unmodified)
+        XAllowEvents (display->xdisplay,
+                      ReplayPointer, event->xbutton.time);
+      break;
+    }
+
+  /* consider passing through the click if it was a focus click */
+  if (was_focus_click &&
+      !frame_was_receiver && unmodified)
+    {
+      /* This is from our synchronous grab since
+       * it has no modifiers and was on the client window
+       */
+      int mode;
+      
+      /* When clicking a different app in click-to-focus
+       * in application-based mode, and the different
+       * app is not a dock or desktop, eat the focus click.
+       */
+      if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK &&
+          meta_prefs_get_application_based () &&
+          !window->has_focus &&
+          window->type != META_WINDOW_DOCK &&
+          window->type != META_WINDOW_DESKTOP &&
+          (display->focus_window == NULL ||
+           !meta_window_same_application (window,
+                                          display->focus_window)))
+        mode = AsyncPointer; /* eat focus click */
+      else
+        mode = ReplayPointer; /* give event back */
+      
+      XAllowEvents (display->xdisplay,
+                    mode, event->xbutton.time);
+    }
+  
+  if (begin_move && window->has_move_func)
+    {
+      meta_display_begin_grab_op (display,
+                                  window->screen,
+                                  window,
+                                  META_GRAB_OP_MOVING,
+                                  TRUE,
+                                  event->xbutton.button,
+                                  0,
+                                  event->xbutton.time,
+                                  event->xbutton.x_root,
+                                  event->xbutton.y_root);
+    }
+}
+
 static gboolean
 event_callback (XEvent   *event,
                 gpointer  data)
@@ -1045,128 +1256,8 @@ event_callback (XEvent   *event,
         }
       else if (window && display->grab_op == META_GRAB_OP_NONE)
         {
-          gboolean begin_move = FALSE;
-          guint grab_mask;
-          gboolean unmodified;
-
-          grab_mask = Mod1Mask;
-          if (g_getenv ("METACITY_DEBUG_BUTTON_GRABS"))
-            grab_mask |= ControlMask;
-
-          /* Two possible sources of an unmodified event; one is a
-           * client that's letting button presses pass through to the
-           * frame, the other is our focus_window_grab on unmodified
-           * button 1.  So for all such events we focus the window.
-           */
-          unmodified = (event->xbutton.state & grab_mask) == 0;
-          
-          if (unmodified ||
-              event->xbutton.button == 1)
-            {
-              if (!frame_was_receiver)
-                {
-                  /* don't focus if frame received, will be
-                   * done in frames.c if the click wasn't on
-                   * the minimize/close button.
-                   */
-                  meta_window_raise (window);
-                  
-                  meta_topic (META_DEBUG_FOCUS,
-                              "Focusing %s due to unmodified button %d press (display.c)\n",
-                              window->desc, event->xbutton.button);
-                  meta_window_focus (window, event->xbutton.time);
-                }
-
-              if (!frame_was_receiver && unmodified)
-                {
-                  /* This is from our synchronous grab since
-                   * it has no modifiers and was on the client window
-                   */
-                  int mode;
-
-                  /* When clicking a different app in click-to-focus
-                   * in application-based mode, and the different
-                   * app is not a dock or desktop, eat the focus click.
-                   */
-                  if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK &&
-                      meta_prefs_get_application_based () &&
-                      !window->has_focus &&
-                      window->type != META_WINDOW_DOCK &&
-                      window->type != META_WINDOW_DESKTOP &&
-                      (display->focus_window == NULL ||
-                       !meta_window_same_application (window,
-                                                      display->focus_window)))
-                    mode = AsyncPointer; /* eat focus click */
-                  else
-                    mode = ReplayPointer; /* give event back */
-                  
-                  XAllowEvents (display->xdisplay,
-                                mode, event->xbutton.time);
-                }
-              
-              /* you can move on alt-click but not on
-               * the click-to-focus
-               */
-              if (!unmodified)
-                begin_move = TRUE;
-            }
-          else if (event->xbutton.button == 2)
-            {
-              if (window->has_resize_func)
-                {
-                  gboolean north;
-                  gboolean west;
-                  int root_x, root_y;
-                  MetaGrabOp op;
-
-                  meta_window_get_position (window, &root_x, &root_y);
-
-                  west = event->xbutton.x_root < (root_x + window->rect.width / 2);
-                  north = event->xbutton.y_root < (root_y + window->rect.height / 2);
-
-                  if (west && north)
-                    op = META_GRAB_OP_RESIZING_NW;
-                  else if (west)
-                    op = META_GRAB_OP_RESIZING_SW;
-                  else if (north)
-                    op = META_GRAB_OP_RESIZING_NE;
-                  else
-                    op = META_GRAB_OP_RESIZING_SE;
-                  
-                  meta_display_begin_grab_op (display,
-                                              window->screen,
-                                              window,
-                                              op,
-                                              TRUE,
-                                              event->xbutton.button,
-                                              0,
-                                              event->xbutton.time,
-                                              event->xbutton.x_root,
-                                              event->xbutton.y_root);
-                }
-            }
-          else if (event->xbutton.button == 3)
-            {
-              meta_window_show_menu (window,
-                                     event->xbutton.x_root,
-                                     event->xbutton.y_root,
-                                     event->xbutton.button,
-                                     event->xbutton.time);
-            }
-
-          if (begin_move && window->has_move_func)
-            {
-              meta_display_begin_grab_op (display,
-                                          window->screen,
-                                          window,
-                                          META_GRAB_OP_MOVING,
-                                          TRUE,
-                                          event->xbutton.button,
-                                          0,
-                                          event->xbutton.time,
-                                          event->xbutton.x_root,
-                                          event->xbutton.y_root);
-            }
+          do_button_press_action (display, window,
+                                  event, frame_was_receiver);
         }
       break;
     case ButtonRelease:
Index: src/frames.c
===================================================================
RCS file: /cvs/gnome/metacity/src/frames.c,v
retrieving revision 1.63
diff -u -p -u -r1.63 frames.c
--- src/frames.c	4 Sep 2002 02:21:26 -0000	1.63
+++ src/frames.c	3 Oct 2002 22:20:46 -0000
@@ -980,6 +980,198 @@ point_in_control (MetaFrames *frames,
   return control == get_control (frames, frame, x, y);
 }
 
+static void
+focus_frame (MetaFrames     *frames,
+             MetaUIFrame    *frame,
+             GdkEventButton *event,
+             gboolean        raise)
+{
+  meta_core_user_raise (gdk_display,
+                        frame->xwindow);
+  
+  meta_topic (META_DEBUG_FOCUS,
+              "Focusing window with frame 0x%lx due to button %d press state = 0x%x\n",
+              frame->xwindow, event->button, event->state);
+  
+  meta_core_user_focus (gdk_display,
+                        frame->xwindow,
+                        event->time);
+}
+
+static void
+do_button_press_action (MetaFrames           *frames,
+                        MetaUIFrame          *frame,
+                        MetaMouseButtonAction action,
+                        MetaFrameControl      control,
+                        GdkEventButton       *event,
+                        gboolean              unmodified,
+                        gboolean              grab_op_active)  
+{
+  /* don't do the rest of this if on client area */
+  if (control == META_FRAME_CONTROL_CLIENT_AREA &&
+      (action != META_MOUSE_BUTTON_ACTION_ACTIVATE &&
+       action != META_MOUSE_BUTTON_ACTION_FOCUS))
+    return; /* not on the frame, just passed through from client */
+
+  switch (action)
+    {
+    case META_MOUSE_BUTTON_ACTION_ACTIVATE:
+      {
+        if (!(control == META_FRAME_CONTROL_MINIMIZE ||
+              control == META_FRAME_CONTROL_DELETE))
+          {
+            focus_frame (frames, frame, event, TRUE);
+          }
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_RESIZE:
+      if (!grab_op_active)
+        {
+          gboolean north;
+          gboolean west;
+          int root_x, root_y;
+          MetaGrabOp op;          
+
+          focus_frame (frames, frame, event, TRUE);
+          
+          /* FIXME (implement this) */
+#if 0
+          meta_core_get_position (gdk_display,
+                                  frame->xwindow,
+                                  &root_x, &root_y);
+
+          west = event->x_root < (root_x + window->rect.width / 2);
+          north = event->y_root < (root_y + window->rect.height / 2);
+#else
+          west = FALSE;
+          north = FALSE;
+#endif
+          
+          if (west && north)
+            op = META_GRAB_OP_RESIZING_NW;
+          else if (west)
+            op = META_GRAB_OP_RESIZING_SW;
+          else if (north)
+            op = META_GRAB_OP_RESIZING_NE;
+          else
+            op = META_GRAB_OP_RESIZING_SE;
+          
+          meta_core_begin_grab_op (gdk_display,
+                                   frame->xwindow,
+                                   op,
+                                   TRUE,
+                                   event->button,
+                                   0,
+                                   event->time,
+                                   event->x_root,
+                                   event->y_root);
+        }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_MENU:
+      if (!grab_op_active)
+        {
+          focus_frame (frames, frame, event, TRUE);
+          
+          meta_core_show_window_menu (gdk_display,
+                                      frame->xwindow,
+                                      event->x_root,
+                                      event->y_root,
+                                      event->button,
+                                      event->time);
+        }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_FOCUS:
+      {
+        if (!(control == META_FRAME_CONTROL_MINIMIZE ||
+              control == META_FRAME_CONTROL_DELETE))
+          {
+            focus_frame (frames, frame, event, FALSE);
+          }
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_MOVE:
+      if (!grab_op_active)
+        {
+          MetaFrameFlags flags;
+
+          focus_frame (frames, frame, event, TRUE);
+          
+          flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+          
+          if (flags & META_FRAME_ALLOWS_MOVE)
+            {          
+              meta_core_begin_grab_op (gdk_display,
+                                       frame->xwindow,
+                                       META_GRAB_OP_MOVING,
+                                       TRUE,
+                                       event->button,
+                                       0,
+                                       event->time,
+                                       event->x_root,
+                                       event->y_root);
+            }          
+        }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_TOGGLE_MAXIMIZE:
+      {
+        MetaFrameFlags flags;
+
+        focus_frame (frames, frame, event, TRUE);
+        
+        flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+
+        if (flags & META_FRAME_MAXIMIZED)
+          meta_core_unmaximize (gdk_display,
+                             frame->xwindow);
+        else if (flags & META_FRAME_ALLOWS_MAXIMIZE)
+          meta_core_maximize (gdk_display,
+                              frame->xwindow);
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_TOGGLE_SHADE:
+      {
+        MetaFrameFlags flags;
+
+        focus_frame (frames, frame, event, TRUE);
+        
+        flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
+
+        if (flags & META_FRAME_SHADED)
+          meta_core_unshade (gdk_display,
+                             frame->xwindow);
+        else if (flags & META_FRAME_ALLOWS_SHADE)
+          meta_core_shade (gdk_display,
+                           frame->xwindow);
+      }
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_LOWER:
+      focus_frame (frames, frame, event, TRUE);
+      
+      /* FIXME */
+      /* meta_core_lower (gdk_display, frame->xwindow); */
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_RAISE:
+      focus_frame (frames, frame, event, TRUE);
+      
+      /* FIXME */
+      /* meta_core_raise (gdk_display, frame->xwindow); */
+
+      break;
+
+    case META_MOUSE_BUTTON_ACTION_NOTHING:
+    case META_MOUSE_BUTTON_ACTION_LAST:
+      break;
+    }
+}
+
 static gboolean
 meta_frames_button_press_event (GtkWidget      *widget,
                                 GdkEventButton *event)
@@ -987,6 +1179,10 @@ meta_frames_button_press_event (GtkWidge
   MetaUIFrame *frame;
   MetaFrames *frames;
   MetaFrameControl control;
+  GdkModifierType grab_mask;
+  gboolean unmodified;
+  gboolean grab_op_active;
+  MetaMouseButtonAction action;
   
   frames = META_FRAMES (widget);
 
@@ -1002,54 +1198,49 @@ meta_frames_button_press_event (GtkWidge
   
   control = get_control (frames, frame, event->x, event->y);
 
-  /* raise/focus on click, even if click was on client area */
-  if (event->button == 1 &&
-      !(control == META_FRAME_CONTROL_MINIMIZE ||
-        control == META_FRAME_CONTROL_DELETE))
-    {
-      meta_core_user_raise (gdk_display,
-                            frame->xwindow);
-      meta_topic (META_DEBUG_FOCUS,
-                  "Focusing window with frame 0x%lx due to button 1 press\n",
-                  frame->xwindow);
-      meta_core_user_focus (gdk_display,
-                            frame->xwindow,
-                            event->time);      
-    }
-
-  /* don't do the rest of this if on client area */
-  if (control == META_FRAME_CONTROL_CLIENT_AREA)
-    return FALSE; /* not on the frame, just passed through from client */
+  grab_mask = GDK_MOD1_MASK;
+  if (g_getenv ("METACITY_DEBUG_BUTTON_GRABS"))
+    grab_mask |= GDK_CONTROL_MASK;
   
-  /* We want to shade even if we have a GrabOp, since we'll have a move grab
-   * if we double click the titlebar.
+  /* Two possible sources of an unmodified event; one is a client
+   * that's letting button presses pass through to the frame, the
+   * other is our focus_window_grab on unmodified buttons 1-3.  So for
+   * all such events we activate the window.
    */
-  if (control == META_FRAME_CONTROL_TITLE &&
-      event->button == 1 &&
-      event->type == GDK_2BUTTON_PRESS)
-    {
-      MetaFrameFlags flags;
-      
-      flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
-
-      if (flags & META_FRAME_ALLOWS_SHADE)
-        {
-          if (flags & META_FRAME_SHADED)
-            meta_core_unshade (gdk_display,
-                               frame->xwindow);
-          else
-            meta_core_shade (gdk_display,
-                             frame->xwindow);
-        }
+  unmodified = (event->state & grab_mask) == 0;
 
-      return TRUE;
+  if (unmodified)
+    {
+      if (control == META_FRAME_CONTROL_TITLE &&
+          event->button == 1 &&
+          event->type == GDK_2BUTTON_PRESS)
+        action = meta_prefs_get_mouse_button_action (META_MOUSE_ACTION_PREF_DOUBLE_LEFT_CLICK_TITLE);
+      else if (event->type == GDK_2BUTTON_PRESS)
+        action = META_MOUSE_BUTTON_ACTION_LAST;
+      else if (event->button == 1)
+        action = META_MOUSE_BUTTON_ACTION_ACTIVATE;
+      else if (event->button == 2)
+        action = META_MOUSE_BUTTON_ACTION_NOTHING;
+      else if (event->button == 3)
+        action = META_MOUSE_BUTTON_ACTION_MENU;
+      else
+        action = META_MOUSE_BUTTON_ACTION_LAST;
     }
+  else if (event->type == GDK_2BUTTON_PRESS)
+    action = META_MOUSE_BUTTON_ACTION_LAST;
+  else if (event->button == 1)
+    action = meta_prefs_get_mouse_button_action (META_MOUSE_ACTION_PREF_MODIFIED_LEFT_CLICK);
+  else if (event->button == 2)
+    action = meta_prefs_get_mouse_button_action (META_MOUSE_ACTION_PREF_MODIFIED_MIDDLE_CLICK);
+  else if (event->button == 3)
+    action = meta_prefs_get_mouse_button_action (META_MOUSE_ACTION_PREF_MODIFIED_RIGHT_CLICK);
+  else
+    action = META_MOUSE_BUTTON_ACTION_LAST;
 
-  if (meta_core_get_grab_op (gdk_display) !=
-      META_GRAB_OP_NONE)
-    return FALSE; /* already up to something */  
-
-  if (event->button == 1 &&
+  grab_op_active = meta_core_get_grab_op (gdk_display) != META_GRAB_OP_NONE;
+  
+  if (!grab_op_active &&
+      event->button == 1 &&
       (control == META_FRAME_CONTROL_MAXIMIZE ||
        control == META_FRAME_CONTROL_UNMAXIMIZE ||
        control == META_FRAME_CONTROL_MINIMIZE ||
@@ -1058,6 +1249,10 @@ meta_frames_button_press_event (GtkWidge
     {
       MetaGrabOp op = META_GRAB_OP_NONE;
 
+      if (control != META_FRAME_CONTROL_MINIMIZE &&
+          control != META_FRAME_CONTROL_DELETE)
+        focus_frame (frames, frame, event, TRUE);
+      
       switch (control)
         {
         case META_FRAME_CONTROL_MINIMIZE:
@@ -1079,7 +1274,7 @@ meta_frames_button_press_event (GtkWidge
           g_assert_not_reached ();
           break;
         }
-
+      
       meta_core_begin_grab_op (gdk_display,
                                frame->xwindow,
                                op,
@@ -1114,7 +1309,8 @@ meta_frames_button_press_event (GtkWidge
                                       event->time);
         }
     }
-  else if (event->button == 1 &&
+  else if (!grab_op_active &&
+           event->button == 1 &&
            (control == META_FRAME_CONTROL_RESIZE_SE ||
             control == META_FRAME_CONTROL_RESIZE_S ||
             control == META_FRAME_CONTROL_RESIZE_SW ||
@@ -1125,6 +1321,8 @@ meta_frames_button_press_event (GtkWidge
             control == META_FRAME_CONTROL_RESIZE_W))
     {
       MetaGrabOp op;
+
+      focus_frame (frames, frame, event, TRUE);
       
       op = META_GRAB_OP_NONE;
       
@@ -1169,11 +1367,14 @@ meta_frames_button_press_event (GtkWidge
                                event->x_root,
                                event->y_root);
     }
-  else if ((control == META_FRAME_CONTROL_TITLE ||
+  else if (!grab_op_active &&
+           (control == META_FRAME_CONTROL_TITLE ||
             control == META_FRAME_CONTROL_NONE) &&
            event->button == 1)
     {
       MetaFrameFlags flags;
+
+      focus_frame (frames, frame, event, TRUE);
       
       flags = meta_core_get_frame_flags (gdk_display, frame->xwindow);
       
@@ -1190,14 +1391,10 @@ meta_frames_button_press_event (GtkWidge
                                    event->y_root);
         }
     }
-  else if (event->button == 3)
+  else
     {
-      meta_core_show_window_menu (gdk_display,
-                                  frame->xwindow,
-                                  event->x_root,
-                                  event->y_root,
-                                  event->button,
-                                  event->time);
+      do_button_press_action (frames, frame, action, control,
+                              event, unmodified, grab_op_active);
     }
   
   return TRUE;
Index: src/prefs.c
===================================================================
RCS file: /cvs/gnome/metacity/src/prefs.c,v
retrieving revision 1.23
diff -u -p -u -r1.23 prefs.c
--- src/prefs.c	27 Sep 2002 01:40:17 -0000	1.23
+++ src/prefs.c	3 Oct 2002 22:20:46 -0000
@@ -40,6 +40,11 @@
 #define KEY_APPLICATION_BASED "/apps/metacity/general/application_based"
 #define KEY_DISABLE_WORKAROUNDS "/apps/metacity/general/disable_workarounds"
 
+#define KEY_ACTION_MODIFIED_LEFT_CLICK   "/apps/metacity/general/action_modified_left_click"
+#define KEY_ACTION_MODIFIED_MIDDLE_CLICK "/apps/metacity/general/action_modified_middle_click"
+#define KEY_ACTION_MODIFIED_RIGHT_CLICK  "/apps/metacity/general/action_modified_right_click"
+#define KEY_ACTION_DOUBLE_CLICK_TITLE    "/apps/metacity/general/action_double_left_click_title"
+
 #define KEY_COMMAND_PREFIX "/apps/metacity/keybinding_commands/command_"
 #define KEY_SCREEN_BINDINGS_PREFIX "/apps/metacity/global_keybindings"
 #define KEY_WINDOW_BINDINGS_PREFIX "/apps/metacity/window_keybindings"
@@ -57,10 +62,20 @@ static gboolean application_based = FALS
 static gboolean disable_workarounds = FALSE;
 static gboolean auto_raise = FALSE;
 static gboolean auto_raise_delay = 500;
+
+static MetaMouseButtonAction mouse_actions[META_MOUSE_ACTION_PREF_LAST] = {
+    META_MOUSE_BUTTON_ACTION_MOVE,           /* modified left click */
+    META_MOUSE_BUTTON_ACTION_RESIZE,         /* modified middle click */
+    META_MOUSE_BUTTON_ACTION_MENU,           /* modified right click */
+    META_MOUSE_BUTTON_ACTION_TOGGLE_MAXIMIZE /* double click titlebar */
+};
+
 #define NUM_COMMANDS 12
 static char *commands[NUM_COMMANDS] = { NULL, NULL, NULL, NULL, NULL, NULL,
                                         NULL, NULL, NULL, NULL, NULL, NULL };
 
+static MetaMouseButtonAction button_action_from_string (const char *str);
+
 static gboolean update_use_system_font   (gboolean    value);
 static gboolean update_titlebar_font      (const char *value);
 static gboolean update_focus_mode         (const char *value);
@@ -74,6 +89,8 @@ static gboolean update_window_binding   
                                            const char *value);
 static gboolean update_screen_binding     (const char *name,
                                            const char *value);
+static gboolean update_action             (const char *value,
+                                           MetaMouseActionPreference pref);
 static void     init_bindings             (void);
 static gboolean update_binding            (MetaKeyPref *binding,
                                            const char  *value);
@@ -635,6 +652,10 @@ update_titlebar_font (const char *value)
                       value, KEY_TITLEBAR_FONT);
     }
 
+  /* FIXME validate size and other features of new_desc to be sure
+   * it isn't wacky
+   */
+  
   if (new_desc && titlebar_font &&
       pango_font_description_equal (new_desc, titlebar_font))
     {
@@ -762,6 +783,35 @@ update_auto_raise_delay (int value)
   return old != auto_raise_delay;
 }
 
+static gboolean
+update_action (const char               *value,
+               MetaMouseActionPreference pref)
+{
+  MetaMouseButtonAction action;
+  MetaMouseButtonAction old;
+  
+  action = META_MOUSE_BUTTON_ACTION_LAST;
+  if (value != NULL)
+    {
+      action = button_action_from_string (value);
+
+      if (action == META_MOUSE_BUTTON_ACTION_LAST)
+        meta_verbose ("Did not understand button action: %s\n",
+                      value);
+    }
+  
+  old = mouse_actions[pref];
+  mouse_actions[pref] = action;
+
+  return old != action;
+}
+
+MetaMouseButtonAction
+meta_prefs_get_mouse_button_action (MetaMouseActionPreference pref)
+{
+  return mouse_actions[pref];
+}
+
 const char*
 meta_preference_to_string (MetaPreference pref)
 {
@@ -799,6 +849,9 @@ meta_preference_to_string (MetaPreferenc
 
     case META_PREF_COMMANDS:
       return "COMMANDS";
+
+    case META_PREF_MOUSE_ACTIONS:
+      return "MOUSE_ACTIONS";
     }
 
   return "(unknown)";
@@ -1209,4 +1262,65 @@ meta_prefs_get_window_binding (const cha
     }
 
   g_assert_not_reached ();
+}
+
+static MetaMouseButtonAction
+get_action (const char *str)
+{
+  if (strcmp (str, "nothing") == 0)
+    return META_MOUSE_BUTTON_ACTION_NOTHING;
+  else if (strcmp (str, "activate") == 0)
+    return META_MOUSE_BUTTON_ACTION_ACTIVATE;
+  else if (strcmp (str, "focus") == 0)
+    return META_MOUSE_BUTTON_ACTION_FOCUS;
+  else if (strcmp (str, "move") == 0)
+    return META_MOUSE_BUTTON_ACTION_MOVE;
+  else if (strcmp (str, "resize") == 0)
+    return META_MOUSE_BUTTON_ACTION_RESIZE;
+  else if (strcmp (str, "menu") == 0)
+    return META_MOUSE_BUTTON_ACTION_MENU;
+  else if (strcmp (str, "toggle_maximize") == 0)
+    return META_MOUSE_BUTTON_ACTION_TOGGLE_MAXIMIZE;
+  else if (strcmp (str, "toggle_shade") == 0)
+    return META_MOUSE_BUTTON_ACTION_TOGGLE_SHADE;
+  else if (strcmp (str, "lower") == 0)
+    return META_MOUSE_BUTTON_ACTION_LOWER;
+  else if (strcmp (str, "raise") == 0)
+    return META_MOUSE_BUTTON_ACTION_RAISE;
+  else
+    return META_MOUSE_BUTTON_ACTION_LAST;
+}
+
+static MetaMouseButtonAction
+button_action_from_string (const char *str)
+{
+  if (strchr (str, ',') != NULL) /* optimize the only-one-action case */
+    {
+      /* We need to parse each action in the comma-separated list and
+       * use the first one we understand. Allows configuring defaults
+       * for both old and new metacity versions at the same time.
+       */
+      char **actions;
+      int i;
+      MetaMouseButtonAction action;
+
+      action = META_MOUSE_BUTTON_ACTION_LAST;
+      actions = g_strsplit (str, ",", -1);
+      i = 0;
+      while (actions[i] && action == META_MOUSE_BUTTON_ACTION_LAST)
+        {
+          action = get_action (actions[i]);
+          
+          ++i;
+        }
+
+      meta_verbose ("Ignored %d actions in the action list \"%s\"\n",
+                    i - 1, str);
+      
+      g_strfreev (actions);
+
+      return action;
+    }
+  else
+    return get_action (str);
 }
Index: src/prefs.h
===================================================================
RCS file: /cvs/gnome/metacity/src/prefs.h,v
retrieving revision 1.19
diff -u -p -u -r1.19 prefs.h
--- src/prefs.h	27 Sep 2002 01:40:17 -0000	1.19
+++ src/prefs.h	3 Oct 2002 22:20:46 -0000
@@ -38,9 +38,19 @@ typedef enum
   META_PREF_WINDOW_KEYBINDINGS,
   META_PREF_SCREEN_KEYBINDINGS,
   META_PREF_DISABLE_WORKAROUNDS,
-  META_PREF_COMMANDS
+  META_PREF_COMMANDS,
+  META_PREF_MOUSE_ACTIONS
 } MetaPreference;
 
+typedef enum
+{
+  META_MOUSE_ACTION_PREF_MODIFIED_LEFT_CLICK,
+  META_MOUSE_ACTION_PREF_MODIFIED_MIDDLE_CLICK,
+  META_MOUSE_ACTION_PREF_MODIFIED_RIGHT_CLICK,
+  META_MOUSE_ACTION_PREF_DOUBLE_LEFT_CLICK_TITLE,
+  META_MOUSE_ACTION_PREF_LAST
+} MetaMouseActionPreference;
+
 typedef void (* MetaPrefsChangedFunc) (MetaPreference pref,
                                        gpointer       data);
 
@@ -63,6 +73,9 @@ gboolean                    meta_prefs_g
 int                         meta_prefs_get_auto_raise_delay   (void);
 
 const char*                 meta_prefs_get_command            (int i);
+
+MetaMouseButtonAction       meta_prefs_get_mouse_button_action (MetaMouseActionPreference pref);
+
 
 char*                       meta_prefs_get_gconf_key_for_command (int i);
 
