Browse Source

Add insert table toolbar button

Le Tan 7 years ago
parent
commit
70374607bb

+ 95 - 0
src/dialog/vinserttabledialog.cpp

@@ -0,0 +1,95 @@
+#include "vinserttabledialog.h"
+
+#include <QSpinBox>
+#include <QRadioButton>
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QButtonGroup>
+
+VInsertTableDialog::VInsertTableDialog(QWidget *p_parent)
+    : QDialog(p_parent),
+      m_alignment(VTable::None)
+{
+    setupUI();
+}
+
+void VInsertTableDialog::setupUI()
+{
+    m_rowCount = new QSpinBox(this);
+    m_rowCount->setToolTip(tr("Number of rows of the table body"));
+    m_rowCount->setMaximum(1000);
+    m_rowCount->setMinimum(0);
+
+    m_colCount = new QSpinBox(this);
+    m_colCount->setToolTip(tr("Number of columns of the table"));
+    m_colCount->setMaximum(1000);
+    m_colCount->setMinimum(1);
+
+    QRadioButton *noneBtn = new QRadioButton(tr("None"), this);
+    QRadioButton *leftBtn = new QRadioButton(tr("Left"), this);
+    QRadioButton *centerBtn = new QRadioButton(tr("Center"), this);
+    QRadioButton *rightBtn = new QRadioButton(tr("Right"), this);
+    QHBoxLayout *alignLayout = new QHBoxLayout();
+    alignLayout->addWidget(noneBtn);
+    alignLayout->addWidget(leftBtn);
+    alignLayout->addWidget(centerBtn);
+    alignLayout->addWidget(rightBtn);
+    alignLayout->addStretch();
+
+    noneBtn->setChecked(true);
+
+    QButtonGroup *bg = new QButtonGroup(this);
+    bg->addButton(noneBtn, VTable::None);
+    bg->addButton(leftBtn, VTable::Left);
+    bg->addButton(centerBtn, VTable::Center);
+    bg->addButton(rightBtn, VTable::Right);
+    connect(bg, static_cast<void(QButtonGroup::*)(int, bool)>(&QButtonGroup::buttonToggled),
+            this, [this](int p_id, bool p_checked){
+                if (p_checked) {
+                    m_alignment = static_cast<VTable::Alignment>(p_id);
+                }
+            });
+
+    QGridLayout *topLayout = new QGridLayout();
+    topLayout->addWidget(new QLabel(tr("Row:")), 0, 0, 1, 1);
+    topLayout->addWidget(m_rowCount, 0, 1, 1, 1);
+    topLayout->addWidget(new QLabel(tr("Column:")), 0, 2, 1, 1);
+    topLayout->addWidget(m_colCount, 0, 3, 1, 1);
+
+    topLayout->addWidget(new QLabel(tr("Alignment:")), 1, 0, 1, 1);
+    topLayout->addLayout(alignLayout, 1, 1, 1, 3);
+
+    // Ok is the default button.
+    m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+    connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+    connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+    QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
+    okBtn->setProperty("SpecialBtn", true);
+
+    QVBoxLayout *mainLayout = new QVBoxLayout();
+    mainLayout->addLayout(topLayout);
+    mainLayout->addWidget(m_btnBox);
+
+    setLayout(mainLayout);
+    setWindowTitle(tr("Insert Table"));
+}
+
+int VInsertTableDialog::getRowCount() const
+{
+    return m_rowCount->value();
+}
+
+int VInsertTableDialog::getColumnCount() const
+{
+    return m_colCount->value();
+}
+
+VTable::Alignment VInsertTableDialog::getAlignment() const
+{
+    return m_alignment;
+}

+ 32 - 0
src/dialog/vinserttabledialog.h

@@ -0,0 +1,32 @@
+#ifndef VINSERTTABLEDIALOG_H
+#define VINSERTTABLEDIALOG_H
+
+#include <QDialog>
+
+#include "../vtable.h"
+
+class QDialogButtonBox;
+class QSpinBox;
+
+class VInsertTableDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    explicit VInsertTableDialog(QWidget *p_parent = nullptr);
+
+    int getRowCount() const;
+    int getColumnCount() const;
+    VTable::Alignment getAlignment() const;
+
+private:
+    void setupUI();
+
+    QSpinBox *m_rowCount;
+    QSpinBox *m_colCount;
+
+    QDialogButtonBox *m_btnBox;
+
+    VTable::Alignment m_alignment;
+};
+
+#endif // VINSERTTABLEDIALOG_H

+ 2 - 0
src/resources/docs/shortcuts_en.md

@@ -96,6 +96,8 @@ Insert inline code. Press `Ctrl+;` again to exit. Current selected text will be
 Insert fenced code block. Press `Ctrl+M` again to exit. Current selected text will be wrapped into a code block if exists.
 - `Ctrl+L`  
 Insert link.
+- `Ctrl+.`  
+Insert table.
 - `Ctrl+'`  
 Insert image.
 - `Ctrl+H`  

+ 2 - 0
src/resources/docs/shortcuts_zh.md

@@ -96,6 +96,8 @@
 插入代码块;再次按`Ctrl+M`退出。如果已经选择文本,则将当前选择文本嵌入到代码块中。
 - `Ctrl+L`  
 插入链接。
+- `Ctrl+.`  
+插入表格。
 - `Ctrl+'`  
 插入图片。
 - `Ctrl+H`  

+ 9 - 0
src/resources/icons/table.svg

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<path style="fill:#000000" d="M409.6,64H102.4C81.3,64,64,81.3,64,102.4v307.2c0,21.1,17.3,38.4,38.4,38.4h307.2c21.1,0,38.4-17.3,38.4-38.4V102.4
+	C448,81.3,430.7,64,409.6,64z M179.2,409.6h-76.8v-76.8h76.8V409.6z M179.2,294.4h-76.8v-76.8h76.8V294.4z M179.2,179.2h-76.8v-76.8
+	h76.8V179.2z M294.4,409.6h-76.8v-76.8h76.8V409.6z M294.4,294.4h-76.8v-76.8h76.8V294.4z M294.4,179.2h-76.8v-76.8h76.8V179.2z
+	 M409.6,409.6h-76.8v-76.8h76.8V409.6z M409.6,294.4h-76.8v-76.8h76.8V294.4z M409.6,179.2h-76.8v-76.8h76.8V179.2z"/>
+</svg>

+ 4 - 2
src/src.pro

@@ -159,7 +159,8 @@ SOURCES += main.cpp\
     vfilelistwidget.cpp \
     widgets/vcombobox.cpp \
     vtablehelper.cpp \
-    vtable.cpp
+    vtable.cpp \
+    dialog/vinserttabledialog.cpp
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
@@ -304,7 +305,8 @@ HEADERS  += vmainwindow.h \
     vfilelistwidget.h \
     widgets/vcombobox.h \
     vtablehelper.h \
-    vtable.h
+    vtable.h \
+    dialog/vinserttabledialog.h
 
 RESOURCES += \
     vnote.qrc \

+ 1 - 1
src/vdirectorytree.cpp

@@ -419,7 +419,7 @@ void VDirectoryTree::contextMenuRequested(QPoint pos)
 
     menu.addSeparator();
 
-    QAction *reloadAct = new QAction(tr("&Reload From Disk"), &menu);
+    QAction *reloadAct = new QAction(tr("Reload From Disk"), &menu);
     reloadAct->setToolTip(tr("Reload the content of this folder (or notebook) from disk"));
     connect(reloadAct, &QAction::triggered,
             this, &VDirectoryTree::reloadFromDisk);

+ 3 - 0
src/veditor.h

@@ -72,6 +72,9 @@ public:
     // User requests to insert a link.
     void insertLink();
 
+    // User requests to insert a table.
+    virtual void insertTable() = 0;
+
     // Used for incremental search.
     // User has enter the content to search, but does not enter the "find" button yet.
     bool peekText(const QString &p_text, uint p_options, bool p_forward = true);

+ 4 - 0
src/vedittab.cpp

@@ -136,6 +136,10 @@ void VEditTab::insertLink()
 {
 }
 
+void VEditTab::insertTable()
+{
+}
+
 void VEditTab::applySnippet(const VSnippet *p_snippet)
 {
     Q_UNUSED(p_snippet);

+ 3 - 0
src/vedittab.h

@@ -56,6 +56,9 @@ public:
     // User requests to insert link.
     virtual void insertLink();
 
+    // User requests to table.
+    virtual void insertTable();
+
     // Search @p_text in current note.
     virtual void findText(const QString &p_text, uint p_options, bool p_peek,
                           bool p_forward = true) = 0;

+ 13 - 0
src/vmainwindow.cpp

@@ -573,6 +573,19 @@ QToolBar *VMainWindow::initEditToolBar(QSize p_iconSize)
 
     m_editToolBar->addAction(codeBlockAct);
 
+    QAction *tableAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/table.svg"),
+                                    tr("Table\t%1").arg(VUtils::getShortcutText("Ctrl+.")),
+                                    this);
+    tableAct->setStatusTip(tr("Insert a table"));
+    connect(tableAct, &QAction::triggered,
+            this, [this](){
+                if (m_curTab) {
+                    m_curTab->insertTable();
+                }
+            });
+
+    m_editToolBar->addAction(tableAct);
+
     // Insert link.
     QAction *insetLinkAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/link.svg"),
                                         tr("Insert Link\t%1").arg(VUtils::getShortcutText("Ctrl+L")),

+ 11 - 0
src/vmdeditoperations.cpp

@@ -543,6 +543,17 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
         break;
     }
 
+    case Qt::Key_Period:
+    {
+        if (modifiers == Qt::ControlModifier) {
+            m_editor->insertTable();
+            p_event->accept();
+            ret = true;
+        }
+
+        break;
+    }
+
     default:
         break;
     }

+ 38 - 0
src/vmdeditor.cpp

@@ -33,6 +33,7 @@
 #include "vmdtab.h"
 #include "vdownloader.h"
 #include "vtablehelper.h"
+#include "dialog/vinserttabledialog.h"
 
 extern VWebUtils *g_webUtils;
 
@@ -2211,3 +2212,40 @@ void VMdEditor::handleLinkToAttachmentAction(QAction *p_act)
         m_editOps->insertLink(linkText, linkUrl);
     }
 }
+
+void VMdEditor::insertTable()
+{
+    // Get the dialog info.
+    VInsertTableDialog td(this);
+    if (td.exec() != QDialog::Accepted) {
+        return;
+    }
+
+    int rowCount = td.getRowCount();
+    int colCount = td.getColumnCount();
+    VTable::Alignment alignment = td.getAlignment();
+
+    QTextCursor cursor = textCursorW();
+    if (cursor.hasSelection()) {
+        cursor.clearSelection();
+        setTextCursorW(cursor);
+    }
+
+    bool newBlock = !cursor.atBlockEnd();
+    if (!newBlock && !cursor.atBlockStart()) {
+        QString text = cursor.block().text().trimmed();
+        if (!text.isEmpty() && text != ">") {
+            // Insert a new block before inserting table.
+            newBlock = true;
+        }
+    }
+
+    if (newBlock) {
+        VEditUtils::insertBlock(cursor, false);
+        VEditUtils::indentBlockAsBlock(cursor, false);
+        setTextCursorW(cursor);
+    }
+
+    // Insert table right at cursor.
+    m_tableHelper->insertTable(rowCount, colCount, alignment);
+}

+ 2 - 0
src/vmdeditor.h

@@ -84,6 +84,8 @@ public:
 
     void updateFontAndPalette() Q_DECL_OVERRIDE;
 
+    void insertTable() Q_DECL_OVERRIDE;
+
 public slots:
     bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
 

+ 10 - 0
src/vmdtab.cpp

@@ -696,6 +696,16 @@ void VMdTab::insertLink()
     m_editor->insertLink();
 }
 
+void VMdTab::insertTable()
+{
+    if (!m_isEditMode) {
+        return;
+    }
+
+    Q_ASSERT(m_editor);
+    m_editor->insertTable();
+}
+
 void VMdTab::findText(const QString &p_text, uint p_options, bool p_peek,
                       bool p_forward)
 {

+ 2 - 0
src/vmdtab.h

@@ -44,6 +44,8 @@ public:
 
     void insertImage() Q_DECL_OVERRIDE;
 
+    void insertTable() Q_DECL_OVERRIDE;
+
     void insertLink() Q_DECL_OVERRIDE;
 
     // Search @p_text in current note.

+ 1 - 0
src/vnote.qrc

@@ -277,5 +277,6 @@
         <file>resources/docs/welcome_zh.md</file>
         <file>resources/icons/256x256/vnote.png</file>
         <file>utils/markdown-it/markdown-it-container.min.js</file>
+        <file>resources/icons/table.svg</file>
     </qresource>
 </RCC>

+ 114 - 21
src/vtable.cpp

@@ -7,14 +7,73 @@
 
 const QString VTable::c_defaultDelimiter = "---";
 
+const QChar VTable::c_borderChar = '|';
+
 enum { HeaderRowIndex = 0, DelimiterRowIndex = 1 };
 
 VTable::VTable(VEditor *p_editor, const VTableBlock &p_block)
-    : m_editor(p_editor)
+    : m_editor(p_editor),
+      m_exist(true),
+      m_spaceWidth(10),
+      m_minusWidth(10),
+      m_colonWidth(10),
+      m_defaultDelimiterWidth(10)
 {
     parseFromTableBlock(p_block);
 }
 
+VTable::VTable(VEditor *p_editor, int p_nrBodyRow, int p_nrCol, VTable::Alignment p_alignment)
+    : m_editor(p_editor),
+      m_exist(false),
+      m_spaceWidth(10),
+      m_minusWidth(10),
+      m_colonWidth(10),
+      m_defaultDelimiterWidth(10)
+{
+    Q_ASSERT(p_nrBodyRow >= 0 && p_nrCol > 0);
+    m_rows.resize(p_nrBodyRow + 2);
+
+    // PreText for each row.
+    QString preText;
+    QTextCursor cursor = m_editor->textCursorW();
+    Q_ASSERT(cursor.atBlockEnd());
+    if (!cursor.atBlockStart()) {
+        preText = cursor.block().text();
+    }
+
+    QString core(c_defaultDelimiter);
+    switch (p_alignment) {
+    case Alignment::Left:
+        core[0] = ':';
+        break;
+
+    case Alignment::Center:
+        core[0] = ':';
+        core[core.size() - 1] = ':';
+        break;
+
+    case Alignment::Right:
+        core[core.size() - 1] = ':';
+        break;
+
+    default:
+        break;
+    }
+    const QString delimiterCell = generateFormattedText(core, 0);
+    const QString contentCell = generateFormattedText(QString(c_defaultDelimiter.size(), ' '), 0);
+
+    for (int rowIdx = 0; rowIdx < m_rows.size(); ++rowIdx) {
+        auto & row = m_rows[rowIdx];
+        row.m_preText = preText;
+        row.m_cells.resize(p_nrCol);
+
+        const QString &content = isDelimiterRow(rowIdx) ? delimiterCell : contentCell;
+        for (auto & cell : row.m_cells) {
+            cell.m_text = content;
+        }
+    }
+}
+
 bool VTable::isValid() const
 {
     return header() && header()->isValid()
@@ -82,6 +141,13 @@ bool VTable::parseOneRow(const QTextBlock &p_block,
         return false;
     }
 
+    // Get pre text.
+    int firstCellOffset = p_borders[p_borderIdx] - startPos;
+    if (text[firstCellOffset] != c_borderChar) {
+        return false;
+    }
+    p_row.m_preText = text.left(firstCellOffset);
+
     for (; p_borderIdx < p_borders.size(); ++p_borderIdx) {
         int border = p_borders[p_borderIdx];
         if (border >= endPos) {
@@ -89,7 +155,7 @@ bool VTable::parseOneRow(const QTextBlock &p_block,
         }
 
         int offset = border - startPos;
-        if (text[offset] != '|') {
+        if (text[offset] != c_borderChar) {
             return false;
         }
 
@@ -101,7 +167,7 @@ bool VTable::parseOneRow(const QTextBlock &p_block,
         }
 
         int nextOffset = p_borders[nextIdx] - startPos;
-        if (text[nextOffset] != '|') {
+        if (text[nextOffset] != c_borderChar) {
             return false;
         }
 
@@ -186,7 +252,7 @@ void VTable::formatOneColumn(int p_idx, int p_cursorRowIdx, int p_cursorPib)
     fetchCellInfoOfColumn(p_idx, cells, targetWidth);
 
     // Get the alignment of this column.
-    const VTable::Alignment align = getColumnAlignment(p_idx);
+    const Alignment align = getColumnAlignment(p_idx);
 
     // Calculate the formatted text of each cell.
     for (int rowIdx = 0; rowIdx < cells.size(); ++rowIdx) {
@@ -246,10 +312,7 @@ void VTable::formatOneColumn(int p_idx, int p_cursorRowIdx, int p_cursorPib)
                     break;
                 }
 
-                Alignment fakeAlign = align == Alignment::None ? Alignment::Left : align;
-                cell.m_formattedText = generateFormattedText(core,
-                                                             0,
-                                                             fakeAlign);
+                cell.m_formattedText = generateFormattedText(core, 0);
             }
         } else {
             Alignment fakeAlign = align;
@@ -385,9 +448,10 @@ QString VTable::generateFormattedText(const QString &p_core,
         rightSpaces = 0;
     }
 
-    return QString("| %1%2%3 ").arg(QString(leftSpaces, ' '))
-                               .arg(p_core)
-                               .arg(QString(rightSpaces, ' '));
+    return QString("%1 %2%3%4 ").arg(c_borderChar)
+                                .arg(QString(leftSpaces, ' '))
+                                .arg(p_core)
+                                .arg(QString(rightSpaces, ' '));
 }
 
 VTable::Alignment VTable::getColumnAlignment(int p_idx) const
@@ -450,7 +514,7 @@ bool VTable::isCellWellFormatted(const Row &p_row,
                                  const Cell &p_cell,
                                  const CellInfo &p_info,
                                  int p_targetWidth,
-                                 VTable::Alignment p_align) const
+                                 Alignment p_align) const
 {
     Q_ASSERT(p_align != Alignment::None);
 
@@ -507,6 +571,15 @@ bool VTable::isCellWellFormatted(const Row &p_row,
 }
 
 void VTable::write()
+{
+    if (m_exist) {
+        writeExist();
+    } else {
+        writeNonExist();
+    }
+}
+
+void VTable::writeExist()
 {
     bool changed = false;
     QTextCursor cursor = m_editor->textCursorW();
@@ -535,14 +608,7 @@ void VTable::write()
         }
 
         // Construct the block text.
-        QString newBlockText;
-        int firstOffset = row.m_cells.first().m_offset;
-        if (firstOffset > 0) {
-            // Get the prefix text.
-            QString text = row.m_block.text();
-            newBlockText = text.left(firstOffset);
-        }
-
+        QString newBlockText(row.m_preText);
         for (auto & cell : row.m_cells) {
             int pos = newBlockText.size();
             if (cell.m_formattedText.isEmpty()) {
@@ -560,7 +626,7 @@ void VTable::write()
             }
         }
 
-        newBlockText += "|";
+        newBlockText += c_borderChar;
 
         // Replace the whole block.
         cursor.setPosition(row.m_block.position());
@@ -583,3 +649,30 @@ void VTable::write()
         }
     }
 }
+
+void VTable::writeNonExist()
+{
+    // Generate the text of the whole table.
+    QString tableText;
+    for (int rowIdx = 0; rowIdx < m_rows.size(); ++rowIdx) {
+        const auto & row = m_rows[rowIdx];
+        tableText += row.m_preText;
+        for (auto & cell : row.m_cells) {
+            tableText += cell.m_text;
+        }
+
+        tableText += c_borderChar;
+
+        if (rowIdx < m_rows.size() - 1) {
+            tableText += '\n';
+        }
+    }
+
+    QTextCursor cursor = m_editor->textCursorW();
+    int pos = cursor.position() + 2;
+    cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
+    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+    cursor.insertText(tableText);
+    cursor.setPosition(pos);
+    m_editor->setTextCursorW(cursor);
+}

+ 37 - 24
src/vtable.h

@@ -10,6 +10,25 @@ class VEditor;
 class VTable
 {
 public:
+    enum Alignment {
+        None,
+        Left,
+        Center,
+        Right
+    };
+
+    VTable(VEditor *p_editor, const VTableBlock &p_block);
+
+    VTable(VEditor *p_editor, int p_nrBodyRow, int p_nrCol, VTable::Alignment p_alignment);
+
+    bool isValid() const;
+
+    void format();
+
+    // Write a formatted table.
+    void write();
+
+private:
     struct Cell
     {
         Cell()
@@ -53,12 +72,13 @@ public:
 
         bool isValid() const
         {
-            return m_block.isValid();
+            return !m_cells.isEmpty();
         }
 
         void clear()
         {
             m_block = QTextBlock();
+            m_preText.clear();
             m_cells.clear();
         }
 
@@ -75,31 +95,11 @@ public:
         }
 
         QTextBlock m_block;
+        // Text before table row.
+        QString m_preText;
         QVector<Cell> m_cells;
     };
 
-    enum Alignment
-    {
-        None,
-        Left,
-        Center,
-        Right
-    };
-
-    VTable(VEditor *p_editor, const VTableBlock &p_block);
-
-    bool isValid() const;
-
-    void format();
-
-    // Write a formatted table.
-    void write();
-
-    VTable::Row *header() const;
-
-    VTable::Row *delimiter() const;
-
-private:
     // Used to hold info about a cell when formatting a column.
     struct CellInfo
     {
@@ -163,10 +163,21 @@ private:
                              const Cell &p_cell,
                              const CellInfo &p_info,
                              int p_targetWidth,
-                             VTable::Alignment p_align) const;
+                             Alignment p_align) const;
+
+    void writeExist();
+
+    void writeNonExist();
+
+    VTable::Row *header() const;
+
+    VTable::Row *delimiter() const;
 
     VEditor *m_editor;
 
+    // Whether this table exist already.
+    bool m_exist;
+
     // Header, delimiter, and body.
     QVector<Row> m_rows;
 
@@ -176,6 +187,8 @@ private:
     int m_defaultDelimiterWidth;
 
     static const QString c_defaultDelimiter;
+
+    static const QChar c_borderChar;
 };
 
 #endif // VTABLE_H

+ 10 - 1
src/vtablehelper.cpp

@@ -1,7 +1,6 @@
 #include "vtablehelper.h"
 
 #include "veditor.h"
-#include "vtable.h"
 
 VTableHelper::VTableHelper(VEditor *p_editor, QObject *p_parent)
     : QObject(p_parent),
@@ -52,3 +51,13 @@ int VTableHelper::currentCursorTableBlock(const QVector<VTableBlock> &p_blocks)
 
     return -1;
 }
+
+void VTableHelper::insertTable(int p_nrRow, int p_nrCol, VTable::Alignment p_alignment)
+{
+    VTable table(m_editor, p_nrRow, p_nrCol, p_alignment);
+    if (!table.isValid()) {
+        return;
+    }
+
+    table.write();
+}

+ 4 - 0
src/vtablehelper.h

@@ -4,6 +4,7 @@
 #include <QObject>
 
 #include "markdownhighlighterdata.h"
+#include "vtable.h"
 
 class VEditor;
 
@@ -13,6 +14,9 @@ class VTableHelper : public QObject
 public:
     explicit VTableHelper(VEditor *p_editor, QObject *p_parent = nullptr);
 
+    // Insert table right at current cursor.
+    void insertTable(int p_nrRow, int p_nrCol, VTable::Alignment p_alignment);
+
 public slots:
     void updateTableBlocks(const QVector<VTableBlock> &p_blocks);