regenerated
[moon.git] / src / textblock.cpp
bloba5a0f329c6d049fc7f0d19c0a4799d731110d510
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * textblock.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include <cairo.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <errno.h>
22 #include "file-downloader.h"
23 #include "textblock.h"
24 #include "control.h"
25 #include "runtime.h"
26 #include "color.h"
27 #include "utils.h"
28 #include "debug.h"
29 #include "uri.h"
30 #include "geometry.h"
31 #include "deployment.h"
33 // Unicode Line Separator (\u2028)
34 static const char utf8_linebreak[3] = { 0xe2, 0x80, 0xa8 };
35 #define utf8_linebreak_len 3
39 // Inline
42 Inline::Inline ()
44 SetObjectType (Type::INLINE);
45 font = new TextFontDescription ();
46 downloaders = g_ptr_array_new ();
47 autogen = false;
50 Inline::~Inline ()
52 CleanupDownloaders ();
53 g_ptr_array_free (downloaders, true);
54 delete font;
57 void
58 Inline::CleanupDownloaders ()
60 Downloader *downloader;
61 guint i;
63 for (i = 0; i < downloaders->len; i++) {
64 downloader = (Downloader *) downloaders->pdata[i];
65 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
66 downloader->Abort ();
67 downloader->unref ();
70 g_ptr_array_set_size (downloaders, 0);
73 void
74 Inline::AddFontSource (Downloader *downloader)
76 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
77 g_ptr_array_add (downloaders, downloader);
78 downloader->ref ();
80 if (downloader->Started () || downloader->Completed ()) {
81 if (downloader->Completed ())
82 DownloaderComplete (downloader);
83 } else {
84 // This is what actually triggers the download
85 downloader->Send ();
89 void
90 Inline::AddFontResource (const char *resource)
92 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
93 Application *application = Application::GetCurrent ();
94 Downloader *downloader;
95 char *path;
96 Uri *uri;
98 uri = new Uri ();
100 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
101 if (IsAttached () && (downloader = GetDeployment ()->GetSurface ()->CreateDownloader ())) {
102 downloader->Open ("GET", resource, FontPolicy);
103 AddFontSource (downloader);
104 downloader->unref ();
107 delete uri;
109 return;
112 manager->AddResource (resource, path);
113 g_free (path);
114 delete uri;
117 void
118 Inline::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
120 if (args->GetProperty ()->GetOwnerType () != Type::INLINE) {
121 DependencyObject::OnPropertyChanged (args, error);
122 return;
125 if (args->GetId () == Inline::FontFamilyProperty) {
126 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
127 char **families, *fragment;
128 int i;
130 CleanupDownloaders ();
132 if (family && family->source) {
133 families = g_strsplit (family->source, ",", -1);
134 for (i = 0; families[i]; i++) {
135 g_strstrip (families[i]);
136 if ((fragment = strchr (families[i], '#'))) {
137 // the first portion of this string is the resource name...
138 *fragment = '\0';
139 AddFontResource (families[i]);
142 g_strfreev (families);
146 NotifyListenersOfPropertyChange (args, error);
149 void
150 Inline::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
152 if (prop && prop->GetId () == Inline::ForegroundProperty) {
153 // this isn't exactly what we want, I don't
154 // think... but it'll have to do.
155 NotifyListenersOfPropertyChange (prop, NULL);
156 } else {
157 DependencyObject::OnSubPropertyChanged (prop, obj, subobj_args);
161 bool
162 Inline::Equals (Inline *item)
164 const char *lang0, *lang1;
166 if (item->GetObjectType () != GetObjectType ())
167 return false;
169 if (*item->GetFontFamily () != *GetFontFamily ())
170 return false;
172 if (item->GetFontSize () != GetFontSize ())
173 return false;
175 if (item->GetFontStyle () != GetFontStyle ())
176 return false;
178 if (item->GetFontWeight () != GetFontWeight ())
179 return false;
181 if (item->GetFontStretch () != GetFontStretch ())
182 return false;
184 if (item->GetTextDecorations () != GetTextDecorations ())
185 return false;
187 lang0 = item->GetLanguage ();
188 lang1 = GetLanguage ();
190 if ((lang0 && !lang1) || (!lang0 && lang1))
191 return false;
193 if (lang0 && lang1 && strcmp (lang0, lang1) != 0)
194 return false;
196 // this isn't really correct - we should be checking
197 // the innards of the foreground brushes, but we're
198 // guaranteed to never have a false positive here.
199 if (item->GetForeground () != GetForeground ())
200 return false;
202 // OK, as best we can tell - they are equal
203 return true;
206 bool
207 Inline::UpdateFontDescription (const char *source, bool force)
209 FontFamily *family = GetFontFamily ();
210 bool changed = false;
212 if (font->SetSource (source))
213 changed = true;
215 if (font->SetFamily (family ? family->source : NULL))
216 changed = true;
218 if (font->SetStretch (GetFontStretch ()->stretch))
219 changed = true;
221 if (font->SetWeight (GetFontWeight ()->weight))
222 changed = true;
224 if (font->SetStyle (GetFontStyle ()->style))
225 changed = true;
227 if (font->SetSize (GetFontSize ()))
228 changed = true;
230 if (font->SetLanguage (GetLanguage ()))
231 changed = true;
233 if (force) {
234 font->Reload ();
235 return true;
238 return changed;
241 void
242 Inline::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
244 ((Inline *) closure)->DownloaderComplete ((Downloader *) sender);
247 void
248 Inline::DownloaderComplete (Downloader *downloader)
250 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
251 char *resource, *filename;
252 InternalDownloader *idl;
253 const char *path;
254 Uri *uri;
256 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
257 if (!(filename = downloader->GetDownloadedFilename (NULL)))
258 return;
260 g_free (filename);
262 if (!(idl = downloader->GetInternalDownloader ()))
263 return;
265 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
266 return;
268 uri = downloader->GetUri ();
270 // If the downloaded file was a zip file, this'll get the path to the
271 // extracted zip directory, else it will simply be the path to the
272 // downloaded file.
273 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
274 return;
276 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
277 manager->AddResource (resource, path);
278 g_free (resource);
283 // Run
286 bool
287 Run::Equals (Inline *item)
289 const char *text, *itext;
291 if (!Inline::Equals (item))
292 return false;
294 itext = ((Run *) item)->GetText ();
295 text = GetText ();
297 // compare the text
298 if (text && itext && strcmp (text, itext) != 0)
299 return false;
300 else if ((text && !itext) || (!text && itext))
301 return false;
303 return true;
308 // TextBlock
311 TextBlock::TextBlock ()
313 SetObjectType (Type::TEXTBLOCK);
315 font = new TextFontDescription ();
317 downloaders = g_ptr_array_new ();
318 layout = new TextLayout ();
319 font_source = NULL;
320 source = NULL;
322 actual_height = 0.0;
323 actual_width = 0.0;
324 setvalue = true;
325 was_set = false;
326 dirty = true;
329 TextBlock::~TextBlock ()
331 CleanupDownloaders (true);
332 g_ptr_array_free (downloaders, true);
334 delete layout;
335 delete font;
338 bool
339 TextBlock::InsideObject (cairo_t *cr, double x, double y)
341 double nx = x, ny = y;
342 Size total = GetRenderSize ().Max (GetActualWidth (), GetActualHeight ());
343 total = total.Max (ApplySizeConstraints (total));
345 TransformPoint (&nx, &ny);
347 if (nx < 0 || ny < 0 || nx > total.width || ny > total.height)
348 return false;
350 return InsideLayoutClip (x, y) && InsideClip (cr, x, y);
353 void
354 TextBlock::CleanupDownloaders (bool all)
356 Downloader *downloader;
357 guint i;
359 for (i = 0; i < downloaders->len; i++) {
360 downloader = (Downloader *) downloaders->pdata[i];
362 if (all || downloader != source) {
363 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
364 downloader->Abort ();
365 downloader->unref ();
369 g_ptr_array_set_size (downloaders, 0);
371 if (source && !all) {
372 g_ptr_array_add (downloaders, source);
373 } else {
374 source = NULL;
377 if (all) {
378 g_free (font_source);
379 font_source = NULL;
383 void
384 TextBlock::AddFontSource (Downloader *downloader)
386 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
387 g_ptr_array_add (downloaders, downloader);
388 downloader->ref ();
390 if (downloader->Started () || downloader->Completed ()) {
391 if (downloader->Completed ())
392 DownloaderComplete (downloader);
393 } else {
394 // This is what actually triggers the download
395 downloader->Send ();
399 void
400 TextBlock::SetFontSource (Downloader *downloader)
402 CleanupDownloaders (true);
403 source = downloader;
405 if (downloader) {
406 font_source = downloader->GetUri ()->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
407 AddFontSource (downloader);
408 return;
411 UpdateFontDescriptions (true);
412 UpdateBounds (true);
413 Invalidate ();
414 dirty = true;
417 void
418 TextBlock::AddFontResource (const char *resource)
420 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
421 Application *application = Application::GetCurrent ();
422 Downloader *downloader;
423 char *path;
424 Uri *uri;
426 uri = new Uri ();
428 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
429 if (IsAttached () && (downloader = GetDeployment ()->GetSurface ()->CreateDownloader ())) {
430 downloader->Open ("GET", resource, FontPolicy);
431 AddFontSource (downloader);
432 downloader->unref ();
435 delete uri;
437 return;
440 manager->AddResource (resource, path);
441 g_free (path);
442 delete uri;
445 void
446 TextBlock::Render (cairo_t *cr, Region *region, bool path_only)
448 cairo_save (cr);
449 cairo_set_matrix (cr, &absolute_xform);
451 if (!path_only)
452 RenderLayoutClip (cr);
454 Paint (cr);
456 cairo_restore (cr);
459 void
460 TextBlock::ComputeBounds ()
462 Rect extents = layout->GetRenderExtents ();
463 Thickness padding = *GetPadding ();
465 extents.x += padding.left;
466 extents.y += padding.top;
468 bounds = bounds_with_children = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
471 Point
472 TextBlock::GetTransformOrigin ()
474 Point *user_xform_origin = GetRenderTransformOrigin ();
475 return Point (actual_width * user_xform_origin->x,
476 actual_height * user_xform_origin->y);
479 Size
480 TextBlock::ComputeActualSize ()
482 Thickness padding = *GetPadding ();
483 Size constraint = ApplySizeConstraints (Size (INFINITY, INFINITY));
484 Size result = Size (0.0, 0.0);
486 if (LayoutInformation::GetLayoutSlot (this) || LayoutInformation::GetPreviousConstraint (this)) {
487 layout->Layout ();
488 layout->GetActualExtents (&actual_width, &actual_height);
489 } else {
490 constraint = constraint.GrowBy (-padding);
491 Layout (constraint);
494 result = Size (actual_width, actual_height);
495 result = result.GrowBy (padding);
497 return result;
500 Size
501 TextBlock::MeasureOverride (Size availableSize)
503 Thickness padding = *GetPadding ();
504 Size constraint;
505 Size desired;
507 constraint = availableSize.GrowBy (-padding);
509 Layout (constraint);
511 desired = Size (actual_width, actual_height).GrowBy (padding);
513 return desired;
516 Size
517 TextBlock::ArrangeOverride (Size finalSize)
519 Thickness padding = *GetPadding ();
520 Size constraint;
521 Size arranged;
523 constraint = finalSize.GrowBy (-padding);
524 Layout (constraint);
526 arranged = Size (actual_width, actual_height);
528 arranged = arranged.Max (constraint);
529 layout->SetAvailableWidth (constraint.width);
531 arranged = arranged.GrowBy (padding);
533 return finalSize;
536 void
537 TextBlock::UpdateLayoutAttributes ()
539 InlineCollection *inlines = GetInlines ();
540 TextLayoutAttributes *attrs;
541 const char *text;
542 int length = 0;
543 Inline *item;
544 List *runs;
546 InvalidateMeasure ();
547 InvalidateArrange ();
548 runs = new List ();
550 UpdateFontDescription (false);
552 if (inlines != NULL) {
553 for (int i = 0; i < inlines->GetCount (); i++) {
554 item = inlines->GetValueAt (i)->AsInline ();
555 item->UpdateFontDescription (font_source, false);
557 switch (item->GetObjectType ()) {
558 case Type::RUN:
559 text = ((Run *) item)->GetText ();
561 if (text && text[0]) {
562 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
563 runs->Append (attrs);
565 length += strlen (text);
568 break;
569 case Type::LINEBREAK:
570 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
571 runs->Append (attrs);
573 length += utf8_linebreak_len;
574 break;
575 default:
576 break;
580 if (inlines->GetCount () > 0)
581 was_set = true;
584 layout->SetText (GetText (), length);
585 layout->SetTextAttributes (runs);
588 bool
589 TextBlock::UpdateFontDescription (bool force)
591 FontFamily *family = GetFontFamily ();
592 bool changed = false;
594 if (font->SetSource (font_source))
595 changed = true;
597 if (font->SetFamily (family ? family->source : NULL))
598 changed = true;
600 if (font->SetStretch (GetFontStretch ()->stretch))
601 changed = true;
603 if (font->SetWeight (GetFontWeight ()->weight))
604 changed = true;
606 if (font->SetStyle (GetFontStyle ()->style))
607 changed = true;
609 if (font->SetSize (GetFontSize ()))
610 changed = true;
612 if (font->SetLanguage (GetLanguage ()))
613 changed = true;
615 if (force) {
616 font->Reload ();
617 changed = true;
620 if (changed)
621 layout->SetBaseFont (font->GetFont ());
623 return changed;
626 bool
627 TextBlock::UpdateFontDescriptions (bool force)
629 InlineCollection *inlines = GetInlines ();
630 bool changed = false;
631 Inline *item;
633 changed = UpdateFontDescription (force);
635 if (inlines != NULL) {
636 for (int i = 0; i < inlines->GetCount (); i++) {
637 item = inlines->GetValueAt (i)->AsInline ();
638 if (item->UpdateFontDescription (font_source, force))
639 changed = true;
642 if (changed)
643 layout->ResetState ();
646 if (changed) {
647 InvalidateMeasure ();
648 InvalidateArrange ();
649 UpdateBounds (true);
650 dirty = true;
653 return changed;
656 void
657 TextBlock::Layout (Size constraint)
659 if (was_set && !GetValueNoDefault (TextBlock::TextProperty)) {
660 // If the Text property had been set once upon a time,
661 // but is currently empty, Silverlight seems to set
662 // the ActualHeight property to the font height. See
663 // bug #405514 for details.
664 TextFontDescription *desc = new TextFontDescription ();
665 FontFamily *family = GetFontFamily ();
666 TextFont *font;
668 desc->SetFamily (family ? family->source : NULL);
669 desc->SetStretch (GetFontStretch ()->stretch);
670 desc->SetWeight (GetFontWeight ()->weight);
671 desc->SetStyle (GetFontStyle ()->style);
672 desc->SetSize (GetFontSize ());
674 font = desc->GetFont ();
675 actual_height = font->Height ();
676 actual_width = 0.0;
677 delete desc;
678 } else if (!was_set) {
679 // If the Text property has never been set, then its
680 // extents should both be 0.0. See bug #435798 for
681 // details.
682 actual_height = 0.0;
683 actual_width = 0.0;
684 } else {
685 layout->SetMaxWidth (constraint.width);
686 layout->Layout ();
688 layout->GetActualExtents (&actual_width, &actual_height);
691 dirty = false;
694 void
695 TextBlock::Paint (cairo_t *cr)
697 Thickness *padding = GetPadding ();
698 Point offset (padding->left, padding->top);
700 cairo_set_matrix (cr, &absolute_xform);
701 layout->Render (cr, GetOriginPoint (), offset);
704 char *
705 TextBlock::GetTextInternal (InlineCollection *inlines)
707 const char *text;
708 GString *block;
709 Inline *item;
710 char *str;
712 if (!inlines)
713 return g_strdup ("");
715 block = g_string_new ("");
717 for (int i = 0; i < inlines->GetCount (); i++) {
718 item = inlines->GetValueAt (i)->AsInline ();
720 switch (item->GetObjectType ()) {
721 case Type::RUN:
722 text = ((Run *) item)->GetText ();
724 if (text && text[0])
725 g_string_append (block, text);
726 break;
727 case Type::LINEBREAK:
728 g_string_append_len (block, utf8_linebreak, utf8_linebreak_len);
729 break;
730 default:
731 break;
735 str = block->str;
736 g_string_free (block, false);
738 return str;
741 void
742 TextBlock::SetTextInternal (const char *text)
744 InlineCollection *inlines;
745 Value *value;
746 Run *run;
748 // Note: calling GetValue() may cause the InlineCollection to be
749 // autocreated, so we need to prevent reentrancy here.
750 setvalue = false;
752 value = GetValue (TextBlock::InlinesProperty);
753 inlines = value->AsInlineCollection ();
754 inlines->Clear ();
756 if (text) {
757 run = new Run ();
758 run->SetAutogenerated (true);
759 run->SetText (text);
760 inlines->Add (run);
761 run->unref ();
762 } else {
763 // setting text to null results in String.Empty
764 SetValue (TextBlock::TextProperty, Value (""));
767 setvalue = true;
770 void
771 TextBlock::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
773 bool invalidate = true;
775 if (args->GetProperty ()->GetOwnerType () != Type::TEXTBLOCK) {
776 FrameworkElement::OnPropertyChanged (args, error);
778 if (args->GetId () == FrameworkElement::LanguageProperty) {
779 // a change in xml:lang might change font characteristics
780 if (UpdateFontDescriptions (false)) {
781 InvalidateMeasure ();
782 InvalidateArrange ();
783 UpdateBounds (true);
784 dirty = true;
789 if (args->GetId () == FrameworkElement::WidthProperty) {
790 //if (layout->SetMaxWidth (args->GetNewValue()->AsDouble ()))
791 // dirty = true;
793 UpdateBounds (true);
796 return;
799 if (args->GetId () == TextBlock::FontFamilyProperty) {
800 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
801 char **families, *fragment;
802 int i;
804 CleanupDownloaders (false);
806 if (family && family->source) {
807 families = g_strsplit (family->source, ",", -1);
808 for (i = 0; families[i]; i++) {
809 g_strstrip (families[i]);
810 if ((fragment = strchr (families[i], '#'))) {
811 // the first portion of this string is the resource name...
812 *fragment = '\0';
813 AddFontResource (families[i]);
816 g_strfreev (families);
819 if (UpdateFontDescriptions (false))
820 dirty = true;
821 } else if (args->GetId () == TextBlock::FontSizeProperty) {
822 if (UpdateFontDescriptions (false))
823 dirty = true;
824 } else if (args->GetId () == TextBlock::FontStretchProperty) {
825 if (UpdateFontDescriptions (false))
826 dirty = true;
827 } else if (args->GetId () == TextBlock::FontStyleProperty) {
828 if (UpdateFontDescriptions (false))
829 dirty = true;
830 } else if (args->GetId () == TextBlock::FontWeightProperty) {
831 if (UpdateFontDescriptions (false))
832 dirty = true;
833 } else if (args->GetId () == TextBlock::TextProperty) {
834 if (setvalue) {
835 // result of a change to the TextBlock.Text property
836 const char *text = args->GetNewValue() ? args->GetNewValue()->AsString () : NULL;
838 SetTextInternal (text);
839 UpdateLayoutAttributes ();
840 dirty = true;
841 } else {
842 // result of a change to the TextBlock.Inlines property
843 UpdateLayoutAttributes ();
844 invalidate = false;
846 } else if (args->GetId () == TextBlock::TextDecorationsProperty) {
847 dirty = true;
848 } else if (args->GetId () == TextBlock::TextWrappingProperty) {
849 dirty = layout->SetTextWrapping ((TextWrapping) args->GetNewValue()->AsInt32 ());
850 } else if (args->GetId () == TextBlock::InlinesProperty) {
851 if (setvalue) {
852 // result of a change to the TextBlock.Inlines property
853 InlineCollection *inlines = args->GetNewValue() ? args->GetNewValue()->AsInlineCollection () : NULL;
855 setvalue = false;
856 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
857 setvalue = true;
859 UpdateLayoutAttributes ();
860 dirty = true;
861 } else {
862 // this should be the result of Inlines being autocreated
863 UpdateLayoutAttributes ();
864 invalidate = false;
866 } else if (args->GetId () == TextBlock::LineStackingStrategyProperty) {
867 dirty = layout->SetLineStackingStrategy ((LineStackingStrategy) args->GetNewValue()->AsInt32 ());
868 } else if (args->GetId () == TextBlock::LineHeightProperty) {
869 dirty = layout->SetLineHeight (args->GetNewValue()->AsDouble ());
870 } else if (args->GetId () == TextBlock::TextAlignmentProperty) {
871 dirty = layout->SetTextAlignment ((TextAlignment) args->GetNewValue()->AsInt32 ());
872 } else if (args->GetId () == TextBlock::PaddingProperty) {
873 dirty = true;
874 } else if (args->GetId () == TextBlock::FontSourceProperty) {
875 FontSource *source = args->GetNewValue () ? args->GetNewValue ()->AsFontSource () : NULL;
876 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
878 // FIXME: ideally we'd remove the old item from the cache (or,
879 // rather, 'unref' it since some other textblocks/boxes might
880 // still be using it).
882 g_free (font_source);
884 if (source && source->stream)
885 font_source = manager->AddResource (source->stream);
886 else
887 font_source = NULL;
889 UpdateFontDescriptions (true);
890 dirty = true;
893 if (invalidate) {
894 if (dirty) {
895 InvalidateMeasure ();
896 InvalidateArrange ();
897 UpdateBounds (true);
900 Invalidate ();
903 NotifyListenersOfPropertyChange (args, error);
906 void
907 TextBlock::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
909 if (prop && prop->GetId () == TextBlock::ForegroundProperty) {
910 Invalidate ();
911 } else {
912 FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
916 void
917 TextBlock::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
919 InlineCollection *inlines = GetInlines ();
921 if (col != inlines) {
922 FrameworkElement::OnCollectionChanged (col, args);
923 return;
926 if (args->GetChangedAction () == CollectionChangedActionClearing)
927 return;
929 if (!setvalue) {
930 // changes being handled elsewhere...
931 return;
934 setvalue = false;
935 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
936 setvalue = true;
938 UpdateLayoutAttributes ();
939 InvalidateMeasure ();
940 InvalidateArrange ();
941 UpdateBounds (true);
942 Invalidate ();
945 void
946 TextBlock::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
948 InlineCollection *inlines = GetInlines ();
950 if (col != inlines) {
951 FrameworkElement::OnCollectionItemChanged (col, obj, args);
952 return;
955 if (args->GetId () != Inline::ForegroundProperty) {
956 if (args->GetId () == Run::TextProperty) {
957 // update our TextProperty
958 setvalue = false;
959 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
960 setvalue = true;
962 UpdateLayoutAttributes ();
963 } else {
964 // likely a font property change...
965 ((Inline *) obj)->UpdateFontDescription (font_source, true);
968 // All non-Foreground property changes require
969 // recalculating layout which can change the bounds.
970 InvalidateMeasure ();
971 InvalidateArrange ();
972 UpdateBounds (true);
973 dirty = true;
974 } else {
975 // A simple Foreground brush change does not require
976 // recalculating layout. Invalidate() and we're done.
979 Invalidate ();
982 void
983 TextBlock::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
985 ((TextBlock *) closure)->DownloaderComplete ((Downloader *) sender);
988 void
989 TextBlock::DownloaderComplete (Downloader *downloader)
991 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
992 char *resource, *filename;
993 InternalDownloader *idl;
994 const char *path;
995 Uri *uri;
997 dirty = true;
998 InvalidateMeasure ();
999 InvalidateArrange ();
1001 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
1002 if (!(filename = downloader->GetDownloadedFilename (NULL)))
1003 return;
1005 g_free (filename);
1007 if (!(idl = downloader->GetInternalDownloader ()))
1008 return;
1010 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
1011 return;
1013 uri = downloader->GetUri ();
1015 // If the downloaded file was a zip file, this'll get the path to the
1016 // extracted zip directory, else it will simply be the path to the
1017 // downloaded file.
1018 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
1019 return;
1021 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
1022 manager->AddResource (resource, path);
1023 g_free (resource);
1025 if (UpdateFontDescriptions (true)) {
1026 dirty = true;
1028 UpdateBounds (true);
1029 Invalidate ();