7

I have always been confused why the file manager in Linux cannot stop applications from opening a single file twice at the same time?

Specifically, I want to stop the PDF file reader Okular from opening the file A.pdf again when I have already opened it. I need to get an warning or just show me the opened copy of the file A.pdf.

More generally, I would like this to happen with any application, not just Okular. I want to make the document management behavior in Linux the same as in Windows.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
lovelyzlf
  • 113
  • 1
  • 6
  • 4
    I believe this behavior is application-specific. Each application is coded differently. Some will check for existing opened instances while others behave as independent standalone processes. As a result, your current question is too general to be answered. You should either ask if there is a way to make okular do as you describe, or if there exists a PDF reader for Linux that behaves as you describe. – jw013 Nov 20 '13 at 04:42
  • @jw013 True. `evince` behaves this way: it would re-focus the window of an already-open document if you try to open a new instance of it. – Joseph R. Nov 20 '13 at 05:24
  • Thank you @jw013 I think I've some kinda understood.So I just install the Adobe reader.So it depends on the specific application. – lovelyzlf Nov 20 '13 at 06:27
  • I would think it possible to write a launcher, than would work with many apps, that can do it. – ctrl-alt-delor May 20 '22 at 20:14

2 Answers2

6

A file manager is responsible for invoking applications to open a file. It has no control over what the application does with the file, and in particular whether the application will open a new window if you open the same file twice.

Having the same file open in multiple windows can be useful, for example when you want to see different sections from the same document. So systematically refusing to open more than one window on the same document would be bad. Which behavior is the default is mostly a matter of taste. Some applications default to opening a new window, others default to focusing the existing window.

Okular defaults to opening a new window. If you start all instances with okular --unique, then the second time you run that command, it doesn't open a new window (though it doesn't focus the existing window, at least if you aren't running KDE). Note that the first instance must have been started with --unique as well as the second one.

Evince, the Gnome PDF viewer, defaults to the behavior you want: if you open the same document a second time, it focuses the existing window. It doesn't have a command line option to open a separate window, you have to do this through the GUI (menu “File” → “Open a Copy” or Ctrl+N).

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • The okular --unique command doesn't work any more, as of Okular version 1.9.3, running in Mint with the XFCE desktop environment. I tried it both through the gui (going into "open with other application" and selecting to use a custom command), and via command line (two different terminal tabs, ran the okular command with that argument in both, and the second one just switched focus to the window the first opened, rather than creating a new window) – Matthew Najmon Aug 30 '21 at 17:58
1

I wrote the following script to accomplish the desired behavior for the flatpak version of Okular (I’m using LMDE 5). The script is based on changing the window title via wmctrl.

Getting the desired behavior was tricky because of the new welcome screen, but I think I made it (I’m not an expert programmer whatsoever, so I appreciate any suggestions to improve the code.)

Before running the script, make sure to uncheck the option “Display document title in titlebar if available”, and to set the option “When not displaying document title" to “Display full file path” (the script will change it, but it requires the full path to make each singular file identifiable). Also, uncheck the option “Open new files in tabs.”

#!/usr/bin/env bash
# Set expectedwindowtitle whether there is an argument or not, to handle the
# welcome screen: 
if [ -z "$1" ]
then
    expectedwindowtitle="Welcome"
else
    pdfbasename=$(basename "$1")
    pdffullpath=$(dirname "$(realpath "$1")")
    pdfpath=${pdffullpath/#$HOME/\~}
    expectedwindowtitle="$pdfbasename ($pdfpath)"
fi

# Search for a window with the substring "— Okular" in the title (with em-dash, 
# U+2014, that is, untouched), and get its ID and filename:
originaltitle=`wmctrl -l | grep -v "okularjump" | grep -v "grep" | grep -m 1 "— Okular" | awk 'BEGIN{RS="  — Okular"; FS="'$HOSTNAME' "}NF>1{print $NF}'`
originalwindowID=`wmctrl -l | grep -v "okularjump" | grep -v "grep" | grep -m 1 "— Okular" | awk '{ print $1; }'`

# If there is an existent window with the substring "— Okular" (with em-dash) 
# and a filename in the title, then rename it to a new format, as follows: 
# [basemane (short path) – Okular
# Note that the separator in the new format is an en-dash (U+2013) 
if [ ! -z "$originaltitle" ]
then
    originalfullpath=$(dirname "$originaltitle")
    originalbasename=$(basename "$originaltitle")
    originalpath=${originalfullpath/#$HOME/\~}
    newwindowname="$originalbasename ($originalpath) – Okular" # With en-dash 
    wmctrl -r $originaltitle -N "$newwindowname"  
fi

# Search for an already modified window title that matches the expected window 
# title and extract the basename and the path
existentwindowtitle=`wmctrl -l | grep -v "okularjump" | grep -v "grep" | grep "– Okular" | grep -m 1 "$expectedwindowtitle" | awk 'BEGIN{RS=" – Okular"; FS="'$HOSTNAME' "}NF>1{print $NF}'` # With en-dashes

# If expected and existent windows are the same, then switch to the existent 
# window
if [[ "$expectedwindowtitle" == "$existentwindowtitle" ]]
then
    wmctrl -a "$existentwindowtitle"
else
    # There is an argument and nothing to switch to. Then, open the argument with 
    # the new format in the title
    if [ ! -z "$1" ]
    then
        /usr/bin/flatpak run --branch=stable --arch=x86_64 --command=okular --file-forwarding org.kde.okular @@ "$1" @@ &>/dev/null &

        while ! test -n "$argoriginalwindowID"; do
            sleep 0.01
            argoriginalwindowID=`wmctrl -l | grep -v "okularjump" | grep -v "grep" | grep "— Okular" | grep -i -m 1 "$(basename "$1")" | awk '{ print $1; }'`
        done

        pdffullpath=$(dirname "$(realpath "$1")")
        pdfpath=${pdffullpath/#$HOME/\~}
        argWname="$(basename "$1") ($pdfpath) – Okular"
        wmctrl -ir $argoriginalwindowID -N "$argWname"

    else
        # There is no argument and nothing to switch to. Open a welcome screen 
        # with the word Welcome in the title (to make possible to find it later)
        /usr/bin/flatpak run --branch=stable --arch=x86_64 --command=okular --file-forwarding org.kde.okular --title "Welcome" &>/dev/null &
        while ! test -n "$welcomeWname"; do
            sleep 0.01
            welcomeWname=`wmctrl -l | grep -v "okularjump" | grep -v "grep" | grep -m 1 "— Okular" | grep -m 1 "Welcome"`
        done
        originalwindowID=`wmctrl -l | grep -v "okularjump" | grep -v "grep" | grep -m 1 "— Okular" | grep -m 1 "Welcome" | awk '{ print $1; }'`
        newwindowname="Welcome – Okular"
        wmctrl -ir $originalwindowID -N "$newwindowname"
    fi
fi

To use the script as default all around the system, I saved the code as ~/okularjump, made it executable and copied it to /usr/bin. Then, I copied the default .desktop file to the user folder twice: one with the purpose of running the script and the other to hide the default flatpak launcher:

chmod +x okularjump
sudo cp okularjump /usr/bin/
cp /var/lib/flatpak/app/org.kde.okular/current/active/export/share/applications/org.kde.okular.desktop ~/.local/share/applications/
cp /var/lib/flatpak/app/org.kde.okular/current/active/export/share/applications/org.kde.okular.desktop ~/.local/share/applications/okular.desktop
echo "Hidden=true" >> ~/.local/share/applications/org.kde.okular.desktop

Then, I changed the Exec line in ~/.local/share/applications/okular.desktop to Exec=okularjump %U

Finally, I set Okular as the default PDF manager in the system settings.

Regarding opening multiple instances of the same file, it is still possible to do it by opening the file from Okular via the open dialog. The script will adjust the window title the next time it runs.

I took inspiration from Jumpapp https://github.com/mkropat/jumpapp (that should work for the non-Flatpak version, but I’m not sure) and Flatjump https://github.com/ceranco/flatjump (which didn’t work for me, but I invite to try).

Goghard
  • 11
  • 2
  • (1) It’s almost never necessary or useful to say `$(echo something)`. Why not just say `expectedwindowtitle="Welcome"` and `expectedwindowtitle="$pdfbasename ($pdfpath)"`?  (2) Please indent your code properly. – G-Man Says 'Reinstate Monica' May 20 '22 at 18:55
  • Thank you @G-Man! You’re totally right, the code works well without `$(echo something)`. Also, I did my best to fix the indentation. I edited my post with your corrections. – Goghard May 23 '22 at 17:09