private void setupInfoContextMenu(@Nonnull Info info,
	                                  @Nonnull AbstractContentPane<?> contentPane,
	                                  @Nonnull Dockable dockable) {
		dockable.withCachedContextMenu(true).withContextMenuFactory(d -> {
			ContextMenu menu = new ContextMenu();
			ObservableList<MenuItem> items = menu.getItems();

			if (info instanceof JvmClassInfo classInfo && contentPane instanceof JvmClassPane content) {
				Menu mode = menu("menu.mode", CarbonIcons.VIEW);
				mode.getItems().addAll(
						action("menu.mode.class.decompile", CarbonIcons.CODE,
								() -> content.setEditorType(JvmClassEditorType.DECOMPILE)),
						action("menu.mode.file.hex", CarbonIcons.NUMBER_0,
								() -> content.setEditorType(JvmClassEditorType.HEX))
				);
				items.add(mode);
			} else if (info instanceof AndroidClassInfo classInfo && contentPane instanceof AndroidClassPane content) {
				Menu mode = menu("menu.mode", CarbonIcons.VIEW);
				mode.getItems().addAll(
						action("menu.mode.class.decompile", CarbonIcons.CODE,
								() -> content.setEditorType(AndroidClassEditorType.DECOMPILE)),
						action("menu.mode.file.smali", CarbonIcons.NUMBER_0,
								() -> content.setEditorType(AndroidClassEditorType.SMALI))
				);
				items.add(mode);
			} else if (info instanceof ImageFileInfo fileInfo) {
				// TODO: We need to copy-paste this a number of times for all the different file info types
				//  and let each toggle between "automatic" (native editor) and hex. The way that it is done
				//  here works but isn't exactly pretty and lets users replace the current pane they have open
				//  with the same kind of pane. Having some abstraction model to alleviate the copy-paste and this
				//  replacement-of-self problem would be nice.
				Menu mode = menu("menu.mode", CarbonIcons.VIEW);
				mode.getItems().addAll(
						action("menu.mode.file.auto", CarbonIcons.IMAGE, () -> {
							ImageFilePane content = imagePaneProvider.get();
							if (d.getNode() instanceof AbstractContentPane<?> existing && existing.getPath() != null) {
								content.onUpdatePath(existing.getPath());
								existing.disable();
							}
							d.setNode(content);
						}),
						action("menu.mode.file.hex", CarbonIcons.CODE, () -> {
							HexFilePane content = hexPaneProvider.get();
							if (d.getNode() instanceof AbstractContentPane<?> existing && existing.getPath() != null) {
								content.onUpdatePath(existing.getPath());
								existing.disable();
							}
							d.setNode(content);
						})
				);
				items.add(mode);
			}

			addCopyPathAction(menu, info);
			addCloseActions(menu, d);
			return menu;
		});
	}
Looks for the {@link Navigable} component representing the path and returns it if found.
If no such component exists, it should be generated by the passed supplier, which then gets returned.
<br>
The dockable containing the {@link Navigable} component is selected when returned.

@param path
		Path to navigate to.
@param factory
		Factory to create a dockable for displaying content located at the given path,
		should a dockable for the content not already exist.
		<br>
		<b>NOTE:</b> It is required/assumed that the {@link Tab#getContent()} is a
		component implementing {@link Navigable}.

@return Navigable content representing content of the path.
recaf-ui/src/main/java/software/coley/recaf/services/navigation/Actions.java