private void BuildContextMenus() { // EDIT MENU menuCopy = new MenuItem("&Copy", new EventHandler(this.menuCopy_Click)); menuPaste = new MenuItem("Paste", new EventHandler(this.menuPaste_Click)); menuDelete = new MenuItem("&Delete", new EventHandler(this.menuDelete_Click)); menuFSep1 = new MenuItem("-"); menuSelectAll = new MenuItem("Select &All", new EventHandler(this.menuSelectAll_Click)); menuFSep2 = new MenuItem("-"); List<MenuItem> insertItems = new List<MenuItem>(); insertItems.Add(new MenuItem("&Chart...", new EventHandler(this.menuInsertChart_Click))); insertItems.Add(new MenuItem("&Grid", new EventHandler(this.menuInsertGrid_Click))); insertItems.Add(new MenuItem("&System.Drawing.Image", new EventHandler(this.menuInsertImage_Click))); insertItems.Add(new MenuItem("&Line", new EventHandler(this.menuInsertLine_Click))); insertItems.Add(new MenuItem("&List", new EventHandler(this.menuInsertList_Click))); insertItems.Add(new MenuItem("&Matrix...", new EventHandler(this.menuInsertMatrix_Click))); insertItems.Add(new MenuItem("&System.Drawing.Rectangle", new EventHandler(this.menuInsertRectangle_Click))); insertItems.Add(new MenuItem("&Subreport", new EventHandler(this.menuInsertSubreport_Click))); insertItems.Add(new MenuItem("Ta&ble...", new EventHandler(this.menuInsertTable_Click))); insertItems.Add(new MenuItem("&Textbox", new EventHandler(this.menuInsertTextbox_Click))); // Now add any CustomReportItems BuildContextMenusCustom(insertItems); menuInsert = new MenuItem("&Insert"); menuInsert.MenuItems.AddRange(insertItems.ToArray()); menuProperties = new MenuItem("&Properties...", new EventHandler(this.menuProperties_Click)); // // Create chart context menu and add array of sub-menu items menuPropertiesLegend = new MenuItem("Legend...", new EventHandler(this.menuPropertiesLegend_Click)); menuPropertiesCategoryAxis = new MenuItem("Category (X) Axis...", new EventHandler(this.menuPropertiesCategoryAxis_Click)); menuPropertiesValueAxis = new MenuItem("Value (Y) Axis...", new EventHandler(this.menuPropertiesValueAxis_Click)); menuPropertiesCategoryAxisTitle = new MenuItem("Category (X) Axis Title...", new EventHandler(this.menuPropertiesCategoryAxisTitle_Click)); menuPropertiesValueAxisTitle = new MenuItem("Value (Y) Axis Title...", new EventHandler(this.menuPropertiesValueAxisTitle_Click)); menuPropertiesValueAxis2Title = new MenuItem("Value (Y) Axis (Right) Title...", new EventHandler(this.menuPropertiesValueAxis2Title_Click)); menuPropertiesChartTitle = new MenuItem("Title...", new EventHandler(this.menuPropertiesChartTitle_Click)); }
private void HighlightString(Graphics g, PageText dtext, RectangleF r, Font f, StringFormat sf) { if (_HighlightText == null || _HighlightText.Length == 0) return; // nothing to highlight bool bhighlightItem = dtext == _HighlightItem || (_HighlightItem != null && dtext.HtmlParent == _HighlightItem); if (!(_HighlightAll || bhighlightItem)) return; // not highlighting all and not on current highlight item string hlt = _HighlightCaseSensitive ? _HighlightText : _HighlightText.ToLower(); string text = _HighlightCaseSensitive ? dtext.Text : dtext.Text.ToLower(); if (text.IndexOf(hlt) < 0) return; // string not in text StringFormat sf2 = null; try { // Create a CharacterRange array with the highlight location and length // Handle multiple occurences of text List<CharacterRange> rangel = new List<CharacterRange>(); int loc = text.IndexOf(hlt); int hlen = hlt.Length; int len = text.Length; while (loc >= 0) { rangel.Add(new CharacterRange(loc, hlen)); if (loc + hlen < len) // out of range of text loc = text.IndexOf(hlt, loc + hlen); else loc = -1; } if (rangel.Count <= 0) // we should have gotten one; but return; CharacterRange[] ranges = rangel.ToArray(); // Construct a new StringFormat object. sf2 = sf.Clone() as StringFormat; // Set the ranges on the StringFormat object. sf2.SetMeasurableCharacterRanges(ranges); // Get the Regions to highlight by calling the // MeasureCharacterRanges method. if (r.Width <= 0 || r.Height <= 0) { SizeF ts = g.MeasureString(dtext.Text, f); r.Height = ts.Height; r.Width = ts.Width; } Region[] charRegion = g.MeasureCharacterRanges(dtext.Text, f, r, sf2); // Fill in the region using a semi-transparent color to highlight foreach (Region rg in charRegion) { Color hl = bhighlightItem ? _HighlightItemColor : _HighlightAllColor; g.FillRegion(new SolidBrush(Color.FromArgb(50, hl)), rg); } } catch { } // if highlighting fails we don't care; need to continue finally { if (sf2 != null) sf2.Dispose(); } }
// FuncIDent: IDENTIFIER ( [Expr] [, Expr]*) | IDENTIFIER private bool MatchFuncIDent(out IExpr result) { IExpr e; string fullname; // will hold the full name string method; // will hold method name or second part of name string firstPart; // will hold the collection name string thirdPart; // will hold third part of name bool bOnePart; // simple name: no ! or . in name result = null; if (curToken.Type != TokenTypes.IDENTIFIER) return false; // Disentangle method calls from collection references method = fullname = curToken.Value; curToken = tokens.Extract(); // Break the name into parts char[] breakChars = new char[] {'!', '.'}; int posBreak = method.IndexOfAny(breakChars); if (posBreak > 0) { bOnePart = false; firstPart = method.Substring(0, posBreak); method = method.Substring(posBreak+1); // rest of expression } else { bOnePart = true; firstPart = method; } posBreak = method.IndexOf('.'); if (posBreak > 0) { thirdPart = method.Substring(posBreak+1); // rest of expression method = method.Substring(0, posBreak); } else thirdPart = null; if (curToken.Type != TokenTypes.LPAREN) switch (firstPart) { case "Fields": Field f = idLookup.LookupField(method); if (f == null && !this._InAggregate) throw new ParserException("Field '" + method + "' not found."); if (thirdPart == null || thirdPart == "Value") { if (f == null) { FunctionField ff; result = ff = new FunctionField(method); this._FieldResolve.Add(ff); } else result = new FunctionField(f); } else if (thirdPart == "IsMissing") { if (f == null) { FunctionField ff; result = ff = new FunctionFieldIsMissing(method); this._FieldResolve.Add(ff); } else result = new FunctionFieldIsMissing(f); } else throw new ParserException("Field '" + method + "' only supports 'Value' and 'IsMissing' properties."); return true; case "Parameters": // see ResolveParametersMethod for resolution of MultiValue parameter function reference ReportParameter p = idLookup.LookupParameter(method); if (p == null) throw new ParserException("Report parameter '" + method + "' not found."); int ci = thirdPart == null? -1: thirdPart.IndexOf(".Count"); if (ci > 0) thirdPart = thirdPart.Substring(0, ci); FunctionReportParameter r; if (thirdPart == null || thirdPart == "Value") r = new FunctionReportParameter(p); else if (thirdPart == "Label") r = new FunctionReportParameterLabel(p); else throw new ParserException("Parameter '" + method + "' only supports 'Value' and 'Label' properties."); if (ci > 0) r.SetParameterMethod("Count", null); result = r; return true; case "ReportItems": Textbox t = idLookup.LookupReportItem(method); if (t == null) throw new ParserException("ReportItem '" + method + "' not found."); if (thirdPart != null && thirdPart != "Value") throw new ParserException("ReportItem '" + method + "' only supports 'Value' property."); result = new FunctionTextbox(t, idLookup.ExpressionName); return true; case "Globals": e = idLookup.LookupGlobal(method); if (e == null) throw new ParserException("Globals '" + method + "' not found."); result = e; return true; case "User": e = idLookup.LookupUser(method); if (e == null) throw new ParserException("User variable '" + method + "' not found."); result = e; return true; case "Recursive": // Only valid for some aggregate functions result = new IdentifierKey(IdentifierKeyEnum.Recursive); return true; case "Simple": // Only valid for some aggregate functions result = new IdentifierKey(IdentifierKeyEnum.Simple); return true; default: if (!bOnePart) throw new ParserException(string.Format("'{0}' is an unknown identifer.", fullname)); switch (method.ToLower()) // lexer should probably mark these { case "true": case "false": result = new ConstantBoolean(method.ToLower()); break; default: // usually this is enum that will be used in an aggregate result = new Identifier(method); break; } return true; } // We've got an function reference curToken = tokens.Extract(); // get rid of '(' // Got a function now obtain the arguments int argCount=0; bool isAggregate = IsAggregate(method, bOnePart); if (_NoAggregate && isAggregate) throw new ParserException("Aggregate function '" + method + "' cannot be used within a Grouping expression."); if (_InAggregate && isAggregate) throw new ParserException("Aggregate function '" + method + "' cannot be nested in another aggregate function."); _InAggregate = isAggregate; if (_InAggregate) _FieldResolve = new List<FunctionField>(); List<IExpr> largs = new List<IExpr>(); while(true) { if (curToken.Type == TokenTypes.RPAREN) { // We've got our function curToken = tokens.Extract(); break; } if (argCount == 0) { // don't need to do anything } else if (curToken.Type == TokenTypes.COMMA) { curToken = tokens.Extract(); } else throw new ParserException("Invalid function arguments. Found '" + curToken.Value + "' At column " + Convert.ToString(curToken.StartCol)); MatchExprAndOr(out e); if (e == null) throw new ParserException("Expecting ',' or ')'. Found '" + curToken.Value + "' At column " + Convert.ToString(curToken.StartCol)); largs.Add(e); argCount++; } if (_InAggregate) { ResolveFields(method, this._FieldResolve, largs); _FieldResolve = null; _InAggregate = false; } IExpr[] args = largs.ToArray(); object scope; bool bSimple; if (!bOnePart) { result = (firstPart == "Parameters")? ResolveParametersMethod(method, thirdPart, args): ResolveMethodCall(fullname, args); // throw exception when fails } else switch(method.ToLower()) { case "iif": if (args.Length != 3) throw new ParserException("iff function requires 3 arguments." + " At column " + Convert.ToString(curToken.StartCol)); // We allow any type for the first argument; it will get converted to boolean at runtime // if (args[0].GetTypeCode() != TypeCode.Boolean) // throw new ParserException("First argument to iif function must be boolean." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionIif(args[0], args[1], args[2]); break; case "choose": if (args.Length <= 2) throw new ParserException("Choose function requires at least 2 arguments." + " At column " + Convert.ToString(curToken.StartCol)); switch (args[0].GetTypeCode()) { case TypeCode.Double: case TypeCode.Single: case TypeCode.Int32: case TypeCode.Decimal: case TypeCode.Int16: case TypeCode.Int64: break; default: throw new ParserException("First argument to Choose function must be numeric." + " At column " + Convert.ToString(curToken.StartCol)); } result = new FunctionChoose(args); break; case "switch": if (args.Length <= 2) throw new ParserException("Switch function requires at least 2 arguments." + " At column " + Convert.ToString(curToken.StartCol)); if (args.Length % 2 != 0) throw new ParserException("Switch function must have an even number of arguments." + " At column " + Convert.ToString(curToken.StartCol)); for (int i=0; i < args.Length; i = i+2) { if (args[i].GetTypeCode() != TypeCode.Boolean) throw new ParserException("Switch function must have a boolean expression every other argument." + " At column " + Convert.ToString(curToken.StartCol)); } result = new FunctionSwitch(args); break; case "format": if (args.Length > 2 || args.Length < 1) throw new ParserException("Format function requires 2 arguments." + " At column " + Convert.ToString(curToken.StartCol)); if (args.Length == 1) { result = new FunctionFormat(args[0], new ConstantString("")); } else { if (args[1].GetTypeCode() != TypeCode.String) throw new ParserException("Second argument to Format function must be a string." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionFormat(args[0], args[1]); } break; case "fields": if (args.Length != 1) throw new ParserException("Fields collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionFieldCollection(idLookup.Fields, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." TODO: generalize this code curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException(curToken.Value + " is not a known property for Fields." + " At column " + Convert.ToString(curToken.StartCol)); } break; case "parameters": if (args.Length != 1) throw new ParserException("Parameters collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionParameterCollection(idLookup.Parameters, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException(curToken.Value + " is not a known property for Fields." + " At column " + Convert.ToString(curToken.StartCol)); } break; case "reportitems": if (args.Length != 1) throw new ParserException("ReportItems collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionReportItemCollection(idLookup.ReportItems, args[0]); if (curToken.Type == TokenTypes.DOT) { // user placed "." curToken = tokens.Extract(); // skip past dot operator if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value") curToken = tokens.Extract(); // only support "value" property for now else throw new ParserException(curToken.Value + " is not a known property for Fields." + " At column " + Convert.ToString(curToken.StartCol)); } break; case "globals": if (args.Length != 1) throw new ParserException("Globals collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionGlobalCollection(idLookup.Globals, args[0]); break; case "user": if (args.Length != 1) throw new ParserException("User collection requires exactly 1 argument." + " At column " + Convert.ToString(curToken.StartCol)); result = new FunctionUserCollection(idLookup.User, args[0]); break; case "sum": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrSum aggrFS = new FunctionAggrSum(_DataCache, args[0], scope); aggrFS.LevelCheck = bSimple; result = aggrFS; break; case "avg": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrAvg aggrFA = new FunctionAggrAvg(_DataCache, args[0], scope); aggrFA.LevelCheck = bSimple; result = aggrFA; break; case "min": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrMin aggrFMin = new FunctionAggrMin(_DataCache, args[0], scope); aggrFMin.LevelCheck = bSimple; result = aggrFMin; break; case "max": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrMax aggrFMax = new FunctionAggrMax(_DataCache, args[0], scope); aggrFMax.LevelCheck = bSimple; result = aggrFMax; break; case "first": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrFirst(_DataCache, args[0], scope); break; case "last": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrLast(_DataCache, args[0], scope); break; case "next": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrNext(_DataCache, args[0], scope); break; case "previous": scope = ResolveAggrScope(args, 2, out bSimple); result = new FunctionAggrPrevious(_DataCache, args[0], scope); break; case "level": scope = ResolveAggrScope(args, 1, out bSimple); result = new FunctionAggrLevel(scope); break; case "aggregate": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrArray aggr = new FunctionAggrArray(_DataCache, args[0], scope); aggr.LevelCheck = bSimple; result = aggr; break; case "count": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrCount aggrFC = new FunctionAggrCount(_DataCache, args[0], scope); aggrFC.LevelCheck = bSimple; result = aggrFC; break; case "countrows": scope = ResolveAggrScope(args, 1, out bSimple); FunctionAggrCountRows aggrFCR = new FunctionAggrCountRows(scope); aggrFCR.LevelCheck = bSimple; result = aggrFCR; break; case "countdistinct": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrCountDistinct aggrFCD = new FunctionAggrCountDistinct(_DataCache, args[0], scope); aggrFCD.LevelCheck = bSimple; result = aggrFCD; break; case "rownumber": scope = ResolveAggrScope(args, 1, out bSimple); IExpr texpr = new ConstantDouble("0"); result = new FunctionAggrRvCount(_DataCache, texpr, scope); break; case "runningvalue": if (args.Length < 2 || args.Length > 3) throw new ParserException("RunningValue takes 2 or 3 arguments." + " At column " + Convert.ToString(curToken.StartCol)); string aggrFunc = args[1].EvaluateString(null, null); if (aggrFunc == null) throw new ParserException("RunningValue 'Function' argument is invalid." + " At column " + Convert.ToString(curToken.StartCol)); scope = ResolveAggrScope(args, 3, out bSimple); switch(aggrFunc.ToLower()) { case "sum": result = new FunctionAggrRvSum(_DataCache, args[0], scope); break; case "avg": result = new FunctionAggrRvAvg(_DataCache, args[0], scope); break; case "count": result = new FunctionAggrRvCount(_DataCache, args[0], scope); break; case "max": result = new FunctionAggrRvMax(_DataCache, args[0], scope); break; case "min": result = new FunctionAggrRvMin(_DataCache, args[0], scope); break; case "stdev": result = new FunctionAggrRvStdev(_DataCache, args[0], scope); break; case "stdevp": result = new FunctionAggrRvStdevp(_DataCache, args[0], scope); break; case "var": result = new FunctionAggrRvVar(_DataCache, args[0], scope); break; case "varp": result = new FunctionAggrRvVarp(_DataCache, args[0], scope); break; default: throw new ParserException("RunningValue function '" + aggrFunc + "' is not supported. At column " + Convert.ToString(curToken.StartCol)); } break; case "stdev": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrStdev aggrSDev = new FunctionAggrStdev(_DataCache, args[0], scope); aggrSDev.LevelCheck = bSimple; result = aggrSDev; break; case "stdevp": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrStdevp aggrSDevP = new FunctionAggrStdevp(_DataCache, args[0], scope); aggrSDevP.LevelCheck = bSimple; result = aggrSDevP; break; case "var": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrVar aggrVar = new FunctionAggrVar(_DataCache, args[0], scope); aggrVar.LevelCheck = bSimple; result = aggrVar; break; case "varp": scope = ResolveAggrScope(args, 2, out bSimple); FunctionAggrVarp aggrVarP = new FunctionAggrVarp(_DataCache, args[0], scope); aggrVarP.LevelCheck = bSimple; result = aggrVarP; break; default: result = ResolveMethodCall(fullname, args); // through exception when fails break; } return true; }
private string[] MeasureString(PageText pt, Graphics g, out float[] width) { StyleInfo si = pt.SI; string s = pt.Text; Font drawFont=null; StringFormat drawFormat=null; SizeF ms; string[] sa=null; width=null; try { // STYLE System.Drawing.FontStyle fs = 0; if (si.FontStyle == FontStyleEnum.Italic) fs |= System.Drawing.FontStyle.Italic; // WEIGHT switch (si.FontWeight) { case FontWeightEnum.Bold: case FontWeightEnum.Bolder: case FontWeightEnum.W500: case FontWeightEnum.W600: case FontWeightEnum.W700: case FontWeightEnum.W800: case FontWeightEnum.W900: fs |= System.Drawing.FontStyle.Bold; break; default: break; } drawFont = new Font(StyleInfo.GetFontFamily(si.FontFamilyFull), si.FontSize, fs); drawFormat = new StringFormat(); drawFormat.Alignment = StringAlignment.Near; // Measure string // pt.NoClip indicates that this was generated by PageTextHtml Build. It has already word wrapped. if (pt.NoClip || pt.SI.WritingMode == WritingModeEnum.tb_rl) // TODO: support multiple lines for vertical text { ms = MeasureString(s, g, drawFont, drawFormat); width = new float[1]; width[0] = RSize.PointsFromPixels(g, ms.Width); // convert to points from pixels sa = new string[1]; sa[0] = s; return sa; } // handle multiple lines; // 1) split the string into the forced line breaks (ie "\n and \r") // 2) foreach of the forced line breaks; break these into words and recombine s = s.Replace("\r\n", "\n"); // don't want this to result in double lines string[] flines = s.Split(lineBreak); List<string> lines = new List<string>(); List<float> lineWidths = new List<float>(); // remove the size reserved for left and right padding float ptWidth = pt.W - pt.SI.PaddingLeft - pt.SI.PaddingRight; if (ptWidth <= 0) ptWidth = 1; foreach (string tfl in flines) { string fl; if (tfl.Length > 0 && tfl[tfl.Length-1] == ' ') fl = tfl.TrimEnd(' '); else fl = tfl; // Check if entire string fits into a line ms = MeasureString(fl, g, drawFont, drawFormat); float tw = RSize.PointsFromPixels(g, ms.Width); if (tw <= ptWidth) { // line fits don't need to break it down further lines.Add(fl); lineWidths.Add(tw); continue; } // Line too long; need to break into multiple lines // 1) break line into parts; then build up again keeping track of word positions string[] parts = fl.Split(wordBreak); // this is the maximum split of lines StringBuilder sb = new StringBuilder(fl.Length); CharacterRange[] cra = new CharacterRange[parts.Length]; for (int i = 0; i < parts.Length; i++) { int sc = sb.Length; // starting character sb.Append(parts[i]); // endding character if (i != parts.Length - 1) // last item doesn't need blank sb.Append(" "); int ec = sb.Length; CharacterRange cr = new CharacterRange(sc, ec - sc); cra[i] = cr; // add to character array } // 2) Measure the word locations within the line string wfl = sb.ToString(); WordStartFinish[] wordLocations = MeasureString(wfl, g, drawFont, drawFormat, cra); if (wordLocations == null) continue; // 3) Loop thru creating new lines as needed int startLoc = 0; CharacterRange crs = cra[startLoc]; CharacterRange cre = cra[startLoc]; float cwidth = wordLocations[0].end; // length of the first float bwidth = wordLocations[0].start; // characters need a little extra on start string ts; bool bLine = true; for (int i=1; i < cra.Length; i++) { cwidth = wordLocations[i].end - wordLocations[startLoc].start + bwidth; if (cwidth > ptWidth) { // time for a new line cre = cra[i-1]; ts = wfl.Substring(crs.First, cre.First + cre.Length - crs.First); lines.Add(ts); lineWidths.Add(wordLocations[i-1].end - wordLocations[startLoc].start + bwidth); // Find the first non-blank character of the next line while (i < cra.Length && cra[i].Length == 1 && fl[cra[i].First] == ' ') { i++; } if (i < cra.Length) // any lines left? { // yes, continue on startLoc = i; crs = cre = cra[startLoc]; cwidth = wordLocations[i].end - wordLocations[startLoc].start + bwidth; } else // no, we can stop bLine = false; // bwidth = wordLocations[startLoc].start - wordLocations[startLoc - 1].end; } else cre = cra[i]; } if (bLine) { ts = fl.Substring(crs.First, cre.First + cre.Length - crs.First); lines.Add(ts); lineWidths.Add(cwidth); } } // create the final array from the Lists string[] la = lines.ToArray(); width = lineWidths.ToArray(); return la; } finally { if (drawFont != null) drawFont.Dispose(); if (drawFormat != null) drawFont.Dispose(); } }