"""
リングコマンド式メモランチャー V2 - 改良版
常駐ボタン + ニョキっと出てくるアニメーション付き
"""

import os
import math
import configparser
import logging
from PyQt5.QtWidgets import (QWidget, QApplication, QVBoxLayout, QHBoxLayout, 
                             QLabel, QPushButton, QDialog, QFileDialog, 
                             QScrollArea, QGridLayout, QMessageBox, QMenu, 
                             QAction, QInputDialog, QTextEdit,
                             QTreeWidgetItemIterator)
from PyQt5.QtCore import Qt, QTimer, QPoint, QRect, pyqtSignal
from PyQt5.QtGui import QPixmap, QPainter, QBrush, QPen, QFont, QCursor, QColor
from PyQt5.QtWidgets import QToolTip
from .shortcut_manager import ShortcutManager, ShortcutSettingsDialog
# テキスト階層解析のインポート
try:
    import sys
    import os
    
    # PyInstaller実行時の対応
    if getattr(sys, 'frozen', False):
        # PyInstallerでビルドされた実行ファイルの場合
        application_path = os.path.dirname(sys.executable)
        sys.path.insert(0, application_path)
    else:
        # 開発環境の場合
        sys.path.append(os.path.dirname(os.path.dirname(__file__)))
    
    # まず汎用パーサーを試す
    try:
        from text_hierarchy_parser_universal import UniversalTextHierarchyParser as TextHierarchyParser
        HIERARCHY_PARSER_AVAILABLE = True
        # print("✅ 汎用階層パーサー使用（全テキストファイル対応）")
    except ImportError:
        try:
            # フォールバック：元のパーサー
            from text_hierarchy_parser import TextHierarchyParser
            HIERARCHY_PARSER_AVAILABLE = True
            print("📌 標準階層パーサー使用（攻略ファイルのみ）")
        except ImportError:
            # 最後のフォールバック：内部実装のダミークラス
            class TextHierarchyParser:
                def __init__(self):
                    pass
                def parse_text_file(self, file_path):
                    return {'title': '', 'level1': {}}
            HIERARCHY_PARSER_AVAILABLE = False
            print("⚠️ TextHierarchyParser not available - using fallback dummy parser")
except ImportError:
    HIERARCHY_PARSER_AVAILABLE = False
    print("⚠️ TextHierarchyParser not available - hierarchical expansion disabled")
    class TextHierarchyParser:
        def __init__(self):
            pass
        def parse_text_file(self, file_path):
            return {'title': '', 'level1': {}}

class RingStrategyDisplayWidget(QWidget):
    """リングランチャー用戦略表示ウィジェット"""
    
    def __init__(self):
        super().__init__()
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.SplashScreen)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setAttribute(Qt.WA_ShowWithoutActivating)
        self.setup_ui()
        
        # ドラッグ用変数
        self.drag_start_position = None
        
    def setup_ui(self):
        """UI初期化"""
        layout = QVBoxLayout()
        
        # スタイル設定
        self.setStyleSheet("""
            QWidget {
                background-color: rgba(0, 0, 0, 200);
                border-radius: 10px;
                border: 2px solid #00ff88;
            }
            QLabel {
                color: white;
                background-color: transparent;
                padding: 5px;
            }
            QTextEdit {
                background-color: rgba(20, 20, 20, 220);
                color: white;
                border: 1px solid #555;
                border-radius: 5px;
                font-size: 12px;
            }
        """)
        
        # タイトル
        self.title_label = QLabel("🥋 戦略情報")
        self.title_label.setFont(QFont("Arial", 14, QFont.Bold))
        self.title_label.setAlignment(Qt.AlignCenter)
        self.title_label.setStyleSheet("""
            QLabel {
                background-color: rgba(0, 255, 136, 100);
                border-radius: 5px;
                padding: 8px;
                border: 1px solid #00ff88;
            }
        """)
        layout.addWidget(self.title_label)
        
        # コンテンツ表示
        self.content_area = QTextEdit()
        self.content_area.setMaximumHeight(300)
        self.content_area.setMinimumWidth(400)
        layout.addWidget(self.content_area)
        
        # 閉じるボタン
        close_btn = QPushButton("❌ 閉じる")
        close_btn.clicked.connect(self.close)
        close_btn.setStyleSheet("""
            QPushButton {
                background-color: #ff4444;
                color: white;
                border: none;
                padding: 8px;
                border-radius: 5px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #ff6666;
            }
        """)
        layout.addWidget(close_btn)
        
        self.setLayout(layout)
        
    def show_character_strategy(self, character, category, content):
        """キャラクター戦略情報を表示"""
        self.title_label.setText(f"🥋 {character} - {category}")
        
        # HieroNoteリッチテキストタグを簡易HTML変換
        html_content = self.convert_hieronote_to_html(content)
        self.content_area.setHtml(html_content)
        
        # 画面中央に表示
        self.move_to_center()
        self.show()
        self.raise_()
        self.activateWindow()
        
    def convert_hieronote_to_html(self, content):
        """HieroNoteタグを簡易HTMLに変換"""
        import re
        
        html_content = content
        
        # フォントサイズ + 太字 + 色
        html_content = re.sub(r'<fs=(\d+);b=1;color=(#[a-fA-F0-9]+)>(.*?)(?=\n|$)', 
                             r'<div style="font-size:\1px; font-weight:bold; color:\2;">\3</div>', html_content)
        
        # フォントサイズ + 色
        html_content = re.sub(r'<fs=(\d+);color=(#[a-fA-F0-9]+)>(.*?)(?=\n|$)', 
                             r'<div style="font-size:\1px; color:\2;">\3</div>', html_content)
        
        # 太字のみ
        html_content = re.sub(r'<b=1>(.*?)(?=\n|$)', r'<b>\1</b>', html_content)
        
        # 改行をHTMLの改行に変換
        html_content = html_content.replace('\n', '<br>')
        
        return html_content
    
    def move_to_center(self):
        """ウィンドウを画面中央に移動"""
        from PyQt5.QtWidgets import QDesktopWidget
        desktop = QDesktopWidget()
        screen_geometry = desktop.screenGeometry()
        x = (screen_geometry.width() - self.width()) // 2
        y = (screen_geometry.height() - self.height()) // 2
        self.move(x, y)
    
    def mousePressEvent(self, event):
        """マウス押下イベント（ドラッグ開始）"""
        if event.button() == Qt.LeftButton:
            self.drag_start_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()
    
    def mouseMoveEvent(self, event):
        """マウス移動イベント（ドラッグ）"""
        if event.buttons() == Qt.LeftButton and self.drag_start_position:
            self.move(event.globalPos() - self.drag_start_position)
            event.accept()

class HierarchicalRingWidget(QWidget):
    """階層リング表示ウィジェット"""
    
    def __init__(self, parent, center_x, center_y, radius, items, level, 
                 icon_key, text_file, text_parser, main_launcher):
        super().__init__(parent)
        
        # ウィンドウ設定
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool | Qt.WindowDoesNotAcceptFocus)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setAttribute(Qt.WA_QuitOnClose, False)
        
        # パラメータ
        self.center_x = center_x
        self.center_y = center_y
        self.level = level
        self.icon_key = icon_key
        self.text_file = text_file
        self.text_parser = text_parser
        self.main_launcher = main_launcher
        
        # ページング機能
        self.all_items = items  # 全アイテム
        self.items_per_page = 15  # 1ページあたりの最大項目数
        self.current_page = 0
        self.total_pages = max(1, (len(items) - 1) // self.items_per_page + 1)
        
        # 現在のページのアイテムを設定
        self.items = self.get_current_page_items()
        
        # 動的半径計算（項目数に応じて調整）
        self.radius = self.calculate_dynamic_radius(radius, len(items))
        
        # レイアウト計算
        self.item_positions = {}
        self.item_rects = {}
        self.hovered_item = None
        
        # サイズ設定（項目数に応じて調整）- 第一階層を大幅に拡大
        base_size = 48 if level == 1 else 24  # 第一階層を32→48に拡大
        self.item_height = max(32, min(base_size, base_size - len(items) // 4))  # 最小高さを24→32に
        # 四角っぽい枠のために幅を3.5倍に設定（2.8→3.5倍に拡大）
        self.item_width = int(self.item_height * 3.5)
        # フォントサイズを少し小さくして、より多くの文字を表示
        self.font_size = max(9, min(13 if level == 1 else 10, 15 - len(items) // 3))
        
        # 色設定
        if level == 1:
            self.ring_color = QColor(0, 150, 255, 120)  # 青
            self.text_color = QColor(255, 255, 255)
        else:  # level == 2
            self.ring_color = QColor(255, 150, 0, 120)  # オレンジ
            self.text_color = QColor(255, 255, 255)
        
        # ホバータイマー（第二階層展開用）
        self.hover_timer = QTimer()
        self.hover_timer.timeout.connect(self.show_level2_on_hover)
        self.hover_timer.setSingleShot(True)
        self.hover_delay = 400  # 400ms
        self.pending_hover_item = None
        
        # ツールチップタイマー
        self.tooltip_timer = QTimer()
        self.tooltip_timer.timeout.connect(self.show_tooltip)
        self.tooltip_timer.setSingleShot(True)
        self.tooltip_delay = 1000  # 1秒
        self.pending_tooltip_item = None
        
        # 位置計算
        self.calculate_positions()
        
        # マウス追跡有効化
        self.setMouseTracking(True)
        
        # 子リング（第二階層用）
        self.child_rings = {}
    
    def create_icon_pixmap(self, shortcut_index, size):
        """アイコンPixmapを作成（共通ロジック）"""
        try:
            shortcuts = self.shortcut_manager.get_all_shortcuts()
            shortcut_info = shortcuts.get(shortcut_index, {'icon_path': None, 'text_file': None, 'is_valid': True})
            
            emoji = self.shortcut_manager.get_shortcut_emoji(shortcut_index)
            preferred_type = self.shortcut_manager.get_preferred_icon_type(shortcut_index)
            
            if preferred_type == "emoji" and emoji:
                # 絵文字アイコンを作成
                pixmap = QPixmap(size, size)
                pixmap.fill(Qt.transparent)
                from PyQt5.QtGui import QPainter, QPen, QFont
                painter = QPainter(pixmap)
                painter.setFont(QFont("Arial", 32, QFont.Bold))
                painter.drawText(pixmap.rect(), Qt.AlignCenter, emoji)
                painter.end()
                return pixmap
            elif preferred_type == "image" and shortcut_info['icon_path'] and os.path.exists(shortcut_info['icon_path']):
                # 画像アイコンを読み込み
                return QPixmap(shortcut_info['icon_path'])
            else:
                # デフォルトアイコンを作成
                pixmap = QPixmap(size, size)
                pixmap.fill(Qt.gray)
                from PyQt5.QtGui import QPainter, QPen, QFont
                painter = QPainter(pixmap)
                painter.setPen(QPen(Qt.white))
                painter.setFont(QFont("Arial", 14, QFont.Bold))
                painter.drawText(pixmap.rect(), Qt.AlignCenter, str(shortcut_index))
                painter.end()
                return pixmap
        except Exception as e:
            # エラー時のデフォルト
            pixmap = QPixmap(size, size)
            pixmap.fill(Qt.gray)
            return pixmap
    
    def get_current_page_items(self):
        """現在のページのアイテムを取得"""
        start_idx = self.current_page * self.items_per_page
        end_idx = start_idx + self.items_per_page
        return self.all_items[start_idx:end_idx]
    
    def next_page(self):
        """次のページに移動"""
        if self.current_page < self.total_pages - 1:
            self.current_page += 1
            self.items = self.get_current_page_items()
            self.calculate_item_positions_only()  # ウィンドウサイズを変えずに位置のみ再計算
            self.update()
            return True
        return False
    
    def prev_page(self):
        """前のページに移動"""
        if self.current_page > 0:
            self.current_page -= 1
            self.items = self.get_current_page_items()
            self.calculate_item_positions_only()  # ウィンドウサイズを変えずに位置のみ再計算
            self.update()
            return True
        return False
    
    def get_page_info(self):
        """ページ情報を取得"""
        return f"{self.current_page + 1}/{self.total_pages}"
    
    def calculate_dynamic_radius(self, base_radius: int, item_count: int) -> int:
        """項目数に応じて動的に半径を調整"""
        # 第一階層はより大きな半径調整を適用
        additional_radius = 0
        if item_count <= 4:
            additional_radius = 0
        elif item_count <= 8:
            additional_radius = 30 if self.level == 1 else 20  # 第一階層は+30
        elif item_count <= 12:
            additional_radius = 60 if self.level == 1 else 40  # 第一階層は+60
        else:
            additional_radius = 90 if self.level == 1 else 60  # 第一階層は+90
        
        return base_radius + additional_radius
    
    def calculate_item_positions_only(self):
        """アイテム位置のみを再計算（ウィンドウサイズは変更しない）"""
        if not self.items:
            return
        
        item_count = len(self.items)
        angle_step = 2 * math.pi / item_count
        
        # 各アイテムの位置を計算
        for i, item in enumerate(self.items):
            angle = i * angle_step - math.pi / 2  # 上から時計回り
            
            x = self.center_x + self.radius * math.cos(angle)
            y = self.center_y + self.radius * math.sin(angle)
            
            self.item_positions[item] = QPoint(int(x), int(y))
            
            # ホバー検出用矩形（横長楕円用）
            rect = QRect(
                int(x) - self.item_width // 2,
                int(y) - self.item_height // 2,
                self.item_width,
                self.item_height
            )
            self.item_rects[item] = rect
    
    def calculate_positions(self):
        """アイテム位置を計算"""
        if not self.items:
            self.setGeometry(self.center_x - 30, self.center_y - 30, 60, 60)
            return
        
        item_count = len(self.items)
        angle_step = 2 * math.pi / item_count
        
        # 各アイテムの位置を計算
        for i, item in enumerate(self.items):
            angle = i * angle_step - math.pi / 2  # 上から時計回り
            
            x = self.center_x + self.radius * math.cos(angle)
            y = self.center_y + self.radius * math.sin(angle)
            
            self.item_positions[item] = QPoint(int(x), int(y))
            
            # ホバー検出用矩形（横長楕円用）
            rect = QRect(
                int(x - self.item_width // 2),
                int(y - self.item_height // 2),
                self.item_width,
                self.item_height
            )
            self.item_rects[item] = rect
        
        # ウィンドウサイズを計算
        min_x = min(rect.left() for rect in self.item_rects.values())
        max_x = max(rect.right() for rect in self.item_rects.values())
        min_y = min(rect.top() for rect in self.item_rects.values())
        max_y = max(rect.bottom() for rect in self.item_rects.values())
        
        # マージンを追加
        margin = 20
        width = max_x - min_x + margin * 2
        height = max_y - min_y + margin * 2
        
        # ウィンドウ位置とサイズを設定
        window_x = min_x - margin
        window_y = min_y - margin
        
        self.setGeometry(window_x, window_y, width, height)
        
        # 相対座標に変換
        offset_x = -window_x
        offset_y = -window_y
        
        for item in self.items:
            self.item_positions[item] = QPoint(
                self.item_positions[item].x() + offset_x,
                self.item_positions[item].y() + offset_y
            )
            self.item_rects[item].translate(offset_x, offset_y)
        
        # 中央点も調整
        self.center_x += offset_x
        self.center_y += offset_y
    
    def paintEvent(self, event):
        """描画イベント"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        
        # 背景リング
        if len(self.items) > 1:
            painter.setBrush(QBrush(self.ring_color))
            painter.setPen(QPen(self.ring_color.darker(), 1))
            painter.drawEllipse(
                self.center_x - self.radius,
                self.center_y - self.radius,
                self.radius * 2,
                self.radius * 2
            )
        
        # アイテム描画
        painter.setFont(QFont("Arial", self.font_size, QFont.Bold))
        
        for item in self.items:
            pos = self.item_positions[item]
            rect = self.item_rects[item]
            
            # ホバー効果
            if item == self.hovered_item:
                # ホバー時のアニメーション効果（パルス＆グロー）
                from PyQt5.QtGui import QRadialGradient
                
                # パルス効果のためのサイズ拡大
                pulse_rect = rect.adjusted(-4, -4, 4, 4)
                
                # 放射状グラデーションでグロー効果
                glow_gradient = QRadialGradient(pulse_rect.center(), max(pulse_rect.width(), pulse_rect.height()) / 2)
                glow_gradient.setColorAt(0, QColor(255, 255, 100, 180))  # 中心：明るい黄色
                glow_gradient.setColorAt(0.7, QColor(255, 200, 0, 120))  # 中間：オレンジ
                glow_gradient.setColorAt(1, QColor(255, 150, 0, 60))     # 外側：薄いオレンジ
                
                painter.setBrush(QBrush(glow_gradient))
                painter.setPen(QPen(QColor(255, 255, 0, 200), 3))
                highlight_corner_radius = min(pulse_rect.width(), pulse_rect.height()) // 6
                painter.drawRoundedRect(pulse_rect, highlight_corner_radius, highlight_corner_radius)
            
            # ドロップシャドウ効果を先に描画
            shadow_offset = 3
            shadow_rect = rect.adjusted(shadow_offset, shadow_offset, shadow_offset, shadow_offset)
            shadow_corner_radius = min(shadow_rect.width(), shadow_rect.height()) // 6
            painter.setBrush(QBrush(QColor(0, 0, 0, 100)))  # 半透明の黒い影
            painter.setPen(Qt.NoPen)
            painter.drawRoundedRect(shadow_rect, shadow_corner_radius, shadow_corner_radius)
            
            # アイテム背景（四角っぽい角丸矩形）- グラデーション効果付き
            from PyQt5.QtGui import QLinearGradient
            
            # グラデーション作成
            gradient = QLinearGradient(rect.topLeft(), rect.bottomRight())
            if self.level == 1:
                # 第一階層：青系グラデーション
                gradient.setColorAt(0, QColor(70, 130, 200, 200))  # 明るい青
                gradient.setColorAt(0.5, QColor(40, 90, 160, 190))  # 中間
                gradient.setColorAt(1, QColor(20, 60, 120, 180))   # 暗い青
            else:
                # 第二階層：オレンジ系グラデーション
                gradient.setColorAt(0, QColor(220, 140, 60, 200))  # 明るいオレンジ
                gradient.setColorAt(0.5, QColor(180, 110, 40, 190)) # 中間
                gradient.setColorAt(1, QColor(140, 80, 20, 180))   # 暗いオレンジ
            
            painter.setBrush(QBrush(gradient))
            painter.setPen(QPen(self.text_color, 1))
            # 角丸矩形で描画（楕円から変更）
            corner_radius = min(rect.width(), rect.height()) // 6  # 角の丸みを調整
            painter.drawRoundedRect(rect, corner_radius, corner_radius)
            
            # テキスト描画
            painter.setPen(QPen(self.text_color))
            
            # 文字数に応じて2行表示を判定（四角っぽい枠で文字数増加）
            max_chars_per_line = 10 if self.level == 1 else 7
            if len(item) > max_chars_per_line:
                # 2行表示：フォントサイズを小さくして2行に分割
                painter.setFont(QFont("Arial", max(8, self.font_size - 2), QFont.Bold))
                
                # 2行表示時は1行あたりの文字数を1.7倍に増加
                line_chars_2row = int(max_chars_per_line * 1.7)  # 第一階層: 10→17文字、第二階層: 7→12文字
                
                # テキストを2行に分割
                line1 = item[:line_chars_2row]
                line2 = item[line_chars_2row:line_chars_2row * 2]
                
                # 2行表示用の矩形計算
                line_height = rect.height() // 2
                rect1 = QRect(rect.x(), rect.y() + rect.height() // 4 - line_height // 2, rect.width(), line_height)
                rect2 = QRect(rect.x(), rect.y() + 3 * rect.height() // 4 - line_height // 2, rect.width(), line_height)
                
                # 文字の縁取り効果（2行表示）
                self.draw_text_with_outline(painter, rect1, line1)
                self.draw_text_with_outline(painter, rect2, line2)
                
                # フォントサイズを元に戻す
                painter.setFont(QFont("Arial", self.font_size, QFont.Bold))
            else:
                # 1行表示 - 文字の縁取り効果
                self.draw_text_with_outline(painter, rect, item)
        
        # 中央にアイコンを描画（アイテムの上に重ねて表示）
        self.draw_center_icon(painter)
        
        # ページ情報を表示（複数ページがある場合のみ）
        if self.total_pages > 1:
            self.draw_page_info(painter)
    
    def draw_text_with_outline(self, painter, rect, text):
        """文字に縁取り効果を追加して描画"""
        # 縁取り用のペン（黒の太い線）
        outline_pen = QPen(QColor(0, 0, 0, 200), 2)
        painter.setPen(outline_pen)
        
        # 8方向に少しずつずらして縁取りを描画
        offsets = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
        for dx, dy in offsets:
            offset_rect = rect.adjusted(dx, dy, dx, dy)
            painter.drawText(offset_rect, Qt.AlignCenter, text)
        
        # メインテキストを描画（白）
        painter.setPen(QPen(self.text_color))
        painter.drawText(rect, Qt.AlignCenter, text)
    
    def draw_center_icon(self, painter):
        """中央にアイコンを描画"""
        try:
            center_size = 60 if self.level == 1 else 40  # アイコンサイズ
            
            # 中央位置計算
            center_x = self.center_x - center_size // 2
            center_y = self.center_y - center_size // 2
            center_rect = QRect(center_x, center_y, center_size, center_size)
            
            # 背景円（半透明の白）
            painter.setBrush(QBrush(QColor(255, 255, 255, 200)))
            painter.setPen(QPen(QColor(200, 200, 200), 2))
            painter.drawEllipse(center_rect)
            
            # ShortcutManagerから現在のアイコン設定を取得（preferred_type対応）
            if hasattr(self.main_launcher, 'shortcut_manager') and self.main_launcher.shortcut_manager:
                try:
                    icon_index = int(self.icon_key)  # icon_key は "1", "2" などの文字列
                    
                    # preferred_type による優先順位でアイコンを決定
                    emoji = self.main_launcher.shortcut_manager.get_shortcut_emoji(icon_index)
                    preferred_type = self.main_launcher.shortcut_manager.get_preferred_icon_type(icon_index)
                    
                    if preferred_type == "emoji" and emoji:
                        # 絵文字アイコンを描画
                        painter.setPen(QPen(QColor(0, 0, 0)))
                        font_size = center_size // 2
                        painter.setFont(QFont("Arial", font_size, QFont.Bold))
                        painter.drawText(center_rect, Qt.AlignCenter, emoji)
                        return
                    elif preferred_type == "image":
                        # 画像アイコンが設定されている場合
                        icon_path = self.main_launcher.shortcut_manager.get_shortcut_icon(icon_index)
                        if icon_path and os.path.exists(icon_path):
                            from PyQt5.QtGui import QPixmap
                            pixmap = QPixmap(icon_path)
                            if not pixmap.isNull():
                                # アイコンサイズに合わせてスケール
                                scaled_pixmap = pixmap.scaled(
                                    center_size - 8, center_size - 8, 
                                    Qt.KeepAspectRatio, Qt.SmoothTransformation
                                )
                                # 中央に描画
                                icon_x = center_x + (center_size - scaled_pixmap.width()) // 2
                                icon_y = center_y + (center_size - scaled_pixmap.height()) // 2
                                painter.drawPixmap(icon_x, icon_y, scaled_pixmap)
                                return
                except Exception as e:
                    pass  # エラー時は次の方法を試す
                    
            # メインランチャーの保存済みpixmapがある場合
            if hasattr(self.main_launcher, 'icons') and self.icon_key in self.main_launcher.icons:
                icon_info = self.main_launcher.icons[self.icon_key]
                if 'pixmap' in icon_info and not icon_info['pixmap'].isNull():
                    pixmap = icon_info['pixmap']
                    # アイコンサイズに合わせてスケール
                    scaled_pixmap = pixmap.scaled(
                        center_size - 8, center_size - 8, 
                        Qt.KeepAspectRatio, Qt.SmoothTransformation
                    )
                    # 中央に描画
                    icon_x = center_x + (center_size - scaled_pixmap.width()) // 2
                    icon_y = center_y + (center_size - scaled_pixmap.height()) // 2
                    painter.drawPixmap(icon_x, icon_y, scaled_pixmap)
                    return
            
            # デフォルトアイコン
            painter.setPen(QPen(QColor(0, 0, 0)))
            painter.setFont(QFont("Arial", center_size // 2, QFont.Bold))
            painter.drawText(center_rect, Qt.AlignCenter, "⭐")
                
        except Exception as e:
            # エラー時はデフォルト表示
            center_size = 60 if self.level == 1 else 40
            center_x = self.center_x - center_size // 2
            center_y = self.center_y - center_size // 2
            center_rect = QRect(center_x, center_y, center_size, center_size)
            
            painter.setBrush(QBrush(QColor(255, 255, 255, 200)))
            painter.setPen(QPen(QColor(200, 200, 200), 2))
            painter.drawEllipse(center_rect)
            
            painter.setPen(QPen(QColor(0, 0, 0)))
            painter.setFont(QFont("Arial", center_size // 2, QFont.Bold))
            painter.drawText(center_rect, Qt.AlignCenter, "⭐")
    
    def draw_page_info(self, painter):
        """ページ情報を描画"""
        try:
            # ページ情報テキスト
            page_text = self.get_page_info()
            
            # 中央下部に表示
            info_y = self.center_y + self.radius + 30
            info_rect = QRect(self.center_x - 50, info_y, 100, 20)
            
            # 背景（半透明）
            painter.setBrush(QBrush(QColor(0, 0, 0, 150)))
            painter.setPen(Qt.NoPen)
            painter.drawRoundedRect(info_rect, 10, 10)
            
            # テキスト描画
            painter.setPen(QPen(QColor(255, 255, 255)))
            painter.setFont(QFont("Arial", 10, QFont.Bold))
            painter.drawText(info_rect, Qt.AlignCenter, page_text)
            
        except Exception as e:
            pass  # エラー時は何も表示しない
    
    def mouseMoveEvent(self, event):
        """マウス移動イベント"""
        old_hovered = self.hovered_item
        self.hovered_item = None
        
        for item, rect in self.item_rects.items():
            if rect.contains(event.pos()):
                self.hovered_item = item
                break
        
        if old_hovered != self.hovered_item:
            self.update()
            
            # ツールチップタイマーを処理
            self.handle_tooltip_hover()
            
            # 第一階層の場合、第二階層展開を処理
            if self.level == 1:
                self.handle_level2_hover()
    
    def handle_level2_hover(self):
        """第二階層のホバー処理"""
        # 前のホバーアイテムの子リングを非表示
        for child_ring in self.child_rings.values():
            child_ring.hide()
            child_ring.deleteLater()
        self.child_rings.clear()
        
        # タイマーをリセット
        self.hover_timer.stop()
        self.pending_hover_item = None
        
        if self.hovered_item:
            # 遅延表示タイマーを開始
            self.pending_hover_item = self.hovered_item
            self.hover_timer.start(self.hover_delay)
    
    def show_level2_on_hover(self):
        """第二階層リングを表示"""
        if not self.pending_hover_item:
            return
        
        level1_key = self.pending_hover_item
        self.pending_hover_item = None
        
        try:
            # 第二階層項目を取得
            level2_items = self.text_parser.get_level2_items(self.text_file, level1_key)
            
            if not level2_items:
                return
            
            # 第二階層リングの位置を計算
            item_pos = self.item_positions[level1_key]
            
            # 方向ベクトルを計算
            direction_x = item_pos.x() - self.center_x
            direction_y = item_pos.y() - self.center_y
            length = math.sqrt(direction_x ** 2 + direction_y ** 2)
            
            if length == 0:
                return
            
            # 正規化
            direction_x /= length
            direction_y /= length
            
            # 第二階層リングの中心位置
            ring2_distance = self.radius + 50
            ring2_center_x = self.center_x + direction_x * ring2_distance
            ring2_center_y = self.center_y + direction_y * ring2_distance
            
            # 絶対座標に変換
            abs_center_x = ring2_center_x + self.x()
            abs_center_y = ring2_center_y + self.y()
            
            # 第二階層リングを作成
            ring2_widget = HierarchicalRingWidget(
                parent=None,
                center_x=int(abs_center_x),
                center_y=int(abs_center_y),
                radius=40,
                items=level2_items[:6],  # 最大6個まで
                level=2,
                icon_key=self.icon_key,
                text_file=self.text_file,
                text_parser=self.text_parser,
                main_launcher=self.main_launcher
            )
            
            ring2_widget.show()
            self.child_rings[level1_key] = ring2_widget
            
            # メインランチャーに登録
            self.main_launcher.level2_rings[(self.icon_key, level1_key)] = ring2_widget
            
            print(f"🌀 第二階層リング表示: {level1_key} -> {len(level2_items)}項目")
            
        except Exception as e:
            print(f"第二階層表示エラー: {e}")
    
    def mousePressEvent(self, event):
        """マウスクリックイベント"""
        if event.button() == Qt.LeftButton:
            if self.hovered_item:
                # 項目がクリックされた場合の処理
                self.handle_item_click(self.hovered_item)
                
                # クリック後に階層リングを非表示にする
                self.hide_with_cleanup()
            else:
                # 空の部分をクリックした場合も階層リングを非表示にする
                self.hide_with_cleanup()
        elif event.button() == Qt.RightButton:
            # 右クリックメニューを表示
            self.show_context_menu(event.pos())
    
    def hide_with_cleanup(self):
        """階層リングを非表示にして関連するリングもクリーンアップ"""
        try:
            print(f"🔵 階層リング非表示: レベル{self.level}")
            
            # 第二階層リングがある場合は先に非表示
            if hasattr(self, 'level2_rings'):
                for ring in self.level2_rings.values():
                    if ring and ring.isVisible():
                        ring.hide()
                        ring.deleteLater()
                self.level2_rings.clear()
            
            # 自身を非表示
            self.hide()
            
            # メインランチャーに階層リング非表示を通知
            if self.main_launcher and hasattr(self.main_launcher, 'hide_all_hierarchy_rings'):
                self.main_launcher.hide_all_hierarchy_rings()
                
        except Exception as e:
            print(f"❌ 階層リング非表示エラー: {e}")
    
    def show_context_menu(self, pos):
        """右クリックメニューを表示"""
        try:
            from PyQt5.QtWidgets import QMenu
            menu = QMenu(self)
            
            # 複数ページの場合のみページ情報を表示
            if self.total_pages > 1:
                # ページ情報を表示
                page_info = menu.addAction(f"ページ {self.get_page_info()}")
                page_info.setEnabled(False)
                menu.addSeparator()
                
                # 前のページ
                if self.current_page > 0:
                    prev_action = menu.addAction("◀ 前のページ")
                    prev_action.triggered.connect(self.prev_page)
                
                # 次のページ
                if self.current_page < self.total_pages - 1:
                    next_action = menu.addAction("次のページ ▶")
                    next_action.triggered.connect(self.next_page)
                
                menu.addSeparator()
            
            # 常に「閉じる」項目を表示
            close_action = menu.addAction("閉じる")
            close_action.triggered.connect(self.hide_with_cleanup)
            
            # メニューを表示
            global_pos = self.mapToGlobal(pos)
            menu.exec_(global_pos)
            
        except Exception as e:
            print(f"右クリックメニューエラー: {e}")
    
    def handle_tooltip_hover(self):
        """ツールチップのホバー処理"""
        # ツールチップタイマーをリセット
        self.tooltip_timer.stop()
        self.pending_tooltip_item = None
        
        if self.hovered_item:
            # 遅延表示タイマーを開始
            self.pending_tooltip_item = self.hovered_item
            self.tooltip_timer.start(self.tooltip_delay)
    
    def show_tooltip(self):
        """ツールチップを表示"""
        if not self.pending_tooltip_item:
            return
        
        item = self.pending_tooltip_item
        self.pending_tooltip_item = None
        
        try:
            print(f"🏷️ ツールチップ表示: {item}")
            
            # 階層に応じたコンテンツ取得
            if self.level == 1:
                content = self.text_parser.get_content_preview(self.text_file, item)
            else:  # level == 2
                # 第二階層の場合、親の階層情報が必要
                # 暫定的に第一階層として扱う
                content = self.text_parser.get_content_preview(self.text_file, item)
            
            # コンテンツが空の場合のフォールバック
            if not content or content.strip() == "":
                content = "詳細情報はファイルをご確認ください"
            
            # ツールチップテキストを整形
            display_content = content[:150] + "..." if len(content) > 150 else content
            tooltip_text = f"📄 {item}\n\n{display_content}"
            
            # ツールチップを手動で表示（カーソル位置に）
            cursor_pos = QCursor.pos()
            QToolTip.showText(cursor_pos, tooltip_text)
            # print(f"✅ ツールチップ表示完了: {len(tooltip_text)}文字")
            
            # ウィジェットのツールチップも設定
            self.setToolTip(tooltip_text)
            
        except Exception as e:
            print(f"❌ ツールチップエラー: {e}")
            self.setToolTip(f"📄 {item}\n\n内容を取得できませんでした")
            import traceback
            traceback.print_exc()
    
    def handle_item_click(self, item):
        """項目クリック処理"""
        try:
            print(f"🖱️ 項目クリック: {item}")
            print(f"🔍 デバッグ - self.text_file: {self.text_file}")
            print(f"🔍 デバッグ - type(self.text_file): {type(self.text_file)}")
            
            # メインウィンドウで該当ファイルを開く
            if hasattr(self.main_launcher, 'main_window') and self.main_launcher.main_window:
                main_window = self.main_launcher.main_window
                
                # ファイル名を取得（拡張子なし）
                if os.path.isabs(self.text_file):
                    # 絶対パスの場合
                    filename_without_ext = os.path.splitext(os.path.basename(self.text_file))[0]
                    print(f"🔍 絶対パス処理 - filename_without_ext: {filename_without_ext}")
                else:
                    # 相対パスの場合（拡張子があれば除去）
                    filename_without_ext = os.path.splitext(self.text_file)[0]
                    print(f"🔍 相対パス処理 - filename_without_ext: {filename_without_ext}")
                
                # .txtファイルとして開く（file_load_managerが拡張子を自動追加する）
                if hasattr(main_window, 'file_load_manager'):
                    main_window.file_load_manager.load_text(filename_without_ext)
                    print(f"📂 ファイル読み込み実行: {filename_without_ext}")
                else:
                    print(f"⚠️ file_load_manager が見つかりません")
                
                # 該当する行を検索して選択
                if hasattr(main_window, 'text_area'):
                    success = self.select_item_in_tree(main_window.text_area, item)
                    if success:
                        # print(f"✅ 項目選択成功: {item}")
                        pass
                    else:
                        print(f"⚠️ 項目選択失敗: {item}")
                
                # メインウィンドウをアクティブ化
                main_window.show()
                main_window.raise_()
                main_window.activateWindow()
                
                # 項目クリック後は階層リングを非表示にする
                print(f"🔵 項目クリック後の階層リング非表示")
                if hasattr(self.main_launcher, 'hide_all_hierarchy_rings'):
                    self.main_launcher.hide_all_hierarchy_rings()
            
        except Exception as e:
            print(f"❌ 項目クリックエラー: {e}")
            import traceback
            traceback.print_exc()
    
    def select_item_in_tree(self, tree_widget, target_text):
        """ツリーウィジェットで指定テキストを含む項目を選択"""
        try:
            print(f"🔍 ツリー検索開始: '{target_text}'")
            
            # クリックされた項目から検索キーワードを抽出
            search_keywords = []
            
            # 「軽確反 (-4～-6F)」のような場合は「軽確反」を抽出
            if "(-" in target_text or "(" in target_text:
                # 括弧前の部分を取得
                main_part = target_text.split('(')[0].strip()
                search_keywords.append(main_part)
                
            # そのまま完全一致も試す
            search_keywords.append(target_text.strip())
            
            # 主要な単語を分割して検索
            for keyword in search_keywords:
                if self._search_in_tree(tree_widget, keyword):
                    # print(f"✅ ツリー検索成功: '{keyword}'")
                    return True
            
            print(f"⚠️ ツリー検索失敗: '{target_text}'")
            return False
            
        except Exception as e:
            print(f"❌ 項目選択エラー: {e}")
            import traceback
            traceback.print_exc()
        
        return False
    
    def _search_in_tree(self, tree_widget, keyword):
        """ツリーで指定キーワードを検索"""
        try:
            iterator = QTreeWidgetItemIterator(tree_widget)
            while iterator.value():
                item = iterator.value()
                item_text = item.text(0)
                
                # リッチテキストタグを除去して比較
                if hasattr(self.text_parser, '_remove_rich_text_tags'):
                    clean_text = self.text_parser._remove_rich_text_tags(item_text)
                else:
                    # フォールバック: 基本的なタグ除去
                    import re
                    clean_text = re.sub(r'<[^>]+>', '', item_text)
                
                # 部分マッチで検索（大文字小文字無視）
                if keyword.lower() in clean_text.lower():
                    tree_widget.setCurrentItem(item)
                    tree_widget.scrollToItem(item)
                    # 親項目も展開
                    parent = item.parent()
                    while parent:
                        parent.setExpanded(True)
                        parent = parent.parent()
                    item.setExpanded(True)
                    print(f"🎯 ツリー項目選択: '{clean_text}' (キーワード: '{keyword}')")
                    return True
                
                iterator += 1
            
        except Exception as e:
            print(f"ツリー検索エラー: {e}")
        
        return False
    
    def leaveEvent(self, event):
        """マウスが離れたときのイベント"""
        self.hovered_item = None
        self.update()
        
        # タイマーを停止
        self.hover_timer.stop()
        self.pending_hover_item = None
        
        # ツールチップタイマーも停止
        self.tooltip_timer.stop()
        self.pending_tooltip_item = None
        
        # ツールチップをクリア
        self.setToolTip("")
        
        super().leaveEvent(event)
    
    def hide_all_hierarchy_rings(self):
        """全ての階層リングを非表示にする"""
        try:
            # 自分の子リングを削除
            for child_ring in self.child_rings.values():
                if child_ring:
                    child_ring.hide()
                    child_ring.deleteLater()
            self.child_rings.clear()
            
            # メインランチャーから第二階層リングを削除
            if hasattr(self.main_launcher, 'level2_rings'):
                rings_to_remove = []
                for key, ring in self.main_launcher.level2_rings.items():
                    if ring:
                        ring.hide()
                        ring.deleteLater()
                        rings_to_remove.append(key)
                
                for key in rings_to_remove:
                    del self.main_launcher.level2_rings[key]
            
            # 自分も非表示
            self.hide()
            
        except Exception as e:
            print(f"階層リング非表示エラー: {e}")

class FloatingCenterButton(QWidget):
    """常駐する中央円形ボタン"""
    
    clicked = pyqtSignal()  # クリックされたときのシグナル
    toggle_main_window = pyqtSignal()  # 本体ウィンドウの表示/非表示切り替えシグナル
    
    def __init__(self, parent=None, main_window=None):
        super().__init__(parent)
        # より強力な最前面表示設定（フォーカス回避対応）
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool | Qt.WindowDoesNotAcceptFocus)
        self.setAttribute(Qt.WA_TranslucentBackground)
        # このウィンドウが閉じられてもアプリケーションを終了させない
        self.setAttribute(Qt.WA_QuitOnClose, False)
        
        # 本体ウィンドウの参照を保持
        self.main_window = main_window
        
        # サイズ設定（位置読み込みより先に設定）
        self.button_size = 50
        self.setFixedSize(self.button_size, self.button_size)
        
        # 設定ファイルから位置を読み込み
        self.config_file = os.path.join(os.path.dirname(__file__), 'config.ini')
        
        # マウスホバー状態
        self.is_hovered = False
        
        # ドラッグ用の変数
        self.drag_position = None
        self.is_dragging = False
        
        # 中央アイコン設定
        self.center_icon_text = "⭐"  # デフォルトの絵文字
        self.center_icon_pixmap = None  # カスタム画像
        
        # 設定読み込み
        self.load_center_icon()
        
        # フォーカス制御設定を読み込み
        self.no_focus_mode = self.load_no_focus_setting()
        
        # 位置読み込み（設定がない場合はデフォルト位置に設定）
        self.load_position()
        
        # マウス追跡を有効化
        self.setMouseTracking(True)
    
    def move_to_default_position(self):
        """デフォルト位置（画面中央）に移動"""
        screen = QApplication.desktop().screenGeometry()
        x = (screen.width() - self.button_size) // 2
        y = (screen.height() - self.button_size) // 2
        self.move(x, y)
    
    def load_position(self):
        """設定ファイルから位置を読み込み"""
        try:
            config = configparser.ConfigParser()
            config.read(self.config_file, encoding='utf-8')
            
            if config.has_section('FloatingButton') and config.has_option('FloatingButton', 'x') and config.has_option('FloatingButton', 'y'):
                x = config.getint('FloatingButton', 'x')
                y = config.getint('FloatingButton', 'y')
                
                # 画面内に収まるか確認
                screen = QApplication.desktop().screenGeometry()
                x = max(0, min(x, screen.width() - self.button_size))
                y = max(0, min(y, screen.height() - self.button_size))
                
                self.move(x, y)
            else:
                self.move_to_default_position()
        except Exception as e:
            logging.error(f"位置読み込みエラー: {e}")
            self.move_to_default_position()
    
    def save_position(self):
        """現在の位置を設定ファイルに保存"""
        try:
            config = configparser.ConfigParser()
            config.read(self.config_file, encoding='utf-8')
            
            if not config.has_section('FloatingButton'):
                config.add_section('FloatingButton')
            
            pos = self.pos()
            config.set('FloatingButton', 'x', str(pos.x()))
            config.set('FloatingButton', 'y', str(pos.y()))
            
            with open(self.config_file, 'w', encoding='utf-8') as f:
                config.write(f)
                
        except Exception as e:
            logging.error(f"位置保存エラー: {e}")
    
    def load_center_icon(self):
        """中央アイコンの設定を読み込み"""
        try:
            config = configparser.ConfigParser()
            config.read(self.config_file, encoding='utf-8')
            
            if config.has_section('FloatingButton') and config.has_option('FloatingButton', 'center_icon'):
                icon_setting = config.get('FloatingButton', 'center_icon')
                
                # 画像ファイルパスの場合
                if icon_setting.endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp')):
                    icon_path = os.path.join(os.path.dirname(__file__), 'shortcut_icons', icon_setting)
                    if os.path.exists(icon_path):
                        self.center_icon_pixmap = QPixmap(icon_path)
                        self.center_icon_text = None
                    else:
                        # ファイルが存在しない場合はデフォルトに戻す
                        self.center_icon_text = "⭐"
                        self.center_icon_pixmap = None
                else:
                    # 絵文字の場合
                    self.center_icon_text = icon_setting
                    self.center_icon_pixmap = None
        except Exception as e:
            logging.error(f"中央アイコン読み込みエラー: {e}")
            self.center_icon_text = "⭐"
            self.center_icon_pixmap = None
    
    def save_center_icon(self, icon_setting):
        """中央アイコンの設定を保存"""
        try:
            config = configparser.ConfigParser()
            config.read(self.config_file, encoding='utf-8')
            
            if not config.has_section('FloatingButton'):
                config.add_section('FloatingButton')
            
            config.set('FloatingButton', 'center_icon', icon_setting)
            
            with open(self.config_file, 'w', encoding='utf-8') as f:
                config.write(f)
                
        except Exception as e:
            logging.error(f"中央アイコン保存エラー: {e}")
    
    def load_no_focus_setting(self):
        """フォーカス制御設定を読み込み"""
        try:
            config = configparser.ConfigParser()
            config.read(self.config_file, encoding='utf-8')
            
            if config.has_section('Settings') and config.has_option('Settings', 'ring_launcher_no_focus'):
                return config.getboolean('Settings', 'ring_launcher_no_focus')
            else:
                return True  # デフォルトはフォーカス無効
        except Exception as e:
            logging.error(f"フォーカス制御設定読み込みエラー: {e}")
            return True  # エラー時はフォーカス無効
    
    def paintEvent(self, event):
        """ボタンの描画"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        
        # ドラッグ中、ホバー状態によって色を変更
        if self.is_dragging:
            painter.setBrush(QBrush(QColor(150, 200, 255, 220)))
            painter.setPen(QPen(QColor(255, 255, 255), 4))
        elif self.is_hovered:
            painter.setBrush(QBrush(QColor(100, 150, 255, 200)))
            painter.setPen(QPen(QColor(255, 255, 255), 3))
        else:
            painter.setBrush(QBrush(QColor(50, 50, 50, 180)))
            painter.setPen(QPen(QColor(200, 200, 200), 2))
        
        # 円を描画
        painter.drawEllipse(2, 2, self.button_size - 4, self.button_size - 4)
        
        # 中央アイコンを描画
        if self.center_icon_pixmap and not self.center_icon_pixmap.isNull():
            # カスタム画像の場合
            icon_size = self.button_size - 12  # 余白を考慮
            scaled_pixmap = self.center_icon_pixmap.scaled(
                icon_size, icon_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
            )
            # 中央に配置
            x = (self.button_size - scaled_pixmap.width()) // 2
            y = (self.button_size - scaled_pixmap.height()) // 2
            painter.drawPixmap(x, y, scaled_pixmap)
        else:
            # 絵文字の場合
            painter.setPen(QPen(QColor(255, 255, 255)))
            painter.setFont(QFont("Arial", 12, QFont.Bold))
            painter.drawText(self.rect(), Qt.AlignCenter, self.center_icon_text or "⭐")
    
    def enterEvent(self, event):
        """マウスホバー開始"""
        self.is_hovered = True
        self.update()
        super().enterEvent(event)
    
    def leaveEvent(self, event):
        """マウスホバー終了"""
        self.is_hovered = False
        self.update()
        super().leaveEvent(event)
    
    def mousePressEvent(self, event):
        """マウスクリック処理"""
        if event.button() == Qt.LeftButton:
            # ドラッグ開始位置を記録
            self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
        elif event.button() == Qt.RightButton:
            # 右クリックでアイコン変更メニューを表示
            self.show_icon_change_menu(event.globalPos())
        super().mousePressEvent(event)
    
    def mouseMoveEvent(self, event):
        """マウス移動処理（ドラッグ）"""
        if event.buttons() == Qt.LeftButton and self.drag_position:
            # ドラッグ中はボタンを移動
            self.move(event.globalPos() - self.drag_position)
            if not self.is_dragging:
                self.is_dragging = True
                self.update()  # 再描画でドラッグ状態を反映
        super().mouseMoveEvent(event)
    
    def mouseReleaseEvent(self, event):
        """マウスリリース処理"""
        if event.button() == Qt.LeftButton:
            if self.drag_position:
                # ドラッグ距離が小さい場合はクリックとして扱う
                drag_distance = (event.globalPos() - self.drag_position - self.frameGeometry().topLeft()).manhattanLength()
                if drag_distance < 3:  # 3ピクセル未満の移動はクリック
                    self.handle_click()
                else:
                    # ドラッグ終了時に位置を保存
                    self.save_position()
                self.drag_position = None
                self.is_dragging = False
                self.update()  # 再描画で通常状態に戻す
        super().mouseReleaseEvent(event)
    
    def handle_click(self):
        """クリック処理"""
        # 設定を読み取ってメインウィンドウの非表示動作を制御
        hide_main_window = self.get_hide_main_window_setting()
        
        if self.main_window:
            if hide_main_window:
                # 設定でメインウィンドウ非表示が有効な場合（従来の動作）
                if self.main_window.isVisible():
                    self.main_window.hide()
                else:
                    self.main_window.show()
                    self.main_window.raise_()
                    # フォーカス制御設定に基づいてactivateWindow()を呼び出し
                    if not self.no_focus_mode:
                        self.main_window.activateWindow()
            else:
                # 設定でメインウィンドウ非表示が無効な場合（常に表示状態を保つ）
                if not self.main_window.isVisible():
                    self.main_window.show()
                    self.main_window.raise_()
                    # フォーカス制御設定に基づいてactivateWindow()を呼び出し
                    if not self.no_focus_mode:
                        self.main_window.activateWindow()
        
        # 従来のクリックシグナルも発行
        self.clicked.emit()
    
    def get_hide_main_window_setting(self):
        """メインウィンドウ非表示設定を取得"""
        try:
            # メインウィンドウの設定変数を直接参照（リアルタイム反映のため）
            if self.main_window and hasattr(self.main_window, 'ring_launcher_hide_main_window'):
                return self.main_window.ring_launcher_hide_main_window
            
            # フォールバック: configファイルから読み取り
            config = configparser.ConfigParser()
            config_file = os.path.join(os.path.dirname(__file__), '..', 'config.ini')
            
            if os.path.exists(config_file):
                config.read(config_file, encoding='utf-8')
                return config.getboolean('RingLauncherBehavior', 'hide_main_window_on_click', fallback=True)
            else:
                return True  # デフォルトは非表示有効
        except Exception as e:
            logging.error(f"メインウィンドウ非表示設定読み取りエラー: {e}")
            return True  # エラー時はデフォルト
    
    def show_icon_change_menu(self, pos):
        """中央円右クリックメニューを表示"""
        menu = QMenu(self)
        
        # リングランチャー設定アクション
        settings_action = QAction("リングランチャー設定", self)
        settings_action.triggered.connect(self.open_shortcut_settings)
        menu.addAction(settings_action)
        
        menu.addSeparator()
        
        # 中央アイコンの絵文字設定
        center_emoji_action = QAction("⭐ 絵文字に変更", self)
        center_emoji_action.triggered.connect(self.select_emoji)
        menu.addAction(center_emoji_action)
        
        # 中央アイコンの画像設定
        center_image_action = QAction("🖼️ 画像に変更", self)
        center_image_action.triggered.connect(self.select_image_file)
        menu.addAction(center_image_action)
        
        # デフォルトに戻すアクション（星に変更）
        default_action = QAction("デフォルトに戻す (⭐)", self)
        default_action.triggered.connect(self.reset_to_default)
        menu.addAction(default_action)
        
        menu.addSeparator()
        
        # リングランチャーを非表示アクション
        from app_utils import get_translation
        hide_action = QAction(get_translation("menu_ring_launcher_hide"), self)
        hide_action.triggered.connect(self.hide_ring_launcher)
        menu.addAction(hide_action)
        
        # HieroNote終了アクション
        quit_action = QAction("HieroNoteを終了", self)
        quit_action.triggered.connect(self.quit_application)
        menu.addAction(quit_action)
        
        menu.exec_(pos)
    
    def open_shortcut_settings(self):
        """ショートカット設定ダイアログを開く"""
        try:
            # メインウィンドウのショートカット設定メソッドを呼び出し
            if self.main_window and hasattr(self.main_window, 'show_ring_launcher_settings'):
                self.main_window.show_ring_launcher_settings()
            else:
                logging.warning("メインウィンドウにshow_ring_launcher_settingsメソッドが見つかりません")
        except Exception as e:
            logging.error(f"ショートカット設定ダイアログ開くエラー: {e}")
    
    def hide_ring_launcher(self):
        """リングランチャーを非表示にする"""
        try:
            if self.main_window and hasattr(self.main_window, 'toggle_ring_launcher_visibility'):
                self.main_window.toggle_ring_launcher_visibility()
            else:
                logging.warning("メインウィンドウにtoggle_ring_launcher_visibilityメソッドが見つかりません")
        except Exception as e:
            logging.error(f"リングランチャー非表示エラー: {e}")
    
    def quit_application(self):
        """アプリケーションを終了"""
        try:
            # logging.info("HieroNote終了が要求されました")
            # メインウィンドウが存在する場合は適切に終了処理を実行
            if self.main_window:
                self.main_window.close()
            else:
                # メインウィンドウがない場合は直接アプリケーションを終了
                QApplication.instance().quit()
        except Exception as e:
            logging.error(f"アプリケーション終了エラー: {e}")
    
    def select_emoji(self):
        """絵文字選択ダイアログ（中央アイコン用）"""
        emoji_list = ["⭐", "🚀", "🎯", "🔥", "💎", "🎨", "🎵", "🎮", "📱", "💡", 
                      "🌟", "❤️", "⚡", "🎪", "🎊", "🏆", "🎭", "🎬", "📚", "✨",
                      "🌈", "🎈", "🎂", "🍀", "🌸", "🌺", "🌻", "🌙", "☀️", "🎯"]
        
        text, ok = QInputDialog.getItem(
            self, "中央アイコン絵文字選択", 
            "使用する絵文字を選択してください：", 
            emoji_list, 0, False
        )
        
        if ok and text:
            self.center_icon_text = text
            self.center_icon_pixmap = None
            self.save_center_icon(text)
            self.update()
    
    def select_image_file(self):
        """画像ファイル選択ダイアログ"""
        shortcut_icons_dir = os.path.join(os.path.dirname(__file__), 'shortcut_icons')
        
        file_path, _ = QFileDialog.getOpenFileName(
            self, "中央アイコン用画像を選択",
            shortcut_icons_dir,
            "画像ファイル (*.jpg *.jpeg *.png *.gif *.bmp)"
        )
        
        if file_path:
            # ファイル名のみを取得
            file_name = os.path.basename(file_path)
            
            # shortcut_iconsフォルダにコピー（異なるファイルから選択された場合）
            target_path = os.path.join(shortcut_icons_dir, file_name)
            
            # パスを正規化して同じファイルかどうかを正確に判定
            source_normalized = os.path.normpath(os.path.abspath(file_path))
            target_normalized = os.path.normpath(os.path.abspath(target_path))
            
            need_copy = True
            if source_normalized == target_normalized:
                need_copy = False
            elif os.path.exists(target_path):
                # さらに確実にするため os.path.samefile でも確認
                try:
                    if os.path.samefile(file_path, target_path):
                        need_copy = False
                except OSError:
                    # samefile が失敗した場合は正規化パス比較の結果を使用
                    pass
            
            if need_copy:
                import shutil
                try:
                    os.makedirs(shortcut_icons_dir, exist_ok=True)
                    shutil.copy2(file_path, target_path)
                except Exception as e:
                    QMessageBox.warning(self, "エラー", f"ファイルのコピーに失敗しました: {e}")
                    return
            
            # アイコンを設定
            self.center_icon_pixmap = QPixmap(target_path)
            self.center_icon_text = None
            self.save_center_icon(file_name)
            self.update()
    
    def reset_to_default(self):
        """デフォルトアイコンに戻す（星）"""
        self.center_icon_text = "⭐"
        self.center_icon_pixmap = None
        self.save_center_icon("⭐")
        self.update()


class RingCommandLauncher(QWidget):
    """リングコマンド式メモランチャー"""
    
    memo_selected = pyqtSignal(str)  # メモファイルが選択されたときのシグナル
    
    def __init__(self, parent=None, shortcut_manager=None, main_window=None):
        super().__init__(parent)
        self.main_window = main_window
        self.floating_button = None  # 縮小ランチャーの参照（後で設定）
        self.icons = {}
        self.icon_positions = {}
        
        # ShortcutManagerを使用（メインウィンドウから共有、または新規作成）
        if shortcut_manager:
            self.shortcut_manager = shortcut_manager
        else:
            from app_utils import get_config_file_path
            config_file = get_config_file_path()
            self.shortcut_manager = ShortcutManager(config_file)
        
        # フォーカス制御設定を読み込み
        from app_utils import get_config_file_path
        config_file = get_config_file_path()
        self.no_focus_mode = self.load_no_focus_setting(config_file)
        
        # ウィンドウ設定 - より強力な最前面表示（フォーカス回避対応）
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool | Qt.WindowDoesNotAcceptFocus)
        self.setAttribute(Qt.WA_TranslucentBackground)
        # メインウィンドウが閉じられてもアプリケーションを終了させない
        self.setAttribute(Qt.WA_QuitOnClose, False)
        
        # 初期設定
        self.ring_radius = 100
        self.icon_size = 48
        self.center_size = 80
        
        # アニメーション用
        self.animation_scale = 0.0  # 0.0～1.0
        self.is_expanding = False
        
        # ホバーエフェクト用
        self.hovered_icon = None  # ホバーされているアイコンのキー
        self.hover_scale = {}  # 各アイコンのホバースケール
        self.hover_glow = {}   # 各アイコンの発光強度
        
        # アイコン読み込み
        self.load_shortcut_icons()
        
        # ウィンドウサイズ計算
        self.calculate_window_size()
        
        # 位置計算
        self.calculate_positions()
        
        # 非表示状態で開始
        self.hide()
        
        # アニメーションタイマー
        self.animation_timer = QTimer()
        self.animation_timer.timeout.connect(self.animate_expansion)
        self.animation_timer.setInterval(16)  # 60fps
        
        # ホバー検出用タイマー
        self.hover_timer = QTimer()
        self.hover_timer.timeout.connect(self.check_hover)
        self.hover_timer.start(100)  # 100ms間隔でチェック
        
        # ホバーエフェクト用タイマー
        self.hover_effect_timer = QTimer()
        self.hover_effect_timer.timeout.connect(self.update_hover_effects)
        self.hover_effect_timer.setInterval(16)  # 60fps
        self.hover_effect_timer.start()
        
        # マウス追跡を有効化
        self.setMouseTracking(True)
        
        # 階層型マインドマップシステム初期化
        self.init_hierarchical_system()
        
    
    def init_hierarchical_system(self):
        """階層型マインドマップシステムの初期化"""
        try:
            # TextHierarchyParserクラスが定義されているかチェック
            if 'TextHierarchyParser' in globals():
                self.text_parser = TextHierarchyParser()
                self.hierarchical_enabled = True
                
                # 階層リング管理
                self.level1_rings = {}  # icon_key -> 第一階層リングウィジェット
                self.level2_rings = {}  # (icon_key, level1_key) -> 第二階層リングウィジェット
                
                # ホバー遅延タイマー
                self.hierarchy_hover_timer = QTimer()
                self.hierarchy_hover_timer.timeout.connect(self.show_hierarchy_on_hover)
                self.hierarchy_hover_timer.setSingleShot(True)
                self.hierarchy_hover_delay = 800  # 800ms遅延でより自然に
                
                # 現在のホバー状態
                self.pending_hierarchy_icon = None
                self.last_hovered_icon = None
                
                print("🌀 階層型マインドマップシステム初期化完了")
                # print(f"✅ HIERARCHY_PARSER_AVAILABLE = {HIERARCHY_PARSER_AVAILABLE}")
            else:
                self.hierarchical_enabled = False
                print("⚠️ TextHierarchyParserクラスが見つかりません - 階層機能無効")
        except Exception as e:
            self.hierarchical_enabled = False
            print(f"❌ 階層システム初期化エラー: {e}")
            print("⚠️ 階層機能を無効化します")
    
    def load_no_focus_setting(self, config_file):
        """フォーカス制御設定を読み込み"""
        try:
            config = configparser.ConfigParser()
            config.read(config_file, encoding='utf-8')
            
            if config.has_section('Settings') and config.has_option('Settings', 'ring_launcher_no_focus'):
                return config.getboolean('Settings', 'ring_launcher_no_focus')
            else:
                return True  # デフォルトはフォーカス無効
        except Exception as e:
            logging.error(f"フォーカス制御設定読み込みエラー: {e}")
            return True  # エラー時はフォーカス無効
    
    def load_shortcut_icons(self):
        """ShortcutManagerからショートカット設定を読み込み"""
        self.icons = {}
        # ホバーエフェクトの状態をリセット
        self.hover_scale = {}
        self.hover_glow = {}
        self.hovered_icon = None
        shortcuts = self.shortcut_manager.get_all_shortcuts()
        
        # ショートカット1-4を最低限表示（設定がなくても）
        for i in range(1, 5):
            shortcut_info = shortcuts.get(i, {'icon_path': None, 'text_file': None, 'is_valid': True})
            
            try:
                # 共通メソッドでアイコンPixmapを作成
                pixmap = self.create_icon_pixmap(i, self.icon_size)
                
                if not pixmap.isNull():
                    # アイコンサイズにスケール
                    scaled_pixmap = pixmap.scaled(
                        self.icon_size, self.icon_size, 
                        Qt.KeepAspectRatio, Qt.SmoothTransformation
                    )
                    
                    icon_key = str(i)
                    self.icons[icon_key] = {
                        'pixmap': scaled_pixmap,
                        'filename': os.path.basename(shortcut_info['icon_path']) if shortcut_info['icon_path'] else f"{i}.png",
                        'memo_file': shortcut_info['text_file'],
                        'is_valid': shortcut_info.get('is_valid', True)
                    }
                    
                    # ホバーエフェクトの初期化
                    self.hover_scale[icon_key] = 1.0
                    self.hover_glow[icon_key] = 0.0
                    
                    # logging.info(f"ショートカット読み込み: {i} -> {shortcut_info['text_file'] or '未設定'}")
                    
            except Exception as e:
                logging.error(f"ショートカット読み込みエラー {i}: {e}")
        
        # 追加のショートカット（5-10）で設定があるものも読み込み
        for i in range(5, 11):
            if i in shortcuts:
                shortcut_info = shortcuts[i]
                if shortcut_info['icon_path'] or shortcut_info['text_file']:
                    try:
                        # 共通メソッドでアイコンPixmapを作成
                        pixmap = self.create_icon_pixmap(i, self.icon_size)
                        
                        if not pixmap.isNull():
                            scaled_pixmap = pixmap.scaled(
                                self.icon_size, self.icon_size, 
                                Qt.KeepAspectRatio, Qt.SmoothTransformation
                            )
                            
                            icon_key = str(i)
                            self.icons[icon_key] = {
                                'pixmap': scaled_pixmap,
                                'filename': os.path.basename(shortcut_info['icon_path']) if shortcut_info['icon_path'] else f"{i}.png",
                                'memo_file': shortcut_info['text_file'],
                                'is_valid': shortcut_info.get('is_valid', True)
                            }
                            
                            # ホバーエフェクトの初期化
                            self.hover_scale[icon_key] = 1.0
                            self.hover_glow[icon_key] = 0.0
                            
                            # logging.info(f"ショートカット読み込み: {i} -> {shortcut_info['text_file'] or '未設定'}")
                            
                    except Exception as e:
                        logging.error(f"ショートカット読み込みエラー {i}: {e}")
        
    
    
    def calculate_window_size(self):
        """ウィンドウサイズを計算"""
        if not self.icons:
            self.setFixedSize(300, 300)
            return
        
        # 必要なサイズを計算
        total_size = (self.ring_radius + self.icon_size) * 2 + 60  # マージン
        self.setFixedSize(total_size, total_size)
    
    def calculate_positions(self):
        """アイコンを円形に配置する座標を計算"""
        self.icon_positions = {}
        
        if not self.icons:
            return
        
        center_x = self.width() // 2
        center_y = self.height() // 2
        
        icon_count = len(self.icons)
        
        for i, icon_key in enumerate(self.icons.keys()):
            angle = (2 * math.pi * i) / icon_count
            x = center_x + self.ring_radius * math.cos(angle) - self.icon_size // 2
            y = center_y + self.ring_radius * math.sin(angle) - self.icon_size // 2
            
            self.icon_positions[icon_key] = QPoint(int(x), int(y))
    
    def paintEvent(self, event):
        """ウィジェットの描画"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        
        # 背景の正方形を削除（透明のまま）
        
        # アニメーションスケールを適用
        if self.animation_scale <= 0.0:
            return
        
        painter.setOpacity(0.9)
        center_x = self.width() // 2
        center_y = self.height() // 2
        
        # 中央円を描画（常駐ボタンと同じデザイン）
        center_size = int(50 * self.animation_scale)  # 常駐ボタンと同じサイズ
        
        # ドラッグ中、ホバー状態によって色を変更（常駐ボタンと同じ）
        painter.setBrush(QBrush(QColor(50, 50, 50, 180)))
        painter.setPen(QPen(QColor(200, 200, 200), 2))
        
        painter.drawEllipse(
            center_x - center_size // 2,
            center_y - center_size // 2,
            center_size,
            center_size
        )
        
        # 中央にアイコン（縮小ランチャーと同じアイコンを使用）
        if self.animation_scale > 0.5:
            if self.floating_button and self.floating_button.center_icon_pixmap and not self.floating_button.center_icon_pixmap.isNull():
                # カスタム画像の場合
                icon_size = int((center_size - 8) * self.animation_scale)  # 余白を考慮
                scaled_pixmap = self.floating_button.center_icon_pixmap.scaled(
                    icon_size, icon_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
                )
                # 中央に配置
                x = center_x - scaled_pixmap.width() // 2
                y = center_y - scaled_pixmap.height() // 2
                painter.drawPixmap(x, y, scaled_pixmap)
            else:
                # 絵文字の場合
                center_text = "⭐"  # デフォルト
                if self.floating_button and self.floating_button.center_icon_text:
                    center_text = self.floating_button.center_icon_text
                
                painter.setPen(QPen(QColor(255, 255, 255)))
                painter.setFont(QFont("Arial", int(12 * self.animation_scale), QFont.Bold))
                painter.drawText(
                    center_x - center_size // 2, center_y - center_size // 2, 
                    center_size, center_size,
                    Qt.AlignCenter, center_text
                )
        
        # アイコンを描画（アニメーション + ホバーエフェクト適用）
        painter.setOpacity(self.animation_scale)
        for icon_key, position in self.icon_positions.items():
            if icon_key in self.icons:
                pixmap = self.icons[icon_key]['pixmap']
                
                # アニメーションされた位置を計算
                animated_x = center_x + int((position.x() - center_x) * self.animation_scale)
                animated_y = center_y + int((position.y() - center_y) * self.animation_scale)
                
                # ホバーエフェクトを適用
                hover_scale = self.hover_scale.get(icon_key, 1.0)
                hover_glow = self.hover_glow.get(icon_key, 0.0)
                
                # アイコンサイズにホバーエフェクトを適用
                icon_size = int(self.icon_size * self.animation_scale * hover_scale)
                
                # ホバー時の発光エフェクト（位置を左上にオフセット）
                if hover_glow > 0.1:
                    # 外側の発光円
                    glow_size = icon_size + int(20 * hover_glow)
                    glow_color = QColor(100, 150, 255, int(50 * hover_glow))
                    painter.setBrush(QBrush(glow_color))
                    painter.setPen(QPen(glow_color, int(3 * hover_glow)))
                    
                    # グロー位置を画像の中心に合わせて左上にオフセット
                    glow_offset = 6  # 左上への調整値
                    glow_x = animated_x - (glow_size - icon_size) // 2 - glow_offset
                    glow_y = animated_y - (glow_size - icon_size) // 2 - glow_offset
                    
                    painter.drawEllipse(glow_x, glow_y, glow_size, glow_size)
                
                
                # アイコンを描画（サイズ調整済み）
                scaled_pixmap = pixmap.scaled(icon_size, icon_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
                
                # ホバー時は中央揃えで配置
                final_x = animated_x + (int(self.icon_size * self.animation_scale) - icon_size) // 2
                final_y = animated_y + (int(self.icon_size * self.animation_scale) - icon_size) // 2
                painter.drawPixmap(final_x, final_y, scaled_pixmap)
                
                # メモファイルが設定されていない、または無効な場合は "?" を表示
                if not self.icons[icon_key]['memo_file'] or not self.icons[icon_key].get('is_valid', True):
                    painter.setPen(QPen(Qt.red))
                    painter.setFont(QFont("Arial", int(16 * self.animation_scale), QFont.Bold))
                    painter.drawText(
                        animated_x + icon_size // 2 - 8,
                        animated_y + icon_size // 2 + 8,
                        "?" if not self.icons[icon_key]['memo_file'] else "!"
                    )
    
    def mousePressEvent(self, event):
        """マウスクリック処理"""
        if event.button() == Qt.LeftButton:
            # アイコンのクリック処理を修正（アニメーション位置を考慮）
            center_x = self.width() // 2
            center_y = self.height() // 2
            
            for icon_key, position in self.icon_positions.items():
                # アニメーションされた位置を計算
                animated_x = center_x + int((position.x() - center_x) * self.animation_scale)
                animated_y = center_y + int((position.y() - center_y) * self.animation_scale)
                icon_size = int(self.icon_size * self.animation_scale)
                
                icon_rect = QRect(animated_x, animated_y, icon_size, icon_size)
                if icon_rect.contains(event.pos()):
                    self.handle_icon_click(icon_key)
                    return
            
            # 中央円のクリック判定を追加
            center_size = int(50 * self.animation_scale)
            center_rect = QRect(center_x - center_size // 2, center_y - center_size // 2, center_size, center_size)
            if center_rect.contains(event.pos()):
                self.handle_center_click()
                return
        
        elif event.button() == Qt.RightButton:
            # 右クリック処理：アイコンか中央円かで分ける
            center_x = self.width() // 2
            center_y = self.height() // 2
            
            # アイコンの右クリック判定
            for icon_key, position in self.icon_positions.items():
                # アニメーションされた位置を計算
                animated_x = center_x + int((position.x() - center_x) * self.animation_scale)
                animated_y = center_y + int((position.y() - center_y) * self.animation_scale)
                icon_size = int(self.icon_size * self.animation_scale)
                
                icon_rect = QRect(animated_x, animated_y, icon_size, icon_size)
                if icon_rect.contains(event.pos()):
                    self.show_icon_context_menu(icon_key, event.globalPos())
                    return
            
            # 中央円の右クリック判定
            center_size = int(50 * self.animation_scale)
            center_rect = QRect(center_x - center_size // 2, center_y - center_size // 2, center_size, center_size)
            if center_rect.contains(event.pos()):
                # 中央ボタンと同じメニューを表示
                if self.floating_button:
                    self.floating_button.show_icon_change_menu(event.globalPos())
                return
            
            # その他の場所での右クリック：設定画面
            self.show_settings_dialog()
    
    def show_icon_context_menu(self, icon_key, pos):
        """アイコン右クリック時のコンテキストメニューを表示"""
        from PyQt5.QtWidgets import QMenu, QAction, QInputDialog, QFileDialog, QMessageBox
        
        menu = QMenu(self)
        
        # ショートカット設定
        settings_action = QAction("ショートカット設定", self)
        settings_action.triggered.connect(lambda: self.show_settings_dialog())
        menu.addAction(settings_action)
        
        menu.addSeparator()
        
        # クリア
        clear_action = QAction(f"ショートカット {icon_key} をクリア", self)
        clear_action.triggered.connect(lambda: self.clear_shortcut(icon_key))
        menu.addAction(clear_action)
        
        menu.exec_(pos)
    
    def select_text_file(self, icon_key):
        """指定アイコンのテキストファイル選択"""
        try:
            # フォールバック：直接ファイルダイアログを開く（親なし）
            texts_dir = self.shortcut_manager.texts_dir
            filename, _ = QFileDialog.getOpenFileName(
                None, f"ショートカット {icon_key} のテキストファイルを選択",
                texts_dir, "テキストファイル (*.txt);;すべてのファイル (*)"
            )
            
            if filename:
                # 相対パスに変換
                if filename.startswith(texts_dir):
                    relative_path = os.path.relpath(filename, texts_dir)
                else:
                    relative_path = filename
                
                self.shortcut_manager.set_shortcut_text(int(icon_key), relative_path)
                self.reload_shortcuts()
        except Exception as e:
            logging.error(f"テキストファイル選択エラー: {e}")
            import traceback
            traceback.print_exc()
    
    def select_icon_file(self, icon_key):
        """指定アイコンのアイコンファイル選択"""
        try:
            # ダイアログをメインウィンドウに統合して安全に実行
            # リングランチャーを一時的に非表示にして、メインウィンドウでダイアログを開く
            if self.main_window and hasattr(self.main_window, 'shortcut_manager'):
                # メインウィンドウのShortcutManagerを使って設定ダイアログを開く
                try:
                    from .shortcut_manager import ShortcutSettingsDialog
                    parent_window = self.main_window
                    dialog = ShortcutSettingsDialog(parent_window, self.shortcut_manager)
                    
                    # 設定が変更された場合のシグナル接続
                    def on_dialog_finished(result):
                        if result == QDialog.Accepted:
                            self.reload_shortcuts()
                    
                    dialog.finished.connect(on_dialog_finished)
                    dialog.show()
                    
                    # リングランチャーを閉じる
                    self.collapse_ring()
                    return
                except Exception as dialog_error:
                    logging.warning(f"設定ダイアログフォールバック: {dialog_error}")
            
            # フォールバック：直接ファイルダイアログを開く（親なし）
            filename, _ = QFileDialog.getOpenFileName(
                None, f"ショートカット {icon_key} のアイコンを選択",
                "", "画像ファイル (*.png *.jpg *.jpeg *.bmp *.gif);;すべてのファイル (*)"
            )
            
            if filename:
                if self.shortcut_manager.set_shortcut_icon(int(icon_key), filename):
                    self.reload_shortcuts()
                    # メッセージボックスも親なしで表示
                    QMessageBox.information(None, "成功", "アイコンを設定しました")
                else:
                    QMessageBox.warning(None, "エラー", "アイコンの設定に失敗しました")
        except Exception as e:
            logging.error(f"アイコン選択エラー: {e}")
            import traceback
            traceback.print_exc()
    
    def clear_shortcut(self, icon_key):
        """指定ショートカットを完全にクリア（画像・絵文字・テキストファイル全て）"""
        try:
            # logging.info(f"🧹 リングランチャーからショートカット {icon_key} を完全クリア")
            
            # 完全クリア実行
            result = self.shortcut_manager.clear_shortcut_completely(int(icon_key))
            
            if result:
                # logging.info(f"✅ ショートカット {icon_key} の完全クリアに成功")
                # リングランチャーを再読み込み（アイコンがリングから消える）
                self.reload_shortcuts()
                # logging.info("🔄 リングランチャーを再読み込みしました")
            else:
                logging.error(f"❌ ショートカット {icon_key} の完全クリアに失敗")
                
        except Exception as e:
            logging.error(f"ショートカット完全クリアエラー: {e}")
            import traceback
            traceback.print_exc()

    def handle_center_click(self):
        """中央円クリック時の処理"""
        # リングランチャーを閉じて縮小ランチャーに戻る
        self.collapse_ring()
    
    def collapse_ring(self):
        """リングランチャーを収縮させて非表示にする"""
        # 階層リングを全て非表示
        if hasattr(self, 'hierarchical_enabled') and self.hierarchical_enabled:
            self.hide_all_hierarchy_rings()
        
        self.start_collapse_animation()
    
    def handle_icon_click(self, icon_key):
        """アイコンクリック時の処理"""
        if icon_key in self.icons:
            memo_file = self.icons[icon_key]['memo_file']
            is_valid = self.icons[icon_key].get('is_valid', True)
            
            if memo_file and is_valid:
                # 実際のファイルパスを取得
                actual_path = self.shortcut_manager.get_actual_file_path(memo_file)
                if actual_path:
                    # logging.info(f"メモファイルを開く: {actual_path}")
                    self.memo_selected.emit(actual_path)
                    self.start_collapse_animation()  # 折りたたみアニメーション
                else:
                    logging.warning(f"メモファイルが見つかりません: {memo_file}")
                    message = f"ショートカット {icon_key} のメモファイルが見つかりません。"
                    message += "\n設定画面を開きます。"
                    QMessageBox.information(self, "設定が必要", message)
                    self.show_settings_dialog()
            else:
                # メモファイルが設定されていない、または無効な場合は設定画面を開く
                if not memo_file:
                    message = f"ショートカット {icon_key} にメモファイルが設定されていません。"
                else:
                    message = f"ショートカット {icon_key} のメモファイルが見つかりません。"
                message += "\n設定画面を開きます。"
                
                QMessageBox.information(self, "設定が必要", message)
                self.show_settings_dialog()
    
    def show_settings_dialog(self):
        """設定ダイアログを表示"""
        try:
            # logging.info("🔧 設定ダイアログを開始")
            
            # 設定ダイアログを開く前にリングランチャーと常駐ボタンを非表示
            self.hide()
            self.setVisible(False)  # より確実に非表示
            if self.floating_button:
                self.floating_button.hide()
                self.floating_button.setVisible(False)  # より確実に非表示
            
            # 親ウィンドウをNoneにしてより安全に
            dialog = ShortcutSettingsDialog(None, self.shortcut_manager)
            # シグナル接続：リングランチャーのみ更新（メインウィンドウとの循環参照を避ける）
            dialog.shortcuts_updated.connect(self.reload_shortcuts)
            
            # logging.info("🔧 設定ダイアログを実行")
            
            # ダイアログ終了時の処理をconnectで設定
            def on_dialog_finished(result):
                # logging.info(f"🔧 設定ダイアログ結果: {result}")
                if result == QDialog.Accepted:
                    # 設定が変更された場合はメインウィンドウのみ更新（リングランチャーはシグナルで更新済み）
                    # logging.info("🔧 設定が承認されたためメインウィンドウを更新")
                    if self.main_window and hasattr(self.main_window, 'refresh_shortcut_buttons_only'):
                        self.main_window.refresh_shortcut_buttons_only()
                
                # logging.info("🔧 設定ダイアログ完了、常駐ボタンを復活")
                # ダイアログ終了後、常駐ボタンを復活させる
                if self.floating_button:
                    self.floating_button.show()
                    self.floating_button.setVisible(True)
                # リングランチャーは閉じたままにする
            
            dialog.finished.connect(on_dialog_finished)
            dialog.show()  # exec_()の代わりにshow()を使用
            dialog.raise_()  # 最前面に表示
            dialog.activateWindow()  # アクティブにする
            
        except Exception as e:
            logging.error(f"🔧 設定ダイアログエラー: {e}")
            import traceback
            traceback.print_exc()
    
    def reload_shortcuts(self):
        """ショートカットを再読み込み"""
        self.load_shortcut_icons()
        self.calculate_positions()
        self.update()
    
    def show_at_position(self, pos):
        """指定位置にウィンドウを表示してアニメーション開始"""
        # logging.info(f"🎭 リングランチャー表示要求: pos={pos}, window_size=({self.width()}, {self.height()})")
        
        # 画面境界を考慮して位置調整
        screen_rect = QApplication.desktop().screenGeometry()
        
        x = pos.x() - self.width() // 2
        y = pos.y() - self.height() // 2
        
        # logging.info(f"🎭 計算された表示位置: x={x}, y={y}")
        
        # 画面外に出ないよう調整
        if x < 0:
            x = 0
        elif x + self.width() > screen_rect.width():
            x = screen_rect.width() - self.width()
        
        if y < 0:
            y = 0
        elif y + self.height() > screen_rect.height():
            y = screen_rect.height() - self.height()
        
        # logging.info(f"🎭 最終表示位置: x={x}, y={y}")
        
        self.move(x, y)
        self.show()
        self.raise_()
        # フォーカス制御設定に基づいてactivateWindow()を呼び出し
        if not self.no_focus_mode:
            self.activateWindow()
        
        # ウィンドウサイズが確定してからアニメーション開始
        from PyQt5.QtCore import QTimer
        QTimer.singleShot(10, self.start_expand_animation)
    
    def start_expand_animation(self):
        """展開アニメーションを開始"""
        self.animation_scale = 0.0
        self.is_expanding = True
        self.animation_timer.start()
    
    def start_collapse_animation(self):
        """収縮アニメーションを開始"""
        self.is_expanding = False
        self.animation_timer.start()
    
    def animate_expansion(self):
        """アニメーション更新"""
        if self.is_expanding:
            self.animation_scale += 0.1
            if self.animation_scale >= 1.0:
                self.animation_scale = 1.0
                self.animation_timer.stop()
        else:
            self.animation_scale -= 0.15
            if self.animation_scale <= 0.0:
                self.animation_scale = 0.0
                self.animation_timer.stop()
                self.hide()
        
        self.update()
    
    def check_hover(self):
        """マウスホバー状態をチェック"""
        if self.isVisible() and self.animation_scale >= 1.0:
            # ウィンドウからマウスが離れたら非表示
            widget_rect = self.geometry()
            cursor_pos = QCursor.pos()
            
            if not widget_rect.contains(cursor_pos):
                # 少し余裕を持たせる
                expanded_rect = widget_rect.adjusted(-30, -30, 30, 30)
                if not expanded_rect.contains(cursor_pos):
                    self.start_collapse_animation()
    
    def keyPressEvent(self, event):
        """キー入力処理"""
        if event.key() == Qt.Key_Escape:
            self.start_collapse_animation()
        super().keyPressEvent(event)
    
    def mouseMoveEvent(self, event):
        """マウス移動イベント - ホバー検出"""
        if self.animation_scale >= 1.0:  # 完全に展開されている時のみ
            # マウス位置を取得
            mouse_pos = event.pos()
            center_x = self.width() // 2
            center_y = self.height() // 2
            
            new_hovered_icon = None
            
            # 各アイコンとの距離をチェック
            for icon_key, position in self.icon_positions.items():
                if icon_key in self.icons:
                    # アニメーションされた位置を計算
                    animated_x = center_x + int((position.x() - center_x) * self.animation_scale)
                    animated_y = center_y + int((position.y() - center_y) * self.animation_scale)
                    icon_size = int(self.icon_size * self.animation_scale)
                    
                    # ホバー領域を少し大きめに設定
                    hover_margin = 10
                    icon_rect = QRect(
                        animated_x - hover_margin, 
                        animated_y - hover_margin, 
                        icon_size + hover_margin * 2, 
                        icon_size + hover_margin * 2
                    )
                    
                    if icon_rect.contains(mouse_pos):
                        new_hovered_icon = icon_key
                        break
            
            # ホバー状態が変更された場合
            if new_hovered_icon != self.hovered_icon:
                old_hovered = self.hovered_icon
                self.hovered_icon = new_hovered_icon
                
                # 階層展開システムのホバー処理
                self.handle_hierarchical_hover(old_hovered, new_hovered_icon)
        
        super().mouseMoveEvent(event)
    
    def update_hover_effects(self):
        """ホバーエフェクトの状態を更新"""
        update_needed = False
        
        for icon_key in self.hover_scale:
            target_scale = 1.3 if icon_key == self.hovered_icon else 1.0
            target_glow = 1.0 if icon_key == self.hovered_icon else 0.0
            
            # スケールのアニメーション
            current_scale = self.hover_scale[icon_key]
            if abs(current_scale - target_scale) > 0.01:
                self.hover_scale[icon_key] += (target_scale - current_scale) * 0.15
                update_needed = True
            
            # グローのアニメーション
            current_glow = self.hover_glow[icon_key]
            if abs(current_glow - target_glow) > 0.01:
                self.hover_glow[icon_key] += (target_glow - current_glow) * 0.15
                update_needed = True
        
        if update_needed:
            self.update()
    
    def handle_hierarchical_hover(self, old_hovered, new_hovered):
        """階層展開のホバー処理"""
        if not hasattr(self, 'hierarchical_enabled') or not self.hierarchical_enabled:
            return
        
        # 古いホバーアイテムからマウスが離れた場合
        if old_hovered and old_hovered != new_hovered:
            self.hierarchy_hover_timer.stop()
            self.pending_hierarchy_icon = None
            
            # 第一階層リングが表示されている場合、マウスがそこにない限り非表示
            if old_hovered in self.level1_rings:
                QTimer.singleShot(500, lambda: self.check_hide_hierarchy(old_hovered))
        
        # 新しいアイテムにホバーした場合
        if new_hovered and new_hovered in self.icons:
            text_file = self.icons[new_hovered].get('memo_file')
            if text_file:
                self.pending_hierarchy_icon = new_hovered
                self.hierarchy_hover_timer.start(self.hierarchy_hover_delay)
    
    def show_hierarchy_on_hover(self):
        """ホバータイマー完了時に階層を表示"""
        print(f"🔍 show_hierarchy_on_hover 実行開始")
        print(f"   hierarchical_enabled: {getattr(self, 'hierarchical_enabled', False)}")
        print(f"   pending_hierarchy_icon: {self.pending_hierarchy_icon}")
        print(f"   text_parser exists: {hasattr(self, 'text_parser')}")
        
        if not getattr(self, 'hierarchical_enabled', False):
            print("⚠️ 階層機能が無効化されています")
            return
            
        if self.pending_hierarchy_icon and self.pending_hierarchy_icon in self.icons:
            text_file = self.icons[self.pending_hierarchy_icon].get('memo_file')
            print(f"📄 対象ファイル: {text_file}")
            
            if text_file:
                # ファイルパスの確認と解決
                base_dir = os.path.dirname(os.path.dirname(__file__))  # HieroNote2025_Claudeディレクトリ
                
                # PyInstaller環境の場合のパス修正
                if getattr(sys, 'frozen', False):
                    # 実行ファイルのディレクトリを基準にする
                    base_dir = os.path.dirname(sys.executable)
                
                # .txt拡張子を確実に付ける
                if not text_file.endswith('.txt'):
                    text_file += '.txt'
                
                if os.path.isabs(text_file):
                    full_path = text_file
                else:
                    # 相対パスの場合、textsディレクトリと結合
                    full_path = os.path.join(base_dir, 'texts', text_file)
                
                print(f"📁 ファイルパス解決: {text_file} -> {full_path}")
                print(f"📁 base_dir: {base_dir}")
                print(f"📁 frozen: {getattr(sys, 'frozen', False)}")
                
                # ファイル存在確認
                if not os.path.exists(full_path):
                    print(f"⚠️ ファイルが見つかりません: {full_path}")
                    # textsディレクトリの一覧を表示
                    texts_dir = os.path.join(base_dir, 'texts')
                    if os.path.exists(texts_dir):
                        files = os.listdir(texts_dir)
                        print(f"📂 textsディレクトリの中身: {files[:5]}...")  # 最初の5件のみ表示
                    return
                
                # テキストファイルの階層構造を解析
                try:
                    hierarchy = self.text_parser.parse_text_file(full_path)
                    print(f"🗂️ 解析結果: {list(hierarchy.keys()) if hierarchy else 'None'}")
                    
                    if hierarchy and hierarchy.get('level1'):
                        # print(f"✅ 第一階層項目数: {len(hierarchy['level1'])}")
                        # 第一階層リングを表示
                        self.show_level1_ring(self.pending_hierarchy_icon, hierarchy, full_path)
                    else:
                        print("⚠️ 有効な階層構造が見つかりません")
                except Exception as e:
                    print(f"❌ テキスト解析エラー: {e}")
            else:
                print("⚠️ memo_fileが設定されていません")
        else:
            print("⚠️ pending_hierarchy_iconまたはアイコンが無効です")
    
    def show_level1_ring(self, icon_key, hierarchy, file_path):
        """第一階層リングを表示"""
        if icon_key not in self.icon_positions:
            return
        
        # 既存の階層リングを非表示
        if icon_key in self.level1_rings:
            self.hide_hierarchy_rings(icon_key)
        
        # アイコンの位置を取得（メインリングランチャーのグローバル座標）
        main_rect = self.geometry()
        center_x = self.width() // 2
        center_y = self.height() // 2
        icon_pos = self.icon_positions[icon_key]
        
        # アニメーションされた位置を計算（グローバル座標）
        animated_x = center_x + int((icon_pos.x() - center_x) * self.animation_scale)
        animated_y = center_y + int((icon_pos.y() - center_y) * self.animation_scale)
        
        # グローバル座標に変換
        global_x = main_rect.x() + animated_x + self.icon_size // 2
        global_y = main_rect.y() + animated_y + self.icon_size // 2
        
        # 第一階層の項目を準備
        level1_items = list(hierarchy['level1'].keys())
        
        if level1_items:
            # 第一階層リングの半径を計算（アイコンから更に外側）
            level1_radius = 120  # メインリングより外側に配置（80→120に拡大）
            
            
            # 第一階層リングを作成
            level1_ring = HierarchicalRingWidget(
                None,  # 親なし（トップレベルウィンドウ）
                global_x,
                global_y,
                level1_radius,
                level1_items,
                1,  # レベル1
                icon_key,
                file_path,  # 解決されたフルパスを使用
                self.text_parser,
                self
            )
            
            # 階層リングを記録
            self.level1_rings[icon_key] = level1_ring
            
            # リングを表示
            level1_ring.show()
            level1_ring.raise_()
    
    def check_hide_hierarchy(self, icon_key):
        """階層リングを非表示にするかチェック"""
        if icon_key in self.level1_rings:
            level1_ring = self.level1_rings[icon_key]
            if level1_ring and level1_ring.isVisible():
                # マウスが階層リング上にない場合は非表示
                cursor_pos = QCursor.pos()
                if not level1_ring.geometry().contains(cursor_pos):
                    self.hide_hierarchy_rings(icon_key)
    
    def hide_hierarchy_rings(self, icon_key):
        """特定のアイコンの階層リングを非表示"""
        if icon_key in self.level1_rings:
            level1_ring = self.level1_rings[icon_key]
            if level1_ring:
                # 第二階層リングも非表示
                if hasattr(level1_ring, 'child_rings'):
                    for child_ring in level1_ring.child_rings.values():
                        if child_ring:
                            child_ring.hide()
                            child_ring.deleteLater()
                
                level1_ring.hide()
                level1_ring.deleteLater()
            
            # 記録を削除
            del self.level1_rings[icon_key]
    
    def hide_all_hierarchy_rings(self):
        """全ての階層リングを非表示"""
        try:
            # 第一階層リングを削除
            if hasattr(self, 'level1_rings'):
                for icon_key in list(self.level1_rings.keys()):
                    self.hide_hierarchy_rings(icon_key)
            
            # 第二階層リングも全て削除
            if hasattr(self, 'level2_rings'):
                rings_to_remove = []
                for key, ring in self.level2_rings.items():
                    if ring:
                        ring.hide()
                        ring.deleteLater() 
                        rings_to_remove.append(key)
                
                for key in rings_to_remove:
                    del self.level2_rings[key]
            
            print("🎆 全階層リングを非表示にしました")
        except Exception as e:
            print(f"⚠️ 階層リング非表示エラー: {e}")
    
    def leaveEvent(self, event):
        """メインランチャーからマウスが離れた時"""
        print("📍 メインランチャーからマウスが離れました")
        
        # ホバー状態をリセット
        self.hovered_icon = None
        
        # タイマーを停止
        if hasattr(self, 'hierarchy_hover_timer'):
            self.hierarchy_hover_timer.stop()
        if hasattr(self, 'pending_hierarchy_icon'):
            self.pending_hierarchy_icon = None
        
        # 階層リングが表示されているかチェック
        has_visible_rings = False
        if hasattr(self, 'level1_rings'):
            for ring in self.level1_rings.values():
                if ring and ring.isVisible():
                    has_visible_rings = True
                    break
        
        if has_visible_rings:
            # 階層リングが表示中の場合、遅延してマウス位置をチェック
            print("🔍 階層リング表示中 - 遅延チェック開始")
            QTimer.singleShot(300, self.check_mouse_position_delayed)
        else:
            # 階層リングが表示されていない場合は通常処理
            print("🚫 階層リング非表示中 - 特に処理なし")
        
        super().leaveEvent(event)
    
    def check_mouse_position_delayed(self):
        """遅延してマウス位置をチェック"""
        try:
            cursor_pos = QCursor.pos()
            mouse_on_any_ring = False
            
            # メインランチャー上にマウスがあるかチェック
            main_rect = self.geometry()
            if main_rect.contains(cursor_pos):
                mouse_on_any_ring = True
                print("��️ マウスがメインランチャー上に戻っています")
            
            # 第一階層リング上にマウスがあるかチェック
            if not mouse_on_any_ring and hasattr(self, 'level1_rings'):
                for ring in self.level1_rings.values():
                    if ring and ring.isVisible() and ring.geometry().contains(cursor_pos):
                        mouse_on_any_ring = True
                        print(f"🔵 マウスが第一階層リング上にあります")
                        break
            
            # 第二階層リング上にマウスがあるかチェック
            if not mouse_on_any_ring and hasattr(self, 'level2_rings'):
                for ring in self.level2_rings.values():
                    if ring and ring.isVisible() and ring.geometry().contains(cursor_pos):
                        mouse_on_any_ring = True
                        print(f"🔶 マウスが第二階層リング上にあります")
                        break
            
            # マウスがどのリング上にもない場合のみ非表示
            if not mouse_on_any_ring:
                print("🎆 マウスが全リングから離れたため非表示")
                self.hide_all_hierarchy_rings()
            else:
                print("🛡️ マウスがリング上にあるため維持")
                
        except Exception as e:
            print(f"⚠️ マウス位置チェックエラー: {e}")
            # エラーの場合は安全のため非表示
            self.hide_all_hierarchy_rings()

    def create_icon_pixmap(self, shortcut_index, size):
        """アイコンPixmapを作成（共通ロジック）"""
        try:
            shortcuts = self.shortcut_manager.get_all_shortcuts()
            shortcut_info = shortcuts.get(shortcut_index, {'icon_path': None, 'text_file': None, 'is_valid': True})
            
            emoji = self.shortcut_manager.get_shortcut_emoji(shortcut_index)
            preferred_type = self.shortcut_manager.get_preferred_icon_type(shortcut_index)
            
            if preferred_type == "emoji" and emoji:
                # 絵文字アイコンを作成
                pixmap = QPixmap(size, size)
                pixmap.fill(Qt.transparent)
                from PyQt5.QtGui import QPainter, QPen, QFont
                painter = QPainter(pixmap)
                painter.setFont(QFont("Arial", 32, QFont.Bold))
                painter.drawText(pixmap.rect(), Qt.AlignCenter, emoji)
                painter.end()
                return pixmap
            elif preferred_type == "image" and shortcut_info['icon_path'] and os.path.exists(shortcut_info['icon_path']):
                # 画像アイコンを読み込み
                return QPixmap(shortcut_info['icon_path'])
            else:
                # デフォルトアイコンを作成
                pixmap = QPixmap(size, size)
                pixmap.fill(Qt.gray)
                from PyQt5.QtGui import QPainter, QPen, QFont
                painter = QPainter(pixmap)
                painter.setPen(QPen(Qt.white))
                painter.setFont(QFont("Arial", 14, QFont.Bold))
                painter.drawText(pixmap.rect(), Qt.AlignCenter, str(shortcut_index))
                painter.end()
                return pixmap
        except Exception as e:
            # エラー時のデフォルト
            pixmap = QPixmap(size, size)
            pixmap.fill(Qt.gray)
            return pixmap




class RingCommandManager:
    """リングコマンドマネージャー - 統合管理クラス"""
    
    def __init__(self, main_window):
        try:
            # logging.info("RingCommandManager 初期化開始")
            self.main_window = main_window
            
            # ShortcutManagerのインスタンスを作成または共有
            if hasattr(main_window, 'shortcut_manager') and main_window.shortcut_manager:
                self.shortcut_manager = main_window.shortcut_manager
                # logging.info("🔗 既存のShortcutManagerを使用")
            else:
                self.shortcut_manager = ShortcutManager()
                
            # フォーカス制御設定を読み込み
            self.no_focus_mode = self.load_no_focus_setting()
            
            # 縮小ランチャー（常駐ボタン）を作成
            self.floating_button = FloatingCenterButton(None, main_window)
            self.floating_button.clicked.connect(self.show_ring_launcher)
            self.floating_button.toggle_main_window.connect(self.toggle_main_window)
            
            # リングランチャーを作成
            self.ring_launcher = RingCommandLauncher(None, self.shortcut_manager, main_window)
            
            # 縮小ランチャーの参照を設定
            self.ring_launcher.floating_button = self.floating_button
            
            # 選択シグナルを接続
            self.ring_launcher.memo_selected.connect(self.handle_memo_selected)
            
            # 初期状態では縮小ランチャーのみ表示（自動表示設定が有効な場合のみ）
            if getattr(self.main_window, 'ring_launcher_auto_show', False):
                self.floating_button.show()
            
            # logging.info("✅ RingCommandManager 初期化完了")
        except Exception as e:
            logging.error(f"リングコマンドマネージャー初期化エラー: {e}")
            import traceback
            traceback.print_exc()
    
    def cleanup(self):
        """RingCommandManagerのクリーンアップ処理"""
        try:
            # logging.info("RingCommandManager クリーンアップ開始")
            
            # リングランチャーを非表示にする
            if hasattr(self, 'ring_launcher') and self.ring_launcher:
                self.ring_launcher.hide()
                self.ring_launcher.close()
            
            # 縮小ランチャーを非表示にする
            if hasattr(self, 'floating_button') and self.floating_button:
                self.floating_button.hide()
                self.floating_button.close()
            
            # logging.info("RingCommandManager クリーンアップ完了")
        except Exception as e:
            logging.error(f"RingCommandManager クリーンアップエラー: {e}")
    
    def load_no_focus_setting(self):
        """フォーカス制御設定を読み込み"""
        try:
            from app_utils import get_config_file_path
            config_file = get_config_file_path()
            config = configparser.ConfigParser()
            config.read(config_file, encoding='utf-8')
            
            if config.has_section('Settings') and config.has_option('Settings', 'ring_launcher_no_focus'):
                return config.getboolean('Settings', 'ring_launcher_no_focus')
            else:
                return True  # デフォルトはフォーカス無効
        except Exception as e:
            logging.error(f"フォーカス制御設定読み込みエラー: {e}")
            return True

    def get_shortcut_manager(self):
        """ShortcutManagerを取得"""
        return self.shortcut_manager
    
    def show_settings_dialog(self):
        """ショートカット設定ダイアログを表示"""
        dialog = ShortcutSettingsDialog(self.main_window, self.shortcut_manager)
        dialog.shortcuts_updated.connect(self.ring_launcher.reload_shortcuts)
        dialog.exec_()
    
    def toggle_main_window(self):
        """メインウィンドウの表示/非表示を切り替え"""
        if self.main_window:
            if self.main_window.isVisible():
                self.main_window.hide()
            else:
                self.main_window.show()
                self.main_window.raise_()
                if not self.no_focus_mode:
                    self.main_window.activateWindow()
    
    def show_ring_launcher(self):
        """リングランチャーを表示"""
        # ボタンの位置を取得
        button_pos = self.floating_button.pos()
        button_center = QPoint(
            button_pos.x() + self.floating_button.width() // 2,
            button_pos.y() + self.floating_button.height() // 2
        )
        
        # リングランチャーを表示
        self.ring_launcher.show_at_position(button_center)
    
    def handle_memo_selected(self, memo_file):
        """メモファイルが選択された時の処理"""
        # メモファイルが空の場合は無視（初期化時の誤動作防止）
        if not memo_file:
            return
            
        # メモ選択処理
        
        try:
            # 相対パスを絶対パスに変換
            if not os.path.isabs(memo_file):
                memo_file = os.path.join(os.path.dirname(__file__), memo_file)
            
            if os.path.exists(memo_file):
                # ファイル名のみを取得
                filename = os.path.basename(memo_file)
                filename_without_ext = os.path.splitext(filename)[0]
                
                
                # text_listが存在する場合はリストから選択
                if hasattr(self.main_window, 'text_list') and self.main_window.text_list is not None:
                    # リストでファイルを検索
                    for i in range(self.main_window.text_list.count()):
                        item = self.main_window.text_list.item(i)
                        if item:
                            item_text = item.text()
                            # 完全一致または拡張子なしで一致
                            if item_text == filename or item_text == filename_without_ext:
                                self.main_window.text_list.setCurrentItem(item)
                                
                                # on_text_selectedメソッドを呼び出し
                                if hasattr(self.main_window, 'on_text_selected'):
                                    self.main_window.on_text_selected(item)
                                break
                    else:
                        # リストに見つからない場合は直接load_textを呼び出し
                        if hasattr(self.main_window, 'load_text'):
                            self.main_window.load_text(filename_without_ext)
                else:
                    # text_listが存在しない場合は直接load_textを呼び出し
                    if hasattr(self.main_window, 'load_text'):
                        self.main_window.load_text(filename_without_ext)
                
                # メインウィンドウをアクティブ化
                self.main_window.show()
                self.main_window.raise_()
                # フォーカス制御設定に基づいてactivateWindow()を呼び出し
                if not self.no_focus_mode:
                    self.main_window.activateWindow()
                
                # メモファイル処理完了
            else:
                QMessageBox.warning(
                    self.main_window, "ファイル未発見",
                    f"メモファイルが見つかりません:\n{memo_file}"
                )
        except Exception as e:
            logging.error(f"リングコマンドからのメモ読み込みエラー: {e}")
            QMessageBox.critical(
                self.main_window, "エラー",
                f"メモファイルの読み込みに失敗しました:\n{e}"
            )
    
    def hide_floating_button(self):
        """常駐ボタンを非表示"""
        self.floating_button.hide()
    
    def show_floating_button(self):
        """常駐ボタンを表示"""
        self.floating_button.show()
    
    def load_no_focus_setting(self):
        """フォーカス制御設定を読み込み"""
        try:
            from app_utils import get_config_file_path
            config_file = get_config_file_path()
            config = configparser.ConfigParser()
            config.read(config_file, encoding='utf-8')
            
            if config.has_section('Settings') and config.has_option('Settings', 'ring_launcher_no_focus'):
                return config.getboolean('Settings', 'ring_launcher_no_focus')
            else:
                return True  # デフォルトはフォーカス無効
        except Exception as e:
            logging.error(f"フォーカス制御設定読み込みエラー: {e}")
            return True  # エラー時はフォーカス無効


if __name__ == "__main__":
    """テスト用のメイン関数"""
    import sys
    
    app = QApplication(sys.argv)
    
    # テスト用のマネージャー
    class DummyMainWindow:
        def show(self): pass
        def raise_(self): pass
        def activateWindow(self): pass
    
    dummy_main = DummyMainWindow()
    manager = RingCommandManager(dummy_main)
    
    
    sys.exit(app.exec_())