2009-09-12 Chris Toshok <toshok@ximian.com>
[moon.git] / src / textblock.cpp
blobe5bfc16392833b2edeab661da0fc919ca7976e5f
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);
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 downloaders = g_ptr_array_new ();
316 layout = new TextLayout ();
317 font_source = NULL;
318 source = NULL;
320 actual_height = 0.0;
321 actual_width = 0.0;
322 setvalue = true;
323 was_set = false;
324 dirty = true;
327 TextBlock::~TextBlock ()
329 CleanupDownloaders (true);
330 g_ptr_array_free (downloaders, true);
332 delete layout;
335 void
336 TextBlock::CleanupDownloaders (bool all)
338 Downloader *downloader;
339 guint i;
341 for (i = 0; i < downloaders->len; i++) {
342 downloader = (Downloader *) downloaders->pdata[i];
344 if (all || downloader != source) {
345 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
346 downloader->Abort ();
347 downloader->unref ();
351 g_ptr_array_set_size (downloaders, 0);
353 if (source && !all) {
354 g_ptr_array_add (downloaders, source);
355 } else {
356 source = NULL;
359 if (all) {
360 g_free (font_source);
361 font_source = NULL;
365 void
366 TextBlock::AddFontSource (Downloader *downloader)
368 downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this);
369 g_ptr_array_add (downloaders, downloader);
370 downloader->ref ();
372 if (downloader->Started () || downloader->Completed ()) {
373 if (downloader->Completed ())
374 DownloaderComplete (downloader);
375 } else {
376 // This is what actually triggers the download
377 downloader->Send ();
381 void
382 TextBlock::SetFontSource (Downloader *downloader)
384 CleanupDownloaders (true);
385 source = downloader;
387 if (downloader) {
388 font_source = downloader->GetUri ()->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
389 AddFontSource (downloader);
390 return;
393 UpdateFontDescriptions (true);
394 UpdateBounds (true);
395 Invalidate ();
396 dirty = true;
399 void
400 TextBlock::AddFontResource (const char *resource)
402 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
403 Application *application = Application::GetCurrent ();
404 Downloader *downloader;
405 Surface *surface;
406 char *path;
407 Uri *uri;
409 uri = new Uri ();
411 if (!application || !uri->Parse (resource) || !(path = application->GetResourceAsPath (GetResourceBase(), uri))) {
412 if ((surface = GetSurface ()) && (downloader = surface->CreateDownloader ())) {
413 downloader->Open ("GET", resource, FontPolicy);
414 AddFontSource (downloader);
415 downloader->unref ();
418 delete uri;
420 return;
423 manager->AddResource (resource, path);
424 g_free (path);
425 delete uri;
428 void
429 TextBlock::Render (cairo_t *cr, Region *region, bool path_only)
431 cairo_save (cr);
432 cairo_set_matrix (cr, &absolute_xform);
434 if (!path_only)
435 RenderLayoutClip (cr);
437 Paint (cr);
439 cairo_restore (cr);
442 void
443 TextBlock::ComputeBounds ()
445 Size actual (GetActualWidth (), GetActualHeight ());
446 Size framework = ApplySizeConstraints (actual);
448 framework = framework.Max (actual);
450 Rect extents = Rect (0,0,framework.width, framework.height);
452 bounds = bounds_with_children = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
455 void
456 TextBlock::GetSizeForBrush (cairo_t *cr, double *width, double *height)
458 *width = actual_width;
459 *height = actual_height;
462 Point
463 TextBlock::GetTransformOrigin ()
465 Point *user_xform_origin = GetRenderTransformOrigin ();
466 return Point (actual_width * user_xform_origin->x,
467 actual_height * user_xform_origin->y);
470 Size
471 TextBlock::ComputeActualSize ()
473 Thickness padding = *GetPadding ();
474 Size result = FrameworkElement::ComputeActualSize ();
476 if (!LayoutInformation::GetPreviousConstraint (this)) {
477 Size constraint = Size (INFINITY, INFINITY);
479 constraint = ApplySizeConstraints (constraint);
481 constraint = constraint.GrowBy (-padding);
482 Layout (constraint);
485 result = Size (actual_width, actual_height);
486 result = result.GrowBy (padding);
488 return result;
491 Size
492 TextBlock::MeasureOverride (Size availableSize)
494 Thickness padding = *GetPadding ();
495 Size constraint;
496 Size desired;
498 constraint = availableSize.GrowBy (-padding);
499 Layout (constraint);
501 desired = Size (actual_width, actual_height).GrowBy (padding);
503 SetActualHeight (desired.height);
504 SetActualWidth (desired.width);
506 if (GetUseLayoutRounding ())
507 desired.width = ceil (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 if (changed) {
603 InvalidateMeasure ();
604 InvalidateArrange ();
605 UpdateBounds (true);
606 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 void
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 run->unref ();
725 } else {
726 // setting text to null results in String.Empty
727 SetValue (TextBlock::TextProperty, Value (""));
730 setvalue = true;
733 void
734 TextBlock::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
736 bool invalidate = true;
738 if (args->GetProperty ()->GetOwnerType () != Type::TEXTBLOCK) {
739 FrameworkElement::OnPropertyChanged (args, error);
741 if (args->GetId () == FrameworkElement::LanguageProperty) {
742 // a change in xml:lang might change font characteristics
743 if (UpdateFontDescriptions (false)) {
744 InvalidateMeasure ();
745 InvalidateArrange ();
746 UpdateBounds (true);
747 dirty = true;
752 if (args->GetId () == FrameworkElement::WidthProperty) {
753 //if (layout->SetMaxWidth (args->GetNewValue()->AsDouble ()))
754 // dirty = true;
756 UpdateBounds (true);
759 return;
762 if (args->GetId () == TextBlock::FontFamilyProperty) {
763 FontFamily *family = args->GetNewValue () ? args->GetNewValue ()->AsFontFamily () : NULL;
764 char **families, *fragment;
765 int i;
767 CleanupDownloaders (false);
769 if (family && family->source) {
770 families = g_strsplit (family->source, ",", -1);
771 for (i = 0; families[i]; i++) {
772 g_strstrip (families[i]);
773 if ((fragment = strchr (families[i], '#'))) {
774 // the first portion of this string is the resource name...
775 *fragment = '\0';
776 AddFontResource (families[i]);
779 g_strfreev (families);
782 if (UpdateFontDescriptions (false))
783 dirty = true;
784 } else if (args->GetId () == TextBlock::FontSizeProperty) {
785 if (UpdateFontDescriptions (false))
786 dirty = true;
787 } else if (args->GetId () == TextBlock::FontStretchProperty) {
788 if (UpdateFontDescriptions (false))
789 dirty = true;
790 } else if (args->GetId () == TextBlock::FontStyleProperty) {
791 if (UpdateFontDescriptions (false))
792 dirty = true;
793 } else if (args->GetId () == TextBlock::FontWeightProperty) {
794 if (UpdateFontDescriptions (false))
795 dirty = true;
796 } else if (args->GetId () == TextBlock::TextProperty) {
797 if (setvalue) {
798 // result of a change to the TextBlock.Text property
799 const char *text = args->GetNewValue() ? args->GetNewValue()->AsString () : NULL;
801 SetTextInternal (text);
802 UpdateLayoutAttributes ();
803 dirty = true;
804 } else {
805 // result of a change to the TextBlock.Inlines property
806 UpdateLayoutAttributes ();
807 invalidate = false;
809 } else if (args->GetId () == TextBlock::TextDecorationsProperty) {
810 dirty = true;
811 } else if (args->GetId () == TextBlock::TextWrappingProperty) {
812 dirty = layout->SetTextWrapping ((TextWrapping) args->GetNewValue()->AsInt32 ());
813 } else if (args->GetId () == TextBlock::InlinesProperty) {
814 if (setvalue) {
815 // result of a change to the TextBlock.Inlines property
816 InlineCollection *inlines = args->GetNewValue() ? args->GetNewValue()->AsInlineCollection () : NULL;
818 setvalue = false;
819 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
820 setvalue = true;
822 UpdateLayoutAttributes ();
823 dirty = true;
824 } else {
825 // this should be the result of Inlines being autocreated
826 UpdateLayoutAttributes ();
827 invalidate = false;
829 } else if (args->GetId () == TextBlock::LineStackingStrategyProperty) {
830 dirty = layout->SetLineStackingStrategy ((LineStackingStrategy) args->GetNewValue()->AsInt32 ());
831 } else if (args->GetId () == TextBlock::LineHeightProperty) {
832 dirty = layout->SetLineHeight (args->GetNewValue()->AsDouble ());
833 } else if (args->GetId () == TextBlock::TextAlignmentProperty) {
834 dirty = layout->SetTextAlignment ((TextAlignment) args->GetNewValue()->AsInt32 ());
835 } else if (args->GetId () == TextBlock::PaddingProperty) {
836 dirty = true;
837 } else if (args->GetId () == TextBlock::FontSourceProperty) {
838 FontSource *source = args->GetNewValue () ? args->GetNewValue ()->AsFontSource () : NULL;
839 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
841 // FIXME: ideally we'd remove the old item from the cache (or,
842 // rather, 'unref' it since some other textblocks/boxes might
843 // still be using it).
845 g_free (font_source);
847 if (source && source->stream)
848 font_source = manager->AddResource (source->stream);
849 else
850 font_source = NULL;
852 UpdateFontDescriptions (true);
853 dirty = true;
856 if (invalidate) {
857 if (dirty) {
858 InvalidateMeasure ();
859 InvalidateArrange ();
860 UpdateBounds (true);
863 Invalidate ();
866 NotifyListenersOfPropertyChange (args, error);
869 void
870 TextBlock::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
872 if (prop && prop->GetId () == TextBlock::ForegroundProperty) {
873 Invalidate ();
874 } else {
875 FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
879 void
880 TextBlock::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
882 InlineCollection *inlines = GetInlines ();
884 if (col != inlines) {
885 FrameworkElement::OnCollectionChanged (col, args);
886 return;
889 if (args->GetChangedAction () == CollectionChangedActionClearing)
890 return;
892 if (!setvalue) {
893 // changes being handled elsewhere...
894 return;
897 setvalue = false;
898 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
899 setvalue = true;
901 UpdateLayoutAttributes ();
902 InvalidateMeasure ();
903 InvalidateArrange ();
904 UpdateBounds (true);
905 Invalidate ();
908 void
909 TextBlock::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
911 InlineCollection *inlines = GetInlines ();
913 if (col != inlines) {
914 FrameworkElement::OnCollectionItemChanged (col, obj, args);
915 return;
918 if (args->GetId () != Inline::ForegroundProperty) {
919 if (args->GetId () == Run::TextProperty) {
920 // update our TextProperty
921 setvalue = false;
922 SetValue (TextBlock::TextProperty, Value (GetTextInternal (inlines), true));
923 setvalue = true;
925 UpdateLayoutAttributes ();
926 } else {
927 // likely a font property change...
928 ((Inline *) obj)->UpdateFontDescription (font_source, true);
931 // All non-Foreground property changes require
932 // recalculating layout which can change the bounds.
933 InvalidateMeasure ();
934 InvalidateArrange ();
935 UpdateBounds (true);
936 dirty = true;
937 } else {
938 // A simple Foreground brush change does not require
939 // recalculating layout. Invalidate() and we're done.
942 Invalidate ();
945 void
946 TextBlock::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
948 ((TextBlock *) closure)->DownloaderComplete ((Downloader *) sender);
951 void
952 TextBlock::DownloaderComplete (Downloader *downloader)
954 FontManager *manager = Deployment::GetCurrent ()->GetFontManager ();
955 char *resource, *filename;
956 InternalDownloader *idl;
957 const char *path;
958 Uri *uri;
960 dirty = true;
961 InvalidateMeasure ();
962 InvalidateArrange ();
964 // get the downloaded file path (enforces a mozilla workaround for files smaller than 64k)
965 if (!(filename = downloader->GetDownloadedFilename (NULL)))
966 return;
968 g_free (filename);
970 if (!(idl = downloader->GetInternalDownloader ()))
971 return;
973 if (!(idl->GetObjectType () == Type::FILEDOWNLOADER))
974 return;
976 uri = downloader->GetUri ();
978 // If the downloaded file was a zip file, this'll get the path to the
979 // extracted zip directory, else it will simply be the path to the
980 // downloaded file.
981 if (!(path = ((FileDownloader *) idl)->GetUnzippedPath ()))
982 return;
984 resource = uri->ToString ((UriToStringFlags) (UriHidePasswd | UriHideQuery | UriHideFragment));
985 manager->AddResource (resource, path);
986 g_free (resource);
988 if (UpdateFontDescriptions (true)) {
989 dirty = true;
991 UpdateBounds (true);
992 Invalidate ();