/// <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(); } } }
/// <summary> /// Ctor: annotated Hanzi /// </summary> public OneResultCtrl(string query, CedictAnnotation ann, ICedictEntryProvider prov, UiTones tones, bool isMobile) { this.query = query; this.ann = ann; this.prov = prov; this.tones = tones; this.isMobile = isMobile; }
/// <summary> /// Ctor: intialize immutable object. /// </summary> public CedictLookupResult(ICedictEntryProvider entryProvider, ReadOnlyCollection<CedictResult> results, SearchLang actualSearchLang) { EntryProvider = entryProvider; Results = results; ActualSearchLang = actualSearchLang; }
/// <summary> /// Ctor: regular lookup result /// </summary> public OneResultCtrl(CedictResult res, ICedictEntryProvider prov, UiScript script, UiTones tones, bool isMobile) { this.res = res; this.prov = prov; this.script = script; this.tones = tones; this.isMobile = isMobile; }
/// <summary> /// Ctor: annotated Hanzi /// </summary> public EntryRenderer(string query, CedictAnnotation ann, ICedictEntryProvider prov, UiTones tones) { this.query = query; this.ann = ann; this.prov = prov; this.tones = tones; this.hanim = true; }
/// <summary> /// Ctor: intialize immutable object. /// </summary> public CedictLookupResult(ICedictEntryProvider entryProvider, string query, List <CedictResult> results, List <CedictAnnotation> annotations, SearchLang actualSearchLang) { Query = query; EntryProvider = entryProvider; Results = new ReadOnlyCollection <CedictResult>(results); Annotations = new ReadOnlyCollection <CedictAnnotation>(annotations); ActualSearchLang = actualSearchLang; }
/// <summary> /// Ctor: regular lookup result /// </summary> public EntryRenderer(CedictResult res, ICedictEntryProvider prov, UiScript script, UiTones tones) { this.res = res; this.prov = prov; this.script = script; this.tones = tones; this.hanim = true; this.dimIdenticalTrad = true; }
protected void Page_Load(object sender, EventArgs e) { // Add CSS files Master.AddCss("tooltipster.css"); Master.AddCss("style.css"); Master.AddCss("entry.css"); Master.AddCss("lookup.css"); // Add JS includes Master.AddJS("jquery.tooltipster.min.js", true); Master.AddJS("common.js", false); Master.AddJS("lookup.js", false); Master.AddJS("strokeanim.js", false); if (string.IsNullOrEmpty(Request.Params["query"])) { loadStatic("Welcome"); return; } string query = Request.Params["query"].Replace('+', ' '); txtSearch.Value = query; CedictLookupResult lr; using (SqlDict.Query q = new SqlDict.Query()) { lr = q.Lookup(query); } // No results if (lr.Results.Count == 0 && lr.Annotations.Count == 0) { loadStatic("NoResults"); return; } prov = lr.EntryProvider; // Add regular results for (int i = 0; i != lr.Results.Count; ++i) { if (i >= 256) { break; } var res = lr.Results[i]; OneResultCtrl resCtrl = new OneResultCtrl(res, lr.EntryProvider, Master.UiScript, Master.UiTones, false); resultsHolder.Controls.Add(resCtrl); } // SOA BOX soaBox.Visible = true; }
/// <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); }
/// <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); } }
/// <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); }
/// <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; } }
/// <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(); } }
protected void Page_Load(object sender, EventArgs e) { // Only do this manually, and offline: generate sitemaps //SitemapGenerator.Generate(); var tprov = TextProvider.Instance; // Server-side inline localization strokeClear.InnerText = tprov.GetString(Master.UILang, "BtnStrokeClear"); strokeUndo.InnerText = tprov.GetString(Master.UILang, "BtnStrokeUndo"); txtSearch.Attributes["placeholder"] = tprov.GetString(Master.UILang, "TxtSearchPlacholder"); resultsHolder.Visible = false; soaBox.Visible = false; isStaticQuery = Request.RawUrl.StartsWith("/search/"); string qlang = Request["lang"]; string query = Request["query"]; if (query != null) { query = query.Trim(); } // No query: show welcome screen, and we're done. if (string.IsNullOrEmpty(query)) { resultsHolder.Visible = false; welcomeScreen.Visible = true; welcomeScreen.InnerHtml = tprov.GetSnippet(Master.UILang, "welcome"); Title = tprov.GetString(Master.UILang, "TitleMain"); // Seed walkthrough Master.SetStaticQuery(null, SearchLang.Chinese, DateTime.UtcNow); return; } // From here on ---> lookup string strMobile = Request["mobile"]; isMobile = strMobile == "yes"; // Auto-add "mobile" class to body if we know it already if (isMobile) { Master.SetMobile(); } queryInfo = new QueryInfo(Request.UserHostAddress, query); resultsHolder.Visible = true; welcomeScreen.Visible = false; SearchLang slang = qlang == "trg" ? SearchLang.Target : SearchLang.Chinese; var lr = Global.Dict.Lookup(query, SearchScript.Both, slang); queryInfo.ResCount = lr.Results.Count; queryInfo.AnnCount = lr.Annotations.Count; queryInfo.Lang = lr.ActualSearchLang; queryInfo.DTLookup = DateTime.UtcNow; prov = lr.EntryProvider; // Add regular results for (int i = 0; i != lr.Results.Count; ++i) { if (i >= 256) { break; } var res = lr.Results[i]; OneResultCtrl resCtrl = new OneResultCtrl(res, lr.EntryProvider, Master.UiScript, Master.UiTones, isMobile); resultsHolder.Controls.Add(resCtrl); } // Add annotations (we never get both, so don't need to worry about the order) for (int i = 0; i != lr.Annotations.Count; ++i) { var ann = lr.Annotations[i]; OneResultCtrl resCtrl = new OneResultCtrl(lr.Query, ann, lr.EntryProvider, Master.UiTones, isMobile); resultsHolder.Controls.Add(resCtrl); } txtSearch.Value = query; // No results if (lr.Results.Count == 0 && lr.Annotations.Count == 0) { resultsHolder.Visible = false; welcomeScreen.Visible = true; welcomeScreen.InnerHtml = tprov.GetSnippet(Master.UILang, "noresults"); Title = tprov.GetString(Master.UILang, "TitleMain"); } // We got results else { // Page title string title; // Regular lookup results if (lr.Results.Count != 0) { if (lr.ActualSearchLang == SearchLang.Chinese) { title = tprov.GetString(Master.UILang, "TitleSearchChinese"); } else { title = tprov.GetString(Master.UILang, "TitleSearchGerman"); } } // Annotation else { title = tprov.GetString(Master.UILang, "TitleSearchAnnotation"); } title = string.Format(title, query); Title = title; // For annotatio mode, show notice at top if (lr.Annotations.Count != 0) { topNotice.Visible = true; tnTitle.InnerText = tprov.GetString(Master.UILang, "AnnotationTitle"); tnMessage.InnerText = tprov.GetString(Master.UILang, "AnnotationMessage"); } // SOA BOX soaBox.Visible = true; soaTitle.InnerText = tprov.GetString(Master.UILang, "AnimPopupTitle"); string attrLink = "<a href='https://github.com/skishore/makemeahanzi' target='_blank'>{0}</a>"; attrLink = string.Format(attrLink, tprov.GetString(Master.UILang, "AnimPopupMMAH")); string attrHtml = tprov.GetString(Master.UILang, "AnimPopupAttr"); attrHtml = string.Format(attrHtml, attrLink); soaFooter.InnerHtml = attrHtml; // Seed walkthrough - if query is static and we have regular results (not annotations) if (isStaticQuery && lr.Results.Count != 0) { Master.SetStaticQuery(query, slang, queryInfo.DTStart); } } }