]> git.lyx.org Git - features.git/commitdiff
This is one of a series of patches that will merge the layout modules development...
authorRichard Heck <rgheck@comcast.net>
Tue, 28 Aug 2007 16:49:40 +0000 (16:49 +0000)
committerRichard Heck <rgheck@comcast.net>
Tue, 28 Aug 2007 16:49:40 +0000 (16:49 +0000)
Design goal: Allow the use of layout "modules", which are to LaTeX packages as layout files are to LaTeX document classes. Thus, one could have a module that defined certain character styles, environments, commands, or what have you, and include it in various documents, each of which uses a different document class, without having to modify the layout files themselves. For example, a theorems.module could be used with article.layout to provide support for theorem-type environments, without having to modify article.layout itself, and the same module could be used with book.layout, etc.

This third patch just re-factors some code presently in QCitation*. (It also incorporates some bug fixes that have been committed separately.) We're going to use essentially the same set of widgets for choosing modules that is used for choosing citation keys, so we pull the controlling logic out into a new class, QSelectionManager. I did not make this a QWidget. That seemed to me to be overkill, and it would have made things much more complicated, I think...and I'm not all that experienced with Qt, anyway. Anyone who wants to do that is of course welcome.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19860 a592a061-630c-0410-9148-cb99ea01b6c8

src/frontends/qt4/Makefile.am
src/frontends/qt4/QCitation.cpp
src/frontends/qt4/QCitation.h
src/frontends/qt4/QCitationDialog.cpp
src/frontends/qt4/QCitationDialog.h
src/frontends/qt4/QSelectionManager.cpp [new file with mode: 0644]
src/frontends/qt4/QSelectionManager.h [new file with mode: 0644]

index 963785d380032caa3f99511ee1a1cbfa114ddf96..7fb791eba9915f1a7d4bae3b2ce9a59eb1e37eab 100644 (file)
@@ -103,6 +103,7 @@ SOURCEFILES = \
        QPrefs.cpp \
        QRef.cpp \
        QSearch.cpp \
+       QSelectionManager.cpp \
        QSendto.cpp \
        QSetBorder.cpp \
        QShowFile.cpp \
@@ -189,6 +190,7 @@ MOCHEADER = \
        QPrefs.h \
        QRef.h \
        QSearch.h \
+       QSelectionManager.h \
        QSendto.h \
        QSetBorder.h \
        QShowFile.h \
index f0292887f0fc33eb4f02ebf81cab79b1e8f8687e..8224bd681373d9c7dde8122e16f403a28c0e3776 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/**
  * \file QCitation.cpp
  * This file is part of LyX, the document processor.
  * Licence details can be found in the file COPYING.
@@ -6,6 +6,7 @@
  * \author Angus Leeming
  * \author Kalle Dalheimer
  * \author Abdelrazak Younes
+ * \author Richard Heck (adapted to QSelectionManager)
  *
  * Full author contact details are available in file CREDITS.
  */
@@ -190,36 +191,6 @@ QStringList QCitation::getEntriesAsQStringList() {
 }
 
 
-void QCitation::addKey(QModelIndex const & index)
-{
-       cited_keys_.append(index.data().toString());
-       selected_model_.setStringList(cited_keys_);
-}
-
-
-void QCitation::deleteKey(QModelIndex const & index)
-{
-       cited_keys_.removeAt(index.row());
-       selected_model_.setStringList(cited_keys_);
-}
-
-
-void QCitation::upKey(QModelIndex const & index)
-{
-       int pos = index.row();
-       cited_keys_.swap(pos, pos - 1);
-       selected_model_.setStringList(cited_keys_);
-}
-
-
-void QCitation::downKey(QModelIndex const & index)
-{
-       int pos = index.row();
-       cited_keys_.swap(pos, pos + 1);
-       selected_model_.setStringList(cited_keys_);
-}
-
-
 QStringList QCitation::citationStyles(int sel)
 {
        docstring const key = qstring_to_ucs4(cited_keys_[sel]);
@@ -232,6 +203,11 @@ QString QCitation::getKeyInfo(QString const & sel)
        return toqstr(getInfo(qstring_to_ucs4(sel)));
 }
 
+void QCitation::setCitedKeys() 
+{
+       cited_keys_ = selected_model_.stringList();
+}
+
 
 } // namespace frontend
 } // namespace lyx
index 3224982580c131c39f235165127b963c876db4b0..e99a4fc24bb4d4baab51bbf00376d67b5d8adc82 100644 (file)
@@ -7,6 +7,7 @@
  * \author Angus Leeming
  * \author Kalle Dalheimer
  * \author Abdelrazak Younes
+ * \author Richard Heck (adapted to QSelectionManager)
  *
  * Full author contact details are available in file CREDITS.
  */
@@ -69,24 +70,14 @@ public:
                bool reset = false //< whether to reset and search all keys
                );
 
-       /// Add key to selected keys
-       void addKey(QModelIndex const &);
-
-       /// Delete key from selected keys
-       void deleteKey(QModelIndex const &);
-
-       /// Move selected key one place up
-       void upKey(QModelIndex const &);
-
-       /// Move selected key one place down
-       void downKey(QModelIndex const &);
-
        /// List of example cite strings
        QStringList citationStyles(int);
 
        /// Set the Params variable for the Controller.
        virtual void apply(int const choice, bool const full, bool const force,
                                          QString before, QString after);
+       
+       void setCitedKeys();
 
 private:
        /// available keys.
index aebb8d71433be5a7804eb91a4c67c82f581e8d79..a6c63e0b2f07a4d054996a8f79402785408e8aaa 100644 (file)
@@ -7,6 +7,7 @@
  * \author John Levon
  * \author Jürgen Spitzmüller
  * \author Abdelrazak Younes
+ * \author Richard Heck
  *
  * Full author contact details are available in file CREDITS.
  */
@@ -19,6 +20,7 @@
 
 #include "frontends/controllers/frontend_helpers.h"
 #include "frontends/controllers/ControlCitation.h"
+#include "qt_helpers.h"
 
 #include "support/docstring.h"
 
@@ -48,9 +50,6 @@ QCitationDialog::QCitationDialog(Dialog & dialog, QCitation * form)
 
        setWindowTitle(toqstr("LyX: " + getTitle()));
 
-       selectedLV->setModel(form_->selected());
-       availableLV->setModel(form_->available());
-
        connect(citationStyleCO, SIGNAL(activated(int)),
                this, SLOT(changed()));
        connect(fulllistCB, SIGNAL(clicked()),
@@ -63,76 +62,24 @@ QCitationDialog::QCitationDialog(Dialog & dialog, QCitation * form)
                this, SLOT(changed()));
        connect(clearPB, SIGNAL(clicked()),
                findLE, SLOT(clear()));
-       connect(availableLV->selectionModel(),
-               SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
-               this, SLOT(availableChanged(const QModelIndex &, const QModelIndex &)));
-       connect(selectedLV->selectionModel(),
-               SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
-               this, SLOT(selectedChanged(const QModelIndex &, const QModelIndex &)));
        connect(this, SIGNAL(rejected()), this, SLOT(cleanUp()));
-       availableLV->installEventFilter(this);
-       selectedLV->installEventFilter(this);
-       availableFocused_ = true;
-}
 
+       selectionManager = 
+               new QSelectionManager(availableLV, selectedLV, 
+                                     addPB, deletePB, upPB, downPB, 
+                                     form_->available(), form_->selected());
+       connect(selectionManager, SIGNAL(selectionChanged()),
+               this, SLOT(setCitedKeys()));
+       connect(selectionManager, SIGNAL(updateHook()),
+               this, SLOT(updateDialog()));
+       connect(selectionManager, SIGNAL(okHook()),
+                                       this, SLOT(on_okPB_clicked()));
 
-QCitationDialog::~QCitationDialog()
-{
 }
 
 
-bool QCitationDialog::eventFilter(QObject * obj, QEvent * event) 
-{
-       if (obj == availableLV) {
-               if (event->type() != QEvent::KeyPress)
-                       return QObject::eventFilter(obj, event);
-               QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
-               int const keyPressed = keyEvent->key();
-               Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
-               //Enter key without modifier will add current item.
-               //Ctrl-Enter will add it and close the dialog.
-               //This is designed to work both with the main enter key
-               //and the one on the numeric keypad.
-               if ((keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) &&
-                               //We want one or both of Control and Keypad, and nothing else
-                               //(KeypadModifier is what you get if you use the Enter key on the
-                               //numeric keypad.)
-                               (!keyModifiers || 
-                                (keyModifiers == Qt::ControlModifier) ||
-                                (keyModifiers == Qt::KeypadModifier)  ||
-                                (keyModifiers == (Qt::ControlModifier | Qt::KeypadModifier))
-                               )
-                       ) {
-                               if (addPB->isEnabled())
-                                       on_addPB_clicked();
-                               if (keyModifiers & Qt::ControlModifier)
-                                       on_okPB_clicked();
-                               event->accept();
-                               return true;
-               } 
-       } else if (obj == selectedLV) {
-               //Delete or backspace key will delete current item
-               //...with control modifier will clear the list
-               if (event->type() != QEvent::KeyPress)
-                       return QObject::eventFilter(obj, event);
-               QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
-               int const keyPressed = keyEvent->key();
-               Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
-               if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
-                       if (keyModifiers == Qt::NoModifier && deletePB->isEnabled())
-                               on_deletePB_clicked();
-                       else if (keyModifiers == Qt::ControlModifier) {
-                               form_->clearSelection();
-                               updateDialog();
-                       } else
-                               //ignore it otherwise
-                               return QObject::eventFilter(obj, event);
-                       event->accept();
-                       return true;
-               }
-       }
-       return QObject::eventFilter(obj, event);
-}
+QCitationDialog::~QCitationDialog()
+{}
 
 
 void QCitationDialog::cleanUp() 
@@ -232,15 +179,17 @@ void QCitationDialog::update()
 //two methods, though they should be divisible.
 void QCitationDialog::updateDialog()
 {
-       if (availableFocused_ || 
-           selectedLV->selectionModel()->selectedIndexes().isEmpty()) {
-               if (availableLV->selectionModel()->selectedIndexes().isEmpty()
-                                       && availableLV->model()->rowCount() > 0)
-                       availableLV->setCurrentIndex(availableLV->model()->index(0,0));
-               updateInfo(availableLV);
-       } else
-               updateInfo(selectedLV);
-
+       if (selectionManager->selectedFocused()) { 
+               if (selectedLV->selectionModel()->selectedIndexes().isEmpty())
+                       updateInfo(availableLV->currentIndex());
+               else
+                       updateInfo(selectedLV->currentIndex());
+       } else {
+               if (availableLV->selectionModel()->selectedIndexes().isEmpty())
+                       updateInfo(QModelIndex());
+               else
+                       updateInfo(availableLV->currentIndex());
+       }
        setButtons();
 
        textBeforeED->setText(form_->textBefore());
@@ -376,26 +325,15 @@ bool QCitationDialog::isSelected(const QModelIndex & idx)
 
 void QCitationDialog::setButtons()
 {
-       int const arows = availableLV->model()->rowCount();
-       QModelIndexList const availSels = 
-                       availableLV->selectionModel()->selectedIndexes();
-       addPB->setEnabled(arows > 0 &&
-                       !availSels.isEmpty() &&
-                       !isSelected(availSels.first()));
-
+       selectionManager->update();
        int const srows = selectedLV->model()->rowCount();
-       QModelIndexList const selSels = 
-                       selectedLV->selectionModel()->selectedIndexes();
-       int const sel_nr =      selSels.empty() ? -1 : selSels.first().row();
-       deletePB->setEnabled(sel_nr >= 0);
-       upPB->setEnabled(sel_nr > 0);
-       downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
+       applyPB->setEnabled(srows > 0);
+       okPB->setEnabled(srows > 0);
 }
 
 
-void QCitationDialog::updateInfo(QListView const * const qlv)
+void QCitationDialog::updateInfo(QModelIndex const & idx)
 {
-       QModelIndex idx = qlv->currentIndex();
        if (idx.isValid()) {
                QString const keytxt = form_->getKeyInfo(idx.data().toString());
                infoML->document()->setPlainText(keytxt);
@@ -404,112 +342,9 @@ void QCitationDialog::updateInfo(QListView const * const qlv)
 }
 
 
-void QCitationDialog::on_selectedLV_clicked(const QModelIndex &)
-{
-       availableFocused_ = false;
-       updateDialog();
-}
-
-
-void QCitationDialog::selectedChanged(const QModelIndex & idx, const QModelIndex &)
-{
-       if (!idx.isValid())
-               return;
-       updateDialog();
-}
-
-
-void QCitationDialog::on_availableLV_clicked(const QModelIndex &)
-{
-       availableFocused_ = true;
-       updateDialog();
-}
-
-
-void QCitationDialog::availableChanged(const QModelIndex & idx, const QModelIndex &)
-{
-       if (!idx.isValid())
-               return;
-       availableFocused_ = true;
-       updateDialog();
-}
-
-
-void QCitationDialog::on_availableLV_doubleClicked(const QModelIndex & idx)
-{
-       if (isSelected(idx))
-               return;
-       availableFocused_ = true;
-       on_addPB_clicked();
-}
-
-
-namespace {
-//helper function for next two
-QModelIndex getSelectedIndex(QListView * lv) {
-       //Encourage compiler to use NRVO
-       QModelIndex retval = QModelIndex();
-       QModelIndexList selIdx = 
-               lv->selectionModel()->selectedIndexes();
-       if (!selIdx.empty())
-               retval = selIdx.first();
-       return retval;
-}
-}//anonymous namespace
-
-
-void QCitationDialog::on_addPB_clicked()
-{
-       QModelIndex const idxToAdd = getSelectedIndex(availableLV);
-       if (!idxToAdd.isValid())
-               return;
-       QModelIndex idx = selectedLV->currentIndex();
-       form_->addKey(idxToAdd);
-       if (idx.isValid())
-               selectedLV->setCurrentIndex(idx);
-       availableFocused_ = true;
-       updateDialog();
-}
-
-
-void QCitationDialog::on_deletePB_clicked()
-{
-       QModelIndex idx = getSelectedIndex(selectedLV);
-       if (!idx.isValid())
-               return;
-       int nrows = selectedLV->model()->rowCount();
-
-       form_->deleteKey(idx);
-
-       if (idx.row() == nrows - 1)
-               idx = idx.sibling(idx.row() - 1, idx.column());
-
-       if (nrows>1)
-               selectedLV->setCurrentIndex(idx);
-       availableFocused_ = true;
-       updateDialog();
-}
-
-
-void QCitationDialog::on_upPB_clicked()
-{
-       QModelIndex idx = selectedLV->currentIndex();
-       form_->upKey(idx);
-       selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
-       availableLV->selectionModel()->reset();
-       availableFocused_ = false;
-       updateDialog();
-}
-
-
-void QCitationDialog::on_downPB_clicked()
+void QCitationDialog::setCitedKeys() 
 {
-       QModelIndex idx = selectedLV->currentIndex();
-       form_->downKey(idx);
-       selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
-       availableLV->selectionModel()->reset();
-       availableFocused_ = false;
-       updateDialog();
+       form_->setCitedKeys();
 }
 
 
index 04469b9a320c4bab9366115a85ac5ff28b6bcd05..26a65ebec8e99a67497bff38fbf38cdb6b018af2 100644 (file)
@@ -6,6 +6,7 @@
  *
  * \author Kalle Dalheimer
  * \author Abdelrazak Younes
+ * \author Richard Heck
  *
  * Full author contact details are available in file CREDITS.
  */
 #define QCITATIONDIALOG_H
 
 #include "Dialog.h"
+#include "QSelectionManager.h"
 #include "ui_CitationUi.h"
 
 #include <QCloseEvent>
 #include <QKeyEvent>
+#include <QStringList>
+#include <QStringListModel>
 
 namespace lyx {
 namespace frontend {
@@ -43,14 +47,12 @@ public:
        /// Create the dialog if necessary, update it and display it.
        void show();
 
-       /// Update the display of the dialog whilst it is still visible.
-       void update();
-
        /// \return true if the dialog is visible.
        bool isVisible() const;
-       
-       ///
-       bool eventFilter(QObject *, QEvent *);
+
+public Q_SLOTS:
+       /// Update the display of the dialog whilst it is still visible.
+       void update();
 
 protected:
        void closeEvent (QCloseEvent * e);
@@ -60,7 +62,7 @@ protected:
        /// check whether key is already selected
        bool isSelected(const QModelIndex &);
        /// update the display of BibTeX information
-       void updateInfo(QListView const * const);
+       void updateInfo(QModelIndex const &);
 
 protected Q_SLOTS:
        void cleanUp();
@@ -68,22 +70,17 @@ protected Q_SLOTS:
        void on_cancelPB_clicked();
        void on_restorePB_clicked();
        void on_applyPB_clicked();
-       void on_addPB_clicked();
-       void on_deletePB_clicked();
-       void on_upPB_clicked();
-       void on_downPB_clicked();
        void on_findLE_textChanged(const QString & text);
        void on_fieldsCO_currentIndexChanged(int index);
        void on_entriesCO_currentIndexChanged(int index);
        void on_caseCB_stateChanged(int);
        void on_regexCB_stateChanged(int);
-       void on_selectedLV_clicked(const QModelIndex &);
-       void selectedChanged(const QModelIndex &, const QModelIndex &);
-       void on_availableLV_clicked(const QModelIndex &);
-       void on_availableLV_doubleClicked(const QModelIndex &);
-       void availableChanged(const QModelIndex &, const QModelIndex &);
        virtual void changed();
-
+       ///
+       void setCitedKeys();
+       /// performs a limited update, suitable for internal call
+       void updateDialog();
+       
 private:
        /// enable/disable buttons
        void setButtons();
@@ -95,16 +92,12 @@ private:
        void fillEntries();
        /// set the styles combo
        void updateStyle();
-       /// performs a limited update, suitable for internal call
-       void updateDialog();
        /// last used citation style
        int style_;
-       /// which of available and selected is "focused", in the sense
-       /// of which one should be used for updating the info via updateInfo().
-       /// true, obviously, if it is availableLV.
-       bool availableFocused_;
        
        QCitation * form_;
+
+       QSelectionManager * selectionManager;
 };
 
 } // namespace frontend
diff --git a/src/frontends/qt4/QSelectionManager.cpp b/src/frontends/qt4/QSelectionManager.cpp
new file mode 100644 (file)
index 0000000..0779e35
--- /dev/null
@@ -0,0 +1,291 @@
+/**
+ * \file QSelectionManager.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Richard Heck
+ * \author Et Alia
+ *
+ * Some of the material in this file previously appeared in 
+ * QCitationDialog.cpp.
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+#include "QSelectionManager.h"
+
+
+namespace lyx {
+namespace frontend {
+
+QSelectionManager::QSelectionManager(
+               QListView * avail, 
+               QListView * sel,
+               QPushButton * add, 
+               QPushButton * del, 
+               QPushButton * up, 
+               QPushButton * down,
+               QStringListModel * amod,
+               QStringListModel * smod)
+{
+       availableLV = avail;
+       selectedLV = sel;
+       addPB = add;
+       deletePB = del;
+       upPB = up;
+       downPB = down;
+       availableModel = amod;
+       selectedModel = smod;
+       
+       selectedLV->setModel(smod);
+       availableLV->setModel(amod);
+
+       connect(availableLV->selectionModel(),
+                                       SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
+                                                                this, SLOT(availableChanged(const QModelIndex &, const QModelIndex &)));
+       connect(selectedLV->selectionModel(),
+                                       SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
+                                                                this, SLOT(selectedChanged(const QModelIndex &, const QModelIndex &)));
+       connect(addPB, SIGNAL(clicked()), 
+                                       this, SLOT(addPB_clicked()));
+       connect(deletePB, SIGNAL(clicked()), 
+                                       this, SLOT(deletePB_clicked()));
+       connect(upPB, SIGNAL(clicked()), 
+                                       this, SLOT(upPB_clicked()));
+       connect(downPB, SIGNAL(clicked()), 
+                                       this, SLOT(downPB_clicked()));
+       connect(availableLV, SIGNAL(clicked(const QModelIndex &)), 
+                                       this, SLOT(availableLV_clicked(const QModelIndex &)));
+       connect(availableLV, SIGNAL(doubleClicked(const QModelIndex &)), 
+                                       this, SLOT(availableLV_doubleClicked(const QModelIndex &)));
+       connect(selectedLV, SIGNAL(clicked(const QModelIndex &)), 
+                                       this, SLOT(selectedLV_clicked(const QModelIndex &)));
+
+       availableLV->installEventFilter(this);
+       selectedLV->installEventFilter(this);
+}
+
+
+void QSelectionManager::update() {
+       int const arows = availableLV->model()->rowCount();
+       QModelIndexList const availSels = 
+                       availableLV->selectionModel()->selectedIndexes();
+       addPB->setEnabled(arows > 0 &&
+                       !availSels.isEmpty() &&
+                       !isSelected(availSels.first()));
+
+       int const srows = selectedLV->model()->rowCount();
+       QModelIndexList const selSels = 
+                       selectedLV->selectionModel()->selectedIndexes();
+       int const sel_nr =      selSels.empty() ? -1 : selSels.first().row();
+       deletePB->setEnabled(sel_nr >= 0);
+       upPB->setEnabled(sel_nr > 0);
+       downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
+}
+
+
+bool QSelectionManager::isSelected(const QModelIndex & idx)
+{
+       QString const str = idx.data().toString();
+       return selectedModel->stringList().contains(str);
+}
+
+
+void QSelectionManager::availableChanged(const QModelIndex & idx, const QModelIndex &)
+{
+       if (!idx.isValid())
+               return;
+
+       selectedHasFocus_ = false;
+       updateHook();
+}
+
+
+void QSelectionManager::selectedChanged(const QModelIndex & idx, const QModelIndex &)
+{
+       if (!idx.isValid())
+               return;
+
+       selectedHasFocus_ = true;
+       updateHook();
+}
+
+
+namespace {
+//helper function for next two
+       QModelIndex getSelectedIndex(QListView * lv) {
+       //Encourage compiler to use NRVO
+               QModelIndex retval = QModelIndex();
+               QModelIndexList selIdx = 
+                               lv->selectionModel()->selectedIndexes();
+               if (!selIdx.empty())
+                       retval = selIdx.first();
+               return retval;
+       }
+}//anonymous namespace
+
+
+void QSelectionManager::addPB_clicked()
+{
+       QModelIndex const idxToAdd = getSelectedIndex(availableLV);
+       if (!idxToAdd.isValid())
+               return;
+       QModelIndex idx = selectedLV->currentIndex();
+       
+       QStringList keys = selectedModel->stringList();
+       keys.append(idxToAdd.data().toString());
+       selectedModel->setStringList(keys);
+       selectionChanged(); //signal
+       
+       if (idx.isValid())
+               selectedLV->setCurrentIndex(idx);
+       updateHook();
+}
+
+
+void QSelectionManager::deletePB_clicked()
+{
+       QModelIndex idx = getSelectedIndex(selectedLV);
+       if (!idx.isValid())
+               return;
+
+       QStringList keys = selectedModel->stringList();
+       keys.removeAt(idx.row());
+       selectedModel->setStringList(keys);
+       selectionChanged(); //signal
+
+       int nrows = selectedLV->model()->rowCount();
+       if (idx.row() == nrows) //was last item on list
+               idx = idx.sibling(idx.row() - 1, idx.column());
+
+       if (nrows > 1)
+               selectedLV->setCurrentIndex(idx);
+       else if (nrows == 1)
+               selectedLV->setCurrentIndex(selectedLV->model()->index(0,0));
+       selectedHasFocus_ = (nrows > 0);
+       updateHook();
+}
+
+
+void QSelectionManager::upPB_clicked()
+{
+       QModelIndex idx = selectedLV->currentIndex();
+       
+       int const pos = idx.row();
+       QStringList keys = selectedModel->stringList();
+       keys.swap(pos, pos - 1);
+       selectedModel->setStringList(keys);
+       selectionChanged(); //signal
+       
+       selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
+       selectedHasFocus_ = true;
+       updateHook();
+}
+
+
+void QSelectionManager::downPB_clicked()
+{
+       QModelIndex idx = selectedLV->currentIndex();
+       
+       int const pos = idx.row();
+       QStringList keys = selectedModel->stringList();
+       keys.swap(pos, pos + 1);
+       selectedModel->setStringList(keys);
+       selectionChanged(); //signal
+       
+       selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
+       selectedHasFocus_ = true;
+       updateHook();
+}
+
+
+//FIXME These slots do not really do what they need to do, since focus
+//can enter the QListView in other ways. But there are no signals sent
+//in that case. We need to reimplement focusInEvent() to capture those,
+//which means subclassing QListView. (rgh)
+void QSelectionManager::availableLV_clicked(const QModelIndex &)
+{
+       selectedHasFocus_ = false;
+       updateHook();
+}
+
+
+void QSelectionManager::availableLV_doubleClicked(const QModelIndex & idx)
+{
+       if (isSelected(idx))
+               return;
+
+       if (idx.isValid())
+               selectedHasFocus_ = false;
+       addPB_clicked();
+       //updateHook() will be emitted there
+}
+
+
+void QSelectionManager::selectedLV_clicked(const QModelIndex &)
+{
+       selectedHasFocus_ = true;
+       updateHook();
+}
+
+
+bool QSelectionManager::eventFilter(QObject * obj, QEvent * event) 
+{
+       if (obj == availableLV) {
+               if (event->type() != QEvent::KeyPress)
+                       return QObject::eventFilter(obj, event);
+               QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
+               int const keyPressed = keyEvent->key();
+               Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
+               //Enter key without modifier will add current item.
+               //Ctrl-Enter will add it and close the dialog.
+               //This is designed to work both with the main enter key
+               //and the one on the numeric keypad.
+               if ((keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) &&
+                               //We want one or both of Control and Keypad, and nothing else
+                               //(KeypadModifier is what you get if you use the Enter key on the
+                               //numeric keypad.)
+                                        (!keyModifiers || 
+                                        (keyModifiers == Qt::ControlModifier) ||
+                                        (keyModifiers == Qt::KeypadModifier)  ||
+                                        (keyModifiers == (Qt::ControlModifier | Qt::KeypadModifier))
+                                        )
+                        ) {
+                       if (addPB->isEnabled()) {
+                               addPB_clicked();
+                               okHook(); //signal
+                       }
+                       event->accept();
+                       return true;
+                        } 
+       } else if (obj == selectedLV) {
+               //Delete or backspace key will delete current item
+               //...with control modifier will clear the list
+               if (event->type() != QEvent::KeyPress)
+                       return QObject::eventFilter(obj, event);
+               QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
+               int const keyPressed = keyEvent->key();
+               Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
+               if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
+                       if (keyModifiers == Qt::NoModifier && deletePB->isEnabled())
+                               deletePB_clicked();
+                       else if (keyModifiers == Qt::ControlModifier) {
+                               QStringList list = selectedModel->stringList();
+                               list.clear();
+                               selectedModel->setStringList(list);
+                               updateHook();
+                       } else
+                               //ignore it otherwise
+                               return QObject::eventFilter(obj, event);
+                               event->accept();
+                               return true;
+               }
+       }
+       return QObject::eventFilter(obj, event);
+}
+
+}//namespace frontend
+}//namespace lyx
+
+#include "QSelectionManager_moc.cpp"
diff --git a/src/frontends/qt4/QSelectionManager.h b/src/frontends/qt4/QSelectionManager.h
new file mode 100644 (file)
index 0000000..1044979
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * \file QSelectionManager.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Richard Heck
+ * \author Et Alia
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef QSELECTIONMANAGER_H
+#define QSELECTIONMANAGER_H
+
+#include "Dialog.h"
+#include <QObject>
+#include <QKeyEvent>
+#include <QStringList>
+#include <QStringListModel>
+#include <QListView>
+#include <QPushButton>
+
+namespace lyx {
+namespace frontend {
+
+/** Class to manage a collection of widgets that allows selection
+ *  of items from a list of available items. Adapted from code originally
+ *  written for QCitationDialog. 
+ *  Note that this is a not a QWidget, though it could be converted to
+ *  one. Rather, the managed widgets---see constructor for descripton 
+ *  of them---should be created independently, and then passed to the
+ *  constructor.
+ */
+class QSelectionManager: public QObject {
+       Q_OBJECT
+
+       public:
+               ///
+               QSelectionManager(
+                       QListView * availableLV, 
+                       QListView * selectedLV,
+                       QPushButton * addPB, 
+                       QPushButton * delPB, 
+                       QPushButton * upPB, 
+                       QPushButton * downPB,
+                       QStringListModel * availableModel,
+                       QStringListModel * selectedModel);
+               /// Sets the state of the various push buttons, depending upon the
+               /// state of the widgets. (E.g., "delete" is enabled only if the
+               /// selection is non-empty.)
+               virtual void update();
+               
+               /// Not strictly a matter of focus, which may be elsewhere, but
+               /// whether selectedLV is `more focused' than availableLV. Intended
+               /// to be used, for example, in displaying information about a
+               /// highlighted item: should it be the highlighted available item
+               /// or the highlighted selected item that is displayed?
+               bool selectedFocused() { return selectedHasFocus_; };
+
+       Q_SIGNALS:
+               ///Emitted when the list of selected items has changed. 
+               void selectionChanged();
+               ///Emitted when something has changed that might lead the containing 
+               ///dialog to want to update---the focused subwidget or selected item.
+               ///(Specifically, it is emitted by *_PB_clicked() and *_LV_clicked.)
+               ///NOTE: No automatic update of the button state is done here. If you
+               ///just want to do that, connect updateHook() to update(). Much of the
+               ///time, though, you will want to do a bit more processing first, so
+               ///you can connect to some other function that itself calls update().
+               void updateHook();
+               ///Emitted on Ctrl-Enter in the availableLV. Intended to be connected 
+               ///to an "OK" event in the parent dialog.
+               void okHook();
+
+       
+       protected:
+               ///Given a QModelIndex from availableLV, determines whether it has
+               ///been selected (i.e., is also in selectedLV).
+               bool isSelected(const QModelIndex & idx);
+
+       protected Q_SLOTS:
+               ///
+               void availableChanged(const QModelIndex & idx, const QModelIndex &);
+               ///
+               void selectedChanged(const QModelIndex & idx, const QModelIndex &);
+               ///
+               void addPB_clicked();
+               ///
+               void deletePB_clicked();
+               ///
+               void upPB_clicked();
+               ///
+               void downPB_clicked();
+               ///
+               void availableLV_clicked(const QModelIndex &);
+               ///
+               void availableLV_doubleClicked(const QModelIndex &);
+               ///
+               void selectedLV_clicked(const QModelIndex &);
+               ///
+               bool eventFilter(QObject *, QEvent *);
+
+       private:
+               QListView * availableLV;
+               QListView * selectedLV;
+               QPushButton * addPB;
+               QPushButton * deletePB; 
+               QPushButton * upPB;
+               QPushButton * downPB;
+               QStringListModel * availableModel;
+               QStringListModel * selectedModel;
+               Dialog::View * dialog;
+               
+               bool selectedHasFocus_;
+};
+}//namespace frontend
+}//namespace lyx
+#endif