Skip to content

Commit 6f67217

Browse files
authored
Merge pull request #422 from j2doll/ExcelTableEditor
- `ExcelTableEditor` : add function to append new col/row
2 parents 9832360 + ee2ab35 commit 6f67217

File tree

2 files changed

+142
-34
lines changed

2 files changed

+142
-34
lines changed

ExcelTableEditor/mainwindow.cpp

Lines changed: 129 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <QComboBox>
1515
#include <QLineEdit>
1616
#include <QPushButton>
17+
#include <QItemSelectionModel>
1718

1819
#include "excelitemdelegate.h"
1920

@@ -36,13 +37,13 @@ MainWindow::~MainWindow() = default;
3637

3738
void MainWindow::setup_ui()
3839
{
39-
// Central widget: top bar (sheet controls) + table view
40+
// central widget: top sheet bar + table view
4041
QWidget *central = new QWidget(this);
4142
QVBoxLayout *v_layout = new QVBoxLayout(central);
4243
v_layout->setContentsMargins(4, 4, 4, 4);
4344
v_layout->setSpacing(4);
4445

45-
// Top bar: sheet selection + rename + new/delete/move
46+
// top sheet bar: sheet selection + rename + new/delete/move + row/column insertion
4647
QWidget *sheet_bar = new QWidget(central);
4748
QHBoxLayout *h_layout = new QHBoxLayout(sheet_bar);
4849
h_layout->setContentsMargins(0, 0, 0, 0);
@@ -61,6 +62,10 @@ void MainWindow::setup_ui()
6162
sheet_move_left_button_ = new QPushButton(tr(""), sheet_bar);
6263
sheet_move_right_button_ = new QPushButton(tr(""), sheet_bar);
6364

65+
// row / column insertion buttons
66+
add_row_button_ = new QPushButton(tr("+Row"), sheet_bar);
67+
add_column_button_ = new QPushButton(tr("+Col"), sheet_bar);
68+
6469
h_layout->addWidget(sheet_label);
6570
h_layout->addWidget(sheet_combo_, 0);
6671
h_layout->addSpacing(8);
@@ -73,9 +78,14 @@ void MainWindow::setup_ui()
7378
h_layout->addWidget(sheet_move_left_button_);
7479
h_layout->addWidget(sheet_move_right_button_);
7580

81+
h_layout->addSpacing(12);
82+
h_layout->addWidget(new QLabel(tr("Insert:"), sheet_bar));
83+
h_layout->addWidget(add_row_button_);
84+
h_layout->addWidget(add_column_button_);
85+
7686
v_layout->addWidget(sheet_bar);
7787

78-
// Table view
88+
// table view
7989
table_view_ = new QTableView(central);
8090
table_view_->setAlternatingRowColors(true);
8191
table_view_->setSelectionBehavior(QAbstractItemView::SelectItems);
@@ -87,13 +97,13 @@ void MainWindow::setup_ui()
8797

8898
setCentralWidget(central);
8999

90-
// Delegate
100+
// delegate
91101
delegate_ = new ExcelItemDelegate(this);
92102
table_view_->setItemDelegate(delegate_);
93103

94104
statusBar()->showMessage(tr("Ready"));
95105

96-
// Connections
106+
// connections
97107
connect(sheet_combo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
98108
this, &MainWindow::on_current_sheet_changed);
99109
connect(sheet_rename_button_, &QPushButton::clicked,
@@ -106,6 +116,12 @@ void MainWindow::setup_ui()
106116
this, &MainWindow::on_sheet_move_left_button_clicked);
107117
connect(sheet_move_right_button_, &QPushButton::clicked,
108118
this, &MainWindow::on_sheet_move_right_button_clicked);
119+
120+
// row / column insertion buttons
121+
connect(add_row_button_, &QPushButton::clicked,
122+
this, &MainWindow::on_add_row_button_clicked);
123+
connect(add_column_button_, &QPushButton::clicked,
124+
this, &MainWindow::on_add_column_button_clicked);
109125
}
110126

111127
void MainWindow::setup_menu()
@@ -127,9 +143,7 @@ void MainWindow::setup_menu()
127143
file_menu->addSeparator();
128144
file_menu->addAction(action_exit_);
129145

130-
// ---------------------------------------------------------
131-
// ★ Help 메뉴 추가
132-
// ---------------------------------------------------------
146+
// Help menu
133147
QMenu *help_menu = menuBar()->addMenu(tr("&Help"));
134148
QAction *action_about = new QAction(tr("&About ExcelTableEditor"), this);
135149
help_menu->addAction(action_about);
@@ -270,7 +284,7 @@ void MainWindow::on_model_data_changed()
270284
is_modified_ = true;
271285
}
272286

273-
// Sheet combo box changed
287+
// sheet combobox change handler
274288
void MainWindow::on_current_sheet_changed(int index)
275289
{
276290
if (index < 0 || index >= sheet_names_.size()) {
@@ -281,7 +295,7 @@ void MainWindow::on_current_sheet_changed(int index)
281295
set_current_sheet(sheet_name);
282296
}
283297

284-
// Rename current sheet
298+
// rename current sheet
285299
void MainWindow::on_sheet_rename_button_clicked()
286300
{
287301
if (current_sheet_name_.isEmpty()) {
@@ -305,10 +319,10 @@ void MainWindow::on_sheet_rename_button_clicked()
305319
return;
306320
}
307321

308-
// Update sheet name list
322+
// update sheet name list
309323
sheet_names_[idx] = new_name;
310324

311-
// Update keys in maps
325+
// change map key
312326
auto model = sheet_models_.take(current_sheet_name_);
313327
auto formats = sheet_formats_.take(current_sheet_name_);
314328
sheet_models_.insert(new_name, model);
@@ -322,13 +336,12 @@ void MainWindow::on_sheet_rename_button_clicked()
322336
statusBar()->showMessage(tr("Sheet renamed to: %1").arg(new_name), 3000);
323337
}
324338

325-
// Create new sheet
339+
// create new sheet
326340
void MainWindow::on_sheet_new_button_clicked()
327341
{
328342
const QString new_name = generate_unique_sheet_name();
329343

330344
auto *model = new QStandardItemModel(this);
331-
// Small default grid
332345
const int default_rows = 10;
333346
const int default_cols = 5;
334347
model->setRowCount(default_rows);
@@ -352,7 +365,7 @@ void MainWindow::on_sheet_new_button_clicked()
352365
statusBar()->showMessage(tr("New sheet created: %1").arg(new_name), 3000);
353366
}
354367

355-
// Delete current sheet
368+
// delete current sheet
356369
void MainWindow::on_sheet_delete_button_clicked()
357370
{
358371
if (current_sheet_name_.isEmpty()) {
@@ -390,7 +403,6 @@ void MainWindow::on_sheet_delete_button_clicked()
390403
sheet_formats_.remove(removed_name);
391404
sheet_names_.removeAt(idx);
392405

393-
// Determine new current sheet index
394406
int new_idx = idx;
395407
if (new_idx >= sheet_names_.size()) {
396408
new_idx = sheet_names_.size() - 1;
@@ -415,7 +427,7 @@ void MainWindow::on_sheet_delete_button_clicked()
415427
statusBar()->showMessage(tr("Sheet deleted: %1").arg(removed_name), 3000);
416428
}
417429

418-
// Move current sheet one step left
430+
// move current sheet to the left
419431
void MainWindow::on_sheet_move_left_button_clicked()
420432
{
421433
if (current_sheet_name_.isEmpty()) {
@@ -424,7 +436,7 @@ void MainWindow::on_sheet_move_left_button_clicked()
424436

425437
const int idx = sheet_names_.indexOf(current_sheet_name_);
426438
if (idx <= 0) {
427-
return; // already first
439+
return;
428440
}
429441

430442
sheet_names_.swapItemsAt(idx, idx - 1);
@@ -434,7 +446,7 @@ void MainWindow::on_sheet_move_left_button_clicked()
434446
statusBar()->showMessage(tr("Sheet moved left"), 2000);
435447
}
436448

437-
// Move current sheet one step right
449+
// move current sheet to the right
438450
void MainWindow::on_sheet_move_right_button_clicked()
439451
{
440452
if (current_sheet_name_.isEmpty()) {
@@ -443,7 +455,7 @@ void MainWindow::on_sheet_move_right_button_clicked()
443455

444456
const int idx = sheet_names_.indexOf(current_sheet_name_);
445457
if (idx < 0 || idx >= sheet_names_.size() - 1) {
446-
return; // already last
458+
return;
447459
}
448460

449461
sheet_names_.swapItemsAt(idx, idx + 1);
@@ -453,7 +465,7 @@ void MainWindow::on_sheet_move_right_button_clicked()
453465
statusBar()->showMessage(tr("Sheet moved right"), 2000);
454466
}
455467

456-
// Update current sheet (table view, name edit, dataChanged connection)
468+
// set current sheet model / view
457469
void MainWindow::set_current_sheet(const QString &sheet_name)
458470
{
459471
if (!sheet_models_.contains(sheet_name)) {
@@ -479,7 +491,7 @@ void MainWindow::set_current_sheet(const QString &sheet_name)
479491
}
480492
}
481493

482-
// Apply QXlsx::Format to Qt item
494+
// apply QXlsx::Format to a Qt item
483495
void MainWindow::apply_format_to_item(QStandardItemModel *model,
484496
int row,
485497
int col,
@@ -535,7 +547,7 @@ void MainWindow::apply_format_to_item(QStandardItemModel *model,
535547
}
536548
}
537549

538-
// Lookup format for a cell
550+
// look up cell format
539551
Format MainWindow::format_for_cell(const QString &sheet_name,
540552
int row,
541553
int col) const
@@ -554,7 +566,7 @@ Format MainWindow::format_for_cell(const QString &sheet_name,
554566
return Format();
555567
}
556568

557-
// Generate a unique sheet name like "Sheet1", "Sheet2", ...
569+
// generate a unique sheet name like "Sheet1", "Sheet2", ...
558570
QString MainWindow::generate_unique_sheet_name() const
559571
{
560572
QString base = QStringLiteral("Sheet");
@@ -568,7 +580,7 @@ QString MainWindow::generate_unique_sheet_name() const
568580
}
569581
}
570582

571-
// Rebuild the sheet combo box and keep the current sheet selected
583+
// rebuild sheet combobox and keep current sheet selection
572584
void MainWindow::rebuild_sheet_combo(const QString &current_name)
573585
{
574586
if (!sheet_combo_) {
@@ -587,8 +599,8 @@ void MainWindow::rebuild_sheet_combo(const QString &current_name)
587599
sheet_combo_->blockSignals(false);
588600
}
589601

590-
// Load all sheets from an Excel file
591-
// All rows (including row 1) are loaded as data, so row 1 is editable.
602+
// load all sheets from an Excel file
603+
// all rows including the first row are loaded as data, so row 1 is editable.
592604
bool MainWindow::load_excel_file(const QString &file_path)
593605
{
594606
Document xlsx(file_path);
@@ -646,7 +658,7 @@ bool MainWindow::load_excel_file(const QString &file_path)
646658

647659
for (int r = first_row; r <= last_row; ++r) {
648660
for (int c = first_col; c <= last_col; ++c) {
649-
auto cell = sheet->cellAt(r, c); // shared_ptr<Cell>
661+
auto cell = sheet->cellAt(r, c);
650662
if (!cell) {
651663
continue;
652664
}
@@ -695,7 +707,7 @@ bool MainWindow::load_excel_file(const QString &file_path)
695707
return true;
696708
}
697709

698-
// Save all sheets in memory to an Excel file
710+
// save all sheets in memory into an Excel file
699711
bool MainWindow::save_excel_file(const QString &file_path)
700712
{
701713
if (sheet_names_.isEmpty()) {
@@ -773,10 +785,98 @@ void MainWindow::on_action_about_triggered()
773785
"<li>Editable sheet data</li>"
774786
"<li>Sheet rename / add / delete / reorder</li>"
775787
"<li>Basic cell formatting</li>"
788+
"<li>Insert rows / columns</li>"
776789
"</ul>";
777790
text += "<br>Project: <a href=\"https://github.com/QtExcel/QXlsx\">QXlsx GitHub</a><br>";
778791
text += "First Author: Jay Two<br>";
779792

780793
QMessageBox::about(this, tr("About ExcelTableEditor"), text);
781794
}
782795

796+
// ----------------------------
797+
// row / column insertion logic
798+
// ----------------------------
799+
800+
// row insertion button: insert a new row above the selected row,
801+
// or append to the bottom if nothing is selected
802+
void MainWindow::on_add_row_button_clicked()
803+
{
804+
if (!current_model_) {
805+
return;
806+
}
807+
808+
int insert_row = current_model_->rowCount();
809+
810+
if (table_view_ && table_view_->selectionModel()) {
811+
const QModelIndexList indexes =
812+
table_view_->selectionModel()->selectedIndexes();
813+
if (!indexes.isEmpty()) {
814+
insert_row = indexes.first().row();
815+
// find the topmost row among selected cells
816+
for (const QModelIndex &idx : indexes) {
817+
if (idx.row() < insert_row) {
818+
insert_row = idx.row();
819+
}
820+
}
821+
}
822+
}
823+
824+
current_model_->insertRow(insert_row);
825+
826+
// update vertical header numbers (1,2,3,...)
827+
const int row_count = current_model_->rowCount();
828+
for (int r = 0; r < row_count; ++r) {
829+
current_model_->setHeaderData(r, Qt::Vertical, QString::number(r + 1));
830+
}
831+
832+
// for simplicity, clear the format map after row insertion
833+
// (format indices would be shifted otherwise)
834+
if (!current_sheet_name_.isEmpty()) {
835+
sheet_formats_[current_sheet_name_].clear();
836+
}
837+
838+
is_modified_ = true;
839+
statusBar()->showMessage(tr("Row inserted"), 2000);
840+
}
841+
842+
// column insertion button: insert a new column to the left of the selected column,
843+
// or append to the right if nothing is selected
844+
void MainWindow::on_add_column_button_clicked()
845+
{
846+
if (!current_model_) {
847+
return;
848+
}
849+
850+
int insert_col = current_model_->columnCount();
851+
852+
if (table_view_ && table_view_->selectionModel()) {
853+
const QModelIndexList indexes =
854+
table_view_->selectionModel()->selectedIndexes();
855+
if (!indexes.isEmpty()) {
856+
insert_col = indexes.first().column();
857+
// find the leftmost column among selected cells
858+
for (const QModelIndex &idx : indexes) {
859+
if (idx.column() < insert_col) {
860+
insert_col = idx.column();
861+
}
862+
}
863+
}
864+
}
865+
866+
current_model_->insertColumn(insert_col);
867+
868+
// update horizontal header numbers (1,2,3,...)
869+
const int col_count = current_model_->columnCount();
870+
for (int c = 0; c < col_count; ++c) {
871+
current_model_->setHeaderData(c, Qt::Horizontal, QString::number(c + 1));
872+
}
873+
874+
// for simplicity, clear the format map after column insertion
875+
// (format indices would be shifted otherwise)
876+
if (!current_sheet_name_.isEmpty()) {
877+
sheet_formats_[current_sheet_name_].clear();
878+
}
879+
880+
is_modified_ = true;
881+
statusBar()->showMessage(tr("Column inserted"), 2000);
882+
}

0 commit comments

Comments
 (0)