๐์๋๋ 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 © 2025</p>"
));
}
// ๋ฉ๋ชจ๋ฆฌํด์ / "<p>Key features: file input/output, text formatting, image insertion and resizing.</p>"
MainWindow::~MainWindow()
{
delete ui;
}