Change folder colour when added to

As a comparative newcomer I am only now trying to use KM properly.
I would like to have a folder change its label colour when an item is added, or when the folder is not empty. Is this possible?
I have found the ‘folder adds an item’ trigger but cannot see a way to change the colour. Many thanks for your help.

You can tag a folder (using the Set File Attribute action). So you could tag it “Red” and it will show with a Red dot. But the folder wont turn red.

To do that you would have to create a custom icon and set the folder’s icon that way.

There is a possible solution for doing that here: https://stackoverflow.com/questions/20172959/change-icon-of-folder-with-applescript

And there are answers here: https://apple.stackexchange.com/questions/6901/how-can-i-change-a-file-or-folder-icon-using-the-terminal

And I strongly suspect this python solution:

#!/usr/bin/env python
import Cocoa
import sys

Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(Cocoa.NSImage.alloc().initWithContentsOfFile_(sys.argv[1].decode('utf-8')), sys.argv[2].decode('utf-8'), 0) or sys.exit("Unable to set file icon")

could be converted to JXA fairly directly to give an easy solution.

Thank you, for the moment the tag will do fine. Cheers.

JXA may very well be possible, but straight-up AppleScriptObjC has been done already.

Feed the script the path to an image and a target path, and et voilà!

-Chris

------------------------------------------------------------------------------
# Auth: Christopher Stone { Heavy Lifting by Shane Stanley }
# dCre: 2017/11/14 04:38
# dMod: 2017/11/14 04:48
# Appl: AppleScriptObjC
# Task: Change the icon of a given item to an image.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @ASObjC, @Change, @Icon, @Image
------------------------------------------------------------------------------
use framework "Foundation"
use framework "AppKit"
use scripting additions
------------------------------------------------------------------------------

set sourceImagePath to "/POSIX Path/to/your/image"
set targetPath to "/POSIX Path/to/your/target/item"

set theImage to (current application's NSImage's alloc()'s initWithContentsOfFile:sourceImagePath)
(current application's NSWorkspace's sharedWorkspace()'s setIcon:theImage forFile:targetPath options:0)

------------------------------------------------------------------------------
2 Likes

and wrapping as a function for re-use and composition (and allowing for ~ expansion and file-not-found return vallues), we could write in AppleScript, and in JavaScript for Automation:

Applescript version

use framework "Foundation"
use framework "AppKit"
use scripting additions

-- fileIconSetMay:: FilePath -> FilePath -> Maybe FilePath
on fileIconSetMay(pathImage, pathTarget)
    set ca to current application
    if doesFileExist(pathImage) then
        if doesFileExist(pathTarget) then
            set img to (ca's NSImage's alloc()'s initWithContentsOfFile:pathImage)
            {nothing:not (ca's NSWorkspace's sharedWorkspace()'s ¬
                setIcon:img forFile:pathTarget options:0), just:pathTarget, msg:"setIconForFile error"}
        else
            {nothing:true, msg:"Target file not found at: " & pathTarget}
        end if
    else
        {nothing:true, msg:"Image not found at: " & pathImage}
    end if
end fileIconSetMay


-- TEST ----------------------------------------------------------------------
on run
    -- Any ~ expanded to full user Home path
    set pathImage to filePath("~/Desktop/redArrow.png")
    set pathTarget to filePath("~/Desktop/report.taskpaper")
    
    set maybeIconSet to fileIconSetMay(pathImage, pathTarget)
    
    if nothing of maybeIconSet then
        msg of maybeIconSet
    else
        "Icon (" & pathImage & ") set for " & pathTarget
    end if
end run


-- GENERIC FUNCTIONS ---------------------------------------------------------

-- doesFileExist :: String -> Bool
on doesFileExist(strPath)
    set ca to current application
    set oPath to (ca's NSString's stringWithString:strPath)'s ¬
        stringByStandardizingPath
    set {bln, int} to (ca's NSFileManager's defaultManager()'s ¬
        fileExistsAtPath:oPath isDirectory:(reference))
    bln and (int ≠ 1)
end doesFileExist

-- filePath :: String -> FilePath
on filePath(s)
    unwrap(stringByStandardizingPath of wrap(s))
end filePath

-- unwrap :: NSObject -> a
on unwrap(objCValue)
    if objCValue is missing value then
        return missing value
    else
        set ca to current application
        item 1 of ((ca's NSArray's arrayWithObject:objCValue) as list)
    end if
end unwrap

-- wrap :: a -> NSObject
on wrap(v)
    set ca to current application
    ca's (NSArray's arrayWithObject:v)'s objectAtIndex:0
end wrap

JavaScript for Automation version

(() => {

    ObjC.import('AppKit');

    // SPECIFIED FILE ICON ---------------------------------------------------

    // fileIconSetMay:: FilePath -> FilePath -> Maybe FilePath
    const fileIconSetMay = (pathImage, pathTarget) =>
        doesFileExist(pathImage) ? (
            doesFileExist(pathTarget) ? {
                nothing: !$.NSWorkspace.sharedWorkspace.setIconForFileOptions(
                    $.NSImage.alloc.initWithContentsOfFile(pathImage),
                    pathTarget, 0
                ),
                just: pathTarget,
                msg: "setIconForFile error ?"
            } : {
                nothing: true,
                msg: 'Target file not found at: ' + pathTarget
            }
        ) : {
            nothing: true,
            msg: 'Image not found at: ' + pathImage
        };

    // GENERIC FUNCTIONS -----------------------------------------------------

    // filePath :: String -> FilePath
    const filePath = s =>
        ObjC.unwrap(ObjC.wrap(s)
            .stringByStandardizingPath);

    // doesFileExist :: String -> IO Bool
    const doesFileExist = strPath => {
        const ref = Ref();
        return $.NSFileManager.defaultManager
            .fileExistsAtPathIsDirectory(
                ObjC.wrap(strPath)
                .stringByStandardizingPath, ref
            ) && ref[0] !== 1;
    };

    // TEST ------------------------------------------------------------------
    const
        // Any ~ expanded to user Home path
        pathImage = filePath('~/Desktop/redArrow.png'),
        pathTarget = filePath('~/Desktop/report.taskpaper');

    const maybeIconSet = fileIconSetMay(pathImage, pathTarget);
    return maybeIconSet.nothing ? (
        maybeIconSet.msg
    ) : 'Icon (' + pathImage + ') set for ' + pathTarget

})();
1 Like

So for a one-off shell script or Execute JavaScript for Automation action, something like:

Bash

#!/bin/bash

osascript -l JavaScript <<JXA_END 2>/dev/null
(() => {
    ObjC.import('AppKit');
    const
        pathImage = '~/Desktop/redArrow.png',
        pathTarget = '~/Desktop/report.taskpaper';
    return $.NSWorkspace.sharedWorkspace.setIconForFileOptions(
        $.NSImage.alloc.initWithContentsOfFile(
            \$(pathImage)
                .stringByStandardizingPath.js
        ),
        \$(pathTarget)
            .stringByStandardizingPath.js, 0
    );
})();
JXA_END

Execute Javascript for Automation Action

(() => {
    ObjC.import('AppKit');
    const
        pathImage = '~/Desktop/redArrow.png',
        pathTarget = '~/Desktop/report.taskpaper';
    return $.NSWorkspace.sharedWorkspace.setIconForFileOptions(
        $.NSImage.alloc.initWithContentsOfFile(
            $(pathImage)
                .stringByStandardizingPath.js
        ),
        $(pathTarget)
            .stringByStandardizingPath.js, 0
    );
})();

Thanks for this one! Very nice.

1 Like