4-4 实现Edit菜单(Implement the Edit menu)
现在我们开始实现菜单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();
}
{
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菜单。首先得到当前的选择项(如果没有明确选择,返回当前的网格),然后把选择项的公式按顺序记录下来。行之间用换行符隔开,同一行中每一列之间用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();
}
{
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的父QTableItemView::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()从当前网格开始向右查找,查找完当前行后向下一行在继续查找,直到发现匹配的文本为止。如果发现了一个匹配,清除当前选择,把匹配的网格做为当前网格,并把相应的窗口激活。如果没有发现则程序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();
}
函数findPrevious()和findNext()很相似,只是搜索顺序是向前向上的,在A1停止。