QT SIMPLEPAD 2.1

Не буду углубляться в процесс пояснения как пользоваться программой QT Creator, а просто приведу здесь текст исходных файлов и поясню как это работает. Честно говоря, я сам с удовльствием пользуюсь этим приложением, поскольку лично для меня оно имеет довольно интересную функцию, а именно автоматическое сохранение текста при любом изменении файла, что позволяет открыть его в том виде, каким он был во время работы даже в том случае, если у вас внезапоно отрубили свет. Примечательно, что исходный файл при этом не переписывается, то есть... Другими словами, во время редактирования исходный сайт остается неизменным, но программа помнит внесенные изменения и их можно сохранить в исходный файл в любой момент. Правда, если после открытия программы вы все же решитесь открыть другой файл, то все ваши изменения будут забыты. В остальном почти все то же, что и везде: "открыть, сохранить, сохранить как, закрыть, отменить, повторить, выровнять текст", есть справка о программе и об авторе, а так же отдельное поле для записи баг-репортов.
Обнаруженные на данный момент проблемы:
1. При отмене действия сочетанием клавиш Сtrl+z статус-бар не получает сообщения об отмене действия. В то же время, соответствующий триггер в меню "файл" это сообщение выводит. Аналогичная функция повтора действия сочетанием клавиш Ctrl+R отлично работает. Возможно, проблема заключается в сочетании клавиш - во втором случае сочетание нестандартное. Возможно, стандартные сочетания перехватываются другим обработчиком.
2. Замечена какая-то фигня с кодировкой русских символов при сохранении файла. Вместо исходного файла программа создает другой файл, имя которого состоит из квадратиков. Примечательно, что это наблюдается в Windows 10, в Windows 7, кажется, ничего подобного не было. Так или иначе, проблема устранена, подробности на https://ru.stackoverflow.com/.
К сожалению, я пока не разобрался с диплоем QT программ под Linux, поэтому для корректной работы релизной версии программы необходимо установить QT Creator с комплектом Desktop QT 5.15.1 GCC 64Bit. Буду рад, если кто-нибудь поможет разобраться с диплоем Linux, если вы разбираетесь в этом, можно связаться где-нибудь в Discorde и организовать небольшую учебную сессию через AnyDesk, я за это заплачу.
SimplePad.pro
QT += widgets
QT += printsupport
QT += core gui
TRANSLATIONS = SimplePad_ru_RU.ts_ru.ts
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
LIBS += -L"./lib"
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
    about.cpp \
    main.cpp \
    mainwindow.cpp \
    service.cpp
HEADERS += \
    about.h \
    global.h \
    mainwindow.h \
    service.h
FORMS += \
    about.ui \
    mainwindow.ui \
    service.ui
TRANSLATIONS += \
    SimplePad_ru_RU.ts
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += res.qrc
RC_FILE = ico.rc //единственный рабочий способ задать иконку программы в Windows. Требует создания соответствующего файла со следующим содержимым:
//IDI_ICON1 ICON DISCARDABLE "SimplePad.ico"
 about.h
#ifndef ABOUT_H
#define ABOUT_H
#include "QDialog"
namespace Ui {
class About;
}
class About : public QDialog
{
    Q_OBJECT
public:
    explicit About(QWidget *parent = nullptr);
    ~About();
private slots:
    void on_pushButton_clicked();
private:
    Ui::About *ui;
};
#endif // ABOUT_H global.h
#ifndef GLOBAL_H #define GLOBAL_H #endif // GLOBAL_H #include "QString" QString FN = "Данная кнопка выводит на экран информацию, записанную в глобальной переменной FN, отвечающей за хранение пути и имени читаемого файла. Если вы видите данное сообщение, значит файл еще не открыт."; //Это сообщение выводится для скрытой кнопки FN, которую обычно не видно и которая, может быть, даже не нужна. QString CR=0; QString TP = "temp";
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "QMainWindow"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void on_act1Open_triggered();
    void on_act3Saveas_triggered();
    void on_act4Close_triggered();
    void on_About1_triggered();
    void on_About2_triggered();
    void on_Btn1_clicked();
    void on_Btn2_clicked();
    void on_Btn3_clicked();
    void on_Btn4_clicked();
    void on_act2Save_triggered();
    void on_act5Quit_triggered();
    void on_textEdit_textChanged();
    //Вот эти новым методом обеспечивают функции "Отмена", "повторение"
    void undo();
    void redo();
    //
    void on_Justify_triggered();
    void on_Left_triggered();
    void on_Right_triggered();
    void on_Center_triggered();
    void on_BR_triggered();
    void on_Undo_triggered();
    void on_Redo_triggered();
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
 service.h
#ifndef SERVICE_H
#define SERVICE_H
#include "QDialog"
namespace Ui {
class service;
}
class service : public QDialog
{
    Q_OBJECT
public:
    explicit service(QWidget *parent = nullptr);
    ~service();
private slots:
    void on_tE2_textChanged();
private:
    Ui::service *ui;
};
#endif // SERVICE_H
 about.cpp
#include "about.h"
#include "ui_about.h"
#include "QPixmap"
#include "QLabel"
About::About(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::About)
{
    ui->setupUi(this);
    QPixmap image("madmentat.jpg");
}
About::~About()
{
    delete ui;
}
void About::on_pushButton_clicked()
{
    close();
}
 main.cpp
#include "mainwindow.h"
#include "QApplication"
int main(int argc, char *argv[])
{
     QApplication app(argc, argv);
     MainWindow w; // Объявляем новое окошко
     w.setWindowTitle(("SimplePad v 2.1")); // Задаем имя окошка. Такое, в полосочке наверху.
     w.show(); // Выводим новое окошко на экран.
     return app.exec();
}
 mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QFile"
#include "QTextStream"
#include "QMessageBox"
#include "QTextEdit"
#include "QPushButton"
#include "QFileDialog"
#include "QDialog"
#include "QDebug"
#include "QString"
#include "QPrinter"
#include "QPrintDialog"
#include "QTextCodec"
#include "about.h"
#include "global.h"
#include "service.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setCentralWidget(ui->textEdit); //вот эта строчка растягивает textEdit на все поле программы.
    //Далее билеберда которая обеспечивает функции "Отменить, повторить"
    connect(ui->Undo, &QAction::triggered, this, &MainWindow::undo);
    connect(ui->Redo, &QAction::triggered, this, &MainWindow::redo);
    //Следующая конструкция читает из temp текст, который был открыт в предыдущий раз когда открывалась программа и выводит его в текстовое поле.
    QFile tempFile("temp");
    if (!tempFile.open(QIODevice::ReadOnly | QIODevice::Text))
    qDebug() << "Ошибка при открытии файла";
    QTextStream stream(&tempFile);
    ui->textEdit->setText(stream.readAll()); //Вывод в текстовое поле содержимое читаемого файла
    //ui->textEdit->setText(fileName); //Вывод в текстовое поле пути файла и его названия
    //Следующая констукция считывает путь и имя файла из fn
    QFile name("fn"); //значит, о том, что тут происходит, по-порядку. Тут мы вызываем метод QFile по имени name, который читает содежимое файла "fn"
    if (!name.open(QIODevice::ReadOnly | QIODevice::Text))
    return;
            QString str;   //объявляем текстовую переменную для хранения пути и имени файла, которые мы собираемся получить в следующей строке
            //Следующий кусок кода необходим для того, чтобы на Винде не вылезали крокозябры, так как по умолчанию Виндоз использует кодировку CP1251
#ifdef Q_OS_WIN64 //задаем условия компиляции для Win64
            QTextCodec *codec = QTextCodec::codecForName("CP1251");
            assert(codec);
            str = codec->toUnicode(name.readAll());
#endif
#ifdef Q_OS_LINUX //задаем условия компиляции для Linux
            str = name.readAll();
#endif
            FN=str; // Глобальная переменная FN теперь содержит тоже, что и переменная str
            statusBar()->showMessage("Открыт файл " + FN);
       //Отладочное окошко где показано содержание FN
       //QTextEdit *editor = new QTextEdit;
       //editor->setText(FN);
       //editor->show();
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::on_act1Open_triggered() // Открываем файл
{
//далее новый вариант открытия файла где можно выбрать тип файла
     QString fileName = QFileDialog::getOpenFileName(this,
                                        QString("Открыть файл"),
                                        QDir::currentPath(),
                                 "Текстовые файлы (*.txt);;Все файлы (*.*)");
     FN=fileName; //Запись имени файла в глобальную переменную FN
     QFile file(fileName);
     if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
             return;
     qDebug() << "Ошибка при открытии файла";
     QTextStream in(&file);
     ui->textEdit->setText(in.readAll()); //Вывод в текстовое поле содержимое читаемого файла
     //ui->textEdit->setText(fileName); //Вывод в текстовое поле пути файла и его названия
     //ui->textEdit->setText(FN); //Вывод в текстовое поле содержимое глобальной переменной FN
     //Следующая конструкция пишет путь и имя файла в файл fn
     QFile fn("fn");
     if (!fn.open(QIODevice::WriteOnly | QIODevice::Text))
              return;
     QTextStream writeStream(&fn);
     writeStream << FN;
     statusBar()->showMessage("Открыт файл "+FN);
}
void MainWindow::on_act3Saveas_triggered()
{
    QString fileName = QFileDialog::getSaveFileName(this,
                                QString("Открыть файл"),
                                QDir::currentPath(),
                                "Текстовые файлы (*.txt);;Все файлы (*.*)");
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
            return;
        QTextStream out(&file); //поток для записи текста
        out <textEdit->toPlainText();
        statusBar()->showMessage("Файл сохранен "+FN); // Эта функция выводит соответствующее сообщение в статусбар. Цифра в конце - время отображения.
}
void MainWindow::on_Btn1_clicked()
{
    QTextEdit textEdit; // Объявляем наше текстовое поле
    ui->textEdit->clear(); // Очищаем его
}
void MainWindow::on_Btn2_clicked()
{
    qApp->closeAllWindows(); // вот эта строчка, по-видимому, закрывает все окна приложения
    qApp->quit(); // а эта закрывает само приложение
}
void MainWindow::on_About1_triggered()
{
    QMessageBox::information(NULL,QObject::tr("О программе"),tr("SimplePad 2.1 - Данное приложение создано для быстрой записи текстовой информации. Тут имеются функции Undo и Redo, позволяющие отменять и повторять ваши действия. Программа может открывать файлы, имеет два способа сохранения, закрывает файлы и выводит соответствующие сообщения в статус-бар. Кроме того, она помнит изменения в файле, который вы редактировали в предыдущий раз, но не сохраняет их, пока вы не сделаете это определенным сочетанием клавиш или кликом по соответствующему пункту меню."));
}
void MainWindow::on_About2_triggered()
{
    About *frm = new About();
                 frm->show();
}
void MainWindow::on_act2Save_triggered()
{
    QFile file(FN); //Запись в файл, имя которого взято из переменной FN
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
             return;
    QTextStream out(&file);//поток для записи текста
    out <textEdit->toPlainText();
    statusBar()->showMessage("Файл сохранен в "+FN);
}
void MainWindow::on_Btn3_clicked()
{
    ui->textEdit->setText(FN);
}
void MainWindow::on_act4Close_triggered()
{
    //Здесь наша задача заставить программу при закрытии файла изменить путь сохранения на файл временного хранения temp
    //чтобы все дальнейшие изменения в текстовом поле сохранились после закрытия и повторного открытия программы
    //FN=TP; //Глобальная переменная FN получает значение глобальной переменной TP //забал зачем... Или же... Вот, в чем дело. Действительно, хотел сделать чтобы 
    QTextEdit textEdit; // Объявляем наше текстовое поле
    ui->textEdit->clear(); // Очищаем его
    //Далее записываем в файл fn путь и имя открытого файла
    QFile fn("fn");
    if (!fn.open(QIODevice::WriteOnly | QIODevice::Text))
             return;
    QTextStream writeStream(&fn);
    writeStream << FN;
    statusBar()->showMessage("Файл закрыт. Во избежание потери данных, все дальнейшие изменения следует сохранить при помощи команды Alt+S");
}
void MainWindow::on_act5Quit_triggered()
{
    QApplication::exit();
}
void MainWindow::on_textEdit_textChanged() //при изменении текста происходит автоматическая запись в файл temp
{
        QFile file("temp");
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
                 return;
        QTextStream out(&file);//поток для записи текста
        out <textEdit->toPlainText();
        statusBar()->showMessage("Сохранено в "+FN, 10000);
}
void MainWindow::on_Btn4_clicked() //кнопка показывает содержимое файла temp
{
    QFile file("temp");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return;
    qDebug() << "Ошибка при открытии файла";
    QTextStream in(&file);
    ui->textEdit->setText(in.readAll());
}
void MainWindow::on_BR_triggered()
{
    service *frm = new service();
                     frm->show();
}
//Функции "отменить", "повторить"
void MainWindow::undo()
{
     ui->textEdit->undo();
     statusBar()->showMessage("Действие отменено "+FN);
}
void MainWindow::redo()
{
    ui->textEdit->redo();
    statusBar()->showMessage("Действие возвращено "+FN);
}
void MainWindow::on_Justify_triggered() //выровнять по горизонтали
{
    QTextCursor cursor = ui->textEdit->textCursor();
    QTextBlockFormat textBlockFormat = cursor.blockFormat();
    textBlockFormat.setAlignment(Qt::AlignJustify);
    cursor.mergeBlockFormat(textBlockFormat);
    ui->textEdit->setTextCursor(cursor);
}
void MainWindow::on_Left_triggered() //выровнять по левому краю
{
    QTextCursor cursor = ui->textEdit->textCursor();
    QTextBlockFormat textBlockFormat = cursor.blockFormat();
    textBlockFormat.setAlignment(Qt::AlignLeft);
    cursor.mergeBlockFormat(textBlockFormat);
    ui->textEdit->setTextCursor(cursor);
}
void MainWindow::on_Right_triggered() //выровнять по правому краю
{
    QTextCursor cursor = ui->textEdit->textCursor();
    QTextBlockFormat textBlockFormat = cursor.blockFormat();
    textBlockFormat.setAlignment(Qt::AlignRight);
    cursor.mergeBlockFormat(textBlockFormat);
    ui->textEdit->setTextCursor(cursor);
}
void MainWindow::on_Center_triggered()
{
    QTextCursor cursor = ui->textEdit->textCursor();
    QTextBlockFormat textBlockFormat = cursor.blockFormat();
    textBlockFormat.setAlignment(Qt::AlignCenter);
    cursor.mergeBlockFormat(textBlockFormat);
    ui->textEdit->setTextCursor(cursor);
}
void MainWindow::on_Undo_triggered()
{
    ui->textEdit->undo();
    statusBar()->showMessage("Действие отменено "+FN);
}
void MainWindow::on_Redo_triggered()
{
    ui->textEdit->redo();
    statusBar()->showMessage("Действие возвращено "+FN);
}
void MainWindow::on_Print_triggered()
{
    QTextEdit textEdit;
#ifndef QT_NO_PRINTER //интересная команда #ifndef - как бы инверсированная версия #ifdef - в данном случае вроде как "если принтер не неопределен, то"...
    QPrinter printer(QPrinter::HighResolution);
    QPrintDialog *dlg = new QPrintDialog(&printer, this);
    if (ui->textEdit->textCursor().hasSelection())
    dlg->addEnabledOption(QAbstractPrintDialog::PrintSelection);
    dlg->setWindowTitle(tr("Print Document"));
    if (dlg->exec() == QDialog::Accepted) {
    ui->textEdit->print(&printer);
    }
    delete dlg;
#endif
}
 res.qrc
<RCC>
<qresource prefix="/text">
<file>temp</file>
<file>fn</file>
</qresource>
<qresource prefix="/images">
<file>simplePad.png</file>
<file>madmentat.jpg</file>
</qresource>
</RCC>
