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