Index: source/gui/scripting/ScriptFunctions.cpp
===================================================================
--- source/gui/scripting/ScriptFunctions.cpp	(revision 8539)
+++ source/gui/scripting/ScriptFunctions.cpp	(working copy)
@@ -128,6 +128,11 @@
 	return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player);
 }
 
+std::vector<entity_id_t> PickSimilarFriendlyEntities(void* UNUSED(cbdata), std::string templateName, bool onScreenOnly)
+{
+	return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetPlayerID(), onScreenOnly);
+}
+
 CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y)
 {
 	CVector3D pos = g_Game->GetView()->GetCamera()->GetWorldCoordinates(x, y, true);
@@ -374,6 +379,7 @@
 	// Entity picking
 	scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, &PickEntitiesAtPoint>("PickEntitiesAtPoint");
 	scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect");
+	scriptInterface.RegisterFunction<std::vector<entity_id_t>, std::string, bool, &PickSimilarFriendlyEntities>("PickSimilarFriendlyEntities");
 	scriptInterface.RegisterFunction<CFixedVector3D, int, int, &GetTerrainAtPoint>("GetTerrainAtPoint");
 
 	// Network / game setup functions
Index: source/simulation2/helpers/Selection.cpp
===================================================================
--- source/simulation2/helpers/Selection.cpp	(revision 8539)
+++ source/simulation2/helpers/Selection.cpp	(working copy)
@@ -23,6 +23,7 @@
 #include "simulation2/Simulation2.h"
 #include "simulation2/components/ICmpOwnership.h"
 #include "simulation2/components/ICmpRangeManager.h"
+#include "simulation2/components/ICmpTemplateManager.h"
 #include "simulation2/components/ICmpSelectable.h"
 #include "simulation2/components/ICmpVisual.h"
 
@@ -130,3 +131,44 @@
 
 	return hitEnts;
 }
+
+std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, std::string templateName, int owner, bool onScreenOnly)
+{
+	CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY);
+	CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
+
+	std::vector<entity_id_t> hitEnts;
+
+ 	std::vector<entity_id_t> entities = cmpTemplateManager->GetEntitiesUsingTemplate(templateName);
+ 	for (std::vector<entity_id_t>::iterator it = entities.begin(); it != entities.end(); ++it)
+ 	{
+ 		entity_id_t ent = *it;
+ 		// Ignore entities not owned by 'owner'
+ 		CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent);
+ 		if (cmpOwnership.null() || cmpOwnership->GetOwner() != owner)
+ 			continue;
+
+ 		if (onScreenOnly)
+ 		{
+ 			// Find the current interpolated model position.
+			// (We just use the centre position and not the whole bounding box, because maybe
+			// that's better for users trying to select objects in busy areas)
+			CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
+			if (cmpVisual.null())
+				continue;
+			CVector3D position = cmpVisual->GetPosition();
+
+			// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
+			// In this case, the checking is done to avoid selecting garrisoned units
+			if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN)
+				continue;
+
+			// Reject if it's not on-screen (e.g. it's behind the camera)
+			if (!camera.GetFrustum().IsPointVisible(position))
+				continue;
+ 		}
+
+ 		hitEnts.push_back(ent);
+ 	}
+ 	return hitEnts;
+}
Index: source/simulation2/helpers/Selection.h
===================================================================
--- source/simulation2/helpers/Selection.h	(revision 8539)
+++ source/simulation2/helpers/Selection.h	(working copy)
@@ -47,6 +47,13 @@
  */
 std::vector<entity_id_t> PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner);
 
+/**
+ * Finds a selectable entity under the given screen coordinate
+ * and returns all units with the same visualActors short name,
+ * when onScreenOnly is set, only Units on screen are returned.
+ */
+std::vector<entity_id_t> PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, std::string templateName, int owner, bool onScreenOnly);
+
 } // namespace
 
 #endif // INCLUDED_SELECTION
Index: source/simulation2/components/ICmpTemplateManager.h
===================================================================
--- source/simulation2/components/ICmpTemplateManager.h	(revision 8539)
+++ source/simulation2/components/ICmpTemplateManager.h	(working copy)
@@ -80,6 +80,10 @@
 	virtual std::string GetCurrentTemplateName(entity_id_t ent) = 0;
 
 	/**
+	 * Returns the list of entities having the specified template
+	 */
+	virtual std::vector<entity_id_t> GetEntitiesUsingTemplate(std::string templateName) = 0;
+	/**
 	 * Returns a list of strings that could be validly passed as @c templateName to LoadTemplate.
 	 * (This includes "actor|foo" etc names).
 	 * Intended for use by the map editor. This is likely to be quite slow.
Index: source/simulation2/components/CCmpTemplateManager.cpp
===================================================================
--- source/simulation2/components/CCmpTemplateManager.cpp	(revision 8539)
+++ source/simulation2/components/CCmpTemplateManager.cpp	(working copy)
@@ -131,6 +131,8 @@
 
 	virtual std::vector<std::wstring> FindAllTemplates();
 
+	virtual std::vector<entity_id_t> GetEntitiesUsingTemplate(std::string templateName);
+
 private:
 	// Entity template XML validator
 	RelaxNGValidator m_Validator;
@@ -405,6 +407,20 @@
 	return templates;
 }
 
+/**
+ * Get the list of entities using the specified template
+ */
+std::vector<entity_id_t> CCmpTemplateManager::GetEntitiesUsingTemplate(std::string templateName)
+{
+	std::vector<entity_id_t> entities;
+	for (std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
+	{
+		if(it->second == templateName)
+			entities.push_back(it->first);
+	}
+	return entities;
+}
+
 void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse)
 {
 	// We only want to include components which are necessary (for the visual previewing of an entity)
Index: source/simulation2/components/ICmpTemplateManager.cpp
===================================================================
--- source/simulation2/components/ICmpTemplateManager.cpp	(revision 8539)
+++ source/simulation2/components/ICmpTemplateManager.cpp	(working copy)
@@ -25,4 +25,5 @@
 DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::string)
 DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::string, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t)
 DEFINE_INTERFACE_METHOD_0("FindAllTemplates", std::vector<std::wstring>, ICmpTemplateManager, FindAllTemplates)
+DEFINE_INTERFACE_METHOD_1("GetEntitiesUsingTemplate", std::vector<entity_id_t>, ICmpTemplateManager, GetEntitiesUsingTemplate, std::string)
 END_INTERFACE_WRAPPER(TemplateManager)
Index: binaries/data/mods/public/gui/session_new/input.js
===================================================================
--- binaries/data/mods/public/gui/session_new/input.js	(revision 8539)
+++ binaries/data/mods/public/gui/session_new/input.js	(working copy)
@@ -38,6 +38,15 @@
 specialKeyStates[SDLK_RALT] = 0;
 specialKeyStates[SDLK_LALT] = 0;
 
+//Number of pixels the mouse can move before the action is considered a drag
+var maxDragDelta = 4;
+
+//Time in milliseconds in which a double click is recognized 
+const doubleClickTime = 500;  
+var doubleClickTimer = 0;
+var doubleClicked = false;
+//Store the previously clicked entity - ensure a double/triple click happens on the same entity
+var prevClickedEntity = 0;
 // (TODO: maybe we should fix the hotkey system to be usable in this situation,
 // rather than hardcoding Shift into this code?)
 
@@ -125,7 +134,6 @@
 			var playerOwned = ((targetState.player == entState.player)? true : false);
 			var enemyOwned = ((targetState.player != entState.player)? true : false);
 			var gaiaOwned = ((targetState.player == 0)? true : false);
-
 			
 			if (targetState.garrisonHolder && playerOwned && ctrlPressed)
 			{
@@ -571,7 +579,7 @@
 			// If the mouse moved further than a limit, switch to bandbox mode
 			var dragDeltaX = ev.x - dragStart[0];
 			var dragDeltaY = ev.y - dragStart[1];
-			var maxDragDelta = 4;
+			
 			if (Math.abs(dragDeltaX) >= maxDragDelta || Math.abs(dragDeltaY) >= maxDragDelta)
 			{
 				inputState = INPUT_BANDBOXING;
@@ -586,19 +594,69 @@
 			if (ev.button == SDL_BUTTON_LEFT)
 			{
 				var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y);
+				var selectedEntity;
+				var onScreenOnly;
+				
+				var now = new Date();
 				if (!ents.length)
 				{
 					g_Selection.reset();
 					inputState = INPUT_NORMAL;
 					return true;
 				}
+				else
+				{
+					selectedEntity = ents[0];
+				}
+				if ((now.getTime() - doubleClickTimer < doubleClickTime) && (selectedEntity == prevClickedEntity))
+				{
+					//Double click or triple click has occured
+					
+					//Check for double click or triple click
+					if (!doubleClicked)
+					{
+						//If double click hasn't already occured
+						//It's a double click
+						//Select only similar on-screen units
+						onScreenOnly = true;
+						doubleClicked = true;
+					}
+					else
+					{
+						//Double click has already occured
+						//This is a triple click
+						//Select all similar units whether they are on-screen or not
+						onScreenOnly = false;
+					}
+					
+					var templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
+					
+					ents = Engine.PickSimilarFriendlyEntities(templateToMatch, onScreenOnly);
+				}
+				else
+				{
+					//It's single click right now but it may become double or triple click
+					doubleClicked = false;
+					doubleClickTimer = now.getTime();
+					prevClickedEntity = selectedEntity;
+				}
 
 				// If shift is pressed, don't reset the selection, but allow units to be added to the existing selection
 				var addition = (specialKeyStates[SDLK_RSHIFT] || specialKeyStates[SDLK_LSHIFT]); 
 				if (!addition)
-					g_Selection.reset(); 
-
-				g_Selection.addList([ents[0]]);
+					g_Selection.reset();
+				if (doubleClicked)
+				{
+					//Either double clicked or triple clicked
+					//So we add all picked units to the selection
+					g_Selection.addList(ents);
+				}
+				else
+				{
+					//We add only the first picked unit to the selection
+					g_Selection.addList([ents[0]]);
+				}
+				
 				inputState = INPUT_NORMAL;
 				return true;
 			}
Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/GarrisonHolder.js	(revision 8539)
+++ binaries/data/mods/public/simulation/components/GarrisonHolder.js	(working copy)
@@ -56,33 +56,44 @@
 };
 
 /**
- * Garrison a unit inside.
- * Returns true if successful, false if not
- * The timer for AutoHeal is started here
+ * Checks if an entity can be allowed to garrison in the building
+ * based on it's class
  */
-GarrisonHolder.prototype.Garrison = function(entity)
+GarrisonHolder.prototype.AllowedToGarrison = function(entity)
 {
-	var entityPopCost = (Engine.QueryInterface(entity, IID_Cost)).GetPopCost();
+	var allowedClasses = this.GetAllowedClassesList();
 	var entityClasses = (Engine.QueryInterface(entity, IID_Identity)).GetClassesList();
-	var allowedClasses = this.GetAllowedClassesList();
 	var classNotAllowed = true;
-
-	if (!this.HasEnoughHealth())
-		return false;
-
 	// Check if the unit is allowed to be garrisoned inside the building
 	for each (var allowedClass in allowedClasses)
 	{
 		if (entityClasses.indexOf(allowedClass) != -1)
 		{
-			classNotAllowed = false;
-			break;
+			return true;
 		}
 	}
 
-	if (classNotAllowed)
+	return false;
+};
+
+/**
+ * Garrison a unit inside.
+ * Returns true if successful, false if not
+ * The timer for AutoHeal is started here
+ */
+GarrisonHolder.prototype.Garrison = function(entity)
+{
+	var entityPopCost = (Engine.QueryInterface(entity, IID_Cost)).GetPopCost();
+	
+	
+	
+
+	if (!this.HasEnoughHealth())
 		return false;
 
+	if(!this.AllowedToGarrison(entity))
+		return false;
+
 	if (this.GetCapacity() < this.spaceOccupied + 1)
 		return false;
 
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(revision 8539)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(working copy)
@@ -1122,8 +1122,7 @@
 	var cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder);
 	if (!cmpGarrisonHolder)
 		return false;
-	
-	return true;
+	return cmpGarrisonHolder.AllowedToGarrison(this.entity);
 };
 
 UnitAI.prototype.CanGather = function(target)

