From da4f9dacfab93baf4ba5b956baa3622bd4b018b1 Mon Sep 17 00:00:00 2001 From: Alexis Deveria Date: Mon, 16 Aug 2010 20:26:06 +0000 Subject: [PATCH] Added experimental context menu (currently only for deleting elements) git-svn-id: http://svg-edit.googlecode.com/svn/trunk@1663 eee81c28-f429-11dd-99c0-75d572ba1ddd --- editor/contextmenu/jquery.contextMenu.js | 198 +++++++++++++++++++ editor/contextmenu/jquery.contextMenu.min.js | 22 +++ editor/svg-editor.css | 63 ++++++ editor/svg-editor.html | 8 + editor/svg-editor.js | 27 ++- editor/svgcanvas.js | 14 +- 6 files changed, 327 insertions(+), 5 deletions(-) create mode 100755 editor/contextmenu/jquery.contextMenu.js create mode 100644 editor/contextmenu/jquery.contextMenu.min.js diff --git a/editor/contextmenu/jquery.contextMenu.js b/editor/contextmenu/jquery.contextMenu.js new file mode 100755 index 00000000..4883797b --- /dev/null +++ b/editor/contextmenu/jquery.contextMenu.js @@ -0,0 +1,198 @@ +// jQuery Context Menu Plugin +// +// Version 1.01 +// +// Cory S.N. LaViska +// A Beautiful Site (http://abeautifulsite.net/) +// Modified by Alexis Deveria +// +// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/ +// +// Terms of Use +// +// This plugin is dual-licensed under the GNU General Public License +// and the MIT License and is copyright A Beautiful Site, LLC. +// +if(jQuery)( function() { + $.extend($.fn, { + + contextMenu: function(o, callback) { + // Defaults + if( o.menu == undefined ) return false; + if( o.inSpeed == undefined ) o.inSpeed = 150; + if( o.outSpeed == undefined ) o.outSpeed = 75; + // 0 needs to be -1 for expected results (no fade) + if( o.inSpeed == 0 ) o.inSpeed = -1; + if( o.outSpeed == 0 ) o.outSpeed = -1; + // Loop each context menu + $(this).each( function() { + var el = $(this); + var offset = $(el).offset(); + // Add contextMenu class + $('#' + o.menu).addClass('contextMenu'); + // Simulate a true right click + $(this).mousedown( function(e) { + var evt = e; + $(this).mouseup( function(e) { + var srcElement = $(this); + $(this).unbind('mouseup'); + if( evt.button == 2 ) { + e.stopPropagation(); + // Hide context menus that may be showing + $(".contextMenu").hide(); + // Get this context menu + var menu = $('#' + o.menu); + + if( $(el).hasClass('disabled') ) return false; + + // Detect mouse position + var d = {}, x = e.pageX, y = e.pageY; + + var x_off = $(window).width() - menu.width(), + y_off = $(window).height() - menu.height(); + + if(x > x_off) x = x_off-5; + if(y > y_off) y = y_off-5; + + // Show the menu + $(document).unbind('click'); + $(menu).css({ top: y, left: x }).fadeIn(o.inSpeed); + // Hover events + $(menu).find('A').mouseover( function() { + $(menu).find('LI.hover').removeClass('hover'); + $(this).parent().addClass('hover'); + }).mouseout( function() { + $(menu).find('LI.hover').removeClass('hover'); + }); + + // Keyboard + $(document).keypress( function(e) { + switch( e.keyCode ) { + case 38: // up + if( $(menu).find('LI.hover').size() == 0 ) { + $(menu).find('LI:last').addClass('hover'); + } else { + $(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover'); + if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:last').addClass('hover'); + } + break; + case 40: // down + if( $(menu).find('LI.hover').size() == 0 ) { + $(menu).find('LI:first').addClass('hover'); + } else { + $(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover'); + if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:first').addClass('hover'); + } + break; + case 13: // enter + $(menu).find('LI.hover A').trigger('click'); + break; + case 27: // esc + $(document).trigger('click'); + break + } + }); + + // When items are selected + $('#' + o.menu).find('A').unbind('click'); + $('#' + o.menu).find('LI:not(.disabled) A').click( function() { + $(document).unbind('click').unbind('keypress'); + $(".contextMenu").hide(); + // Callback + if( callback ) callback( $(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y} ); + return false; + }); + + // Hide bindings + setTimeout( function() { // Delay for Mozilla + $(document).click( function() { + $(document).unbind('click').unbind('keypress'); + $(menu).fadeOut(o.outSpeed); + return false; + }); + }, 0); + } + }); + }); + + // Disable text selection + if( $.browser.mozilla ) { + $('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); }); + } else if( $.browser.msie ) { + $('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); }); + } else { + $('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); }); + } + // Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome) + $(el).add($('UL.contextMenu')).bind('contextmenu', function() { return false; }); + + }); + return $(this); + }, + + // Disable context menu items on the fly + disableContextMenuItems: function(o) { + if( o == undefined ) { + // Disable all + $(this).find('LI').addClass('disabled'); + return( $(this) ); + } + $(this).each( function() { + if( o != undefined ) { + var d = o.split(','); + for( var i = 0; i < d.length; i++ ) { + $(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled'); + + } + } + }); + return( $(this) ); + }, + + // Enable context menu items on the fly + enableContextMenuItems: function(o) { + if( o == undefined ) { + // Enable all + $(this).find('LI.disabled').removeClass('disabled'); + return( $(this) ); + } + $(this).each( function() { + if( o != undefined ) { + var d = o.split(','); + for( var i = 0; i < d.length; i++ ) { + $(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled'); + + } + } + }); + return( $(this) ); + }, + + // Disable context menu(s) + disableContextMenu: function() { + $(this).each( function() { + $(this).addClass('disabled'); + }); + return( $(this) ); + }, + + // Enable context menu(s) + enableContextMenu: function() { + $(this).each( function() { + $(this).removeClass('disabled'); + }); + return( $(this) ); + }, + + // Destroy context menu(s) + destroyContextMenu: function() { + // Destroy specified context menus + $(this).each( function() { + // Disable action + $(this).unbind('mousedown').unbind('mouseup'); + }); + return( $(this) ); + } + + }); +})(jQuery); \ No newline at end of file diff --git a/editor/contextmenu/jquery.contextMenu.min.js b/editor/contextmenu/jquery.contextMenu.min.js new file mode 100644 index 00000000..2517baf5 --- /dev/null +++ b/editor/contextmenu/jquery.contextMenu.min.js @@ -0,0 +1,22 @@ +// jQuery Context Menu Plugin +// +// Version 1.01 +// +// Cory S.N. LaViska +// A Beautiful Site (http://abeautifulsite.net/) +// +// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/ +// +// Terms of Use +// +// This plugin is dual-licensed under the GNU General Public License +// and the MIT License and is copyright A Beautiful Site, LLC. +// +jQuery&&function(){$.extend($.fn,{contextMenu:function(a,e){if(a.menu==undefined)return false;if(a.inSpeed==undefined)a.inSpeed=150;if(a.outSpeed==undefined)a.outSpeed=75;if(a.inSpeed==0)a.inSpeed=-1;if(a.outSpeed==0)a.outSpeed=-1;$(this).each(function(){var d=$(this),i=$(d).offset();$("#"+a.menu).addClass("contextMenu");$(this).mousedown(function(j){j.stopPropagation();$(this).mouseup(function(f){f.stopPropagation();var k=$(this);$(this).unbind("mouseup");if(j.button==2){$(".contextMenu").hide(); +var b=$("#"+a.menu);if($(d).hasClass("disabled"))return false;var c={},g,h;if(self.innerHeight){c.pageYOffset=self.pageYOffset;c.pageXOffset=self.pageXOffset;c.innerHeight=self.innerHeight;c.innerWidth=self.innerWidth}else if(document.documentElement&&document.documentElement.clientHeight){c.pageYOffset=document.documentElement.scrollTop;c.pageXOffset=document.documentElement.scrollLeft;c.innerHeight=document.documentElement.clientHeight;c.innerWidth=document.documentElement.clientWidth}else if(document.body){c.pageYOffset= +document.body.scrollTop;c.pageXOffset=document.body.scrollLeft;c.innerHeight=document.body.clientHeight;c.innerWidth=document.body.clientWidth}f.pageX?g=f.pageX:g=f.clientX+c.scrollLeft;f.pageY?h=f.pageY:h=f.clientY+c.scrollTop;$(document).unbind("click");$(b).css({top:h,left:g}).fadeIn(a.inSpeed);$(b).find("A").mouseover(function(){$(b).find("LI.hover").removeClass("hover");$(this).parent().addClass("hover")}).mouseout(function(){$(b).find("LI.hover").removeClass("hover")});$(document).keypress(function(l){switch(l.keyCode){case 38:if($(b).find("LI.hover").size()== +0)$(b).find("LI:last").addClass("hover");else{$(b).find("LI.hover").removeClass("hover").prevAll("LI:not(.disabled)").eq(0).addClass("hover");$(b).find("LI.hover").size()==0&&$(b).find("LI:last").addClass("hover")}break;case 40:if($(b).find("LI.hover").size()==0)$(b).find("LI:first").addClass("hover");else{$(b).find("LI.hover").removeClass("hover").nextAll("LI:not(.disabled)").eq(0).addClass("hover");$(b).find("LI.hover").size()==0&&$(b).find("LI:first").addClass("hover")}break;case 13:$(b).find("LI.hover A").trigger("click"); +break;case 27:$(document).trigger("click");break}});$("#"+a.menu).find("A").unbind("click");$("#"+a.menu).find("LI:not(.disabled) A").click(function(){$(document).unbind("click").unbind("keypress");$(".contextMenu").hide();e&&e($(this).attr("href").substr(1),$(k),{x:g-i.left,y:h-i.top,docX:g,docY:h});return false});setTimeout(function(){$(document).click(function(){$(document).unbind("click").unbind("keypress");$(b).fadeOut(a.outSpeed);return false})},0)}})});if($.browser.mozilla)$("#"+a.menu).each(function(){$(this).css({MozUserSelect:"none"})}); +else $.browser.msie?$("#"+a.menu).each(function(){$(this).bind("selectstart.disableTextSelect",function(){return false})}):$("#"+a.menu).each(function(){$(this).bind("mousedown.disableTextSelect",function(){return false})});$(d).add($("UL.contextMenu")).bind("contextmenu",function(){return false})});return $(this)},disableContextMenuItems:function(a){if(a==undefined){$(this).find("LI").addClass("disabled");return $(this)}$(this).each(function(){if(a!=undefined)for(var e=a.split(","),d=0;d + @@ -665,5 +666,12 @@ script type="text/javascript" src="locale/locale.min.js"> + + diff --git a/editor/svg-editor.js b/editor/svg-editor.js index 1e154ec7..dfc56488 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -1472,6 +1472,8 @@ } // if (elem != null) else if (multiselected) { $('#multiselected_panel').show(); + } else { + $('#cmenu_canvas li').disableContextMenu(); } // update history buttons @@ -1493,6 +1495,9 @@ if ( (elem && !is_node) || multiselected) { // update the selected elements' layer $('#selLayerNames').removeAttr('disabled').val(currentLayer); + + // Enable regular menu options + $('#cmenu_canvas').enableContextMenuItems('#delete'); } else { $('#selLayerNames').attr('disabled', 'disabled'); @@ -1847,8 +1852,11 @@ } on_button = false; }).mousedown(function(evt) { - var islib = $(evt.target).closest('div.tools_flyout').length; - if(!islib) $('.tools_flyout:visible').fadeOut(); +// $(".contextMenu").hide(); +// console.log('cm', $(evt.target).closest('.contextMenu')); + + var islib = $(evt.target).closest('div.tools_flyout, .contextMenu').length; + if(!islib) $('.tools_flyout:visible,.contextMenu').fadeOut(250); }); overlay.bind('mousedown',function() { @@ -3703,6 +3711,21 @@ $('#blur').SpinButton({ step: .1, min: 0, max: 10, callback: changeBlur }); $('#zoom').SpinButton({ min: 0.001, max: 10000, step: 50, stepfunc: stepZoom, callback: changeZoom }); + $("#workarea").contextMenu({ + menu: 'cmenu_canvas', + inSpeed: 0 + }, + function(action, el, pos) { + switch ( action ) { + case 'delete': + deleteSelected(); + break; + } + }); + + $('#cmenu_canvas li').disableContextMenu(); + $('#cmenu_canvas').enableContextMenuItems('#delete'); + window.onbeforeunload = function() { // Suppress warning if page is empty if(undoMgr.getUndoStackSize() === 0) { diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index aecbcdf3..14269fdb 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -4200,7 +4200,14 @@ var getMouseTarget = this.getMouseTarget = function(evt) { // and do nothing else var mouseDown = function(evt) { - if(evt.button === 1 || canvas.spaceKey) return; + console.log(evt.button); + if(canvas.spaceKey) return; + + var right_click = evt.button === 2; + if(right_click) { + current_mode = "select"; + } + root_sctm = svgcontent.getScreenCTM().inverse(); var pt = transformPoint( evt.pageX, evt.pageY, root_sctm ), mouse_x = pt.x * current_zoom, @@ -4242,6 +4249,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { case "select": started = true; current_resize_mode = "none"; + if(right_click) started = false; if (mouse_target != svgroot) { // if this element is not yet selected, clear selection and select it @@ -4266,7 +4274,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { slist.insertItemBefore(svgroot.createSVGTransform(), 0); } } - else { + else if(!right_click){ clearSelection(); current_mode = "multiselect"; if (rubberBox == null) { @@ -4849,7 +4857,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { // this is done in when we recalculate the selected dimensions() var mouseUp = function(evt) { - if(evt.button === 1) return; + if(evt.button === 2) return; var tempJustSelected = justSelected; justSelected = null; if (!started) return;