[QT]Rich Text Editor(2)

๐Ÿ‘‰์•„๋ž˜๋Š” QT6๋ฒ„์ „ ํ”„๋กœ๊ทธ๋žจ ์†Œ์Šค ์ฝ”๋“œ ์ž…๋‹ˆ๋‹ค.
Below is the QT6 version program source code.

๐Ÿ‘‰์•ฝ๊ฐ„์˜ ๊ธฐ๋Šฅ์ด ๋” ์ถ”๊ฐ€ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
There are a few more features added.

1.main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
/*
 * fontAwesome : ํฐํŠธ๋‹ค์šด๋กœ๋“œ / fontdownload
 * https://fontawesome.com/search?q=pdf&o=r
 *
 *
 *
*/

2.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTextCharFormat> // QTextCharFormat ์‚ฌ์šฉ์„ ์œ„ํ•ด ํฌํ•จ / Included for use with QTextCharFormat
#include <QFileInfo>       // QFileInfo ์‚ฌ์šฉ์„ ์œ„ํ•ด ํฌํ•จ / Included for using QFileInfo
// ๋“œ๋ž˜๊ทธ ์ด๋ฏธ์ง€ / drag image
#include <QPoint>          // ๋งˆ์šฐ์Šค ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ ์œ„์น˜ ์ €์žฅ์„ ์œ„ํ•ด ์ถ”๊ฐ€ / Added to save the mouse drag start position

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    // QObject์˜ ์ด๋ฒคํŠธ ํ•„ํ„ฐ ํ•จ์ˆ˜๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ฉ๋‹ˆ๋‹ค. / Override QObject's event filter function.
    bool eventFilter(QObject *obj, QEvent *event) override;

private slots:

    // == ํŒŒ์ผ ๊ด€๋ จ ์Šฌ๋กฏ / File-related slots ==
    bool saveFile(const QString &path);
    void save(); // Save
    void saveAs(); // Save As
    void actionNew();  // ์ƒˆ ํŒŒ์ผ / new file
    void actionOpen(); // ํŒŒ์ผ ์—ด๊ธฐ / open
    void actionExit(); // ์ข…๋ฃŒ / Exit

    // == ํŽธ์ง‘ / Edit ==
    void actionFind();     // ์ฐพ๊ธฐ ๊ธฐ๋Šฅ / find
    void actionReplace();  // ๋ฐ”๊พธ๊ธฐ ๊ธฐ๋Šฅ / replace

    // == ๋ทฐ ==
    void actionZoomIn();  // ์คŒ์ธ / zoom in
    void actionZoomOut(); // ์คŒ์•„์›ƒ / zoom out
    void actionZoomReset(); // ์คŒ ์ดˆ๊ธฐํ™” / zoom reset

    // == ์ด๋ฏธ์ง€ / Image ==
    void actionInsertImage(); // ์ด๋ฏธ์ง€ ์‚ฝ์ž… / insert image

    // == Export ==
    void actionExportBase64(); // Export As Base64
    void actionExportBase64WithImages(); // Export As Base64 for image
    void actionExportPdf(); //Export As Pdf

    // == Import ==
    void actionImportBase64(); // Import As Base64

    // == ํ…์ŠคํŠธ ๊ธฐ๋Šฅ / Text function ==
    void actionBold(bool checked);
    void actionItalic(bool checked);
    void actionUnderline(bool checked);
    void actionStrike(bool checked);

    // -- ์ž๋™์—ฐ๊ฒฐ์„ค์ •(์ด์ „์— ์‚ฌ์šฉํ•˜๋˜ ๋ฐฉ์‹์œผ๋กœ ๊ฒฝ๊ณ  ๋ฐœ์ƒ) --
    // -- Automatic connection setup (warning occurs in the previous method) --
    // void on_actionColor_triggered();
    // void on_actionFont_triggered();

    // -- ์ˆ˜๋™์—ฐ๊ฒฐ --
    void actionColor(); // ์ƒ‰์ƒ ์„ ํƒ์€ ํ† ๊ธ€์ด ์•„๋‹ˆ๋ฏ€๋กœ bool ์ธ์ž๊ฐ€ ์—†์Œ / Color selection is not a toggle, so there is no bool argument.
    void actionFont();  // ํฐํŠธ ์„ ํƒ์€ ํ† ๊ธ€์ด ์•„๋‹ˆ๋ฏ€๋กœ bool ์ธ์ž๊ฐ€ ์—†์Œ / Font selection is not a toggle, so there is no bool argument.

    // ํ˜„์žฌ ์ปค์„œ ์œ„์น˜์˜ ์„œ์‹ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๋Š” ์Šฌ๋กฏ
    // Slot to detect format changes at the current cursor position
    void updateFormat(const QTextCharFormat &format);


    // == ๋„์›€๋ง ๊ธฐ๋Šฅ / help ==
    void actionHelp();  // ๋„์›€๋ง / help
    void actionAbout(); // ์ •๋ณด / about


private:
    Ui::MainWindow *ui;
    QString currentFilePath; // ํ˜„์žฌ ํŒŒ์ผ์„ ์ถ”์ ํ•˜๋Š” ๋ณ€์ˆ˜ ์ถ”๊ฐ€ / Add a variable to track the current file

    // == ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ฆˆ๋ฅผ ์œ„ํ•œ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ / Member variables for image resizing ==
    bool m_isResizing = false;
    QPoint m_dragStartPos;
    int m_originalImageWidth = 0;
    int m_originalImageHeight = 0;


    // == ์คŒ์ธ/์คŒ์•„์›ƒ ์„ค์ •์„ ์œ„ํ•œ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ / Member variables for zoom in/zoom out settings ==
    // ํฐํŠธ ํฌ๊ธฐ ์ดˆ๊ธฐํ™”์šฉ ๋ณ€์ˆ˜ ์ถ”๊ฐ€(๋ณ€ํ™”ํ•˜๋Š” ํ˜„์žฌ์ƒํƒœ ์ €์žฅ)
    // Add a variable to initialize the font size (save the current state as it changes)
    int m_currentFontSize = 10;

    // ๊ธฐ๋ณธ ํฐํŠธ ํฌ๊ธฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฉค๋ฒ„ ๋ณ€์ˆ˜(๋ฆฌ์…‹ํ• ๋•Œ์˜ ๊ฐ’)
    // Member variable that stores the default font size (value when reset)
    int m_defaultPointSize = 10; // ์ดˆ๊ธฐ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ • / Set factory defaults

};

#endif // MAINWINDOW_H

3.mainwindow.cpp

#include "mainwindow.h"
#include "./ui_mainwindow.h"

// ์ฝ”๋“œ ์ถ”๊ฐ€ / Add code
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QTextCharFormat>
#include <QColorDialog> // ์ƒ‰์ƒ ์„ ํƒ ๋‹ค์ด์–ผ๋กœ๊ทธ / Color selection dialog
#include <QFontDialog>  // ํฐํŠธ ์„ ํƒ ๋‹ค์ด์–ผ๋กœ๊ทธ / Font selection dialog

#include <QDir>         // QDir ์‚ฌ์šฉ์„ ์œ„ํ•ด ์ถ”๊ฐ€ / Added for QDir use
#include <QFileInfo>    // QFileInfo ์‚ฌ์šฉ์„ ์œ„ํ•ด ์ถ”๊ฐ€ / Added for using QFileInfo
#include <QUrl>         // ์ด๋ฏธ์ง€ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ URL๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š” / Needed to convert image file path to URL
#include <QImage>       // ์ด๋ฏธ์ง€ ๋กœ๋“œ๋ฅผ ์œ„ํ•ด ํ•„์š” / Required for image loading
#include <QTextDocument> // QTextDocument::addResource ์‚ฌ์šฉ์„ ์œ„ํ•ด ํ•„์š” / Required for using QTextDocument::addResource
#include <QTextImageFormat> // ์ด๋ฏธ์ง€ ์‚ฝ์ž… ํฌ๋งท ์ง€์ •์„ ์œ„ํ•ด ํ•„์š” / Required to specify image insertion format
#include <QTextCursor>
#include <QInputDialog> // ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์œ„ํ•ด ์ถ”๊ฐ€ / Added for user input dialog
#include <QMouseEvent>  // ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์ถ”๊ฐ€ / Added for mouse event handling
#include <QtMath>       // qMax, qMin ๋“ฑ์„ ์œ„ํ•ด ํ•„์š” (์—ฌ๊ธฐ์„œ๋Š” qMax ์‚ฌ์šฉ) / Required for qMax, qMin, etc. (qMax is used here)

// export as base64
#include <QByteArray>
#include <QClipboard>
#include <QApplication>

// export base64 with images
#include <QRegularExpression>
#include <QRegularExpressionMatchIterator>
#include <QBuffer>

// pdf
#include <QPrinter>
#include <QFileDialog>
#include <QMessageBox>
#include <QPdfWriter>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // ๋ฉ”์ธ ์œˆ๋„์šฐ์˜ ์ค‘์•™ ์˜์—ญ์— textEdit ์œ„์ ฏ์„ ๋ฐฐ์น˜
    // Place the textEdit widget in the center area of โ€‹โ€‹the main window
    setCentralWidget(ui->textEdit);

    // == ํŒŒ์ผ ๊ธฐ๋Šฅ / File functon ==
    connect(ui->actionSave, &QAction::triggered, this, &MainWindow::save);  // save
    connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::saveAs); // saveas
    connect(ui->actionExit, &QAction::triggered, this, &MainWindow::actionExit); // exit
    connect(ui->actionNew, &QAction::triggered, this, &MainWindow::actionNew); // new
    connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::actionOpen);// open
    connect(ui->actionExport_As_Base64, &QAction::triggered, this, &MainWindow::actionExportBase64); //save as base64
    connect(ui->actionImport_Base64, &QAction::triggered, this, &MainWindow::actionImportBase64); //Import base64
    connect(ui->actionExport_Base64_With_Images, &QAction::triggered, this, &MainWindow::actionExportBase64WithImages); //Import base64
    connect(ui->actionExport_As_Pdf, &QAction::triggered, this, &MainWindow::actionExportPdf); //Export As Pdf


    // == ํŽธ์ง‘ ๊ธฐ๋Šฅ ๊ตฌํ˜„ / Implementing editing functions ==
    connect(ui->actionCopy, &QAction::triggered, ui->textEdit, &QTextEdit::copy); //copy
    connect(ui->actionCut, &QAction::triggered, ui->textEdit, &QTextEdit::cut); // cut
    connect(ui->actionPaste, &QAction::triggered, ui->textEdit, &QTextEdit::paste); // past
    connect(ui->actionSelect_All, &QAction::triggered, ui->textEdit, &QTextEdit::selectAll); // select all
    connect(ui->actionUndo, &QAction::triggered, ui->textEdit, &QTextEdit::undo); // undo
    connect(ui->actionRedo, &QAction::triggered, ui->textEdit, &QTextEdit::redo); // redo

    // == ๋ทฐ / View ==
    connect(ui->actionZoom_in, &QAction::triggered, this, &MainWindow::actionZoomIn);
    connect(ui->actionZoom_out, &QAction::triggered, this, &MainWindow::actionZoomOut);
    connect(ui->actionZoom_reset, &QAction::triggered, this, &MainWindow::actionZoomReset);

    // ์‚ฌ์šฉ์ž ์ •์˜ ์Šฌ๋กฏ / custom slots
    connect(ui->actionFind, &QAction::triggered, this, &MainWindow::actionFind); // find
    connect(ui->actionReplace, &QAction::triggered, this, &MainWindow::actionReplace); // replace




    // == ํ…์ŠคํŠธ / Text ==

    // ๋ณผ๋“œ์ฒด / Bold
    // -- actionBold๋ฅผ ๊ตต๊ฒŒ/์–‡๊ฒŒ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” 'ํ† ๊ธ€' ๋ฒ„ํŠผ์œผ๋กœ ์„ค์ •
    // -- Set actionBold to a 'toggle' button that maintains the bold/thin state.
    ui->actionBold->setCheckable(true);

    // -- ํˆด๋ฐ” ์•ก์…˜ ํด๋ฆญ ์‹œ ์„œ์‹์„ ์ ์šฉํ•˜๋„๋ก ์—ฐ๊ฒฐ
    // -- Connect to apply formatting when clicking a toolbar action
    connect(ui->actionBold, &QAction::triggered, this, &MainWindow::actionBold);

    // ์ดํƒค๋ฆญ์ฒด / Italic
    ui->actionItalic->setCheckable(true);
    connect(ui->actionItalic, &QAction::triggered, this, &MainWindow::actionItalic);

    // ๋ฐ‘์ค„ / Underline
    ui->actionUnderline->setCheckable(true);
    connect(ui->actionUnderline, &QAction::triggered, this, &MainWindow::actionUnderline);

    // ์ทจ์†Œ์„  / strikethrough
    ui->actionStrike->setCheckable(true);
    connect(ui->actionStrike, &QAction::triggered, this, &MainWindow::actionStrike);

    // ๊ธ€๊ผด ์ƒ‰์ƒ / font color
    connect(ui->actionColor, &QAction::triggered, this, &MainWindow::actionColor);

    // ๊ธ€๊ผด ์„ ํƒ / Select font
    connect(ui->actionFont, &QAction::triggered, this, &MainWindow::actionFont);

    // ์ด๋ฏธ์ง€ ์ด๋ฏธ์ง€ ๋“œ๋ž˜๊ทธ ๋ฆฌ์‚ฌ์ด์ฆˆ /  Image Image Drag Resize
    ui->textEdit->viewport()->installEventFilter(this);




    // == ์ด๋ฏธ์ง€ ์‚ฝ์ž… / Insert image ==
    connect(ui->actionInsert_image, &QAction::triggered, this, &MainWindow::actionInsertImage);

    // -- ํ…์ŠคํŠธ ์ปค์„œ์˜ ์„œ์‹์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ํˆด๋ฐ” ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ์—ฐ๊ฒฐ
    // -- Connect to update the toolbar state whenever the text cursor's format changes.
    connect(ui->textEdit, &QTextEdit::currentCharFormatChanged,
            this, &MainWindow::updateFormat);


    // == ๋„์›€๋ง ๊ธฐ๋Šฅ / Help ==
    // Help
    connect(ui->actionHelp, &QAction::triggered, this, &MainWindow::actionHelp);

    // About
    connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::actionAbout);
}


// == ์Šฌ๋กฏ / Slot ==

// Export As PDF
void MainWindow::actionExportPdf()
{
    // ์ €์žฅํ•  PDF ํŒŒ์ผ ๊ฒฝ๋กœ ์„ ํƒ / Select the path to save the PDF file
    QString filePath = QFileDialog::getSaveFileName(this,
                                                    tr("PDF๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ/Export pdf"),
                                                    QDir::homePath() + "/export.pdf",
                                                    tr("PDF ํŒŒ์ผ/PDF file (*.pdf)"));

    if (filePath.isEmpty())
        return;

    // QPrinter ๊ฐ์ฒด๋ฅผ PDF ๋ชจ๋“œ๋กœ ์„ค์ •
    // Set the QPrinter object to PDF mode
    QPrinter printer(QPrinter::HighResolution);
    printer.setOutputFormat(QPrinter::PdfFormat);
    printer.setOutputFileName(filePath);

    // ํŽ˜์ด์ง€ ์—ฌ๋ฐฑ ๋ฐ ์šฉ์ง€ ์„ค์ •
    // Page margins and paper settings
    printer.setPageMargins(QMarginsF(15, 15, 15, 15));
    printer.setPageSize(QPageSize(QPageSize::A4));

    // QTextEdit์˜ ๋ฌธ์„œ๋ฅผ ํ”„๋ฆฐํ„ฐ๋กœ ์ถœ๋ ฅ
    ui->textEdit->document()->print(&printer);

    QMessageBox::information(this, tr("์™„๋ฃŒ/comolete"), tr("PDF๋กœ ์„ฑ๊ณต์ ์œผ๋กœ ๋‚ด๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค:\n%1").arg(filePath));
}

// Export base64 with images
void MainWindow::actionExportBase64WithImages()
{
    QTextDocument *doc = ui->textEdit->document();
    QString html = doc->toHtml();

    // ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ๋ฅผ Base64 ๋ฐ์ดํ„ฐ URI๋กœ ๋ณ€ํ™˜
    // Convert image path to Base64 data URI
    static const QRegularExpression imgRegex(R"###(<img[^>]*src="([^"]+)"[^>]*>)###");
    QRegularExpressionMatchIterator it = imgRegex.globalMatch(html);

    while (it.hasNext()) {
        QRegularExpressionMatch match = it.next();
        QString imgTag = match.captured(0);
        QString imgPath = match.captured(1);

        if (imgPath.startsWith("data:image"))
            continue;

        QImage image(QUrl(imgPath).toLocalFile());
        if (image.isNull())
            continue;

        QByteArray ba;
        QBuffer buffer(&ba);
        buffer.open(QIODevice::WriteOnly);
        image.save(&buffer, "PNG");

        QString base64Data = QString("data:image/png;base64,%1")
                                 .arg(QString::fromLatin1(ba.toBase64()));

        QString newTag = imgTag;
        newTag.replace(imgPath, base64Data);
        html.replace(imgTag, newTag);
    }


    // ์ตœ์ข… HTML์„ Base64๋กœ ์ธ์ฝ”๋”ฉ
    // Encode the final HTML to Base64
    QByteArray finalBase64 = html.toUtf8().toBase64();

    // ํŒŒ์ผ ์ €์žฅ ๋Œ€ํ™” ์ƒ์ž / Save file dialog box
    QString filePath = QFileDialog::getSaveFileName(this, tr("Export as Base64 HTML"),
                                                    QDir::homePath() + "/export_with_images.txt",
                                                    tr("ํ…์ŠคํŠธ ํŒŒ์ผ/text file (*.txt);;๋ชจ๋“  ํŒŒ์ผ (*)"));
    if (filePath.isEmpty())
        return;

    // ํŒŒ์ผ ์ €์žฅ / Save file
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&file);
        out << finalBase64;
        file.close();
        QMessageBox::information(this, tr("์™„๋ฃŒ/Complete"), tr("์ด๋ฏธ์ง€๋ฅผ ํฌํ•จํ•œ Base64 HTML๋กœ ๋‚ด๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค."));
    }
}


// base64 Import
void MainWindow::actionImportBase64()
{
    // ํŒŒ์ผ ์—ด๊ธฐ ๋Œ€ํ™” ์ƒ์ž / Open file dialog box
    QString filePath = QFileDialog::getOpenFileName(
        this, tr("Base64 ํŒŒ์ผ ์—ด๊ธฐ"),
        QDir::homePath(),
        tr("ํ…์ŠคํŠธ ํŒŒ์ผ (*.txt);;๋ชจ๋“  ํŒŒ์ผ (*)"));

    if (filePath.isEmpty())
        return;

    // ํŒŒ์ผ ์ฝ๊ธฐ / read file
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QByteArray base64Data = file.readAll();
    // Base64 ๋””์ฝ”๋”ฉ / Base64 Encoding
    QByteArray decoded = QByteArray::fromBase64(base64Data);
    QString html = QString::fromUtf8(decoded);


    // ๋ฌธ์„œ๋ฅผ ์™„์ „ํžˆ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์—ฌ ๊ธฐ์กด ์ƒํƒœ ๋ฆฌ์…‹ / Reset the previous state by creating a completely new document
    QTextDocument *newDoc = new QTextDocument(this);
    ui->textEdit->setDocument(newDoc);

    // HTML ๋กœ๋“œ / HTML Load
    ui->textEdit->setHtml(html);

    QMessageBox::information(this, tr("์™„๋ฃŒ/Complete"), tr("Base64 ๋ฌธ์„œ๋ฅผ ๋ถˆ๋Ÿฌ์™”์Šต๋‹ˆ๋‹ค."));
}


// base 64๋กœ ์ €์žฅํ•˜๊ธฐ / Save as base 64
void MainWindow::actionExportBase64()
{
    // QTextEdit์˜ ๋‚ด์šฉ์„ HTML๋กœ ๊ฐ€์ ธ์˜ด / Get the contents of QTextEdit as HTML
    QString htmlContent = ui->textEdit->toHtml();

    // HTML ๋ฌธ์ž์—ด์„ Base64๋กœ ์ธ์ฝ”๋”ฉ / Encode HTML string to Base64
    QByteArray base64Data = htmlContent.toUtf8().toBase64();

    // ์‚ฌ์šฉ์ž์—๊ฒŒ ์ €์žฅ ๋˜๋Š” ํด๋ฆฝ๋ณด๋“œ ๋ณต์‚ฌ ์„ ํƒ์ง€ ์ œ๊ณต / Give users the option to save or copy to clipboard
    QStringList options = {tr("ํŒŒ์ผ๋กœ ์ €์žฅ"), tr("ํด๋ฆฝ๋ณด๋“œ์— ๋ณต์‚ฌ"), tr("์ทจ์†Œ")};
    bool ok;
    QString choice = QInputDialog::getItem(this, tr("Base64 ๋‚ด๋ณด๋‚ด๊ธฐ"),
                                           tr("์ž‘์—… ์„ ํƒ:"), options, 0, false, &ok);
    if (!ok || choice == tr("์ทจ์†Œ"))
        return;

    if (choice == tr("ํŒŒ์ผ๋กœ ์ €์žฅ")) {
        QString filePath = QFileDialog::getSaveFileName(this,
                                                        tr("Base64 ํŒŒ์ผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ"),
                                                        QDir::homePath() + "/export_base64.txt",
                                                        tr("ํ…์ŠคํŠธ ํŒŒ์ผ (*.txt);;๋ชจ๋“  ํŒŒ์ผ (*)"));

        if (!filePath.isEmpty()) {
            QFile file(filePath);
            if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
                QTextStream out(&file);
                out << base64Data;
                file.close();
                QMessageBox::information(this, tr("์™„๋ฃŒ/Complete"), tr("Base64 ๋ฐ์ดํ„ฐ๊ฐ€ ํŒŒ์ผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."));
            } else {
                QMessageBox::critical(this, tr("์˜ค๋ฅ˜/Error"), tr("ํŒŒ์ผ์„ ์ €์žฅํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค:\n") + file.errorString());
            }
        }
    } else if (choice == tr("ํด๋ฆฝ๋ณด๋“œ์— ๋ณต์‚ฌ/Copy to clipboard")) {
        QClipboard *clipboard = QApplication::clipboard();
        clipboard->setText(QString::fromUtf8(base64Data));
        QMessageBox::information(this, tr("์™„๋ฃŒ/Complete"), tr("Base64 ๋ฐ์ดํ„ฐ๊ฐ€ ํด๋ฆฝ๋ณด๋“œ์— ๋ณต์‚ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."));
    }
}


// HTML์ €์žฅ / Save html
bool MainWindow::saveFile(const QString &path)
{
    QFile file(path);
    // QTextEdit์˜ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    //QString content = ui->textEdit->toPlainText(); // ์ผ๋ฐ˜ ํ…์ŠคํŠธ ์ €์žฅ ์˜ˆ์‹œ / Plain text storage example
    // ๋งŒ์•ฝ ๋ฆฌ์น˜ ํ…์ŠคํŠธ(HTML)๋กœ ์ €์žฅํ•˜๋ ค๋ฉด:
     QString content = ui->textEdit->toHtml();

    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&file);
        out << content;
        file.close();
        currentFilePath = path; // ์ €์žฅ ์„ฑ๊ณต ์‹œ ๊ฒฝ๋กœ ์—…๋ฐ์ดํŠธ / Update path when save is successful
        setWindowTitle(QFileInfo(path).fileName() + " - Editor"); // ์ฐฝ ์ œ๋ชฉ ๋ณ€๊ฒฝ / Change window title
        return true;
    } else {
        QMessageBox::critical(this, tr("์˜ค๋ฅ˜/Error"), tr("ํŒŒ์ผ์„ ์ €์žฅํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค:\n") + file.errorString());
        return false;
    }
}


// ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์ €์žฅํ•˜๊ธฐ / Save As
void MainWindow::saveAs()
{
    // QFileDialog๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๋ฌป์Šต๋‹ˆ๋‹ค.
    // Use QFileDialog to ask the user for a file path.
    QString filePath = QFileDialog::getSaveFileName(this,
                                                    tr("๋ฌธ์„œ ์ €์žฅ/Save Document"),
                                                    currentFilePath.isEmpty() ? QDir::homePath() : currentFilePath,
                                                    tr("ํ…์ŠคํŠธ ํŒŒ์ผ (*.txt);;HTML ํŒŒ์ผ (*.html);;๋ชจ๋“  ํŒŒ์ผ (*)"));

    if (!filePath.isEmpty()) {
        saveFile(filePath);
    }
}

// ์ €์žฅํ•˜๊ธฐ / Save
void MainWindow::save()
{
    if (currentFilePath.isEmpty()) {
        // ๊ฒฝ๋กœ๊ฐ€ ์—†์œผ๋ฉด 'Save As'๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. / If there is no path, call 'Save As'.
        saveAs();
    } else {
        // ๊ฒฝ๋กœ๊ฐ€ ์žˆ์œผ๋ฉด ๊ธฐ์กด ํŒŒ์ผ์— ๋ฎ์–ด์”๋‹ˆ๋‹ค. / If the path exists, it will overwrite the existing file.
        saveFile(currentFilePath);
    }
}

// ์ข…๋ฃŒํ•˜๊ธฐ ์Šฌ๋กฏ / Exit slot
void MainWindow::actionExit()
{
    close(); // ๋ฉ”์ธ ์œˆ๋„์šฐ๋ฅผ ๋‹ซ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. / Close the main window and exit the application.
}

// ์ƒˆ ํŒŒ์ผ ์ƒ์„ฑ ์Šฌ๋กฏ / New file creation slot
void MainWindow::actionNew()
{
    ui->textEdit->clear();
    currentFilePath.clear();
    setWindowTitle(tr("Untitled - Editor"));
}

// ํŒŒ์ผ ์—ด๊ธฐ ์Šฌ๋กฏ / file open slot
void MainWindow::actionOpen()
{
    QString filePath = QFileDialog::getOpenFileName(this,
                                                    tr("๋ฌธ์„œ ์—ด๊ธฐ/Open"),
                                                    QDir::homePath(),
                                                    tr("๋ฆฌ์น˜ ํ…์ŠคํŠธ ํŒŒ์ผ (*.html *.htm);;ํ…์ŠคํŠธ ํŒŒ์ผ (*.txt);;์ด๋ฏธ์ง€ ํŒŒ์ผ (*.png *.jpg *.jpeg *.gif);;๋ชจ๋“  ํŒŒ์ผ (*)"));
    if (!filePath.isEmpty()) {
        QFile file(filePath);
        if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            QString content = file.readAll();

            if (filePath.endsWith(".html", Qt::CaseInsensitive) || filePath.endsWith(".htm", Qt::CaseInsensitive)) {
                ui->textEdit->setHtml(content); // HTML ํ˜•์‹์œผ๋กœ ๋กœ๋“œ / Load in HTML format
            }
            // ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ๋ฐ”๋กœ ์—ด๋ฉด ํ…์ŠคํŠธ ํŽธ์ง‘๊ธฐ์— ์‚ฝ์ž… / Open the image file directly and insert it into a text editor
            else if (filePath.endsWith(".png", Qt::CaseInsensitive) ||
                     filePath.endsWith(".jpg", Qt::CaseInsensitive) ||
                     filePath.endsWith(".jpeg", Qt::CaseInsensitive) ||
                     filePath.endsWith(".gif", Qt::CaseInsensitive)) {

                ui->textEdit->clear();
                QTextDocument *doc = ui->textEdit->document();
                QUrl imageUrl = QUrl::fromLocalFile(filePath);
                QImage image(filePath);

                doc->addResource(QTextDocument::ImageResource, imageUrl, image);

                QTextImageFormat imageFormat;
                imageFormat.setWidth(image.width());
                imageFormat.setHeight(image.height());
                imageFormat.setName(imageUrl.toString());

                QTextCursor cursor = ui->textEdit->textCursor();
                cursor.insertImage(imageFormat);

            } else {
                ui->textEdit->setText(content); // ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ ๋กœ๋“œ / Load as plain text
            }

            file.close();
            currentFilePath = filePath;
            setWindowTitle(QFileInfo(filePath).fileName() + tr(" - Editor"));
        } else {
            QMessageBox::critical(this, tr("์˜ค๋ฅ˜/Error"), tr("ํŒŒ์ผ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค:\n") + file.errorString());
        }
    }
}


// == ํŽธ์ง‘ ๊ธฐ๋Šฅ ์Šฌ๋กฏ / Edit function slot ==

// '์ฐพ๊ธฐ' ๊ธฐ๋Šฅ / 'Find' function
void MainWindow::actionFind()
{
    bool ok;
    // ์‚ฌ์šฉ์ž์—๊ฒŒ ์ฐพ์„ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅ๋ฐ›์Šต๋‹ˆ๋‹ค. / Ask the user to enter the text they want to find.
    QString text = QInputDialog::getText(this, tr("ํ…์ŠคํŠธ ์ฐพ๊ธฐ"),
                                         tr("์ฐพ์„ ๋‹จ์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”:"), QLineEdit::Normal,
                                         "", &ok);
    if (ok && !text.isEmpty()) {
        QTextDocument::FindFlags flags = QTextDocument::FindFlags();

        // QTextEdit::find()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ ์ผ์น˜ ํ•ญ๋ชฉ์„ ์ฐพ๊ณ  ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
        bool found = ui->textEdit->find(text, flags);

        if (!found) {
            QMessageBox::information(this, tr("์ฐพ๊ธฐ ๊ฒฐ๊ณผ"), tr("'%1'์„(๋ฅผ) ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.").arg(text));
        }
    }
}

// '๋ฐ”๊พธ๊ธฐ' ๊ธฐ๋Šฅ / replace function
void MainWindow::actionReplace()
{
    //  ์ฐพ์„ ํ…์ŠคํŠธ ์ž…๋ ฅ / Enter text to find
    bool ok;
    QString findText = QInputDialog::getText(this, tr("ํ…์ŠคํŠธ ๋ฐ”๊พธ๊ธฐ"),
                                             tr("์ฐพ์„ ๋‹จ์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”:"), QLineEdit::Normal,
                                             "", &ok);
    if (!ok || findText.isEmpty()) return;

    // ๋ฐ”๊ฟ€ ํ…์ŠคํŠธ ์ž…๋ ฅ / Enter text to find
    QString replaceText = QInputDialog::getText(this, tr("ํ…์ŠคํŠธ ๋ฐ”๊พธ๊ธฐ"),
                                                tr("๋ฐ”๊ฟ€ ๋‹จ์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”:"), QLineEdit::Normal,
                                                "", &ok);
    if (!ok) return;

    QTextCursor cursor = ui->textEdit->textCursor();
    QTextDocument::FindFlags flags = QTextDocument::FindFlags();
    int count = 0;

    // ํ…์ŠคํŠธ๋ฅผ ๋ฌธ์„œ ์‹œ์ž‘๋ถ€ํ„ฐ ๋‹ค์‹œ ์ฐพ๊ธฐ ์‹œ์ž‘ํ•˜๋„๋ก ์ปค์„œ๋ฅผ ๋ฌธ์„œ ์‹œ์ž‘์œผ๋กœ ์ด๋™
    // Move the cursor to the beginning of the document to start searching text again from the beginning of the document
    cursor.movePosition(QTextCursor::Start);
    ui->textEdit->setTextCursor(cursor);

    // ๋ฌธ์„œ ๋๊นŒ์ง€ ๋ฐ˜๋ณตํ•˜๋ฉฐ ์ฐพ๊ธฐ ๋ฐ ๋ฐ”๊พธ๊ธฐ
    // Find and replace until the end of the document
    while (ui->textEdit->find(findText, flags)) {
        // find()๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด ํ…์ŠคํŠธ๋Š” ์ด๋ฏธ ์„ ํƒ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
        // If find() succeeds, the text is already selected.
        QTextCursor currentSelection = ui->textEdit->textCursor();

        // ์„ ํƒ๋œ ํ…์ŠคํŠธ๋ฅผ replaceText๋กœ ๋Œ€์ฒด
        // Replace the selected text with replaceText
        currentSelection.insertText(replaceText);
        count++;

        // ๋‹ค์Œ ๊ฒ€์ƒ‰์„ ์œ„ํ•ด ์ปค์„œ๋ฅผ ์‚ฝ์ž…๋œ ํ…์ŠคํŠธ ๋’ค๋กœ ์ด๋™ (find๊ฐ€ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•จ)
        // Move the cursor after the inserted text for the next search (find handles this automatically)
    }

    QMessageBox::information(this, tr("๋ฐ”๊พธ๊ธฐ ์™„๋ฃŒ"), tr("์ด %1๊ฐœ์˜ ํ•ญ๋ชฉ์„ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.").arg(count));
}

// ==  ๋ทฐ ๊ธฐ๋Šฅ ์Šฌ๋กฏ / View function slot ==

//----------------------------------------------------------------
// -- ๋‚ด์žฅํ•จ์ˆ˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ €์žฅ์ด๋‚˜ ๋‚ด๋ณด๋‚ด๊ธฐ ํ•œ ํŒŒ์ผ์„ ์ ์šฉ๋˜์ง€ ์•Š์Œ--
// -- Using built-in functions does not apply to files saved or exported--
//----------------------------------------------------------------
// ์คŒ์ธ(๋‚ด์žฅํ•จ์ˆ˜) / Zoom in (built-in function)
// void MainWindow::actionZoomIn()
// {
//     // ui->textEdit์€ QMainWindow์— ํฌํ•จ๋œ QTextEdit ์œ„์ ฏ์˜ objectName์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
//     if (ui->textEdit) {
//         qDebug() << "Zoom In triggered";
//         // ๊ธฐ๋ณธ ์คŒ ๋‹จ๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™•๋Œ€ / Zoom in using the default zoom level
//         ui->textEdit->zoomIn(1);
//     }
// }

// ์คŒ์•„์›ƒ(๋‚ด์žฅํ•จ์ˆ˜) / Zoom out (built-in function)
// void MainWindow::actionZoomOut()
// {
//     if (ui->textEdit) {
//         qDebug() << "Zoom Out triggered";
//         // ๊ธฐ๋ณธ ์คŒ ๋‹จ๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ•์†Œ / Zoom out using the default zoom level
//         ui->textEdit->zoomOut(1);
//     }
// }
//-----------------------------------------------------------------------


// ์คŒ์ธ ์ปค์Šคํ…€ / Zoom in custom
void MainWindow::actionZoomIn()
{
    // ui->textEdit์ด ์œ ํšจํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. / Check if ui->textEdit is valid.
    if (!ui->textEdit) {
        return;
    }

    // ํ˜„์žฌ ๋ฌธ์„œ์˜ ๊ธฐ๋ณธ ํฐํŠธ(๋˜๋Š” ์ปค์„œ ์œ„์น˜์˜ ํฐํŠธ) ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    // Get information about the default font of the current document (or the font at the cursor position).

    // ๋ฌธ์„œ ์ „์ฒด์˜ ๊ธฐ๋ณธ ํฐํŠธ ํฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ• ์ง€, ํ˜„์žฌ ์ปค์„œ์˜ ํฐํŠธ ํฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    // Determines whether to use the default font size for the entire document or the font size of the current cursor.

    // ์คŒ ๊ธฐ๋Šฅ์€ ๋ณดํ†ต ๋ฌธ์„œ ์ „์ฒด์˜ ๊ธฐ๋ณธ ํฐํŠธ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
    // The zoom feature usually changes the default font size for the entire document.
    QFont font = ui->textEdit->document()->defaultFont();
    int currentSize = font.pointSize();

    // ํฐํŠธ ํฌ๊ธฐ๊ฐ€ ํฌ์ธํŠธ ๋‹จ์œ„๋กœ ์„ค์ •๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ (์˜ˆ: ํ”ฝ์…€) ๋˜๋Š” 0์ผ ๊ฒฝ์šฐ,
    // ์ ์ ˆํ•œ ๊ธฐ๋ณธ๊ฐ’(์˜ˆ: 12)์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    // If the font size is not set in points (e.g., pixels) or is 0,
    // use an appropriate default value (e.g., 12).
    if (currentSize <= 0) {
        currentSize = 12;
    }

    // ์คŒ ๋‹จ๊ณ„ ์„ค์ • ๋ฐ ์ตœ๋Œ€ ํฌ๊ธฐ ์ œํ•œ (Max Zoom)
    // Set zoom level and limit maximum size (Max Zoom)
    const int ZOOM_STEP = 2; // ํ™•๋Œ€ ๋‹จ์œ„๋ฅผ 2pt๋กœ ์„ค์ • / Set the zoom unit to 2pt
    const int MAX_FONT_SIZE = 48; // ์ตœ๋Œ€ ํฐํŠธ ํฌ๊ธฐ๋ฅผ 48pt๋กœ ์ œํ•œ / Limit maximum font size to 48pt

    if (currentSize < MAX_FONT_SIZE) {
        int newSize = qMin(currentSize + ZOOM_STEP, MAX_FONT_SIZE);

        // ์ƒˆ ํฐํŠธ ํฌ๊ธฐ ์ ์šฉ / Apply new font size
        font.setPointSize(newSize);
        ui->textEdit->document()->setDefaultFont(font);

        // ํ…์ŠคํŠธ ์ „์ฒด์— ์ƒˆ ํฐํŠธ ํฌ๊ธฐ ์ ์šฉ (๊ธฐ์กด HTML ๋กœ๋“œ ์‹œ ์ธ๋ผ์ธ ์Šคํƒ€์ผ์ด ์ ์šฉ๋˜์–ด ์žˆ๋‹ค๋ฉด ํ•„์š”)
        // Apply new font size to all text (needed if inline styles were applied when loading existing HTML)
        QTextCursor cursor(ui->textEdit->document());
        cursor.select(QTextCursor::Document);
        QTextCharFormat fmt;
        fmt.setFontPointSize(newSize);

        // ํฌ๋งท์„ ๋ณ‘ํ•ฉํ•˜์—ฌ ํฐํŠธ ํฌ๊ธฐ๋งŒ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‹ค๋ฅธ ์Šคํƒ€์ผ์€ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
        // Merge formats to change only the font size and keep other styles.
        cursor.mergeCharFormat(fmt);
        cursor.clearSelection();

        qDebug() << "Zoom In to size:" << newSize;

        // (์˜ต์…˜) ์คŒ ์ƒํƒœ ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ
        // (Optional) Update the zoom status display
        // updateZoomStatus(newSize);
    } else {
        qDebug() << "Maximum zoom level reached.";
    }
}



// ์คŒ์•„์›ƒ ์ปค์Šคํ…€ / Zoom out coustom
void MainWindow::actionZoomOut()
{
    // ui->textEdit์ด ์œ ํšจํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    // Check if ui->textEdit is valid.
    if (!ui->textEdit) {
        return;
    }

    // ํ˜„์žฌ ๋ฌธ์„œ์˜ ๊ธฐ๋ณธ ํฐํŠธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    // Get the default font information for the current document.
    QFont font = ui->textEdit->document()->defaultFont();
    int currentSize = font.pointSize();

    // ํฐํŠธ ํฌ๊ธฐ๊ฐ€ ํฌ์ธํŠธ ๋‹จ์œ„๋กœ ์„ค์ •๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ 0์ผ ๊ฒฝ์šฐ, ์ ์ ˆํ•œ ๊ธฐ๋ณธ๊ฐ’(์˜ˆ: 12)์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    // If the font size is not set in points or is 0, a reasonable default (e.g. 12) is used.
    if (currentSize <= 0) {
        currentSize = 12;
    }

    // ์คŒ ๋‹จ๊ณ„ ์„ค์ • ๋ฐ ์ตœ์†Œ ํฌ๊ธฐ ์ œํ•œ (Min Zoom)
    // Set zoom level and minimum size limit (Min Zoom)
    const int ZOOM_STEP = 2; // ์ถ•์†Œ ๋‹จ์œ„๋ฅผ 2pt๋กœ ์„ค์ • (์คŒ ์ธ๊ณผ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€) / Set the zoom unit to 2pt (keep it the same as zoom in)
    const int MIN_FONT_SIZE = 8; // ์ตœ์†Œ ํฐํŠธ ํฌ๊ธฐ๋ฅผ 8pt๋กœ ์ œํ•œ / Limit minimum font size to 8pt

    if (currentSize > MIN_FONT_SIZE) {

        // ํ˜„์žฌ ํฌ๊ธฐ์—์„œ ์คŒ ๋‹จ๊ณ„๋งŒํผ ๋นผ๊ณ , ์ตœ์†Œ ํฌ๊ธฐ(MIN_FONT_SIZE)๋ณด๋‹ค ์ž‘์•„์ง€์ง€ ์•Š๋„๋ก ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.
        // Subtract the zoom level from the current size, and limit it to no smaller than the minimum size (MIN_FONT_SIZE).
        int newSize = qMax(currentSize - ZOOM_STEP, MIN_FONT_SIZE);

        // ์ƒˆ ํฐํŠธ ํฌ๊ธฐ ์ ์šฉ / Apply new font size
        font.setPointSize(newSize);
        ui->textEdit->document()->setDefaultFont(font);

        // ํ…์ŠคํŠธ ์ „์ฒด์— ์ƒˆ ํฐํŠธ ํฌ๊ธฐ ์ ์šฉ (์คŒ ์•„์›ƒ์ด ๋ทฐํฌํŠธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์‹ค์ œ ํฐํŠธ ํฌ๊ธฐ์— ๋ฐ˜์˜๋˜๋„๋ก)
        // Apply the new font size to the entire text (so that zooming out reflects the actual font size, not just the viewport)
        QTextCursor cursor(ui->textEdit->document());
        cursor.select(QTextCursor::Document);
        QTextCharFormat fmt;
        fmt.setFontPointSize(newSize);

        // ํฌ๋งท์„ ๋ณ‘ํ•ฉํ•˜์—ฌ ํฐํŠธ ํฌ๊ธฐ๋งŒ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‹ค๋ฅธ ์Šคํƒ€์ผ์€ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. / Merge formats to change only the font size and keep other styles.
        cursor.mergeCharFormat(fmt);
        cursor.clearSelection();

        qDebug() << "Zoom Out to size:" << newSize;

        // ์คŒ ์ƒํƒœ ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ / Updated zoom status display
        // updateZoomStatus(newSize);
    } else {
        qDebug() << "Minimum zoom level reached.";
    }
}

// ์คŒ ์ดˆ๊ธฐํ™” / Reset zoom
void MainWindow::actionZoomReset()
{
    // ui->textEdit์ด ์œ ํšจํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    // Check if ui->textEdit is valid.
    if (!ui->textEdit) {
        return;
    }

    // ๊ธฐ๋ณธ ํฐํŠธ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. / Get the default font size.
    const int newSize = m_defaultPointSize;

    // ๋ฌธ์„œ์˜ ๊ธฐ๋ณธ ํฐํŠธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. / Sets the default font for the document.
    QFont font = ui->textEdit->document()->defaultFont();
    font.setPointSize(newSize);
    ui->textEdit->document()->setDefaultFont(font);

    // ํ…์ŠคํŠธ ์ „์ฒด์— ์„œ์‹ ์ผ๊ด„ ์ ์šฉ (์ธ๋ผ์ธ ์Šคํƒ€์ผ์„ ๋ฎ์–ด์“ฐ๊ธฐ ์œ„ํ•จ)
    // Apply formatting to all text (to overwrite inline styles)
    QTextCursor cursor(ui->textEdit->document());
    cursor.select(QTextCursor::Document);
    QTextCharFormat fmt;
    fmt.setFontPointSize(newSize);

    // ํฌ๋งท์„ ๋ณ‘ํ•ฉํ•˜์—ฌ ํฐํŠธ ํฌ๊ธฐ๋งŒ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
    // Merge formats to only change the font size to default.
    cursor.mergeCharFormat(fmt);
    cursor.clearSelection();

    qDebug() << "Zoom Reset to default size:" << newSize;

    // ์คŒ ์ƒํƒœ ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ / Updated zoom status display
    // updateZoomStatus(newSize);
}


// ==  ํ…์ŠคํŠธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์Šฌ๋กฏ / Text function implementation slot ==

// ๊ตต๊ฒŒ ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์Šฌ๋กฏ / A slot called when the bold button is clicked
// ๋ณผ๋“œ์ฒด ์ ์šฉ ์Šฌ๋กฏ ๊ตฌํ˜„ / Implementing a bold font slot
void MainWindow::actionBold(bool checked)
{
    // ์ƒˆ๋กœ์šด ๋ฌธ์ž ์„œ์‹ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ / Create a new character format object
    QTextCharFormat format;

    // QAction์˜ ์ฒดํฌ ์ƒํƒœ(๋ˆŒ๋ฆผ ์ƒํƒœ)์— ๋”ฐ๋ผ ๊ตต๊ฒŒ/๋ณดํ†ต์„ ์„ค์ •
    // Set bold/normal depending on the checked state (pressed state) of QAction

    // QAction::triggered(bool checked) ์‹œ๊ทธ๋„์˜ 'checked' ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    // Use the 'checked' value of the QAction::triggered(bool checked) signal.
    format.setFontWeight(checked ? QFont::Bold : QFont::Normal);

    // ์„œ์‹์„ ํ…์ŠคํŠธ ํŽธ์ง‘๊ธฐ์— ๋ณ‘ํ•ฉ (์„ ํƒ ์˜์—ญ ๋˜๋Š” ์ปค์„œ ์œ„์น˜์˜ ๋‹จ์–ด์— ์ ์šฉ)
    // Merge formatting into text editor (applies to selection or word at cursor position)

    // ์ด ์ฝ”๋“œ๋Š” QTextEdit::mergeCurrentCharFormat()์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    // This code uses QTextEdit::mergeCurrentCharFormat().
    ui->textEdit->mergeCurrentCharFormat(format);

    // QAction์˜ ์ƒํƒœ๋Š” ์—ฐ๊ฒฐ๋œ QToolButton์— ์˜ํ•ด ์ž๋™์œผ๋กœ ํ† ๊ธ€๋ฉ๋‹ˆ๋‹ค.
    // The state of a QAction is automatically toggled by the connected QToolButton.
}



// ์ดํƒค๋ฆญ ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์Šฌ๋กฏ / A slot called when the italic button is clicked.
void MainWindow::actionItalic(bool checked)
{
    QTextCharFormat format;

    // checked ์ƒํƒœ์— ๋”ฐ๋ผ ์ดํƒค๋ฆญ ์†์„ฑ ์„ค์ • / Set italic property based on checked state
    format.setFontItalic(checked);
    ui->textEdit->mergeCurrentCharFormat(format);
}

// ๋ฐ‘์ค„ ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์Šฌ๋กฏ
// Slot called when the underline button is clicked
void MainWindow::actionUnderline(bool checked)
{
    QTextCharFormat format;

    // checked ์ƒํƒœ์— ๋”ฐ๋ผ ๋ฐ‘์ค„ ์†์„ฑ ์„ค์ •
    // Set the underline property according to the checked state
    format.setFontUnderline(checked);
    ui->textEdit->mergeCurrentCharFormat(format);
}

// ์ทจ์†Œ์„  ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์Šฌ๋กฏ
// Slot called when the cancel button is clicked
void MainWindow::actionStrike(bool checked)
{
    QTextCharFormat format;

    // checked ์ƒํƒœ์— ๋”ฐ๋ผ ์ทจ์†Œ์„  ์†์„ฑ ์„ค์ •
    // Set strikethrough property based on checked status
    format.setFontStrikeOut(checked);
    ui->textEdit->mergeCurrentCharFormat(format);
}

// ๊ธ€๊ผด ์ƒ‰์ƒ ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์Šฌ๋กฏ (์ˆ˜๋™์—ฐ๊ฒฐ ์‚ฌ์šฉ์‹œ)
// Slot called when the font color button is clicked (when using manual linking)
void MainWindow::actionColor()
{
    // ํ˜„์žฌ ํ…์ŠคํŠธ ์ƒ‰์ƒ์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ / Use current text color as default
    QColor initialColor = ui->textEdit->currentCharFormat().foreground().color();
    QColor color = QColorDialog::getColor(initialColor, this, tr("๊ธ€๊ผด ์ƒ‰์ƒ ์„ ํƒ"));

    if (color.isValid()) {
        QTextCharFormat format;
        // ์„ ํƒ๋œ ์ƒ‰์ƒ์„ ์ „๊ฒฝ์ƒ‰(๊ธ€๊ผด ์ƒ‰์ƒ)์œผ๋กœ ์„ค์ • / Set the selected color as the foreground color (font color)
        format.setForeground(QBrush(color));
        ui->textEdit->mergeCurrentCharFormat(format);
    }
}

// ๊ธ€๊ผด ์„ ํƒ ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์Šฌ๋กฏ (์ˆ˜๋™์—ฐ๊ฒฐ ์‚ฌ์šฉ์‹œ)
// Slot called when the font selection button is clicked (when using manual linking)
void MainWindow::actionFont()
{
    // ํ˜„์žฌ ํฐํŠธ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ / Use the current font as default
    QFont initialFont = ui->textEdit->currentCharFormat().font();
    bool ok;
    QFont font = QFontDialog::getFont(&ok, initialFont, this, tr("๊ธ€๊ผด ์„ ํƒ"));

    if (ok) {
        QTextCharFormat format;
        // ์„ ํƒ๋œ ํฐํŠธ๋ฅผ ์ ์šฉ / Apply selected font
        format.setFont(font);
        ui->textEdit->mergeCurrentCharFormat(format);
    }
}

// == ์ž๋™์—ฐ๊ฒฐ์€ ๊ฒฝ๊ณ  ๋ฐœ์ƒํ•จ ==
// void MainWindow::on_actionColor_triggered()
// {
//     // ํ˜„์žฌ ํ…์ŠคํŠธ ์ƒ‰์ƒ์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ
//     QColor initialColor = ui->textEdit->currentCharFormat().foreground().color();
//     QColor color = QColorDialog::getColor(initialColor, this, tr("๊ธ€๊ผด ์ƒ‰์ƒ ์„ ํƒ"));

//     if (color.isValid()) {
//         QTextCharFormat format;
//         // ์„ ํƒ๋œ ์ƒ‰์ƒ์„ ์ „๊ฒฝ์ƒ‰(๊ธ€๊ผด ์ƒ‰์ƒ)์œผ๋กœ ์„ค์ •
//         format.setForeground(QBrush(color));
//         ui->textEdit->mergeCurrentCharFormat(format);
//     }
// }

// void MainWindow::on_actionFont_triggered()
// {
//     // ํ˜„์žฌ ํฐํŠธ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ
//     QFont initialFont = ui->textEdit->currentCharFormat().font();
//     bool ok;
//     QFont font = QFontDialog::getFont(&ok, initialFont, this, tr("๊ธ€๊ผด ์„ ํƒ"));

//     if (ok) {
//         QTextCharFormat format;
//         // ์„ ํƒ๋œ ํฐํŠธ๋ฅผ ์ ์šฉ
//         format.setFont(font);
//         ui->textEdit->mergeCurrentCharFormat(format);
//     }
// }


// == ์ด๋ฏธ์ง€ ์‚ฝ์ž… ์Šฌ๋กฏ / Image insertion slot  ==
void MainWindow::actionInsertImage()
{
    QString imagePath = QFileDialog::getOpenFileName(this,
                                                     tr("์ด๋ฏธ์ง€ ์‚ฝ์ž…"),
                                                     QDir::homePath(),
                                                     tr("์ด๋ฏธ์ง€ ํŒŒ์ผ (*.png *.jpg *.jpeg *.gif *.bmp)"));

    if (!imagePath.isEmpty()) {
        QImage image(imagePath);

        if (image.isNull()) {
            QMessageBox::critical(this, tr("์˜ค๋ฅ˜"), tr("์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."));
            return;
        }

        QTextDocument *doc = ui->textEdit->document();
        QUrl imageUrl = QUrl::fromLocalFile(imagePath);

        doc->addResource(QTextDocument::ImageResource, imageUrl, image);

        QTextImageFormat imageFormat;
        // ๊ธฐ๋ณธ ์‚ฝ์ž… ํฌ๊ธฐ๋ฅผ ์›๋ณธ ํฌ๊ธฐ๋กœ ์„ค์ • / Set default insert size to original size
        imageFormat.setWidth(image.width());
        imageFormat.setHeight(image.height());
        imageFormat.setName(imageUrl.toString());

        QTextCursor cursor = ui->textEdit->textCursor();
        cursor.insertImage(imageFormat);
    }
}

// == ์ด๋ฒคํŠธ ํ•„ํ„ฐ ๊ตฌํ˜„ (์ด๋ฏธ์ง€ ๋“œ๋ž˜๊ทธ ๋ฆฌ์‚ฌ์ด์ฆˆ) / Implementing an event filter (image drag resize) ==
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == ui->textEdit->viewport()) {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
        QTextCursor cursor = ui->textEdit->textCursor();
        QTextCharFormat format = cursor.charFormat();

        // ๋งˆ์šฐ์Šค ๋ˆ„๋ฆ„ ์ด๋ฒคํŠธ (Resize ์‹œ์ž‘ ๊ฐ์ง€) / Mouse press event (detect resize start)
        if (event->type() == QEvent::MouseButtonPress && mouseEvent->button() == Qt::LeftButton) {
            if (format.isImageFormat()) {
                // ์ด๋ฏธ์ง€๊ฐ€ ์„ ํƒ๋œ ์œ„์น˜์—์„œ ํด๋ฆญ์ด ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ / If a click occurs at the location where the image is selected
                m_isResizing = true;
                m_dragStartPos = mouseEvent->pos();

                // ํ˜„์žฌ ์ด๋ฏธ์ง€ ํฌ๋งท์—์„œ ๊ธฐ์กด ํฌ๊ธฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. / Get the original size from the current image format.
                QTextImageFormat imageFormat = format.toImageFormat();
                m_originalImageWidth = imageFormat.width();
                m_originalImageHeight = imageFormat.height();

                // ๋ฆฌ์‚ฌ์ด์ฆˆ ์ค‘์—๋Š” ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๋ฅผ QTextEdit์œผ๋กœ ์ „๋‹ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
                // Mouse events are not passed to QTextEdit during resizing.
                return true;
            }
        }

        // ๋งˆ์šฐ์Šค ์ด๋™ ์ด๋ฒคํŠธ (Resize ์ง„ํ–‰) / Mouse move event (Resize in progress)
        else if (event->type() == QEvent::MouseMove) {
            if (m_isResizing && mouseEvent->buttons() & Qt::LeftButton) {

                // X์ถ• ์ด๋™ ๊ฑฐ๋ฆฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํฌ๊ธฐ๋ฅผ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. / Scales based on the distance moved along the X-axis.
                int deltaX = mouseEvent->pos().x() - m_dragStartPos.x();

                // ์ƒˆ๋กœ์šด ๋„ˆ๋น„ ๊ณ„์‚ฐ (์ตœ์†Œ 50ํ”ฝ์…€ ์ œํ•œ) / New width calculation (minimum 50 pixels limit)
                int newWidth = qMax(50, m_originalImageWidth + deltaX);

                // ์ข…ํšก๋น„ ์œ ์ง€ํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋†’์ด ๊ณ„์‚ฐ / Calculate new height while maintaining aspect ratio
                // ์†Œ์ˆ˜์  ์—ฐ์‚ฐ์„ ์œ„ํ•ด double๋กœ ์บ์ŠคํŒ… / Cast to double for decimal operations
                int newHeight = (int)((double)m_originalImageHeight * ((double)newWidth / m_originalImageWidth));

                // ํ˜„์žฌ ์ปค์„œ ์œ„์น˜์— ์žˆ๋Š” ์ด๋ฏธ์ง€ ํฌ๋งท์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
                // Updates the image format at the current cursor position.
                QTextCursor currentCursor = ui->textEdit->textCursor();
                if (currentCursor.charFormat().isImageFormat()) {
                    QTextImageFormat newImageFormat = currentCursor.charFormat().toImageFormat();

                    newImageFormat.setWidth(newWidth);
                    newImageFormat.setHeight(newHeight);

                    // ์„œ์‹ ์ ์šฉ / Apply Formatting
                    ui->textEdit->mergeCurrentCharFormat(newImageFormat);
                }

                return true; // ์ด๋ฒคํŠธ๋ฅผ ์†Œ๋น„ํ•˜์—ฌ ํ…์ŠคํŠธ ์„ ํƒ ๋“ฑ ๊ธฐ๋ณธ ๋™์ž‘ ๋ฐฉ์ง€ / Consuming events to prevent default actions such as text selection
            }
        }

        // ๋งˆ์šฐ์Šค ๋†“๊ธฐ ์ด๋ฒคํŠธ (Resize ์ข…๋ฃŒ) / Mouse release event (Resize ends)
        else if (event->type() == QEvent::MouseButtonRelease) {
            if (m_isResizing && mouseEvent->button() == Qt::LeftButton) {
                m_isResizing = false;
                // ๋ฆฌ์‚ฌ์ด์ฆˆ ์ข…๋ฃŒ / End of resize
                return true;
            }
        }
    }

    // ํ•„ํ„ฐ๋งํ•˜์ง€ ์•Š์€ ๋ชจ๋“  ์ด๋ฒคํŠธ๋ฅผ ๋Œ€์ƒ ๊ฐ์ฒด๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
    // Forward all unfiltered events to the target object.
    return QMainWindow::eventFilter(obj, event);
}


// ์ปค์„œ ์œ„์น˜์˜ ๋ฌธ์ž ์„œ์‹์ด ๋ฐ”๋€” ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์Šฌ๋กฏ
// Slot called when the character format at the cursor position changes

// ํˆด๋ฐ” ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์Šฌ๋กฏ ๊ตฌํ˜„ / Implementing a Toolbar State Update Slot
void MainWindow::updateFormat(const QTextCharFormat &format)
{
    // ํ˜„์žฌ ์„œ์‹์—์„œ ํฐํŠธ์˜ ๊ตต๊ธฐ๋ฅผ ํ™•์ธ / Check the font weight in the current format
    bool isBold = format.font().bold();

    // -- actionBold์˜ ์ฒดํฌ ์ƒํƒœ๋ฅผ ํฐํŠธ์˜ ๊ตต๊ธฐ ์ƒํƒœ์— ๋งž๊ฒŒ ์—…๋ฐ์ดํŠธ / Update the check state of actionBold to match the bold state of the font.
    // -- ์ด๋ ‡๊ฒŒ ํ•ด์•ผ ์‚ฌ์šฉ์ž๊ฐ€ ๊ตต์€ ๊ธ€์”จ๋กœ ์ปค์„œ๋ฅผ ์˜ฎ๊ธฐ๋ฉด ๋ฒ„ํŠผ์ด ๋ˆŒ๋ฆฐ ์ƒํƒœ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. / This way, when the user moves the cursor over the bold text, the button appears pressed.
    ui->actionBold->setChecked(isBold);

    // ๋‹ค๋ฅธ ์„œ์‹(Italic, Underline)์— ๋Œ€ํ•ด์„œ๋„ ์ด ํ•จ์ˆ˜ ๋‚ด์—์„œ ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    // You can handle other formats (Italic, Underline) in the same way within this function.
    // ui->actionItalic->setChecked(format.font().italic());


    // ์ดํƒค๋ฆญ์ฒด ์ƒํƒœ ์—…๋ฐ์ดํŠธ / Italic status updates
    bool isItalic = format.font().italic();
    ui->actionItalic->setChecked(isItalic);

    // ๋ฐ‘์ค„ ์ƒํƒœ ์—…๋ฐ์ดํŠธ / Underline status update
    bool isUnderline = format.font().underline();
    ui->actionUnderline->setChecked(isUnderline);

    // ์ทจ์†Œ์„  ์ƒํƒœ ์—…๋ฐ์ดํŠธ / Strikethrough status update
    bool isStrike = format.font().strikeOut();
    ui->actionStrike->setChecked(isStrike);
}

// == ๋„์›€๋ง & Help ๊ธฐ๋Šฅ / Help & Help function ==
void MainWindow::actionHelp()
{
    QMessageBox::information(this,
                             tr("๋„์›€๋ง/Help"),
                             tr("<h3>๊ฐ„๋‹จํ•œ ํ…์ŠคํŠธ ํŽธ์ง‘๊ธฐ ์‚ฌ์šฉ๋ฒ•/How to use a simple text editor</h3>"
                                "<p><strong>ํŒŒ์ผ ๋ฉ”๋‰ด:</strong> ์ƒˆ ํŒŒ์ผ, ์—ด๊ธฐ, ์ €์žฅ, PDF/Base64 ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.</p>"
                                "<p><strong>File Menu:</strong> Supports New File, Open, Save, and PDF/Base64 Export.</p>"
                                "<p><strong>ํŽธ์ง‘ ๋ฉ”๋‰ด:</strong> ์ฐพ๊ธฐ ๋ฐ ๋ฐ”๊พธ๊ธฐ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>"
                                "<p><strong>Edit menu:</strong> You can use the Find and Replace feature.</p>"
                                "<p><strong>๋ทฐ ๋ฉ”๋‰ด:</strong> ํ…์ŠคํŠธ ์คŒ์ธ/์คŒ์•„์›ƒ/์ดˆ๊ธฐํ™”๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>"
                                "<p><strong>View Menu:</strong> You can zoom in/out/reset the text.</p>"
                                "<p><strong>์„œ์‹ ์ง€์ •:</strong> ํ…์ŠคํŠธ๋ฅผ ๊ตต๊ฒŒ, ๊ธฐ์šธ์ž„๊ผด, ๋ฐ‘์ค„, ์ทจ์†Œ์„ ์œผ๋กœ ์„ค์ •ํ•˜๊ณ , ์ƒ‰์ƒ ๋ฐ ํฐํŠธ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>"
                                "<p><strong>Formatting:</strong> You can make text bold, italic, underline, strikethrough, and change the color and font.</p>"
                                "<p><strong>์ด๋ฏธ์ง€:</strong> ๋กœ์ปฌ ์ด๋ฏธ์ง€๋ฅผ ์‚ฝ์ž…ํ•˜๊ณ  ๋งˆ์šฐ์Šค๋กœ ํฌ๊ธฐ๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>"
                                "<p><strong>Image:</strong> Insert a local image and resize it with your mouse.</p>"
                                ),
                             QMessageBox::Ok);
}

// about slot
void MainWindow::actionAbout()
{
    QMessageBox::about(this,
                       tr("Rich Text Editor ์ •๋ณด/Rich Text Editor information"),
                       tr("<h2>Rich Text Editor v1.0 (Qt)</h2>"
                          "<p>Qt ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœ๋œ ๊ฐ„๋‹จํ•œ ํ…์ŠคํŠธ ํŽธ์ง‘๊ธฐ์ž…๋‹ˆ๋‹ค.</p>"
                          "<p>A Rich text editor developed based on the Qt framework.</p>"
                          "<p>์ฃผ์š” ๊ธฐ๋Šฅ: ํŒŒ์ผ ์ž…์ถœ๋ ฅ, ํ…์ŠคํŠธ ์„œ์‹ ์ง€์ •, ์ด๋ฏธ์ง€ ์‚ฝ์ž… ๋ฐ ํฌ๊ธฐ ์กฐ์ ˆ.</p>"
                          "<p>Key features: file input/output, text formatting, image insertion and resizing.</p>"
                          "<p>Copyright &copy; 2025</p>"

                          ));
}


// ๋ฉ”๋ชจ๋ฆฌํ•ด์ œ / "<p>Key features: file input/output, text formatting, image insertion and resizing.</p>"
MainWindow::~MainWindow()
{
    delete ui;
}


Leave a Reply

Your email address will not be published. Required fields are marked *