File size: 5,603 Bytes
5e99d47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import tkinter as tk
from tkinter import scrolledtext
from tkinterdnd2 import DND_FILES, TkinterDnD
import re
import os

# --- Conversion Logic ---

def convert_md_to_smf(markdown_text):
    """

    Converts a Markdown string to an SMF-compatible BBCode string.

    

    The order of replacements is important to avoid conflicts.

    """
    smf_text = markdown_text

    # 1. Block elements (Code blocks, blockquotes)
    # Code blocks (```...```) - DOTALL flag allows `.` to match newlines
    smf_text = re.sub(r'```(.*?)```', r'[code]\1[/code]', smf_text, flags=re.DOTALL)
    
    # Blockquotes (>)
    smf_text = re.sub(r'^\> (.*)', r'[quote]\1[/quote]', smf_text, flags=re.MULTILINE)

    # 2. Headings (must be at the start of a line)
    smf_text = re.sub(r'^# (.*)', r'[size=18pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
    smf_text = re.sub(r'^## (.*)', r'[size=16pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
    smf_text = re.sub(r'^### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
    smf_text = re.sub(r'^#### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)

    # 3. Lists (must be at the start of a line)
    # This is a simplified conversion. It doesn't wrap the whole block in [list] tags,
    # as that requires more complex state management, but it's often sufficient.
    smf_text = re.sub(r'^\* (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
    smf_text = re.sub(r'^- (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
    smf_text = re.sub(r'^\d+\. (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE) # Note: SMF may need [list type=decimal] wrapper

    # 4. Images and Links
    smf_text = re.sub(r'!\[.*?\]\((.*?)\)', r'[img]\1[/img]', smf_text) # Images
    smf_text = re.sub(r'\[(.*?)\]\((.*?)\)', r'[url=\2]\1[/url]', smf_text) # Links

    # 5. Inline formatting
    smf_text = re.sub(r'\*\*(.*?)\*\*', r'[b]\1[/b]', smf_text) # Bold (**)
    smf_text = re.sub(r'__(.*?)__', r'[b]\1[/b]', smf_text) # Bold (__)
    smf_text = re.sub(r'\*(.*?)\*', r'[i]\1[/i]', smf_text) # Italic (*)
    smf_text = re.sub(r'_(.*?)_', r'[i]\1[/i]', smf_text) # Italic (_)
    smf_text = re.sub(r'~~(.*?)~~', r'[s]\1[/s]', smf_text) # Strikethrough
    smf_text = re.sub(r'`(.*?)`', r'[code]\1[/code]', smf_text) # Inline code

    # 6. Horizontal Rule
    smf_text = re.sub(r'^\s*---\s*$', '\n[hr]\n', smf_text, flags=re.MULTILINE)

    return smf_text

# --- GUI Application ---

class App(TkinterDnD.Tk):
    def __init__(self):
        super().__init__()
        self.title("Markdown to SMF Converter")
        self.geometry("500x400")

        # Main frame
        main_frame = tk.Frame(self)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Label for instructions
        self.drop_label = tk.Label(
            main_frame,
            text="Drag and Drop .md files here",
            font=("Arial", 14),
            bg="lightblue",
            relief="solid",
            bd=2
        )
        self.drop_label.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        # Scrolled text for status logs
        self.status_log = scrolledtext.ScrolledText(
            main_frame,
            height=10,
            state='disabled' # Make it read-only
        )
        self.status_log.pack(fill=tk.BOTH, expand=True)

        # Register the drop zone
        self.drop_target_register(DND_FILES)
        self.dnd_bind('<<Drop>>', self.handle_drop)
        
        self.log("Ready to convert files.")

    def log(self, message):
        """Adds a message to the status log."""
        self.status_log.config(state='normal')
        self.status_log.insert(tk.END, message + "\n")
        self.status_log.see(tk.END) # Auto-scroll to the bottom
        self.status_log.config(state='disabled')

    def handle_drop(self, event):
        """Handles the file drop event."""
        # The event.data is a string of file paths, sometimes in curly braces
        # We clean it up and split it into a list of individual paths
        file_paths = self.tk.splitlist(event.data)
        
        self.log("\n--- New Drop Detected ---")
        
        for file_path in file_paths:
            if file_path.lower().endswith('.md'):
                self.process_file(file_path)
            else:
                self.log(f"Skipped: '{os.path.basename(file_path)}' is not a .md file.")
        
        self.log("--- All files processed ---")

    def process_file(self, file_path):
        """Reads, converts, and saves a single markdown file."""
        filename = os.path.basename(file_path)
        self.log(f"Processing: '{filename}'...")

        try:
            # Read the markdown file
            with open(file_path, 'r', encoding='utf-8') as f:
                markdown_content = f.read()

            # Convert the content
            smf_content = convert_md_to_smf(markdown_content)

            # Define the output path
            base_name = os.path.splitext(file_path)[0]
            output_path = f"{base_name}_smf.txt"

            # Save the converted content
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write(smf_content)
            
            self.log(f"Success! Saved to: '{os.path.basename(output_path)}'")

        except Exception as e:
            self.log(f"Error processing '{filename}': {e}")

# --- Main Execution ---

if __name__ == "__main__":
    app = App()
    app.mainloop()