add the 2.1-bootstrap dir to MONO_PATH when running smcs
[moon.git] / src / textblock.cpp
blobe1f9b0aa3311bd4e7cbf5e3b9d1f0fdd4c4d319a
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"
32 // Unicode Line Separator (\u2028)
33 static const char utf8_linebreak[3] = { 0xe2, 0x80, 0xa8 };
34 #define utf8_linebreak_len 3
38 // Inline
41 Inline::Inline ()
43 SetObjectType (Type::INLINE);
44 font = new TextFontDescription ();
45 downloaders = g_ptr_array_new ();
46 autogen = false;
49 Inline::~Inline ()
51 CleanupDownloaders ();
52 g_ptr_array_free (downloaders, true);
53 delete font;
56 void
57 Inline::CleanupDownloaders ()
59 Downloader *downloader;
60 guint i;
62 for (i = 0; i < downloaders->len; i++) {
63 downloader = (Downloader *) downloaders->pdata[i];
64 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
65 downloader->Abort ();
66 downloader->unref ();
69 g_ptr_array_set_size (downloaders, 0);
72 void
73 Inline::AddFontSource (Downloader *downloader)
75 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
76 g_ptr_array_add (downloaders, downloader);
77 downloader->ref ();
79 if (downloader->Started () || downloader->Completed ()) {
80 if (downloader->Completed ())
81 DownloaderComplete (downloader);
82 } else {
83 // This is what actually triggers the download
84 downloader->Send ();
88 void
89 Inline::AddFontResource (const char *resource)
91 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
92 Application *application = Application::GetCurrent ();
93 Downloader *downloader;
94 Surface *surface;
95 char *path;
96 Uri *uri;
98 uri = new Uri ();
100 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
101 if ((surface = GetSurface ()) && (downloader = surface->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);
284 // 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;
307 // TextBlock
310 TextBlock::TextBlock ()
312 SetObjectType (Type::TEXTBLOCK);
314 downloaders = g_ptr_array_new ();
315 layout = new TextLayout ();
316 font_source = NULL;
317 source = NULL;
319 actual_height = 0.0;
320 actual_width = 0.0;
321 setvalue = true;
322 was_set = false;
323 dirty = true;
326 TextBlock::~TextBlock ()
328 CleanupDownloaders (true);
329 g_ptr_array_free (downloaders, true);
331 delete layout;
334 void
335 TextBlock::CleanupDownloaders (bool all)
337 Downloader *downloader;
338 guint i;
340 for (i = 0; i < downloaders->len; i++) {
341 downloader = (Downloader *) downloaders->pdata[i];
343 if (all || downloader != source) {
344 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
345 downloader->Abort ();
346 downloader->unref ();
350 g_ptr_array_set_size (downloaders, 0);
352 if (source && !all) {
353 g_ptr_array_add (downloaders, source);
354 } else {
355 source = NULL;
358 if (all) {
359 g_free (font_source);
360 font_source = NULL;
364 void
365 TextBlock::AddFontSource (Downloader *downloader)
367 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
368 g_ptr_array_add (downloaders, downloader);
369 downloader->ref ();
371 if (downloader->Started () || downloader->Completed ()) {
372 if (downloader->Completed ())
373 DownloaderComplete (downloader);
374 } else {
375 // This is what actually triggers the download
376 downloader->Send ();
380 void
381 TextBlock::SetFontSource (Downloader *downloader)
383 CleanupDownloaders (true);
384 source = downloader;
386 if (downloader) {
387 font_source = downloader->GetUri ()->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
388 AddFontSource (downloader);
389 return;
392 UpdateFontDescriptions (true);
393 UpdateBounds (true);
394 Invalidate ();
395 dirty = true;
398 void
399 TextBlock::AddFontResource (const char *resource)
401 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
402 Application *application = Application::GetCurrent ();
403 Downloader *downloader;
404 Surface *surface;
405 char *path;
406 Uri *uri;
408 uri = new Uri ();
410 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
411 if ((surface = GetSurface ()) && (downloader = surface->CreateDownloader ())) {
412 downloader->Open ("GET", resource, FontPolicy);
413 AddFontSource (downloader);
414 downloader->unref ();
417 delete uri;
419 return;
422 manager->AddResource (resource, path);
423 g_free (path);
424 delete uri;
427 void
428 TextBlock::Render (cairo_t *cr, Region *region, bool path_only)
430 cairo_save (cr);
431 cairo_set_matrix (cr, &absolute_xform);
433 if (!path_only)
434 RenderLayoutClip (cr);
436 Paint (cr);
438 cairo_restore (cr);
441 void
442 TextBlock::ComputeBounds ()
444 Size actual (GetActualWidth (), GetActualHeight ());
445 Size framework = ApplySizeConstraints (actual);
447 framework = framework.Max (actual);
449 Rect extents = Rect (0,0,framework.width, framework.height);
451 bounds = bounds_with_children = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
454 void
455 TextBlock::GetSizeForBrush (cairo_t *cr, double *width, double *height)
457 *width = actual_width;
458 *height = actual_height;
461 Point
462 TextBlock::GetTransformOrigin ()
464 Point *user_xform_origin = GetRenderTransformOrigin ();
465 return Point (actual_width * user_xform_origin->x,
466 actual_height * user_xform_origin->y);
471 Size
472 TextBlock::ComputeActualSize ()
474 Thickness padding = *GetPadding ();
475 Size result = FrameworkElement::ComputeActualSize ();
477 //if (dirty) {
478 if (!LayoutInformation::GetPreviousConstraint (this)) {
479 Size constraint = Size (INFINITY, INFINITY);
481 constraint = ApplySizeConstraints (constraint);
483 constraint = constraint.GrowBy (-padding);
484 Layout (constraint);
488 result = Size (actual_width, actual_height);
489 result = result.GrowBy (padding);
491 return result;
494 Size
495 TextBlock::MeasureOverride (Size availableSize)
497 Thickness padding = *GetPadding ();
498 Size constraint;
499 Size desired;
501 constraint = availableSize.GrowBy (-padding);
502 Layout (constraint);
504 desired = Size (actual_width, actual_height).GrowBy (padding);
506 SetActualHeight (desired.height);
507 SetActualWidth (desired.width);
509 desired = desired.Min (availableSize);
511 return desired;
514 Size
515 TextBlock::ArrangeOverride (Size finalSize)
517 Thickness padding = *GetPadding ();
518 Size constraint;
519 Size arranged;
521 constraint = finalSize.GrowBy (-padding);
522 Layout (constraint);
524 arranged = Size (actual_width, actual_height).GrowBy (padding);
526 arranged = arranged.Max (finalSize);
527 arranged = ApplySizeConstraints (arranged);
529 layout->SetAvailableWidth (arranged.GrowBy (-padding).width);
531 return arranged;
534 void
535 TextBlock::UpdateLayoutAttributes ()
537 InlineCollection *inlines = GetInlines ();
538 TextLayoutAttributes *attrs;
539 const char *text;
540 int length = 0;
541 Inline *item;
542 List *runs;
544 InvalidateMeasure ();
545 InvalidateArrange ();
546 runs = new List ();
548 if (inlines != NULL) {
549 for (int i = 0; i < inlines->GetCount (); i++) {
550 item = inlines->GetValueAt (i)->AsInline ();
551 item->UpdateFontDescription (font_source, false);
553 switch (item->GetObjectType ()) {
554 case Type::RUN:
555 text = ((Run *) item)->GetText ();
557 if (text && text[0]) {
558 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
559 runs->Append (attrs);
561 length += strlen (text);
564 break;
565 case Type::LINEBREAK:
566 attrs = new TextLayoutAttributes ((ITextAttributes *) item, length);
567 runs->Append (attrs);
569 length += utf8_linebreak_len;
570 break;
571 default:
572 break;
576 if (inlines->GetCount () > 0)
577 was_set = true;
580 layout->SetText (GetText (), length);
581 layout->SetTextAttributes (runs);
584 bool
585 TextBlock::UpdateFontDescriptions (bool force)
587 InlineCollection *inlines = GetInlines ();
588 bool changed = false;
589 Inline *item;
591 if (inlines != NULL) {
592 for (int i = 0; i < inlines->GetCount (); i++) {
593 item = inlines->GetValueAt (i)->AsInline ();
594 if (item->UpdateFontDescription (font_source, force))
595 changed = true;
598 if (changed)
599 layout->ResetState ();
602 //ClearValue (TextBlock::ActualWidthProperty);
603 //ClearValue (TextBlock::ActualHeightProperty);
604 InvalidateMeasure ();
605 InvalidateArrange ();
606 UpdateBounds (true);
607 dirty = true;
609 return changed;
612 void
613 TextBlock::Layout (Size constraint)
615 if (was_set && !GetValueNoDefault (TextBlock::TextProperty)) {
616 // If the Text property had been set once upon a time,
617 // but is currently empty, Silverlight seems to set
618 // the ActualHeight property to the font height. See
619 // bug #405514 for details.
620 TextFontDescription *desc = new TextFontDescription ();
621 FontFamily *family = GetFontFamily ();
622 TextFont *font;
624 desc->SetFamily (family ? family->source : NULL);
625 desc->SetStretch (GetFontStretch ()->stretch);
626 desc->SetWeight (GetFontWeight ()->weight);
627 desc->SetStyle (GetFontStyle ()->style);
628 desc->SetSize (GetFontSize ());
630 font = desc->GetFont ();
631 actual_height = font->Height ();
632 actual_width = 0.0;
633 delete desc;
634 } else if (!was_set) {
635 // If the Text property has never been set, then its
636 // extents should both be 0.0. See bug #435798 for
637 // details.
638 actual_height = 0.0;
639 actual_width = 0.0;
640 } else {
641 layout->SetMaxWidth (constraint.width);
642 layout->Layout ();
644 layout->GetActualExtents (&actual_width, &actual_height);
647 dirty = false;
650 void
651 TextBlock::Paint (cairo_t *cr)
653 Thickness *padding = GetPadding ();
654 Point offset (padding->left, padding->top);
656 cairo_set_matrix (cr, &absolute_xform);
657 layout->Render (cr, GetOriginPoint (), offset);
659 if (moonlight_flags & RUNTIME_INIT_SHOW_TEXTBOXES) {
660 cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0);
661 cairo_set_line_width (cr, 1);
662 cairo_rectangle (cr, padding->left, padding->top, actual_width, actual_height);
663 cairo_stroke (cr);
667 char *
668 TextBlock::GetTextInternal (InlineCollection *inlines)
670 const char *text;
671 GString *block;
672 Inline *item;
673 char *str;
675 if (!inlines)
676 return g_strdup ("");
678 block = g_string_new ("");
680 for (int i = 0; i < inlines->GetCount (); i++) {
681 item = inlines->GetValueAt (i)->AsInline ();
683 switch (item->GetObjectType ()) {
684 case Type::RUN:
685 text = ((Run *) item)->GetText ();
687 if (text && text[0])
688 g_string_append (block, text);
689 break;
690 case Type::LINEBREAK:
691 g_string_append_len (block, utf8_linebreak, utf8_linebreak_len);
692 break;
693 default:
694 break;
698 str = block->str;
699 g_string_free (block, false);
701 return str;
704 bool
705 TextBlock::SetTextInternal (const char *text)
707 InlineCollection *inlines;
708 Value *value;
709 Run *run;
711 // Note: calling GetValue() may cause the InlineCollection to be
712 // autocreated, so we need to prevent reentrancy here.
713 setvalue = false;
715 value = GetValue (TextBlock::InlinesProperty);
716 inlines = value->AsInlineCollection ();
717 inlines->Clear ();
719 if (text) {
720 run = new Run ();
721 run->SetAutogenerated (true);
722 run->SetText (text);
723 inlines->Add (run);
724 } else {
725 // setting text to null results in String.Empty
726 SetValue (TextBlock::TextProperty, Value (""));
729 setvalue = true;
731 return true;
734 void
735 TextBlock::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
737 bool invalidate = true;
739 if (args->GetProperty ()->GetOwnerType () != Type::TEXTBLOCK) {
740 FrameworkElement::OnPropertyChanged (args, error);
742 if (args->GetId () == FrameworkElement::LanguageProperty) {
743 // a change in xml:lang might change font characteristics
744 if (UpdateFontDescriptions (false)) {
745 InvalidateMeasure ();
746 InvalidateArrange ();
747 UpdateBounds (true);
748 dirty = true;
753 if (args->GetId () == FrameworkElement::WidthProperty) {
754 //if (layout->SetMaxWidth (args->GetNewValue()->AsDouble ()))
755 // dirty = true;
757 UpdateBounds (true);
760 return;
763 if (args->GetId () == TextBlock::FontFamilyProperty) {
764 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
765 char **families, *fragment;
766 int i;
768 CleanupDownloaders (false);
770 if (family && family->source) {
771 families = g_strsplit (family->source, ",", -1);
772 for (i = 0; families[i]; i++) {
773 g_strstrip (families[i]);
774 if ((fragment = strchr (families[i], '#'))) {
775 // the first portion of this string is the resource name...
776 *fragment = '\0';
777 AddFontResource (families[i]);
780 g_strfreev (families);
783 if (UpdateFontDescriptions (false))
784 dirty = true;
785 } else if (args->GetId () == TextBlock::FontSizeProperty) {
786 if (UpdateFontDescriptions (false))
787 dirty = true;
788 } else if (args->GetId () == TextBlock::FontStretchProperty) {
789 if (UpdateFontDescriptions (false))
790 dirty = true;
791 } else if (args->GetId () == TextBlock::FontStyleProperty) {
792 if (UpdateFontDescriptions (false))
793 dirty = true;
794 } else if (args->GetId () == TextBlock::FontWeightProperty) {
795 if (UpdateFontDescriptions (false))
796 dirty = true;
797 } else if (args->GetId () == TextBlock::TextProperty) {
798 if (setvalue) {
799 // result of a change to the TextBlock.Text property
800 const char *text = args->GetNewValue() ? args->GetNewValue()->AsString () : NULL;
802 if (!SetTextInternal (text)) {
803 // no change so nothing to invalidate
804 invalidate = false;
805 } else {
806 UpdateLayoutAttributes ();
807 dirty = true;
809 } else {
810 // result of a change to the TextBlock.Inlines property
811 UpdateLayoutAttributes ();
812 invalidate = false;
814 } else if (args->GetId () == TextBlock::TextDecorationsProperty) {
815 dirty = true;
816 } else if (args->GetId () == TextBlock::TextWrappingProperty) {
817 dirty = layout->SetTextWrapping ((TextWrapping) args->GetNewValue()->AsInt32 ());
818 } else if (args->GetId () == TextBlock::InlinesProperty) {
819 if (setvalue) {
820 // result of a change to the TextBlock.Inlines property
821 InlineCollection *inlines = args->GetNewValue() ? args->GetNewValue()->AsInlineCollection () : NULL;
823 setvalue = false;
824 // Note: this will cause UpdateLayoutAttributes() to be called in the TextProperty changed logic above
825 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
826 setvalue = true;
828 dirty = true;
829 } else {
830 // result of a change to the TextBlock.Text property
831 invalidate = false;
833 } else if (args->GetId () == TextBlock::LineStackingStrategyProperty) {
834 dirty = layout->SetLineStackingStrategy ((LineStackingStrategy) args->GetNewValue()->AsInt32 ());
835 } else if (args->GetId () == TextBlock::LineHeightProperty) {
836 dirty = layout->SetLineHeight (args->GetNewValue()->AsDouble ());
837 } else if (args->GetId () == TextBlock::TextAlignmentProperty) {
838 dirty = layout->SetTextAlignment ((TextAlignment) args->GetNewValue()->AsInt32 ());
839 } else if (args->GetId () == TextBlock::PaddingProperty) {
840 dirty = true;
841 } else if (args->GetId () == TextBlock::FontSourceProperty) {
842 FontSource *source = args->GetNewValue () ? args->GetNewValue ()->AsFontSource () : NULL;
843 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
845 g_free (font_source);
847 if (source && source->stream) {
848 font_source = g_strdup_printf ("font-source://%p.%p", this, source);
849 manager->AddResource (font_source, source->stream);
850 } else {
851 font_source = NULL;
855 if (invalidate) {
856 if (dirty) {
857 InvalidateMeasure ();
858 InvalidateArrange ();
859 UpdateBounds (true);
862 Invalidate ();
865 NotifyListenersOfPropertyChange (args, error);
868 void
869 TextBlock::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
871 if (prop && prop->GetId () == TextBlock::ForegroundProperty) {
872 Invalidate ();
873 } else {
874 FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
878 void
879 TextBlock::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
881 InlineCollection *inlines = GetInlines ();
883 if (col != inlines) {
884 FrameworkElement::OnCollectionChanged (col, args);
885 return;
888 if (args->GetChangedAction () == CollectionChangedActionClearing)
889 return;
891 if (!setvalue) {
892 // changes being handled elsewhere...
893 return;
896 setvalue = false;
897 // Note: this will cause UpdateLayoutAttributes() to be called in the TextProperty changed logic above
898 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
899 setvalue = true;
901 InvalidateMeasure ();
902 InvalidateArrange ();
903 UpdateBounds (true);
904 Invalidate ();
907 void
908 TextBlock::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
910 InlineCollection *inlines = GetInlines ();
912 if (col != inlines) {
913 FrameworkElement::OnCollectionItemChanged (col, obj, args);
914 return;
917 if (args->GetId () != Inline::ForegroundProperty) {
918 if (args->GetId () == Run::TextProperty) {
919 // update our TextProperty
920 setvalue = false;
921 // Note: this will cause UpdateLayoutAttributes() to be called in the TextProperty changed logic above
922 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
923 setvalue = true;
924 } else {
925 // likely a font property change...
926 ((Inline *) obj)->UpdateFontDescription (font_source, true);
929 // All non-Foreground property changes require
930 // recalculating layout which can change the bounds.
931 InvalidateMeasure ();
932 InvalidateArrange ();
933 UpdateBounds (true);
934 dirty = true;
935 } else {
936 // A simple Foreground brush change does not require
937 // recalculating layout. Invalidate() and we're done.
940 Invalidate ();
943 void
944 TextBlock::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
946 ((TextBlock *) closure)->DownloaderComplete ((Downloader *) sender);
949 void
950 TextBlock::DownloaderComplete (Downloader *downloader)
952 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
953 char *resource, *filename;
954 InternalDownloader *idl;
955 const char *path;
956 Uri *uri;
958 dirty = true;
959 InvalidateMeasure ();
960 InvalidateArrange ();
962 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
963 if (!(filename = downloader->GetDownloadedFilename (NULL)))
964 return;
966 g_free (filename);
968 if (!(idl = downloader->GetInternalDownloader ()))
969 return;
971 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
972 return;
974 uri = downloader->GetUri ();
976 // If the downloaded file was a zip file, this'll get the path to the
977 // extracted zip directory, else it will simply be the path to the
978 // downloaded file.
979 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
980 return;
982 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
983 manager->AddResource (resource, path);
984 g_free (resource);
986 if (UpdateFontDescriptions (true)) {
987 dirty = true;
989 UpdateBounds (true);
990 Invalidate ();