Option Explicit

'===========================================================
' 部品_シート名変更 / RenameSheet
'   ・指定したシート（Worksheet）の名前を、指定した名前に変更する
'     → Renames the specified sheet (Worksheet) to the specified name.
'
'   ・シートコピーの直後に、コピーしたシートへ、名前を付けるときなどに使う
'     → Used e.g. to name a sheet right after copying it.
'
'   ※ この部品は Sub（プロシージャ）ではなく「Function（関数）」で、成功／失敗を Boolean で返す
'     → ※ This component is a Function (not a Sub) that returns success/failure as a Boolean.
'===========================================================

'===========================================================
' ■区分 / Classification
'   ・基礎区分：土台 ／ 用途区分：処理 ／ 操作対象区分：シート
'     → Base: foundation / Purpose: process / Target: sheet
'
' ■バージョン / Version
'   ・Version 1.0（2026-10-30）
'===========================================================

'===========================================================
' ■URL（WordPress部品ページ）
'   https://feelcodelab.com/vba_parts/renamesheet/
'===========================================================

'===========================================================
' ■前提条件（AI誤動作防止の最重要項目） / Preconditions
'   ・【最重要】この部品は Function（関数）であり、戻り値（Boolean）を返す
'     → 名前を変更できた場合は True、実行時にできなかった場合は False（この場合は、エラーでは止まらない）
'     → 呼び出し側は、必ず戻り値を確認してから後続処理を行い、メッセージも呼び出し側で表示する
'       （False のときは、MsgBox のあとに Exit Sub まで書く）
'     → [MOST IMPORTANT] This component is a Function that returns a Boolean: True if it worked, False if it
'        failed at run time (no error is raised then). The caller must check the return value before the
'        following processing and show any message itself (write Exit Sub after the MsgBox).
'
'   ・【最重要】名前を変更するシート（vWs）は、Worksheet で渡す
'     → シートコピーの直後は、ブックの ActiveSheet が、コピーしたシートになる（例：wbDst.ActiveSheet）
'     → [MOST IMPORTANT] Pass the sheet as a Worksheet (right after CopySheet it is wbDst.ActiveSheet).
'
'   ・新しい名前（vNewName）が、Excel で使えない名前の場合は、False ではなく、原因を示すエラーで止まる（1020）
'     - 空白だけ・空文字
'     - 31 文字を超える
'     - 次の文字を含む：  :  \  /  ?  *  [  ]
'     - 先頭または末尾が、アポストロフィ（'）
'     - 「History」（Excel が、予約している名前。大文字・小文字は区別しない）
'     → A name Excel cannot use stops with an error naming the cause (1020).
'
'   ・同じブックに、同じ名前のシートが、すでにある場合は、変更できず、False を返す
'     → 呼び出し側が、先に、シート存在チェックで確認する（迷ったら分ける。例 2）
'     → 大文字・小文字は、区別されない（"Data" と "data" は、同じ名前）
'     → 今の名前と、同じ名前を指定した場合は、変更せずに True を返す
'     → If a sheet with the same name already exists, the rename fails and False is returned; check with
'        SheetExists beforehand. Names are case-insensitive.
'
'   ・ブックの構成が保護されているときなど、実行時にできなかった場合は False を返す（原因は、イミディエイトウィンドウに出る）
'     → If the workbook structure is protected or the rename fails otherwise, False is returned (the cause is printed).
'
'   ・シート名を変えると、そのシートを指している数式（'旧名'!A1）は、Excel が、自動で、新しい名前に直す。
'     しかし、VBA のコードの中の、シート名の文字列（Worksheets("旧名")）は、直らない
'     → Formulas that refer to the sheet are updated by Excel, but sheet-name strings in VBA code are not.
'
'   ・設定の誤りは、False ではなく、原因を示すエラーで止まる（Err.Raise）
'     - シート（vWs）が Nothing（vbObjectError + 1006）
'     - 新しい名前が、使えない名前（vbObjectError + 1020）
'     → Setting mistakes stop with an error naming the cause.
'
'   ・Offset / Find / Resize / With は禁止
'     → Offset, Find, Resize, and With statements are prohibited.
'===========================================================

'===========================================================
' ■依存関係（部品名明記） / Component Dependencies
'   ・他の部品を参照しない（土台）
'     → References no other component (foundation).
'
'   ・組み合わせ：シートコピー → シート名変更
'     （コピーした直後の、アクティブなシートに、名前を付ける）
'     → Typical combination: [CopySheet] -> [RenameSheet]
'
'   ・組み合わせ：シート存在チェック → シート名変更
'     （同じ名前のシートが、すでにないかを、先に確認する）
'     → Typical combination: [SheetExists] -> [RenameSheet]
'
'===========================================================

'===========================================================
' ■依存関係（シート・表） / Sheet Dependencies
'   ・対象のシートは、Worksheet で渡す（ブックは、シートが持っているので、ブックの引数は不要）
'     → Pass the sheet as a Worksheet; no workbook argument is needed because the sheet knows its workbook.
'
'   ・同じブックに、同じ名前のシートが、すでにある場合は、変更できず、False を返す（先に、シート存在チェックで確認する）
'     → If a sheet with the same name already exists in the workbook, the rename fails and False is returned; check with [SheetExists] first.
'===========================================================

'===========================================================
' ■技術解説 / Technical Notes
'
'   ・Worksheet で受け取る理由
'       → シートそのものを操作する部品は、Worksheet で受け取る（ルール 1-1）。シートは、自分のブックを持っているので、ブックの引数が要らない。
'       → A sheet-operating component receives a Worksheet; no workbook argument is needed.
'
'   ・使えない名前を、事前に確認して、エラーで止める理由
'       → 使えない名前は、「指定の誤り」であって、「実行してみてできなかった」ではない。False で返すと、同名のシートがある、ブックが保護されている、などの失敗と、区別がつかず、原因が分かりにくくなる。
'       → An unusable name is a setting mistake, not a run-time failure; returning False would hide the cause.
'
'   ・同名のシートの確認を、部品の中に入れない理由
'       → 確認が必要かどうかは、呼び出し側のコードによる。迷ったら、分ける。同名のシートがあって、変更に失敗した場合は、False を返し、原因（同じ名前があること）は、Debug.Print に出る。
'       → Whether a duplicate check is needed depends on the caller; when in doubt, split. A duplicate makes the rename fail with False and a printed cause.
'
'   ・確認を On Error GoTo より前に置く理由
'       → On Error GoTo ErrHandler の有効な範囲でエラーを出すと、False に変えられてしまい、原因が伝わらない。
'       → Raising inside the On Error range would turn it into False and hide the cause.
'
'   ・Offset / Find / Resize / With を禁止する理由
'       → 生成AIが誤って不正な範囲操作を行う可能性が高く、行数可変の処理で誤動作を引き起こしやすいため。
'       → These statements often cause misaligned or ambiguous range operations when generated by AI.
'===========================================================

'---------------------------------------------
' ■引数（型＋意味） / Arguments
'   vWs         : 名前を変更するシート（Worksheet）
'                  → The sheet to rename (Worksheet).
'
'   vNewName    : 新しい名前（String）。31 文字以内で、使えない文字を含まないこと
'                  → The new name (String), up to 31 characters without forbidden characters.
'---------------------------------------------

'---------------------------------------------
' ■戻り値（型＋意味） / Return Value
'   Boolean : 名前を変更できた場合は True、実行時にできなかった場合は False
'             → True if renamed, False if it failed at run time.
'---------------------------------------------

'---------------------------------------------
' ■エラー / Errors (Err.Raise)
'   ・vbObjectError + 1006：シート（vWs）が Nothing
'   ・vbObjectError + 1020：新しい名前（vNewName）が、Excel で使えない名前（空白・31 文字超・使えない文字・先頭か末尾が ' ・History）
'---------------------------------------------

'===========================================================
' ■禁止事項（AI向け） / Prohibitions
'   ・Function を Sub（プロシージャ）に変更することの禁止
'     → Do not convert this Function into a Sub; the Boolean return value is essential.
'
'   ・戻り値（Boolean）を確認せずに後続処理を行うことの禁止
'     → Do not proceed without checking the Boolean return value.
'
'   ・Set していない Worksheet 変数を渡すことの禁止
'     → Do not pass a Worksheet variable that has not been set.
'
'   ・同じ名前のシートが、あるかを確認せずに、名前を変更することの禁止（必要な場合は、シート存在チェックで確認する）
'     → Do not rename without checking for a duplicate when it matters.
'
'   ・名前を変更した後も、古い名前の文字列（Worksheets("旧名")）を、使い続けることの禁止
'     → Do not keep using the old name string after renaming.
'
'   ・Offset禁止 / Find禁止 / Resize禁止 / With禁止
'     → Offset, Find, Resize, and With blocks are prohibited.
'===========================================================

'===========================================================
' ■使用例（サンプルコード） / Example
'   ◆例1：シートコピーの直後に、名前を付ける / Example 1: name a sheet right after copying it◆
'   If 部品_シートコピー(wsTemplate, wsLast) = False Then MsgBox "コピーに失敗しました": Exit Sub
'   If 部品_シート名変更(wbDst.ActiveSheet, "2026年10月") = False Then
'       MsgBox "名前を変更できませんでした"
'       Exit Sub
'   End If
'
'   ◆例2：同じ名前のシートが、すでにあるかを、先に確認する / Example 2: check for a duplicate first◆
'   If 部品_シート存在チェック(wbDst, "2026年10月") Then
'       MsgBox "「2026年10月」は、すでにあります"
'       Exit Sub
'   End If
'   If 部品_シート名変更(wbDst.ActiveSheet, "2026年10月") = False Then MsgBox "名前を変更できませんでした": Exit Sub
'
'   ※ シートが Nothing の場合や、使えない名前の場合は、False ではなく、原因を示すエラーで止まる
'===========================================================

'===========================================================
' ■ラッパー関数（日本語名） / Wrapper Function
'   ・辞書の日本語名で呼び出すための窓口
'     → Entry point callable by the Japanese dictionary name.
'
'   ・内部では英語名の本体処理を呼び出し、結果（Boolean）をそのまま返す
'     → Internally calls the English-named core function and returns its result (Boolean) as it is.
'===========================================================
Function 部品_シート名変更( _
        ByVal vWs As Worksheet, _
        ByVal vNewName As String) As Boolean

    '本体処理（英語名）を呼び出し、結果をそのまま返す / Call core function and return its result
    部品_シート名変更 = f_RenameSheet(vWs, vNewName)

End Function

'===========================================================
' ■本体処理（英語名） / Core Function
'   ・シートの名前を、指定した名前に変更する
'     → Renames the sheet to the specified name.
'===========================================================
Function f_RenameSheet( _
        ByVal vWs As Worksheet, _
        ByVal vNewName As String) As Boolean

    Dim badChars As Variant
    Dim i As Long

    f_RenameSheet = False

    '---------------------------------------------
    ' ■シート・新しい名前を確認（誤りの場合はエラーで止める）
    '   ※ On Error GoTo より前に置く（Err.Raise を False に変えず、呼び出し側へ届けるため）
    '   / Check the sheet and the new name (stop with an error if wrong);
    '     placed before On Error GoTo so that Err.Raise reaches the caller
    '---------------------------------------------
    If vWs Is Nothing Then
        Err.Raise vbObjectError + 1006, "f_RenameSheet", _
                  "シート（vWs）が設定されていません（Nothing）。"
    End If

    If Len(Trim$(vNewName)) = 0 Then
        Err.Raise vbObjectError + 1020, "f_RenameSheet", _
                  "新しい名前（vNewName）が空です。"
    End If

    If Len(vNewName) > 31 Then
        Err.Raise vbObjectError + 1020, "f_RenameSheet", _
                  "新しい名前は、31 文字以内にしてください（現在は " & Len(vNewName) & " 文字：" & vNewName & "）。"
    End If

    badChars = Array(":", "\", "/", "?", "*", "[", "]")
    For i = LBound(badChars) To UBound(badChars)
        If InStr(vNewName, badChars(i)) > 0 Then
            Err.Raise vbObjectError + 1020, "f_RenameSheet", _
                      "新しい名前に、使えない文字「" & badChars(i) & "」が含まれています（" & vNewName & "）。"
        End If
    Next i

    If Left$(vNewName, 1) = "'" Or Right$(vNewName, 1) = "'" Then
        Err.Raise vbObjectError + 1020, "f_RenameSheet", _
                  "新しい名前の、先頭または末尾に、アポストロフィ（'）は使えません（" & vNewName & "）。"
    End If

    If StrComp(vNewName, "History", vbTextCompare) = 0 Then
        Err.Raise vbObjectError + 1020, "f_RenameSheet", _
                  "「History」は、Excel が予約している名前のため、使えません。"
    End If

    '---------------------------------------------
    ' ■エラー時は ErrHandler へ（False を返す） / On error, jump to ErrHandler (returns False)
    '---------------------------------------------
    On Error GoTo ErrHandler

    '---------------------------------------------
    ' ■名前を変更（同じ名前のシートがあるときは、ここでエラーになる）
    '   / Rename (an error occurs here if a sheet with the same name exists)
    '---------------------------------------------
    vWs.Name = vNewName

    f_RenameSheet = True
    Exit Function

ErrHandler:
    Debug.Print "f_RenameSheet: " & Err.Number & " " & Err.Description
    f_RenameSheet = False

End Function

