// Get Length of a special, in meters. public static float ComputeSpecialLength(EventDB eventDB, Id<Special> specialId) { Special special = eventDB.GetSpecial(specialId); SymPath path = new SymPath(special.locations); return (float)((eventDB.GetEvent().mapScale * path.Length) / 1000.0); }
// Describe a special. private static TextPart[] DescribeSpecial(EventDB eventDB, Id<Special> specialId, float scaleRatio, DescKind descKind) { Debug.Assert(descKind == DescKind.Tooltip || descKind == DescKind.DescPane); List<TextPart> list = new List<TextPart>(); Special special = eventDB.GetSpecial(specialId); // Name of the special. list.Add(new TextPart(TextFormat.Title, SpecialName(eventDB, specialId))); if (descKind == DescKind.DescPane) { // Special location. if (special.kind == SpecialKind.FirstAid || special.kind == SpecialKind.Water || special.kind == SpecialKind.Forbidden || special.kind == SpecialKind.OptCrossing || special.kind == SpecialKind.RegMark || special.kind == SpecialKind.Descriptions) { list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.Location + " ")); list.Add(new TextPart(TextFormat.SameLine, string.Format("({0:##0.0}, {1:##0.0})", special.locations[0].X, special.locations[0].Y))); } } if (special.kind == SpecialKind.Image) { list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.FileName + " ")); list.Add(new TextPart(TextFormat.SameLine, string.Format("{0}", special.text))); } if (special.kind == SpecialKind.Boundary || special.kind == SpecialKind.Line) { list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.Length)); list.Add(new TextPart(TextFormat.SameLine, string.Format("{0:#,###} m", QueryEvent.ComputeSpecialLength(eventDB, specialId)))); } // Line height for descriptions. if (special.kind == SpecialKind.Descriptions) { list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.LineHeight + " ")); list.Add(new TextPart(TextFormat.SameLine, string.Format("{0:#0.0} mm", Geometry.Distance(special.locations[0], special.locations[1]) / scaleRatio))); } // Which courses is it used in? list.Add(new TextPart(TextFormat.Header, (descKind == DescKind.Tooltip ? SelectionDescriptionText.UsedIn : SelectionDescriptionText.UsedInCourses))); if (special.allCourses) list.Add(new TextPart(descKind == DescKind.Tooltip ? TextFormat.SameLine : TextFormat.NewLine, SelectionDescriptionText.CourseList_AllCourses)); else list.Add(new TextPart(descKind == DescKind.Tooltip ? TextFormat.SameLine : TextFormat.NewLine, CourseListText(eventDB, special.courses))); return list.ToArray(); }
// Get the text name for a control. private static string SpecialName(EventDB eventDB, Id<Special> specialId) { Special special = eventDB.GetSpecial(specialId); // Resources have the name "SpecialKind_xxx" where "xxx" is the enumeration name. return SelectionDescriptionText.ResourceManager.GetString("SpecialName_" + special.kind.ToString()); }
// Create the course objects associated with this special. Assign the given layer to it. static CourseObj CreateDescriptionSpecial(EventDB eventDB, SymbolDB symbolDB, CourseView.DescriptionView descriptionView, CourseLayer layer) { Special special = eventDB.GetSpecial(descriptionView.SpecialId); Debug.Assert(special.kind == SpecialKind.Descriptions); DescriptionKind descKind; DescriptionLine[] description = GetCourseDescription(eventDB, symbolDB, descriptionView.CourseDesignator, out descKind); CourseObj courseObj = new DescriptionCourseObj(descriptionView.SpecialId, special.locations[0], (float)Geometry.Distance(special.locations[0], special.locations[1]), symbolDB, description, descKind, special.numColumns); courseObj.layer = layer; return courseObj; }
// Create the course objects associated with this special. Assign the given layer to it. static CourseObj CreateSpecial(EventDB eventDB, CourseView courseView, float scaleRatio, CourseAppearance appearance, Id<Special> specialId, CourseLayer normalLayer) { Special special = eventDB.GetSpecial(specialId); CourseObj courseObj = null; switch (special.kind) { case SpecialKind.FirstAid: courseObj = new FirstAidCourseObj(specialId, scaleRatio, appearance, special.locations[0]); break; case SpecialKind.Water: courseObj = new WaterCourseObj(specialId, scaleRatio, appearance, special.locations[0]); break; case SpecialKind.OptCrossing: courseObj = new CrossingCourseObj(Id<ControlPoint>.None, Id<CourseControl>.None, specialId, scaleRatio, appearance, special.orientation, special.locations[0]); break; case SpecialKind.Forbidden: courseObj = new ForbiddenCourseObj(specialId, scaleRatio, appearance, special.locations[0]); break; case SpecialKind.RegMark: courseObj = new RegMarkCourseObj(specialId, scaleRatio, appearance, special.locations[0]); break; case SpecialKind.Boundary: courseObj = new BoundaryCourseObj(specialId, scaleRatio, appearance, new SymPath(special.locations)); break; case SpecialKind.Rectangle: courseObj = new RectSpecialCourseObj(specialId, appearance, special.color, special.lineKind, special.lineWidth, special.cornerRadius, special.gapSize, special.dashSize, Geometry.RectFromPoints(special.locations[0], special.locations[1])); break; case SpecialKind.Line: courseObj = new LineSpecialCourseObj(specialId, appearance, special.color, special.lineKind, special.lineWidth, special.gapSize, special.dashSize, new SymPath(special.locations)); break; case SpecialKind.OOB: courseObj = new OOBCourseObj(specialId, scaleRatio, appearance, special.locations); break; case SpecialKind.Dangerous: courseObj = new DangerousCourseObj(specialId, scaleRatio, appearance, special.locations); break; case SpecialKind.WhiteOut: courseObj = new WhiteOutCourseObj(specialId, scaleRatio, appearance, special.locations); break; case SpecialKind.Image: Special imageSpecial = eventDB.GetSpecial(specialId); courseObj = new ImageCourseObj(specialId, scaleRatio, appearance, special.locations, imageSpecial.text, imageSpecial.imageBitmap); break; case SpecialKind.Text: string text = ExpandText(eventDB, courseView, special.text); FontStyle fontStyle = Util.GetFontStyle(special.fontBold, special.fontItalic); RectangleF boundingRect = RectangleF.FromLTRB((float)Math.Min(special.locations[0].X, special.locations[1].X), (float)Math.Min(special.locations[0].Y, special.locations[1].Y), (float)Math.Max(special.locations[0].X, special.locations[1].X), (float)Math.Max(special.locations[0].Y, special.locations[1].Y)); courseObj = new BasicTextCourseObj(specialId, text, boundingRect, special.fontName, fontStyle, special.color); break; case SpecialKind.Descriptions: Debug.Fail("description specials should not be passed to this function"); return null; default: Debug.Fail("bad special kind"); return null; } courseObj.layer = normalLayer; return courseObj; }
// Get the set of courses that a special is displayed on. public static CourseDesignator[] GetSpecialDisplayedCourses(EventDB eventDB, Id<Special> specialId) { Special special = eventDB.GetSpecial(specialId); if (special.allCourses) { // special is on all courses. Return an array with all courses in it. List<CourseDesignator> list = new List<CourseDesignator>(); foreach (Id<Course> courseId in SortedCourseIds(eventDB)) { list.Add(new CourseDesignator(courseId)); } if (special.kind == SpecialKind.Descriptions) { // Descriptions also are on all controls separatedly. list.Add(CourseDesignator.AllControls); } return list.ToArray(); } else { return (CourseDesignator[])Util.CloneArrayAndElements(special.courses); // clone so that changes don't affect it. } }
// Change the orientation associated with a special. Must be an optional crossing point. public static void ChangeSpecialOrientation(EventDB eventDB, Id<Special> specialId, float newOrientation) { Special special = eventDB.GetSpecial(specialId); Debug.Assert(special.kind == SpecialKind.OptCrossing); special = (Special) special.Clone(); special.orientation = newOrientation; eventDB.ReplaceSpecial(specialId, special); }
// Change the line properties associated with a special internal static void ChangeSpecialLineAppearance(EventDB eventDB, Id<Special> specialId, SpecialColor color, LineKind lineKind, float lineWidth, float gapSize, float dashSize, float cornerRadius) { Special special = eventDB.GetSpecial(specialId); Debug.Assert(special.kind == SpecialKind.Rectangle || special.kind == SpecialKind.Line); special = (Special)special.Clone(); special.color = color; special.lineKind = lineKind; special.lineWidth = lineWidth; special.gapSize = gapSize; special.dashSize = dashSize; if (special.kind == SpecialKind.Rectangle) special.cornerRadius = cornerRadius; eventDB.ReplaceSpecial(specialId, special); }
// Add a corner to a special. public static void AddSpecialCorner(EventDB eventDB, Id<Special> specialId, PointF newCorner) { Special special = eventDB.GetSpecial(specialId); Debug.Assert(special.kind == SpecialKind.OOB || special.kind == SpecialKind.Dangerous || special.kind == SpecialKind.Boundary || special.kind == SpecialKind.Line || special.kind == SpecialKind.WhiteOut); bool isArea = (special.kind != SpecialKind.Boundary && special.kind != SpecialKind.Line); PointF[] oldLocations, newLocations; // If it's an area special, add the first location as the last also. if (isArea) { oldLocations = new PointF[special.locations.Length + 1]; Array.Copy(special.locations, oldLocations, special.locations.Length); oldLocations[special.locations.Length] = oldLocations[0]; } else { oldLocations = special.locations; } // Add the corner in the right place. PointF[] newPoints = Util.AddPointToArray(oldLocations, newCorner); // If it's an area special, remove the first location from the end. if (isArea) { newLocations = new PointF[newPoints.Length - 1]; Array.Copy(newPoints, newLocations, newPoints.Length - 1); } else { newLocations = newPoints; } // Update the special. ChangeSpecialLocations(eventDB, specialId, newLocations); }
// Remove a corner from a special. public static void RemoveSpecialCorner(EventDB eventDB, Id<Special> specialId, PointF cornerToRemove) { Special special = eventDB.GetSpecial(specialId); Debug.Assert(((special.kind == SpecialKind.OOB || special.kind == SpecialKind.Dangerous || special.kind == SpecialKind.WhiteOut) && special.locations.Length > 3) || ((special.kind == SpecialKind.Boundary || special.kind == SpecialKind.Line) && special.locations.Length > 2)); // Remove the corner PointF[] newPoints = Util.RemovePointFromArray(special.locations, cornerToRemove); // Update the special. ChangeSpecialLocations(eventDB, specialId, newPoints); }
// Check all descriptions other than the passed one, and remove any duplicate courses. public static void UpdateDescriptionCourses(EventDB eventDB, Id<Special> descriptionId) { // Which courses to remove? CourseDesignator[] coursesToRemove = QueryEvent.GetSpecialDisplayedCourses(eventDB, descriptionId); // Find all descriptions to change. List<Id<Special>> allDescriptionIds = new List<Id<Special>>(); foreach (Id<Special> specialId in eventDB.AllSpecialIds) { if (eventDB.GetSpecial(specialId).kind == SpecialKind.Descriptions && specialId != descriptionId) allDescriptionIds.Add(specialId); } foreach (Id<Special> descriptionToChange in allDescriptionIds) { // Remove any courses that overlap with the courses the given description has.. bool changes = false; // track if any changes made. List<CourseDesignator> courses = new List<CourseDesignator>(QueryEvent.GetSpecialDisplayedCourses(eventDB, descriptionToChange)); for (int courseIndex = 0; courseIndex < courses.Count; ++courseIndex) { foreach (CourseDesignator courseToRemove in coursesToRemove) { if (courseIndex >= 0 && courseIndex < courses.Count) { if (courseToRemove == courses[courseIndex]) { changes = true; courses.RemoveAt(courseIndex--); } else if (courseToRemove.CourseId == courses[courseIndex].CourseId) { changes = true; if (!courseToRemove.AllParts && courses[courseIndex].AllParts) { int removedPart = courseToRemove.Part; courses.RemoveAt(courseIndex--); for (int part = 0; part < QueryEvent.CountCourseParts(eventDB, courseToRemove.CourseId); ++part) { if (part != removedPart) courses.Add(new CourseDesignator(courseToRemove.CourseId, part)); } } else if (courseToRemove.AllParts) { courses.RemoveAt(courseIndex--); } } } } } // Commit the removal to the event database. if (changes) { if (courses.Count == 0) { // Remove the given description entire, since it has no displayed courses. eventDB.RemoveSpecial(descriptionToChange); } else { Special newDescription = (Special) eventDB.GetSpecial(descriptionToChange).Clone(); newDescription.allCourses = false; newDescription.courses = courses.ToArray(); eventDB.ReplaceSpecial(descriptionToChange, newDescription); } } } }
// Duplicate a course with a new name, new course controls (since course controls can't be shared), // but all other attributes the same. public static Id<Course> DuplicateCourse(EventDB eventDB, Id<Course> oldCourseId, string newName) { Course oldCourse = eventDB.GetCourse(oldCourseId); int newSortOrder = oldCourse.sortOrder + 1; // Update existing sort orders after by adding one to existing course orders after the new one. foreach (Id<Course> courseToChangeId in eventDB.AllCourseIds.ToList()) { int sortOrder = eventDB.GetCourse(courseToChangeId).sortOrder; if (sortOrder >= newSortOrder) ChangeCourseSortOrder(eventDB, courseToChangeId, sortOrder + 1); } // Duplicate the course controls with blank course controls, and record the mapping. Dictionary<Id<CourseControl>, Id<CourseControl>> mapCourseControl = new Dictionary<Id<CourseControl>, Id<CourseControl>>(); foreach (Id<CourseControl> oldCourseControlId in QueryEvent.EnumCourseControlIds(eventDB, new CourseDesignator(oldCourseId))) { CourseControl newCourseControl = new CourseControl(); Id<CourseControl> newCourseControlId = eventDB.AddCourseControl(newCourseControl); mapCourseControl[oldCourseControlId] = newCourseControlId; } // Create a new course with no course controls in it and the new name, sort order. Course newCourse = (Course)oldCourse.Clone(); if (oldCourse.firstCourseControl.IsNotNone) newCourse.firstCourseControl = mapCourseControl[oldCourse.firstCourseControl]; newCourse.name = newName; newCourse.sortOrder = newSortOrder; Id<Course> newCourseId = eventDB.AddCourse(newCourse); // Now copy all the old course control, updating all the linking fields. foreach (Id<CourseControl> oldCourseControlId in QueryEvent.EnumCourseControlIds(eventDB, new CourseDesignator(oldCourseId))) { // Add a new course control to the new course. CourseControl oldCourseControl = eventDB.GetCourseControl(oldCourseControlId); CourseControl newCourseControl = (CourseControl) oldCourseControl.Clone(); if (newCourseControl.nextCourseControl.IsNotNone) newCourseControl.nextCourseControl = mapCourseControl[newCourseControl.nextCourseControl]; if (newCourseControl.splitEnd.IsNotNone) newCourseControl.splitEnd = mapCourseControl[newCourseControl.splitEnd]; if (newCourseControl.splitCourseControls != null) { for (int i = 0; i < newCourseControl.splitCourseControls.Length; ++i) { newCourseControl.splitCourseControls[i] = mapCourseControl[newCourseControl.splitCourseControls[i]]; } } eventDB.ReplaceCourseControl(mapCourseControl[oldCourseControlId], newCourseControl); } // Duplicate any specials from the old course. foreach (Id<Special> specialId in eventDB.AllSpecialIds.ToList()) { Special special = eventDB.GetSpecial(specialId); if (!special.allCourses) { List<CourseDesignator> addedCourseDesignators = new List<CourseDesignator>(); foreach (CourseDesignator designatorWithSpecial in special.courses) { if (designatorWithSpecial.CourseId == oldCourseId) { if (designatorWithSpecial.AllParts) addedCourseDesignators.Add(new CourseDesignator(newCourseId)); else addedCourseDesignators.Add(new CourseDesignator(newCourseId, designatorWithSpecial.Part)); } } if (addedCourseDesignators.Count > 0) { ChangeDisplayedCourses(eventDB, specialId, special.courses.Concat(addedCourseDesignators).ToArray()); } } } return newCourseId; }
// Delete a course and all of its course controls. public static void DeleteCourse(EventDB eventDB, Id<Course> courseId) { // Remember the set of course controls. List<Id<CourseControl>> courseControls = new List<Id<CourseControl>>(QueryEvent.EnumCourseControlIds(eventDB, new CourseDesignator(courseId))); // Remove the course. eventDB.RemoveCourse(courseId); // Remove each of the course controls in that course foreach (Id<CourseControl> courseControlId in courseControls) { eventDB.RemoveCourseControl(courseControlId); } // Now check specials, and see which need to be modified List<Id<Special>> specialsToChange = new List<Id<Special>>(); foreach (Id<Special> specialId in eventDB.AllSpecialIds) { Special special = eventDB.GetSpecial(specialId); if (!special.allCourses && special.courses.Any(cd => cd.CourseId == courseId)) { // This special is not an all controls special, and is present on the course being deleted. Update it. specialsToChange.Add(specialId); } } // Update each of the specials. foreach (Id<Special> specialId in specialsToChange) { Special special = eventDB.GetSpecial(specialId); CourseDesignator[] newCourses = special.courses.Where(cd => cd.CourseId != courseId).ToArray(); if (newCourses.Length == 0) ChangeEvent.DeleteSpecial(eventDB, specialId); else { special = (Special) special.Clone(); special.courses = newCourses; eventDB.ReplaceSpecial(specialId, special); } } }
// Change the text associated with a special. Must be an text special public static void ChangeSpecialText(EventDB eventDB, Id<Special> specialId, string newText, string fontName, bool fontBold, bool fontItalic, SpecialColor specialColor) { Special special = eventDB.GetSpecial(specialId); Debug.Assert(special.kind == SpecialKind.Text); special = (Special) special.Clone(); special.text = newText; special.fontName = fontName; special.fontBold = fontBold; special.fontItalic = fontItalic; special.color = specialColor; eventDB.ReplaceSpecial(specialId, special); }
// Does the given course (or all controls) contain the given special? public static bool CourseContainsSpecial(EventDB eventDB, CourseDesignator courseDesignator, Id<Special> specialId) { Special special = eventDB.GetSpecial(specialId); if (special.allCourses) return true; if (courseDesignator.AllParts) return special.courses.Any(cd => cd.CourseId == courseDesignator.CourseId); else return special.courses.Contains(courseDesignator) || special.courses.Contains(new CourseDesignator(courseDesignator.CourseId)); }
// Change the number of columns of a descriptions public static void ChangeDescriptionColumns(EventDB eventDB, Id<Special> specialId, int numColumns) { Special special = eventDB.GetSpecial(specialId); Debug.Assert(special.kind == SpecialKind.Descriptions); special = (Special)special.Clone(); special.numColumns = numColumns; eventDB.ReplaceSpecial(specialId, special); }
public static int GetDescriptionColumns(EventDB eventDB, Id<Special> specialId) { Special special = eventDB.GetSpecial(specialId); Debug.Assert(special.kind == SpecialKind.Descriptions); return special.numColumns; }
// Change the set of courses that a special is on. If all the courses are in the new array, changes the special to display in // all courses. public static void ChangeDisplayedCourses(EventDB eventDB, Id<Special> specialId, CourseDesignator[] displayedCourses) { // Check if the array contains all of the courses. bool allCourses = true; Special special = (Special) eventDB.GetSpecial(specialId).Clone(); foreach (Id<Course> courseId in eventDB.AllCourseIds) { if (Array.IndexOf(displayedCourses, new CourseDesignator(courseId)) < 0) { allCourses = false; break; } } if (special.kind == SpecialKind.Descriptions) { if (Array.IndexOf(displayedCourses, CourseDesignator.AllControls) < 0) allCourses = false; // all courses includes all controls only for descriptions. } special.allCourses = allCourses; if (allCourses) special.courses = null; else special.courses = displayedCourses; eventDB.ReplaceSpecial(specialId, special); // Descriptions special are unique per course -- enforce this. if (special.kind == SpecialKind.Descriptions) UpdateDescriptionCourses(eventDB, specialId); }
// Create an filtered All Controls view -- show controls from the control collection, but only includes some. // excludedCourses contains an array of course ids to excluded from the contgrols. // kindFilter, if non-null, limits the controls to this kind of controls. public static CourseView CreateFilteredAllControlsView(EventDB eventDB, CourseDesignator[] excludedCourses, ControlPointKind kindFilter, bool addSpecials, bool addDescription) { CourseView courseView = new CourseView(eventDB, CourseDesignator.AllControls); courseView.courseName = MiscText.AllControls; courseView.scoreColumn = -1; // Add every control to the course view, subject to the filters. foreach (Id<ControlPoint> controlId in eventDB.AllControlPointIds) { ControlPoint control = eventDB.GetControl(controlId); // Check if the control is filtered out. if (excludedCourses != null) { // Filter excluded courses. foreach (CourseDesignator excludedCourseDesignator in excludedCourses) { if (QueryEvent.CourseUsesControl(eventDB, excludedCourseDesignator, controlId)) goto SKIP; } } if (kindFilter != ControlPointKind.None) { // Filter on control type. if (control.kind != kindFilter) goto SKIP; } // We are going to include this control in the collection. ControlView controlView = new ControlView(); controlView.courseControlIds = new[] { Id<CourseControl>.None }; controlView.controlId = controlId; // All controls doesn't have ordinals. controlView.ordinal = -1; controlView.joinIndex = -1; courseView.controlViews.Add(controlView); SKIP: ; } // Sort the control views: first by kind, then by code. courseView.controlViews.Sort((view1, view2) => QueryEvent.CompareControlIds(eventDB, view1.controlId, view2.controlId)); courseView.Finish(); if (addSpecials) { // Add every special, regardless of courses it is on, except for descriptions. Descriptions are added to all // controls only if they appear in all courses (or specifically for the all controls view), and if "addDescription" is true foreach (Id<Special> specialId in eventDB.AllSpecialIds) { Special special = eventDB.GetSpecial(specialId); if (special.kind == SpecialKind.Descriptions) { if (addDescription && QueryEvent.CourseContainsSpecial(eventDB, CourseDesignator.AllControls, specialId)) courseView.descriptionViews.Add(new DescriptionView(specialId, CourseDesignator.AllControls)); } else courseView.specialIds.Add(specialId); } } return courseView; }
// Change the locations associated with a special. public static void ChangeSpecialLocations(EventDB eventDB, Id<Special> specialId, PointF[] newLocations) { Special special = eventDB.GetSpecial(specialId); special = (Special) special.Clone(); special.locations = (PointF[]) newLocations.Clone(); eventDB.ReplaceSpecial(specialId, special); }