现在我们开始实现菜单Edit相应的槽函数。
void Spreadsheet::cut()
{
copy();
del();
}
槽函数cut()相应Edit|Cut菜单,这里调用了两个函数,因为剪切的操作和拷贝然后删除是等价的
void
Spreadsheet::copy()
{
QTableWidgetSelectionRange range
=
selectedRange();
QString str;
for
(
int
i
=
0
; i
<
range.rowCount();
++
i) {
if
(i
>
0
)
str
+=
"
"
;
for
(
int
j
=
0
; j
<
range.columnCount();
++
j) {
if
(j
>
0
)
str
+=
"
"
;
str
+=
formula(range.topRow()
+
i, range.leftColumn()
+
j);
}
}
QApplication::clipboard()
->
setText(str);
}
QTableWidgetSelectionRange Spreadsheet::selectedRange()
const
{
QList
<
QTableWidgetSelectionRange
>
ranges
=
selectedRanges();
if
(ranges.isEmpty())
return
QTableWidgetSelectionRange();
return
ranges.first();
}
槽copy()对应Edit|Copy菜单。遍历当前的选择项(如果没有明确选择,返回当前单元格),然后把选择项的公式按顺序记录下来,加到QString中,。行之间用换行符隔开,同一行中每一列之间用TAB隔开。
调用静态函数QApplication::clipboard()可以得到系统的剪贴板。调用QClipboard::setText()把文本放到剪贴板上,这样该应用程序和其他支持素文本的Qt程序就可以使用这些文本。用换行符和tab的形式把行列分开也被许多应用程序支持。
QTableWidget::selectedRanges()返回所有的选择范围列表。在Spreadsheet构造函数中我们设置了选择模式为QAbstractItemView::contiguousSelection,因此选择范围只能有一个。为了程序使用方便,定义了selectedRange()函数返回当前的选择范围。
如果有选择范围,则返回第一个且也是唯一的一个选择范围。如果没有明确选择范围,则当前的单元格为一个选择(由于ContiguousSelection选择模式)。但是为了程序中可能出现的bug,也处理了选择为空的情况。
void
Spreadsheet::paste()
{
QTableWidgetSelectionRange range
=
selectedRange();
QString str
=
QApplication::clipboard()
->
text();
QStringList rows
=
str.split(
'
'
);
int
numRows
=
rows.count();
int
numColumns
=
rows.first().count(
'
'
)
+
1
;
if
(range.rowCount()
*
range.columnCount()
!=
1
&&
(range.rowCount()
!=
numRows
||
range.columnCount()
!=
numColumns)) {
QMessageBox::information(
this
, tr(
"
Spreadsheet
"
),
tr(
"
The information cannot be pasted because the copy
"
"
and paste areas aren't the same size.
"
));
return
;
}
for
(
int
i
=
0
; i
<
numRows;
++
i) {
QStringList columns
=
rows[i].split(
'
'
);
for
(
int
j
=
0
; j
<
numColumns;
++
j) {
int
row
=
range.topRow()
+
i;
int
column
=
range.leftColumn()
+
j;
if
(row
<
RowCount
&&
column
<
ColumnCount)
setFormula(row, column, columns[j]);
}
}
somethingChanged();
}
菜单Edit|Paste的槽函数为paste()。我们首先得到剪贴板里的文本,然后调用静态函数QString::split()按行把文本分成QStringList。每一行为一个字符串。
接着我们确定拷贝区域的维度。行数为QStringList里QString的个数。列数为第一行中tab的个数加一。如果只有一个单元格被选中,我们使用左上角的那个粘贴区域,否则使用当前选择范围为粘贴区域。
粘贴文本时,遍历行,再一次调用QString::split()把一行文本分裂为每一列文本的组合。
void Spreadsheet::del()
{
foreach (QTableWidgetItem *item, selectedItems())
delete item;
}
槽del()对应菜单Edit|Delete。它通过删除表格里选定的Cell对象清除单元格。QTableWidget发现QTableWidgetItem被删除后会自动重新绘制所有可见区域。删除单元格后,如果调用该处的cell(),将会返回一个空指针。
void Spreadsheet::selectCurrentRow()
{
selectRow(currentRow());
}
void Spreadsheet::selectCurrentColumn()
{
selectColumn(currentColumn());
}
以上两个函数分别对应菜单Edit|Select|Row和Edit|Select|Column。通过调用QTableWidget::selectRow()和QTableWidget::selectColumn()实现。我们不需要实现Edit|Select|All菜单操作,这由QTableWidget继承来的 QAbstractItemView::selectAll()实现的。
void Spreadsheet::findNext(const QString &str, Qt::CaseSensitivity cs)
{
int row = currentRow();
int column = currentColumn() + 1;
while (row < RowCount) {
while (column < ColumnCount) {
if (text(row, column).contains(str, cs)) {
clearSelection();
setCurrentCell(row, column);
activateWindow();
return;
}
++column;
}
column = 0;
++row;
}
QApplication::beep();
}
槽findNext()从当前单元格开始向右查找,查找完当前行后向下一行再继续查找,直到发现匹配的文本或到最后单元格为止。如果发现了一个匹配,清除当前选择,把匹配的单元格做为当前单元格,并把相应的窗口激活。如果没有发现匹配,则程序鸣叫,说明查找完成但没有成功找到匹配的单元格。
void Spreadsheet::findPrevious(const QString &str,
Qt::CaseSensitivity cs)
{
int row = currentRow();
int column = currentColumn() - 1;
while (row >= 0) {
while (column >= 0) {
if (text(row, column).contains(str, cs)) {
clearSelection();
setCurrentCell(row, column);
activateWindow();
return;
}
--column;
}
column = ColumnCount - 1;
--row;
}
QApplication::beep();
}
函数findPrevious()和findNext()很相似,只是搜索顺序是向前向上的,在A1停止。