实现Edit菜单
现在我们开始实现菜单 Edit 相应的槽函数。
void Spreadsheet::cut()
{
copy();
del();
}
cut()槽可以对Edit->Cut菜单做出响应。由于Cut 的执行效果与Copy之后再加上一个Delete的执行效果相同,所以其实现代码很简单。
void Spreadsheet::copy()
{
QTableWidgetSelectionRange range = selectedRange();
QString str;
for (int i = 0; i < range.rowCount(); ++i) {
if (i > 0)
str += "\n";
for (int j = 0; j < range.columnCount(); ++j) {
if (j > 0)
str += "\t";
str += formula(range.topRow() + i, range.leftColumn() + j);
}
}
QApplication::clipboard()->setText(str);
}
copy()槽能够对Edit->Copy做出响应。它会遍历当前选择(如果没有明确的选择,那么就认为选择的只是当前单元格)。每一个选中单元格的公式都会被添加到一个QString中,行与行之间利用换行符“\n”分隔,列与列之间则以制表符“\t’来分隔。下面给出了这一实现方法的示意图。
在Qt中,通过调用QApplication::clipboard()静态函数可以使用系统的剪贴板。通过调用QClipboard::setText(),就既可以在本应用程序中又可以在其他应用程序中使用放在剪贴板上的这些文本。这种使用制表符“\t”和换行符“\n"作为文件分隔符的形式可以被包括微软Excel在内的许多应用程序所支持。
函数QTableWidget::selectedRanges()返回一个选择范围列表。我们知道由于在构造函数中已经将选择模式设置为QAbstractemView::ContiguousSelection,所以选择范围不可能再超过1。为方便起见,我们定义了一个selectedRange( )函数来返回这个选择范围。
QTableWidgetSelectionRange Spreadsheet::selectedRange() const
{
QList<QTableWidgetSelectionRange> ranges = selectedRanges();
if (ranges.isEmpty())
return QTableWidgetSelectionRange();
return ranges.first();
}
如果只有一个选择,则只需简单地返回第一个(并且也只有这一个)选择即可。没有选择的情况应该永远不会发生,因为ContigousSeletion模式至少可以把当前单元格当作是已经选中的选择。但是,为了避免使程序出现缺陷的可能性,还是需要对这种当前没有选中单元格的情况进行单独处理。
void Spreadsheet::paste()
{
QTableWidgetSelectionRange range = selectedRange();
QString str = QApplication::clipboard()->text();
QStringList rows = str.split('\n');
int numRows = rows.count();
int numColumns = rows.first().count('\t') + 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('\t');
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();
}
paste()槽对Edit->Paste菜单选项做出响应。我们从剪贴板中取回文本,并且调用静态函数QString::split()把这串字符变成一个QStringList。每行都会变成这个列表中的一个字符串。
接下来,需要求出复制区域的维数。行数就是QStringList 中字符串的个数;列数就是第一行中制表符“\t”字符的个数再加上1。如果只选中了一个单元格,就把这个单元格作为粘贴区域放在左上角;否则,就把当前选择作为要粘贴的区域。
为了执行粘贴操作,我们遍历所有行并且再次使用QString::split()把它们分隔到每一个单元格中,但是这一次要把制表符“\t”当作分隔符。
void Spreadsheet::del()
{
QList<QTableWidgetItem *> items = selectedItems();
if (!items.isEmpty()) {
foreach (QTableWidgetItem *item, items)
delete item;
somethingChanged();
}
}
del()槽对Edit->Delete菜单选项做出响应。如果有选中的项,那么该函数就会删除它们并且调用somethingChanged()函数。对选择中的每一个Cell对象使用delete足以清空所有这些单元格。当删除QTFableWidget的QTableWidgetTtem的时候,QTableWidget就会注意到这一情况的发生,而如果这些项中有可见的任意项,QTableWidget将会自动对自己进行重绘。如果在一个已经删除过的单元格位置上又调用了cell(),那么该函数将会返回一个空指针。
void Spreadsheet::selectCurrentRow()
{
selectRow(currentRow());
}
void Spreadsheet::selectCurrentColumn()
{
selectColumn(currentColumn());
}
selectCurentRow()和selectCurrentColumn()对Edit->Select->Row和Edit->Select->Column菜单选项做出响应。这些实现分别依赖于QTableWidget的selectRow()和slectClum()函数。我们不必再去实现Edit->Select->All菜单选项的功能,因为该功能可以由QTableWidget从QAstactemView::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()槽会遍历单元格一遍,它从当前光标右侧的单元格开始遍历到这一行的最后一列,然后再从下一行的第一个单元格开始继续遍历,如此反复,直到找到所要查找的文本,或者是直到最后一个单元格为止。
例如,如果当前的单元格是C24,那么就会搜索D24、E24、… 、Z24,然后再去搜索A25、B25、C25、… 、Z25;等等,一直遍历到Z999为止。如果找到了一个匹配项,那么就清空当前选择,把单元格光标移动到那个匹配的单元格上,并且让包含Spreasheet的窗口变成激活状态。如果没能找到匹配的单元格,那么就让应用程序发出"哔"(beep)的一声来表明搜索已经结束,匹配没有成功。
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();
}
fndPrevious()槽与findNext()槽相似,区别之处是它会向相反的方向遍历并且会在单元格A1处停下来。