Launch app only if not already open

Update April 7: A different version added and found Albert, see update and Bonus bellow !!!

Concerning dash functionality: You have asked " Is there anyway to change the default behavior of the launcher to check for this before opening a new window". Basic answer is, no, as a regular user you have no way of adding that behavior to dash. However, if there would be a unity scope developer who'd be willing to implement that, you might approach them or develop one yourself if you have resolve and willing to learn. My coding skills are very modest, hence I use shell scripting and the available graphical front-end for the scripts as a workaround.

Related information

Original post:

I have written a script that uses zenity dialogue and wmctrl to achieve what you asked for. Notice that this is a graphical script, meaning it will only work with windows, in GUI, and won't work if you try to launch something in tty. Besides, from what I understand Alfred does exactly the same thing. You can create a desktop shortcut to it or launcher shortcut to it, as described here and here.

The script:

#!/bin/bash
# Author: Serg Kolo
# Description: A launcher script that checks whether
#       or not a window of a particular program already exists
#       If a window of such program is open, bring it to focus
#       Otherwise - launch a new window
#       Written for https://askubuntu.com/q/440142/295286
# Date: April 6 , 2015
#


MYPROG=$( zenity --entry --title='MY LAUNCHER' --text='Type the name of application to run' )
sleep 0.5
wmctrl -lx | awk '{print $3}' | grep -i "$MYPROG"

if [ $? -eq 0 ]; then
    sleep 1         
    wmctrl -xa $MYPROG
   #as an alternative try the line bellow
   #wmctrl -a $MYPROG
    exit 1
else 
    $MYPROG &
    exit 0
fi

Side notes: in the previous version , script used echo $?, to test if previous expressions exited successfully. As per muru's suggestion ( from the edit ), I changed the code to somewhat more compact version, so i suggest you take a look at the previous version and the current.

Also , previously wmctrl -a $MYPROG didn't work with testing google-chrome or chromium-browser; for some stupid reason some programs have WM_CLASS property of the window capitalized , while the program as listed by dpkg --get-selections is lowercase (just read man wmctrl and run wmctrl -lx, you'll know). Adding that -ax should take care of this. The script brings up the already open chromium window as it should

Another thing - wmctlr is somewhat weird in that it needs a delay sometimes (had experience with it in another script), so i had to add sleep 1 line. Previously it would be kind of on and off with firefox, but now works swimmingly.

The script in action

In the animation bellow you can see that on the first run of the script, there is one instance of firefox open, and the script switches focus to that window; on the second test, I open new instance of google-chrome, which hasn't been open previously. (Side note: If you are currious about the desktop, by the way, that is openbox with cairo dock)

Per suggestion in the comments, embedded animation removed, posted link only. Report if it is broken please ! http://i.stack.imgur.com/puuPZ.gif

Update, April 7

I improved the script somewhat to make all the programs listed in zenity's drop-down entry box. Now the user doesn't have to memorize each program, but can just scroll through a list of them using arrow keys or just open the drop down menu. Also, this improved version raises windows not by name, but by window id, which gives much better performance. Note, the way i go through .desktop files is kind of redundant, using cut command twice, but since my script-fu isn't that good so far, this is all i can do. Suggestions for improvement are welcome!

#!/bin/bash
# Author: Serg Kolo
# Description: Second version of a launcher script that checks whether
#       or not a window of a particular program already exists
#       If a window of such program is open, bring it to focus
#       Otherwise - launch a new window
#       Written for https://askubuntu.com/q/440142/295286
# Date: April 7 , 2015
#

set -x

MYPROG=$(zenity --entry --text 'Select program from list' --entry-text $(ls /usr/share/applications/*.desktop | cut -d'/' -f5 | cut -d'.' -f1 | xargs echo))
sleep 0.5
# Do we have a window of such program ?
wmctrl -lx| awk '{print $3}'  | grep -i $MYPROG

if [ $? -eq 0 ]; then
    sleep 0.5 # if yes, find that window id, and raise it
    WINID=$(wmctrl -lx | grep -i $MYPROG | awk 'NR==1{print $1}')
    wmctrl -ia $WINID &
 #  exit 0  
else
    echo $MYPROG | grep -i libreoffice
    if [ $? -eq 0  ]
    then
        MYPROG=$(echo $MYPROG | sed 's/-/ --/g')
    fi
    $MYPROG &

#  exit 0 
fi

enter image description here

Bonus:

I have actually found Albert , which is Linux version of Alfred, but have not tried it myself. Worth checking out though. However, as Jacob noted already, it is still buggy.

There is an app called Gnome-Do, which graphically looks similar to Alfred, however it does not have the same functionality as this script.

enter image description here

Let me know if you like this script, if there's anything needs fixing, and don't forget to upvote the answer if you find it useful


1. Dash the Second

Below a script that can be used as an alternative to Dash, when it comes to running applications as described in your question.

It exists of a window with the same functionality as Dash; if type one or more characters of the application, the application will appear in the list. Press Enter to either start or raise the application, depending on if it is already running or not.

You can call it from a shortcut key combination, or set an icon in the launcher to use it similarly to Dash (see further below), or both.

enter image description here

The script

#!/usr/bin/env python3
import subprocess
import os
import getpass
import time

user = getpass.getuser()
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
skip = ["%F", "%U", "%f", "%u"]; trim = ["chrome", "chromium", "nautilus"]

def apply(command):
    if "libreoffice" in command:
        proc = [l.split()[0] for l in get("ps -u "+user).splitlines() if "soffice.bin" in l]
        module = command.split("--")[-1]
        time.sleep(0.1)
        try:
            ws = sum([[w.split()[0] for w in get("wmctrl -lp").splitlines() if process in w and module in w.lower()] for process in proc], [])[0]
            subprocess.call(["wmctrl", "-ia", ws])
        except IndexError:
            subprocess.Popen(["/bin/bash", "-c", command+"&"])
    else:
        check = command.split("/")[-1][:14]
        proc = [p.split()[0] for p in get("ps -u "+user).splitlines() if check in p]
        time.sleep(0.5)
        try:
            ws = sum([[w.split()[0] for w in get("wmctrl -lp").splitlines() if process in w] for process in proc], [])
            if command == "nautilus":
                real_window = [w for w in ws if "_NET_WM_WINDOW_TYPE_NORMAL" in get("xprop -id "+w)][0]
            else:
                real_window = ws[0]
            subprocess.call(["wmctrl", "-ia", real_window])
        except IndexError:
            subprocess.Popen(["/bin/bash", "-c", command+"&"])
# default directories of .desktop files; globally, locally, LibreOffice- specific when separately installed
globally = "/usr/share/applications"; locally = os.environ["HOME"]+"/.local/share/applications"; lo_dir = "/opt/libreoffice4.4/share/xdg"
# create list of .desktop files; local ones have preference
local_files = [it for it in os.listdir(locally) if it.endswith(".desktop")]
global_files = [it for it in os.listdir(globally) if it.endswith(".desktop")]
lo_spec = [it for it in os.listdir(lo_dir) if it.endswith(".desktop")] if os.path.exists(lo_dir) else []
for f in [f for f in local_files if f in global_files]:
    global_files.remove(f)
for f in [f for f in local_files if f in lo_spec]:
    lo_spec.remove(f)
dtfiles = [globally+"/"+f for f in global_files]+[locally+"/"+f for f in local_files]+[lo_dir+"/"+f for f in lo_spec]
# create list of application names / commands
valid = []
for f in dtfiles:
    content = open(f).read()
    if all(["NoDisplay=true" not in content,"Exec=" in content]):
        lines = content.splitlines()
        name = [l.replace("Name=", "") for l in lines if "Name=" in l][0]
        command = [l.replace("Exec=", "") for l in lines if all(["Exec=" in l, not "TryExec=" in l])][0]
        valid.append((name, command))
valid.sort(key=lambda x: x[0])
# create zenity list + window
list_items = '"'+'" "'.join([f[0] for f in valid])+'"'
proposed = 'zenity --list --text "Type one or more characters... " --column="Application List" '+\
           '--title="Dash the Second" --height 450 --width 300 '+list_items
try:
    choice = subprocess.check_output(["/bin/bash", "-c", proposed]).decode("utf-8").strip().split("|")[0]
    command = [r[1] for r in valid if r[0] == choice][0]
    # command fixes:
    for s in skip:
        command = command.replace(" "+s, "")
    for t in trim:
        if t in command:
            command = t
    apply(command)
except subprocess.CalledProcessError:
    pass

How to use

The script needs wmctrl installed:

sudo apt-get install wmctrl

Then:

  1. Paste the script above into an empty file, save it as dash_alternative.py
  2. Add it to a shortcut key combination: Choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:

    python3 /path/to/dash_alternative.py
    

Explanation

When the script is run, It lists all applications, represented in /usr/share/applications. It searches the .dektop files, creating a list of all application names (from the first "Name=" line) and the command to run the application (from the first "Exec=" line).

Subsequently, a Zenity list is created, presenting all applications in a sorted manner.

Whenever an application is selected, the script looks in the list of running processes if the application is running. If so, the corresponding window is raised. If not, a new instance is opened.

Notes

  1. To run the script on 12.04 (since the original question was tagged 12.04 simply change the shebang to #!/usr/bin/env python and run it by the command

    python /path/to/dash_alternative.py
    
  2. As far as I tested it, the script works fine. Commands and their (not-) corresponding process names (e.g. LibreOffice <> soffice.bin), different window types (nautilus has several different window types, besides "real" windows) , multiple pids per application (Chromium, Google-chrome) can cause exceptions, that I fixed in the examples above. If anyone runs into an issue, please mention it.

2. Additional: setting it as an alternative to the "real" Dash for running applications

  1. Copy and safe the script as mentioned above
  2. Save the icon below (right-click > safe as) as dash_alternative.png

    enter image description here

  3. Copy the code below into an empty file, save it in ~/.local/share/applications as dash_thesecond.desktop. Set the correct paths for /path/to/dash_alternative.py (the script) and /path/to/dash_alternative.png (the icon)

    [Desktop Entry]
    Name=Dash the Second
    Exec=python3 /path/to/dash_alternative.py
    Icon=/path/to/dash_alternative.png
    Type=Application
    Hidden=false
    
  4. Drag the .desktop file on to the launcher: