第48课 替换对话框的设计与实现

1. 替换对话框需求分析

(1)可复用的软件部件

(2)查找文本框中的指定字符串

(3)替换单个指定字符串

(4)替换所有指定字符串

(5)附加需求:点击“关闭”按钮后隐藏

2. 替换对话框的设计与实现

(1)继承类图

(2)界面布局

3. 替换算法流程图

4. MainWindow与ReplaceDialog之间的关系

【编程实现】替换功能的实现

//main.cpp与上例相同

#include "MainWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow* w = MainWindow::NewInstance();
    int ret = -1;

    if(w != NULL)
    {
        w->show();
        ret = a.exec();
    }

    return ret;
}

//FindDialog.h与上例相同

#ifndef _FINDDIALOG_H_
#define _FINDDIALOG_H_

#include <QDialog>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QRadioButton>
#include <QCheckBox>
#include <QGroupBox>
#include <QPlainTextEdit>
#include <QPointer>

class FindDialog : public QDialog
{
    Q_OBJECT
protected:
    QGroupBox m_radioGrpBx;

    QGridLayout m_layout;
    QHBoxLayout m_hbLayout;

    QLabel m_findLbl;
    QLineEdit m_findEdit;
    QPushButton m_findBtn;
    QPushButton m_closeBtn;
    QCheckBox m_matchChkBx;
    QRadioButton m_forwardBtn;
    QRadioButton m_backwardBtn;

    //QPointer的特点:
    //(1)当QPointer对象本身被销毁时,不会自动销毁智能指针所指的对象。
    //(2)当m_pText所指的Qt对象被销毁时,m_pText会被自动设置为NULL,
    //从而不会造成多次释放内存的错误。
    //这两个特点很适合用来设计对象间的松耦合
    //注意:m_pText
    //(1)m_pText本身是一个对象,会在作用域结束会被自动释放。
    //(2)其内部重载了delete操作符,使delete m_pText删除的是其所指的对象。
    QPointer<QPlainTextEdit> m_pText;

    void initControl();
    void connectSlot();

protected slots:
    void onFindClicked();
    void onCloseClicked();

public:
    FindDialog(QWidget* parent = 0, QPlainTextEdit* pText = 0);
    void setPlainTextEdit(QPlainTextEdit* pText);
    QPlainTextEdit* getPlainTextEdit();
    bool event(QEvent* evt);
};

#endif // _FINDDIALOG_H_

//FindDialog.cpp与上例相同

#include "FindDialog.h"
#include <QEvent>
#include <QMessageBox>
#include <QTextCursor>

FindDialog::FindDialog(QWidget* parent,QPlainTextEdit* pText):QDialog(parent, Qt::WindowCloseButtonHint | Qt::Drawer)
{
    initControl();
    connectSlot();

    setWindowTitle("Find");
    setPlainTextEdit(pText);
}

void FindDialog::initControl()
{
    m_findLbl.setText("Find What:");
    m_findBtn.setText("Find Next");
    m_closeBtn.setText("Close");
    m_matchChkBx.setText("Match Case");
    m_backwardBtn.setText("Backward");
    m_backwardBtn.setChecked(true);
    m_forwardBtn.setText("Forward");
    m_radioGrpBx.setTitle("Direction");

    m_hbLayout.addWidget(&m_forwardBtn);
    m_hbLayout.addWidget(&m_backwardBtn);
    m_radioGrpBx.setLayout(&m_hbLayout);

    m_layout.setSpacing(10);
    m_layout.addWidget(&m_findLbl, 0, 0);
    m_layout.addWidget(&m_findEdit, 0, 1);
    m_layout.addWidget(&m_findBtn, 0, 2);
    m_layout.addWidget(&m_matchChkBx, 1, 0);
    m_layout.addWidget(&m_radioGrpBx, 1, 1);
    m_layout.addWidget(&m_closeBtn, 1, 2);

    setLayout(&m_layout);
}

void FindDialog::connectSlot()
{
    connect(&m_findBtn, SIGNAL(clicked()), this, SLOT(onFindClicked()));
    connect(&m_closeBtn, SIGNAL(clicked()), this, SLOT(onCloseClicked()));
}

void FindDialog::setPlainTextEdit(QPlainTextEdit* pText)
{
    //m_pText是个智能指针,使用上可以当成一般的指针来使用。
    m_pText = pText;
}

QPlainTextEdit* FindDialog::getPlainTextEdit()
{
    return m_pText;
}

//附加需求,当点击“关闭”后隐藏起来
bool FindDialog::event(QEvent *evt)
{
    if(evt->type() == QEvent::Close)
    {
        hide();
        return true;
    }

    return QDialog::event(evt);
}

void FindDialog::onFindClicked()
{
    QString target = m_findEdit.text();

    if ((m_pText == NULL) || (target == ""))
        return ;

    QString text = m_pText->toPlainText();//编辑框中的文本
    QTextCursor c = m_pText->textCursor();//光标对象
    int index = -1;

    //向后(即字符串末尾)查找
    if(m_backwardBtn.isChecked())
    {
        index = text.indexOf(target, c.position(),
                             m_matchChkBx.isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);

        if(index >=0)
        {
            c.setPosition(index);
            c.setPosition(index + target.length(), QTextCursor::KeepAnchor);//KeepAnchor锚点位置不改变,起到选择范围的作用

            m_pText->setTextCursor(c);
        }
    }
    else //向前(向字符串起始处方向)查找
    {
        index = text.lastIndexOf(target, c.position() - text.length() - 1,
                             m_matchChkBx.isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);

        if(index >= 0)
        {
            c.setPosition(index + target.length());
            c.setPosition(index, QTextCursor::KeepAnchor);//KeepAnchor锚点位置不改变,起到选择范围的作用

            m_pText->setTextCursor(c);
        }
    }

    if(index < 0)
    {
        QMessageBox msg(this);
        msg.setWindowTitle("Find");
        msg.setText("Can not find \"" + target + "\" any more...");
        msg.setStandardButtons(QMessageBox::Ok);

        msg.exec();
    }
}

void FindDialog::onCloseClicked()
{
    close();
}

//ReplaceDialog.h

#ifndef _REPLACEDIALOG_H_
#define _REPLACEDIALOG_H_

#include "FindDialog.h"

class ReplaceDialog : public FindDialog
{
    Q_OBJECT
protected:
    QLabel m_replaceLbl;
    QLineEdit m_replaceEdit;
    QPushButton m_replaceBtn;
    QPushButton m_replaceAllBtn;

    void initControl();
    void connectSlot();

protected slots:
    void onReplaceClicked();
    void onReplaceAllClicked();

public:
    ReplaceDialog(QWidget* parent = 0, QPlainTextEdit* pText = 0);
};

#endif // _REPLACEDIALOG_H_

//ReplaceDialog.cpp

#include "ReplaceDialog.h"

ReplaceDialog::ReplaceDialog(QWidget* parent, QPlainTextEdit* pText):
    FindDialog(parent, pText)
{
    initControl();
    connectSlot();
    setWindowTitle("Replace");
}

void ReplaceDialog::initControl()
{
    m_replaceLbl.setText("Replace To:");
    m_replaceBtn.setText("Replace");
    m_replaceAllBtn.setText("Replace All");

    m_layout.removeWidget(&m_matchChkBx);
    m_layout.removeWidget(&m_radioGrpBx);
    m_layout.removeWidget(&m_closeBtn);

    m_layout.addWidget(&m_replaceLbl,   1, 0);
    m_layout.addWidget(&m_replaceEdit,  1, 1);
    m_layout.addWidget(&m_replaceBtn,   1, 2);
    m_layout.addWidget(&m_matchChkBx,   2, 0);
    m_layout.addWidget(&m_radioGrpBx,   2, 1);
    m_layout.addWidget(&m_replaceAllBtn,2, 2);
    m_layout.addWidget(&m_closeBtn,     3, 2);
}

void ReplaceDialog::connectSlot()
{
    connect(&m_replaceBtn, SIGNAL(clicked()), this, SLOT(onReplaceClicked()));
    connect(&m_replaceAllBtn, SIGNAL(clicked()), this, SLOT(onReplaceAllClicked()));
}

void ReplaceDialog::onReplaceClicked()
{
    QString from = m_findEdit.text();
    QString to = m_replaceEdit.text();

    if((m_pText == NULL) || (from ==""))
        return;

    QString selText = m_pText->textCursor().selectedText();
    if(selText == from)
    {
        m_pText->insertPlainText(to);
    }

    onFindClicked();//继续查找下一个
}

void ReplaceDialog::onReplaceAllClicked()
{
    QString from = m_findEdit.text();
    QString to = m_replaceEdit.text();

    if((m_pText == NULL) || (from ==""))
        return;

    QString text = m_pText->toPlainText();
    text.replace(from, to, m_matchChkBx.isChecked() ?
                 Qt::CaseSensitive : Qt::CaseInsensitive);
    //m_pText->clear();
    //m_pText->insertPlainText(text);
    m_pText->document()->setPlainText(text);
}

//MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMenuBar>
//#include <QKeySequence>
//#include <QAction>
#include <QPlainTextEdit>
#include <QLabel>
#include <QFileDialog>
#include <QSharedPointer>
#include "FindDialog.h"
#include "ReplaceDialog.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT
private:

    QPlainTextEdit mainEditor;
    QLabel statusLbl;
    QString m_filePath;//当前操作的文件路径
    bool m_isTextChanged; //标识编辑框中的内容是否改变
    QSharedPointer<FindDialog> m_pFindDlg;//组合关系,利用智能指针来管理其生命周期
    QSharedPointer<ReplaceDialog> m_pRelaceDlg;

    //将构造函数、复制构造、赋值函数等私有化
    MainWindow(QWidget *parent = 0);
    MainWindow(const MainWindow&);
    MainWindow& operator= (const MainWindow&);

    bool construct(); //二阶构造模式

    bool initMenuBar();   //初始化菜单栏
    bool initToolBar();   //初始化工具栏
    bool initStatusBar(); //初始化状态栏
    bool initMainEditor();//初始化文本编辑组件

    //菜单设置
    bool initFileMenu(QMenuBar* mb);    //“文件”菜单
    bool initEditMenu(QMenuBar* mb);    //“编辑”菜单
    bool initFormatMenu(QMenuBar* mb);  //“格式”菜单
    bool initViewMenu(QMenuBar* mb);    //“查看”菜单
    bool initHelpMenu(QMenuBar* mb);    //“帮助”菜单

    //工具栏设置
    bool initFileToolItem(QToolBar* tb);
    bool initEditToolItem(QToolBar* tb);
    bool initFormatToolItem(QToolBar* tb);
    bool initViewToolItem(QToolBar* tb);

    //生成菜单项
    bool makeAction(QAction*& action, QWidget* parent, QString text, int key);
    //生成工具栏中的各按钮
    bool makeAction(QAction*& action, QWidget* parent, QString tip, QString icon);

    QString showFileDialog(QFileDialog::AcceptMode mode, QString title);//显示打开和保存对话框
    void showErrorMessage(QString message);//显示“错误对话框”(QMessagBox)
    int showQueryMessage(QString message); //显示“询问对话框”
    QString saveCurrentData(QString path = "", QString title = "Save"); //保存编辑框中的内容
    void preEditorChanged(); //判断文本框是否被更改,并决定是否弹出“保存对话框”
    void openFileToEditor(QString path);
    QAction* findMenuBarAction(QString text);
    QAction* findToolBarAction(QString text);

protected:
    void closeEvent(QCloseEvent *e); //重写窗体的关闭事件
    void dragEnterEvent(QDragEnterEvent* e);
    void dropEvent(QDropEvent * e);

private slots:
    void onFileNew();
    void onFileOpen();
    void onFileSave();
    void onFileSaveAs();
    void onFilePrint();
    void onFileExit();
    void onTextChanged();//文本框内容发生改变里,会收到textChanged信号,这里是接收该信号的槽函数

    void onCopyAvailable(bool available);
    void onRedoAvailable(bool available);
    void onUndoAvailable(bool available);
    void onCursorPositionChanged();
    void onEditDelete();
    void onEditFind();
    void onEditReplace();
public:
    static MainWindow* NewInstance();

    ~MainWindow();
};

#endif // MAINWINDOW_H

//MainWindowUI.cpp

#include "MainWindow.h"
#include <QMenu>
#include <QToolBar>
#include <QStatusBar>
#include <QLabel>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      m_pFindDlg(new FindDialog(this,&mainEditor)),
      m_pRelaceDlg(new ReplaceDialog(this, &mainEditor))
{
    setWindowTitle("NotePad - [ New ]");
    mainEditor.setAcceptDrops(false);//默认下,QPlainTextEdit是接收拖放事件的
                                     //其父对象会收不到“拖放”事件,这里设为false
    setAcceptDrops(true); //由MainWindow来接管拖放事件

    m_filePath = "";

    m_isTextChanged = false;
}

//二阶构造中的第2阶构造
bool MainWindow::construct()
{
    bool ret = true;

    ret = ret && initMenuBar();
    ret = ret && initToolBar();
    ret = ret && initStatusBar();
    ret = ret && initMainEditor();

    return ret;
}

MainWindow* MainWindow::NewInstance()
{
    MainWindow* ret = new MainWindow();

    if((ret == NULL) || !ret->construct())
    {
        delete ret;
        ret = NULL;
    }

    return ret;
}

//初始化菜单栏
bool MainWindow::initMenuBar()
{
    bool ret = true;
    QMenuBar* mb = menuBar(); //menuBar为QMainWindow的成员变量

    ret = ret && initFileMenu(mb);
    ret = ret && initEditMenu(mb);
    ret = ret && initFormatMenu(mb);
    ret = ret && initViewMenu(mb);
    ret = ret && initHelpMenu(mb);

    return ret;
}

//初始化工具栏
bool MainWindow::initToolBar()
{
    bool ret = true;

    QToolBar* tb = addToolBar("Tool Bar");

    tb->setIconSize(QSize(16, 16));

    ret = ret && initFileToolItem(tb);
    tb->addSeparator();

    ret = ret && initEditToolItem(tb);
    tb->addSeparator();

    ret = ret && initFormatToolItem(tb);
    tb->addSeparator();

    ret = ret && initViewToolItem(tb);

    return ret;
}

//初始化状态栏
bool MainWindow::initStatusBar()
{
    bool ret = true;
    QStatusBar* sb = statusBar();
    QLabel* label = new QLabel("D.T.Software");

    if( label != NULL )
    {
        statusLbl.setMinimumWidth(200);
        statusLbl.setAlignment(Qt::AlignCenter);
        statusLbl.setText("Ln: 1    Col: 1");

        label->setMinimumWidth(200);
        label->setAlignment(Qt::AlignCenter);

        sb->addPermanentWidget(new QLabel()); //添加分隔线
        sb->addPermanentWidget(&statusLbl);
        sb->addPermanentWidget(label);
    }
    else
    {
        ret = false;
    }
    return ret;
}

//初始化主文本编辑组件
bool MainWindow::initMainEditor()
{
    bool ret = true;
    QPalette p = mainEditor.palette();

    p.setColor(QPalette::Inactive, QPalette::Highlight,
               p.color(QPalette::Active, QPalette::Highlight));
    p.setColor(QPalette::Inactive, QPalette::HighlightedText,
               p.color(QPalette::Active, QPalette::HighlightedText));

    mainEditor.setPalette(p);
    mainEditor.setParent(this);

    connect(&mainEditor, SIGNAL(textChanged()), this, SLOT(onTextChanged()));
    connect(&mainEditor, SIGNAL(copyAvailable(bool)),this,SLOT(onCopyAvailable(bool)));
    connect(&mainEditor, SIGNAL(redoAvailable(bool)),this,SLOT(onRedoAvailable(bool)));
    connect(&mainEditor, SIGNAL(undoAvailable(bool)),this,SLOT(onUndoAvailable(bool)));
    connect(&mainEditor, SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged()));
    setCentralWidget(&mainEditor);

    return ret;
}

//文件(下拉)菜单
bool MainWindow::initFileMenu(QMenuBar* mb)
{
    QMenu* menu = new QMenu("File(&F)", mb);//指定parent为mb
    bool ret = (menu != NULL);

    if ( ret )
    {
        QAction* action = NULL;
        //"新建"菜单项
        ret = ret &&  makeAction(action, mb, "New(&N)", Qt::CTRL + Qt::Key_N);
        if(ret)
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onFileNew()));
            menu->addAction(action); //add Action to Menu Item
        }

        //"打开"菜单项
        ret = ret &&  makeAction(action, mb, "Open(&O)...", Qt::CTRL + Qt::Key_O);
        if(ret)
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onFileOpen()));
            menu->addAction(action); //add Action to Menu Item
        }

        //"保存"菜单项
        ret = ret &&  makeAction(action, mb, "Save(&S)", Qt::CTRL + Qt::Key_S);
        if(ret)
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onFileSave()));
            menu->addAction(action); //add Action to Menu Item
        }

        //"另存为"菜单项
        ret = ret &&  makeAction(action, mb, "Save As(&A)...", 0);
        if(ret)
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onFileSaveAs()));
            menu->addAction(action); //add Action to Menu Item
        }

        //水平分隔线
        menu->addSeparator();

        //"打印"菜单项
        ret = ret &&  makeAction(action, mb, "Print(&P)...", Qt::CTRL + Qt::Key_P);
        if(ret)
        {
            connect(action, SIGNAL(triggered()),this, SLOT(onFilePrint()));
            menu->addAction(action); //add Action to Menu Item
        }

        menu->addSeparator();

        //"退出"菜单项
        ret = ret && makeAction(action, mb, "Exit(&X)", Qt::CTRL + Qt::Key_Q);
        if(ret)
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onFileExit()));
            menu->addAction(action); //add Action Item to Menu
        }
    }

    if ( ret )
    {
        mb->addMenu(menu); //add Menu to Menu Bar
    }
    else
    {
        delete menu;
    }

    return ret;
}

//“编辑”菜单
bool MainWindow::initEditMenu(QMenuBar* mb)
{
    QMenu* menu = new QMenu("Edit(&E)", mb);
    bool ret = (menu != NULL);

    if( ret )
    {
        QAction* action = NULL;

        ret = ret && makeAction(action, mb, "Undo(&U)", Qt::CTRL + Qt::Key_Z);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), &mainEditor, SLOT(undo()));
            action->setEnabled(false);
            menu->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Redo(&R)...", Qt::CTRL + Qt::Key_Y);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), &mainEditor, SLOT(redo()));
            action->setEnabled(false);
            menu->addAction(action);
        }

        menu->addSeparator();

        ret = ret && makeAction(action, mb, "Cut(&T)", Qt::CTRL + Qt::Key_X);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), &mainEditor, SLOT(cut()));
            action->setEnabled(false);
            menu->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Copy(&C)", Qt::CTRL + Qt::Key_C);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), &mainEditor, SLOT(copy()));
            action->setEnabled(false);
            menu->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Paste(&P)", Qt::CTRL + Qt::Key_V);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), &mainEditor, SLOT(paste()));
            menu->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Delete(&L)", Qt::Key_Delete);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onEditDelete()));
            menu->addAction(action);
        }

        menu->addSeparator();

        ret = ret && makeAction(action, mb,  "Find(&F)...", Qt::CTRL + Qt::Key_F);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onEditFind()));
            menu->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Replace(&R)...", Qt::CTRL + Qt::Key_H);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), this, SLOT(onEditReplace()));
            menu->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Goto(&G)...", Qt::CTRL + Qt::Key_G);
        if ( ret )
        {
            menu->addAction(action);
        }

        menu->addSeparator();

        ret = ret && makeAction(action, mb, "Select All(&A)...", Qt::CTRL + Qt::Key_A);
        if ( ret )
        {
            connect(action, SIGNAL(triggered()), &mainEditor, SLOT(selectAll()));
            menu->addAction(action);
        }
    }

    if ( ret )
    {
        mb->addMenu(menu);
    }
    else
    {
        delete menu;
    }

    return ret;
}

//“格式”菜单
bool MainWindow::initFormatMenu(QMenuBar* mb)
{
    QMenu* menu = new QMenu("Format(&O)", mb);
    bool ret = ( menu != NULL);

    if (ret)
    {
        QAction* action = NULL;

        ret = ret && makeAction(action, mb, "Auto Wrap(&W)", 0);
        if ( ret )
        {
            menu ->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Font(&F)...", 0);
        if ( ret )
        {
            menu ->addAction(action);
        }
    }

    if ( ret )
    {
        mb->addMenu(menu);
    }
    else
    {
        delete menu;
    }

    return ret;
}

//“查看”菜单
bool MainWindow::initViewMenu(QMenuBar* mb)
{
    QMenu* menu = new QMenu("View(&V)", mb);
    bool ret = ( menu != NULL);

    if (ret)
    {
        QAction* action = NULL;

        ret = ret && makeAction(action, mb, "Tool Bar(&T)", 0);
        if ( ret )
        {
            menu ->addAction(action);
        }

        ret = ret && makeAction(action, mb, "Status Bar(&S)", 0);
        if ( ret )
        {
            menu ->addAction(action);
        }
    }

    if ( ret )
    {
        mb->addMenu(menu);
    }
    else
    {
        delete menu;
    }

    return ret;
}

//“帮助”菜单
bool MainWindow::initHelpMenu(QMenuBar* mb)
{
    QMenu* menu = new QMenu("Help(&H)", mb);
    bool ret = ( menu != NULL);

    if (ret)
    {
        QAction* action = NULL;

        ret = ret && makeAction(action, mb, "User Manual", 0);
        if ( ret )
        {
            menu ->addAction(action);
        }

        ret = ret && makeAction(action, mb, "About NotePad...", 0);
        if ( ret )
        {
            menu ->addAction(action);
        }
    }

    if ( ret )
    {
        mb->addMenu(menu);
    }
    else
    {
        delete menu;
    }

    return ret;
}

//工具栏设置
bool MainWindow::initFileToolItem(QToolBar* tb)
{
    bool ret = true;
    QAction* action = NULL;

    ret = ret && makeAction(action, tb, "New", ":/Res/pic/new.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), this, SLOT(onFileNew()));
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Open", ":/Res/pic/open.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), this, SLOT(onFileOpen()));
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Save", ":/Res/pic/save.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), this, SLOT(onFileSave()));
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Save As", ":/Res/pic/saveas.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), this, SLOT(onFileSaveAs()));
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Print", ":/Res/pic/print.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()),this, SLOT(onFilePrint()));
        tb->addAction(action);
    }

    return ret;
}

bool MainWindow::initEditToolItem(QToolBar* tb)
{
    bool ret = true;
    QAction* action = NULL;

    ret = ret && makeAction(action, tb, "Undo", ":/Res/pic/undo.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), &mainEditor, SLOT(undo()));
        action->setEnabled(false);
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Redo", ":/Res/pic/redo.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), &mainEditor, SLOT(redo()));
        action->setEnabled(false);
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Cut", ":/Res/pic/cut.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), &mainEditor, SLOT(cut()));
        action->setEnabled(false);
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Copy", ":/Res/pic/copy.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), &mainEditor, SLOT(copy()));
        action->setEnabled(false);
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Paste", ":/Res/pic/paste.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), &mainEditor, SLOT(paste()));
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Find", ":/Res/pic/find.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), this, SLOT(onEditFind()));
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Replace", ":/Res/pic/replace.png");
    if ( ret )
    {
        connect(action, SIGNAL(triggered()), this, SLOT(onEditReplace()));
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Goto", ":/Res/pic/goto.png");
    if ( ret )
    {
        tb->addAction(action);
    }

    return ret;
}

bool MainWindow::initFormatToolItem(QToolBar* tb)
{
    bool ret = true;
    QAction* action = NULL;

    ret = ret && makeAction(action, tb, "Auto Wrap", ":/Res/pic/wrap.png");
    if ( ret )
    {
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Font", ":/Res/pic/font.png");
    if ( ret )
    {
        tb->addAction(action);
    }

    return ret;
}

bool MainWindow::initViewToolItem(QToolBar* tb)
{
    bool ret = true;
    QAction* action = NULL;

    ret = ret && makeAction(action, tb, "Tool Bar", ":/Res/pic/tool.png");
    if ( ret )
    {
        tb->addAction(action);
    }

    ret = ret && makeAction(action, tb, "Status Bar", ":/Res/pic/status.png");
    if ( ret )
    {
        tb->addAction(action);
    }

    return ret;
}

//生成菜单项(第1个参数是菜单项对象,第2个参数为父组件,第3个参数为显示文本,第4个参数为快捷键)
bool MainWindow::makeAction(QAction*& action, QWidget* parent, QString text, int key)
{
    bool ret = true;

    action = new QAction(text, parent); //设置parent

    if(action != NULL)
    {
        action->setShortcut(QKeySequence(key)); //设置快捷键
    }
    else
    {
        ret = false;
    }

    return ret;
}

//生成工具栏中的各按钮
bool MainWindow::makeAction(QAction*& action, QWidget* parent, QString tip, QString icon)
{
    bool ret = true;
    action = new QAction("", parent);

    if( action != NULL )
    {
        action ->setToolTip(tip);
        action->setIcon(QIcon(icon));
    }
    else
    {
       ret = false;
    }

    return ret;
}

MainWindow::~MainWindow()
{

}

//MainWindowSlots.cpp

//该文件MainWindowSlots.cpp与MainWindowUI.cpp的分离
//体现了界面和功能代码分离的思想
#include "MainWindow.h"
#include <QMessageBox>
#include <QFile>
#include <QTextStream>
#include <QMap>
#include <QCloseEvent>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QToolBar>
#include <QPrintDialog> //QT += printsupport
#include <QApplication>

#include <QDebug>

void MainWindow::showErrorMessage(QString message)
{
    QMessageBox msg(this);

    msg.setWindowTitle("Erro");
    msg.setText(message);
    msg.setIcon(QMessageBox::Critical);
    msg.setStandardButtons(QMessageBox::Ok);

    msg.exec();
}

int MainWindow::showQueryMessage(QString message)
{
    QMessageBox msg(this);

    msg.setWindowTitle("Query");
    msg.setText(message);
    msg.setIcon(QMessageBox::Question);
    msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);

    return msg.exec();
}

QString MainWindow::showFileDialog(QFileDialog::AcceptMode mode, QString title)
{
    QString ret = "";
    QFileDialog fd(this);
    QStringList filters;
    QMap<QString, QString> map;
    const char* filterArray[][2] =
    {
        {"Text(*.txt)",    ".txt"},
        {"All Files(*.*)", "*"   },
        {NULL,             NULL}
    };

    for (int i=0; filterArray[i][0] != NULL; i++)
    {
        filters.append(filterArray[i][0]);
        map.insert(filterArray[i][0], filterArray[i][1]);
    }

    fd.setWindowTitle(title);
    fd.setAcceptMode(mode); //QFileDialog::AcceptOpen或AcceptSave
    fd.setNameFilters(filters);

    if(mode == QFileDialog::AcceptOpen)
    {
        fd.setFileMode(QFileDialog::ExistingFile); //打开文件必须存在!
    }

    if(fd.exec() == QFileDialog::Accepted)
    {
        ret = fd.selectedFiles()[0];

        //Qt5中ret返回的是完整的路径名,含后缀。因此,后面的if块可省略,但Qt4可能
        //会返回不带后缀的文件名,当保存文件时,须手动加上去。
        if(mode == QFileDialog::AcceptSave)
        {
            QString postfix = map[fd.selectedNameFilter()];
            if((postfix != "*") && !ret.endsWith(postfix))
            {
                ret = ret + postfix;
            }
        }
    }

    return ret;
}

void MainWindow::preEditorChanged()
{
    if (m_isTextChanged)
    {
        int r = showQueryMessage("Do you want to save the changes to file?");

        switch(r)
        {
        case QMessageBox::Yes:
            saveCurrentData(m_filePath);
            break;
        case QMessageBox::No:
            m_isTextChanged = false;
            break;
        case QMessageBox::Cancel:
            break;
        }
    }
}

void MainWindow::onFileNew()
{
    preEditorChanged();

    if(!m_isTextChanged)
    {
        mainEditor.clear();

        setWindowTitle("NotePad - [ New ]");

        m_filePath = "";

        m_isTextChanged = false;
    }
}

void MainWindow::openFileToEditor(QString path)
{
    if( path != "")
    {
        QFile file(path);

        if (file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            mainEditor.setPlainText(QString(file.readAll()));

            file.close();

            m_filePath = path; //记录当前打开的文件路径和文件名

            m_isTextChanged = false;

            setWindowTitle("NotePad - [" + m_filePath + "]");
        }
        else
        {
            showErrorMessage(QString("Open file Error!\n\n") + "\"" + path + "\"");
        }
    }
}

void MainWindow::onFileOpen()
{
    preEditorChanged();

    if( !m_isTextChanged)
    {
        QString path = showFileDialog(QFileDialog::AcceptOpen, "Open");
        openFileToEditor(path);
    }
}

QString MainWindow::saveCurrentData(QString path, QString title)
{
    QString ret = path;
    if (ret =="")
    {
        //执行下面语句时,用户可以点击“取消”,此时ret返回""
        ret = showFileDialog(QFileDialog::AcceptSave, title);
    }

    if (ret != "")
    {
        QFile file(ret);

        if (file.open(QIODevice::WriteOnly | QIODevice::Text))
        {
            QTextStream out(&file);

            out << mainEditor.toPlainText();

            file.close();

            setWindowTitle("NotePad - [" + ret + "]");

            m_isTextChanged = false; //己保存
        }
        else
        {
            showErrorMessage(QString("Save file Error!\n\n") + "\"" + m_filePath + "\"");
            ret =""; //保存失败时
        }
    }

    return ret;
}

void MainWindow::onFileSave()
{
    QString path = saveCurrentData(m_filePath, "Save");

    if( path != "" )
    {
        m_filePath = path;
    }
}

void MainWindow::onFileSaveAs()
{
    QString path = saveCurrentData(m_filePath, "Save As");

    if( path != "" )
    {
        m_filePath = path;
    }
}

void MainWindow::onTextChanged()
{
    if( !m_isTextChanged )
    {
        setWindowTitle("*" + windowTitle());
    }

    m_isTextChanged = true;
    //qDebug()<< "onTextChanged()";
}

void MainWindow::closeEvent(QCloseEvent* e)
{
    preEditorChanged();

    if(!m_isTextChanged)
    {
        QMainWindow::closeEvent(e);
    }
    else
    {
        //当用户按下“取消”
        e->ignore();//忽略关闭事件
    }
}

void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
    if(e->mimeData()->hasUrls())
    {
        e->acceptProposedAction();
    }
    else
    {
        e->ignore();
    }
}

void MainWindow::dropEvent(QDropEvent *e)
{
    qDebug() << "dropEvent";
    if(e->mimeData()->hasUrls())
    {
        QList<QUrl> list = e->mimeData()->urls();
        QString path = list[0].toLocalFile();
        QFileInfo fi(path);

        if(fi.isFile())
        {
            preEditorChanged();
            if(!m_isTextChanged)
            {
                openFileToEditor(path);
            }
        }
        else
        {
            showErrorMessage("Can‘t open a folder!");
        }
    }
    else
    {
        e->ignore();
    }
}

void MainWindow::onCopyAvailable(bool available)
{
    QAction* action = findMenuBarAction("Copy");
    if (action != NULL) action->setEnabled(available);

    action = findMenuBarAction("Cut");
    if (action != NULL) action->setEnabled(available);

    action = findToolBarAction("Copy");
    if (action != NULL) action->setEnabled(available);

    action = findToolBarAction("Cut");
    if (action != NULL) action->setEnabled(available);
}

void MainWindow::onRedoAvailable(bool available)
{
    QAction* action = findMenuBarAction("Redo");
    if (action != NULL) action->setEnabled(available);

    action =  findToolBarAction("Redo");
    if (action != NULL) action->setEnabled(available);
}

void MainWindow::onUndoAvailable(bool available)
{
    QAction* action = findMenuBarAction("Undo");
    if (action != NULL) action->setEnabled(available);

    action =  findToolBarAction("Undo");
    if (action != NULL) action->setEnabled(available);
}

QAction* MainWindow::findMenuBarAction(QString text)
{
    QAction* ret = NULL;
    const QObjectList& list = menuBar()->children();

    for( int i=0; i<list.count(); i++)
    {
        //各菜单(如文件、编辑、查看等)
        QMenu* menu = dynamic_cast<QMenu*>(list[i]);

        if(menu != NULL)
        {
            QList<QAction*> actions = menu->actions();

            for(int j=0; j<actions.count(); j++)
            {
                if(actions[j]->text().startsWith(text))
                {
                    ret = actions[j];
                    break;
                }
            }
        }
    }

    return ret;
}

QAction* MainWindow::findToolBarAction(QString text)
{
    QAction* ret = NULL;
    const QObjectList& list = children();

    for( int i=0; i<list.count(); i++)
    {
        QToolBar* toolBar = dynamic_cast<QToolBar*>(list[i]);

        if(toolBar != NULL)
        {
            QList<QAction*> actions = toolBar->actions();

            for(int j=0; j<actions.count(); j++)
            {
                if(actions[j]->toolTip().startsWith(text))
                {
                    ret = actions[j];
                    break;
                }
            }
        }
    }

    return ret;
}

void MainWindow::onFilePrint()
{
    QPrintDialog dlg(this);
    dlg.setWindowTitle("Print");

    if(dlg.exec() == QPrintDialog::Accepted)
    {
        QPrinter* p = dlg.printer();

        mainEditor.document()->print(reinterpret_cast<QPagedPaintDevice*>(p));
    }
}

void MainWindow::onCursorPositionChanged()
{
    int pos = mainEditor.textCursor().position();
    QString text = mainEditor.toPlainText();
    int col = 0;//列数
    int ln = 0; //行数
    int flag = -1; //用于记录光标之前最后一个‘\n‘所在索引位置

    //从头到光标所在位置
    for(int i=0; i<pos; i++)
    {
        if( text[i] == ‘\n‘)
        {
            ln++;
            flag = i;
        }
    }

    col = pos -(flag + 1);
    statusLbl.setText("Ln: " + QString::number(ln+1) + "    Col: " + QString::number(col+1));
}

void MainWindow::onEditDelete()
{
    QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier);
    QKeyEvent keyRelease(QEvent::KeyRelease, Qt::Key_Delete, Qt::NoModifier);

    QApplication::sendEvent(&mainEditor, &keyPress);
    QApplication::sendEvent(&mainEditor, &keyRelease);
}

void MainWindow::onEditFind()
{
    m_pFindDlg->show();
}

void MainWindow::onEditReplace()
{
    m_pRelaceDlg->show();
}

void MainWindow::onFileExit()
{
    close();
}

5. 小结

(1)替换对话框的功能涵盖了查找对话框的功能

(2)替换对话框可以继承自查找对话框

(3)替换功能的实现是基于查找算法完成的。

(4)替换对话框是一个可复用的软件部件

时间: 2024-10-10 02:14:03

第48课 替换对话框的设计与实现的相关文章

第18课 登录对话框实例分析

1. 登录对话框 (1)需求分析 ①可复用的软件部件 ②获取用户名和密码 (2)附加需求:随机验证码 2. 登录对话框的设计与架构 (1)架构 (2)如何在两个不同的对话框间传递数据? 通过附加的成员变量和成员函数完成不同对话框间的数据传递 3. 进一步开发 (1)检查用户名和密码是否为空:当用户名和密码为空时提示错误 (2)随机验证码:①当验证码输入错误时进行提示:②验证码随时刷新 [实例分析]登录对话框实现(验证码部分在后续部分实现) 4. 小结 (1)登录对话框作为可复用的软件部件进行开发

第48课 函数设计原则

函数设计原则: 函数应该是无状态的,就是第一次调用和第二次调用是一样的. getchar返回值是int型. 优秀代码欣赏:Eclipse代码 1 /******************************************************************************* 2 * Copyright (c) 2000, 2005 IBM Corporation and others. 3 * All rights reserved. This program a

Java 类的热替换 —— 概念、设计与实现

别人的一篇技术博客,直接摘抄过来,免得再查看找不到了(本人在看的过程中可能会对原文格式或文字有修改) 原文地址:http://blog.163.com/web_promise/blog/static/109631655201222804931240/ 在本文中,我们将不对 Java ClassLoader 的细节进行过于详细的讲解,而是关注于与构建在线升级系统相关的基础概念.关于 ClassLoader 的详细细节有许多资料可以参考,有兴趣的读者可以自行研读. 要构建在线升级系统,一个重要的技术

第46课 查找对话框的功能实现

1. 文本查找功能的核心思想 (1)获取当前光标的位置并作为起始点 (2)向后(前)查找目标第一次出现的位置 (3)通过目标位置以及目标长度在文本框中进行标记 2. QString类中提供的子串查找相关函数 (1)indexOf:从指定位置向后(即字符串末尾位置)查找目标子串的下标位置 (2)lastIndexOf:从指定位置向前(即字符串开始位置)查找目标子串的下标位置 (3)QString类中查找函数所使用的下标位置 QString text = "abcdefcdg"; QStr

Qt——消息对话框的设计

1.消息对话框是什么 消息对话框(MessageBox)提供了一个模态对话框,用来通知用户某些信息,或者用来询问用户一个问题并获得一个答复. 先看下面2张图—— 第一张图是网易云音乐的界面截图,在删除歌单时,防止用户误操作,弹出了一个提示,提醒用户确认删除歌单: 第二张图是Photoshop中,用户输入的颜色值不合法之后弹出的提示框,告知用户输入的颜色值要求在000000和ffffff直接. 由此大概可以知道消息对话框有哪些作用了,它可以作为删除保护框,或提示用户某些信息等等. 在Qt中有一个Q

(转)Java 类的热替换 —— 概念、设计与实现

构建基于 Java 的在线升级系统 对于许多关键性业务或者庞大的 Java 系统来说,如果必须暂停系统服务才能进行系统升级,既会大大影响到系统的可用性,同时也增加了系统的管理和维护成本.因此,如果能够方便地在不停止系统业务的情况下进行系统升级,则可以很好地解决上述问题.在本文中,我们将基于实例,对构建在线升级 Java 系统的基础技术和设计原则进行了深入的讲解.相信读者能够根据文中的技术构建出自己的在线升级系统来. Java ClassLoader 技术剖析 在本文中,我们将不对 Java Cl

cocos2d-x3.x 弹出对话框的设计与实现

先定义一个类PopupLayer 代码PopupLayer.h #ifndef __crossDT_PopupLayer__ #define __crossDT_PopupLayer__ #include "cocos2d.h" #include "cocos-ext.h" USING_NS_CC; using namespace cocos2d::extension; class PopupLayer :public Layer { public: PopupLa

蹭课助手系统的设计与实现 文献收集

1.基于Android平台的学校课表查询及学习交流系统 2.基于Android平台学生选课蹭课系统的设计与实现 3.教师课表管理系统的设计与实现 4.二维码教室课表系统设计与开发 5.基于Web Sevice的实验室课表移动查询系统的设计与实现 6.某高校排课系统的设计与实现 7.基于Android的课表查询APP设计与实现 8.基于Android系统的掌上班级课表与学生考勤管理系统 9基于Android和Web Service的课表管理系统的设计与实现 10.基于Android的学生课表系统的

Android通用对话框组件设计

DialogFragment实现自定义的通用对话框,效果图如下面显示的: 一二两张图是DialogFragment实现,第三张图为一个自定义的Toast.一二张图满足基本需求,稍微修改可以用到自己的项目中去,一二中可以根据代码传递View,实现内容区不仅仅显示文字,也可修改后显示你想现实的任何View组件[自己实现即可,这里不给源码].                      源代码下载地址:http://download.csdn.net/detail/itjavawfc/9009887 版