Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(543)

Side by Side Diff: chrome/browser/ui/cocoa/tabpose_window.mm

Issue 5618007: Mac: make favicon and title visible during in/out animation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/xcodebuild/Release
Patch Set: '' Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/tabpose_window.h" 5 #import "chrome/browser/ui/cocoa/tabpose_window.h"
6 6
7 #import <QuartzCore/QuartzCore.h> 7 #import <QuartzCore/QuartzCore.h>
8 8
9 #include "app/resource_bundle.h" 9 #include "app/resource_bundle.h"
10 #include "base/mac_util.h" 10 #include "base/mac_util.h"
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 class Tile { 368 class Tile {
369 public: 369 public:
370 Tile() {} 370 Tile() {}
371 371
372 // Returns the rectangle this thumbnail is at at the beginning of the zoom-in 372 // Returns the rectangle this thumbnail is at at the beginning of the zoom-in
373 // animation. |tile| is the rectangle that's covering the whole tab area when 373 // animation. |tile| is the rectangle that's covering the whole tab area when
374 // the animation starts. 374 // the animation starts.
375 NSRect GetStartRectRelativeTo(const Tile& tile) const; 375 NSRect GetStartRectRelativeTo(const Tile& tile) const;
376 NSRect thumb_rect() const { return thumb_rect_; } 376 NSRect thumb_rect() const { return thumb_rect_; }
377 377
378 NSRect GetFaviconStartRectRelativeTo(const Tile& tile) const;
378 NSRect favicon_rect() const { return favicon_rect_; } 379 NSRect favicon_rect() const { return favicon_rect_; }
379 SkBitmap favicon() const; 380 SkBitmap favicon() const;
380 381
381 // This changes |title_rect| and |favicon_rect| such that the favicon is on 382 // This changes |title_rect| and |favicon_rect| such that the favicon is on
382 // the font's baseline and that the minimum distance between thumb rect and 383 // the font's baseline and that the minimum distance between thumb rect and
383 // favicon and title rects doesn't change. 384 // favicon and title rects doesn't change.
384 // The view code 385 // The view code
385 // 1. queries desired font size by calling |title_font_size()| 386 // 1. queries desired font size by calling |title_font_size()|
386 // 2. loads that font 387 // 2. loads that font
387 // 3. calls |set_font_metrics()| which updates the title rect 388 // 3. calls |set_font_metrics()| which updates the title rect
388 // 4. receives the title rect and puts the title on it with the font from 2. 389 // 4. receives the title rect and puts the title on it with the font from 2.
389 void set_font_metrics(CGFloat ascender, CGFloat descender); 390 void set_font_metrics(CGFloat ascender, CGFloat descender);
390 CGFloat title_font_size() const { return title_font_size_; } 391 CGFloat title_font_size() const { return title_font_size_; }
391 392
393 NSRect GetTitleStartRectRelativeTo(const Tile& tile) const;
392 NSRect title_rect() const { return title_rect_; } 394 NSRect title_rect() const { return title_rect_; }
393 395
394 // Returns an unelided title. The view logic is responsible for eliding. 396 // Returns an unelided title. The view logic is responsible for eliding.
395 const string16& title() const { return contents_->GetTitle(); } 397 const string16& title() const { return contents_->GetTitle(); }
396 398
397 TabContents* tab_contents() const { return contents_; } 399 TabContents* tab_contents() const { return contents_; }
398 void set_tab_contents(TabContents* new_contents) { contents_ = new_contents; } 400 void set_tab_contents(TabContents* new_contents) { contents_ = new_contents; }
399 401
400 private: 402 private:
401 friend class TileSet; 403 friend class TileSet;
(...skipping 13 matching lines...) Expand all
415 DISALLOW_COPY_AND_ASSIGN(Tile); 417 DISALLOW_COPY_AND_ASSIGN(Tile);
416 }; 418 };
417 419
418 NSRect Tile::GetStartRectRelativeTo(const Tile& tile) const { 420 NSRect Tile::GetStartRectRelativeTo(const Tile& tile) const {
419 NSRect rect = start_thumb_rect_; 421 NSRect rect = start_thumb_rect_;
420 rect.origin.x -= tile.start_thumb_rect_.origin.x; 422 rect.origin.x -= tile.start_thumb_rect_.origin.x;
421 rect.origin.y -= tile.start_thumb_rect_.origin.y; 423 rect.origin.y -= tile.start_thumb_rect_.origin.y;
422 return rect; 424 return rect;
423 } 425 }
424 426
427 NSRect Tile::GetFaviconStartRectRelativeTo(const Tile& tile) const {
428 NSRect thumb_start = GetStartRectRelativeTo(tile);
429 NSRect rect = favicon_rect_;
430 rect.origin.x += NSMinX(thumb_start) - NSMinX(thumb_rect_);
431 rect.origin.y += NSMinY(thumb_start) - NSMinY(thumb_rect_);
432 return rect;
433 }
434
425 SkBitmap Tile::favicon() const { 435 SkBitmap Tile::favicon() const {
426 if (contents_->is_app()) { 436 if (contents_->is_app()) {
427 SkBitmap* icon = contents_->GetExtensionAppIcon(); 437 SkBitmap* icon = contents_->GetExtensionAppIcon();
428 if (icon) 438 if (icon)
429 return *icon; 439 return *icon;
430 } 440 }
431 return contents_->GetFavIcon(); 441 return contents_->GetFavIcon();
432 } 442 }
433 443
444 NSRect Tile::GetTitleStartRectRelativeTo(const Tile& tile) const {
445 NSRect thumb_start = GetStartRectRelativeTo(tile);
446 NSRect rect = title_rect_;
447 rect.origin.x += NSMinX(thumb_start) - NSMinX(thumb_rect_);
448 rect.origin.y += NSMinY(thumb_start) - NSMinY(thumb_rect_);
449 return rect;
450 }
451
434 // Changes |title_rect| and |favicon_rect| such that the favicon is on the 452 // Changes |title_rect| and |favicon_rect| such that the favicon is on the
435 // font's baseline and that the minimum distance between thumb rect and 453 // font's baseline and that the minimum distance between thumb rect and
436 // favicon and title rects doesn't change. 454 // favicon and title rects doesn't change.
437 void Tile::set_font_metrics(CGFloat ascender, CGFloat descender) { 455 void Tile::set_font_metrics(CGFloat ascender, CGFloat descender) {
438 title_rect_.origin.y -= ascender + descender - NSHeight(title_rect_); 456 title_rect_.origin.y -= ascender + descender - NSHeight(title_rect_);
439 title_rect_.size.height = ascender + descender; 457 title_rect_.size.height = ascender + descender;
440 458
441 if (NSHeight(favicon_rect_) < ascender) { 459 if (NSHeight(favicon_rect_) < ascender) {
442 // Move favicon down. 460 // Move favicon down.
443 favicon_rect_.origin.y = title_rect_.origin.y + descender; 461 favicon_rect_.origin.y = title_rect_.origin.y + descender;
(...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after
837 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 855 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
838 856
839 // Update the layer's position so that the layer doesn't snap back when the 857 // Update the layer's position so that the layer doesn't snap back when the
840 // animation completes. 858 // animation completes.
841 layer.position = NSPointToCGPoint(point); 859 layer.position = NSPointToCGPoint(point);
842 860
843 // Add the animation, overriding the implicit animation. 861 // Add the animation, overriding the implicit animation.
844 [layer addAnimation:animation forKey:@"position"]; 862 [layer addAnimation:animation forKey:@"position"];
845 } 863 }
846 864
865 void AnimateCALayerOpacityFromTo(
866 CALayer* layer, double from, double to, NSTimeInterval duration) {
867 CABasicAnimation* animation;
868 animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
869 animation.fromValue = [NSNumber numberWithFloat:from];
870 animation.toValue = [NSNumber numberWithFloat:to];
871 animation.duration = duration;
872
873 layer.opacity = to;
874 // Add the animation, overriding the implicit animation.
875 [layer addAnimation:animation forKey:@"opacity"];
876 }
877
847 @interface TabposeWindow (Private) 878 @interface TabposeWindow (Private)
848 - (id)initForWindow:(NSWindow*)parent 879 - (id)initForWindow:(NSWindow*)parent
849 rect:(NSRect)rect 880 rect:(NSRect)rect
850 slomo:(BOOL)slomo 881 slomo:(BOOL)slomo
851 tabStripModel:(TabStripModel*)tabStripModel; 882 tabStripModel:(TabStripModel*)tabStripModel;
852 - (void)setUpLayersInSlomo:(BOOL)slomo; 883 - (void)setUpLayersInSlomo:(BOOL)slomo;
853 - (void)fadeAway:(BOOL)slomo; 884 - (void)fadeAway:(BOOL)slomo;
854 - (void)selectTileAtIndex:(int)newIndex; 885 - (void)selectTileAtIndex:(int)newIndex;
855 @end 886 @end
856 887
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
910 - (void)addLayersForTile:(tabpose::Tile&)tile 941 - (void)addLayersForTile:(tabpose::Tile&)tile
911 showZoom:(BOOL)showZoom 942 showZoom:(BOOL)showZoom
912 slomo:(BOOL)slomo 943 slomo:(BOOL)slomo
913 animationDelegate:(id)animationDelegate { 944 animationDelegate:(id)animationDelegate {
914 scoped_nsobject<CALayer> layer([[ThumbnailLayer alloc] 945 scoped_nsobject<CALayer> layer([[ThumbnailLayer alloc]
915 initWithTabContents:tile.tab_contents() 946 initWithTabContents:tile.tab_contents()
916 fullSize:tile.GetStartRectRelativeTo( 947 fullSize:tile.GetStartRectRelativeTo(
917 tileSet_->selected_tile()).size]); 948 tileSet_->selected_tile()).size]);
918 [layer setNeedsDisplay]; 949 [layer setNeedsDisplay];
919 950
951 NSTimeInterval interval =
952 kDefaultAnimationDuration * (slomo ? kSlomoFactor : 1);
953
920 // Background color as placeholder for now. 954 // Background color as placeholder for now.
921 layer.get().backgroundColor = CGColorGetConstantColor(kCGColorWhite); 955 layer.get().backgroundColor = CGColorGetConstantColor(kCGColorWhite);
922 if (showZoom) { 956 if (showZoom) {
viettrungluu 2010/12/09 19:09:30 Can you make the three cases of |if (showZoom) { .
Nico 2010/12/09 19:12:03 I did, I extracted the common code to a function!
viettrungluu 2010/12/09 20:15:42 I'd like an "AnimateOrSetFrame()" (or whatever) fu
923 AnimateCALayerFrameFromTo( 957 AnimateCALayerFrameFromTo(
924 layer, 958 layer,
925 tile.GetStartRectRelativeTo(tileSet_->selected_tile()), 959 tile.GetStartRectRelativeTo(tileSet_->selected_tile()),
926 tile.thumb_rect(), 960 tile.thumb_rect(),
927 kDefaultAnimationDuration * (slomo ? kSlomoFactor : 1), 961 interval,
928 animationDelegate); 962 animationDelegate);
929 } else { 963 } else {
930 layer.get().frame = NSRectToCGRect(tile.thumb_rect()); 964 layer.get().frame = NSRectToCGRect(tile.thumb_rect());
931 } 965 }
932 966
933 layer.get().shadowRadius = 10; 967 layer.get().shadowRadius = 10;
934 layer.get().shadowOffset = CGSizeMake(0, -10); 968 layer.get().shadowOffset = CGSizeMake(0, -10);
935 if (state_ == tabpose::kFadedIn) 969 if (state_ == tabpose::kFadedIn)
936 layer.get().shadowOpacity = 0.5; 970 layer.get().shadowOpacity = 0.5;
937 971
(...skipping 10 matching lines...) Expand all
948 if (!nsFavicon) { 982 if (!nsFavicon) {
949 NSImage* defaultFavIcon = 983 NSImage* defaultFavIcon =
950 ResourceBundle::GetSharedInstance().GetNativeImageNamed( 984 ResourceBundle::GetSharedInstance().GetNativeImageNamed(
951 IDR_DEFAULT_FAVICON); 985 IDR_DEFAULT_FAVICON);
952 nsFavicon = defaultFavIcon; 986 nsFavicon = defaultFavIcon;
953 } 987 }
954 base::mac::ScopedCFTypeRef<CGImageRef> favicon( 988 base::mac::ScopedCFTypeRef<CGImageRef> favicon(
955 mac_util::CopyNSImageToCGImage(nsFavicon)); 989 mac_util::CopyNSImageToCGImage(nsFavicon));
956 990
957 CALayer* faviconLayer = [CALayer layer]; 991 CALayer* faviconLayer = [CALayer layer];
958 faviconLayer.frame = NSRectToCGRect(tile.favicon_rect()); 992 if (showZoom) {
993 AnimateCALayerFrameFromTo(
994 faviconLayer,
995 tile.GetFaviconStartRectRelativeTo(tileSet_->selected_tile()),
996 tile.favicon_rect(),
997 interval,
998 nil);
999 AnimateCALayerOpacityFromTo(faviconLayer, 0.0, 1.0, interval);
1000 } else {
1001 faviconLayer.frame = NSRectToCGRect(tile.favicon_rect());
1002 }
959 faviconLayer.contents = (id)favicon.get(); 1003 faviconLayer.contents = (id)favicon.get();
960 faviconLayer.zPosition = 1; // On top of the thumb shadow. 1004 faviconLayer.zPosition = 1; // On top of the thumb shadow.
961 if (state_ == tabpose::kFadingIn)
962 faviconLayer.hidden = YES;
963 [bgLayer_ addSublayer:faviconLayer]; 1005 [bgLayer_ addSublayer:faviconLayer];
964 [allFaviconLayers_ addObject:faviconLayer]; 1006 [allFaviconLayers_ addObject:faviconLayer];
965 1007
966 CATextLayer* titleLayer = [CATextLayer layer]; 1008 CATextLayer* titleLayer = [CATextLayer layer];
967 titleLayer.frame = NSRectToCGRect(tile.title_rect()); 1009 if (showZoom) {
1010 AnimateCALayerFrameFromTo(
1011 titleLayer,
1012 tile.GetTitleStartRectRelativeTo(tileSet_->selected_tile()),
1013 tile.title_rect(),
1014 interval,
1015 nil);
1016 AnimateCALayerOpacityFromTo(titleLayer, 0.0, 1.0, interval);
1017 } else {
1018 titleLayer.frame = NSRectToCGRect(tile.title_rect());
1019 }
968 titleLayer.string = base::SysUTF16ToNSString(tile.title()); 1020 titleLayer.string = base::SysUTF16ToNSString(tile.title());
969 titleLayer.fontSize = [font pointSize]; 1021 titleLayer.fontSize = [font pointSize];
970 titleLayer.truncationMode = kCATruncationEnd; 1022 titleLayer.truncationMode = kCATruncationEnd;
971 titleLayer.font = font; 1023 titleLayer.font = font;
972 titleLayer.zPosition = 1; // On top of the thumb shadow. 1024 titleLayer.zPosition = 1; // On top of the thumb shadow.
973 if (state_ == tabpose::kFadingIn)
974 titleLayer.hidden = YES;
975 [bgLayer_ addSublayer:titleLayer]; 1025 [bgLayer_ addSublayer:titleLayer];
976 [allTitleLayers_ addObject:titleLayer]; 1026 [allTitleLayers_ addObject:titleLayer];
977 } 1027 }
978 1028
979 - (void)setUpLayersInSlomo:(BOOL)slomo { 1029 - (void)setUpLayersInSlomo:(BOOL)slomo {
980 // Root layer -- covers whole window. 1030 // Root layer -- covers whole window.
981 rootLayer_ = [CALayer layer]; 1031 rootLayer_ = [CALayer layer];
982 1032
983 // In a block so that the layers don't fade in. 1033 // In a block so that the layers don't fade in.
984 { 1034 {
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after
1194 DCHECK_EQ(tileSet_->selected_index(), 0); 1244 DCHECK_EQ(tileSet_->selected_index(), 0);
1195 } 1245 }
1196 1246
1197 { 1247 {
1198 ScopedCAActionDisabler disableCAActions; 1248 ScopedCAActionDisabler disableCAActions;
1199 1249
1200 // Move the selected layer on top of all other layers. 1250 // Move the selected layer on top of all other layers.
1201 [self selectedLayer].zPosition = 1; 1251 [self selectedLayer].zPosition = 1;
1202 1252
1203 selectionHighlight_.hidden = YES; 1253 selectionHighlight_.hidden = YES;
1204 for (CALayer* layer in allFaviconLayers_.get())
1205 layer.hidden = YES;
1206 for (CALayer* layer in allTitleLayers_.get())
1207 layer.hidden = YES;
1208
1209 // Running animations with shadows is slow, so turn shadows off before 1254 // Running animations with shadows is slow, so turn shadows off before
1210 // running the exit animation. 1255 // running the exit animation.
1211 for (CALayer* layer in allThumbnailLayers_.get()) 1256 for (CALayer* layer in allThumbnailLayers_.get())
1212 layer.shadowOpacity = 0.0; 1257 layer.shadowOpacity = 0.0;
1213 } 1258 }
1214 1259
1215 // Animate layers out, all in one transaction. 1260 // Animate layers out, all in one transaction.
1216 CGFloat duration = 1261 CGFloat duration =
1217 1.3 * kDefaultAnimationDuration * (slomo ? kSlomoFactor : 1); 1262 1.3 * kDefaultAnimationDuration * (slomo ? kSlomoFactor : 1);
1218 ScopedCAActionSetDuration durationSetter(duration); 1263 ScopedCAActionSetDuration durationSetter(duration);
1219 for (NSUInteger i = 0; i < [allThumbnailLayers_ count]; ++i) { 1264 for (int i = 0; i < tabStripModel_->count(); ++i) {
1265 const tabpose::Tile& tile = tileSet_->tile_at(i);
1220 CALayer* layer = [allThumbnailLayers_ objectAtIndex:i]; 1266 CALayer* layer = [allThumbnailLayers_ objectAtIndex:i];
1221 // |start_thumb_rect_| was relative to the initial index, now this needs to
1222 // be relative to |selectedIndex_| (whose start rect was relative to
1223 // the initial index, too).
1224 CGRect newFrame = NSRectToCGRect(
1225 tileSet_->tile_at(i).GetStartRectRelativeTo(tileSet_->selected_tile()));
1226
1227 // Add a delegate to one of the implicit animations to get a notification 1267 // Add a delegate to one of the implicit animations to get a notification
1228 // once the animations are done. 1268 // once the animations are done.
1229 if (static_cast<int>(i) == tileSet_->selected_index()) { 1269 if (static_cast<int>(i) == tileSet_->selected_index()) {
1230 CAAnimation* animation = [CAAnimation animation]; 1270 CAAnimation* animation = [CAAnimation animation];
1231 animation.delegate = self; 1271 animation.delegate = self;
1232 [animation setValue:kAnimationIdFadeOut forKey:kAnimationIdKey]; 1272 [animation setValue:kAnimationIdFadeOut forKey:kAnimationIdKey];
1233 [layer addAnimation:animation forKey:@"frame"]; 1273 [layer addAnimation:animation forKey:@"frame"];
1234 } 1274 }
1235 1275
1236 layer.frame = newFrame; 1276 layer.frame = NSRectToCGRect(
1277 tile.GetStartRectRelativeTo(tileSet_->selected_tile()));
1237 1278
1238 if (static_cast<int>(i) == tileSet_->selected_index()) { 1279 if (static_cast<int>(i) == tileSet_->selected_index()) {
1239 // Redraw layer at big resolution, so that zoom-in isn't blocky. 1280 // Redraw layer at big resolution, so that zoom-in isn't blocky.
1240 [layer setNeedsDisplay]; 1281 [layer setNeedsDisplay];
1241 } 1282 }
1283
1284 CALayer* faviconLayer = [allFaviconLayers_ objectAtIndex:i];
1285 faviconLayer.frame = NSRectToCGRect(
1286 tile.GetFaviconStartRectRelativeTo(tileSet_->selected_tile()));
1287 faviconLayer.opacity = 0;
1288 CALayer* titleLayer = [allTitleLayers_ objectAtIndex:i];
1289 titleLayer.frame = NSRectToCGRect(
1290 tile.GetTitleStartRectRelativeTo(tileSet_->selected_tile()));
1291 titleLayer.opacity = 0;
1242 } 1292 }
1243 } 1293 }
1244 1294
1245 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished { 1295 - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished {
1246 NSString* animationId = [animation valueForKey:kAnimationIdKey]; 1296 NSString* animationId = [animation valueForKey:kAnimationIdKey];
1247 if ([animationId isEqualToString:kAnimationIdFadeIn]) { 1297 if ([animationId isEqualToString:kAnimationIdFadeIn]) {
1248 if (finished && state_ == tabpose::kFadingIn) { 1298 if (finished && state_ == tabpose::kFadingIn) {
1249 // If the user clicks while the fade in animation is still running, 1299 // If the user clicks while the fade in animation is still running,
1250 // |state_| is already kFadingOut. In that case, don't do anything. 1300 // |state_| is already kFadingOut. In that case, don't do anything.
1251 state_ = tabpose::kFadedIn; 1301 state_ = tabpose::kFadedIn;
1252 1302
1253 selectionHighlight_.hidden = NO; 1303 selectionHighlight_.hidden = NO;
1254 for (CALayer* layer in allFaviconLayers_.get())
1255 layer.hidden = NO;
1256 for (CALayer* layer in allTitleLayers_.get())
1257 layer.hidden = NO;
1258 1304
1259 // Running animations with shadows is slow, so turn shadows on only after 1305 // Running animations with shadows is slow, so turn shadows on only after
1260 // the animation is done. 1306 // the animation is done.
1261 ScopedCAActionDisabler disableCAActions; 1307 ScopedCAActionDisabler disableCAActions;
1262 for (CALayer* layer in allThumbnailLayers_.get()) 1308 for (CALayer* layer in allThumbnailLayers_.get())
1263 layer.shadowOpacity = 0.5; 1309 layer.shadowOpacity = 0.5;
1264 } 1310 }
1265 } else if ([animationId isEqualToString:kAnimationIdFadeOut]) { 1311 } else if ([animationId isEqualToString:kAnimationIdFadeOut]) {
1266 DCHECK_EQ(tabpose::kFadingOut, state_); 1312 DCHECK_EQ(tabpose::kFadingOut, state_);
1267 [self close]; 1313 [self close];
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
1427 tile.set_tab_contents(contents->tab_contents()); 1473 tile.set_tab_contents(contents->tab_contents());
1428 ThumbnailLayer* thumbLayer = [allThumbnailLayers_ objectAtIndex:index]; 1474 ThumbnailLayer* thumbLayer = [allThumbnailLayers_ objectAtIndex:index];
1429 [thumbLayer setTabContents:contents->tab_contents()]; 1475 [thumbLayer setTabContents:contents->tab_contents()];
1430 } 1476 }
1431 1477
1432 - (void)tabStripModelDeleted { 1478 - (void)tabStripModelDeleted {
1433 [self close]; 1479 [self close];
1434 } 1480 }
1435 1481
1436 @end 1482 @end
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698