blob: ac5e8bd41d05b2f4eed64427cdb66f0752e94247 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "gui/config_dialog/generic_table_editor.h"
#include <QtCore/QFile>
#include <QtGui/QFileDialog>
#include <QtGui/QtGui>
#include <algorithm> // for unique
#include <cctype>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "base/file_stream.h"
#include "base/util.h"
#include "session/commands.pb.h"
namespace mozc {
namespace gui {
namespace {
const size_t kMaxEntrySize = 10000;
int GetTableHeight(QTableWidget *widget) {
// Dragon Hack:
// Here we use "龍" to calc font size, as it looks almsot square
// const char kHexBaseChar[]= "龍";
const char kHexBaseChar[]= "\xE9\xBE\x8D";
const QRect rect =
QFontMetrics(widget->font()).boundingRect(
QObject::trUtf8(kHexBaseChar));
#ifdef OS_WIN
return static_cast<int>(rect.height() * 1.3);
#else
return static_cast<int>(rect.height() * 1.4);
#endif // OS_WIN
}
} // namespace
GenericTableEditorDialog::GenericTableEditorDialog(QWidget *parent,
size_t column_size)
: QDialog(parent),
edit_menu_(new QMenu(this)),
column_size_(column_size) {
setupUi(this);
editorTableWidget->setAlternatingRowColors(true);
setWindowFlags(Qt::WindowSystemMenuHint | Qt::Tool);
editorTableWidget->setColumnCount(column_size_);
CHECK_GT(column_size_, 0);
// Mac style
#ifdef OS_MACOSX
editorTableWidget->setShowGrid(false);
layout()->setContentsMargins(0, 0, 0, 4);
gridLayout->setHorizontalSpacing(12);
gridLayout->setVerticalSpacing(12);
#endif // OS_MACOSX
editButton->setText(tr("Edit"));
editButton->setMenu(edit_menu_);
QObject::connect(edit_menu_,
SIGNAL(triggered(QAction *)),
this,
SLOT(OnEditMenuAction(QAction *)));
QObject::connect(editorButtonBox,
SIGNAL(clicked(QAbstractButton *)),
this,
SLOT(Clicked(QAbstractButton *)));
editorTableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(editorTableWidget, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(OnContextMenuRequested(const QPoint &)));
editorTableWidget->horizontalHeader()->setStretchLastSection(true);
editorTableWidget->horizontalHeader()->setSortIndicatorShown(true);
editorTableWidget->horizontalHeader()->setHighlightSections(false);
// Do not use QAbstractItemView::AllEditTriggers so that user can easily
// select multiple items. See b/6488800.
editorTableWidget->setEditTriggers(QAbstractItemView::AnyKeyPressed |
QAbstractItemView::DoubleClicked |
QAbstractItemView::SelectedClicked);
editorTableWidget->setSortingEnabled(true);
editorTableWidget->verticalHeader()->setResizeMode(QHeaderView::Fixed);
editorTableWidget->verticalHeader()->setDefaultSectionSize(
GetTableHeight(editorTableWidget));
UpdateMenuStatus();
}
GenericTableEditorDialog::~GenericTableEditorDialog() {}
QTableWidget *GenericTableEditorDialog::mutable_table_widget() {
return editorTableWidget;
}
const string &GenericTableEditorDialog::table() const {
return table_;
}
string *GenericTableEditorDialog::mutable_table() {
return &table_;
}
QMenu *GenericTableEditorDialog::mutable_edit_menu() {
return edit_menu_;
}
bool GenericTableEditorDialog::LoadFromString(const string &str) {
istringstream istr(str);
return LoadFromStream(&istr);
}
void GenericTableEditorDialog::DeleteSelectedItems() {
vector<int> rows;
QList<QTableWidgetItem *> selected =
editorTableWidget->selectedItems();
// remember the last column as user chooses the
// last rows from top to bottom in general.
const int column = selected.isEmpty() ? 0 :
selected.back()->column();
for (int i = 0; i < selected.size(); ++i) {
rows.push_back(selected[i]->row());
}
vector<int>::iterator last = unique(rows.begin(), rows.end());
rows.erase(last, rows.end());
if (rows.empty()) {
QMessageBox::warning(this,
windowTitle(),
tr("No entry is selected"));
return;
}
// Choose next or prev item.
QTableWidgetItem *item = editorTableWidget->item(rows.back() + 1, column);
if (item == NULL) {
item = editorTableWidget->item(rows.back() - 1, column);
}
// select item
if (item != NULL) {
editorTableWidget->setCurrentItem(item);
}
// remove from the buttom
for (int i = rows.size() - 1; i >= 0; --i) {
editorTableWidget->removeRow(rows[i]);
}
UpdateMenuStatus();
}
void GenericTableEditorDialog::InsertEmptyItem(int row) {
editorTableWidget->verticalHeader()->hide();
// It is important to disable auto-sorting before we programmatically edit
// multiple items. Otherwise, one single edit of cell such as
// editorTableWidget->setItem(row, col, data);
// will cause auto-sorting and the target row will be moved to different
// place.
const bool sorting_enabled = editorTableWidget->isSortingEnabled();
if (sorting_enabled) {
editorTableWidget->setSortingEnabled(false);
}
editorTableWidget->insertRow(row);
for (size_t i = 0; i < column_size_; ++i) {
editorTableWidget->setItem(row, i, new QTableWidgetItem(""));
}
QTableWidgetItem *item = editorTableWidget->item(row, 0);
if (item != NULL) {
editorTableWidget->setCurrentItem(item);
editorTableWidget->scrollToItem(item,
QAbstractItemView::PositionAtCenter);
editorTableWidget->editItem(item);
}
// Restore auto-sorting setting if necessary.
if (sorting_enabled) {
// From the usability perspective, auto-sorting should be disabled until
// a user explicitly enables it again by clicking the table header.
// To achieve it, set -1 to the |logicalIndex| in setSortIndicator.
editorTableWidget->horizontalHeader()->setSortIndicator(
-1, Qt::AscendingOrder);
editorTableWidget->setSortingEnabled(true);
}
UpdateMenuStatus();
}
void GenericTableEditorDialog::InsertItem() {
QTableWidgetItem *current = editorTableWidget->currentItem();
if (current == NULL) {
QMessageBox::warning(this,
windowTitle(),
tr("No entry is selected"));
return;
}
InsertEmptyItem(current->row() + 1);
}
void GenericTableEditorDialog::AddNewItem() {
if (editorTableWidget->rowCount() >= max_entry_size()) {
QMessageBox::warning(
this,
windowTitle(),
tr("You can't have more than %1 entries").arg(max_entry_size()));
return;
}
InsertEmptyItem(editorTableWidget->rowCount());
}
void GenericTableEditorDialog::Import() {
const QString filename =
QFileDialog::getOpenFileName(this, tr("import from file"),
QDir::homePath());
if (filename.isEmpty()) {
return;
}
QFile file(filename);
if (!file.exists()) {
QMessageBox::warning(this,
windowTitle(),
tr("File not found"));
return;
}
const qint64 kMaxSize = 100 * 1024;
if (file.size() >= kMaxSize) {
QMessageBox::warning(this,
windowTitle(),
tr("The specified file is too large (>=100K byte)"));
return;
}
InputFileStream ifs(filename.toStdString().c_str());
if (!LoadFromStream(&ifs)) {
QMessageBox::warning(this,
windowTitle(),
tr("Import failed"));
return;
}
}
void GenericTableEditorDialog::Export() {
if (!Update()) {
return;
}
const QString filename =
QFileDialog::getSaveFileName(this, tr("export to file"),
QDir::homePath() + QDir::separator() +
QString(GetDefaultFilename().c_str()));
if (filename.isEmpty()) {
return;
}
OutputFileStream ofs(filename.toStdString().c_str());
if (!ofs) {
QMessageBox::warning(this,
windowTitle(),
tr("Export failed"));
return;
}
ofs << table();
}
void GenericTableEditorDialog::Clicked(QAbstractButton *button) {
// Workaround for http://b/242686
// By changing the foucs, incomplete entries in QTableView are
// submitted to the model.
editButton->setFocus(Qt::MouseFocusReason);
switch (editorButtonBox->buttonRole(button)) {
/// number of role might be increased in the future.
case QDialogButtonBox::AcceptRole:
if (Update()) {
QDialog::accept();
}
break;
default:
QDialog::reject();
break;
}
}
void GenericTableEditorDialog::OnContextMenuRequested(const QPoint &pos) {
QTableWidgetItem *item = editorTableWidget->itemAt(pos);
if (item == NULL) {
return;
}
QMenu *menu = new QMenu(this);
QAction *edit_action = NULL;
const QList<QTableWidgetItem *> selected_items =
editorTableWidget->selectedItems();
if (selected_items.count() == 1) {
edit_action = menu->addAction(tr("Edit entry"));
}
QAction *rename_action = menu->addAction(tr("New entry"));
QAction *delete_action = menu->addAction(tr("Remove entry"));
QAction *selected_action = menu->exec(QCursor::pos());
if (edit_action != NULL && selected_action == edit_action) {
editorTableWidget->editItem(selected_items[0]);
} else if (selected_action == rename_action) {
AddNewItem();
} else if (selected_action == delete_action) {
DeleteSelectedItems();
}
}
void GenericTableEditorDialog::UpdateOKButton(bool status) {
QPushButton *button = editorButtonBox->button(QDialogButtonBox::Ok);
if (button != NULL) {
button->setEnabled(status);
}
}
size_t GenericTableEditorDialog::max_entry_size() const {
return kMaxEntrySize;
}
bool GenericTableEditorDialog::LoadFromStream(istream *is) {
return true;
}
bool GenericTableEditorDialog::Update() {
return true;
}
void GenericTableEditorDialog::UpdateMenuStatus() {}
void GenericTableEditorDialog::OnEditMenuAction(QAction *action) {}
} // namespace gui
} // namespace mozc