+/////////////////////////////////////////////////////////////////////
+//
+// PrefShortcuts
+//
+/////////////////////////////////////////////////////////////////////
+
+
+GuiShortcutDialog::GuiShortcutDialog(QWidget * parent) : QDialog(parent)
+{
+ Ui::shortcutUi::setupUi(this);
+ QDialog::setModal(true);
+}
+
+
+PrefShortcuts::PrefShortcuts(GuiPreferences * form, QWidget * parent)
+ : PrefModule(_("Shortcuts"), form, parent)
+{
+ setupUi(this);
+
+ shortcutsTW->setColumnCount(2);
+ shortcutsTW->headerItem()->setText(0, qt_("Function"));
+ shortcutsTW->headerItem()->setText(1, qt_("Shortcut"));
+ shortcutsTW->setSortingEnabled(true);
+ // Multi-selection can be annoying.
+ // shortcutsTW->setSelectionMode(QAbstractItemView::MultiSelection);
+ shortcutsTW->header()->resizeSection(0, 200);
+
+ connect(bindFilePB, SIGNAL(clicked()),
+ this, SLOT(select_bind()));
+ connect(bindFileED, SIGNAL(textChanged(QString)),
+ this, SIGNAL(changed()));
+ connect(removePB, SIGNAL(clicked()),
+ this, SIGNAL(changed()));
+
+ shortcut_ = new GuiShortcutDialog(this);
+ shortcut_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
+ shortcut_bc_.setOK(shortcut_->okPB);
+ shortcut_bc_.setCancel(shortcut_->cancelPB);
+
+ connect(shortcut_->okPB, SIGNAL(clicked()),
+ shortcut_, SLOT(accept()));
+ connect(shortcut_->okPB, SIGNAL(clicked()),
+ this, SIGNAL(changed()));
+ connect(shortcut_->cancelPB, SIGNAL(clicked()),
+ shortcut_, SLOT(reject()));
+ connect(shortcut_->clearPB, SIGNAL(clicked()),
+ this, SLOT(shortcut_clearPB_pressed()));
+ connect(shortcut_->okPB, SIGNAL(clicked()),
+ this, SLOT(shortcut_okPB_pressed()));
+}
+
+
+void PrefShortcuts::apply(LyXRC & rc) const
+{
+ rc.bind_file = internal_path(fromqstr(bindFileED->text()));
+ // write user_bind and user_unbind to .lyx/bind/user.bind
+ FileName bind_dir(addPath(package().user_support().absFilename(), "bind"));
+ if (!bind_dir.exists() && !bind_dir.createDirectory(0777)) {
+ lyxerr << "LyX could not create the user bind directory '"
+ << bind_dir << "'. All user-defined key bindings will be lost." << endl;
+ return;
+ }
+ if (!bind_dir.isDirWritable()) {
+ lyxerr << "LyX could not write to the user bind directory '"
+ << bind_dir << "'. All user-defined key bindings will be lost." << endl;
+ return;
+ }
+ FileName user_bind_file(bind_dir.absFilename() + "/user.bind");
+ user_bind_.write(user_bind_file.toFilesystemEncoding(), false, false);
+ user_unbind_.write(user_bind_file.toFilesystemEncoding(), true, true);
+ // immediately apply the keybindings. Why this is not done before?
+ // The good thing is that the menus are updated automatically.
+ theTopLevelKeymap().clear();
+ theTopLevelKeymap().read("site");
+ theTopLevelKeymap().read(rc.bind_file);
+ theTopLevelKeymap().read("user");
+}
+
+
+void PrefShortcuts::update(LyXRC const & rc)
+{
+ bindFileED->setText(toqstr(external_path(rc.bind_file)));
+ //
+ system_bind_.clear();
+ user_bind_.clear();
+ user_unbind_.clear();
+ system_bind_.read(rc.bind_file);
+ // \unbind in user.bind is added to user_unbind_
+ user_bind_.read("user", &user_unbind_);
+ updateShortcutsTW();
+}
+
+
+void PrefShortcuts::updateShortcutsTW()
+{
+ shortcutsTW->clear();
+
+ editItem_ = new QTreeWidgetItem(shortcutsTW);
+ editItem_->setText(0, toqstr("Cursor, Mouse and Editing functions"));
+ editItem_->setFlags(editItem_->flags() & ~Qt::ItemIsSelectable);
+
+ mathItem_ = new QTreeWidgetItem(shortcutsTW);
+ mathItem_->setText(0, toqstr("Mathematical Symbols"));
+ mathItem_->setFlags(mathItem_->flags() & ~Qt::ItemIsSelectable);
+
+ bufferItem_ = new QTreeWidgetItem(shortcutsTW);
+ bufferItem_->setText(0, toqstr("Buffer and Window"));
+ bufferItem_->setFlags(bufferItem_->flags() & ~Qt::ItemIsSelectable);
+
+ layoutItem_ = new QTreeWidgetItem(shortcutsTW);
+ layoutItem_->setText(0, toqstr("Font, Layouts and Textclasses"));
+ layoutItem_->setFlags(layoutItem_->flags() & ~Qt::ItemIsSelectable);
+
+ systemItem_ = new QTreeWidgetItem(shortcutsTW);
+ systemItem_->setText(0, toqstr("System and Miscellaneous"));
+ systemItem_->setFlags(systemItem_->flags() & ~Qt::ItemIsSelectable);
+
+ // listBindings(unbound=true) lists all bound and unbound lfuns
+ // Items in this list is tagged by its source.
+ KeyMap::BindingList bindinglist = system_bind_.listBindings(true,
+ static_cast<int>(System));
+ KeyMap::BindingList user_bindinglist = user_bind_.listBindings(false,
+ static_cast<int>(UserBind));
+ KeyMap::BindingList user_unbindinglist = user_unbind_.listBindings(false,
+ static_cast<int>(UserUnbind));
+ bindinglist.insert(bindinglist.end(), user_bindinglist.begin(),
+ user_bindinglist.end());
+ bindinglist.insert(bindinglist.end(), user_unbindinglist.begin(),
+ user_unbindinglist.end());
+
+ KeyMap::BindingList::const_iterator it = bindinglist.begin();
+ KeyMap::BindingList::const_iterator it_end = bindinglist.end();
+ for (; it != it_end; ++it)
+ insertShortcutItem(it->request, it->sequence, item_type(it->tag));
+
+ shortcutsTW->sortItems(0, Qt::AscendingOrder);
+ QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
+ removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
+}
+
+
+void PrefShortcuts::setItemType(QTreeWidgetItem * item, item_type tag)
+{
+ item->setData(0, Qt::UserRole, QVariant(tag));
+ QFont font;
+
+ switch (tag) {
+ case System:
+ break;
+ case UserBind:
+ font.setBold(true);
+ break;
+ case UserUnbind:
+ font.setStrikeOut(true);
+ break;
+ // this item is not displayed now.
+ case UserExtraUnbind:
+ font.setStrikeOut(true);
+ break;
+ }
+
+ item->setFont(1, font);
+}
+
+
+QTreeWidgetItem * PrefShortcuts::insertShortcutItem(FuncRequest const & lfun,
+ KeySequence const & seq, item_type tag)
+{
+ kb_action action = lfun.action;
+ string const action_name = lyxaction.getActionName(action);
+ QString const lfun_name = toqstr(from_utf8(action_name)
+ + " " + lfun.argument());
+ QString const shortcut = toqstr(seq.print(KeySequence::ForGui));
+ item_type item_tag = tag;
+
+ QTreeWidgetItem * newItem = NULL;
+ // for unbind items, try to find an existing item in the system bind list
+ if (tag == UserUnbind) {
+ QList<QTreeWidgetItem*> const items = shortcutsTW->findItems(lfun_name,
+ Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
+ for (int i = 0; i < items.size(); ++i) {
+ if (items[i]->text(1) == shortcut)
+ newItem = items[i];
+ break;
+ }
+ // if not found, this unbind item is UserExtraUnbind
+ // Such an item is not displayed to avoid confusion (what is
+ // unmatched removed?).
+ if (!newItem) {
+ item_tag = UserExtraUnbind;
+ return NULL;
+ }
+ }
+ if (!newItem) {
+ switch(lyxaction.getActionType(action)) {
+ case LyXAction::Hidden:
+ return NULL;
+ case LyXAction::Edit:
+ newItem = new QTreeWidgetItem(editItem_);
+ break;
+ case LyXAction::Math:
+ newItem = new QTreeWidgetItem(mathItem_);
+ break;
+ case LyXAction::Buffer:
+ newItem = new QTreeWidgetItem(bufferItem_);
+ break;
+ case LyXAction::Layout:
+ newItem = new QTreeWidgetItem(layoutItem_);
+ break;
+ case LyXAction::System:
+ newItem = new QTreeWidgetItem(systemItem_);
+ break;
+ default:
+ // this should not happen
+ newItem = new QTreeWidgetItem(shortcutsTW);
+ }
+ }
+
+ newItem->setText(0, lfun_name);
+ newItem->setText(1, shortcut);
+ // record BindFile representation to recover KeySequence when needed.
+ newItem->setData(1, Qt::UserRole, toqstr(seq.print(KeySequence::BindFile)));
+ setItemType(newItem, item_tag);
+ return newItem;
+}
+
+
+void PrefShortcuts::on_shortcutsTW_itemSelectionChanged()
+{
+ QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
+ removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
+ if (items.isEmpty())
+ return;
+
+ item_type tag = static_cast<item_type>(items[0]->data(0, Qt::UserRole).toInt());
+ if (tag == UserUnbind)
+ removePB->setText(toqstr("Restore"));
+ else
+ removePB->setText(toqstr("Remove"));
+}
+
+
+void PrefShortcuts::on_shortcutsTW_itemDoubleClicked()
+{
+ QTreeWidgetItem * item = shortcutsTW->currentItem();
+ if (item->flags() & Qt::ItemIsSelectable) {
+ shortcut_->lfunLE->setText(item->text(0));
+ // clear the shortcut because I assume that a user will enter
+ // a new shortcut.
+ shortcut_->shortcutLE->reset();
+ shortcut_->shortcutLE->setFocus();
+ shortcut_->exec();
+ }
+}
+
+
+void PrefShortcuts::select_bind()