Use Clipboard to Find Text in a File, Select Text and Copy It

I'm looking for something that will save years of my lifetime :grinning_face_with_smiling_eyes:

I want KM to refer to the last text copied to the clipboard, and search for this text in a specific text file. Then I want it to go to the text right underneath it (in the next line, which may or may not be in the beginning of the next line). Then select that text fully (selecting any character before and after blank spaces) and copy that text.

Hopefully a KM guru can help me here! Many thanks in advance.

That sounds like a Graphical User Interface event – do you have a particular editor in mind ?

Or do you simply want to repopulate the clipboard with the following line (if there is one, when and if a match is found) from the file ?

(in which case no editing app needs to be involved)

the text right underneath it

  • The string in the same column ?
  • At the same starting position defined as a character offset from the left ?
  • The length of the text copied defined by word boundaries ?

As always, you would need to show us an example.

Tip: How Do I Get The Best Answer in the Shortest Time? - Tips & Tutorials - Keyboard Maestro Discourse

Yes, I just want to repopulate the clipboard. The find and copy match event would take place in TextEdit.

I guess showing an example is indeed the best way to answer your questions. Note that even though you'll be looking at a graphic chord representation, this is actually a custom font -- thus text. So, I want KM to find the chord name (which I've copied from somewhere else), find the text (chord name) in another file and copy the text (visually a chord graph) underneath the chord name.

Screenshot 2021-12-08 at 12.20.58

Chord name means, for example A7M/9 ?

The chord graph is several lines of text, or a single custom character ?

The roman number to the left is part of the chord graph ?

You would need to show us a sample file – no matter that we don't have the font – it sounds as if this is RTF ?

(you could post a sample file here as a .zip)

Putting aside the solution you are suggesting {Editor, search, line below, etc} it sounds as if the problem that you want Keyboard Maestro to solve is a:

chord name -> chord graph

conversion ?

Copy a chord name as a chord graph for pasting somewhere ?

It's a single line of characters. if you open the rtf file attached, you will see a regular string of characters which the custom font represents as chords. E.g. II3bbbbbx3353

Yes it is.
Chord Library-1.zip (1.6 KB)

I'd say that's the ultimate goal in this task, as you describe! I copy the chord name, Keyboard Maestro finds the chord graph as represented in my chord library and copy it to the clipboard.

Can you tell us which font product you are using ?

It's possible that we might be able to see its technical spec and set up a lookup table inside a KM macro, so that you can directly copy a chord name and paste it as a chord graph.

(But that depends a bit on the details of the font and the chord namegraph id mappings)

If you simply apply that special font to one of those strings, is that enough for the string to be displayed as a chord graph ?

This is it https://www.manneschlaier.com

Yes, that's exactly how it works.

I'll take a look this evening. I think the trick might, ideally, to be to set up a completed (and checked) table of this kind for a KM "Execute a JavaScript" action to read and feed into a clipboard update, including font application

{
  "A13/b7/#9": "III3bbbbbx3353",
  "Am7": "5x555x",
  "A6/9": "III3xx45b5",
  "Adim": "5x454x",
  "A7": "II4x454x",
  "A9/C#": "x4x455",
  "Am6": "5x455x",
  "A6": "III3x243x",
  "A6/9/B": "x2x222",
  "A7/b13": "x0x021",
  "A7/13": "V1x123x",
  "A7/b9": "x0532x",
  "A7M": "V1x221x",
  "Am7/9": "Vx01100",
  "A7M/9": "VIx01100"
}

PS do you have an RTF file with a full listing of all the required names and corresponding graphs ?

(It might be possible to convert that automatically to the JSON format above)

That looks like a logical solution. However, I see the following disadvantage with this method: I will add chords to this library continuously. Your solution would mean I'd have to maintain two sources, if I understand you correctly.

If that's the way to address this task, then ok, but I would need to be able to convert them into the JSON format.

To give you a little more context, it is essential to me to be able to see the chords graphically, so to maintain a visual library of chords. I can infer the chord by the numbers as shown in your list, but that's much less intelligible. Also because one chord name have multiple representations on the guitar, sometimes with subtle differences in finger positioning, but they matter to me musically.

Of course chord names would then have to be unique to each graphical representation, but I'd go about it by creating sequences such as "Am7", "Am7(II)", etc.

I'm curious why you don't want to write a KM macro that goes to a website which has a search box for the chord name and then returns the answer? The KM macro could do this with only a few short KM actions:

  • Prompt the user for the name of the chord
  • Bring up the website page with the portal in a browser
  • Send the chord name (probably using Javascript) to a search box in the website

The result is displayed on your screen in your browser. You would see the chords graphically. Bingo.

It should, I think, be possible for a macro to automatically derive a JSON dictionary from the current state of the RTF library file (given its filepath).

(as long as the structure and layout of the library file is reasonably regular and predictable)

Do you have a full current copy of your library RTF file that you could either zip here or attach to direct message on this forum ?

(So that we could test the automation of that JSON update/conversion)

Multiple reasons:

  • I don’t want to continuously depend on a third-party
  • Chords are not standardized: there are different ways of representing chord names and chord graphs, and I want to stick with one standard that I fully control
  • I want to be able to customize how chords look like and are referred to. This is why I’ve chosen this font in the first place.

But thanks for your suggestion!

Okay. That's fine. Part of my philosophy is to suggest other solutions. Especially when the solutions are simpler. Occasionally people like my solutions.

1 Like

Whether the JSON dictionary is built on the fly as @ComplexPoint suggests, or you do it each time you change your chord library file, the format you use for your chord library file makes the conversion relatively easy. I do think the first step is to convert from RTF to plain text:

textutil -convert txt -output library.txt 'Chord Library.rtf'

I would use Python to make the JSON:

#!/usr/bin/python3

import os
import sys
import json

# Initialize
chordFile = os.path.expanduser('~/Desktop/chords/library.txt')
names = []
chords = []

# Process the chord file
with open(chordFile) as f:
	# Read in all the lines, delete title, strip leading and trailing whitespace,
	# and eliminate blank lines
	allLines = f.readlines()
	allLines = allLines[1:]
	allLines = [ x.strip() for x in allLines ]
	allLines = [ x for x in allLines if x != '' ]

	# Build up the names and chords lists
	for i in range(0, len(allLines), 2):
		newnames = allLines[i].split()
		newchords = allLines[i+1].split()
		if len(newnames) == len(newchords):
			names += newnames
			chords += newchords
		else:
			sys.stderr.write(f"Mismatch of names and chords on lines {i+1}-{i+2}\n")
			sys.stderr.write(f"{len(newnames)} names: {' '.join(newnames)}\n")
			sys.stderr.write(f"{len(newchords)} chords: {' '.join(newchords)}\n")

# Create a dictionary and print it out in JSON form
library = dict(zip(names, chords))
print(json.dumps(library, sort_keys=True, indent=2))

But lots of languages can do this. I'm sure Rob will post a JavaScript version soon. The guts of the script is reading in pairs of lines and treating the first as a list of chord names and the second as a list of chord fingerings. The rest—which takes up more lines than the fundamental logic—is error handling and dealing with extra whitespace.

2 Likes

And perhaps the macro could keep a checksum of the library file, only regenerating the JSON when the library file appears to have changed (i.e. when the current checksum differs from that last recorded)

An advantage of a JSON lookup is, of course, that the library file doesn't necessarily have to be open whenever you want to copy a chord name as a chord graph.

@drdrang is right that I would personally tend to do it all in JS, which gives us rather direct access to the macOS clipboard and AttributedString libraries. It means that given a plain text chord string, we can put it in the clipboard as RTF with your CHORD font applied, using functions like this:

Expand disclosure triangle to view JS Source
// copyTextInFont :: String -> Float -> String -> IO String
const copyTextInFont = fontName =>
    pointSize => txt => {
        const pb = $.NSPasteboard.generalPasteboard;
        return (
            pb.clearContents,
            // As RTF in specified font and size.
            pb.setDataForType(
                $.NSAttributedString.alloc.init
                .initWithStringAttributes(
                    txt, $({
                        'NSFont': $.NSFont.fontWithNameSize(
                            fontName,
                            pointSize
                        )
                    })
                ).RTFFromRangeDocumentAttributes({
                    'location': 0,
                    'length': txt.length
                }, {
                    DocumentType: $.NSRTFTextDocumentType
                }),
                $.NSPasteboardTypeRTF
            ),
            // Also a plain text version for text editor etc.
            pb.setStringForType(
                $(txt),
                $.NSPasteboardTypeString
            ),
            txt
        );
    };

I'm already quite impressed on where this is going. I will send you my current library via message, which might contain around 25% of all the chords I'll eventually add to it (guesstimate)

Note that, because I was doing this process manually so far, there are duplicate chord names for different fingerings.

Ok, so we might need to think of offering an interactive choice in such cases ?

(The JSON could contain a list of matches for each key, rather than a single match)

(Or perhaps your plan is to edit towards unique names ?)