"""Handles /live/view/* OSC addresses (UI navigation).""" import logging from typing import Optional from .handler import AbletonOSCHandler logger = logging.getLogger(__name__) class ViewHandler(AbletonOSCHandler): def init_api(self) -> None: self.clear_listeners() view = self._song_view() # selected scene self._add("/live/view/get/selected_scene", self._get_selected_scene) self._add("/live/view/set/selected_scene", self._set_selected_scene) self._add("/live/view/start_listen/selected_scene", self._start_listen_selected_scene) self._add("/live/view/stop_listen/selected_scene", self._stop_listen_selected_scene) # selected track self._add("/live/view/get/selected_track", self._get_selected_track) self._add("/live/view/set/selected_track", self._set_selected_track) self._add("/live/view/start_listen/selected_track", self._start_listen_selected_track) self._add("/live/view/stop_listen/selected_track", self._stop_listen_selected_track) # selected clip self._add("/live/view/get/selected_clip", self._get_selected_clip) self._add("/live/view/set/selected_clip", self._set_selected_clip) # selected device self._add("/live/view/get/selected_device", self._get_selected_device) # focus self._add("/live/view/show_clip_detail_view", self._show_clip_detail_view) self._add("/live/view/show_device_detail_view", self._show_device_detail_view) self._add("/live/view/focus_browser", self._focus_browser) # arrangement / session toggle self._add("/live/view/get/focused_document_view", self._get_focused_document_view) # ------------------------------------------------------------------ def _song_view(self): try: return self.song.view except Exception: return None def _get_selected_scene(self, params: tuple) -> Optional[tuple]: try: view = self._song_view() scenes = list(self.song.scenes) scene = view.selected_scene idx = scenes.index(scene) return (idx,) except Exception as e: logger.warning("get selected_scene: %s", e) return None def _set_selected_scene(self, params: tuple) -> None: if not params: return None try: idx = int(params[0]) scenes = list(self.song.scenes) if 0 <= idx < len(scenes): self._song_view().selected_scene = scenes[idx] except Exception as e: logger.warning("set selected_scene: %s", e) return None def _start_listen_selected_scene(self, params: tuple) -> None: view = self._song_view() if view is None: return key = "view.selected_scene" self._remove_listener(key) def cb(): result = self._get_selected_scene(()) if result: self._send("/live/view/get/selected_scene", result) try: view.add_selected_scene_listener(cb) self._listener_store[key] = (view.remove_selected_scene_listener, cb) cb() except Exception as e: logger.warning("selected_scene listener: %s", e) def _stop_listen_selected_scene(self, params: tuple) -> None: self._remove_listener("view.selected_scene") def _get_selected_track(self, params: tuple) -> Optional[tuple]: try: view = self._song_view() all_tracks = list(self.song.tracks) + list(self.song.return_tracks) track = view.selected_track idx = all_tracks.index(track) return (idx,) except Exception as e: logger.warning("get selected_track: %s", e) return None def _set_selected_track(self, params: tuple) -> None: if not params: return None try: idx = int(params[0]) all_tracks = list(self.song.tracks) + list(self.song.return_tracks) if 0 <= idx < len(all_tracks): self._song_view().selected_track = all_tracks[idx] except Exception as e: logger.warning("set selected_track: %s", e) return None def _start_listen_selected_track(self, params: tuple) -> None: view = self._song_view() if view is None: return key = "view.selected_track" self._remove_listener(key) def cb(): result = self._get_selected_track(()) if result: self._send("/live/view/get/selected_track", result) try: view.add_selected_track_listener(cb) self._listener_store[key] = (view.remove_selected_track_listener, cb) cb() except Exception as e: logger.warning("selected_track listener: %s", e) def _stop_listen_selected_track(self, params: tuple) -> None: self._remove_listener("view.selected_track") def _get_selected_clip(self, params: tuple) -> Optional[tuple]: try: view = self._song_view() clip_view = view.detail_clip if clip_view is None: return None tracks = list(self.song.tracks) for ti, track in enumerate(tracks): for ci, slot in enumerate(track.clip_slots): if slot.has_clip and slot.clip == clip_view: return (ti, ci) except Exception as e: logger.warning("get selected_clip: %s", e) return None def _set_selected_clip(self, params: tuple) -> None: if len(params) < 2: return None try: track_idx = int(params[0]) clip_idx = int(params[1]) tracks = list(self.song.tracks) if 0 <= track_idx < len(tracks): slots = list(tracks[track_idx].clip_slots) if 0 <= clip_idx < len(slots) and slots[clip_idx].has_clip: self._song_view().detail_clip = slots[clip_idx].clip except Exception as e: logger.warning("set selected_clip: %s", e) return None def _get_selected_device(self, params: tuple) -> Optional[tuple]: try: view = self._song_view() track = view.selected_track tracks = list(self.song.tracks) + list(self.song.return_tracks) track_idx = tracks.index(track) selected = track.view.selected_device if selected is None: return None devices = list(track.devices) device_idx = devices.index(selected) return (track_idx, device_idx) except Exception as e: logger.warning("get selected_device: %s", e) return None def _show_clip_detail_view(self, params: tuple) -> None: try: app = self.manager.application() app.view.show_view("Detail/Clip") except Exception as e: logger.warning("show_clip_detail_view: %s", e) return None def _show_device_detail_view(self, params: tuple) -> None: try: app = self.manager.application() app.view.show_view("Detail/DeviceChain") except Exception as e: logger.warning("show_device_detail_view: %s", e) return None def _focus_browser(self, params: tuple) -> None: try: app = self.manager.application() app.view.show_view("Browser") except Exception as e: logger.warning("focus_browser: %s", e) return None def _get_focused_document_view(self, params: tuple) -> Optional[tuple]: try: app = self.manager.application() view = app.view.focused_document_view return (view,) except Exception as e: logger.warning("get focused_document_view: %s", e) return None