Showing posts with label openlayers. Show all posts
Showing posts with label openlayers. Show all posts

2011-01-13

Creating a global control in OpenLayers for specific key combinations

This is largely a follow-up to a previous post I made discussing how to effectively capture right clicks on an OpenLayers map object.  That had been related to another objective that I had, which was to have a global control that always responds to the right click in order to display a basic context menu on the map.  It's easy enough to get this working on the right-click, with all other controls working using the left click event - but what about Mac users, or people suffering from some kind of right-click impairment?  This needs a modifier key to go along with the left-click, but then there needs to be some way to prevent other controls from being triggered at the same time.

In OpenLayers, there is a common property that can be passed to handlers/controls, called keyMask.  It is used in a bitwise comparison in the checkModifiers method of the OpenLayers.Handler class.  The main issue I had was that it currently only works to allow a given control to be restricted to a specified key combination.  It also does not allow you to have a single control that responds to different keys.  Rather, any event keys added to the keyMask property of a control/handler must ALL be present in order for checkModifers to return true.  However, here's the situation I wanted:

  1. Global control (context menu): triggers on right click, or left-click + alt, or left-click + ctrl (this allows users with a one-button mouse to still access the menu), and
  2. All other controls: trigger on any left-click event that excludes the alt or ctrl keys.
So to accomplish what I needed, I added a bit of code the checkModifiers function in the OpenLayers.Handler class to make it also check for a keyIMask property (i.e., an inverse key mask...though maybe that's not the best name for it).  Where the current keyMask must match all keys included in the event in order for checkModifiers function to return true, if the keyIMask matches any keys included in the event, then checkModifiers returns false.  Here's what it looks like:

checkModifiers: function (evt) {
if(this.keyMask == null && this.keyIMask == null) {
return true;
}
/* calculate the keyboard modifier mask for this event */
var keyModifiers =
(evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
(evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
(evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0);
/* if it differs from the handler object's key mask,
bail out of the event handler */
return ((this.keyMask == null || keyModifiers == this.keyMask)
&& (this.keyIMask == null || (keyModifiers & this.keyIMask) == 0));
}

With the Handler modified in this way, I can tell any control to not respond if any key in an event matches keyIMask.  In my global control, it internally checks if the event is either right-click, or if it's a left-click with either the ctrlKey or altKey present.  All of my other controls are set with the property "keyIMask = OpenLayers.Handler.MOD_CTRL | OpenLayers.Handler.MOD_ALT;", which prevents controls from being triggered when the menu is displayed, and still leaves room for MOD_SHIFT to be used either as a keyMask, or to internally modify the behaviour of control's events (e.g., the freehand toggle in the OpenLayers.Handler.Path class).

2010-11-21

Capturing right-click events in OpenLayers

I've been looking at using OpenLayers as the mapping API for an application I've been working on for some time.  One of the key features of the application requires capturing mouse click events on the map.  This is fairly normal with OpenLayers for many of its controls, but when the right-click event is involved, I would always get the default context menu from the browser, and OpenLayers didn't seem to capture the rightclick event on its own.  There are a few workarounds posted on the web...but I found they were not easily adaptable, or didn't quite work the way I wanted.  So here's what I came up with - paste the code below at the end of the init() function in any of OpenLayers' examples:


===================================================

// Get control of the right-click event:
document.getElementById('map').oncontextmenu = function(e){
 e = e?e:window.event;
 if (e.preventDefault) e.preventDefault(); // For non-IE browsers.
 else return false; // For IE browsers.
};


// A control class for capturing click events...
OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {                

defaultHandlerOptions: {
'single': true,
'double': true,
'pixelTolerance': 0,
'stopSingle': false,
'stopDouble': false
},
handleRightClicks:true,
initialize: function(options) {
this.handlerOptions = OpenLayers.Util.extend(
{}, this.defaultHandlerOptions
);
OpenLayers.Control.prototype.initialize.apply(
this, arguments
); 
this.handler = new OpenLayers.Handler.Click(
this, this.eventMethods, this.handlerOptions
);
},
CLASS_NAME: "OpenLayers.Control.Click"

});


// Add an instance of the Click control that listens to various click events:
var oClick = new OpenLayers.Control.Click({eventMethods:{
'rightclick': function(e) {
alert('rightclick at '+e.xy.x+','+e.xy.y);
},
'click': function(e) {
alert('click at '+e.xy.x+','+e.xy.y);
},
'dblclick': function(e) {
alert('dblclick at '+e.xy.x+','+e.xy.y);
},
'dblrightclick': function(e) {
alert('dblrightclick at '+e.xy.x+','+e.xy.y);
}
}});
map.addControl(oClick);
oClick.activate();

===================================================



The first bit overrides the oncontextmenu event that causes the browser to display a context menu when the right-click event happens.  The next bit creates a simple control class for handling the click events, with the handleRightClicks option enabled.  After that, an instance of the control class is created, with functions for the four different click event types included in the eventMethods option.  Finally, this control is added to the map and activated...any better solutions are welcome.


EDIT: This has been corrected to utilize the 'handleRightClicks' property in the OpenLayers class. Other than this, all that is essentially needed is the overridden oncontextmenu function, which uses preventDefault (for non-IE browsers), or returns false (for IE browsers).