upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -0,0 +1,493 @@
|
||||
package com.roli.juce;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public final class ComponentPeerView extends ViewGroup
|
||||
implements View.OnFocusChangeListener, Application.ActivityLifecycleCallbacks
|
||||
{
|
||||
public ComponentPeerView (Context context, boolean opaque_, long host)
|
||||
{
|
||||
super (context);
|
||||
|
||||
if (Application.class.isInstance (context))
|
||||
{
|
||||
((Application) context).registerActivityLifecycleCallbacks (this);
|
||||
} else
|
||||
{
|
||||
((Application) context.getApplicationContext ()).registerActivityLifecycleCallbacks (this);
|
||||
}
|
||||
|
||||
this.host = host;
|
||||
setWillNotDraw (false);
|
||||
opaque = opaque_;
|
||||
|
||||
setFocusable (true);
|
||||
setFocusableInTouchMode (true);
|
||||
setOnFocusChangeListener (this);
|
||||
|
||||
// swap red and blue colours to match internal opengl texture format
|
||||
ColorMatrix colorMatrix = new ColorMatrix ();
|
||||
|
||||
float[] colorTransform = {0, 0, 1.0f, 0, 0,
|
||||
0, 1.0f, 0, 0, 0,
|
||||
1.0f, 0, 0, 0, 0,
|
||||
0, 0, 0, 1.0f, 0};
|
||||
|
||||
colorMatrix.set (colorTransform);
|
||||
paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix));
|
||||
|
||||
java.lang.reflect.Method method = null;
|
||||
|
||||
try
|
||||
{
|
||||
method = getClass ().getMethod ("setLayerType", int.class, Paint.class);
|
||||
} catch (SecurityException e)
|
||||
{
|
||||
} catch (NoSuchMethodException e)
|
||||
{
|
||||
}
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
int layerTypeNone = 0;
|
||||
method.invoke (this, layerTypeNone, null);
|
||||
} catch (java.lang.IllegalArgumentException e)
|
||||
{
|
||||
} catch (java.lang.IllegalAccessException e)
|
||||
{
|
||||
} catch (java.lang.reflect.InvocationTargetException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear ()
|
||||
{
|
||||
host = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private native void handlePaint (long host, Canvas canvas, Paint paint);
|
||||
|
||||
@Override
|
||||
public void onDraw (Canvas canvas)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
handlePaint (host, canvas, paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque ()
|
||||
{
|
||||
return opaque;
|
||||
}
|
||||
|
||||
private boolean opaque;
|
||||
private long host;
|
||||
private Paint paint = new Paint ();
|
||||
|
||||
//==============================================================================
|
||||
private native void handleMouseDown (long host, int index, float x, float y, long time);
|
||||
private native void handleMouseDrag (long host, int index, float x, float y, long time);
|
||||
private native void handleMouseUp (long host, int index, float x, float y, long time);
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent (MotionEvent event)
|
||||
{
|
||||
if (host == 0)
|
||||
return false;
|
||||
|
||||
int action = event.getAction ();
|
||||
long time = event.getEventTime ();
|
||||
|
||||
switch (action & MotionEvent.ACTION_MASK)
|
||||
{
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP:
|
||||
handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
{
|
||||
handleMouseDrag (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time);
|
||||
|
||||
int n = event.getPointerCount ();
|
||||
|
||||
if (n > 1)
|
||||
{
|
||||
int point[] = new int[2];
|
||||
getLocationOnScreen (point);
|
||||
|
||||
for (int i = 1; i < n; ++i)
|
||||
handleMouseDrag (host, event.getPointerId (i), event.getX (i) + point[0], event.getY (i) + point[1], time);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
{
|
||||
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
handleMouseUp (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time);
|
||||
} else
|
||||
{
|
||||
int point[] = new int[2];
|
||||
getLocationOnScreen (point);
|
||||
|
||||
handleMouseUp (host, event.getPointerId (i), event.getX (i) + point[0], event.getY (i) + point[1], time);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
{
|
||||
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
handleMouseDown (host, event.getPointerId (0), event.getRawX (), event.getRawY (), time);
|
||||
} else
|
||||
{
|
||||
int point[] = new int[2];
|
||||
getLocationOnScreen (point);
|
||||
|
||||
handleMouseDown (host, event.getPointerId (i), event.getX (i) + point[0], event.getY (i) + point[1], time);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private native void handleKeyDown (long host, int keycode, int textchar);
|
||||
private native void handleKeyUp (long host, int keycode, int textchar);
|
||||
private native void handleBackButton (long host);
|
||||
private native void handleKeyboardHidden (long host);
|
||||
|
||||
public void showKeyboard (String type)
|
||||
{
|
||||
InputMethodManager imm = (InputMethodManager) getContext ().getSystemService (Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
if (imm != null)
|
||||
{
|
||||
if (type.length () > 0)
|
||||
{
|
||||
imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
|
||||
imm.setInputMethod (getWindowToken (), type);
|
||||
keyboardDismissListener.startListening ();
|
||||
} else
|
||||
{
|
||||
imm.hideSoftInputFromWindow (getWindowToken (), 0);
|
||||
keyboardDismissListener.stopListening ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void backButtonPressed ()
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
handleBackButton (host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown (int keyCode, KeyEvent event)
|
||||
{
|
||||
if (host == 0)
|
||||
return false;
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
return super.onKeyDown (keyCode, event);
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
{
|
||||
backButtonPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
handleKeyDown (host, keyCode, event.getUnicodeChar ());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp (int keyCode, KeyEvent event)
|
||||
{
|
||||
if (host == 0)
|
||||
return false;
|
||||
|
||||
handleKeyUp (host, keyCode, event.getUnicodeChar ());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyMultiple (int keyCode, int count, KeyEvent event)
|
||||
{
|
||||
if (host == 0)
|
||||
return false;
|
||||
|
||||
if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction () != KeyEvent.ACTION_MULTIPLE)
|
||||
return super.onKeyMultiple (keyCode, count, event);
|
||||
|
||||
if (event.getCharacters () != null)
|
||||
{
|
||||
int utf8Char = event.getCharacters ().codePointAt (0);
|
||||
handleKeyDown (host, utf8Char, utf8Char);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private final class KeyboardDismissListener
|
||||
{
|
||||
public KeyboardDismissListener (ComponentPeerView viewToUse)
|
||||
{
|
||||
view = viewToUse;
|
||||
}
|
||||
|
||||
private void startListening ()
|
||||
{
|
||||
view.getViewTreeObserver ().addOnGlobalLayoutListener (viewTreeObserver);
|
||||
}
|
||||
|
||||
private void stopListening ()
|
||||
{
|
||||
view.getViewTreeObserver ().removeGlobalOnLayoutListener (viewTreeObserver);
|
||||
}
|
||||
|
||||
private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener
|
||||
{
|
||||
TreeObserver ()
|
||||
{
|
||||
keyboardShown = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout ()
|
||||
{
|
||||
Rect r = new Rect ();
|
||||
|
||||
View parentView = getRootView ();
|
||||
int diff = 0;
|
||||
|
||||
if (parentView == null)
|
||||
{
|
||||
getWindowVisibleDisplayFrame (r);
|
||||
diff = getHeight () - (r.bottom - r.top);
|
||||
} else
|
||||
{
|
||||
parentView.getWindowVisibleDisplayFrame (r);
|
||||
diff = parentView.getHeight () - (r.bottom - r.top);
|
||||
}
|
||||
|
||||
// Arbitrary threshold, surely keyboard would take more than 20 pix.
|
||||
if (diff < 20 && keyboardShown)
|
||||
{
|
||||
keyboardShown = false;
|
||||
handleKeyboardHidden (view.host);
|
||||
}
|
||||
|
||||
if (!keyboardShown && diff > 20)
|
||||
keyboardShown = true;
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
private boolean keyboardShown;
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
private ComponentPeerView view;
|
||||
private TreeObserver viewTreeObserver = new TreeObserver ();
|
||||
}
|
||||
|
||||
private KeyboardDismissListener keyboardDismissListener = new KeyboardDismissListener (this);
|
||||
|
||||
// this is here to make keyboard entry work on a Galaxy Tab2 10.1
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection (EditorInfo outAttrs)
|
||||
{
|
||||
outAttrs.actionLabel = "";
|
||||
outAttrs.hintText = "";
|
||||
outAttrs.initialCapsMode = 0;
|
||||
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
|
||||
outAttrs.label = "";
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
||||
outAttrs.inputType = InputType.TYPE_NULL;
|
||||
|
||||
return new BaseInputConnection (this, false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@Override
|
||||
protected void onSizeChanged (int w, int h, int oldw, int oldh)
|
||||
{
|
||||
super.onSizeChanged (w, h, oldw, oldh);
|
||||
|
||||
if (host != 0)
|
||||
viewSizeChanged (host);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout (boolean changed, int left, int top, int right, int bottom)
|
||||
{
|
||||
}
|
||||
|
||||
private native void viewSizeChanged (long host);
|
||||
|
||||
@Override
|
||||
public void onFocusChange (View v, boolean hasFocus)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
if (v == this)
|
||||
focusChanged (host, hasFocus);
|
||||
}
|
||||
|
||||
private native void focusChanged (long host, boolean hasFocus);
|
||||
|
||||
public void setViewName (String newName)
|
||||
{
|
||||
}
|
||||
|
||||
public void setSystemUiVisibilityCompat (int visibility)
|
||||
{
|
||||
Method systemUIVisibilityMethod = null;
|
||||
try
|
||||
{
|
||||
systemUIVisibilityMethod = this.getClass ().getMethod ("setSystemUiVisibility", int.class);
|
||||
} catch (SecurityException e)
|
||||
{
|
||||
return;
|
||||
} catch (NoSuchMethodException e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (systemUIVisibilityMethod == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
systemUIVisibilityMethod.invoke (this, visibility);
|
||||
} catch (java.lang.IllegalArgumentException e)
|
||||
{
|
||||
} catch (java.lang.IllegalAccessException e)
|
||||
{
|
||||
} catch (java.lang.reflect.InvocationTargetException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVisible ()
|
||||
{
|
||||
return getVisibility () == VISIBLE;
|
||||
}
|
||||
|
||||
public void setVisible (boolean b)
|
||||
{
|
||||
setVisibility (b ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
|
||||
public boolean containsPoint (int x, int y)
|
||||
{
|
||||
return true; //xxx needs to check overlapping views
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private native void handleAppPaused (long host);
|
||||
private native void handleAppResumed (long host);
|
||||
|
||||
@Override
|
||||
public void onActivityPaused (Activity activity)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
handleAppPaused (host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped (Activity activity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState (Activity activity, Bundle bundle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed (Activity activity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated (Activity activity, Bundle bundle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted (Activity activity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed (Activity activity)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
// Ensure that navigation/status bar visibility is correctly restored.
|
||||
handleAppResumed (host);
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package com.roli.juce;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.FileObserver;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.lang.String;
|
||||
|
||||
public final class JuceSharingContentProvider extends ContentProvider
|
||||
{
|
||||
private Object lock = new Object ();
|
||||
|
||||
private native Cursor contentSharerQuery (Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder);
|
||||
private native AssetFileDescriptor contentSharerOpenFile (Uri uri, String mode);
|
||||
private native String[] contentSharerGetStreamTypes (Uri uri, String mimeTypeFilter);
|
||||
|
||||
public final class ProviderFileObserver extends FileObserver
|
||||
{
|
||||
public ProviderFileObserver (long hostToUse, String path, int mask)
|
||||
{
|
||||
super (path, mask);
|
||||
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
public void onEvent (int event, String path)
|
||||
{
|
||||
contentSharerFileObserverEvent (host, event, path);
|
||||
}
|
||||
|
||||
private long host;
|
||||
|
||||
private native void contentSharerFileObserverEvent (long host, int event, String path);
|
||||
}
|
||||
|
||||
public final class ProviderCursor extends MatrixCursor
|
||||
{
|
||||
ProviderCursor (long hostToUse, String[] columnNames)
|
||||
{
|
||||
super (columnNames);
|
||||
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close ()
|
||||
{
|
||||
super.close ();
|
||||
|
||||
contentSharerCursorClosed (host);
|
||||
}
|
||||
|
||||
private native void contentSharerCursorClosed (long host);
|
||||
|
||||
private long host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query (Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
return contentSharerQuery (url, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert (Uri uri, ContentValues values)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update (Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete (Uri uri, String selection, String[] selectionArgs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType (Uri uri)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetFileDescriptor openAssetFile (Uri uri, String mode)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
return contentSharerOpenFile (uri, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile (Uri uri, String mode)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
AssetFileDescriptor result = contentSharerOpenFile (uri, mode);
|
||||
|
||||
if (result != null)
|
||||
return result.getParcelFileDescriptor ();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getStreamTypes (Uri uri, String mimeTypeFilter)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
return contentSharerGetStreamTypes (uri, mimeTypeFilter);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,55 +23,93 @@
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
FIELD (providers, "providers", "[Landroid/content/pm/ProviderInfo;")
|
||||
//==============================================================================
|
||||
// This byte-code is generated from native/javacore/app/com/roli/juce/JuceSharingContentProvider.java with min sdk version 16
|
||||
// See juce_core/native/java/README.txt on how to generate this byte-code.
|
||||
static const uint8 javaJuceSharingContentProvider[] =
|
||||
{31,139,8,8,143,106,229,91,0,3,74,117,99,101,83,104,97,114,105,110,103,67,111,110,116,101,110,116,80,114,111,118,105,100,
|
||||
101,114,46,100,101,120,0,149,151,93,108,20,85,20,199,207,157,153,157,253,236,178,91,170,20,145,178,229,83,80,216,242,165,
|
||||
96,5,11,45,72,183,91,139,161,52,218,190,56,221,157,148,129,221,153,101,102,118,133,23,2,106,162,209,196,24,125,64,19,73,
|
||||
48,33,106,140,15,36,26,227,131,49,152,24,163,241,65,77,148,248,160,209,152,152,24,193,68,227,131,6,37,241,127,63,118,219,
|
||||
173,197,232,194,111,238,185,231,156,123,238,185,231,222,153,206,148,237,19,137,190,173,219,105,239,208,216,231,67,47,106,
|
||||
177,200,154,39,135,207,172,189,226,63,113,230,173,189,99,175,63,244,123,185,131,168,70,68,39,38,182,117,146,250,157,79,
|
||||
17,141,146,212,223,4,46,48,34,110,252,3,109,4,237,103,26,209,82,222,71,171,163,189,132,203,80,156,40,103,16,125,111,18,
|
||||
253,4,126,6,191,129,107,224,58,232,137,18,245,130,53,96,3,216,2,14,131,6,120,25,188,11,190,1,191,128,100,140,104,19,112,
|
||||
192,235,224,50,184,6,110,193,28,187,192,3,192,6,117,240,52,120,6,60,15,206,130,115,224,101,240,10,120,3,188,9,222,6,159,
|
||||
128,175,192,183,224,42,136,38,136,214,129,33,48,5,60,240,8,56,5,206,130,87,193,69,240,54,120,31,124,12,62,5,95,130,31,
|
||||
192,21,240,43,248,19,24,73,162,197,96,57,88,5,242,224,78,176,27,12,131,7,65,9,56,224,56,56,9,78,129,199,192,83,0,101,37,
|
||||
148,142,16,138,208,37,148,159,176,45,148,6,139,64,6,100,73,238,193,98,208,165,246,229,102,176,4,116,147,220,143,91,193,
|
||||
106,176,134,228,190,240,223,195,168,189,166,228,10,228,152,154,235,4,100,148,65,236,231,105,165,71,233,233,89,200,248,47,
|
||||
108,252,23,83,50,247,143,170,60,94,48,229,92,205,3,179,92,201,231,249,62,43,249,53,200,43,148,124,17,242,42,37,191,11,
|
||||
121,165,146,63,130,220,171,228,47,32,231,148,252,181,41,215,177,120,78,14,93,42,135,4,170,181,85,212,42,69,247,137,122,
|
||||
201,126,82,245,83,168,214,157,196,215,28,19,99,13,172,176,143,248,154,22,137,190,9,253,58,17,51,45,250,9,81,105,222,74,
|
||||
125,2,255,214,171,120,36,218,36,109,16,109,156,238,17,241,101,220,20,42,113,187,104,53,186,67,180,58,109,20,45,163,77,
|
||||
202,190,89,180,81,218,34,90,131,182,171,252,250,213,184,93,162,53,105,183,26,191,71,237,253,1,177,231,49,149,151,172,185,
|
||||
169,106,193,247,171,15,157,109,50,61,113,94,178,170,70,77,251,0,236,35,202,158,82,118,109,142,253,32,236,211,202,206,245,
|
||||
157,144,187,83,179,114,111,74,158,201,13,41,238,31,17,250,231,146,114,142,41,198,168,150,211,104,128,38,53,126,66,117,
|
||||
120,242,179,118,46,41,207,137,151,209,225,127,8,91,89,235,139,146,198,210,34,119,83,248,92,104,197,208,97,53,104,32,50,
|
||||
169,105,136,17,129,149,231,117,49,41,215,121,8,241,107,227,113,210,54,167,17,139,137,92,222,73,202,181,214,50,60,183,149,
|
||||
168,79,45,195,207,253,84,198,16,59,25,17,167,154,232,189,164,90,7,246,155,199,229,249,125,152,148,117,24,239,53,104,57,
|
||||
171,245,165,104,139,145,162,30,150,197,222,247,176,117,34,183,152,152,39,78,186,170,212,103,173,56,89,68,150,119,211,229,
|
||||
57,58,77,100,133,103,86,83,151,153,157,239,251,121,243,117,252,203,124,166,26,115,37,41,239,233,241,45,24,163,241,49,131,
|
||||
145,20,237,128,159,155,225,51,165,88,143,150,101,157,184,222,118,189,3,215,117,76,222,227,89,17,167,19,126,188,202,140,
|
||||
174,183,205,221,48,121,5,111,60,119,68,172,33,158,154,173,89,206,160,182,223,29,243,250,59,230,245,121,55,138,168,89,220,
|
||||
161,186,144,179,226,94,213,148,28,17,109,151,208,102,91,122,93,84,47,218,58,151,89,209,231,232,170,205,170,216,252,126,
|
||||
202,42,61,151,155,177,179,202,175,139,204,123,28,215,9,119,19,27,38,99,184,88,44,82,132,95,139,196,10,180,162,80,47,217,
|
||||
135,142,88,190,227,206,12,122,110,104,187,225,65,223,107,56,101,219,223,116,212,106,88,196,138,164,193,85,231,254,102,81,
|
||||
252,168,183,104,185,101,223,115,202,249,146,28,146,159,55,180,159,86,220,200,101,194,170,212,237,160,159,214,255,195,193,
|
||||
183,131,252,158,32,176,195,253,78,197,30,178,131,146,239,212,66,15,177,150,182,92,203,86,104,77,91,129,157,31,172,251,
|
||||
129,215,54,77,203,52,106,133,190,115,162,233,144,109,57,184,118,152,63,236,59,115,195,121,65,158,207,53,54,29,216,126,
|
||||
131,103,221,59,215,116,208,242,75,118,101,126,50,59,139,37,175,154,247,189,138,147,63,138,210,229,111,92,191,213,77,161,
|
||||
153,203,189,255,127,104,123,122,27,254,115,128,126,90,89,44,91,149,134,115,44,111,185,174,23,90,161,227,185,249,125,110,
|
||||
169,226,5,220,187,98,5,216,131,158,5,124,134,93,23,25,75,123,239,2,246,81,187,58,173,28,248,54,118,22,249,41,201,87,44,
|
||||
119,38,63,54,125,212,46,133,237,186,67,33,207,174,159,210,237,197,160,174,133,86,72,108,130,244,137,97,156,184,137,2,25,
|
||||
19,5,33,225,236,77,20,113,112,39,138,5,28,92,126,29,38,54,73,139,167,22,152,37,105,149,74,118,16,236,175,88,51,1,69,248,
|
||||
98,109,74,150,188,74,189,234,222,111,85,237,128,150,170,195,198,171,214,204,101,144,187,149,169,167,205,52,55,173,125,13,
|
||||
168,105,89,155,253,62,59,196,164,182,85,29,63,89,67,220,155,218,140,99,53,219,229,1,168,179,77,253,64,221,246,79,146,89,
|
||||
182,43,118,104,83,196,22,97,151,204,216,225,66,39,141,210,51,237,83,68,209,231,18,25,71,188,32,164,56,191,142,123,135,
|
||||
177,66,211,113,145,104,72,70,197,43,29,35,163,106,5,199,40,93,117,170,54,119,71,212,16,149,53,170,94,25,67,93,84,129,98,
|
||||
158,59,136,184,200,33,234,185,114,113,29,30,82,110,221,124,240,104,174,192,168,89,225,17,74,212,124,143,239,45,14,0,69,
|
||||
142,203,101,224,118,173,87,144,71,128,229,72,75,71,75,220,227,163,254,113,212,54,28,243,203,124,246,240,136,19,144,201,
|
||||
175,171,251,200,172,215,202,124,118,189,238,59,252,82,161,72,131,63,21,200,20,77,64,155,244,3,219,215,71,211,27,119,109,
|
||||
164,187,40,154,222,53,73,203,140,3,219,7,118,72,213,42,173,111,32,154,158,196,147,24,38,178,244,194,208,190,104,154,30,
|
||||
99,90,97,39,20,14,205,176,2,250,227,90,97,20,205,16,156,168,170,21,238,22,166,134,20,138,58,254,116,108,156,26,193,147,
|
||||
119,36,50,178,103,104,223,126,97,157,50,10,163,34,150,214,193,70,186,83,90,90,91,107,100,239,94,114,75,83,88,166,45,98,
|
||||
35,183,106,221,137,238,36,105,26,195,159,238,103,115,145,211,167,141,75,49,237,81,141,76,246,93,140,171,53,174,142,157,
|
||||
57,109,60,30,103,80,39,216,133,56,49,35,110,104,73,232,46,9,93,147,69,236,199,56,99,127,129,139,9,198,62,0,95,129,171,
|
||||
224,124,146,177,31,193,75,41,249,110,75,234,89,222,108,155,223,30,252,57,223,252,254,208,105,246,27,196,160,217,239,16,
|
||||
222,54,191,69,76,154,253,30,209,51,82,230,127,207,88,78,190,75,15,64,54,115,82,207,223,161,88,70,190,103,139,119,228,156,
|
||||
156,151,127,191,232,202,159,191,243,24,57,57,31,127,47,34,53,86,188,123,101,100,174,252,91,233,111,138,244,241,33,100,13,
|
||||
0,0};
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidPackageInfo, "android/content/pm/PackageInfo");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
FIELD (authority, "authority", "Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidProviderInfo, "android/content/pm/ProviderInfo");
|
||||
DECLARE_JNI_CLASS (AndroidProviderInfo, "android/content/pm/ProviderInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Landroid/os/ParcelFileDescriptor;JJ)V") \
|
||||
METHOD (createInputStream, "createInputStream", "()Ljava/io/FileInputStream;") \
|
||||
METHOD (getLength, "getLength", "()J")
|
||||
|
||||
DECLARE_JNI_CLASS (AssetFileDescriptor, "android/content/res/AssetFileDescriptor");
|
||||
DECLARE_JNI_CLASS (AssetFileDescriptor, "android/content/res/AssetFileDescriptor")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (close, "close", "()V")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaCloseable, "java/io/Closeable");
|
||||
DECLARE_JNI_CLASS (JavaCloseable, "java/io/Closeable")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH ";JLjava/lang/String;I)V") \
|
||||
METHOD (startWatching, "startWatching", "()V") \
|
||||
METHOD (stopWatching, "stopWatching", "()V")
|
||||
|
||||
DECLARE_JNI_CLASS (JuceContentProviderFileObserver, JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH "$ProviderFileObserver");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \
|
||||
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH ";J[Ljava/lang/String;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (JuceContentProviderFileObserverCursor, JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSPATH "$ProviderCursor");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (open, "open", "(Ljava/io/File;I)Landroid/os/ParcelFileDescriptor;")
|
||||
|
||||
DECLARE_JNI_CLASS (ParcelFileDescriptor, "android/os/ParcelFileDescriptor");
|
||||
DECLARE_JNI_CLASS (ParcelFileDescriptor, "android/os/ParcelFileDescriptor")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
@ -90,8 +128,8 @@ public:
|
||||
const LocalRef<jobject>& contentProvider,
|
||||
const LocalRef<jobjectArray>& resultColumns)
|
||||
: owner (ownerToUse),
|
||||
cursor (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderFileObserverCursor,
|
||||
JuceContentProviderFileObserverCursor.constructor,
|
||||
cursor (GlobalRef (LocalRef<jobject> (env->NewObject (JuceContentProviderCursor,
|
||||
JuceContentProviderCursor.constructor,
|
||||
contentProvider.get(),
|
||||
reinterpret_cast<jlong> (this),
|
||||
resultColumns.get()))))
|
||||
@ -107,11 +145,35 @@ public:
|
||||
MessageManager::callAsync ([this] { owner.cursorClosed (*this); });
|
||||
}
|
||||
|
||||
void addRow (LocalRef<jobjectArray>& values)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (cursor.get(), JuceContentProviderCursor.addRow, values.get());
|
||||
}
|
||||
|
||||
private:
|
||||
Owner& owner;
|
||||
GlobalRef cursor;
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (addRow, "addRow", "([Ljava/lang/Object;)V") \
|
||||
METHOD (constructor, "<init>", "(Lcom/roli/juce/JuceSharingContentProvider;J[Ljava/lang/String;)V") \
|
||||
CALLBACK (contentSharerCursorClosed, "contentSharerCursorClosed", "(J)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (JuceContentProviderCursor, "com/roli/juce/JuceSharingContentProvider$ProviderCursor")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
static void JNICALL contentSharerCursorClosed(JNIEnv*, jobject, jlong host)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<AndroidContentSharerCursor*> (host))
|
||||
myself->cursorClosed();
|
||||
}
|
||||
};
|
||||
|
||||
AndroidContentSharerCursor::JuceContentProviderCursor_Class AndroidContentSharerCursor::JuceContentProviderCursor;
|
||||
|
||||
//==============================================================================
|
||||
class AndroidContentSharerFileObserver
|
||||
{
|
||||
@ -182,8 +244,26 @@ private:
|
||||
Owner& owner;
|
||||
String filepath;
|
||||
GlobalRef fileObserver;
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Lcom/roli/juce/JuceSharingContentProvider;JLjava/lang/String;I)V") \
|
||||
METHOD (startWatching, "startWatching", "()V") \
|
||||
METHOD (stopWatching, "stopWatching", "()V") \
|
||||
CALLBACK (contentSharerFileObserverEvent, "contentSharerFileObserverEvent", "(JILjava/lang/String;)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (JuceContentProviderFileObserver, "com/roli/juce/JuceSharingContentProvider$ProviderFileObserver")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
static void JNICALL contentSharerFileObserverEvent (JNIEnv*, jobject /*fileObserver*/, jlong host, int event, jstring path)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<AndroidContentSharerFileObserver*> (host))
|
||||
myself->onFileEvent (event, LocalRef<jstring> (path));
|
||||
}
|
||||
};
|
||||
|
||||
AndroidContentSharerFileObserver::JuceContentProviderFileObserver_Class AndroidContentSharerFileObserver::JuceContentProviderFileObserver;
|
||||
|
||||
//==============================================================================
|
||||
class AndroidContentSharerPrepareFilesThread : private Thread
|
||||
{
|
||||
@ -220,7 +300,7 @@ public:
|
||||
private:
|
||||
struct StreamCloser
|
||||
{
|
||||
StreamCloser (jobject streamToUse)
|
||||
StreamCloser (const LocalRef<jobject>& streamToUse)
|
||||
: stream (GlobalRef (streamToUse))
|
||||
{
|
||||
}
|
||||
@ -289,12 +369,12 @@ private:
|
||||
|
||||
URL copyAssetFileToTemporaryFile (JNIEnv* env, const String& filename)
|
||||
{
|
||||
auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
|
||||
auto resources = LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), AndroidContext.getResources));
|
||||
int fileId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (filename).get(),
|
||||
javaString ("raw").get(), javaString (packageName).get());
|
||||
|
||||
// Raw resource not found. Please make sure that you include your file as a raw resource
|
||||
// and that you specify just the file name, without an extention.
|
||||
// and that you specify just the file name, without an extension.
|
||||
jassert (fileId != 0);
|
||||
|
||||
if (fileId == 0)
|
||||
@ -307,14 +387,10 @@ private:
|
||||
auto inputStream = StreamCloser (LocalRef<jobject> (env->CallObjectMethod (assetFd,
|
||||
AssetFileDescriptor.createInputStream)));
|
||||
|
||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
||||
|
||||
if (exception != 0)
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// Failed to open file stream for resource
|
||||
jassertfalse;
|
||||
|
||||
env->ExceptionClear();
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -326,35 +402,27 @@ private:
|
||||
JavaFileOutputStream.constructor,
|
||||
javaString (tempFile.getFullPathName()).get())));
|
||||
|
||||
exception = LocalRef<jobject> (env->ExceptionOccurred());
|
||||
|
||||
if (exception != 0)
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// Failed to open file stream for temporary file
|
||||
jassertfalse;
|
||||
|
||||
env->ExceptionClear();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto buffer = LocalRef<jbyteArray> (env->NewByteArray (1024));
|
||||
int bytesRead = 0;
|
||||
|
||||
while (true)
|
||||
for (;;)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
return {};
|
||||
|
||||
bytesRead = env->CallIntMethod (inputStream.stream, JavaFileInputStream.read, buffer.get());
|
||||
|
||||
exception = LocalRef<jobject> (env->ExceptionOccurred());
|
||||
|
||||
if (exception != 0)
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// Failed to read from resource file.
|
||||
jassertfalse;
|
||||
|
||||
env->ExceptionClear();
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -363,12 +431,10 @@ private:
|
||||
|
||||
env->CallVoidMethod (outputStream.stream, JavaFileOutputStream.write, buffer.get(), 0, bytesRead);
|
||||
|
||||
if (exception != 0)
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// Failed to write to temporary file.
|
||||
jassertfalse;
|
||||
|
||||
env->ExceptionClear();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@ -400,8 +466,7 @@ class ContentSharer::ContentSharerNativeImpl : public ContentSharer::Pimpl,
|
||||
public:
|
||||
ContentSharerNativeImpl (ContentSharer& cs)
|
||||
: owner (cs),
|
||||
packageName (juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (android.activity,
|
||||
JuceAppActivity.getPackageName)))),
|
||||
packageName (juceString (LocalRef<jstring> ((jstring) getEnv()->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageName)))),
|
||||
uriBase ("content://" + packageName + ".sharingcontentprovider/")
|
||||
{
|
||||
}
|
||||
@ -445,7 +510,14 @@ public:
|
||||
auto chooserIntent = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidIntent, AndroidIntent.createChooser,
|
||||
intent.get(), javaString ("Choose share target").get()));
|
||||
|
||||
env->CallVoidMethod (android.activity, JuceAppActivity.startActivityForResult, chooserIntent.get(), 1003);
|
||||
WeakReference<ContentSharerNativeImpl> weakRef (this);
|
||||
|
||||
startAndroidActivityForResult (chooserIntent, 1003,
|
||||
[weakRef] (int /*requestCode*/, int resultCode, LocalRef<jobject> /*intentData*/) mutable
|
||||
{
|
||||
if (weakRef != nullptr)
|
||||
weakRef->sharingFinished (resultCode);
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -460,7 +532,7 @@ public:
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void* openFile (const LocalRef<jobject>& contentProvider,
|
||||
jobject openFile (const LocalRef<jobject>& contentProvider,
|
||||
const LocalRef<jobject>& uri, const LocalRef<jstring>& mode)
|
||||
{
|
||||
ignoreUnused (mode);
|
||||
@ -480,7 +552,7 @@ public:
|
||||
return getAssetFileDescriptor (env, contentProvider, uriElements.filepath);
|
||||
}
|
||||
|
||||
void* query (const LocalRef<jobject>& contentProvider, const LocalRef<jobject>& uri,
|
||||
jobject query (const LocalRef<jobject>& contentProvider, const LocalRef<jobject>& uri,
|
||||
const LocalRef<jobjectArray>& projection, const LocalRef<jobject>& selection,
|
||||
const LocalRef<jobjectArray>& selectionArgs, const LocalRef<jobject>& sortOrder)
|
||||
{
|
||||
@ -535,13 +607,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
auto nativeCursor = cursor->getNativeCursor();
|
||||
env->CallVoidMethod (nativeCursor, JuceContentProviderFileObserverCursor.addRow, values.get());
|
||||
|
||||
return nativeCursor;
|
||||
cursor->addRow (values);
|
||||
return cursor->getNativeCursor();
|
||||
}
|
||||
|
||||
void* getStreamTypes (const LocalRef<jobject>& uri, const LocalRef<jstring>& mimeTypeFilter)
|
||||
jobjectArray getStreamTypes (const LocalRef<jobject>& uri, const LocalRef<jstring>& mimeTypeFilter)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
@ -572,8 +642,7 @@ private:
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
auto packageManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
|
||||
JuceAppActivity.getPackageManager));
|
||||
LocalRef<jobject> packageManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
|
||||
|
||||
constexpr int getProviders = 8;
|
||||
auto packageInfo = LocalRef<jobject> (env->CallObjectMethod (packageManager,
|
||||
@ -634,8 +703,14 @@ private:
|
||||
AndroidIntent.createChooser,
|
||||
intent.get(),
|
||||
javaString ("Choose share target").get()));
|
||||
WeakReference<ContentSharerNativeImpl> weakRef (this);
|
||||
|
||||
env->CallVoidMethod (android.activity, JuceAppActivity.startActivityForResult, chooserIntent.get(), 1003);
|
||||
startAndroidActivityForResult (chooserIntent, 1003,
|
||||
[weakRef] (int /*requestCode*/, int resultCode, LocalRef<jobject> /*intentData*/) mutable
|
||||
{
|
||||
if (weakRef != nullptr)
|
||||
weakRef->sharingFinished (resultCode);
|
||||
});
|
||||
}
|
||||
|
||||
void decrementPendingFileCountAndNotifyOwnerIfReady()
|
||||
@ -688,7 +763,7 @@ private:
|
||||
return StringArray ("_display_name", "_size");
|
||||
}
|
||||
|
||||
void* getAssetFileDescriptor (JNIEnv* env, const LocalRef<jobject>& contentProvider,
|
||||
jobject getAssetFileDescriptor (JNIEnv* env, const LocalRef<jobject>& contentProvider,
|
||||
const String& filepath)
|
||||
{
|
||||
// This function can be called from multiple threads.
|
||||
@ -714,14 +789,10 @@ private:
|
||||
ParcelFileDescriptor.open,
|
||||
javaFile.get(), modeReadOnly));
|
||||
|
||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
||||
|
||||
if (exception != 0)
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// Failed to create file descriptor. Have you provided a valid file path/resource name?
|
||||
jassertfalse;
|
||||
|
||||
env->ExceptionClear();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -758,6 +829,51 @@ private:
|
||||
|
||||
WeakReference<ContentSharerNativeImpl>::Master masterReference;
|
||||
friend class WeakReference<ContentSharerNativeImpl>;
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
CALLBACK (contentSharerQuery, "contentSharerQuery", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \
|
||||
CALLBACK (contentSharerOpenFile, "contentSharerOpenFile", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;") \
|
||||
CALLBACK (contentSharerGetStreamTypes, "contentSharerGetStreamTypes", "(Landroid/net/Uri;Ljava/lang/String;)[Ljava/lang/String;") \
|
||||
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceSharingContentProvider, "com/roli/juce/JuceSharingContentProvider", 16, javaJuceSharingContentProvider, sizeof(javaJuceSharingContentProvider))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
static jobject JNICALL contentSharerQuery (JNIEnv*, jobject contentProvider, jobject uri, jobjectArray projection,
|
||||
jobject selection, jobjectArray selectionArgs, jobject sortOrder)
|
||||
{
|
||||
if (auto *pimpl = (ContentSharer::ContentSharerNativeImpl *) ContentSharer::getInstance ()->pimpl.get ())
|
||||
return pimpl->query (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
|
||||
LocalRef<jobject> (static_cast<jobject> (uri)),
|
||||
LocalRef<jobjectArray> (
|
||||
static_cast<jobjectArray> (projection)),
|
||||
LocalRef<jobject> (static_cast<jobject> (selection)),
|
||||
LocalRef<jobjectArray> (
|
||||
static_cast<jobjectArray> (selectionArgs)),
|
||||
LocalRef<jobject> (static_cast<jobject> (sortOrder)));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static jobject JNICALL contentSharerOpenFile (JNIEnv*, jobject contentProvider, jobject uri, jstring mode)
|
||||
{
|
||||
if (auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get())
|
||||
return pimpl->openFile (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
|
||||
LocalRef<jobject> (static_cast<jobject> (uri)),
|
||||
LocalRef<jstring> (static_cast<jstring> (mode)));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static jobjectArray JNICALL contentSharerGetStreamTypes (JNIEnv*, jobject /*contentProvider*/, jobject uri, jstring mimeTypeFilter)
|
||||
{
|
||||
if (auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get())
|
||||
return pimpl->getStreamTypes (LocalRef<jobject> (static_cast<jobject> (uri)),
|
||||
LocalRef<jstring> (static_cast<jstring> (mimeTypeFilter)));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
@ -766,81 +882,6 @@ ContentSharer::Pimpl* ContentSharer::createPimpl()
|
||||
return new ContentSharerNativeImpl (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void* juce_contentSharerQuery (void* contentProvider, void* uri, void* projection,
|
||||
void* selection, void* selectionArgs, void* sortOrder)
|
||||
{
|
||||
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
|
||||
return pimpl->query (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
|
||||
LocalRef<jobject> (static_cast<jobject> (uri)),
|
||||
LocalRef<jobjectArray> (static_cast<jobjectArray> (projection)),
|
||||
LocalRef<jobject> (static_cast<jobject> (selection)),
|
||||
LocalRef<jobjectArray> (static_cast<jobjectArray> (selectionArgs)),
|
||||
LocalRef<jobject> (static_cast<jobject> (sortOrder)));
|
||||
}
|
||||
|
||||
void* juce_contentSharerOpenFile (void* contentProvider, void* uri, void* mode)
|
||||
{
|
||||
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
|
||||
return pimpl->openFile (LocalRef<jobject> (static_cast<jobject> (contentProvider)),
|
||||
LocalRef<jobject> (static_cast<jobject> (uri)),
|
||||
LocalRef<jstring> (static_cast<jstring> (mode)));
|
||||
}
|
||||
|
||||
void juce_contentSharingCompleted (int resultCode)
|
||||
{
|
||||
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
|
||||
return pimpl->sharingFinished (resultCode);
|
||||
}
|
||||
|
||||
void* juce_contentSharerGetStreamTypes (void* uri, void* mimeTypeFilter)
|
||||
{
|
||||
auto* pimpl = (ContentSharer::ContentSharerNativeImpl*) ContentSharer::getInstance()->pimpl.get();
|
||||
return pimpl->getStreamTypes (LocalRef<jobject> (static_cast<jobject> (uri)),
|
||||
LocalRef<jstring> (static_cast<jstring> (mimeTypeFilter)));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerFileObserverEvent, void,
|
||||
(JNIEnv* env, jobject /*fileObserver*/, jlong host, int event, jstring path))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
reinterpret_cast<AndroidContentSharerFileObserver*> (host)->onFileEvent (event, LocalRef<jstring> (path));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerQuery, jobject,
|
||||
(JNIEnv* env, jobject contentProvider, jobject uri, jobjectArray projection,
|
||||
jobject selection, jobjectArray selectionArgs, jobject sortOrder))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
return (jobject) juce_contentSharerQuery (contentProvider, uri, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerCursorClosed, void,
|
||||
(JNIEnv* env, jobject /*cursor*/, jlong host))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
reinterpret_cast<AndroidContentSharerCursor*> (host)->cursorClosed();
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerOpenFile, jobject,
|
||||
(JNIEnv* env, jobject contentProvider, jobject uri, jstring mode))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
return (jobject) juce_contentSharerOpenFile ((void*) contentProvider, (void*) uri, (void*) mode);
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_SHARING_CONTENT_PROVIDER_CLASSNAME, contentSharerGetStreamTypes, jobject,
|
||||
(JNIEnv* env, jobject /*contentProvider*/, jobject uri, jstring mimeTypeFilter))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
return (jobject) juce_contentSharerGetStreamTypes ((void*) uri, (void*) mimeTypeFilter);
|
||||
}
|
||||
ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider_Class ContentSharer::ContentSharerNativeImpl::JuceSharingContentProvider;
|
||||
|
||||
} // namespace juce
|
||||
|
@ -30,6 +30,7 @@ namespace juce
|
||||
class FileChooser::Native : public FileChooser::Pimpl
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Native (FileChooser& fileChooser, int flags) : owner (fileChooser)
|
||||
{
|
||||
if (currentFileChooser == nullptr)
|
||||
@ -37,7 +38,7 @@ public:
|
||||
currentFileChooser = this;
|
||||
auto* env = getEnv();
|
||||
|
||||
auto sdkVersion = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion);
|
||||
auto sdkVersion = getAndroidSDKVersion();
|
||||
auto saveMode = ((flags & FileBrowserComponent::saveMode) != 0);
|
||||
auto selectsDirectories = ((flags & FileBrowserComponent::canSelectDirectories) != 0);
|
||||
|
||||
@ -64,8 +65,8 @@ public:
|
||||
: "android.intent.action.GET_CONTENT")));
|
||||
|
||||
|
||||
intent = GlobalRef (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
|
||||
javaString (action).get()));
|
||||
intent = GlobalRef (LocalRef<jobject> (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
|
||||
javaString (action).get())));
|
||||
|
||||
if (owner.startingFile != File())
|
||||
{
|
||||
@ -135,6 +136,7 @@ public:
|
||||
|
||||
~Native()
|
||||
{
|
||||
masterReference.clear();
|
||||
currentFileChooser = nullptr;
|
||||
}
|
||||
|
||||
@ -146,13 +148,26 @@ public:
|
||||
|
||||
void launch() override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
if (currentFileChooser != nullptr)
|
||||
android.activity.callVoidMethod (JuceAppActivity.startActivityForResult, intent.get(), /*READ_REQUEST_CODE*/ 42);
|
||||
{
|
||||
WeakReference<Native> myself (this);
|
||||
|
||||
startAndroidActivityForResult (LocalRef<jobject> (env->NewLocalRef (intent.get())), /*READ_REQUEST_CODE*/ 42,
|
||||
[myself] (int requestCode, int resultCode, LocalRef<jobject> intentData) mutable
|
||||
{
|
||||
if (myself != nullptr)
|
||||
myself->onActivityResult (requestCode, resultCode, intentData);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse; // There is already a file chooser running
|
||||
}
|
||||
}
|
||||
|
||||
void completed (int resultCode, jobject intentData)
|
||||
void onActivityResult (int /*requestCode*/, int resultCode, const LocalRef<jobject>& intentData)
|
||||
{
|
||||
currentFileChooser = nullptr;
|
||||
auto* env = getEnv();
|
||||
@ -161,7 +176,7 @@ public:
|
||||
|
||||
if (resultCode == /*Activity.RESULT_OK*/ -1 && intentData != nullptr)
|
||||
{
|
||||
LocalRef<jobject> uri (env->CallObjectMethod (intentData, AndroidIntent.getData));
|
||||
LocalRef<jobject> uri (env->CallObjectMethod (intentData.get(), AndroidIntent.getData));
|
||||
|
||||
if (uri != nullptr)
|
||||
{
|
||||
@ -197,23 +212,23 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (Native)
|
||||
|
||||
FileChooser& owner;
|
||||
GlobalRef intent;
|
||||
};
|
||||
|
||||
FileChooser::Native* FileChooser::Native::currentFileChooser = nullptr;
|
||||
|
||||
void juce_fileChooserCompleted (int resultCode, void* intentData)
|
||||
{
|
||||
if (FileChooser::Native::currentFileChooser != nullptr)
|
||||
FileChooser::Native::currentFileChooser->completed (resultCode, (jobject) intentData);
|
||||
}
|
||||
|
||||
|
||||
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
|
||||
FilePreviewComponent*)
|
||||
{
|
||||
return new FileChooser::Native (owner, flags);
|
||||
if (FileChooser::Native::currentFileChooser == nullptr)
|
||||
return new FileChooser::Native (owner, flags);
|
||||
|
||||
// there can only be one file chooser on Android at a once
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FileChooser::isPlatformDialogAvailable()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,7 @@ struct MimeTypeTableEntry
|
||||
{
|
||||
const char* fileExtension, *mimeType;
|
||||
|
||||
static MimeTypeTableEntry table[640];
|
||||
static MimeTypeTableEntry table[641];
|
||||
};
|
||||
|
||||
static StringArray getMimeTypesForFileExtension (const String& fileExtension)
|
||||
@ -88,7 +88,7 @@ static String getCommonMimeType (const StringArray& mimeTypes)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MimeTypeTableEntry MimeTypeTableEntry::table[640] =
|
||||
MimeTypeTableEntry MimeTypeTableEntry::table[641] =
|
||||
{
|
||||
{"3dm", "x-world/x-3dmf"},
|
||||
{"3dmf", "x-world/x-3dmf"},
|
||||
@ -384,6 +384,7 @@ MimeTypeTableEntry MimeTypeTableEntry::table[640] =
|
||||
{"mp2", "video/mpeg"},
|
||||
{"mp2", "video/x-mpeg"},
|
||||
{"mp2", "video/x-mpeq2a"},
|
||||
{"mp3", "audio/mpeg"},
|
||||
{"mp3", "audio/mpeg3"},
|
||||
{"mp3", "audio/x-mpeg-3"},
|
||||
{"mp3", "video/mpeg"},
|
||||
@ -477,7 +478,7 @@ MimeTypeTableEntry MimeTypeTableEntry::table[640] =
|
||||
{"pvu", "paleovu/x-pv"},
|
||||
{"pwz", "application/vnd.ms-powerpoint"},
|
||||
{"py", "text/x-script.phyton"},
|
||||
{"pyc", "applicaiton/x-bytecode.python"},
|
||||
{"pyc", "application/x-bytecode.python"},
|
||||
{"qcp", "audio/vnd.qcelp"},
|
||||
{"qd3", "x-world/x-3dmf"},
|
||||
{"qd3d", "x-world/x-3dmf"},
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
|
||||
void shareFiles (const Array<URL>& files) override
|
||||
{
|
||||
auto* urls = [NSMutableArray arrayWithCapacity: (NSUInteger) files.size()];
|
||||
auto urls = [NSMutableArray arrayWithCapacity: (NSUInteger) files.size()];
|
||||
|
||||
for (const auto& f : files)
|
||||
{
|
||||
@ -86,7 +86,7 @@ public:
|
||||
|
||||
void shareText (const String& text) override
|
||||
{
|
||||
auto* array = [NSArray arrayWithObject: juceStringToNS (text)];
|
||||
auto array = [NSArray arrayWithObject: juceStringToNS (text)];
|
||||
share (array);
|
||||
}
|
||||
|
||||
|
@ -85,11 +85,34 @@ public:
|
||||
|
||||
setOpaque (false);
|
||||
|
||||
auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
|
||||
setBounds (chooserBounds);
|
||||
if (SystemStats::isRunningInAppExtensionSandbox())
|
||||
{
|
||||
if (fileChooser.parent != nullptr)
|
||||
{
|
||||
[controller.get() setModalPresentationStyle:UIModalPresentationFullScreen];
|
||||
|
||||
setAlwaysOnTop (true);
|
||||
addToDesktop (0);
|
||||
auto chooserBounds = fileChooser.parent->getBounds();
|
||||
setBounds (chooserBounds);
|
||||
|
||||
setAlwaysOnTop (true);
|
||||
fileChooser.parent->addAndMakeVisible (this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Opening a native top-level window in an AUv3 is not allowed (sandboxing). You need to specify a
|
||||
// parent component (for example your editor) to parent the native file chooser window. To do this
|
||||
// specify a parent component in the FileChooser's constructor!
|
||||
jassert (fileChooser.parent != nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
|
||||
setBounds (chooserBounds);
|
||||
|
||||
setAlwaysOnTop (true);
|
||||
addToDesktop (0);
|
||||
}
|
||||
}
|
||||
|
||||
~Native()
|
||||
@ -199,7 +222,7 @@ private:
|
||||
|
||||
NSArray<NSFileAccessIntent*>* intents = @[fileAccessIntent];
|
||||
|
||||
auto* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter: nil];
|
||||
auto fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter: nil];
|
||||
|
||||
[fileCoordinator coordinateAccessWithIntents: intents queue: [NSOperationQueue mainQueue] byAccessor: ^(NSError* err)
|
||||
{
|
||||
@ -228,7 +251,7 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* desc = [error localizedDescription];
|
||||
auto desc = [error localizedDescription];
|
||||
ignoreUnused (desc);
|
||||
jassertfalse;
|
||||
}
|
||||
@ -237,7 +260,7 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* desc = [err localizedDescription];
|
||||
auto desc = [err localizedDescription];
|
||||
ignoreUnused (desc);
|
||||
jassertfalse;
|
||||
}
|
||||
@ -308,7 +331,7 @@ bool FileChooser::isPlatformDialogAvailable()
|
||||
#if JUCE_DISABLE_NATIVE_FILECHOOSERS
|
||||
return false;
|
||||
#else
|
||||
return [[NSFileManager defaultManager] ubiquityIdentityToken] != nil;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -163,8 +163,17 @@ using namespace juce;
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct UIViewPeerControllerReceiver
|
||||
{
|
||||
virtual ~UIViewPeerControllerReceiver();
|
||||
virtual void setViewController (UIViewController*) = 0;
|
||||
};
|
||||
|
||||
UIViewPeerControllerReceiver::~UIViewPeerControllerReceiver() {}
|
||||
|
||||
class UIViewComponentPeer : public ComponentPeer,
|
||||
public FocusChangeListener
|
||||
public FocusChangeListener,
|
||||
public UIViewPeerControllerReceiver
|
||||
{
|
||||
public:
|
||||
UIViewComponentPeer (Component&, int windowStyleFlags, UIView* viewToAttachTo);
|
||||
@ -176,6 +185,12 @@ public:
|
||||
void setTitle (const String& title) override;
|
||||
void setBounds (const Rectangle<int>&, bool isNowFullScreen) override;
|
||||
|
||||
void setViewController (UIViewController* newController) override
|
||||
{
|
||||
jassert (controller == nullptr);
|
||||
controller = [newController retain];
|
||||
}
|
||||
|
||||
Rectangle<int> getBounds() const override { return getBounds (! isSharedWindow); }
|
||||
Rectangle<int> getBounds (bool global) const;
|
||||
Point<float> localToGlobal (Point<float> relativePosition) override;
|
||||
@ -218,7 +233,7 @@ public:
|
||||
//==============================================================================
|
||||
UIWindow* window;
|
||||
JuceUIView* view;
|
||||
JuceUIViewController* controller;
|
||||
UIViewController* controller;
|
||||
bool isSharedWindow, fullScreen, insideDrawRect, isAppex;
|
||||
|
||||
static int64 getMouseTime (UIEvent* e) noexcept
|
||||
@ -375,6 +390,13 @@ MultiTouchMapper<UITouch*> UIViewComponentPeer::currentTouches;
|
||||
return isKioskModeView (self);
|
||||
}
|
||||
|
||||
#if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
|
||||
- (BOOL) prefersHomeIndicatorAutoHidden
|
||||
{
|
||||
return isKioskModeView (self);
|
||||
}
|
||||
#endif
|
||||
|
||||
- (UIStatusBarStyle) preferredStatusBarStyle
|
||||
{
|
||||
return UIStatusBarStyleDefault;
|
||||
@ -702,7 +724,7 @@ void UIViewComponentPeer::updateTransformAndScreenBounds()
|
||||
const Rectangle<int> oldArea (component.getBounds());
|
||||
const Rectangle<int> oldDesktop (desktop.getDisplays().getMainDisplay().userArea);
|
||||
|
||||
const_cast<Desktop::Displays&> (desktop.getDisplays()).refresh();
|
||||
const_cast<Displays&> (desktop.getDisplays()).refresh();
|
||||
|
||||
window.transform = Orientations::getCGTransformFor (desktop.getCurrentOrientation());
|
||||
view.transform = CGAffineTransformIdentity;
|
||||
|
@ -48,7 +48,8 @@ namespace juce
|
||||
}
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
- (id)init;
|
||||
- (id) init;
|
||||
- (void) dealloc;
|
||||
- (void) applicationDidFinishLaunching: (UIApplication*) application;
|
||||
- (void) applicationWillTerminate: (UIApplication*) application;
|
||||
- (void) applicationDidEnterBackground: (UIApplication*) application;
|
||||
@ -88,7 +89,7 @@ namespace juce
|
||||
|
||||
NSObject* _pushNotificationsDelegate;
|
||||
|
||||
- (id)init
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
appSuspendTask = UIBackgroundTaskInvalid;
|
||||
@ -100,6 +101,11 @@ namespace juce
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) applicationDidFinishLaunching: (UIApplication*) application
|
||||
{
|
||||
ignoreUnused (application);
|
||||
@ -640,13 +646,13 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType /*i
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*)
|
||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*, std::function<void()>)
|
||||
{
|
||||
jassertfalse; // no such thing on iOS!
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*)
|
||||
bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*, std::function<void()>)
|
||||
{
|
||||
jassertfalse; // no such thing on iOS!
|
||||
return false;
|
||||
@ -730,7 +736,7 @@ Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
|
||||
return Orientations::convertToJuce (orientation);
|
||||
}
|
||||
|
||||
void Desktop::Displays::findDisplays (float masterScale)
|
||||
void Displays::findDisplays (float masterScale)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -128,7 +128,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
~Native()
|
||||
~Native() override
|
||||
{
|
||||
exitModalState (0);
|
||||
removeFromDesktop();
|
||||
@ -188,6 +188,14 @@ public:
|
||||
finished (result);
|
||||
}
|
||||
|
||||
bool canModalEventBeSentToComponent (const Component* targetComponent) override
|
||||
{
|
||||
if (targetComponent == nullptr)
|
||||
return false;
|
||||
|
||||
return targetComponent->findParentComponentOfClass<FilePreviewComponent>() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
@ -202,13 +210,22 @@ private:
|
||||
|
||||
exitModalState (0);
|
||||
|
||||
if (panel != nil && result == NSFileHandlingPanelOKButton)
|
||||
if (panel != nil && result ==
|
||||
#if defined (MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
|
||||
NSModalResponseOK)
|
||||
#else
|
||||
NSFileHandlingPanelOKButton)
|
||||
#endif
|
||||
{
|
||||
auto addURLResult = [&chooserResults] (NSURL* urlToAdd)
|
||||
{
|
||||
auto scheme = nsStringToJuce ([urlToAdd scheme]);
|
||||
auto path = nsStringToJuce ([urlToAdd path]);
|
||||
chooserResults.add (URL (scheme + "://" + path));
|
||||
auto pathComponents = StringArray::fromTokens (nsStringToJuce ([urlToAdd path]), "/", {});
|
||||
|
||||
for (auto& component : pathComponents)
|
||||
component = URL::addEscapeChars (component, false);
|
||||
|
||||
chooserResults.add (URL (scheme + "://" + pathComponents.joinIntoString ("/")));
|
||||
};
|
||||
|
||||
if (isSave)
|
||||
@ -218,7 +235,7 @@ private:
|
||||
else
|
||||
{
|
||||
auto* openPanel = (NSOpenPanel*) panel;
|
||||
auto* urls = [openPanel URLs];
|
||||
auto urls = [openPanel URLs];
|
||||
|
||||
for (unsigned int i = 0; i < [urls count]; ++i)
|
||||
addURLResult ([urls objectAtIndex: i]);
|
||||
|
@ -33,11 +33,11 @@ struct JuceMainMenuBarHolder : private DeletedAtShutdown
|
||||
JuceMainMenuBarHolder()
|
||||
: mainMenuBar ([[NSMenu alloc] initWithTitle: nsStringLiteral ("MainMenu")])
|
||||
{
|
||||
auto* item = [mainMenuBar addItemWithTitle: nsStringLiteral ("Apple")
|
||||
action: nil
|
||||
auto item = [mainMenuBar addItemWithTitle: nsStringLiteral ("Apple")
|
||||
action: nil
|
||||
keyEquivalent: nsEmptyString()];
|
||||
|
||||
auto* appMenu = [[NSMenu alloc] initWithTitle: nsStringLiteral ("Apple")];
|
||||
auto appMenu = [[NSMenu alloc] initWithTitle: nsStringLiteral ("Apple")];
|
||||
|
||||
[NSApp performSelector: @selector (setAppleMenu:) withObject: appMenu];
|
||||
[mainMenuBar setSubmenu: appMenu forItem: item];
|
||||
@ -73,7 +73,7 @@ public:
|
||||
JuceMenuCallbackClass::setOwner (callback, this);
|
||||
}
|
||||
|
||||
~JuceMainMenuHandler()
|
||||
~JuceMainMenuHandler() override
|
||||
{
|
||||
setMenu (nullptr, nullptr, String());
|
||||
|
||||
@ -277,9 +277,9 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* item = [[NSMenuItem alloc] initWithTitle: text
|
||||
action: @selector (menuItemInvoked:)
|
||||
keyEquivalent: nsEmptyString()];
|
||||
auto item = [[NSMenuItem alloc] initWithTitle: text
|
||||
action: @selector (menuItemInvoked:)
|
||||
keyEquivalent: nsEmptyString()];
|
||||
|
||||
[item setTag: topLevelIndex];
|
||||
[item setEnabled: i.isEnabled];
|
||||
@ -478,9 +478,9 @@ private:
|
||||
{
|
||||
if (isPositiveAndBelow (menuItemIndex, (int) [parentMenu numberOfItems]))
|
||||
{
|
||||
auto* menuItem = [parentMenu itemAtIndex:menuItemIndex];
|
||||
auto menuItem = [parentMenu itemAtIndex:menuItemIndex];
|
||||
|
||||
if (auto* submenu = [menuItem submenu])
|
||||
if (auto submenu = [menuItem submenu])
|
||||
removeItemRecursive (submenu);
|
||||
|
||||
[parentMenu removeItem:menuItem];
|
||||
@ -706,7 +706,7 @@ namespace MainMenuHelpers
|
||||
{
|
||||
if ([mainMenu numberOfItems] > 0)
|
||||
{
|
||||
if (auto* appMenu = [[mainMenu itemAtIndex:0] submenu])
|
||||
if (auto appMenu = [[mainMenu itemAtIndex: 0] submenu])
|
||||
{
|
||||
[appMenu removeAllItems];
|
||||
MainMenuHelpers::createStandardAppMenu (appMenu, app->getApplicationName(), extraItems);
|
||||
@ -765,7 +765,7 @@ const PopupMenu* MenuBarModel::getMacExtraAppleItemsMenu()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
typedef void (*MenuTrackingChangedCallback) (bool);
|
||||
using MenuTrackingChangedCallback = void (*)(bool);
|
||||
extern MenuTrackingChangedCallback menuTrackingChangedCallback;
|
||||
|
||||
static void mainMenuTrackingChanged (bool isTracking)
|
||||
|
@ -164,11 +164,6 @@ void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool /*isSt
|
||||
[((NSCursor*) cursorHandle) release];
|
||||
}
|
||||
|
||||
void MouseCursor::showInAllWindows() const
|
||||
{
|
||||
showInWindow (nullptr);
|
||||
}
|
||||
|
||||
void MouseCursor::showInWindow (ComponentPeer*) const
|
||||
{
|
||||
auto c = (NSCursor*) getHandle();
|
||||
@ -184,7 +179,6 @@ void MouseCursor::showInWindow (ComponentPeer*) const
|
||||
void* CustomMouseCursorInfo::create() const { return nullptr; }
|
||||
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType) { return nullptr; }
|
||||
void MouseCursor::deleteMouseCursor (void*, bool) {}
|
||||
void MouseCursor::showInAllWindows() const {}
|
||||
void MouseCursor::showInWindow (ComponentPeer*) const {}
|
||||
|
||||
#endif
|
||||
|
@ -64,10 +64,6 @@ static NSRect flippedScreenRect (NSRect r) noexcept
|
||||
return r;
|
||||
}
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_opengl
|
||||
void componentPeerAboutToChange (Component&, bool);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class NSViewComponentPeer : public ComponentPeer,
|
||||
private Timer
|
||||
@ -75,6 +71,7 @@ class NSViewComponentPeer : public ComponentPeer,
|
||||
public:
|
||||
NSViewComponentPeer (Component& comp, const int windowStyleFlags, NSView* viewToAttachTo)
|
||||
: ComponentPeer (comp, windowStyleFlags),
|
||||
safeComponent (&comp),
|
||||
isSharedWindow (viewToAttachTo != nil),
|
||||
lastRepaintTime (Time::getMillisecondCounter())
|
||||
{
|
||||
@ -95,24 +92,6 @@ public:
|
||||
name: NSViewFrameDidChangeNotification
|
||||
object: view];
|
||||
|
||||
if (! isSharedWindow)
|
||||
{
|
||||
[notificationCenter addObserver: view
|
||||
selector: @selector (frameChanged:)
|
||||
name: NSWindowDidMoveNotification
|
||||
object: window];
|
||||
|
||||
[notificationCenter addObserver: view
|
||||
selector: @selector (frameChanged:)
|
||||
name: NSWindowDidMiniaturizeNotification
|
||||
object: window];
|
||||
|
||||
[notificationCenter addObserver: view
|
||||
selector: @selector (frameChanged:)
|
||||
name: NSWindowDidDeminiaturizeNotification
|
||||
object: window];
|
||||
}
|
||||
|
||||
[view setPostsFrameChangedNotifications: YES];
|
||||
|
||||
if (isSharedWindow)
|
||||
@ -137,7 +116,18 @@ public:
|
||||
#else
|
||||
[window setDelegate: window];
|
||||
#endif
|
||||
|
||||
[window setOpaque: component.isOpaque()];
|
||||
|
||||
#if defined (MAC_OS_X_VERSION_10_14)
|
||||
if (! [window isOpaque])
|
||||
[window setBackgroundColor: [NSColor clearColor]];
|
||||
|
||||
#if defined (MAC_OS_X_VERSION_10_9) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9)
|
||||
[view setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
[window setHasShadow: ((windowStyleFlags & windowHasDropShadow) != 0)];
|
||||
|
||||
if (component.isAlwaysOnTop())
|
||||
@ -165,8 +155,28 @@ public:
|
||||
|
||||
#if defined (MAC_OS_X_VERSION_10_13) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13)
|
||||
if ([window respondsToSelector: @selector (setTabbingMode:)])
|
||||
[window setTabbingMode:NSWindowTabbingModeDisallowed];
|
||||
[window setTabbingMode: NSWindowTabbingModeDisallowed];
|
||||
#endif
|
||||
|
||||
[notificationCenter addObserver: view
|
||||
selector: @selector (frameChanged:)
|
||||
name: NSWindowDidMoveNotification
|
||||
object: window];
|
||||
|
||||
[notificationCenter addObserver: view
|
||||
selector: @selector (frameChanged:)
|
||||
name: NSWindowDidMiniaturizeNotification
|
||||
object: window];
|
||||
|
||||
[notificationCenter addObserver: view
|
||||
selector: @selector (windowWillMiniaturize:)
|
||||
name: NSWindowWillMiniaturizeNotification
|
||||
object: window];
|
||||
|
||||
[notificationCenter addObserver: view
|
||||
selector: @selector (windowDidDeminiaturize:)
|
||||
name: NSWindowDidDeminiaturizeNotification
|
||||
object: window];
|
||||
}
|
||||
|
||||
auto alpha = component.getAlpha();
|
||||
@ -187,7 +197,7 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
~NSViewComponentPeer()
|
||||
~NSViewComponentPeer() override
|
||||
{
|
||||
[notificationCenter removeObserver: view];
|
||||
setOwner (view, nullptr);
|
||||
@ -216,7 +226,10 @@ public:
|
||||
{
|
||||
if (isSharedWindow)
|
||||
{
|
||||
[view setHidden: ! shouldBeVisible];
|
||||
if (shouldBeVisible)
|
||||
[view setHidden: false];
|
||||
else if ([window firstResponder] != view || ([window firstResponder] == view && [window makeFirstResponder: nil]))
|
||||
[view setHidden: true];
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -478,10 +491,14 @@ public:
|
||||
bool setAlwaysOnTop (bool alwaysOnTop) override
|
||||
{
|
||||
if (! isSharedWindow)
|
||||
{
|
||||
[window setLevel: alwaysOnTop ? ((getStyleFlags() & windowIsTemporary) != 0 ? NSPopUpMenuWindowLevel
|
||||
: NSFloatingWindowLevel)
|
||||
: NSNormalWindowLevel];
|
||||
|
||||
isAlwaysOnTop = alwaysOnTop;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -705,12 +722,11 @@ public:
|
||||
|
||||
void redirectWillMoveToWindow (NSWindow* newWindow)
|
||||
{
|
||||
#if JUCE_MODULE_AVAILABLE_juce_opengl
|
||||
if ([view window] == window)
|
||||
componentPeerAboutToChange (getComponent(), newWindow == nullptr);
|
||||
#else
|
||||
ignoreUnused (newWindow);
|
||||
#endif
|
||||
if (isSharedWindow && [view window] == window && newWindow == nullptr)
|
||||
{
|
||||
if (auto* comp = safeComponent.get())
|
||||
comp->setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void sendMouseEvent (NSEvent* ev)
|
||||
@ -780,7 +796,7 @@ public:
|
||||
{
|
||||
// (need to retain this in case a modal loop runs in handleKeyEvent and
|
||||
// our event object gets lost)
|
||||
const NSObjectRetainer<NSEvent> r (ev);
|
||||
const std::unique_ptr<NSEvent, NSObjectDeleter> r ([ev retain]);
|
||||
|
||||
updateKeysDown (ev, true);
|
||||
bool used = handleKeyEvent (ev, true);
|
||||
@ -810,7 +826,7 @@ public:
|
||||
void redirectModKeyChange (NSEvent* ev)
|
||||
{
|
||||
// (need to retain this in case a modal loop runs and our event object gets lost)
|
||||
const NSObjectRetainer<NSEvent> r (ev);
|
||||
const std::unique_ptr<NSEvent, NSObjectDeleter> r ([ev retain]);
|
||||
|
||||
keysCurrentlyDown.clear();
|
||||
handleKeyUpOrDown (true);
|
||||
@ -1046,7 +1062,15 @@ public:
|
||||
void viewMovedToWindow()
|
||||
{
|
||||
if (isSharedWindow)
|
||||
window = [view window];
|
||||
{
|
||||
auto newWindow = [view window];
|
||||
bool shouldSetVisible = (window == nullptr && newWindow != nullptr);
|
||||
|
||||
window = newWindow;
|
||||
|
||||
if (shouldSetVisible)
|
||||
getComponent().setVisible (true);
|
||||
}
|
||||
}
|
||||
|
||||
void liveResizingStart()
|
||||
@ -1314,10 +1338,13 @@ public:
|
||||
|
||||
while ((track = [enumerator nextObject]) != nil)
|
||||
{
|
||||
NSURL* url = [NSURL URLWithString: [track valueForKey: nsStringLiteral ("Location")]];
|
||||
if (id value = [track valueForKey: nsStringLiteral ("Location")])
|
||||
{
|
||||
NSURL* url = [NSURL URLWithString: value];
|
||||
|
||||
if ([url isFileURL])
|
||||
files.add (nsStringToJuce ([url path]));
|
||||
if ([url isFileURL])
|
||||
files.add (nsStringToJuce ([url path]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1382,6 +1409,7 @@ public:
|
||||
//==============================================================================
|
||||
NSWindow* window = nil;
|
||||
NSView* view = nil;
|
||||
WeakReference<Component> safeComponent;
|
||||
bool isSharedWindow = false, fullScreen = false;
|
||||
bool isWindowInKioskMode = false;
|
||||
#if USE_COREGRAPHICS_RENDERING
|
||||
@ -1392,6 +1420,7 @@ public:
|
||||
bool isZooming = false, isFirstLiveResize = false, textWasInserted = false;
|
||||
bool isStretchingTop = false, isStretchingLeft = false, isStretchingBottom = false, isStretchingRight = false;
|
||||
bool windowRepresentsFile = false;
|
||||
bool isAlwaysOnTop = false, wasAlwaysOnTop = false;
|
||||
String stringBeingComposed;
|
||||
NSNotificationCenter* notificationCenter = nil;
|
||||
|
||||
@ -1561,6 +1590,8 @@ struct JuceNSViewClass : public ObjCClass<NSView>
|
||||
addMethod (@selector (magnifyWithEvent:), magnify, "v@:@");
|
||||
addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "c@:@");
|
||||
addMethod (@selector (frameChanged:), frameChanged, "v@:@");
|
||||
addMethod (@selector (windowWillMiniaturize:), windowWillMiniaturize, "v@:@");
|
||||
addMethod (@selector (windowDidDeminiaturize:), windowDidDeminiaturize, "v@:@");
|
||||
addMethod (@selector (wantsDefaultClipping:), wantsDefaultClipping, "c@:");
|
||||
addMethod (@selector (worksWhenModal), worksWhenModal, "c@:");
|
||||
addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow, "v@:");
|
||||
@ -1656,6 +1687,31 @@ private:
|
||||
static void frameChanged (id self, SEL, NSNotification*) { if (auto* p = getOwner (self)) p->redirectMovedOrResized(); }
|
||||
static void viewDidMoveToWindow (id self, SEL) { if (auto* p = getOwner (self)) p->viewMovedToWindow(); }
|
||||
|
||||
static void windowWillMiniaturize (id self, SEL, NSNotification*)
|
||||
{
|
||||
if (auto* p = getOwner (self))
|
||||
{
|
||||
if (p->isAlwaysOnTop)
|
||||
{
|
||||
// there is a bug when restoring minimised always on top windows so we need
|
||||
// to remove this behaviour before minimising and restore it afterwards
|
||||
p->setAlwaysOnTop (false);
|
||||
p->wasAlwaysOnTop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void windowDidDeminiaturize (id self, SEL, NSNotification*)
|
||||
{
|
||||
if (auto* p = getOwner (self))
|
||||
{
|
||||
if (p->wasAlwaysOnTop)
|
||||
p->setAlwaysOnTop (true);
|
||||
|
||||
p->redirectMovedOrResized();
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL isOpaque (id self, SEL)
|
||||
{
|
||||
auto* owner = getOwner (self);
|
||||
@ -2102,6 +2158,8 @@ void Desktop::setKioskComponent (Component* kioskComp, bool shouldBeEnabled, boo
|
||||
{
|
||||
if (shouldBeEnabled && ! allowMenusAndBars)
|
||||
[NSApp setPresentationOptions: NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar];
|
||||
else if (! shouldBeEnabled)
|
||||
[NSApp setPresentationOptions: NSApplicationPresentationDefault];
|
||||
|
||||
[peer->window performSelector: @selector (toggleFullScreen:) withObject: nil];
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ static NSRect getDragRect (NSView* view, NSEvent* event)
|
||||
static NSView* getNSViewForDragEvent (Component* sourceComp)
|
||||
{
|
||||
if (sourceComp == nullptr)
|
||||
if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource(0))
|
||||
if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
|
||||
sourceComp = draggingSource->getComponentUnderMouse();
|
||||
|
||||
if (sourceComp != nullptr)
|
||||
@ -177,14 +177,22 @@ static NSView* getNSViewForDragEvent (Component* sourceComp)
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct TextDragDataProviderClass : public ObjCClass<NSObject>
|
||||
struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>>
|
||||
{
|
||||
TextDragDataProviderClass() : ObjCClass<NSObject> ("JUCE_NSTextDragDataProvider_")
|
||||
NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_")
|
||||
{
|
||||
addIvar<std::function<void()>*> ("callback");
|
||||
addIvar<String*> ("text");
|
||||
addIvar<NSDragOperation*> ("operation");
|
||||
|
||||
addMethod (@selector (dealloc), dealloc, "v@:");
|
||||
addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType, "v@:@@@");
|
||||
|
||||
addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@");
|
||||
addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded, "v@:@@@");
|
||||
|
||||
addProtocol (@protocol (NSPasteboardItemDataProvider));
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
@ -193,10 +201,23 @@ struct TextDragDataProviderClass : public ObjCClass<NSObject>
|
||||
object_setInstanceVariable (self, "text", new String (text));
|
||||
}
|
||||
|
||||
static void setCompletionCallback (id self, std::function<void()> cb)
|
||||
{
|
||||
object_setInstanceVariable (self, "callback", new std::function<void()> (cb));
|
||||
}
|
||||
|
||||
static void setDragOperation (id self, NSDragOperation op)
|
||||
{
|
||||
object_setInstanceVariable (self, "operation", new NSDragOperation (op));
|
||||
}
|
||||
|
||||
private:
|
||||
static void dealloc (id self, SEL)
|
||||
{
|
||||
delete getIvar<String*> (self, "text");
|
||||
delete getIvar<std::function<void()>*> (self, "callback");
|
||||
delete getIvar<NSDragOperation*> (self, "operation");
|
||||
|
||||
sendSuperclassMessage (self, @selector (dealloc));
|
||||
}
|
||||
|
||||
@ -207,9 +228,29 @@ private:
|
||||
[sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding]
|
||||
forType: NSPasteboardTypeString];
|
||||
}
|
||||
|
||||
static NSDragOperation sourceOperationMaskForDraggingContext (id self, SEL, NSDraggingSession*, NSDraggingContext)
|
||||
{
|
||||
return *getIvar<NSDragOperation*> (self, "operation");
|
||||
}
|
||||
|
||||
static void draggingSessionEnded (id self, SEL, NSDraggingSession*, NSPoint p, NSDragOperation)
|
||||
{
|
||||
// Our view doesn't receive a mouse up when the drag ends so we need to generate one here and send it...
|
||||
if (auto* view = getNSViewForDragEvent (nullptr))
|
||||
if (auto* cgEvent = CGEventCreateMouseEvent (nullptr, kCGEventLeftMouseUp, CGPointMake (p.x, p.y), kCGMouseButtonLeft))
|
||||
if (id e = [NSEvent eventWithCGEvent: cgEvent])
|
||||
[view mouseUp: e];
|
||||
|
||||
if (auto* cb = getIvar<std::function<void()>*> (self, "callback"))
|
||||
cb->operator()();
|
||||
}
|
||||
};
|
||||
|
||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent)
|
||||
static NSDraggingSourceHelper draggingSourceHelper;
|
||||
|
||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent,
|
||||
std::function<void()> callback)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return false;
|
||||
@ -218,28 +259,33 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
if (auto* event = [[view window] currentEvent])
|
||||
if (auto event = [[view window] currentEvent])
|
||||
{
|
||||
static TextDragDataProviderClass dataProviderClass;
|
||||
id delegate = [dataProviderClass.createInstance() init];
|
||||
TextDragDataProviderClass::setText (delegate, text);
|
||||
id helper = [draggingSourceHelper.createInstance() init];
|
||||
NSDraggingSourceHelper::setText (helper, text);
|
||||
NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy);
|
||||
|
||||
auto* pasteboardItem = [[NSPasteboardItem new] autorelease];
|
||||
[pasteboardItem setDataProvider: delegate
|
||||
if (callback != nullptr)
|
||||
NSDraggingSourceHelper::setCompletionCallback (helper, callback);
|
||||
|
||||
auto pasteboardItem = [[NSPasteboardItem new] autorelease];
|
||||
[pasteboardItem setDataProvider: helper
|
||||
forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]];
|
||||
|
||||
auto* dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease];
|
||||
auto dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease];
|
||||
|
||||
NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()];
|
||||
[dragItem setDraggingFrame: getDragRect (view, event) contents: image];
|
||||
|
||||
auto* draggingSession = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem]
|
||||
event: event
|
||||
source: delegate];
|
||||
if (auto session = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem]
|
||||
event: event
|
||||
source: helper])
|
||||
{
|
||||
session.animatesToStartingPositionsOnCancelOrFail = YES;
|
||||
session.draggingFormation = NSDraggingFormationNone;
|
||||
|
||||
draggingSession.animatesToStartingPositionsOnCancelOrFail = YES;
|
||||
draggingSession.draggingFormation = NSDraggingFormationNone;
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -247,24 +293,8 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co
|
||||
return false;
|
||||
}
|
||||
|
||||
struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>>
|
||||
{
|
||||
NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_")
|
||||
{
|
||||
addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@");
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static NSDragOperation sourceOperationMaskForDraggingContext (id, SEL, NSDraggingSession*, NSDraggingContext)
|
||||
{
|
||||
return NSDragOperationCopy;
|
||||
}
|
||||
};
|
||||
|
||||
static NSDraggingSourceHelper draggingSourceHelper;
|
||||
|
||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool /*canMoveFiles*/,
|
||||
Component* sourceComponent)
|
||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
|
||||
Component* sourceComponent, std::function<void()> callback)
|
||||
{
|
||||
if (files.isEmpty())
|
||||
return false;
|
||||
@ -273,20 +303,20 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
if (auto* event = [[view window] currentEvent])
|
||||
if (auto event = [[view window] currentEvent])
|
||||
{
|
||||
auto* dragItems = [[[NSMutableArray alloc] init] autorelease];
|
||||
auto dragItems = [[[NSMutableArray alloc] init] autorelease];
|
||||
|
||||
for (auto& filename : files)
|
||||
{
|
||||
auto* nsFilename = juceStringToNS (filename);
|
||||
auto* fileURL = [NSURL fileURLWithPath: nsFilename];
|
||||
auto* dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: fileURL];
|
||||
auto fileURL = [NSURL fileURLWithPath: nsFilename];
|
||||
auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: fileURL];
|
||||
|
||||
auto eventPos = [event locationInWindow];
|
||||
auto dragRect = [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
|
||||
fromView: nil];
|
||||
auto* dragImage = [[NSWorkspace sharedWorkspace] iconForFile: nsFilename];
|
||||
auto dragImage = [[NSWorkspace sharedWorkspace] iconForFile: nsFilename];
|
||||
[dragItem setDraggingFrame: dragRect
|
||||
contents: dragImage];
|
||||
|
||||
@ -294,11 +324,17 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi
|
||||
[dragItem release];
|
||||
}
|
||||
|
||||
auto* helper = [draggingSourceHelper.createInstance() autorelease];
|
||||
auto helper = [draggingSourceHelper.createInstance() autorelease];
|
||||
|
||||
if (callback != nullptr)
|
||||
NSDraggingSourceHelper::setCompletionCallback (helper, callback);
|
||||
|
||||
NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove
|
||||
: NSDragOperationCopy);
|
||||
|
||||
return [view beginDraggingSessionWithItems: dragItems
|
||||
event: event
|
||||
source: helper];
|
||||
source: helper] != nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -428,18 +464,18 @@ struct DisplaySettingsChangeCallback : private DeletedAtShutdown
|
||||
{
|
||||
DisplaySettingsChangeCallback()
|
||||
{
|
||||
CGDisplayRegisterReconfigurationCallback (displayReconfigurationCallBack, 0);
|
||||
CGDisplayRegisterReconfigurationCallback (displayReconfigurationCallBack, nullptr);
|
||||
}
|
||||
|
||||
~DisplaySettingsChangeCallback()
|
||||
{
|
||||
CGDisplayRemoveReconfigurationCallback (displayReconfigurationCallBack, 0);
|
||||
CGDisplayRemoveReconfigurationCallback (displayReconfigurationCallBack, nullptr);
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
static void displayReconfigurationCallBack (CGDirectDisplayID, CGDisplayChangeSummaryFlags, void*)
|
||||
{
|
||||
const_cast<Desktop::Displays&> (Desktop::getInstance().getDisplays()).refresh();
|
||||
const_cast<Displays&> (Desktop::getInstance().getDisplays()).refresh();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (DisplaySettingsChangeCallback)
|
||||
@ -455,9 +491,9 @@ static Rectangle<int> convertDisplayRect (NSRect r, CGFloat mainScreenBottom)
|
||||
return convertToRectInt (r);
|
||||
}
|
||||
|
||||
static Desktop::Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenBottom, const float masterScale)
|
||||
static Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenBottom, const float masterScale)
|
||||
{
|
||||
Desktop::Displays::Display d;
|
||||
Displays::Display d;
|
||||
|
||||
d.isMain = (mainScreenBottom == 0);
|
||||
|
||||
@ -479,7 +515,7 @@ static Desktop::Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& ma
|
||||
return d;
|
||||
}
|
||||
|
||||
void Desktop::Displays::findDisplays (const float masterScale)
|
||||
void Displays::findDisplays (const float masterScale)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
|
@ -241,49 +241,114 @@ namespace DragAndDropHelpers
|
||||
return hDrop;
|
||||
}
|
||||
|
||||
bool performDragDrop (FORMATETC* const format, STGMEDIUM* const medium, const DWORD whatToDo)
|
||||
struct DragAndDropJob : public ThreadPoolJob
|
||||
{
|
||||
auto source = new JuceDropSource();
|
||||
auto data = new JuceDataObject (source, format, medium);
|
||||
DragAndDropJob (FORMATETC f, STGMEDIUM m, DWORD d, std::function<void()> cb)
|
||||
: ThreadPoolJob ("DragAndDrop"),
|
||||
format (f), medium (m), whatToDo (d),
|
||||
completionCallback (cb)
|
||||
{
|
||||
}
|
||||
|
||||
DWORD effect;
|
||||
auto res = DoDragDrop (data, source, whatToDo, &effect);
|
||||
JobStatus runJob() override
|
||||
{
|
||||
OleInitialize (0);
|
||||
|
||||
data->Release();
|
||||
source->Release();
|
||||
auto source = new JuceDropSource();
|
||||
auto data = new JuceDataObject (source, &format, &medium);
|
||||
|
||||
return res == DRAGDROP_S_DROP;
|
||||
}
|
||||
DWORD effect;
|
||||
DoDragDrop (data, source, whatToDo, &effect);
|
||||
|
||||
data->Release();
|
||||
source->Release();
|
||||
|
||||
OleUninitialize();
|
||||
|
||||
if (completionCallback != nullptr)
|
||||
MessageManager::callAsync (completionCallback);
|
||||
|
||||
return jobHasFinished;
|
||||
}
|
||||
|
||||
FORMATETC format;
|
||||
STGMEDIUM medium;
|
||||
DWORD whatToDo;
|
||||
|
||||
std::function<void()> completionCallback;
|
||||
};
|
||||
|
||||
class ThreadPoolHolder : private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
ThreadPoolHolder() = default;
|
||||
|
||||
~ThreadPoolHolder()
|
||||
{
|
||||
// Wait forever if there's a job running. The user needs to cancel the transfer
|
||||
// in the GUI.
|
||||
pool.removeAllJobs (true, -1);
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED (ThreadPoolHolder, false)
|
||||
|
||||
// We need to make sure we don't do simultaneous text and file drag and drops,
|
||||
// so use a pool that can only run a single job.
|
||||
ThreadPool pool { 1 };
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (ThreadPoolHolder)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove, Component*)
|
||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove,
|
||||
Component*, std::function<void()> callback)
|
||||
{
|
||||
if (files.isEmpty())
|
||||
return false;
|
||||
|
||||
FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
|
||||
|
||||
medium.hGlobal = DragAndDropHelpers::createHDrop (files);
|
||||
|
||||
return DragAndDropHelpers::performDragDrop (&format, &medium, canMove ? (DWORD) (DROPEFFECT_COPY | DROPEFFECT_MOVE)
|
||||
: (DWORD) DROPEFFECT_COPY);
|
||||
auto& pool = DragAndDropHelpers::ThreadPoolHolder::getInstance()->pool;
|
||||
pool.addJob (new DragAndDropHelpers::DragAndDropJob (format, medium,
|
||||
canMove ? (DROPEFFECT_COPY | DROPEFFECT_MOVE) : DROPEFFECT_COPY,
|
||||
callback),
|
||||
true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component*)
|
||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component*, std::function<void()> callback)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return false;
|
||||
|
||||
FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
|
||||
|
||||
auto numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
|
||||
|
||||
medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2);
|
||||
WCHAR* const data = static_cast<WCHAR*> (GlobalLock (medium.hGlobal));
|
||||
auto* data = static_cast<WCHAR*> (GlobalLock (medium.hGlobal));
|
||||
|
||||
text.copyToUTF16 (data, numBytes);
|
||||
text.copyToUTF16 (data, numBytes + 2);
|
||||
format.cfFormat = CF_UNICODETEXT;
|
||||
|
||||
GlobalUnlock (medium.hGlobal);
|
||||
|
||||
return DragAndDropHelpers::performDragDrop (&format, &medium, DROPEFFECT_COPY | DROPEFFECT_MOVE);
|
||||
auto& pool = DragAndDropHelpers::ThreadPoolHolder::getInstance()->pool;
|
||||
pool.addJob (new DragAndDropHelpers::DragAndDropJob (format,
|
||||
medium,
|
||||
DROPEFFECT_COPY | DROPEFFECT_MOVE,
|
||||
callback),
|
||||
true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -120,6 +120,8 @@ public:
|
||||
EndDialog (hwnd, 0);
|
||||
}
|
||||
|
||||
Component* getCustomComponent() { return customComponent.get(); }
|
||||
|
||||
Array<URL> results;
|
||||
|
||||
private:
|
||||
@ -355,38 +357,42 @@ private:
|
||||
{
|
||||
nativeDialogRef.set (hdlg);
|
||||
|
||||
if (customComponent)
|
||||
if (customComponent != nullptr)
|
||||
{
|
||||
Component::SafePointer<Component> custom (customComponent.get());
|
||||
Component::SafePointer<Component> safeCustomComponent (customComponent.get());
|
||||
|
||||
RECT r, cr;
|
||||
GetWindowRect (hdlg, &r);
|
||||
GetClientRect (hdlg, &cr);
|
||||
RECT dialogScreenRect, dialogClientRect;
|
||||
GetWindowRect (hdlg, &dialogScreenRect);
|
||||
GetClientRect (hdlg, &dialogClientRect);
|
||||
|
||||
auto componentWidth = custom->getWidth();
|
||||
auto screenRectangle = Rectangle<int>::leftTopRightBottom (dialogScreenRect.left, dialogScreenRect.top,
|
||||
dialogScreenRect.right, dialogScreenRect.bottom);
|
||||
|
||||
SetWindowPos (hdlg, 0,
|
||||
r.left, r.top,
|
||||
componentWidth + jmax (150, (int) (r.right - r.left)),
|
||||
jmax (150, (int) (r.bottom - r.top)),
|
||||
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
|
||||
auto scale = Desktop::getInstance().getDisplays().findDisplayForRect (screenRectangle, true).scale;
|
||||
auto physicalComponentWidth = roundToInt (safeCustomComponent->getWidth() * scale);
|
||||
|
||||
SetWindowPos (hdlg, 0, screenRectangle.getX(), screenRectangle.getY(),
|
||||
physicalComponentWidth + jmax (150, screenRectangle.getWidth()),
|
||||
jmax (150, screenRectangle.getHeight()),
|
||||
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
|
||||
|
||||
auto appendCustomComponent = [safeCustomComponent, dialogClientRect, scale, hdlg]() mutable
|
||||
{
|
||||
if (safeCustomComponent != nullptr)
|
||||
{
|
||||
auto scaledClientRectangle = Rectangle<int>::leftTopRightBottom (dialogClientRect.left, dialogClientRect.top,
|
||||
dialogClientRect.right, dialogClientRect.bottom) / scale;
|
||||
|
||||
safeCustomComponent->setBounds (scaledClientRectangle.getRight(), scaledClientRectangle.getY(),
|
||||
safeCustomComponent->getWidth(), scaledClientRectangle.getHeight());
|
||||
safeCustomComponent->addToDesktop (0, hdlg);
|
||||
}
|
||||
};
|
||||
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
{
|
||||
custom->setBounds (cr.right, cr.top, componentWidth, cr.bottom - cr.top);
|
||||
custom->addToDesktop (0, hdlg);
|
||||
}
|
||||
appendCustomComponent();
|
||||
else
|
||||
{
|
||||
MessageManager::callAsync ([custom, cr, componentWidth, hdlg]() mutable
|
||||
{
|
||||
if (custom != nullptr)
|
||||
{
|
||||
custom->setBounds (cr.right, cr.top, componentWidth, cr.bottom - cr.top);
|
||||
custom->addToDesktop (0, hdlg);
|
||||
}
|
||||
});
|
||||
}
|
||||
MessageManager::callAsync (appendCustomComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -397,6 +403,11 @@ private:
|
||||
|
||||
getNativeDialogList().remove (hdlg);
|
||||
nativeDialogRef.set (nullptr);
|
||||
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
customComponent = nullptr;
|
||||
else
|
||||
MessageManager::callAsync ([this] { customComponent = nullptr; });
|
||||
}
|
||||
|
||||
void selectionChanged (HWND hdlg)
|
||||
@ -553,6 +564,17 @@ public:
|
||||
owner.finished (nativeFileChooser->results);
|
||||
}
|
||||
|
||||
bool canModalEventBeSentToComponent (const Component* targetComponent) override
|
||||
{
|
||||
if (targetComponent == nullptr)
|
||||
return false;
|
||||
|
||||
if (targetComponent == nativeFileChooser->getCustomComponent())
|
||||
return true;
|
||||
|
||||
return targetComponent->findParentComponentOfClass<FilePreviewComponent>() != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
FileChooser& owner;
|
||||
Win32NativeFileChooser::Ptr nativeFileChooser;
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user