You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
10 KiB
331 lines
10 KiB
15 years ago
|
/***************************************************************************
|
||
|
copyright : (C) 2003-2006 by Robby Stephenson
|
||
|
email : robby@periapsis.org
|
||
|
***************************************************************************/
|
||
|
|
||
|
/***************************************************************************
|
||
|
* *
|
||
|
* This program is free software; you can redistribute it and/or modify *
|
||
|
* it under the terms of version 2 of the GNU General Public License as *
|
||
|
* published by the Free Software Foundation; *
|
||
|
* *
|
||
|
***************************************************************************/
|
||
|
|
||
|
#include "tablefieldwidget.h"
|
||
|
#include "../field.h"
|
||
|
#include "../tellico_utils.h"
|
||
|
#include "../tellico_kernel.h"
|
||
|
|
||
|
#include <klocale.h>
|
||
|
#include <kpopupmenu.h>
|
||
|
#include <kiconloader.h>
|
||
|
#include <kinputdialog.h>
|
||
|
|
||
|
#include <qtable.h>
|
||
|
|
||
|
namespace {
|
||
|
static const int MIN_TABLE_ROWS = 5;
|
||
|
static const int MAX_TABLE_COLS = 10;
|
||
|
}
|
||
|
|
||
|
using Tellico::GUI::TableFieldWidget;
|
||
|
|
||
|
TableFieldWidget::TableFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/)
|
||
|
: FieldWidget(field_, parent_, name_), m_field(field_), m_row(-1), m_col(-1) {
|
||
|
|
||
|
bool ok;
|
||
|
m_columns = Tellico::toUInt(field_->property(QString::fromLatin1("columns")), &ok);
|
||
|
if(!ok) {
|
||
|
m_columns = 1;
|
||
|
} else {
|
||
|
m_columns = QMIN(m_columns, MAX_TABLE_COLS); // max of 5 columns
|
||
|
}
|
||
|
|
||
|
m_table = new QTable(MIN_TABLE_ROWS, m_columns, this);
|
||
|
labelColumns(m_field);
|
||
|
// allow renaming of column titles
|
||
|
m_table->horizontalHeader()->setClickEnabled(true);
|
||
|
m_table->horizontalHeader()->installEventFilter(this);
|
||
|
|
||
|
m_table->verticalHeader()->setClickEnabled(true);
|
||
|
m_table->verticalHeader()->installEventFilter(this);
|
||
|
connect(m_table->verticalHeader(), SIGNAL(indexChange(int, int, int)), SIGNAL(modified()));
|
||
|
|
||
|
m_table->setDragEnabled(false);
|
||
|
m_table->setFocusStyle(QTable::FollowStyle);
|
||
|
m_table->setRowMovingEnabled(true); // rows can be moved
|
||
|
m_table->setColumnMovingEnabled(false); // columns remain fixed
|
||
|
|
||
|
m_table->setColumnStretchable(m_columns-1, true);
|
||
|
m_table->adjustColumn(m_columns-1);
|
||
|
m_table->setSelectionMode(QTable::NoSelection);
|
||
|
m_table->setHScrollBarMode(QScrollView::AlwaysOff);
|
||
|
|
||
|
connect(m_table, SIGNAL(valueChanged(int, int)), SIGNAL(modified()));
|
||
|
connect(m_table, SIGNAL(currentChanged(int, int)), SLOT(slotCheckRows(int, int)));
|
||
|
connect(m_table, SIGNAL(valueChanged(int, int)), SLOT(slotResizeColumn(int, int)));
|
||
|
connect(m_table, SIGNAL(contextMenuRequested(int, int, const QPoint&)), SLOT(contextMenu(int, int, const QPoint&)));
|
||
|
|
||
|
registerWidget();
|
||
|
}
|
||
|
|
||
|
QString TableFieldWidget::text() const {
|
||
|
QString text, str, rstack, cstack, rowStr;
|
||
|
for(int row = 0; row < m_table->numRows(); ++row) {
|
||
|
rowStr.truncate(0);
|
||
|
cstack.truncate(0);
|
||
|
for(int col = 0; col < m_table->numCols(); ++col) {
|
||
|
str = m_table->text(row, col).simplifyWhiteSpace();
|
||
|
if(str.isEmpty()) {
|
||
|
cstack += QString::fromLatin1("::");
|
||
|
} else {
|
||
|
rowStr += cstack + str + QString::fromLatin1("::");
|
||
|
cstack.truncate(0);
|
||
|
}
|
||
|
}
|
||
|
if(rowStr.isEmpty()) {
|
||
|
rstack += QString::fromLatin1("; ");
|
||
|
} else {
|
||
|
rowStr.truncate(rowStr.length()-2); // remove last semi-colon and space
|
||
|
text += rstack + rowStr + QString::fromLatin1("; ");
|
||
|
rstack.truncate(0);
|
||
|
}
|
||
|
}
|
||
|
if(!text.isEmpty()) {
|
||
|
text.truncate(text.length()-2); // remove last semi-colon and space
|
||
|
}
|
||
|
|
||
|
// now reduce number of rows if necessary
|
||
|
bool loop = true;
|
||
|
for(int row = m_table->numRows()-1; loop && row > MIN_TABLE_ROWS; --row) {
|
||
|
bool empty = true;
|
||
|
for(int col = 0; col < m_table->numCols(); ++col) {
|
||
|
if(!m_table->text(row, col).isEmpty()) {
|
||
|
empty = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(empty) {
|
||
|
m_table->removeRow(row);
|
||
|
} else {
|
||
|
loop = false;
|
||
|
}
|
||
|
}
|
||
|
return text;
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::setText(const QString& text_) {
|
||
|
QStringList list = Data::Field::split(text_, true);
|
||
|
// add additional rows if needed
|
||
|
if(static_cast<int>(list.count()) > m_table->numRows()) {
|
||
|
m_table->insertRows(m_table->numRows(), list.count()-m_table->numRows());
|
||
|
}
|
||
|
int row;
|
||
|
for(row = 0; row < static_cast<int>(list.count()); ++row) {
|
||
|
for(int col = 0; col < m_table->numCols(); ++col) {
|
||
|
m_table->setText(row, col, list[row].section(QString::fromLatin1("::"), col, col));
|
||
|
}
|
||
|
m_table->showRow(row);
|
||
|
}
|
||
|
// remove any un-needed rows
|
||
|
int minRow = QMAX(row, MIN_TABLE_ROWS);
|
||
|
for(row = m_table->numRows()-1; row >= minRow; --row) {
|
||
|
m_table->removeRow(row);
|
||
|
}
|
||
|
// adjust all columns
|
||
|
for(int col = 0; col < m_table->numCols()-1; ++col) {
|
||
|
m_table->adjustColumn(col);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::clear() {
|
||
|
bool wasEmpty = true;
|
||
|
for(int row = 0; row < m_table->numRows(); ++row) {
|
||
|
if(!emptyRow(row)) {
|
||
|
wasEmpty = false;
|
||
|
}
|
||
|
for(int col = 0; col < m_table->numCols(); ++col) {
|
||
|
m_table->setText(row, col, QString::null);
|
||
|
}
|
||
|
if(row >= MIN_TABLE_ROWS) {
|
||
|
m_table->removeRow(row);
|
||
|
--row;
|
||
|
}
|
||
|
}
|
||
|
editMultiple(false);
|
||
|
if(!wasEmpty) {
|
||
|
emit modified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QWidget* TableFieldWidget::widget() {
|
||
|
return m_table;
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::slotCheckRows(int row_, int) {
|
||
|
if(row_ == m_table->numRows()-1 && !emptyRow(row_)) { // if is last row and row above is not empty
|
||
|
m_table->insertRows(m_table->numRows());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::slotResizeColumn(int, int col_) {
|
||
|
m_table->adjustColumn(col_);
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::slotRenameColumn() {
|
||
|
if(m_col < 0 || m_col >= m_columns) {
|
||
|
return;
|
||
|
}
|
||
|
QString name = m_table->horizontalHeader()->label(m_col);
|
||
|
bool ok;
|
||
|
QString newName = KInputDialog::getText(i18n("Rename Column"), i18n("New column name:"),
|
||
|
name, &ok, this);
|
||
|
if(ok && !newName.isEmpty()) {
|
||
|
Data::FieldPtr newField = new Data::Field(*m_field);
|
||
|
newField->setProperty(QString::fromLatin1("column%1").arg(m_col+1), newName);
|
||
|
if(Kernel::self()->modifyField(newField)) {
|
||
|
m_field = newField;
|
||
|
labelColumns(m_field);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool TableFieldWidget::emptyRow(int row_) const {
|
||
|
for(int col = 0; col < m_table->numCols(); ++col) {
|
||
|
if(!m_table->text(row_, col).isEmpty()) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::labelColumns(Data::FieldPtr field_) {
|
||
|
for(int i = 0; i < m_columns; ++i) {
|
||
|
QString s = field_->property(QString::fromLatin1("column%1").arg(i+1));
|
||
|
if(s.isEmpty()) {
|
||
|
s = i18n("Column %1").arg(i+1);
|
||
|
}
|
||
|
m_table->horizontalHeader()->setLabel(i, s);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::updateFieldHook(Data::FieldPtr, Data::FieldPtr newField_) {
|
||
|
bool ok;
|
||
|
m_columns = Tellico::toUInt(newField_->property(QString::fromLatin1("columns")), &ok);
|
||
|
if(!ok) {
|
||
|
m_columns = 1;
|
||
|
} else {
|
||
|
m_columns = QMIN(m_columns, MAX_TABLE_COLS); // max of 5 columns
|
||
|
}
|
||
|
if(m_columns != m_table->numCols()) {
|
||
|
m_table->setNumCols(m_columns);
|
||
|
}
|
||
|
m_table->horizontalHeader()->adjustHeaderSize();
|
||
|
labelColumns(newField_);
|
||
|
}
|
||
|
|
||
|
bool TableFieldWidget::eventFilter(QObject* obj_, QEvent* ev_) {
|
||
|
if(ev_->type() == QEvent::MouseButtonPress
|
||
|
&& static_cast<QMouseEvent*>(ev_)->button() == Qt::RightButton) {
|
||
|
if(obj_ == m_table->horizontalHeader()) {
|
||
|
QMouseEvent* ev = static_cast<QMouseEvent*>(ev_);
|
||
|
// might be scrolled
|
||
|
int pos = ev->x() + m_table->horizontalHeader()->offset();
|
||
|
int col = m_table->horizontalHeader()->sectionAt(pos);
|
||
|
if(col >= m_columns) {
|
||
|
return false;
|
||
|
}
|
||
|
m_row = -1;
|
||
|
m_col = col;
|
||
|
KPopupMenu menu(this);
|
||
|
menu.insertItem(SmallIconSet(QString::fromLatin1("edit")), i18n("Rename Column..."),
|
||
|
this, SLOT(slotRenameColumn()));
|
||
|
menu.exec(ev->globalPos());
|
||
|
return true;
|
||
|
} else if(obj_ == m_table->verticalHeader()) {
|
||
|
QMouseEvent* ev = static_cast<QMouseEvent*>(ev_);
|
||
|
// might be scrolled
|
||
|
int pos = ev->y() + m_table->verticalHeader()->offset();
|
||
|
int row = m_table->verticalHeader()->sectionAt(pos);
|
||
|
if(row < 0 || row > m_table->numRows()-1) {
|
||
|
return false;
|
||
|
}
|
||
|
m_row = row;
|
||
|
m_col = -1;
|
||
|
// show regular right-click menu
|
||
|
contextMenu(m_row, m_col, ev->globalPos());
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return FieldWidget::eventFilter(obj_, ev_);
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::contextMenu(int row_, int col_, const QPoint& p_) {
|
||
|
// might get called with col == -1 for clicking on vertical header
|
||
|
// but a negative row means clicking outside bounds of table
|
||
|
if(row_ < 0) {
|
||
|
return;
|
||
|
}
|
||
|
m_row = row_;
|
||
|
m_col = col_;
|
||
|
|
||
|
int id;
|
||
|
KPopupMenu menu(this);
|
||
|
menu.insertItem(SmallIconSet(QString::fromLatin1("insrow")), i18n("Insert Row"),
|
||
|
this, SLOT(slotInsertRow()));
|
||
|
menu.insertItem(SmallIconSet(QString::fromLatin1("remrow")), i18n("Remove Row"),
|
||
|
this, SLOT(slotRemoveRow()));
|
||
|
id = menu.insertItem(SmallIconSet(QString::fromLatin1("1uparrow")), i18n("Move Row Up"),
|
||
|
this, SLOT(slotMoveRowUp()));
|
||
|
if(m_row == 0) {
|
||
|
menu.setItemEnabled(id, false);
|
||
|
}
|
||
|
id = menu.insertItem(SmallIconSet(QString::fromLatin1("1downarrow")), i18n("Move Row Down"),
|
||
|
this, SLOT(slotMoveRowDown()));
|
||
|
if(m_row == m_table->numRows()-1) {
|
||
|
menu.setItemEnabled(id, false);
|
||
|
}
|
||
|
menu.insertSeparator();
|
||
|
id = menu.insertItem(SmallIconSet(QString::fromLatin1("edit")), i18n("Rename Column..."),
|
||
|
this, SLOT(slotRenameColumn()));
|
||
|
if(m_col < 0 || m_col > m_columns-1) {
|
||
|
menu.setItemEnabled(id, false);
|
||
|
}
|
||
|
menu.insertSeparator();
|
||
|
menu.insertItem(SmallIconSet(QString::fromLatin1("locationbar_erase")), i18n("Clear Table"),
|
||
|
this, SLOT(clear()));
|
||
|
menu.exec(p_);
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::slotInsertRow() {
|
||
|
if(m_row > -1) {
|
||
|
m_table->insertRows(m_row);
|
||
|
emit modified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::slotRemoveRow() {
|
||
|
if(m_row > -1) {
|
||
|
m_table->removeRow(m_row);
|
||
|
emit modified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::slotMoveRowUp() {
|
||
|
if(m_row > 0) {
|
||
|
m_table->swapRows(m_row, m_row-1, true);
|
||
|
m_table->updateContents();
|
||
|
emit modified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void TableFieldWidget::slotMoveRowDown() {
|
||
|
if(m_row < m_table->numRows()-1) {
|
||
|
m_table->swapRows(m_row, m_row+1, true);
|
||
|
m_table->updateContents();
|
||
|
emit modified();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#include "tablefieldwidget.moc"
|