mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-29 02:40:05 +00:00
Initial version of a CoreGraphics-based rendering context for the mac. Also an intial version of JACK support for linux.
This commit is contained in:
parent
2d0600d594
commit
ebeaa40689
17 changed files with 1349 additions and 15 deletions
|
|
@ -639,6 +639,7 @@
|
|||
8484E9D8103C95A6008B7C6C /* juce_posix_SharedCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8484E9D6103C95A6008B7C6C /* juce_posix_SharedCode.h */; };
|
||||
8484E9D9103C95A6008B7C6C /* juce_posix_NamedPipe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8484E9D7103C95A6008B7C6C /* juce_posix_NamedPipe.cpp */; };
|
||||
84A63C02107DF286000326FD /* juce_mac_ObjCSuffix.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A63C01107DF286000326FD /* juce_mac_ObjCSuffix.h */; };
|
||||
84D0F00C109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */; };
|
||||
84F1E6E710403605006A1807 /* juce_Application.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E6DC10403605006A1807 /* juce_Application.cpp */; };
|
||||
84F1E6E810403605006A1807 /* juce_Application.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F1E6DD10403605006A1807 /* juce_Application.h */; };
|
||||
84F1E6E910403605006A1807 /* juce_ApplicationCommandID.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F1E6DE10403605006A1807 /* juce_ApplicationCommandID.h */; };
|
||||
|
|
@ -1243,6 +1244,7 @@
|
|||
8484E9D6103C95A6008B7C6C /* juce_posix_SharedCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_posix_SharedCode.h; path = ../../src/native/common/juce_posix_SharedCode.h; sourceTree = SOURCE_ROOT; };
|
||||
8484E9D7103C95A6008B7C6C /* juce_posix_NamedPipe.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_posix_NamedPipe.cpp; path = ../../src/native/common/juce_posix_NamedPipe.cpp; sourceTree = SOURCE_ROOT; };
|
||||
84A63C01107DF286000326FD /* juce_mac_ObjCSuffix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = juce_mac_ObjCSuffix.h; sourceTree = "<group>"; };
|
||||
84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_CoreGraphicsContext.mm; sourceTree = "<group>"; };
|
||||
84F1E6DC10403605006A1807 /* juce_Application.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Application.cpp; path = ../../src/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; };
|
||||
84F1E6DD10403605006A1807 /* juce_Application.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_Application.h; path = ../../src/application/juce_Application.h; sourceTree = SOURCE_ROOT; };
|
||||
84F1E6DE10403605006A1807 /* juce_ApplicationCommandID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_ApplicationCommandID.h; path = ../../src/application/juce_ApplicationCommandID.h; sourceTree = SOURCE_ROOT; };
|
||||
|
|
@ -1877,6 +1879,7 @@
|
|||
8484E9A9103C9595008B7C6C /* juce_mac_CarbonViewWrapperComponent.h */,
|
||||
8484E9AA103C9595008B7C6C /* juce_mac_CoreAudio.cpp */,
|
||||
8484E9AB103C9595008B7C6C /* juce_mac_CoreMidi.cpp */,
|
||||
84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */,
|
||||
8484E9AC103C9595008B7C6C /* juce_mac_Debugging.mm */,
|
||||
8484E9AD103C9595008B7C6C /* juce_mac_FileChooser.mm */,
|
||||
8484E9AE103C9595008B7C6C /* juce_mac_Files.mm */,
|
||||
|
|
@ -4146,6 +4149,7 @@
|
|||
84816E5910809D07008FEC33 /* juce_iphone_MessageManager.mm in Sources */,
|
||||
84816E5A10809D07008FEC33 /* juce_iphone_MiscUtilities.mm in Sources */,
|
||||
84816E5C10809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm in Sources */,
|
||||
84D0F00C109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,10 +40,15 @@ endif
|
|||
|
||||
OBJECTS := \
|
||||
$(OBJDIR)/ApplicationStartup.o \
|
||||
$(OBJDIR)/juce_LibrarySource.o \
|
||||
$(OBJDIR)/MainDemoWindow.o \
|
||||
$(OBJDIR)/BinaryData.o \
|
||||
$(OBJDIR)/AudioDemo.o \
|
||||
$(OBJDIR)/MainDemoWindow.o \
|
||||
$(OBJDIR)/juce_LibrarySource.o \
|
||||
$(OBJDIR)/AudioDemoLatencyPage.o \
|
||||
$(OBJDIR)/AudioDemoPlaybackPage.o \
|
||||
$(OBJDIR)/AudioDemoRecordPage.o \
|
||||
$(OBJDIR)/AudioDemoSetupPage.o \
|
||||
$(OBJDIR)/AudioDemoSynthPage.o \
|
||||
$(OBJDIR)/AudioDemoTabComponent.o \
|
||||
$(OBJDIR)/CameraDemo.o \
|
||||
$(OBJDIR)/DragAndDropDemo.o \
|
||||
$(OBJDIR)/FontsAndTextDemo.o \
|
||||
|
|
@ -102,7 +107,7 @@ $(OBJDIR)/ApplicationStartup.o: ../../src/ApplicationStartup.cpp
|
|||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp
|
||||
$(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
|
@ -112,12 +117,37 @@ $(OBJDIR)/MainDemoWindow.o: ../../src/MainDemoWindow.cpp
|
|||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp
|
||||
$(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/AudioDemo.o: ../../src/demos/AudioDemo.cpp
|
||||
$(OBJDIR)/AudioDemoLatencyPage.o: ../../src/demos/AudioDemoLatencyPage.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/AudioDemoPlaybackPage.o: ../../src/demos/AudioDemoPlaybackPage.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/AudioDemoRecordPage.o: ../../src/demos/AudioDemoRecordPage.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/AudioDemoSetupPage.o: ../../src/demos/AudioDemoSetupPage.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/AudioDemoSynthPage.o: ../../src/demos/AudioDemoSynthPage.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
||||
$(OBJDIR)/AudioDemoTabComponent.o: ../../src/demos/AudioDemoTabComponent.cpp
|
||||
-@$(CMD_MKOBJDIR)
|
||||
@echo $(notdir $<)
|
||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@
|
|||
//#define JUCE_LOG_ASSERTIONS 1
|
||||
#define JUCE_WASAPI 1
|
||||
//#define JUCE_ASIO 1
|
||||
//#define JUCE_ALSA 1
|
||||
#define JUCE_ALSA 1
|
||||
#define JUCE_JACK 1
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_IPHONE
|
||||
#define JUCE_QUICKTIME 0 // (This is disabled here by default because on windows it requires the QT SDK,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,12 @@
|
|||
#define JUCE_ALSA 1
|
||||
#endif
|
||||
|
||||
/** Comment out this macro to disable building of JACK device support on Linux.
|
||||
*/
|
||||
#ifndef JUCE_JACK
|
||||
#define JUCE_JACK 1
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
/** Comment out this macro if you don't want to enable QuickTime or if you don't
|
||||
have the SDK installed.
|
||||
|
|
|
|||
1177
juce_amalgamated.cpp
1177
juce_amalgamated.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -277,6 +277,12 @@
|
|||
#define JUCE_ALSA 1
|
||||
#endif
|
||||
|
||||
/** Comment out this macro to disable building of JACK device support on Linux.
|
||||
*/
|
||||
#ifndef JUCE_JACK
|
||||
#define JUCE_JACK 1
|
||||
#endif
|
||||
|
||||
/** Comment out this macro if you don't want to enable QuickTime or if you don't
|
||||
have the SDK installed.
|
||||
|
||||
|
|
@ -38755,6 +38761,11 @@ public:
|
|||
*/
|
||||
const Colour getColour (const int index) const throw();
|
||||
|
||||
/** Returns the an interpolated colour at any position along the gradient.
|
||||
@param position the position along the gradient, between 0 and 1
|
||||
*/
|
||||
const Colour getColourAtPosition (const float position) const throw();
|
||||
|
||||
/** Creates a set of interpolated premultiplied ARGB values.
|
||||
|
||||
The caller must delete the array that is returned using juce_free().
|
||||
|
|
@ -38975,6 +38986,10 @@ public:
|
|||
int newHeight = -1,
|
||||
const Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const;
|
||||
|
||||
/** Returns a new single-channel image which is a copy of the alpha-channel of this image.
|
||||
*/
|
||||
virtual Image* createCopyOfAlphaChannel() const;
|
||||
|
||||
/** Returns the colour of one of the pixels in the image.
|
||||
|
||||
If the co-ordinates given are beyond the image's boundaries, this will
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ AudioIODeviceType* juce_createAudioIODeviceType_WASAPI();
|
|||
AudioIODeviceType* juce_createAudioIODeviceType_DirectSound();
|
||||
AudioIODeviceType* juce_createAudioIODeviceType_ASIO();
|
||||
AudioIODeviceType* juce_createAudioIODeviceType_ALSA();
|
||||
AudioIODeviceType* juce_createAudioIODeviceType_JACK();
|
||||
|
||||
void AudioDeviceManager::createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& list)
|
||||
{
|
||||
|
|
@ -137,6 +138,10 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray <AudioIODeviceType>&
|
|||
#if JUCE_LINUX && JUCE_ALSA
|
||||
list.add (juce_createAudioIODeviceType_ALSA());
|
||||
#endif
|
||||
|
||||
#if JUCE_LINUX && JUCE_JACK
|
||||
list.add (juce_createAudioIODeviceType_JACK());
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -112,6 +112,36 @@ const Colour ColourGradient::getColour (const int index) const throw()
|
|||
return Colour (colours [(index << 1) + 1]);
|
||||
}
|
||||
|
||||
const Colour ColourGradient::getColourAtPosition (const float position) const throw()
|
||||
{
|
||||
jassert (colours.getUnchecked (0) == 0); // the first colour specified has to go at position 0
|
||||
|
||||
const int integerPos = jlimit (0, 65535, roundFloatToInt (position * 65536.0f));
|
||||
|
||||
if (integerPos <= 0 || colours.size() <= 2)
|
||||
return getColour (0);
|
||||
|
||||
int i = colours.size() - 2;
|
||||
while (integerPos < colours.getUnchecked(i))
|
||||
i -= 2;
|
||||
|
||||
if (i >= colours.size() - 2)
|
||||
return Colour (colours.getUnchecked(i));
|
||||
|
||||
const int pos1 = colours.getUnchecked (i);
|
||||
PixelARGB pix1 (colours.getUnchecked (i + 1));
|
||||
pix1.premultiply();
|
||||
|
||||
const int pos2 = colours.getUnchecked (i + 2);
|
||||
PixelARGB pix2 (colours.getUnchecked (i + 3));
|
||||
pix2.premultiply();
|
||||
|
||||
pix1.tween (pix2, ((integerPos - pos1) << 8) / (pos2 - pos1));
|
||||
pix1.unpremultiply();
|
||||
|
||||
return Colour (pix1.getARGB());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
PixelARGB* ColourGradient::createLookupTable (int& numEntries) const throw()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -116,6 +116,11 @@ public:
|
|||
*/
|
||||
const Colour getColour (const int index) const throw();
|
||||
|
||||
/** Returns the an interpolated colour at any position along the gradient.
|
||||
@param position the position along the gradient, between 0 and 1
|
||||
*/
|
||||
const Colour getColourAtPosition (const float position) const throw();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a set of interpolated premultiplied ARGB values.
|
||||
|
||||
|
|
|
|||
|
|
@ -218,6 +218,43 @@ Image* Image::createCopy (int newWidth, int newHeight,
|
|||
return newImage;
|
||||
}
|
||||
|
||||
Image* Image::createCopyOfAlphaChannel() const
|
||||
{
|
||||
jassert (format != SingleChannel);
|
||||
|
||||
Image* const newImage = new Image (SingleChannel, imageWidth, imageHeight, false);
|
||||
|
||||
if (! hasAlphaChannel())
|
||||
{
|
||||
newImage->clear (0, 0, imageWidth, imageHeight, Colours::black);
|
||||
}
|
||||
else
|
||||
{
|
||||
int dls, dps;
|
||||
uint8* dstData = newImage->lockPixelDataReadWrite (0, 0, imageWidth, imageHeight, dls, dps);
|
||||
|
||||
int sls, sps;
|
||||
const uint8* srcData = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, sls, sps);
|
||||
|
||||
for (int y = 0; y < imageHeight; ++y)
|
||||
{
|
||||
const PixelARGB* src = (const PixelARGB*) (srcData + y * sls);
|
||||
uint8* dst = dstData + y * dls;
|
||||
|
||||
for (int x = imageWidth; --x >= 0;)
|
||||
{
|
||||
*dst++ = src->getAlpha();
|
||||
++src;
|
||||
}
|
||||
}
|
||||
|
||||
releasePixelDataReadOnly (srcData);
|
||||
newImage->releasePixelDataReadWrite (dstData);
|
||||
}
|
||||
|
||||
return newImage;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Colour Image::getPixelAt (const int x, const int y) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -128,6 +128,10 @@ public:
|
|||
int newHeight = -1,
|
||||
const Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const;
|
||||
|
||||
/** Returns a new single-channel image which is a copy of the alpha-channel of this image.
|
||||
*/
|
||||
virtual Image* createCopyOfAlphaChannel() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the colour of one of the pixels in the image.
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ BEGIN_JUCE_NAMESPACE
|
|||
#include "linux/juce_linux_Fonts.cpp"
|
||||
#include "linux/juce_linux_Windowing.cpp"
|
||||
#include "linux/juce_linux_Audio.cpp"
|
||||
#include "linux/juce_linux_JackAudio.cpp"
|
||||
#include "linux/juce_linux_Midi.cpp"
|
||||
#include "linux/juce_linux_AudioCDReader.cpp"
|
||||
#include "linux/juce_linux_FileChooser.cpp"
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ BEGIN_JUCE_NAMESPACE
|
|||
#include "mac/juce_iphone_Audio.cpp"
|
||||
#include "mac/juce_mac_CoreMidi.cpp"
|
||||
#else
|
||||
#include "mac/juce_mac_CoreGraphicsContext.mm"
|
||||
#include "mac/juce_mac_NSViewComponentPeer.mm"
|
||||
#include "mac/juce_mac_MouseCursor.mm"
|
||||
#include "mac/juce_mac_NSViewComponent.mm"
|
||||
|
|
|
|||
|
|
@ -111,6 +111,19 @@
|
|||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_JACK
|
||||
/* Got an include error here? If so, you've either not got jack-audio-connection-kit
|
||||
installed, or you've not got your paths set up correctly to find its header files.
|
||||
|
||||
The package you need to install to get JACK support is "libjack-dev".
|
||||
|
||||
If you don't have the jack-audio-connection-kit library and don't want to build
|
||||
Juce with low latency audio support, just disable the JUCE_JACK flag in juce_Config.h
|
||||
*/
|
||||
#include <jack/jack.h>
|
||||
//#include <jack/transport.h>
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
|
||||
#endif // __JUCE_LINUX_NATIVEINCLUDES_JUCEHEADER__
|
||||
|
|
|
|||
|
|
@ -482,7 +482,6 @@ private:
|
|||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout));
|
||||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout));
|
||||
|
||||
|
||||
AURenderCallbackStruct inputProc;
|
||||
inputProc.inputProc = processStatic;
|
||||
inputProc.inputProcRefCon = this;
|
||||
|
|
|
|||
|
|
@ -312,15 +312,19 @@ public:
|
|||
lineStride, pixelStride);
|
||||
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0);
|
||||
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
imageRef = CGImageCreate (width, height,
|
||||
CGImageRef imageRef = CGImageCreate (width, height,
|
||||
8, pixelStride * 8, lineStride,
|
||||
CGColorSpaceCreateDeviceRGB(),
|
||||
colourSpace,
|
||||
hasAlpha ? (kCGImageAlphaFirst | kCGBitmapByteOrder32Little) : kCGBitmapByteOrderDefault,
|
||||
provider,
|
||||
0,
|
||||
true, kCGRenderingIntentDefault);
|
||||
|
||||
CGColorSpaceRelease (colourSpace);
|
||||
CGDataProviderRelease (provider);
|
||||
|
||||
juceImage.releasePixelDataReadWrite (imageData);
|
||||
|
||||
uiImage = [[UIImage imageWithCGImage: imageRef] retain];
|
||||
|
|
@ -329,7 +333,7 @@ public:
|
|||
~JuceUIImage()
|
||||
{
|
||||
[uiImage release];
|
||||
CFRelease (imageRef);
|
||||
CGImageRelease (imageRef);
|
||||
}
|
||||
|
||||
Image& getJuceImage() throw() { return juceImage; }
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
// compiled on its own).
|
||||
#if JUCE_INCLUDED_FILE
|
||||
|
||||
#define USE_COREGRAPHICS_RENDERING 1
|
||||
|
||||
class NSViewComponentPeer;
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -1433,6 +1435,11 @@ void NSViewComponentPeer::drawRect (NSRect r)
|
|||
if (r.size.width < 1.0f || r.size.height < 1.0f)
|
||||
return;
|
||||
|
||||
#if USE_COREGRAPHICS_RENDERING
|
||||
CoreGraphicsContext context ((CGContextRef) [[NSGraphicsContext currentContext] graphicsPort],
|
||||
[view frame].size.height);
|
||||
handlePaint (context);
|
||||
#else
|
||||
const float y = [view frame].size.height - (r.origin.y + r.size.height);
|
||||
|
||||
JuceNSImage temp ((int) (r.size.width + 0.5f),
|
||||
|
|
@ -1465,6 +1472,7 @@ void NSViewComponentPeer::drawRect (NSRect r)
|
|||
|
||||
temp.draw (r.origin.x, r.origin.y, clip, originX, originY);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NSViewComponentPeer::canBecomeKeyWindow()
|
||||
|
|
@ -1511,7 +1519,7 @@ void NSViewComponentPeer::repaint (int x, int y, int w, int h)
|
|||
|
||||
if (insideDrawRect)
|
||||
{
|
||||
[view performSelectorOnMainThread: @selector (asyncRepaint:)
|
||||
[view performSelectorOnMainThread: @selector (asyncRepaint:)
|
||||
withObject: [NSData dataWithBytes: &r length: sizeof (r)]
|
||||
waitUntilDone: NO];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue