Beispiel #1
0
        /// <summary>
        /// <para>Finds the right font size to fit characters (2x5 with default size, but it varies).</para>
        /// <para>Finds right vertical area based on font's actual display properties.</para>
        /// </summary>
        private void calibrateFont()
        {
            float width = Width;

            fontSize = 10.0F;

            // Measuring artefacts
            using (Bitmap bmp = new Bitmap(1, 1))
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    StringFormat sf = StringFormat.GenericTypographic;
                    g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

                    // Keep growing font until we reach a comfortable width
                    while (true)
                    {
                        //var xsi = HanziMeasure.Instance.GetMeasures(fontFace, fontSize);
                        //charSize = xsi.RealRect.Size;
                        charSize = HanziRenderer.GetCharSize(Magic.ZhoContentFontFamily, fontSize);
                        if (charSize.Width * 5.0F >= width * 0.9F)
                        {
                            break;
                        }
                        fontSize += 0.5F;
                    }
                }

            // Width of rectangle: using my space equally
            float rectWidth = (width - 2.0F) / 5.0F;
            // Height of rectange: depends on font's actual drawing behavior!
            float rectHeight = charSize.Height;
            // Horizontal padding is rectangle width minus measured char width, over two
            float hPad = (rectWidth - charSize.Width) / 2.0F;

            // Add twice horizontal padding to rectangle height; offset chars from top by padding
            rectHeight += 2.0F * hPad;

            lock (animLO)
            {
                for (int i = 0; i != 5; ++i)
                {
                    float      x    = ((float)i) * rectWidth + 1.0F;
                    RectangleF rtop = new RectangleF(x, 1.0F, rectWidth, rectHeight);
                    RectangleF rbot = new RectangleF(x, rectHeight + 1.0F, rectWidth, rectHeight);
                    charRects[i].Rect     = rtop;
                    charRects[i + 5].Rect = rbot;
                }
            }
            charOfsX = (rectWidth - charSize.Width) / 2.0F;
            charOfsY = (rectHeight - charSize.Height) / 2.0F;
            Height   = (int)Math.Round((rectHeight) * 2.0F + 0.5F);
            MakeMePaint(false, RenderMode.Invalidate);
        }
Beispiel #2
0
        /// <summary>
        /// Ctor: initializes main form.
        /// </summary>
        public MainForm(ICedictEngineFactory dictFact, ITextProvider tprov)
            : base(tprov)
        {
            this.tprov = tprov;

            // Initialize hanzi renderer
            // -- Scale (DPI)
            // -- Available systems fonts
            HanziRenderer.Scale = Scale;
            if (HanziRenderer.IsWinKaiAvailable())
            {
                Magic.SetZhoContentFontFamily(IdeoFamily.WinKai);
            }
            else
            {
                Magic.SetZhoContentFontFamily(IdeoFamily.ArphicKai);
            }

            // Initialize system font provider with our own
            if (SystemFontProvider.Instance as ZydeoSystemFontProvider == null)
            {
                SystemFontProvider.Instance = new ZydeoSystemFontProvider();
            }

            // Find out last window size and location from settings
            Size  size = AppSettings.WindowLogicalSize;
            Point loc  = AppSettings.WindowLoc;

            ignoredSavedSizeAndLocation = !verifySizeAndLoc(size, loc);
            // If location+size do not make sense, let system position window, and go with default size.
            if (ignoredSavedSizeAndLocation)
            {
                WinForm.StartPosition = FormStartPosition.WindowsDefaultLocation;
                LogicalSize           = Magic.WinDefaultLogicalSize;
            }
            // Otherwise, position at last location
            else
            {
                WinForm.StartPosition = FormStartPosition.Manual;
                Location    = loc;
                LogicalSize = size;
            }
            // Set (logical) minimum size
            LogicalMinimumSize = Magic.WinMinimumLogicalSize;

            Header  = tprov.GetString("WinHeader");
            lc      = new LookupControl(this, dictFact, tprov);
            stgs    = new SettingsControl(this, tprov, dictFact);
            MainTab = new ZenTab(stgs, tprov.GetString("TabMain"));
            Tabs.Add(new ZenTab(lc, tprov.GetString("TabLookup")));
        }
        /// <summary>
        /// Calculates right-aligned layout in headword area.
        /// </summary>
        private static bool doAnalyzeHanzi(Graphics g, string str, bool isSimp, StringFormat sf,
                                           List <HeadBlock> blocks, ref PointF loc, float right)
        {
            byte  fntZhoHead      = isSimp ? fntZhoHeadSimp : fntZhoHeadTrad;
            float left            = loc.X;
            bool  lineBreak       = false;
            int   firstCharOfLine = 0;
            int   charsOnLine     = 0;

            // Measure and position each character
            for (int i = 0; i != str.Length; ++i)
            {
                ++charsOnLine;
                // Measure each character. They may not all be hanzi: there are latin letters in some HWS
                HeadBlock hb = new HeadBlock
                {
                    Char = str[i],
                    Size = HanziRenderer.MeasureChar(g, Magic.ZhoContentFontFamily, str[i], Magic.ZhoResultFontSize),
                    //Size = g.MeasureString(cstr, getFont(fntZhoHead), 65535, sf),
                    Loc   = loc,
                    Faded = false,
                };
                blocks.Add(hb);
                // Location moves right
                loc.X += hb.Size.Width;
                // If location is beyond headword's right edge, break line now.
                // This involves
                // - moving last added block to next line
                // - right-aligning blocks added so far
                if (loc.X > right)
                {
                    lineBreak = true;
                    loc.X     = left;
                    loc.Y    += ideoLineHeight;
                    doRightAlign(blocks, firstCharOfLine, charsOnLine - 1, right);
                    charsOnLine     = 1;
                    firstCharOfLine = blocks.Count - 1;
                    hb.Loc          = loc;
                    loc.X          += hb.Size.Width;
                }
            }
            // Right-align the final bit
            doRightAlign(blocks, firstCharOfLine, charsOnLine, right);
            // Done - tell call if we had line break or not
            return(lineBreak);
        }
Beispiel #4
0
 /// <summary>
 /// Loads dictionary in worker thread.
 /// </summary>
 /// <param name="ctxt"></param>
 private void loadDictionary(object ctxt)
 {
     Thread.Sleep(100);
     dict = dictFact.Create(Magic.DictFileName, HanziRenderer.GetFontCoverage(Magic.ZhoContentFontFamily));
 }
Beispiel #5
0
        /// <summary>
        /// Paints full control. Analyzes on demand, but meant to be called after analysis up front.
        /// </summary>
        public override void DoPaint(Graphics g)
        {
            // If size changed and we get a pain requested without having re-analized:
            // Analyze now. Not the best time here in paint, but must do.
            if (analyzedWidth != Width)
            {
                Analyze(g, Width);
            }

            // Background.
            Color bgcol = ZenParams.WindowColor;

            using (Brush b = new SolidBrush(bgcol))
            {
                g.FillRectangle(b, 0, 0, Width, Height);
            }
            // Dotted line at bottom
            if (!last)
            {
                using (Pen p = new Pen(Magic.ResultsSeparator))
                {
                    float dp     = (int)(1.3F * scale);
                    int   margin = (int)(8F * scale);
                    p.DashPattern   = new float[] { dp, dp };
                    p.Width         = 1;
                    g.SmoothingMode = SmoothingMode.None;
                    g.DrawLine(p, margin, Height - 1, Width - margin, Height - 1);
                }
            }

            // Hanzi highlights. May draw on top, so must come before actual characters are drawn.
            doPaintHanziHilites(g, bgcol);
            // Target text highlights (characters will come on top).
            doPaintTargetHilites(g, bgcol);

            // This is how we draw text
            StringFormat sf = StringFormat.GenericTypographic;

            // Headword, pinyin, entry body
            using (Brush bnorm = new SolidBrush(Color.Black))
                using (Brush bfade = new SolidBrush(Color.FromArgb(200, 200, 200)))
                    using (Pen pnorm = new Pen(bnorm))
                    {
                        // This works best for Hanzi
                        g.TextRenderingHint = TextRenderingHint.AntiAlias;
                        // Simplified and traditional - headword
                        foreach (HeadBlock hb in headInfo.SimpBlocks)
                        {
                            PointF loc = new PointF(hb.Loc.X, hb.Loc.Y);
                            HanziRenderer.DrawString(g, hb.Char.ToString(), loc, bnorm, Magic.ZhoContentFontFamily,
                                                     IdeoScript.Simp, Magic.ZhoResultFontSize, FontStyle.Regular);
                        }
                        foreach (HeadBlock hb in headInfo.TradBlocks)
                        {
                            PointF loc = new PointF(hb.Loc.X, hb.Loc.Y);
                            Brush  b   = hb.Faded ? bfade : bnorm;
                            HanziRenderer.DrawString(g, hb.Char.ToString(), loc, b, Magic.ZhoContentFontFamily,
                                                     IdeoScript.Trad, Magic.ZhoResultFontSize, FontStyle.Regular);
                        }
                        // Pinyin
                        using (SolidBrush bhilite = new SolidBrush(Magic.HiliteColor))
                            using (SolidBrush bpinyin = new SolidBrush(Magic.PinyinColor))
                            {
                                doPaintPinyin(g, bpinyin, bhilite, sf, bgcol);
                            }
                        // Target (body)
                        doPaintTarget(g, pnorm, bnorm, sf);
                    }
        }
Beispiel #6
0
        /// <summary>
        /// Paints target (entry body).
        /// </summary>
        private void doPaintTarget(Graphics g, Pen pnorm, Brush bnorm, StringFormat sf)
        {
            // Take index of hovered-over sense once - this is atomic, and we're prolly in different thread from mouse
            short hsix   = hoverSenseIx;
            Brush bhover = null;

            try
            {
                // All the measured and positioned blocks in entry body
                short currSenseIx = -1;
                foreach (PositionedBlock pb in positionedBlocks)
                {
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                    Block block = measuredBlocks[pb.BlockIdx];
                    if (block.FirstInCedictSense)
                    {
                        ++currSenseIx;
                    }
                    bool hoverSense = currSenseIx == hsix;
                    // Sense ID
                    if (block.SenseId)
                    {
                        float pad = lemmaLineHeight * 0.1F;
                        if (hoverSense)
                        {
                            using (Brush bh = new SolidBrush(Magic.SenseHoverColor))
                            {
                                g.FillEllipse(bh,
                                              pb.LocX + 1F,
                                              pb.LocY + scale * pad + 1F,
                                              ((float)block.Width) - 2.0F * pad - 2F,
                                              lemmaCharHeight - 2.0F * pad - 2F);
                            }
                        }
                        g.DrawEllipse(pnorm,
                                      pb.LocX,
                                      pb.LocY + scale * pad,
                                      ((float)block.Width) - 2.0F * pad,
                                      lemmaCharHeight - 2.0F * pad);
                        float idOfsV = -2.896F + scale * 4.396F;
                        g.DrawString(textPool.GetString(block.TextPos), getFont(fntSenseId), bnorm,
                                     pb.LocX + 2.0F * pad,
                                     pb.LocY + idOfsV, sf); // TO-DO: vertical paddig of character will need more work.
                    }
                    // Text
                    else
                    {
                        // Extra vertical offset on Hanzi blocks
                        float vOfs    = 0;
                        bool  isHanzi = false;
                        if (block.FontIdx == fntMetaHanziSimp || block.FontIdx == fntMetaHanziTrad ||
                            block.FontIdx == fntSenseHanziSimp || block.FontIdx == fntSenseHanziTrad)
                        {
                            vOfs   += getTargetHanziOfs();
                            isHanzi = true;
                        }
                        // No hover: draw with normal brush
                        Brush brush = bnorm;
                        // Hover: create hover brush on demand; draw with that
                        if (hoverLink != null)
                        {
                            if (hoverLink.BlockIds.Contains(pb.BlockIdx))
                            {
                                if (bhover == null)
                                {
                                    bhover = new SolidBrush(Magic.LinkHoverColor);
                                }
                                brush = bhover;
                            }
                        }
                        if (isHanzi)
                        {
                            g.TextRenderingHint = TextRenderingHint.AntiAlias;
                        }
                        else
                        {
                            g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                        }
                        // Not a hanzi range (by font ID)
                        if (block.FontIdx == fntSenseLatin || block.FontIdx == fntMetaLatin)
                        {
                            g.DrawString(textPool.GetString(block.TextPos), getFont(block.FontIdx),
                                         brush, pb.LocX, pb.LocY, sf);
                        }
                        // Hanzi range
                        else
                        {
                            IdeoScript script = block.FontIdx == fntMetaHanziSimp || block.FontIdx == fntSenseHanziSimp
                                ? IdeoScript.Simp : IdeoScript.Trad;
                            bool      meta     = block.FontIdx == fntMetaHanziSimp || block.FontIdx == fntMetaHanziTrad;
                            FontStyle fntStyle = meta ? FontStyle.Italic : FontStyle.Regular;
                            HanziRenderer.DrawString(g, textPool.GetString(block.TextPos), new PointF(pb.LocX, pb.LocY + vOfs),
                                                     brush, Magic.ZhoContentFontFamily, script, Magic.LemmaHanziFontSize, fntStyle);
                        }
                    }
                }
            }
            finally
            {
                if (bhover != null)
                {
                    bhover.Dispose();
                }
            }
        }
        /// <summary>
        /// Breaks down body content into typographic blocks and caches the size of these.
        /// </summary>
        /// <param name="g">A Graphics object used for measurements.</param>
        private void doMeasureBlocks(Graphics g)
        {
            // Once measured, blocks don't change. Nothing to do then.
            if (measuredBlocks != null)
            {
                return;
            }

            // This is how we measure
            StringFormat sf = StringFormat.GenericTypographic;

            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

            // Decide about size of sense ID up front: that's always a square, letter-height
            SizeF  xSize         = g.MeasureString("x", getFont(fntSenseLatin), 65535, sf);
            ushort senseIdxWidth = (ushort)Math.Ceiling(xSize.Height);

            // Create array with as many items as senses
            // Each item is null, or highlight in sense's equiv
            CedictTargetHighlight[] hlArr = new CedictTargetHighlight[entry.SenseCount];
            foreach (CedictTargetHighlight hl in res.TargetHilites)
            {
                hlArr[hl.SenseIx] = hl;
            }

            // Recreate list of blocks
            List <Block> newBlocks = new List <Block>();
            // Collect links here. Will only keep at end if not empty.
            List <LinkArea> newLinks = new List <LinkArea>();

            int  senseIdx          = -1;
            int  displaySenseIdx   = -1;
            bool lastWasClassifier = false;

            foreach (CedictSense cm in entry.Senses)
            {
                ++senseIdx;
                // Is this sense a classifier?
                bool classifier = cm.Domain.EqualsPlainText("CL:");
                if (!classifier)
                {
                    ++displaySenseIdx;
                }
                // Add one block for sense ID, unless this is a classifier "sense"
                if (!classifier)
                {
                    Block sidBlock = new Block
                    {
                        Width              = senseIdxWidth,
                        StickRight         = true,
                        TextPos            = textPool.PoolString(getSenseIdString(displaySenseIdx)),
                        NewLine            = lastWasClassifier,
                        SenseId            = true,
                        FirstInCedictSense = true,
                    };
                    newBlocks.Add(sidBlock);
                }
                // Split domain, equiv and note into typographic parts
                // Splits along spaces and dashes
                // Unpacks Chinese ranges
                // Domain is localized text for "Classifier:" if, well, this is a classifier sense
                int startIX = newBlocks.Count;
                if (!classifier)
                {
                    makeBlocks(cm.Domain, true, null, newBlocks, newLinks);
                }
                else
                {
                    string     strClassifier = tprov.GetString("ResultCtrlClassifier");
                    HybridText htClassifier  = new HybridText(strClassifier);
                    int        ix            = newBlocks.Count;
                    makeBlocks(htClassifier, true, null, newBlocks, newLinks);
                    Block xb = newBlocks[ix];
                    xb.NewLine    = true;
                    newBlocks[ix] = xb;
                }
                makeBlocks(cm.Equiv, false, hlArr[senseIdx], newBlocks, newLinks);
                makeBlocks(cm.Note, true, null, newBlocks, newLinks);
                // If sense is a classifier, mark first block as sense starter
                if (classifier)
                {
                    Block sstart = newBlocks[startIX];
                    sstart.FirstInCedictSense = true;
                    newBlocks[startIX]        = sstart;
                }
                // Measure each block
                for (int i = startIX; i != newBlocks.Count; ++i)
                {
                    Block tb      = newBlocks[i];
                    bool  isHanzi = !(tb.FontIdx == fntMetaLatin || tb.FontIdx == fntSenseLatin);
                    SizeF sz;
                    if (!isHanzi)
                    {
                        sz = g.MeasureString(textPool.GetString(tb.TextPos), getFont(tb.FontIdx), 65535, sf);
                    }
                    else
                    {
                        sz = HanziRenderer.MeasureString(g, Magic.ZhoContentFontFamily, textPool.GetString(tb.TextPos), Magic.LemmaHanziFontSize);
                    }
                    tb.Width     = (ushort)Math.Round(sz.Width);
                    newBlocks[i] = tb;
                }
                lastWasClassifier = classifier;
            }
            if (newLinks.Count != 0)
            {
                targetLinks = newLinks;
            }
            measuredBlocks = newBlocks.ToArray();
        }
        /// <summary>
        /// Analyzes layout of headword.
        /// </summary>
        /// <param name="g">A Graphics object used for measurements.</param>
        private void doAnalyzeHeadword(Graphics g)
        {
            // If already analyzed, nothing to do
            if (headInfo != null)
            {
                return;
            }

            // This is how we measure
            StringFormat sf = StringFormat.GenericTypographic;

            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

            // On-demand: measure a single ideograph's dimensions
            // We only measure simplified. Assume simplified and traditional fonts come in matching pairs -> same size.
            if (ideoSize.Width == 0)
            {
                ideoSize = HanziRenderer.GetCharSize(Magic.ZhoContentFontFamily, Magic.ZhoResultFontSize);
                float hanziLinePad = 6.0F;
                hanziLinePad  *= scale;
                ideoLineHeight = ideoSize.Height + hanziLinePad;
            }

            headInfo = new HeadInfo();
            if (analyzedScript == SearchScript.Simplified)
            {
                headInfo.HeadMode = HeadMode.OnlySimp;
            }
            else if (analyzedScript == SearchScript.Traditional)
            {
                headInfo.HeadMode = HeadMode.OnlyTrad;
            }
            else
            {
                headInfo.HeadMode = HeadMode.BothSingleLine;
            }
            //// For width of headword, use padLeft from border, plus 4 to 6 ideographs' worth of space
            //// Depending on longest headword in entire list
            //int hwChars = maxHeadLength;
            //if (hwChars < 4) hwChars = 4;
            //if (hwChars > 6) hwChars = 6;
            //float hwidth = ((float)hwChars) * ideoSize.Width;
            // Revised
            // For width of headword, always take 5 characters' width of space
            float hwidth = 5.0F * ideoSize.Width;

            headInfo.HeadwordRight = padLeft + hwidth;
            // Measure simplified chars from start; break when needed
            PointF loc  = new PointF(((float)padLeft) + ideoSize.Width, padTop);
            bool   lbrk = false;

            if (analyzedScript == SearchScript.Simplified || analyzedScript == SearchScript.Both)
            {
                lbrk |= doAnalyzeHanzi(g, entry.ChSimpl, true, sf, headInfo.SimpBlocks, ref loc, headInfo.HeadwordRight);
            }
            if (analyzedScript == SearchScript.Traditional || analyzedScript == SearchScript.Both)
            {
                //loc.X = padLeft;
                loc.X = ((float)padLeft) + ideoSize.Width;
                if (analyzedScript == SearchScript.Both)
                {
                    loc.Y += ideoLineHeight;
                }
                lbrk |= doAnalyzeHanzi(g, entry.ChTrad, false, sf, headInfo.TradBlocks, ref loc, headInfo.HeadwordRight);
            }
            // If we're displaying both simplified and traditional, fade out
            // traditional chars that are same as simplified, right above them
            if (analyzedScript == SearchScript.Both)
            {
                for (int i = 0; i != headInfo.SimpBlocks.Count; ++i)
                {
                    if (headInfo.SimpBlocks[i].Char == headInfo.TradBlocks[i].Char)
                    {
                        headInfo.TradBlocks[i].Faded = true;
                    }
                }
            }
            // Bottom of headword area
            headInfo.HeadwordBottom = loc.Y + ideoLineHeight;
            // If we had a line break and we're showing both scripts, update info
            if (analyzedScript == SearchScript.Both && lbrk)
            {
                headInfo.HeadMode = HeadMode.BothMultiLine;
            }
        }
Beispiel #9
0
        /// <summary>
        /// Paints the control.
        /// </summary>
        public override void DoPaint(System.Drawing.Graphics g)
        {
            // Get character rectangles
            CharRect[] rects = getCharRects();

            // Background
            using (Brush b = new SolidBrush(ZenParams.WindowColor))
            {
                g.FillRectangle(b, 0, 0, Width, Height);
            }
            // Characters
            if (items != null)
            {
                StringFormat sf = StringFormat.GenericTypographic;
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                using (Brush b = new SolidBrush(Color.Black))
                {
                    for (int i = 0; i != rects.Length; ++i)
                    {
                        RectangleF rect = rects[i].Rect;
                        // Background
                        using (Brush bgb = new SolidBrush(rects[i].BgColor))
                        {
                            g.FillRectangle(bgb, rect);
                        }
                        // Draw character, if any
                        if (i >= items.Length)
                        {
                            continue;
                        }
                        string str = ""; str += items[i];
                        HanziRenderer.DrawString(g, str, new PointF(rect.X + charOfsX, rect.Y + charOfsY), b,
                                                 fontFam, fontScript, fontSize, FontStyle.Regular);
                    }
                }
            }
            // Error message
            else
            {
                StringFormat sf = StringFormat.GenericDefault;
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
                string line1    = tprov.GetString("CharPickerError1");
                string line2    = tprov.GetString("CharPickerError2");
                float  fontSize = Magic.CharPickerErrorFontSize;
                using (Font f1 = SystemFontProvider.Instance.GetSystemFont(FontStyle.Bold, fontSize))
                    using (Font f2 = SystemFontProvider.Instance.GetSystemFont(FontStyle.Regular, fontSize))
                        using (Brush b = new SolidBrush(Color.Black))
                        {
                            float  padL   = 10F * Scale;
                            float  padR   = 10F * Scale;
                            SizeF  sz1    = g.MeasureString(line1, f1, (int)(Width - padL - padR), sf);
                            SizeF  sz2    = g.MeasureString(line2, f2, (int)(Width - padL - padR), sf);
                            float  hblock = sz1.Height + sz2.Height;
                            PointF pt1    = new PointF(padL, (((float)Height - 2) - hblock) / 2F);
                            PointF pt2    = new PointF(pt1.X, pt1.Y + sz1.Height);
                            g.DrawString(line1, f1, b, pt1);
                            g.DrawString(line2, f2, b, new RectangleF(pt2.X, pt2.Y, Width - pt2.X - 1, Height - pt2.Y - 1));
                        }
            }
            // Border
            using (Pen p = new Pen(ZenParams.BorderColor))
            {
                g.DrawLine(p, 0, 0, Width, 0);
                g.DrawLine(p, Width - 1, 0, Width - 1, Height);
                g.DrawLine(p, Width - 1, Height - 1, 0, Height - 1);
                g.DrawLine(p, 0, Height - 1, 0, 0);
            }
        }