How to associate file type with MacOS X App without launching it first?

61

Solution 1

When a user drags an application into the Applications folder, the system should register it with Launch Services automatically. See Application Registration in the Launch Services Guide.

Solution 2

Although Tony's information is correct -- also explained here -- (that dragging the MyApp.app to Applications will register with Launch Services automatically using information from Info.plist xml file and setup file associations), it doesn't fully answer the question about file association.

If people follow these instructions, they'll have their application open, but no specific file opened.

[...] make sure the system associates that particular file type with my newly installed app

File association is a bit different on Mac as when compared to other platforms. Most platforms, when *.foo is registered with myapp, when MyFile.foo is double-clicked, it dispatches something along the lines of:

/path/to/myapp MyFile.foo

And although you're free to use this technique on command line on a Mac with success, it simply won't work through Finder and also won't work through double-clicking the associated file on the desktop.

Asked and answered here:

https://stackoverflow.com/a/19702342/3196753

Some claim is this approach is advantageous over conservative document-open-file-handling in main() because it "only opens one instance of the application". Regardless of the reasoning behind this decision, it further complicates matters from a C++ perspective.

  • The 108 page PDF available here (warning, this is marked as "legacy", linkrot may occur).
  • Relevant documentation from apple's developer portal available here.
  • Qt's approach is documented here.

From qt.io:

When a user double clicks on a file in the Finder, Finder sends an Apple event to the application associated with the file and asks it to open the file. If the application is not running, it is launched and then the request is made. The advantage of this approach is that there is only one instance of the application running.

On Windows, this usually is done by launching the application and passing the name of the file as a command line argument. Looking at many files results in launching many instances of the same application. (The QtSingleApplication component, available as a Qt Solution, addresses this issue.)

Qt 3 does not provide an abstraction for handling Apple events, but it is straightforward to add support for them in your application using Mac's Carbon API. Let's assume we have an application called XpmViewer that displays XPM files (an X11 image format). The main window class declaration follows: [code snippet removed]

(BOOL)application:(NSApplication *)theApplication
       openFile:(NSString *)filename

Handling Mac OS X file open event BEFORE C++ main() executes AEInstallEventHandler handler not being called on startup

Solution 3

You can do that by typing apple-I on the file you want to associate the application with, use the little box called "Open with..." and select your app. You can check the little box below "Change All..." to make the change for all similar files.

Share:
61
E. Kaufman
Author by

E. Kaufman

Updated on June 04, 2022

Comments

  • E. Kaufman
    E. Kaufman almost 2 years

    I'm trying to display 100 strings with the given JTextField once I press the draw button, and then be able to rotate them all 90 degrees when I press the rotate button. However, when I rotate, some of the strings from earlier still appear. But then the whole panel clears once the strings are pointing downward. Why is this?

    import java.awt.Color;
    import java.awt.Component;
    import java.awt.FlowLayout;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.AffineTransform;
    import java.util.ArrayList;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.SwingConstants;
    public class RotateAndDraw extends JFrame implements ActionListener{
        // Declare all member variables
        private JFrame window;
        private JButton drawButton;
        private JButton rotateButton;
        JTextField userInput = new JTextField("Enter text here");
        private static JPanel p3 = new JPanel(); // Drawing surface
        private static Graphics g = p3.getGraphics(); // Graphics context
    
        private static int[] xArray = new int[100];
        private static int[] yArray = new int[100];
        private static int[] fontArray = new int[100];
        private static float[] colorArray = new float[100];
    
        private static int rotateCount = 0;
        private static int theta;
        public RotateAndDraw(){
            // Declare all components
            window = new JFrame("Rotate and Draw");
            drawButton = new JButton("Draw");
            rotateButton = new JButton("Rotate");
            drawButton.addActionListener(this);
            rotateButton.addActionListener(this);
            JPanel drawingSurface = new JPanel();
            JLabel userInputLabel = new JLabel("Text:         ");
    
            // Create some containers
            JPanel content = new JPanel();
            content.setLayout(new GridLayout(3, 1));
    
            JPanel p1 = new JPanel();
            p1.setLayout(new GridLayout(1, 2));
            p1.add(drawButton);
            p1.add(rotateButton);
    
            JPanel p2 = new JPanel();
            p2.setLayout(new GridLayout(1, 3));
            userInputLabel.setHorizontalAlignment(SwingConstants.RIGHT);
            p2.add(userInputLabel);
            p2.add(userInput);
            JLabel nullLabel = new JLabel("");
            p2.add(nullLabel);
    
            p3.setBackground(Color.BLACK);
    
            content.add(p1);
            content.add(p2);
            content.add(p3);
    
            window.add(content);
        }
    
        public void run(){
            window.setLocation(100, 100);
            window.setSize(10000, 10000);
            window.setDefaultCloseOperation(EXIT_ON_CLOSE);
            window.setVisible(true);
        }
    
        private void paintComponent(Graphics g){
            // TODO Auto-generated method stub
            super.paintComponents(g);
    
       }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            // TODO Auto-generated method stub
            super.paintComponents(g);
            Graphics2D g2 = ( Graphics2D )g;
    
            String message = userInput.getText();
    
            Font font1, font2, font3, font4, font5;  // The five fonts.
            font1 = new Font("Serif", Font.BOLD, 14);
            font2 = new Font("SansSerif", Font.BOLD + Font.ITALIC, 24);
            font3 = new Font("Monospaced", Font.PLAIN, 30);
            font4 = new Font("Dialog", Font.PLAIN, 36);
            font5 = new Font("Serif", Font.ITALIC, 48);
    
            if(e.getSource() == drawButton){
                g = p3.getGraphics();
                g.setColor(Color.BLACK);
                g.fillRect(0, 0, p3.getWidth(), p3.getHeight());
    
                rotateCount = 0;
    
                int width = p3.getWidth();
                int height = p3.getHeight();
    
                for (int i = 0; i < 100; i++) {
                    int fontNum = (int)(5*Math.random()) + 1;
                    fontArray[i] = fontNum;
                    switch (fontNum) {
                    case 1:
                        g.setFont(font1);
                        break;
                    case 2:
                        g.setFont(font2);
                        break;
                    case 3:
                        g.setFont(font3);
                        break;
                    case 4:
                        g.setFont(font4);
                        break;
                    case 5:
                        g.setFont(font5);
                        break;
                    } // end switch
    
    
                    float hue = (float)Math.random();
                    colorArray[i] = hue;
                    g.setColor( Color.getHSBColor(hue, 1.0F, 1.0F) );
    
    
                    int x,y;
                    x = (int)(Math.random()*(width));
                    y = (int)(Math.random()*(height));
                    xArray[i] = x;
                    yArray[i] = y;
    
                    // Draw the message.
                    g.drawString(message,x,y);
                } // end for
            }// end if
            if(e.getSource() == rotateButton){
                super.paintComponents(g2);
                g2.setColor(Color.BLACK);
                g2.fillRect(0, 0, p3.getWidth(), p3.getHeight());
    
                rotateCount++;
                if(rotateCount == 4){
                    rotateCount = 0;
                }
    
                switch(rotateCount){
                case 0:
                    theta = 0;
                    break;
                case 1:
                    theta = 90;
                    break;
                case 2:
                    theta = 180;
                    break;
                case 3:
                    theta = 270;
                    break;
                }
    
                for(int i = 0; i < 100; i++){
                    switch(fontArray[i]){
                    case 1:
                        g2.setFont(font1);
                        break;
                    case 2:
                        g2.setFont(font2);
                        break;
                    case 3:
                        g2.setFont(font3);
                        break;
                    case 4:
                        g2.setFont(font4);
                        break;
                    case 5:
                        g2.setFont(font5);
                        break;
                    } // end switch 
    
                g2.setColor( Color.getHSBColor(colorArray[i], 1.0F, 1.0F) );
    
                AffineTransform at = new AffineTransform(); 
                at.rotate(Math.toRadians(theta), xArray[i], yArray[i]);
                g2.setTransform(at); 
                g2.drawString(message, xArray[i], yArray[i]);
                g2.transform(new AffineTransform());
                }// end for
            }
        }
    }
    
  • Keltia
    Keltia over 15 years
    Uh, the system does not do that, the application does. How the system is supposed to know which extensions are handled by the application?
  • Carl Seleborg
    Carl Seleborg over 15 years
    @Tony: Thanks Tony, I suspected that was the behavior. My experimentation leads me to believe that it does not always work as expected, but it could simply be me doing something wrong. I'll investigate further based on the documentation you point at.
  • Tony
    Tony over 15 years
    @Carl: Sometimes the database gets a little confused. I've noticed this particularly after doing many rebuilds of a given application, it can stop registering it correctly. Do a search for "lsregister" for info on forcing a rebuild.
  • Carl Seleborg
    Carl Seleborg over 15 years
    What I really want is for this to happen during the installation: it's my application that's being installed on the user's computer. Sorry for not being clear on that one.
  • Keltia
    Keltia over 15 years
    Then look the URL Tony posted, it has the answer.
  • tresf
    tresf about 8 years
    Downvote because the question asks how to achieve this without launching it first?
  • Keltia
    Keltia almost 8 years
    Uh, I didn't say "launch it", just use the fact that in the Info box, you can select which app to run, that's all.
  • tresf
    tresf about 7 years
    For those looking for a way to do this specifically with Qt, here's a sample pull request with a mechanistic for doing it, handling both "already running" and "still starting" use-cases. github.com/LMMS/lmms/pull/3219/files