mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-30 19:20:37 +00:00
Dummy implementation of new methods.
[dissolveToPoint:fromRect:fraction:] Move the drawing rectangle to the origin of the image rep before the intersection of the two rects. [TIFFRepresentationUsingCompression:factor:] and [TIFFRepresentation] call method on NSBitmapImageRep. List explicit flag settings in [init]. Moved background drawing to [drawRepresentation:inRect:]. Restructured [_doImageCache] and [_cacheForRep:]. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@10679 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
f9054d3ecf
commit
468daba33d
1 changed files with 121 additions and 78 deletions
199
Source/NSImage.m
199
Source/NSImage.m
|
@ -170,6 +170,8 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
NSArray *array;
|
NSArray *array;
|
||||||
NSString *the_name = aName;
|
NSString *the_name = aName;
|
||||||
|
|
||||||
|
// FIXME: This should use [NSBundle pathForImageResource], but this will
|
||||||
|
// only allow imageUnfilteredFileTypes.
|
||||||
/* If there is no image with that name, search in the main bundle */
|
/* If there is no image with that name, search in the main bundle */
|
||||||
main_bundle = [NSBundle mainBundle];
|
main_bundle = [NSBundle mainBundle];
|
||||||
ext = [aName pathExtension];
|
ext = [aName pathExtension];
|
||||||
|
@ -253,16 +255,6 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) isFlipped
|
|
||||||
{
|
|
||||||
return _flags.flipDraw;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) setFlipped: (BOOL)flag
|
|
||||||
{
|
|
||||||
_flags.flipDraw = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id) init
|
- (id) init
|
||||||
{
|
{
|
||||||
return [self initWithSize: NSMakeSize(0, 0)];
|
return [self initWithSize: NSMakeSize(0, 0)];
|
||||||
|
@ -272,17 +264,24 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
- (id) initWithSize: (NSSize)aSize
|
- (id) initWithSize: (NSSize)aSize
|
||||||
{
|
{
|
||||||
[super init];
|
[super init];
|
||||||
_reps = [[NSMutableArray alloc] initWithCapacity: 2];
|
|
||||||
|
//_flags.archiveByName = NO;
|
||||||
|
//_flags.scalable = NO;
|
||||||
|
//_flags.dataRetained = NO;
|
||||||
|
//_flags.flipDraw = NO;
|
||||||
if (aSize.width && aSize.height)
|
if (aSize.width && aSize.height)
|
||||||
{
|
{
|
||||||
_size = aSize;
|
_size = aSize;
|
||||||
_flags.sizeWasExplicitlySet = YES;
|
_flags.sizeWasExplicitlySet = YES;
|
||||||
}
|
}
|
||||||
|
//_flags.usesEPSOnResolutionMismatch = NO;
|
||||||
_flags.colorMatchPreferred = YES;
|
_flags.colorMatchPreferred = YES;
|
||||||
_flags.multipleResolutionMatching = YES;
|
_flags.multipleResolutionMatching = YES;
|
||||||
//_flags.usesEPSOnResolutionMismatch = NO;
|
//_flags.cacheSeparately = NO;
|
||||||
//_flags.flipDraw = NO;
|
//_flags.unboundedCacheDepth = NO;
|
||||||
_color = RETAIN(clearColor);
|
//_flags.syncLoad = NO;
|
||||||
|
_reps = [[NSMutableArray alloc] initWithCapacity: 2];
|
||||||
|
ASSIGN(_color, clearColor);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -290,9 +289,7 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
- (id) initByReferencingFile: (NSString *)fileName
|
- (id) initByReferencingFile: (NSString *)fileName
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
// FIXME: The documentation says to archive only the file name,
|
|
||||||
// this has to be stored somewhere!
|
|
||||||
_flags.dataRetained = YES;
|
|
||||||
if (![self _useFromFile: fileName])
|
if (![self _useFromFile: fileName])
|
||||||
{
|
{
|
||||||
RELEASE(self);
|
RELEASE(self);
|
||||||
|
@ -305,7 +302,8 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
- (id) initWithContentsOfFile: (NSString *)fileName
|
- (id) initWithContentsOfFile: (NSString *)fileName
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
//_flags.dataRetained = YES;
|
|
||||||
|
_flags.dataRetained = YES;
|
||||||
if (![self _loadFromFile: fileName])
|
if (![self _loadFromFile: fileName])
|
||||||
{
|
{
|
||||||
RELEASE(self);
|
RELEASE(self);
|
||||||
|
@ -318,6 +316,8 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
- (id) initWithData: (NSData *)data;
|
- (id) initWithData: (NSData *)data;
|
||||||
{
|
{
|
||||||
self = [self init];
|
self = [self init];
|
||||||
|
|
||||||
|
_flags.dataRetained = YES;
|
||||||
if (![self _loadFromData: data])
|
if (![self _loadFromData: data])
|
||||||
{
|
{
|
||||||
RELEASE(self);
|
RELEASE(self);
|
||||||
|
@ -366,6 +366,7 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
self = [self init];
|
self = [self init];
|
||||||
|
_flags.dataRetained = YES;
|
||||||
[self addRepresentations: array];
|
[self addRepresentations: array];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -387,28 +388,11 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_flags.dataRetained = YES;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setSize: (NSSize)aSize
|
|
||||||
{
|
|
||||||
_size = aSize;
|
|
||||||
_flags.sizeWasExplicitlySet = YES;
|
|
||||||
// TODO: This invalidates any cached data
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSize) size
|
|
||||||
{
|
|
||||||
if (_size.width == 0)
|
|
||||||
{
|
|
||||||
NSImageRep *rep = [self bestRepresentationForDevice: nil];
|
|
||||||
|
|
||||||
_size = [rep size];
|
|
||||||
}
|
|
||||||
return _size;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) dealloc
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
RELEASE(_reps);
|
RELEASE(_reps);
|
||||||
|
@ -430,13 +414,13 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
NSEnumerator *enumerator = [reps objectEnumerator];
|
NSEnumerator *enumerator = [reps objectEnumerator];
|
||||||
NSImageRep *rep;
|
NSImageRep *rep;
|
||||||
|
|
||||||
// FIXME: maybe we should retain if _flags.dataRetained = NO
|
|
||||||
copy = (NSImage*)NSCopyObject (self, 0, zone);
|
copy = (NSImage*)NSCopyObject (self, 0, zone);
|
||||||
|
|
||||||
RETAIN(_name);
|
RETAIN(_name);
|
||||||
RETAIN(_fileName);
|
RETAIN(_fileName);
|
||||||
RETAIN(_color);
|
RETAIN(_color);
|
||||||
copy->_lockedView = nil;
|
copy->_lockedView = nil;
|
||||||
|
// FIXME: maybe we should retain if _flags.dataRetained = NO
|
||||||
copy->_reps = [[NSMutableArray alloc] initWithCapacity: [_reps count]];
|
copy->_reps = [[NSMutableArray alloc] initWithCapacity: [_reps count]];
|
||||||
|
|
||||||
// Only copy non-cached reps.
|
// Only copy non-cached reps.
|
||||||
|
@ -456,6 +440,9 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
if (!string || [nameDict objectForKey: string])
|
if (!string || [nameDict objectForKey: string])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
|
if (_name && self == [nameDict objectForKey: _name])
|
||||||
|
[nameDict removeObjectForKey: _name];
|
||||||
|
|
||||||
ASSIGN(_name, string);
|
ASSIGN(_name, string);
|
||||||
|
|
||||||
[nameDict setObject: self forKey: _name];
|
[nameDict setObject: self forKey: _name];
|
||||||
|
@ -467,6 +454,34 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) setSize: (NSSize)aSize
|
||||||
|
{
|
||||||
|
_size = aSize;
|
||||||
|
_flags.sizeWasExplicitlySet = YES;
|
||||||
|
// TODO: This invalidates any cached data
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSize) size
|
||||||
|
{
|
||||||
|
if (_size.width == 0)
|
||||||
|
{
|
||||||
|
NSImageRep *rep = [self bestRepresentationForDevice: nil];
|
||||||
|
|
||||||
|
_size = [rep size];
|
||||||
|
}
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) isFlipped
|
||||||
|
{
|
||||||
|
return _flags.flipDraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setFlipped: (BOOL)flag
|
||||||
|
{
|
||||||
|
_flags.flipDraw = flag;
|
||||||
|
}
|
||||||
|
|
||||||
// Choosing Which Image Representation to Use
|
// Choosing Which Image Representation to Use
|
||||||
- (void) setUsesEPSOnResolutionMismatch: (BOOL)flag
|
- (void) setUsesEPSOnResolutionMismatch: (BOOL)flag
|
||||||
{
|
{
|
||||||
|
@ -659,6 +674,26 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
NS_ENDHANDLER
|
NS_ENDHANDLER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) compositeToPoint: (NSPoint)aPoint
|
||||||
|
operation: (NSCompositingOperation)op
|
||||||
|
fraction: (float)delta
|
||||||
|
{
|
||||||
|
NSRect rect;
|
||||||
|
NSSize size = [self size];
|
||||||
|
|
||||||
|
rect = NSMakeRect(0, 0, size.width, size.height);
|
||||||
|
[self compositeToPoint: aPoint fromRect: rect
|
||||||
|
operation: op fraction: delta];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) compositeToPoint: (NSPoint)aPoint
|
||||||
|
fromRect: (NSRect)srcRect
|
||||||
|
operation: (NSCompositingOperation)op
|
||||||
|
fraction: (float)delta
|
||||||
|
{
|
||||||
|
// FIXME We need another PS command for this
|
||||||
|
}
|
||||||
|
|
||||||
- (void) dissolveToPoint: (NSPoint)aPoint fraction: (float)aFloat;
|
- (void) dissolveToPoint: (NSPoint)aPoint fraction: (float)aFloat;
|
||||||
{
|
{
|
||||||
NSRect rect;
|
NSRect rect;
|
||||||
|
@ -686,7 +721,12 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
|
|
||||||
if ([[ctxt focusView] isFlipped])
|
if ([[ctxt focusView] isFlipped])
|
||||||
y -= rect.size.height;
|
y -= rect.size.height;
|
||||||
// FIXME This should be able to cut out part of the image
|
|
||||||
|
// Move the drawing rectangle to the origin of the image rep
|
||||||
|
// and intersect the two rects.
|
||||||
|
aRect.origin.x += rect.origin.x;
|
||||||
|
aRect.origin.y += rect.origin.y;
|
||||||
|
rect = NSIntersectionRect(aRect, rect);
|
||||||
PSdissolve(NSMinX(rect), NSMinY(rect), NSWidth(rect), NSHeight(rect),
|
PSdissolve(NSMinX(rect), NSMinY(rect), NSWidth(rect), NSHeight(rect),
|
||||||
[[(NSCachedImageRep *)rep window] gState], aPoint.x, y, aFloat);
|
[[(NSCachedImageRep *)rep window] gState], aPoint.x, y, aFloat);
|
||||||
}
|
}
|
||||||
|
@ -715,11 +755,33 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
|
|
||||||
- (BOOL) drawRepresentation: (NSImageRep *)imageRep inRect: (NSRect)aRect
|
- (BOOL) drawRepresentation: (NSImageRep *)imageRep inRect: (NSRect)aRect
|
||||||
{
|
{
|
||||||
|
if (_color != nil && [_color alphaComponent] != 0.0)
|
||||||
|
{
|
||||||
|
[_color set];
|
||||||
|
NSRectFill(aRect);
|
||||||
|
}
|
||||||
|
|
||||||
if (!_flags.scalable)
|
if (!_flags.scalable)
|
||||||
return [imageRep drawAtPoint: aRect.origin];
|
return [imageRep drawAtPoint: aRect.origin];
|
||||||
return [imageRep drawInRect: aRect];
|
return [imageRep drawInRect: aRect];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) drawAtPoint: (NSPoint)point
|
||||||
|
fromRect: (NSRect)srcRect
|
||||||
|
operation: (NSCompositingOperation)op
|
||||||
|
fraction: (float)delta
|
||||||
|
{
|
||||||
|
//FIXME We need another PS command for this
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) drawInRect: (NSRect)dstRect
|
||||||
|
fromRect: (NSRect)srcRect
|
||||||
|
operation: (NSCompositingOperation)op
|
||||||
|
fraction: (float)delta
|
||||||
|
{
|
||||||
|
//FIXME We need another PS command for this
|
||||||
|
}
|
||||||
|
|
||||||
- (void) addRepresentation: (NSImageRep *)imageRep
|
- (void) addRepresentation: (NSImageRep *)imageRep
|
||||||
{
|
{
|
||||||
GSRepData *repd;
|
GSRepData *repd;
|
||||||
|
@ -776,6 +838,9 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
{
|
{
|
||||||
NSWindow *window;
|
NSWindow *window;
|
||||||
|
|
||||||
|
if (imageRep == nil)
|
||||||
|
imageRep = [self bestRepresentationForDevice: nil];
|
||||||
|
|
||||||
imageRep = [self _cacheForRep: imageRep];
|
imageRep = [self _cacheForRep: imageRep];
|
||||||
window = [(NSCachedImageRep *)imageRep window];
|
window = [(NSCachedImageRep *)imageRep window];
|
||||||
_lockedView = [window contentView];
|
_lockedView = [window contentView];
|
||||||
|
@ -808,7 +873,7 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
* What's the best representation?
|
* What's the best representation?
|
||||||
* FIXME: At the moment we take the last bitmap we find.
|
* FIXME: At the moment we take the last bitmap we find.
|
||||||
* If we can't find a bitmap, we take whatever we can.
|
* If we can't find a bitmap, we take whatever we can.
|
||||||
* Do no change this without changing the Backend stuff on image dragging!
|
* NSCursor will only handle returned NSBitmapImageReps!
|
||||||
*/
|
*/
|
||||||
if ([rep isKindOfClass: bitmapClass])
|
if ([rep isKindOfClass: bitmapClass])
|
||||||
best = rep;
|
best = rep;
|
||||||
|
@ -862,38 +927,15 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
// Producing TIFF Data for the Image
|
// Producing TIFF Data for the Image
|
||||||
- (NSData *) TIFFRepresentation
|
- (NSData *) TIFFRepresentation
|
||||||
{
|
{
|
||||||
NSArray *reps = [self representations];
|
return [bitmapClass TIFFRepresentation];
|
||||||
NSEnumerator *enumerator = [reps objectEnumerator];
|
|
||||||
NSImageRep *rep;
|
|
||||||
|
|
||||||
while ((rep = [enumerator nextObject]) != nil)
|
|
||||||
{
|
|
||||||
if ([rep isKindOfClass: bitmapClass])
|
|
||||||
{
|
|
||||||
return [(NSBitmapImageRep*)rep TIFFRepresentation];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSData *) TIFFRepresentationUsingCompression: (NSTIFFCompression)comp
|
- (NSData *) TIFFRepresentationUsingCompression: (NSTIFFCompression)comp
|
||||||
factor: (float)aFloat
|
factor: (float)aFloat
|
||||||
{
|
{
|
||||||
NSArray *reps = [self representations];
|
return [bitmapClass TIFFRepresentationOfImageRepsInArray: [self representations]
|
||||||
NSEnumerator *enumerator = [reps objectEnumerator];
|
usingCompression: comp
|
||||||
NSImageRep *rep;
|
|
||||||
|
|
||||||
while ((rep = [enumerator nextObject]) != nil)
|
|
||||||
{
|
|
||||||
if ([rep isKindOfClass: bitmapClass])
|
|
||||||
{
|
|
||||||
return [(NSBitmapImageRep*)rep TIFFRepresentationUsingCompression: comp
|
|
||||||
factor: aFloat];
|
factor: aFloat];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NSCoding
|
// NSCoding
|
||||||
|
@ -940,6 +982,8 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
|
||||||
flag = _flags.unboundedCacheDepth;
|
flag = _flags.unboundedCacheDepth;
|
||||||
[coder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
[coder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
||||||
|
|
||||||
|
// FIXME: The documentation says to archive only the file name,
|
||||||
|
// if not data retained!
|
||||||
/*
|
/*
|
||||||
* Now encode an array of all the image reps (excluding cache)
|
* Now encode an array of all the image reps (excluding cache)
|
||||||
*/
|
*/
|
||||||
|
@ -1149,7 +1193,8 @@ iterate_reps_for_types(NSArray* imageReps, SEL method)
|
||||||
array = [isa imageFileTypes];
|
array = [isa imageFileTypes];
|
||||||
if ([array indexOfObject: ext] == NSNotFound)
|
if ([array indexOfObject: ext] == NSNotFound)
|
||||||
return NO;
|
return NO;
|
||||||
_fileName = RETAIN(fileName);
|
|
||||||
|
ASSIGN(_fileName, fileName);
|
||||||
_flags.syncLoad = YES;
|
_flags.syncLoad = YES;
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
@ -1160,11 +1205,14 @@ iterate_reps_for_types(NSArray* imageReps, SEL method)
|
||||||
// image (if there is one).
|
// image (if there is one).
|
||||||
- (NSImageRep *)_doImageCache
|
- (NSImageRep *)_doImageCache
|
||||||
{
|
{
|
||||||
NSImageRep *rep = [self _cacheForRep: nil];
|
NSImageRep *rep = [self bestRepresentationForDevice: nil];
|
||||||
|
|
||||||
if (NSImageDoesCaching == YES)
|
if (NSImageDoesCaching == YES)
|
||||||
{
|
{
|
||||||
GSRepData *repd = repd_for_rep(_reps, rep);
|
GSRepData *repd;
|
||||||
|
|
||||||
|
rep = [self _cacheForRep: rep];
|
||||||
|
repd = repd_for_rep(_reps, rep);
|
||||||
|
|
||||||
NSDebugLLog(@"NSImage", @"Cached image rep is %d", (int)rep);
|
NSDebugLLog(@"NSImage", @"Cached image rep is %d", (int)rep);
|
||||||
/*
|
/*
|
||||||
|
@ -1174,13 +1222,13 @@ iterate_reps_for_types(NSArray* imageReps, SEL method)
|
||||||
*/
|
*/
|
||||||
if (repd->bg == nil)
|
if (repd->bg == nil)
|
||||||
{
|
{
|
||||||
NSRect drawRect = NSMakeRect(0, 0, _size.width, _size.height);
|
|
||||||
|
|
||||||
[self lockFocusOnRepresentation: rep];
|
[self lockFocusOnRepresentation: rep];
|
||||||
|
[self drawRepresentation: repd->original
|
||||||
|
inRect: NSMakeRect(0, 0, _size.width, _size.height)];
|
||||||
|
[self unlockFocus];
|
||||||
|
|
||||||
if (_color != nil && [_color alphaComponent] != 0.0)
|
if (_color != nil && [_color alphaComponent] != 0.0)
|
||||||
{
|
{
|
||||||
[_color set];
|
|
||||||
NSRectFill(drawRect);
|
|
||||||
repd->bg = [_color copy];
|
repd->bg = [_color copy];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1188,8 +1236,6 @@ iterate_reps_for_types(NSArray* imageReps, SEL method)
|
||||||
repd->bg = [clearColor copy];
|
repd->bg = [clearColor copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self drawRepresentation: repd->original inRect: drawRect];
|
|
||||||
[self unlockFocus];
|
|
||||||
if ([repd->bg alphaComponent] == 1.0)
|
if ([repd->bg alphaComponent] == 1.0)
|
||||||
{
|
{
|
||||||
[rep setOpaque: YES];
|
[rep setOpaque: YES];
|
||||||
|
@ -1208,14 +1254,11 @@ iterate_reps_for_types(NSArray* imageReps, SEL method)
|
||||||
|
|
||||||
- (NSImageRep*) _cacheForRep: (NSImageRep*)rep
|
- (NSImageRep*) _cacheForRep: (NSImageRep*)rep
|
||||||
{
|
{
|
||||||
if (rep == nil)
|
|
||||||
rep = [self bestRepresentationForDevice: nil];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is not a cached image rep - create a cache to be used to
|
* If this is not a cached image rep - create a cache to be used to
|
||||||
* render the image rep into, and switch to the cached rep.
|
* render the image rep into, and switch to the cached rep.
|
||||||
*/
|
*/
|
||||||
if (NSImageDoesCaching == YES && [rep isKindOfClass: cachedClass] == NO)
|
if ([rep isKindOfClass: cachedClass] == NO)
|
||||||
{
|
{
|
||||||
NSImageRep *cacheRep = nil;
|
NSImageRep *cacheRep = nil;
|
||||||
unsigned count = [_reps count];
|
unsigned count = [_reps count];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue