Пример #1
0
 /// <summary>
 /// Ctor: init immutable instance.
 /// </summary>
 public CedictAnnotation(int entryId, SearchScript script, int start, int length)
 {
     EntryId       = entryId;
     Script        = script;
     StartInQuery  = start;
     LengthInQuery = length;
 }
Пример #2
0
        /// <summary>
        /// Displays the received results, discarding existing data.
        /// </summary>
        /// <param name="lookupId">ID of lookup whose results are shown. If ID is smaller than last seen value, we don't show results.</param>
        /// <param name="entryProvider">The entry provider; ownership passed by caller to me.</param>
        /// <param name="results">Cedict lookup results to show.</param>
        /// <param name="script">Defines which script(s) to show.</param>
        /// <returns>True if results got shown; false if they're discarded because newer results are already on display.</returns>
        public bool SetResults(int lookupId,
                               ICedictEntryProvider entryProvider,
                               ReadOnlyCollection <CedictResult> results,
                               SearchScript script)
        {
#if DEBUG
            // Make us crash at bottom if first result "柏林" (comes up for "bolin")
            if (results.Count > 0)
            {
                CedictEntry entry = entryProvider.GetEntry(results[0].EntryId);
                if (entry.ChSimpl == "柏林")
                {
                    crashForTest = true;
                }
                else
                {
                    crashForTest = false;
                }
            }
#endif
            try
            {
                return(doSetResults(lookupId, entryProvider, results, script));
            }
            finally
            {
                if (entryProvider != null)
                {
                    entryProvider.Dispose();
                }
            }
        }
Пример #3
0
 /// <summary>
 /// Initializes tooltip provider for "search language" or "script" button.
 /// </summary>
 /// <param name="button">The actual button.</param>
 /// <param name="isLang">If true, this is tooltip for "search lang"; otherwise, for "script".</param>
 /// <param name="tprov">Localized UI strings provider.</param>
 /// <param name="script">Current search script.</param>
 /// <param name="lang">Current search language.</param>
 /// <param name="needleHeight">Needle's height at today's scaling.</param>
 public SearchOptionsTooltip(ZenGradientButton button, bool isLang, ITextProvider tprov,
                             SearchScript script, SearchLang lang, int needleHeight, int boxRight)
 {
     this.button       = button;
     this.needleHeight = needleHeight;
     this.topOrSide    = -boxRight;
     if (isLang)
     {
         if (lang == SearchLang.Chinese)
         {
             text = tprov.GetString("LangZhoTooltip");
         }
         else
         {
             text = tprov.GetString("LangTrgTooltip");
         }
     }
     else
     {
         if (script == SearchScript.Simplified)
         {
             text = tprov.GetString("ScriptSimpTooltip");
         }
         else if (script == SearchScript.Traditional)
         {
             text = tprov.GetString("ScriptTradTooltip");
         }
         else
         {
             text = tprov.GetString("ScriptBothTooltip");
         }
     }
 }
Пример #4
0
        /// <summary>
        /// Gets the sense formatted in HTML.
        /// </summary>
        public static string GetSenseHtml(CedictSense sense, SearchScript script)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(templateOuter);
            string htmlSense = templateSense.Replace("{sense}", getSenseHtmlPure(null, sense, script));

            htmlSense = templateSenseSingleOpen + htmlSense + templateSenseSingleClose;
            sb.Replace("{body}", htmlSense);
            return(sb.ToString());
        }
Пример #5
0
        private int annotateFrom(MySqlCommand cmdSelBinary10, Dictionary <int, CedictEntry> loadedEntries, byte[] buf,
                                 string query, bool simp, int start, int farthestEnd, List <CedictAnnotation> anns)
        {
            // Get candidates
            List <Index.AnnCandidate> cands = index.GetAnnotationCandidates(query, simp, start, farthestEnd);

            if (cands.Count == 0)
            {
                return(-1);
            }
            // Try to verify longest candidates, then increasingly shorter ones
            int cix = 0;

            for (int length = cands[0].Length; length > 0; --length)
            {
                List <int> idList = new List <int>();
                while (cix < cands.Count && cands[cix].Length == length)
                {
                    idList.Add(cands[cix].EntryId);
                    ++cix;
                }
                loadMissingEntries(cmdSelBinary10, loadedEntries, buf, idList);
                SearchScript scr      = simp ? SearchScript.Simplified : SearchScript.Traditional;
                string       expected = query.Substring(start, length);
                bool         foundAny = false;
                foreach (int id in idList)
                {
                    CedictEntry entry = loadedEntries[id];
                    string      hw    = simp ? entry.ChSimpl : entry.ChTrad;
                    if (hw == expected)
                    {
                        CedictAnnotation ann = new CedictAnnotation(id, entry, scr, start, length);
                        anns.Add(ann);
                        foundAny = true;
                    }
                }
                if (foundAny)
                {
                    return(start + length);
                }
            }
            return(-1);
        }
Пример #6
0
        /// <summary>
        /// Event handler: script selector button clicked.
        /// </summary>
        private void onSimpTrad(ZenControlBase sender)
        {
            // Next in row
            int scri = (int)searchScript;

            ++scri;
            if (scri > 2)
            {
                scri = 0;
            }
            searchScript = (SearchScript)scri;
            // Update button
            simpTradChanged();
            // Change character picker's font
            // Only if it really changes - triggers calibration
            ctrlCharPicker.FontScript = searchScript == SearchScript.Traditional ? IdeoScript.Trad : IdeoScript.Simp;
            // Re-recognize strokes, if there are any
            startNewCharRecog(writingPad.Strokes);
        }
Пример #7
0
        /// <summary>
        /// Verifies which candidates really fit query's indicated substring.
        /// </summary>
        private List <CedictAnnotation> doVerifyAnns(BinReader br, string query, bool simp,
                                                     int start, int length, HashSet <int> cands)
        {
            List <CedictAnnotation> vers = new List <CedictAnnotation>();
            SearchScript            scr  = simp ? SearchScript.Simplified : SearchScript.Traditional;
            string expected = query.Substring(start, length);

            foreach (var ix in cands)
            {
                br.Position = ix;
                CedictEntry entry = new CedictEntry(br);
                string      hw    = simp ? entry.ChSimpl : entry.ChTrad;
                if (hw == expected)
                {
                    CedictAnnotation ann = new CedictAnnotation(ix, scr, start, length);
                    vers.Add(ann);
                }
            }
            return(vers);
        }
Пример #8
0
        /// <summary>
        /// Retrieves entries for a Chinese search expression (pinyin vs. hanzi auto-detected)
        /// </summary>
        private List <CedictResult> doChineseLookup(BinReader br, string query, SearchScript script)
        {
            List <CedictResult> res = new List <CedictResult>();

            // If query string has ideographic characters, do hanzi looup
            if (hasIdeo(query))
            {
                res = doHanziLookupHead(br, query, script);
            }
            // Otherwise, do pinyin lookup
            else
            {
                // Parse pinyin query string
                List <PinyinSyllable> sylls = ParsePinyinQuery(query);
                // Lookup
                res = doPinyinLookupHead(br, sylls);
            }
            // Done
            return(res);
        }
Пример #9
0
 /// <summary>
 /// Ctor: sets the link's query string based on available information.
 /// </summary>
 /// <param name="simp">Simplified Hanzi, or empty string.</param>
 /// <param name="trad">Traditional Hanzi, or empty string.</param>
 /// <param name="pinyin">Pinyin, or empty string.</param>
 /// <param name="script">Search script (to choose simp/trad Hanzi, if available).</param>
 public LinkArea(string simp, string trad, string pinyin, SearchScript script)
 {
     if (simp == string.Empty && trad != string.Empty)
     {
         simp = trad;
     }
     else if (trad == string.Empty && simp != string.Empty)
     {
         trad = simp;
     }
     // We have hanzi. Use that.
     if (simp != string.Empty)
     {
         QueryString = script == SearchScript.Traditional ? trad : simp;
     }
     // No hanzi. Must have pinyin, use that.
     else
     {
         QueryString = pinyin;
     }
 }
Пример #10
0
        /// <summary>
        /// Ctor: takes data to display.
        /// </summary>
        /// <param name="owner">Zen control that owns me.</param>
        /// <param name="tprov">Localized display text provider.</param>
        /// <param name="lookupThroughLink">Delegate to call when user initiates lookup by clicking on a link.</param>
        /// <param name="getEntry">Delegate to call when an entry must be retrieved (for "copy" context menu).</param>
        /// <param name="entryProvider">Dictionary entry provider.</param>
        /// <param name="cr">The lookup result this control will show.</param>
        /// <param name="maxHeadLength">Longest headword in full results list.</param>
        /// <param name="script">Scripts to show in headword.</param>
        /// <param name="odd">Odd/even position in list, for alternating BG color.</param>
        public OneResultControl(ZenControlBase owner, float scale, ITextProvider tprov,
                                LookupThroughLinkDelegate lookupThroughLink,
                                ParentPaintDelegate parentPaint, GetEntryDelegate getEntry,
                                ICedictEntryProvider entryProvider, CedictResult cr,
                                SearchScript script, bool last)
            : base(owner)
        {
            this.scale             = scale;
            this.tprov             = tprov;
            this.lookupThroughLink = lookupThroughLink;
            this.parentPaint       = parentPaint;
            this.getEntry          = getEntry;
            this.entry             = entryProvider.GetEntry(cr.EntryId);
            this.res            = cr;
            this.analyzedScript = script;
            this.last           = last;

            padLeft   = (int)(5.0F * scale);
            padTop    = (int)(4.0F * scale);
            padBottom = (int)(8.0F * scale);
            padMid    = (int)(20.0F * scale);
            padRight  = (int)(10.0F * scale);
        }
Пример #11
0
        /// <summary>
        /// Gets the entry formatted in HTML.
        /// </summary>
        public static string GetHtml(ITextProvider tprov, CedictEntry entry, SearchScript script)
        {
            StringBuilder bodyHtml = new StringBuilder();

            // Are we showing one or two Hanzi headwords?
            string hanzi1 = script == SearchScript.Traditional ? entry.ChTrad : entry.ChSimpl;
            string hanzi2 = null;

            if (script == SearchScript.Both && entry.ChSimpl != entry.ChTrad)
            {
                hanzi2 = entry.ChTrad;
            }
            // Find simplest possible template, work with that
            // Only one hanzi, no longer than 2 chars, only one sense
            bool mustDoSenses = true;

            if (hanzi2 == null && hanzi1.Length <= 2 && entry.SenseCount == 1)
            {
                mustDoSenses = false;
                bodyHtml.Append(template1);
                bodyHtml.Replace("{hanzi}", escape(hanzi1));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
                bodyHtml.Replace("{sense}", getSenseHtmlPure(tprov, entry.GetSenseAt(0), script));
            }
            // Only one script, no more than 6 chars
            else if (hanzi2 == null && hanzi1.Length <= 6)
            {
                bodyHtml.Append(template2);
                bodyHtml.Replace("{hanzi}", escape(hanzi1));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
            }
            // Only one script
            else if (hanzi2 == null)
            {
                bodyHtml.Append(template3);
                bodyHtml.Replace("{hanzi}", escape(hanzi1));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
            }
            // Everything else: very full-fledged entry
            else
            {
                bodyHtml.Append(template4);
                bodyHtml.Replace("{hanzi1}", escape(hanzi1));
                bodyHtml.Replace("{hanzi2}", escape(hanzi2));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
            }
            // In all but the first, simplest case, dealing with senses is the same
            if (mustDoSenses)
            {
                StringBuilder sbSenses = new StringBuilder();
                foreach (CedictSense sense in entry.Senses)
                {
                    string senseHtml = "";
                    if (!sense.Domain.EqualsPlainText("CL:"))
                    {
                        senseHtml += templateDiamond;
                        senseHtml += " ";
                    }
                    senseHtml += getSenseHtmlPure(tprov, sense, script);
                    senseHtml  = templateSense.Replace("{sense}", senseHtml);
                    sbSenses.Append(senseHtml);
                }
                bodyHtml.Replace("{senses}", sbSenses.ToString());
            }

            // Assemble the whole HTML
            StringBuilder sb = new StringBuilder();

            sb.Append(templateOuter);
            sb.Replace("{body}", bodyHtml.ToString());
            // Purge new lines and tabs: this avoids extra spaces e.g. when pasting into Word
            sb.Replace("\r\n", "");
            sb.Replace("\t", "");
            // Done
            return(sb.ToString());
        }
Пример #12
0
        /// <summary>
        /// Retrieves entries (sorted) whose headword contains hanzi from search expression.
        /// </summary>
        List<CedictResult> doHanziLookupHead(BinReader br, string query, SearchScript script)
        {
            // Get every character once - we ignore repeats
            HashSet<char> queryChars = new HashSet<char>();
            foreach (char c in query) queryChars.Add(c);
            // Map from keys (entry positions) to # of query chars found in entry
            Dictionary<int, int> posToCountSimp = new Dictionary<int, int>();
            Dictionary<int, int> posToCountTrad = new Dictionary<int, int>();
            // Look at each character's entry position vector, increment counts
            foreach (char c in queryChars)
            {
                // If there's a hanzi that's not in index, we'll sure have not hits!
                if (!index.IdeoIndex.ContainsKey(c))
                    return new List<CedictResult>();

                IdeoIndexItem iii = index.IdeoIndex[c];
                // Count separately for simplified and traditional
                foreach (int pos in iii.EntriesHeadwordSimp)
                {
                    if (posToCountSimp.ContainsKey(pos)) ++posToCountSimp[pos];
                    else posToCountSimp[pos] = 1;
                }
                foreach (int pos in iii.EntriesHeadwordTrad)
                {
                    if (posToCountTrad.ContainsKey(pos)) ++posToCountTrad[pos];
                    else posToCountTrad[pos] = 1;
                }
            }
            // Get positions that contain all chars from query
            HashSet<int> matchingPositions = new HashSet<int>();
            foreach (var x in posToCountSimp) if (x.Value == queryChars.Count) matchingPositions.Add(x.Key);
            foreach (var x in posToCountTrad) if (x.Value == queryChars.Count) matchingPositions.Add(x.Key);
            // Now fetch and verify results
            List<ResWithEntry> resWE = doLoadVerifyHanzi(br, matchingPositions, query, script);
            // Sort hanzi results
            resWE.Sort((a, b) => hrComp(a, b));
            // Done.
            List<CedictResult> res = new List<CedictResult>(resWE.Capacity);
            for (int i = 0; i != resWE.Count; ++i) res.Add(resWE[i].Res);
            return res;
        }
Пример #13
0
        /// <summary>
        /// Retrieves hanzi lookup candidates, verifies actual presence of search expression in headword.
        /// </summary>
        List <ResWithEntry> doLoadVerifyHanzi(BinReader br, IEnumerable <int> poss, string query, SearchScript script)
        {
            List <ResWithEntry> resList = new List <ResWithEntry>();

            // Yes, we only open our file on-demand
            // But we do this within each lookup's scope, so lookup stays thread-safe
            // Look at each entry: load, verify, keep or drop
            foreach (int pos in poss)
            {
                // Load up entry from file
                br.Position = pos;
                CedictEntry entry = new CedictEntry(br);

                // Figure out position/length of query string in simplified and traditional headwords
                int hiliteStart  = -1;
                int hiliteLength = 0;
                hiliteStart = entry.ChSimpl.IndexOf(query);
                if (hiliteStart != -1)
                {
                    hiliteLength = query.Length;
                }
                // If not found in simplified, check in traditional
                if (hiliteLength == 0)
                {
                    hiliteStart = entry.ChTrad.IndexOf(query);
                    if (hiliteStart != -1)
                    {
                        hiliteLength = query.Length;
                    }
                }
                // Entry is a keeper if either source or target headword contains query
                if (hiliteLength != 0)
                {
                    // Drop if there's any unprintable hanzi
                    if (!areHanziCovered(entry))
                    {
                        continue;
                    }

                    // TO-DO: indicate wrong script in result
                    CedictResult res = new CedictResult(CedictResult.SimpTradWarning.None,
                                                        pos, entry.HanziPinyinMap,
                                                        hiliteStart, hiliteLength);
                    ResWithEntry resWE = new ResWithEntry(res, entry);
                    resList.Add(resWE);
                }
            }
            return(resList);
        }
Пример #14
0
        /// <summary>
        /// Retrieves entries (sorted) whose headword contains hanzi from search expression.
        /// </summary>
        List <CedictResult> doHanziLookupHead(BinReader br, string query, SearchScript script)
        {
            // Get every character once - we ignore repeats
            HashSet <char> queryChars = new HashSet <char>();

            foreach (char c in query)
            {
                queryChars.Add(c);
            }
            // Map from keys (entry positions) to # of query chars found in entry
            Dictionary <int, int> posToCountSimp = new Dictionary <int, int>();
            Dictionary <int, int> posToCountTrad = new Dictionary <int, int>();

            // Look at each character's entry position vector, increment counts
            foreach (char c in queryChars)
            {
                // If there's a hanzi that's not in index, we'll sure have not hits!
                if (!index.IdeoIndex.ContainsKey(c))
                {
                    return(new List <CedictResult>());
                }

                IdeoIndexItem iii = index.IdeoIndex[c];
                // Count separately for simplified and traditional
                foreach (var iep in iii.EntriesHeadwordSimp)
                {
                    if (posToCountSimp.ContainsKey(iep.EntryIdx))
                    {
                        ++posToCountSimp[iep.EntryIdx];
                    }
                    else
                    {
                        posToCountSimp[iep.EntryIdx] = 1;
                    }
                }
                foreach (var iep in iii.EntriesHeadwordTrad)
                {
                    if (posToCountTrad.ContainsKey(iep.EntryIdx))
                    {
                        ++posToCountTrad[iep.EntryIdx];
                    }
                    else
                    {
                        posToCountTrad[iep.EntryIdx] = 1;
                    }
                }
            }
            // Get positions that contain all chars from query
            HashSet <int> matchingPositions = new HashSet <int>();

            foreach (var x in posToCountSimp)
            {
                if (x.Value == queryChars.Count)
                {
                    matchingPositions.Add(x.Key);
                }
            }
            foreach (var x in posToCountTrad)
            {
                if (x.Value == queryChars.Count)
                {
                    matchingPositions.Add(x.Key);
                }
            }
            // Now fetch and verify results
            List <ResWithEntry> resWE = doLoadVerifyHanzi(br, matchingPositions, query, script);

            // Sort hanzi results
            resWE.Sort((a, b) => hrComp(a, b));
            // Done.
            List <CedictResult> res = new List <CedictResult>(resWE.Capacity);

            for (int i = 0; i != resWE.Count; ++i)
            {
                res.Add(resWE[i].Res);
            }
            return(res);
        }
Пример #15
0
 /// <summary>
 /// Event handler: script selector button clicked.
 /// </summary>
 private void onSimpTrad(ZenControlBase sender)
 {
     // Next in row
     int scri = (int)searchScript;
     ++scri;
     if (scri > 2) scri = 0;
     searchScript = (SearchScript)scri;
     // Update button
     simpTradChanged();
     // Change character picker's font
     // Only if it really changes - triggers calibration
     ctrlCharPicker.FontScript = searchScript == SearchScript.Traditional ? IdeoScript.Trad : IdeoScript.Simp;
     // Re-recognize strokes, if there are any
     startNewCharRecog(writingPad.Strokes);
 }
Пример #16
0
 /// <summary>
 /// Initializes tooltip provider for "search language" or "script" button.
 /// </summary>
 /// <param name="button">The actual button.</param>
 /// <param name="isLang">If true, this is tooltip for "search lang"; otherwise, for "script".</param>
 /// <param name="tprov">Localized UI strings provider.</param>
 /// <param name="script">Current search script.</param>
 /// <param name="lang">Current search language.</param>
 /// <param name="needleHeight">Needle's height at today's scaling.</param>
 public SearchOptionsTooltip(ZenGradientButton button, bool isLang, ITextProvider tprov,
     SearchScript script, SearchLang lang, int needleHeight, int boxRight)
 {
     this.button = button;
     this.needleHeight = needleHeight;
     this.topOrSide = -boxRight;
     if (isLang)
     {
         if (lang == SearchLang.Chinese) text = tprov.GetString("LangZhoTooltip");
         else text = tprov.GetString("LangTrgTooltip");
     }
     else
     {
         if (script == SearchScript.Simplified) text = tprov.GetString("ScriptSimpTooltip");
         else if (script == SearchScript.Traditional) text = tprov.GetString("ScriptTradTooltip");
         else text = tprov.GetString("ScriptBothTooltip");
     }
 }
Пример #17
0
        /// <summary>
        /// Ctor: init.
        /// </summary>
        /// <param name="cmdTriggered">Delegate that will be called when a command is issued.</param>
        /// <param name="entry">Cedict entry to fetch clipboard data from.</param>
        /// <param name="senseIX">Index of sense over which user right-clicked, or -1.</param>
        /// <param name="script">Search script (so two Hanzi items are shown if needed).</param>
        public ResultsCtxtControl(CommandTriggeredDelegate cmdTriggered, ITextProvider tprov,
            CedictEntry entry,
            int senseIx,
            SearchScript script)
        {
            this.cmdTriggered = cmdTriggered;
            this.tprov = tprov;
            this.entry = entry;
            this.senseIx = senseIx;
            this.script = script;
            InitializeComponent();
            BackColor = ZenParams.BorderColor;
            pnlTop.BackColor = ZenParams.WindowColor;
            tblFull.BackColor = ZenParams.WindowColor;
            tblZho.BackColor = ZenParams.WindowColor;
            tblSense.BackColor = ZenParams.WindowColor;

            // Display strings
            string title = tprov.GetString("CtxtCopyTitle");
            string fullFormatted, fullCedict, hanzi1, hanzi2, pinyin, sense;
            getDisplayStrings(tprov, senseIx, out fullFormatted, out fullCedict,
                out hanzi1, out hanzi2, out pinyin, out sense);
            lblFullFormatted.Text = fullFormatted;
            lblFullCedict.Text = fullCedict;
            lblHanzi1.Text = hanzi1;
            lblHanzi2.Text = hanzi2;
            lblPinyin.Text = pinyin;
            lblSense.Text = sense;

            // Margin/border tweaks: 1px also at higher DPIs
            tblLayout.Location = new Point(1, 1);
            pnlTop.Margin = new Padding(0, 0, 0, 1);
            tblLayout.RowStyles[1].Height = pnlTop.Height + 1;
            tblFull.Margin = new Padding(0, 0, 0, 1);
            tblLayout.RowStyles[1].Height = tblFull.Height + 1;
            tblZho.Margin = new Padding(0, 0, 0, 1);
            tblLayout.RowStyles[2].Height = tblZho.Height + 1;
            tblSense.Margin = new Padding(0, 0, 0, 0);
            tblLayout.RowStyles[3].Height = tblSense.Height;
            tblLayout.Height = tblSense.Bottom;

            // Hide rows we don't need: second hanzi
            if (hanzi2 == null)
            {
                int hHanzi2 = lblHanzi2.Height;
                tblZho.Controls.Remove(lblHanzi2);
                lblPinyin.Top = lblHanzi2.Top;
                lblHanzi2.Dispose();
                lblHanzi2 = null;
                tblZho.Controls.Remove(lblPinyin);
                tblZho.Controls.Add(lblPinyin, 0, 2);
                tblZho.RowCount -= 1;
                tblZho.RowStyles.RemoveAt(2);
                tblZho.Height -= hHanzi2;
                tblLayout.RowStyles[2].Height -= hHanzi2;
                tblLayout.Height -= hHanzi2;
            }
            // Sense
            if (sense == null)
            {
                int hSense = tblSense.Height;
                tblLayout.Controls.Remove(tblSense);
                tblSense.Dispose();
                tblSense = null;
                tblLayout.RowStyles.RemoveAt(tblLayout.RowStyles.Count - 1);
                tblLayout.RowCount -= 1;
                tblLayout.Height -= hSense + 1;
            }

            // Label collection for hover
            int lblCount = 6;
            if (lblHanzi2 == null) --lblCount;
            if (lblSense == null) --lblCount;
            lblColl = new Label[lblCount];
            lblColl[0] = lblFullFormatted;
            lblColl[1] = lblFullCedict;
            lblColl[2] = lblHanzi1;
            int ix = 3;
            if (lblHanzi2 != null) { lblColl[ix] = lblHanzi2; ++ix; }
            lblColl[ix] = lblPinyin; ++ix;
            if (lblSense != null) { lblColl[ix] = lblSense; ++ix; }

            // Event handling for hover
            tblFull.CellPaint += onTblLayoutCellPaint;
            tblZho.CellPaint += onTblLayoutCellPaint;
            if (tblSense != null) tblSense.CellPaint += onTblLayoutCellPaint;
        }
Пример #18
0
        /// <summary>
        /// Gets the HTML for a single sense, not including enclosing paragraph etc., only inline markup.
        /// </summary>
        /// <param name="tprov">Text provider if meta-labels (e.g. "Classifier") are to be included. If null, they are stripped.</param>
        private static string getSenseHtmlPure(ITextProvider tprov, CedictSense sense, SearchScript script)
        {
            StringBuilder sb = new StringBuilder();

            string strDomain = HybridToHtml(sense.Domain, script);
            string strEquiv = HybridToHtml(sense.Equiv, script);
            string strNote = HybridToHtml(sense.Note, script);
            if (sense.Domain != HybridText.Empty)
            {
                if (sense.Domain.EqualsPlainText("CL:"))
                {
                    if (tprov != null)
                    {
                        sb.Append(templateItalicsOpen);
                        sb.Append(escape(tprov.GetString("ResultCtrlClassifier")) + " ");
                        sb.Append(templateItalicsClose);
                    }
                }
                else
                {
                    sb.Append(templateItalicsOpen);
                    sb.Append(strDomain);
                    sb.Append(templateItalicsClose);
                }
            }
            if (sense.Domain != HybridText.Empty && !sense.Domain.EqualsPlainText("CL:"))
                if (sense.Equiv != HybridText.Empty || sense.Note != HybridText.Empty)
                    sb.Append(' ');
            sb.Append(strEquiv);
            if (sense.Equiv != HybridText.Empty && sense.Note != HybridText.Empty)
                sb.Append(' ');
            if (sense.Note != HybridText.Empty)
            {
                sb.Append(templateItalicsOpen);
                sb.Append(strNote);
                sb.Append(templateItalicsClose);
            }

            // Done
            return sb.ToString();
        }
Пример #19
0
        /// <summary>
        /// Converts a hybrid text to HTML (marking up hanzi+pinyin sections).
        /// </summary>
        public static string HybridToHtml(HybridText ht, SearchScript script)
        {
            StringBuilder sb = new StringBuilder();

            bool first = true;
            for (int i = 0; i != ht.RunCount; ++i)
            {
                TextRun tr = ht.GetRunAt(i);
                if (tr is TextRunLatin)
                {
                    string strRun = tr.GetPlainText();
                    if (!first && strRun != string.Empty && !char.IsPunctuation(strRun[0])) sb.Append(' ');
                    sb.Append(strRun);
                }
                else
                {
                    if (!first) sb.Append(' ');
                    TextRunZho trz = tr as TextRunZho;

                    string hanzi1 = (script == SearchScript.Traditional) ? trz.Trad : trz.Simp;
                    if (string.IsNullOrEmpty(hanzi1)) hanzi1 = null;
                    string hanzi2 = null;
                    if (hanzi1 != null && script == SearchScript.Both && !string.IsNullOrEmpty(trz.Trad))
                        hanzi2 = trz.Trad;
                    if (hanzi1 != null) hanzi1 = escape(hanzi1);
                    if (hanzi2 != null) hanzi2 = escape(hanzi2);

                    if (hanzi1 != null || hanzi2 != null) sb.Append(templateSenseHanziOpen);
                    if (hanzi1 != null) sb.Append(hanzi1);
                    if (hanzi2 != null)
                    {
                        sb.Append(' ');
                        sb.Append(templateBullet);
                        sb.Append(' ');
                        sb.Append(hanzi2);
                    }
                    if (hanzi1 != null || hanzi2 != null) sb.Append(templateSenseHanziClose);

                    if (trz.Pinyin != null)
                    {
                        if (hanzi1 != null) sb.Append(' ');
                        sb.Append('[');
                        sb.Append(escape(trz.GetPinyinInOne(true)));
                        sb.Append(']');
                    }
                }
                first = false;
            }
            return sb.ToString();
        }
Пример #20
0
        /// <summary>
        /// Gets the sense formatted in HTML.
        /// </summary>
        public static string GetSenseHtml(CedictSense sense, SearchScript script)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(templateOuter);
            string htmlSense = templateSense.Replace("{sense}", getSenseHtmlPure(null, sense, script));
            htmlSense = templateSenseSingleOpen + htmlSense + templateSenseSingleClose;
            sb.Replace("{body}", htmlSense);
            return sb.ToString();
        }
Пример #21
0
        /// <summary>
        /// Find entries that match the search expression.
        /// </summary>
        /// <param name="query">The query string, as entered by the user.</param>
        /// <param name="script">For hanzi lookup: simplified, traditional or both.</param>
        /// <param name="lang">Chinese or target language (English).</param>
        /// <returns>The lookup result.</returns>
        public CedictLookupResult Lookup(string query, SearchScript script, SearchLang lang)
        {
            List<CedictResult> res = new List<CedictResult>();
            // BinReader: I own it until I successfully return results to caller.
            BinReader br = new BinReader(dictFileName);
            EntryProvider ep = new EntryProvider(br);

            try
            {
                // Try first in language requested by user
                // If no results that way, try in opposite language
                // Override if lookup in opposite language is successful
                if (lang == SearchLang.Chinese)
                {
                    res = doChineseLookup(br, query, script);
                    // We got fish
                    if (res.Count > 0)
                        return new CedictLookupResult(ep, new ReadOnlyCollection<CedictResult>(res), lang);
                    // OK, try opposite (target)
                    res = doTargetLookup(br, query);
                    // We got fish: override
                    if (res.Count > 0)
                        return new CedictLookupResult(ep, new ReadOnlyCollection<CedictResult>(res), SearchLang.Target);
                }
                else
                {
                    res = doTargetLookup(br, query);
                    // We got fish
                    if (res.Count > 0)
                        return new CedictLookupResult(ep, new ReadOnlyCollection<CedictResult>(res), lang);
                    // OK, try opposite (target)
                    res = doChineseLookup(br, query, script);
                    // We got fish: override
                    if (res.Count > 0)
                        return new CedictLookupResult(ep, new ReadOnlyCollection<CedictResult>(res), SearchLang.Chinese);
                }
                // Sorry, no results, no override
                return new CedictLookupResult(ep, new ReadOnlyCollection<CedictResult>(res), lang);
            }
            catch
            {
                br.Dispose();
                throw;
            }
        }
Пример #22
0
        /// <summary>
        /// Retrieves hanzi lookup candidates, verifies actual presence of search expression in headword.
        /// </summary>
        List<ResWithEntry> doLoadVerifyHanzi(BinReader br, IEnumerable<int> poss, string query, SearchScript script)
        {
            List<ResWithEntry> resList = new List<ResWithEntry>();
            // Yes, we only open our file on-demand
            // But we do this within each lookup's scope, so lookup stays thread-safe
            // Look at each entry: load, verify, keep or drop
            foreach (int pos in poss)
            {
                // Load up entry from file
                br.Position = pos;
                CedictEntry entry = new CedictEntry(br);

                // Figure out position/length of query string in simplified and traditional headwords
                int hiliteStart = -1;
                int hiliteLength = 0;
                hiliteStart = entry.ChSimpl.IndexOf(query);
                if (hiliteStart != -1) hiliteLength = query.Length;
                // If not found in simplified, check in traditional
                if (hiliteLength == 0)
                {
                    hiliteStart = entry.ChTrad.IndexOf(query);
                    if (hiliteStart != -1) hiliteLength = query.Length;
                }
                // Entry is a keeper if either source or target headword contains query
                if (hiliteLength != 0)
                {
                    // Drop if there's any unprintable hanzi
                    if (!areHanziCovered(entry)) continue;

                    // TO-DO: indicate wrong script in result
                    CedictResult res = new CedictResult(CedictResult.SimpTradWarning.None,
                        pos, entry.HanziPinyinMap,
                        hiliteStart, hiliteLength);
                    ResWithEntry resWE = new ResWithEntry(res, entry);
                    resList.Add(resWE);
                }
            }
            return resList;
        }
Пример #23
0
 /// <summary>
 /// Displays the received results, discarding existing data.
 /// </summary>
 /// <param name="lookupId">ID of lookup whose results are shown. If ID is smaller than last seen value, we don't show results.</param>
 /// <param name="entryProvider">The entry provider; ownership passed by caller to me.</param>
 /// <param name="results">Cedict lookup results to show.</param>
 /// <param name="script">Defines which script(s) to show.</param>
 /// <returns>True if results got shown; false if they're discarded because newer results are already on display.</returns>
 public bool SetResults(int lookupId,
     ICedictEntryProvider entryProvider,
     ReadOnlyCollection<CedictResult> results,
     SearchScript script)
 {
     #if DEBUG
     // Make us crash at bottom if first result "柏林" (comes up for "bolin")
     if (results.Count > 0)
     {
         CedictEntry entry = entryProvider.GetEntry(results[0].EntryId);
         if (entry.ChSimpl == "柏林") crashForTest = true;
         else crashForTest = false;
     }
     #endif
     try
     {
         return doSetResults(lookupId, entryProvider, results, script);
     }
     finally
     {
         if (entryProvider != null) entryProvider.Dispose();
     }
 }
Пример #24
0
        /// <summary>
        /// Gets the entry formatted in HTML.
        /// </summary>
        public static string GetHtml(ITextProvider tprov, CedictEntry entry, SearchScript script)
        {
            StringBuilder bodyHtml = new StringBuilder();

            // Are we showing one or two Hanzi headwords?
            string hanzi1 = script == SearchScript.Traditional ? entry.ChTrad : entry.ChSimpl;
            string hanzi2 = null;
            if (script == SearchScript.Both && entry.ChSimpl != entry.ChTrad)
                hanzi2 = entry.ChTrad;
            // Find simplest possible template, work with that
            // Only one hanzi, no longer than 2 chars, only one sense
            bool mustDoSenses = true;
            if (hanzi2 == null && hanzi1.Length <= 2 && entry.SenseCount == 1)
            {
                mustDoSenses = false;
                bodyHtml.Append(template1);
                bodyHtml.Replace("{hanzi}", escape(hanzi1));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
                bodyHtml.Replace("{sense}", getSenseHtmlPure(tprov, entry.GetSenseAt(0), script));
            }
            // Only one script, no more than 6 chars
            else if (hanzi2 == null && hanzi1.Length <= 6)
            {
                bodyHtml.Append(template2);
                bodyHtml.Replace("{hanzi}", escape(hanzi1));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
            }
            // Only one script
            else if (hanzi2 == null)
            {
                bodyHtml.Append(template3);
                bodyHtml.Replace("{hanzi}", escape(hanzi1));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
            }
            // Everything else: very full-fledged entry
            else
            {
                bodyHtml.Append(template4);
                bodyHtml.Replace("{hanzi1}", escape(hanzi1));
                bodyHtml.Replace("{hanzi2}", escape(hanzi2));
                bodyHtml.Replace("{pinyin}", escape(GetPinyinString(entry.GetPinyinForDisplay(true))));
            }
            // In all but the first, simplest case, dealing with senses is the same
            if (mustDoSenses)
            {
                StringBuilder sbSenses = new StringBuilder();
                foreach (CedictSense sense in entry.Senses)
                {
                    string senseHtml = "";
                    if (!sense.Domain.EqualsPlainText("CL:"))
                    {
                        senseHtml += templateDiamond;
                        senseHtml += " ";
                    }
                    senseHtml += getSenseHtmlPure(tprov, sense, script);
                    senseHtml = templateSense.Replace("{sense}", senseHtml);
                    sbSenses.Append(senseHtml);
                }
                bodyHtml.Replace("{senses}", sbSenses.ToString());
            }

            // Assemble the whole HTML
            StringBuilder sb = new StringBuilder();
            sb.Append(templateOuter);
            sb.Replace("{body}", bodyHtml.ToString());
            // Purge new lines and tabs: this avoids extra spaces e.g. when pasting into Word
            sb.Replace("\r\n", "");
            sb.Replace("\t", "");
            // Done
            return sb.ToString();
        }
Пример #25
0
        /// <summary>
        /// See <see cref="SetResults"/>.
        /// </summary>
        private bool doSetResults(int lookupId,
                                  ICedictEntryProvider entryProvider,
                                  ReadOnlyCollection <CedictResult> results,
                                  SearchScript script)
        {
            lock (displayIdLO)
            {
                // If we're already too late, don't bother changing display.
                if (displayId > lookupId)
                {
                    return(false);
                }
                displayId = lookupId;
                // Empty result set - special handling
                if (results.Count == 0)
                {
                    lock (resCtrlsLO)
                    {
                        doDisposeResultControls();
                        txtResCount = tprov.GetString("ResultsCountNone");
                        setScrollbarVisibility(false);
                    }
                    // Render
                    doFade(false);
                    MakeMePaint(false, RenderMode.Invalidate);
                    return(true);
                }
            }

            // Decide if we first try with scrollbar visible or not
            // This is a very rough heuristics (10 results or more), but doesn't matter
            // Recalc costs much if there are many results, and the number covers that safely
            bool sbarVisible = results.Count > 10;

            // Content rectangle height and width
            int cw, ch;

            getContentSize(sbarVisible, out cw, out ch);

            // Create new result controls. At this point, not overwriting old ones!
            // This is the cycle that takes *long*.
            List <OneResultControl> newCtrls = new List <OneResultControl>(results.Count);
            int y = 0;

            using (Bitmap bmp = new Bitmap(1, 1))
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    bool canceled = false;
                    for (int rix = 0; rix != results.Count; ++rix)
                    {
                        CedictResult     cr  = results[rix];
                        OneResultControl orc = new OneResultControl(null, Scale, tprov,
                                                                    onLookupFromCtrl, onPaintFromCtrl, onGetEntry,
                                                                    entryProvider, cr, script, rix == results.Count - 1);
                        orc.Analyze(g, cw);
                        // Cannot use RelLocation b/c control has no parent yet
                        orc.AbsLocation = new Point(AbsLeft + 1, AbsTop + y + 1);
                        y += orc.Height;
                        newCtrls.Add(orc);
                        // At any point, if we realize lookup ID has changed, we stop
                        // This can happen if a later, quick lookup completes and shows results before us
                        // Checking integers is atomic, no locking
                        if (displayId > lookupId)
                        {
                            canceled = true; break;
                        }
                    }
                    if (canceled)
                    {
                        foreach (OneResultControl orc in newCtrls)
                        {
                            orc.Dispose();
                        }
                        return(false);
                    }
                }
            // OK, last chance to change our mind about showing results.
            // The rest is synchronized - but it's also fast
            lock (displayIdLO)
            {
                if (displayId > lookupId)
                {
                    return(false);
                }
                displayId = lookupId;
                // Rest must be invoked on GUI. Otherwise, as we're adding children,
                // Collections are modified that are also accessed by paint in a resize event handler etc.
                InvokeOnForm((MethodInvoker) delegate
                {
                    // Stop any scrolling that may be going on. Cannot scroll what's being replaced.
                    if (sbar.Parent == this)
                    {
                        sbar.StopAnyScrolling();
                    }
                    // Prevent any painting from worker threads - also accesses collection we're changing
                    lock (resCtrlsLO)
                    {
                        // Get rid of old result controls, remember/own new ones
                        doDisposeResultControls();
                        resCtrls = newCtrls;
                        foreach (OneResultControl orc in resCtrls)
                        {
                            AddChild(orc);
                        }
                        // Actually show or hide scrollbar as per original decision
                        setScrollbarVisibility(sbarVisible);
                        // Now, by the time we're here, size may have changed
                        // That is unlikely, but then we got to re-layout stuff
                        int cwNew, chNew;
                        getContentSize(sbarVisible, out cwNew, out chNew);
                        if (cwNew != cw || chNew != ch)
                        {
                            reAnalyzeResultsDisplay();
                        }
                        else
                        {
                            // Everything as big as it used to be...
                            // Change our mind about scrollbar?
                            cw = showOrHideScrollbar();
                        }
                    }
                    // Results count text
                    if (resCtrls.Count == 1)
                    {
                        txtResCount = tprov.GetString("ResultsCountOne");
                    }
                    else
                    {
                        txtResCount = tprov.GetString("ResultsCountN");
                        txtResCount = string.Format(txtResCount, resCtrls.Count);
                    }
                    // Update first visible control's index
                    updateFirstVisibleIdx();
                    // Render
                    doFade(false);
                    MakeMePaint(false, RenderMode.Invalidate);
                });
                // Done.
                return(true);
            }
        }
Пример #26
0
        /// <summary>
        /// Find entries that match the search expression.
        /// </summary>
        /// <param name="query">The query string, as entered by the user.</param>
        /// <param name="script">For hanzi lookup: simplified, traditional or both.</param>
        /// <param name="lang">Chinese or target language (English).</param>
        /// <returns>The lookup result.</returns>
        public CedictLookupResult Lookup(string query, SearchScript script, SearchLang lang)
        {
            List <CedictResult>     res  = new List <CedictResult>();
            List <CedictAnnotation> anns = new List <CedictAnnotation>();
            // BinReader: I own it until I successfully return results to caller.
            BinReader     br = new BinReader(dictFileName);
            EntryProvider ep = new EntryProvider(br);

            try
            {
                // Try first in language requested by user
                // If no results that way, try in opposite language
                // Override if lookup in opposite language is successful
                if (lang == SearchLang.Chinese)
                {
                    res = doChineseLookup(br, query, script);
                    // We got fish
                    if (res.Count > 0)
                    {
                        return(new CedictLookupResult(ep, query, res, anns, lang));
                    }
                    // Try to annotate
                    anns = doAnnotate(br, query);
                    if (anns.Count > 0)
                    {
                        return(new CedictLookupResult(ep, query, res, anns, lang));
                    }
                    // OK, try opposite (target)
                    res = doTargetLookup(br, query);
                    // We got fish: override
                    if (res.Count > 0)
                    {
                        return(new CedictLookupResult(ep, query, res, anns, SearchLang.Target));
                    }
                }
                else
                {
                    res = doTargetLookup(br, query);
                    // We got fish
                    if (res.Count > 0)
                    {
                        return(new CedictLookupResult(ep, query, res, anns, lang));
                    }
                    // OK, try opposite (target)
                    res = doChineseLookup(br, query, script);
                    // We got fish: override
                    if (res.Count > 0)
                    {
                        return(new CedictLookupResult(ep, query, res, anns, SearchLang.Chinese));
                    }
                    // Try to annotate
                    anns = doAnnotate(br, query);
                    if (anns.Count > 0)
                    {
                        return(new CedictLookupResult(ep, query, res, anns, SearchLang.Chinese));
                    }
                }
                // Sorry, no results, no override
                return(new CedictLookupResult(ep, query, res, anns, lang));
            }
            catch
            {
                br.Dispose();
                throw;
            }
        }
Пример #27
0
        /// <summary>
        /// Ctor: init.
        /// </summary>
        /// <param name="cmdTriggered">Delegate that will be called when a command is issued.</param>
        /// <param name="entry">Cedict entry to fetch clipboard data from.</param>
        /// <param name="senseIX">Index of sense over which user right-clicked, or -1.</param>
        /// <param name="script">Search script (so two Hanzi items are shown if needed).</param>
        public ResultsCtxtControl(CommandTriggeredDelegate cmdTriggered, ITextProvider tprov,
                                  CedictEntry entry,
                                  int senseIx,
                                  SearchScript script)
        {
            this.cmdTriggered = cmdTriggered;
            this.tprov        = tprov;
            this.entry        = entry;
            this.senseIx      = senseIx;
            this.script       = script;
            InitializeComponent();
            BackColor          = ZenParams.BorderColor;
            pnlTop.BackColor   = ZenParams.WindowColor;
            tblFull.BackColor  = ZenParams.WindowColor;
            tblZho.BackColor   = ZenParams.WindowColor;
            tblSense.BackColor = ZenParams.WindowColor;

            // Display strings
            string title = tprov.GetString("CtxtCopyTitle");
            string fullFormatted, fullCedict, hanzi1, hanzi2, pinyin, sense;

            getDisplayStrings(tprov, senseIx, out fullFormatted, out fullCedict,
                              out hanzi1, out hanzi2, out pinyin, out sense);
            lblFullFormatted.Text = fullFormatted;
            lblFullCedict.Text    = fullCedict;
            lblHanzi1.Text        = hanzi1;
            lblHanzi2.Text        = hanzi2;
            lblPinyin.Text        = pinyin;
            lblSense.Text         = sense;

            // Margin/border tweaks: 1px also at higher DPIs
            tblLayout.Location            = new Point(1, 1);
            pnlTop.Margin                 = new Padding(0, 0, 0, 1);
            tblLayout.RowStyles[1].Height = pnlTop.Height + 1;
            tblFull.Margin                = new Padding(0, 0, 0, 1);
            tblLayout.RowStyles[1].Height = tblFull.Height + 1;
            tblZho.Margin                 = new Padding(0, 0, 0, 1);
            tblLayout.RowStyles[2].Height = tblZho.Height + 1;
            tblSense.Margin               = new Padding(0, 0, 0, 0);
            tblLayout.RowStyles[3].Height = tblSense.Height;
            tblLayout.Height              = tblSense.Bottom;

            // Hide rows we don't need: second hanzi
            if (hanzi2 == null)
            {
                int hHanzi2 = lblHanzi2.Height;
                tblZho.Controls.Remove(lblHanzi2);
                lblPinyin.Top = lblHanzi2.Top;
                lblHanzi2.Dispose();
                lblHanzi2 = null;
                tblZho.Controls.Remove(lblPinyin);
                tblZho.Controls.Add(lblPinyin, 0, 2);
                tblZho.RowCount -= 1;
                tblZho.RowStyles.RemoveAt(2);
                tblZho.Height -= hHanzi2;
                tblLayout.RowStyles[2].Height -= hHanzi2;
                tblLayout.Height -= hHanzi2;
            }
            // Sense
            if (sense == null)
            {
                int hSense = tblSense.Height;
                tblLayout.Controls.Remove(tblSense);
                tblSense.Dispose();
                tblSense = null;
                tblLayout.RowStyles.RemoveAt(tblLayout.RowStyles.Count - 1);
                tblLayout.RowCount -= 1;
                tblLayout.Height   -= hSense + 1;
            }

            // Label collection for hover
            int lblCount = 6;

            if (lblHanzi2 == null)
            {
                --lblCount;
            }
            if (lblSense == null)
            {
                --lblCount;
            }
            lblColl    = new Label[lblCount];
            lblColl[0] = lblFullFormatted;
            lblColl[1] = lblFullCedict;
            lblColl[2] = lblHanzi1;
            int ix = 3;

            if (lblHanzi2 != null)
            {
                lblColl[ix] = lblHanzi2; ++ix;
            }
            lblColl[ix] = lblPinyin; ++ix;
            if (lblSense != null)
            {
                lblColl[ix] = lblSense; ++ix;
            }

            // Event handling for hover
            tblFull.CellPaint += onTblLayoutCellPaint;
            tblZho.CellPaint  += onTblLayoutCellPaint;
            if (tblSense != null)
            {
                tblSense.CellPaint += onTblLayoutCellPaint;
            }
        }
Пример #28
0
        /// <summary>
        /// Ctor: takes data to display.
        /// </summary>
        /// <param name="owner">Zen control that owns me.</param>
        /// <param name="tprov">Localized display text provider.</param>
        /// <param name="lookupThroughLink">Delegate to call when user initiates lookup by clicking on a link.</param>
        /// <param name="getEntry">Delegate to call when an entry must be retrieved (for "copy" context menu).</param>
        /// <param name="entryProvider">Dictionary entry provider.</param>
        /// <param name="cr">The lookup result this control will show.</param>
        /// <param name="maxHeadLength">Longest headword in full results list.</param>
        /// <param name="script">Scripts to show in headword.</param>
        /// <param name="odd">Odd/even position in list, for alternating BG color.</param>
        public OneResultControl(ZenControlBase owner, float scale, ITextProvider tprov,
            LookupThroughLinkDelegate lookupThroughLink,
            ParentPaintDelegate parentPaint, GetEntryDelegate getEntry,
            ICedictEntryProvider entryProvider, CedictResult cr,
            SearchScript script, bool last)
            : base(owner)
        {
            this.scale = scale;
            this.tprov = tprov;
            this.lookupThroughLink = lookupThroughLink;
            this.parentPaint = parentPaint;
            this.getEntry = getEntry;
            this.entry = entryProvider.GetEntry(cr.EntryId);
            this.res = cr;
            this.analyzedScript = script;
            this.last = last;

            padLeft = (int)(5.0F * scale);
            padTop = (int)(4.0F * scale);
            padBottom = (int)(8.0F * scale);
            padMid = (int)(20.0F * scale);
            padRight = (int)(10.0F * scale);
        }
Пример #29
0
        /// <summary>
        /// Ctor.
        /// </summary>
        public LookupControl(ZenControlBase owner, ICedictEngineFactory dictFact, ITextProvider tprov)
            : base(owner)
        {
            this.dictFact = dictFact;
            this.tprov = tprov;
            padding = (int)Math.Round(5.0F * Scale);

            // Init search language and script from user settings
            searchLang = AppSettings.SearchLang;
            searchScript = AppSettings.SearchScript;

            // Init HanziLookup
            fsStrokes = new FileStream(Magic.StrokesFileName, FileMode.Open, FileAccess.Read);
            brStrokes = new BinaryReader(fsStrokes);
            strokesData = new StrokesDataSource(brStrokes);

            // Writing pad
            writingPad = new WritingPad(this, tprov);
            writingPad.RelLocation = new Point(padding, padding);
            writingPad.LogicalSize = new Size(200, 200);
            writingPad.StrokesChanged += writingPad_StrokesChanged;

            // Images for buttons under writing pad; will get owned by buttons, not that it matters.
            Assembly a = Assembly.GetExecutingAssembly();
            var imgStrokesClear = Image.FromStream(a.GetManifestResourceStream("ZD.Gui.Resources.strokes-clear.png"));
            var imgStrokesUndo = Image.FromStream(a.GetManifestResourceStream("ZD.Gui.Resources.strokes-undo.png"));

            // Clear and undo buttons under writing pad.
            float leftBtnWidth = writingPad.Width / 2 + 1;
            float btnHeight = 22.0F * Scale;
            // --
            btnClearWritingPad = new ZenGradientButton(this);
            btnClearWritingPad.RelLocation = new Point(writingPad.RelLeft, writingPad.RelBottom - 1);
            btnClearWritingPad.Size = new Size((int)leftBtnWidth, (int)btnHeight);
            btnClearWritingPad.Text = tprov.GetString("WritingPadClear");
            btnClearWritingPad.SetFont(SystemFontProvider.Instance.GetSystemFont(FontStyle.Regular, 9.0F));
            btnClearWritingPad.Padding = (int)(3.0F * Scale);
            btnClearWritingPad.ImageExtraPadding = (int)(3.0F * Scale);
            btnClearWritingPad.Image = imgStrokesClear;
            btnClearWritingPad.Enabled = false;
            btnClearWritingPad.MouseClick += onClearWritingPad;
            // --
            btnUndoStroke = new ZenGradientButton(this);
            btnUndoStroke.RelLocation = new Point(btnClearWritingPad.RelRight - 1, writingPad.RelBottom - 1);
            btnUndoStroke.Size = new Size(writingPad.RelRight - btnUndoStroke.RelLeft, (int)btnHeight);
            btnUndoStroke.Text = tprov.GetString("WritingPadUndo");
            btnUndoStroke.SetFont(SystemFontProvider.Instance.GetSystemFont(FontStyle.Regular, 9.0F));
            btnUndoStroke.Padding = (int)(3.0F * Scale);
            btnUndoStroke.ImageExtraPadding = (int)(1.5F * Scale);
            btnUndoStroke.Image = imgStrokesUndo;
            btnUndoStroke.Enabled = false;
            btnUndoStroke.MouseClick += onUndoStroke;
            // --
            btnClearWritingPad.Tooltip = new ClearUndoTooltips(btnClearWritingPad, true, tprov, padding);
            btnUndoStroke.Tooltip = new ClearUndoTooltips(btnUndoStroke, false, tprov, padding);
            // --

            // Character picker control under writing pad.
            ctrlCharPicker = new CharPicker(this, tprov);
            ctrlCharPicker.FontFam = Magic.ZhoContentFontFamily;
            ctrlCharPicker.FontScript = searchScript == SearchScript.Traditional ? IdeoScript.Trad : IdeoScript.Simp;
            ctrlCharPicker.RelLocation = new Point(padding, btnClearWritingPad.RelBottom + padding);
            ctrlCharPicker.LogicalSize = new Size(200, 80);
            ctrlCharPicker.CharPicked += onCharPicked;

            // Search input control at top
            ctrlSearchInput = new SearchInputControl(this, tprov);
            ctrlSearchInput.RelLocation = new Point(writingPad.RelRight + padding, padding);
            ctrlSearchInput.StartSearch += onStartSearch;

            // Tweaks for Chinese text on UI buttons
            // This is specific to Segoe UI and Noto Sans S Chinese fonts.
            float ofsZho = 0;
            //if (!(SystemFontProvider.Instance as ZydeoSystemFontProvider).SegoeExists)
            //    ofsZho = Magic.ZhoButtonFontSize * Scale / 3.7F;

            // Script selector button to the right of search input control
            btnSimpTrad = new ZenGradientButton(this);
            btnSimpTrad.RelTop = padding;
            btnSimpTrad.Height = ctrlSearchInput.Height;
            btnSimpTrad.SetFont((SystemFontProvider.Instance as ZydeoSystemFontProvider).GetZhoButtonFont(
                FontStyle.Regular, Magic.ZhoButtonFontSize));
            btnSimpTrad.Width = getSimpTradWidth();
            btnSimpTrad.ForcedCharVertOfs = ofsZho;
            btnSimpTrad.RelLeft = Width - padding - btnSimpTrad.Width;
            btnSimpTrad.Height = ctrlSearchInput.Height;
            btnSimpTrad.MouseClick += onSimpTrad;

            // Search language selector to the right of search input control
            btnSearchLang = new ZenGradientButton(this);
            btnSearchLang.RelTop = padding;
            btnSearchLang.Height = ctrlSearchInput.Height;
            btnSearchLang.SetFont((SystemFontProvider.Instance as ZydeoSystemFontProvider).GetZhoButtonFont(
                FontStyle.Regular, Magic.ZhoButtonFontSize));
            btnSearchLang.Width = getSearchLangWidth();
            btnSearchLang.ForcedCharVertOfs = ofsZho;
            btnSearchLang.RelLeft = btnSimpTrad.RelLeft - padding - btnSearchLang.Width;
            btnSearchLang.Height = ctrlSearchInput.Height;
            btnSearchLang.MouseClick += onSearchLang;

            // Update button texts; do it here so tooltip locations will be correct.
            simpTradChanged();
            searchLangChanged();

            // Lookup results control.
            ctrlResults = new ResultsControl(this, tprov, onLookupThroughLink, onGetEntry);
            ctrlResults.RelLocation = new Point(writingPad.RelRight + padding, ctrlSearchInput.RelBottom + padding);
        }
Пример #30
0
        /// <summary>
        /// Converts a hybrid text to HTML (marking up hanzi+pinyin sections).
        /// </summary>
        public static string HybridToHtml(HybridText ht, SearchScript script)
        {
            StringBuilder sb = new StringBuilder();

            bool first = true;

            for (int i = 0; i != ht.RunCount; ++i)
            {
                TextRun tr = ht.GetRunAt(i);
                if (tr is TextRunLatin)
                {
                    string strRun = tr.GetPlainText();
                    if (!first && strRun != string.Empty && !char.IsPunctuation(strRun[0]))
                    {
                        sb.Append(' ');
                    }
                    sb.Append(strRun);
                }
                else
                {
                    if (!first)
                    {
                        sb.Append(' ');
                    }
                    TextRunZho trz = tr as TextRunZho;

                    string hanzi1 = (script == SearchScript.Traditional) ? trz.Trad : trz.Simp;
                    if (string.IsNullOrEmpty(hanzi1))
                    {
                        hanzi1 = null;
                    }
                    string hanzi2 = null;
                    if (hanzi1 != null && script == SearchScript.Both && !string.IsNullOrEmpty(trz.Trad))
                    {
                        hanzi2 = trz.Trad;
                    }
                    if (hanzi1 != null)
                    {
                        hanzi1 = escape(hanzi1);
                    }
                    if (hanzi2 != null)
                    {
                        hanzi2 = escape(hanzi2);
                    }

                    if (hanzi1 != null || hanzi2 != null)
                    {
                        sb.Append(templateSenseHanziOpen);
                    }
                    if (hanzi1 != null)
                    {
                        sb.Append(hanzi1);
                    }
                    if (hanzi2 != null)
                    {
                        sb.Append(' ');
                        sb.Append(templateBullet);
                        sb.Append(' ');
                        sb.Append(hanzi2);
                    }
                    if (hanzi1 != null || hanzi2 != null)
                    {
                        sb.Append(templateSenseHanziClose);
                    }

                    if (trz.Pinyin != null)
                    {
                        if (hanzi1 != null)
                        {
                            sb.Append(' ');
                        }
                        sb.Append('[');
                        sb.Append(escape(trz.GetPinyinInOne(true)));
                        sb.Append(']');
                    }
                }
                first = false;
            }
            return(sb.ToString());
        }
Пример #31
0
 public LookupItem(int id, string text, SearchScript script, SearchLang lang)
 {
     ID = id; Text = text; Script = script; Lang = lang;
 }
Пример #32
0
        /// <summary>
        /// Gets the HTML for a single sense, not including enclosing paragraph etc., only inline markup.
        /// </summary>
        /// <param name="tprov">Text provider if meta-labels (e.g. "Classifier") are to be included. If null, they are stripped.</param>
        private static string getSenseHtmlPure(ITextProvider tprov, CedictSense sense, SearchScript script)
        {
            StringBuilder sb = new StringBuilder();

            string strDomain = HybridToHtml(sense.Domain, script);
            string strEquiv  = HybridToHtml(sense.Equiv, script);
            string strNote   = HybridToHtml(sense.Note, script);

            if (sense.Domain != HybridText.Empty)
            {
                if (sense.Domain.EqualsPlainText("CL:"))
                {
                    if (tprov != null)
                    {
                        sb.Append(templateItalicsOpen);
                        sb.Append(escape(tprov.GetString("ResultCtrlClassifier")) + " ");
                        sb.Append(templateItalicsClose);
                    }
                }
                else
                {
                    sb.Append(templateItalicsOpen);
                    sb.Append(strDomain);
                    sb.Append(templateItalicsClose);
                }
            }
            if (sense.Domain != HybridText.Empty && !sense.Domain.EqualsPlainText("CL:"))
            {
                if (sense.Equiv != HybridText.Empty || sense.Note != HybridText.Empty)
                {
                    sb.Append(' ');
                }
            }
            sb.Append(strEquiv);
            if (sense.Equiv != HybridText.Empty && sense.Note != HybridText.Empty)
            {
                sb.Append(' ');
            }
            if (sense.Note != HybridText.Empty)
            {
                sb.Append(templateItalicsOpen);
                sb.Append(strNote);
                sb.Append(templateItalicsClose);
            }

            // Done
            return(sb.ToString());
        }
Пример #33
0
 public LookupItem(int id, string text, SearchScript script, SearchLang lang)
 {
     ID = id; Text = text; Script = script; Lang = lang;
 }
Пример #34
0
        /// <summary>
        /// See <see cref="SetResults"/>.
        /// </summary>
        private bool doSetResults(int lookupId,
            ICedictEntryProvider entryProvider,
            ReadOnlyCollection<CedictResult> results,
            SearchScript script)
        {
            lock (displayIdLO)
            {
                // If we're already too late, don't bother changing display.
                if (displayId > lookupId) return false;
                displayId = lookupId;
                // Empty result set - special handling
                if (results.Count == 0)
                {
                    lock (resCtrlsLO)
                    {
                        doDisposeResultControls();
                        txtResCount = tprov.GetString("ResultsCountNone");
                        setScrollbarVisibility(false);
                    }
                    // Render
                    doFade(false);
                    MakeMePaint(false, RenderMode.Invalidate);
                    return true;
                }
            }

            // Decide if we first try with scrollbar visible or not
            // This is a very rough heuristics (10 results or more), but doesn't matter
            // Recalc costs much if there are many results, and the number covers that safely
            bool sbarVisible = results.Count > 10;

            // Content rectangle height and width
            int cw, ch;
            getContentSize(sbarVisible, out cw, out ch);

            // Create new result controls. At this point, not overwriting old ones!
            // This is the cycle that takes *long*.
            List<OneResultControl> newCtrls = new List<OneResultControl>(results.Count);
            int y = 0;
            using (Bitmap bmp = new Bitmap(1, 1))
            using (Graphics g = Graphics.FromImage(bmp))
            {
                bool canceled = false;
                for (int rix = 0; rix != results.Count; ++rix)
                {
                    CedictResult cr = results[rix];
                    OneResultControl orc = new OneResultControl(null, Scale, tprov,
                        onLookupFromCtrl, onPaintFromCtrl, onGetEntry,
                        entryProvider, cr, script, rix == results.Count - 1);
                    orc.Analyze(g, cw);
                    // Cannot use RelLocation b/c control has no parent yet
                    orc.AbsLocation = new Point(AbsLeft + 1, AbsTop + y + 1);
                    y += orc.Height;
                    newCtrls.Add(orc);
                    // At any point, if we realize lookup ID has changed, we stop
                    // This can happen if a later, quick lookup completes and shows results before us
                    // Checking integers is atomic, no locking
                    if (displayId > lookupId) { canceled = true; break; }
                }
                if (canceled)
                {
                    foreach (OneResultControl orc in newCtrls) orc.Dispose();
                    return false;
                }
            }
            // OK, last chance to change our mind about showing results.
            // The rest is synchronized - but it's also fast
            lock (displayIdLO)
            {
                if (displayId > lookupId) return false;
                displayId = lookupId;
                // Rest must be invoked on GUI. Otherwise, as we're adding children,
                // Collections are modified that are also accessed by paint in a resize event handler etc.
                InvokeOnForm((MethodInvoker)delegate
                {
                    // Stop any scrolling that may be going on. Cannot scroll what's being replaced.
                    if (sbar.Parent == this) sbar.StopAnyScrolling();
                    // Prevent any painting from worker threads - also accesses collection we're changing
                    lock (resCtrlsLO)
                    {
                        // Get rid of old result controls, remember/own new ones
                        doDisposeResultControls();
                        resCtrls = newCtrls;
                        foreach (OneResultControl orc in resCtrls) AddChild(orc);
                        // Actually show or hide scrollbar as per original decision
                        setScrollbarVisibility(sbarVisible);
                        // Now, by the time we're here, size may have changed
                        // That is unlikely, but then we got to re-layout stuff
                        int cwNew, chNew;
                        getContentSize(sbarVisible, out cwNew, out chNew);
                        if (cwNew != cw || chNew != ch) reAnalyzeResultsDisplay();
                        else
                        {
                            // Everything as big as it used to be...
                            // Change our mind about scrollbar?
                            cw = showOrHideScrollbar();
                        }
                    }
                    // Results count text
                    if (resCtrls.Count == 1) txtResCount = tprov.GetString("ResultsCountOne");
                    else
                    {
                        txtResCount = tprov.GetString("ResultsCountN");
                        txtResCount = string.Format(txtResCount, resCtrls.Count);
                    }
                    // Update first visible control's index
                    updateFirstVisibleIdx();
                    // Render
                    doFade(false);
                    MakeMePaint(false, RenderMode.Invalidate);
                });
                // Done.
                return true;
            }
        }
Пример #35
0
        /// <summary>
        /// Ctor.
        /// </summary>
        public LookupControl(ZenControlBase owner, ICedictEngineFactory dictFact, ITextProvider tprov)
            : base(owner)
        {
            this.dictFact = dictFact;
            this.tprov    = tprov;
            padding       = (int)Math.Round(5.0F * Scale);

            // Init search language and script from user settings
            searchLang   = AppSettings.SearchLang;
            searchScript = AppSettings.SearchScript;

            // Init HanziLookup
            fsStrokes   = new FileStream(Magic.StrokesFileName, FileMode.Open, FileAccess.Read);
            brStrokes   = new BinaryReader(fsStrokes);
            strokesData = new StrokesDataSource(brStrokes);

            // Writing pad
            writingPad                 = new WritingPad(this, tprov);
            writingPad.RelLocation     = new Point(padding, padding);
            writingPad.LogicalSize     = new Size(200, 200);
            writingPad.StrokesChanged += writingPad_StrokesChanged;

            // Images for buttons under writing pad; will get owned by buttons, not that it matters.
            Assembly a = Assembly.GetExecutingAssembly();
            var      imgStrokesClear = Image.FromStream(a.GetManifestResourceStream("ZD.Gui.Resources.strokes-clear.png"));
            var      imgStrokesUndo  = Image.FromStream(a.GetManifestResourceStream("ZD.Gui.Resources.strokes-undo.png"));

            // Clear and undo buttons under writing pad.
            float leftBtnWidth = writingPad.Width / 2 + 1;
            float btnHeight    = 22.0F * Scale;

            // --
            btnClearWritingPad             = new ZenGradientButton(this);
            btnClearWritingPad.RelLocation = new Point(writingPad.RelLeft, writingPad.RelBottom - 1);
            btnClearWritingPad.Size        = new Size((int)leftBtnWidth, (int)btnHeight);
            btnClearWritingPad.Text        = tprov.GetString("WritingPadClear");
            btnClearWritingPad.SetFont(SystemFontProvider.Instance.GetSystemFont(FontStyle.Regular, 9.0F));
            btnClearWritingPad.Padding           = (int)(3.0F * Scale);
            btnClearWritingPad.ImageExtraPadding = (int)(3.0F * Scale);
            btnClearWritingPad.Image             = imgStrokesClear;
            btnClearWritingPad.Enabled           = false;
            btnClearWritingPad.MouseClick       += onClearWritingPad;
            // --
            btnUndoStroke             = new ZenGradientButton(this);
            btnUndoStroke.RelLocation = new Point(btnClearWritingPad.RelRight - 1, writingPad.RelBottom - 1);
            btnUndoStroke.Size        = new Size(writingPad.RelRight - btnUndoStroke.RelLeft, (int)btnHeight);
            btnUndoStroke.Text        = tprov.GetString("WritingPadUndo");
            btnUndoStroke.SetFont(SystemFontProvider.Instance.GetSystemFont(FontStyle.Regular, 9.0F));
            btnUndoStroke.Padding           = (int)(3.0F * Scale);
            btnUndoStroke.ImageExtraPadding = (int)(1.5F * Scale);
            btnUndoStroke.Image             = imgStrokesUndo;
            btnUndoStroke.Enabled           = false;
            btnUndoStroke.MouseClick       += onUndoStroke;
            // --
            btnClearWritingPad.Tooltip = new ClearUndoTooltips(btnClearWritingPad, true, tprov, padding);
            btnUndoStroke.Tooltip      = new ClearUndoTooltips(btnUndoStroke, false, tprov, padding);
            // --

            // Character picker control under writing pad.
            ctrlCharPicker             = new CharPicker(this, tprov);
            ctrlCharPicker.FontFam     = Magic.ZhoContentFontFamily;
            ctrlCharPicker.FontScript  = searchScript == SearchScript.Traditional ? IdeoScript.Trad : IdeoScript.Simp;
            ctrlCharPicker.RelLocation = new Point(padding, btnClearWritingPad.RelBottom + padding);
            ctrlCharPicker.LogicalSize = new Size(200, 80);
            ctrlCharPicker.CharPicked += onCharPicked;

            // Search input control at top
            ctrlSearchInput              = new SearchInputControl(this, tprov);
            ctrlSearchInput.RelLocation  = new Point(writingPad.RelRight + padding, padding);
            ctrlSearchInput.StartSearch += onStartSearch;

            // Tweaks for Chinese text on UI buttons
            // This is specific to Segoe UI and Noto Sans S Chinese fonts.
            float ofsZho = 0;

            //if (!(SystemFontProvider.Instance as ZydeoSystemFontProvider).SegoeExists)
            //    ofsZho = Magic.ZhoButtonFontSize * Scale / 3.7F;

            // Script selector button to the right of search input control
            btnSimpTrad        = new ZenGradientButton(this);
            btnSimpTrad.RelTop = padding;
            btnSimpTrad.Height = ctrlSearchInput.Height;
            btnSimpTrad.SetFont((SystemFontProvider.Instance as ZydeoSystemFontProvider).GetZhoButtonFont(
                                    FontStyle.Regular, Magic.ZhoButtonFontSize));
            btnSimpTrad.Width             = getSimpTradWidth();
            btnSimpTrad.ForcedCharVertOfs = ofsZho;
            btnSimpTrad.RelLeft           = Width - padding - btnSimpTrad.Width;
            btnSimpTrad.Height            = ctrlSearchInput.Height;
            btnSimpTrad.MouseClick       += onSimpTrad;

            // Search language selector to the right of search input control
            btnSearchLang        = new ZenGradientButton(this);
            btnSearchLang.RelTop = padding;
            btnSearchLang.Height = ctrlSearchInput.Height;
            btnSearchLang.SetFont((SystemFontProvider.Instance as ZydeoSystemFontProvider).GetZhoButtonFont(
                                      FontStyle.Regular, Magic.ZhoButtonFontSize));
            btnSearchLang.Width             = getSearchLangWidth();
            btnSearchLang.ForcedCharVertOfs = ofsZho;
            btnSearchLang.RelLeft           = btnSimpTrad.RelLeft - padding - btnSearchLang.Width;
            btnSearchLang.Height            = ctrlSearchInput.Height;
            btnSearchLang.MouseClick       += onSearchLang;

            // Update button texts; do it here so tooltip locations will be correct.
            simpTradChanged();
            searchLangChanged();

            // Lookup results control.
            ctrlResults             = new ResultsControl(this, tprov, onLookupThroughLink, onGetEntry);
            ctrlResults.RelLocation = new Point(writingPad.RelRight + padding, ctrlSearchInput.RelBottom + padding);
        }
Пример #36
0
 /// <summary>
 /// Retrieves entries for a Chinese search expression (pinyin vs. hanzi auto-detected)
 /// </summary>
 private List<CedictResult> doChineseLookup(BinReader br, string query, SearchScript script)
 {
     List<CedictResult> res = new List<CedictResult>();
     // If query string has ideographic characters, do hanzi looup
     if (hasIdeo(query)) res = doHanziLookupHead(br, query, script);
     // Otherwise, do pinyin lookup
     else
     {
         // Parse pinyin query string
         List<PinyinSyllable> sylls = doParsePinyin(query);
         // Lookup
         res = doPinyinLookupHead(br, sylls);
     }
     // Done
     return res;
 }