changeset 329:ef977e7482cd

Merge from branch "filelist_right_button_menu"
author Chris Cannam
date Fri, 11 Mar 2011 18:08:35 +0000
parents 913d213dd427 (current diff) ea62eb083ed4 (diff)
children bf4bbb53e217
files mainwindow.cpp
diffstat 10 files changed, 424 insertions(+), 245 deletions(-) [+]
line wrap: on
line diff
--- a/easyhg-extdiff.sh	Wed Mar 02 16:29:17 2011 +0000
+++ b/easyhg-extdiff.sh	Fri Mar 11 18:08:35 2011 +0000
@@ -7,24 +7,17 @@
 while [ $# -gt 2 ]; do
     shift
 done
-found=""
 for d in kdiff3 kdiff3.exe; do
     if [ -x "$p/$d" ]; then
-	found=true
-	"$p/$d" "$1" "$2"
-	break
+	exec "$p/$d" "$1" "$2"
     elif [ -x "$(type -path $d)" ]; then
-	found=true
-	"$d" "$1" "$2"
-	break;
+	exec "$d" "$1" "$2"
     fi
 done
-if [ -z "$found" ]; then
-    od=/usr/bin/opendiff
-    if [ -x "$od" ]; then
-	found=true
-	"$od" "$1" "$2" | cat
-    fi
+od=/usr/bin/opendiff
+if [ -x "$od" ]; then
+    "$od" "$1" "$2" | cat
+    exit 0
 fi
-[ -n "$found" ]
+exit 1
 
--- a/easyhg-merge.sh	Wed Mar 02 16:29:17 2011 +0000
+++ b/easyhg-merge.sh	Fri Mar 11 18:08:35 2011 +0000
@@ -12,23 +12,15 @@
 left="$1"
 ancestor="$2"
 right="$3"
-found=""
 for d in kdiff3 kdiff3.exe; do
     if [ -x "$p/$d" ]; then
-	found=true
-	"$p/$d" "$ancestor" "$left" "$right" -o "$out"
-	break
+	exec "$p/$d" "$ancestor" "$left" "$right" -o "$out"
     elif [ -x "$(type -path $d)" ]; then
-	found=true
-	"$d" "$ancestor" "$left" "$right" -o "$out"
-	break;
+	exec "$d" "$ancestor" "$left" "$right" -o "$out"
     fi
 done
-if [ -z "$found" ]; then
-    fm=/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge
-    if [ -x "$fm" ]; then
-	found=true
-	"$fm" -left "$left" -merge "$out" -ancestor "$ancestor" -right "$right"
-    fi
+fm=/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge
+if [ -x "$fm" ]; then
+    exec "$fm" -left "$left" -merge "$out" -ancestor "$ancestor" -right "$right"
 fi
-[ -n "$found" ]
+exit 1
--- a/filestates.cpp	Wed Mar 02 16:29:17 2011 +0000
+++ b/filestates.cpp	Fri Mar 11 18:08:35 2011 +0000
@@ -63,7 +63,7 @@
 QStringList *FileStates::stateToBucket(State s)
 {
     switch (s) {
-    case Clean: return &m_clean; // not implemented yet
+    case Clean: return &m_clean;
     case Modified: return &m_modified;
     case Added: return &m_added;
     case Unknown: return &m_unknown;
@@ -112,14 +112,19 @@
           << m_unknown.size() << " unknown" << endl;
 }
 
-QStringList FileStates::getFilesInState(State s) const
+QStringList FileStates::filesInState(State s) const
 {
     QStringList *sl = const_cast<FileStates *>(this)->stateToBucket(s);
     if (sl) return *sl;
     else return QStringList();
 }
 
-FileStates::State FileStates::getStateOfFile(QString file) const
+bool FileStates::isInState(QString file, State s) const
+{
+    return filesInState(s).contains(file);
+}
+
+FileStates::State FileStates::stateOf(QString file) const
 {
     if (m_stateMap.contains(file)) {
         return m_stateMap[file];
@@ -131,3 +136,85 @@
             << endl;
     return Unknown;
 }
+
+FileStates::Activities FileStates::activitiesSupportedBy(State s)
+{
+    Activities a;
+
+    switch (s) {
+
+    case Modified:
+        a << Annotate << Diff << Commit << Revert << Remove;
+        break;
+
+    case Added:
+        a << Commit << Revert << Remove;
+        break;
+        
+    case Removed:
+        a << Commit << Revert << Add;
+        break;
+
+    case InConflict:
+        a << Annotate << Diff << RedoMerge << MarkResolved << Revert;
+        break;
+
+    case Missing:
+        a << Revert << Remove;
+        break;
+        
+    case Unknown:
+        a << Add << Ignore;
+        break;
+
+    case Clean:
+        a << Annotate << Remove;
+        break;
+
+    case Ignored:
+        a << UnIgnore;
+        break;
+    }
+
+    return a;
+}
+
+bool FileStates::supportsActivity(State s, Activity a)
+{
+    return activitiesSupportedBy(s).contains(a);
+}
+
+int FileStates::activityGroup(Activity a)
+{
+    switch (a) {
+    case Annotate: case Diff: return 0;
+    case Commit: case Revert: return 1;
+    case Add: case Remove: return 2;
+    case RedoMerge: case MarkResolved: return 3;
+    case Ignore: case UnIgnore: return 4;
+    }
+    return 0;
+}
+
+bool FileStates::supportsActivity(QString file, Activity a) const
+{
+    return supportsActivity(stateOf(file), a);
+}
+
+QStringList FileStates::filesSupportingActivity(Activity a) const
+{
+    QStringList f;
+    for (int i = int(FirstState); i <= int(LastState); ++i) {
+        State s = (State)i;
+        if (supportsActivity(s, a)) {
+            f << filesInState(s);
+        }
+    }
+    return f;
+}
+
+FileStates::Activities FileStates::activitiesSupportedBy(QString file) const
+{
+    return activitiesSupportedBy(stateOf(file));
+}
+
--- a/filestates.h	Wed Mar 02 16:29:17 2011 +0000
+++ b/filestates.h	Fri Mar 11 18:08:35 2011 +0000
@@ -47,10 +47,11 @@
 
     void parseStates(QString text);
 
-    void clearBuckets();
+    bool isInState(QString file, State s) const;
+    QStringList filesInState(State s) const;
+    State stateOf(QString file) const;
 
-    QStringList getFilesInState(State) const;
-
+/*!!! -- to remove: */
     QStringList modified() const { return m_modified; }
     QStringList added() const { return m_added; }
     QStringList unknown() const { return m_unknown; }
@@ -60,7 +61,39 @@
     QStringList clean() const { return m_clean; }
     QStringList ignored() const { return m_ignored; }
 
-    State getStateOfFile(QString file) const;
+    enum Activity {
+
+        // These are in the order in which they want to be listed in
+        // the context menu
+
+        Diff,
+        Annotate,
+
+        Commit,
+        Revert,
+
+        Add,
+        Remove,
+
+        RedoMerge,
+        MarkResolved,
+
+        Ignore,
+        UnIgnore,
+
+        FirstActivity = Diff,
+        LastActivity = UnIgnore
+    };
+
+    typedef QList<Activity> Activities;
+
+    static bool supportsActivity(State s, Activity a);
+    static Activities activitiesSupportedBy(State s);
+    static int activityGroup(Activity a);
+    
+    bool supportsActivity(QString file, Activity a) const;
+    QStringList filesSupportingActivity(Activity) const;
+    Activities activitiesSupportedBy(QString file) const;
 
 private:
     QStringList m_modified;
@@ -73,6 +106,8 @@
     QStringList m_ignored;
     QMap<QString, State> m_stateMap;
 
+    void clearBuckets();
+
     State charToState(QChar, bool * = 0);
     QStringList *stateToBucket(State);
 };
--- a/filestatuswidget.cpp	Wed Mar 02 16:29:17 2011 +0000
+++ b/filestatuswidget.cpp	Fri Mar 11 18:08:35 2011 +0000
@@ -31,6 +31,7 @@
 #include <QProcess>
 #include <QCheckBox>
 #include <QSettings>
+#include <QAction>
 
 FileStatusWidget::FileStatusWidget(QWidget *parent) :
     QWidget(parent),
@@ -56,6 +57,17 @@
     m_simpleLabels[FileStates::Unknown] = tr("Untracked:");
     m_simpleLabels[FileStates::Ignored] = tr("Ignored:");
 
+    m_actionLabels[FileStates::Annotate] = tr("Show annotated version");
+    m_actionLabels[FileStates::Diff] = tr("Diff to parent");
+    m_actionLabels[FileStates::Commit] = tr("Commit...");
+    m_actionLabels[FileStates::Revert] = tr("Revert to last committed state");
+    m_actionLabels[FileStates::Add] = tr("Add to version control");
+    m_actionLabels[FileStates::Remove] = tr("Remove from version control");
+    m_actionLabels[FileStates::RedoMerge] = tr("Redo merge");
+    m_actionLabels[FileStates::MarkResolved] = tr("Mark conflict as resolved");
+    m_actionLabels[FileStates::Ignore] = tr("Ignore");
+    m_actionLabels[FileStates::UnIgnore] = tr("Stop ignoring");
+
     m_descriptions[FileStates::Clean] = tr("You have not changed these files.");
     m_descriptions[FileStates::Modified] = tr("You have changed these files since you last committed them.");
     m_descriptions[FileStates::Added] = tr("These files will be added to version control next time you commit them.");
@@ -106,6 +118,30 @@
         connect(w, SIGNAL(itemSelectionChanged()),
                 this, SLOT(itemSelectionChanged()));
 
+        FileStates::Activities activities = m_fileStates.activitiesSupportedBy(s);
+        int prevGroup = -1;
+        foreach (FileStates::Activity a, activities) {
+            // Skip activities which are not yet implemented
+            if (a == FileStates::Annotate ||
+                a == FileStates::Ignore ||
+                a == FileStates::UnIgnore) {
+                continue;
+            }
+            int group = FileStates::activityGroup(a);
+            if (group != prevGroup && prevGroup != -1) {
+                QAction *sep = new QAction("", w);
+                sep->setSeparator(true);
+                w->insertAction(0, sep);
+            }
+            prevGroup = group;
+            QAction *act = new QAction(m_actionLabels[a], w);
+            act->setProperty("state", s);
+            act->setProperty("activity", a);
+            connect(act, SIGNAL(triggered()), this, SLOT(menuActionActivated()));
+            w->insertAction(0, act);
+        }
+        w->setContextMenuPolicy(Qt::ActionsContextMenu);
+
         boxlayout->addItem(new QSpacerItem(2, 2), 3, 0);
 
         boxesLayout->addWidget(box, ++boxRow, 0);
@@ -173,6 +209,44 @@
     }
 }
 
+
+void FileStatusWidget::menuActionActivated()
+{
+    QAction *act = qobject_cast<QAction *>(sender());
+    if (!act) return;
+    
+    FileStates::State state = (FileStates::State)
+        act->property("state").toUInt();
+    FileStates::Activity activity = (FileStates::Activity)
+        act->property("activity").toUInt();
+
+    DEBUG << "menuActionActivated: state = " << state << ", activity = "
+          << activity << endl;
+
+    if (!FileStates::supportsActivity(state, activity)) {
+        std::cerr << "WARNING: FileStatusWidget::menuActionActivated: "
+                  << "Action state " << state << " does not support activity "
+                  << activity << std::endl;
+        return;
+    }
+
+    QStringList files = getSelectedFilesInState(state);
+
+    switch (activity) {
+    case FileStates::Annotate: emit annotateFiles(files); break;
+    case FileStates::Diff: emit diffFiles(files); break;
+    case FileStates::Commit: emit commitFiles(files); break;
+    case FileStates::Revert: emit revertFiles(files); break;
+    case FileStates::Add: emit addFiles(files); break;
+    case FileStates::Remove: emit removeFiles(files); break;
+    case FileStates::RedoMerge: emit redoFileMerges(files); break;
+    case FileStates::MarkResolved: emit markFilesResolved(files); break;
+    case FileStates::Ignore: emit ignoreFiles(files); break;
+    case FileStates::UnIgnore: emit unIgnoreFiles(files); break;
+    }
+}
+
+
 void FileStatusWidget::itemSelectionChanged()
 {
     DEBUG << "FileStatusWidget::itemSelectionChanged" << endl;
@@ -212,9 +286,7 @@
 
 bool FileStatusWidget::haveChangesToCommit() const
 {
-    return !m_fileStates.added().empty() ||
-           !m_fileStates.removed().empty() ||
-           !m_fileStates.modified().empty();
+    return !getAllCommittableFiles().empty();
 }
 
 bool FileStatusWidget::haveSelection() const
@@ -222,136 +294,47 @@
     return !m_selectedFiles.empty();
 }
 
-QStringList FileStatusWidget::getAllSelectedFiles() const
-{
-    return m_selectedFiles;
-}
-
-QStringList FileStatusWidget::getSelectedCommittableFiles() const
+QStringList FileStatusWidget::getSelectedFilesInState(FileStates::State s) const
 {
     QStringList files;
     foreach (QString f, m_selectedFiles) {
-        switch (m_fileStates.getStateOfFile(f)) {
-        case FileStates::Added:
-        case FileStates::Modified:
-        case FileStates::Removed:
-            files.push_back(f);
-            break;
-        default: break;
-        }
+        if (m_fileStates.stateOf(f) == s) files.push_back(f);
     }
     return files;
-}
+}    
+
+QStringList FileStatusWidget::getSelectedFilesSupportingActivity(FileStates::Activity a) const
+{
+    QStringList files;
+    foreach (QString f, m_selectedFiles) {
+        if (m_fileStates.supportsActivity(f, a)) files.push_back(f);
+    }
+    return files;
+}    
 
 QStringList FileStatusWidget::getAllCommittableFiles() const
 {
-    QStringList files;
-    files << m_fileStates.getFilesInState(FileStates::Modified);
-    files << m_fileStates.getFilesInState(FileStates::Added);
-    files << m_fileStates.getFilesInState(FileStates::Removed);
-    return files;
-}
-
-QStringList FileStatusWidget::getSelectedRevertableFiles() const
-{
-    QStringList files;
-    foreach (QString f, m_selectedFiles) {
-        switch (m_fileStates.getStateOfFile(f)) {
-        case FileStates::Added:
-        case FileStates::Modified:
-        case FileStates::Removed:
-        case FileStates::Missing:
-        case FileStates::InConflict:
-            files.push_back(f);
-            break;
-        default: break;
-        }
-    }
-    return files;
+    return m_fileStates.filesSupportingActivity(FileStates::Commit);
 }
 
 QStringList FileStatusWidget::getAllRevertableFiles() const
 {
-    QStringList files;
-    files << m_fileStates.getFilesInState(FileStates::Modified);
-    files << m_fileStates.getFilesInState(FileStates::Added);
-    files << m_fileStates.getFilesInState(FileStates::Removed);
-    files << m_fileStates.getFilesInState(FileStates::Missing);
-    files << m_fileStates.getFilesInState(FileStates::InConflict);
-    return files;
-}
-
-QStringList FileStatusWidget::getSelectedUnresolvedFiles() const
-{
-    QStringList files;
-    foreach (QString f, m_selectedFiles) {
-        switch (m_fileStates.getStateOfFile(f)) {
-        case FileStates::InConflict:
-            files.push_back(f);
-            break;
-        default: break;
-        }
-    }
-    return files;
+    return m_fileStates.filesSupportingActivity(FileStates::Revert);
 }
 
 QStringList FileStatusWidget::getAllUnresolvedFiles() const
 {
-    QStringList files;
-    files << m_fileStates.getFilesInState(FileStates::InConflict);
-    return files;
+    return m_fileStates.filesInState(FileStates::InConflict);
 }
 
 QStringList FileStatusWidget::getSelectedAddableFiles() const
 {
-    QStringList files;
-    foreach (QString f, m_selectedFiles) {
-        switch (m_fileStates.getStateOfFile(f)) {
-        case FileStates::Unknown:
-        case FileStates::Removed:
-            files.push_back(f);
-            break;
-        default: break;
-        }
-    }
-    return files;
-}
-
-QStringList FileStatusWidget::getAllAddableFiles() const
-{
-    QStringList files;
-    files << m_fileStates.getFilesInState(FileStates::Removed);
-    files << m_fileStates.getFilesInState(FileStates::Unknown);
-    return files;
+    return getSelectedFilesSupportingActivity(FileStates::Add);
 }
 
 QStringList FileStatusWidget::getSelectedRemovableFiles() const
 {
-    QStringList files;
-    foreach (QString f, m_selectedFiles) {
-        switch (m_fileStates.getStateOfFile(f)) {
-        case FileStates::Clean:
-        case FileStates::Added:
-        case FileStates::Modified:
-        case FileStates::Missing:
-        case FileStates::InConflict:
-            files.push_back(f);
-            break;
-        default: break;
-        }
-    }
-    return files;
-}
-
-QStringList FileStatusWidget::getAllRemovableFiles() const
-{
-    QStringList files;
-    files << m_fileStates.getFilesInState(FileStates::Clean);
-    files << m_fileStates.getFilesInState(FileStates::Added);
-    files << m_fileStates.getFilesInState(FileStates::Modified);
-    files << m_fileStates.getFilesInState(FileStates::Missing);
-    files << m_fileStates.getFilesInState(FileStates::InConflict);
-    return files;
+    return getSelectedFilesSupportingActivity(FileStates::Remove);
 }
 
 QString
@@ -403,7 +386,7 @@
 
         QListWidget *w = m_stateListMap[s];
         w->clear();
-        QStringList files = m_fileStates.getFilesInState(s);
+        QStringList files = m_fileStates.filesInState(s);
 
         QStringList highPriority, lowPriority;
 
@@ -434,7 +417,7 @@
         foreach (QString file, highPriority) {
             QListWidgetItem *item = new QListWidgetItem(file);
             w->addItem(item);
-            item->setForeground(QColor("#d40000")); //!!! and a nice gold star
+            item->setForeground(QColor("#d40000"));
             item->setSelected(selectedFiles.contains(file));
         }
 
--- a/filestatuswidget.h	Wed Mar 02 16:29:17 2011 +0000
+++ b/filestatuswidget.h	Fri Mar 11 18:08:35 2011 +0000
@@ -46,32 +46,34 @@
     bool haveChangesToCommit() const;
     bool haveSelection() const;
 
-    QStringList getAllSelectedFiles() const;
-
-    QStringList getSelectedCommittableFiles() const;
     QStringList getAllCommittableFiles() const;
-
-    QStringList getSelectedRevertableFiles() const;
     QStringList getAllRevertableFiles() const;
+    QStringList getAllUnresolvedFiles() const;
 
     QStringList getSelectedAddableFiles() const;
-    QStringList getAllAddableFiles() const;
+    QStringList getSelectedRemovableFiles() const;
 
-    QStringList getSelectedRemovableFiles() const;
-    QStringList getAllRemovableFiles() const;
-
-    QStringList getSelectedUnresolvedFiles() const;
-    QStringList getAllUnresolvedFiles() const;
-    
 signals:
     void selectionChanged();
     void showAllChanged(bool);
 
+    void annotateFiles(QStringList);
+    void diffFiles(QStringList);
+    void commitFiles(QStringList);
+    void revertFiles(QStringList);
+    void addFiles(QStringList);
+    void removeFiles(QStringList);
+    void redoFileMerges(QStringList);
+    void markFilesResolved(QStringList);
+    void ignoreFiles(QStringList);
+    void unIgnoreFiles(QStringList);
+
 public slots:
     void clearSelections();
     void updateWidgets();
 
 private slots:
+    void menuActionActivated();
     void itemSelectionChanged();
 
 private:
@@ -84,6 +86,7 @@
     QMap<FileStates::State, QString> m_simpleLabels;
     QMap<FileStates::State, QString> m_descriptions;
     QMap<FileStates::State, QListWidget *> m_stateListMap;
+    QMap<FileStates::Activity, QString> m_actionLabels;
     QString m_highlightExplanation;
 
     QFileInfo *m_dateReference;
@@ -99,6 +102,9 @@
     void setNoModificationsLabelText();
     QString labelFor(FileStates::State, bool addHighlightExplanation = false);
     void setLabelFor(QWidget *w, FileStates::State, bool addHighlightExplanation);
+
+    QStringList getSelectedFilesInState(FileStates::State s) const;
+    QStringList getSelectedFilesSupportingActivity(FileStates::Activity) const;
 };
 
 #endif
--- a/hgtabwidget.cpp	Wed Mar 02 16:29:17 2011 +0000
+++ b/hgtabwidget.cpp	Fri Mar 11 18:08:35 2011 +0000
@@ -30,16 +30,49 @@
                          QString workFolderPath) :
     QTabWidget(parent)
 {
-    // Work page
+    // Work tab
     m_fileStatusWidget = new FileStatusWidget;
     m_fileStatusWidget->setLocalPath(workFolderPath);
+
     connect(m_fileStatusWidget, SIGNAL(selectionChanged()),
             this, SIGNAL(selectionChanged()));
+
     connect(m_fileStatusWidget, SIGNAL(showAllChanged(bool)),
             this, SIGNAL(showAllChanged(bool)));
+
+    connect(m_fileStatusWidget, SIGNAL(annotateFiles(QStringList)),
+            this, SIGNAL(annotateFiles(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(diffFiles(QStringList)),
+            this, SIGNAL(diffFiles(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(commitFiles(QStringList)),
+            this, SIGNAL(commitFiles(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(revertFiles(QStringList)),
+            this, SIGNAL(revertFiles(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(addFiles(QStringList)),
+            this, SIGNAL(addFiles(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(removeFiles(QStringList)),
+            this, SIGNAL(removeFiles(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(redoFileMerges(QStringList)),
+            this, SIGNAL(redoFileMerges(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(markFilesResolved(QStringList)),
+            this, SIGNAL(markFilesResolved(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(ignoreFiles(QStringList)),
+            this, SIGNAL(ignoreFiles(QStringList)));
+
+    connect(m_fileStatusWidget, SIGNAL(unIgnoreFiles(QStringList)),
+            this, SIGNAL(unIgnoreFiles(QStringList)));
+
     addTab(m_fileStatusWidget, tr("My work"));
 
-    // History graph page
+    // History graph tab
     m_historyWidget = new HistoryWidget;
     addTab(m_historyWidget, tr("History"));
 
@@ -116,16 +149,7 @@
 {
     if (!m_fileStatusWidget->haveChangesToCommit()) return false;
     if (!m_fileStatusWidget->getAllUnresolvedFiles().empty()) return false;
-
-    QStringList addable = m_fileStatusWidget->getSelectedAddableFiles();
-    if (addable.empty()) return true;
-
-    QStringList committable = m_fileStatusWidget->getSelectedCommittableFiles();
-
-    // "Removed" files are both committable and addable; don't return
-    // a false negative if the selection only contains these
-    if (committable == addable) return true;
-    return false;
+    return true;
 }
 
 bool HgTabWidget::canRevert() const
@@ -134,35 +158,28 @@
     // unresolved files, but we can't commit them
     if (!m_fileStatusWidget->haveChangesToCommit() &&
         m_fileStatusWidget->getAllUnresolvedFiles().empty()) return false;
-
-    // The rest of this logic is as in canCommit though
-
-    QStringList addable = m_fileStatusWidget->getSelectedAddableFiles();
-    if (addable.empty()) return true;
-
-    QStringList committable = m_fileStatusWidget->getSelectedCommittableFiles();
-    if (committable == addable) return true;
-    return false;
+    return true;
 }
 
 bool HgTabWidget::canAdd() const
 {
+    // Permit this only when work tab is visible
+    if (currentIndex() != 0) return false;
+
     QStringList addable = m_fileStatusWidget->getSelectedAddableFiles();
     if (addable.empty()) return false;
 
     QStringList removable = m_fileStatusWidget->getSelectedRemovableFiles();
     if (!removable.empty()) return false;
 
-    QStringList committable = m_fileStatusWidget->getSelectedCommittableFiles();
-
-    // "Removed" files are both committable and addable; don't return
-    // a false negative if the selection only contains these
-    if (committable == addable || committable.empty()) return true;
-    return false;
+    return true;
 }
 
 bool HgTabWidget::canRemove() const
 {
+    // Permit this only when work tab is visible
+    if (currentIndex() != 0) return false;
+
     if (m_fileStatusWidget->getSelectedRemovableFiles().empty()) return false;
     if (!m_fileStatusWidget->getSelectedAddableFiles().empty()) return false;
     return true;
@@ -170,7 +187,7 @@
 
 bool HgTabWidget::canResolve() const
 {
-    return !m_fileStatusWidget->getSelectedUnresolvedFiles().empty();
+    return !m_fileStatusWidget->getAllUnresolvedFiles().empty();
 }
 
 bool HgTabWidget::haveChangesToCommit() const
@@ -178,41 +195,21 @@
     return m_fileStatusWidget->haveChangesToCommit();
 }
 
-QStringList HgTabWidget::getAllSelectedFiles() const
-{
-    return m_fileStatusWidget->getAllSelectedFiles();
-}
-
 QStringList HgTabWidget::getAllCommittableFiles() const
 {
     return m_fileStatusWidget->getAllCommittableFiles();
 }
 
-QStringList HgTabWidget::getSelectedCommittableFiles() const
-{
-    return m_fileStatusWidget->getSelectedCommittableFiles();
-}
-
 QStringList HgTabWidget::getAllRevertableFiles() const
 {
     return m_fileStatusWidget->getAllRevertableFiles();
 }
 
-QStringList HgTabWidget::getSelectedRevertableFiles() const
-{
-    return m_fileStatusWidget->getSelectedRevertableFiles();
-}
-
 QStringList HgTabWidget::getSelectedAddableFiles() const
 {
     return m_fileStatusWidget->getSelectedAddableFiles();
 }
 
-QStringList HgTabWidget::getAllRemovableFiles() const
-{
-    return m_fileStatusWidget->getAllRemovableFiles();
-}
-
 QStringList HgTabWidget::getSelectedRemovableFiles() const
 {
     return m_fileStatusWidget->getSelectedRemovableFiles();
@@ -223,11 +220,6 @@
     return m_fileStatusWidget->getAllUnresolvedFiles();
 }
 
-QStringList HgTabWidget::getSelectedUnresolvedFiles() const
-{
-    return m_fileStatusWidget->getSelectedUnresolvedFiles();
-}
-
 void HgTabWidget::updateWorkFolderFileList(QString fileList)
 {
     m_fileStates.parseStates(fileList);
--- a/hgtabwidget.h	Wed Mar 02 16:29:17 2011 +0000
+++ b/hgtabwidget.h	Fri Mar 11 18:08:35 2011 +0000
@@ -62,22 +62,12 @@
     bool canResolve() const;
     bool haveChangesToCommit() const;
 
-    QStringList getAllSelectedFiles() const;
-
-    QStringList getSelectedCommittableFiles() const;
     QStringList getAllCommittableFiles() const;
-
-    QStringList getSelectedRevertableFiles() const;
     QStringList getAllRevertableFiles() const;
+    QStringList getAllUnresolvedFiles() const;
 
     QStringList getSelectedAddableFiles() const;
-    QStringList getAllAddableFiles() const;
-
     QStringList getSelectedRemovableFiles() const;
-    QStringList getAllRemovableFiles() const;
-
-    QStringList getSelectedUnresolvedFiles() const;
-    QStringList getAllUnresolvedFiles() const;
 
 signals:
     void selectionChanged();
@@ -98,6 +88,17 @@
     void newBranch(QString id);
     void tag(QString id);
 
+    void annotateFiles(QStringList);
+    void diffFiles(QStringList);
+    void commitFiles(QStringList);
+    void revertFiles(QStringList);
+    void addFiles(QStringList);
+    void removeFiles(QStringList);
+    void redoFileMerges(QStringList);
+    void markFilesResolved(QStringList);
+    void ignoreFiles(QStringList);
+    void unIgnoreFiles(QStringList);
+
 public slots:
     void clearSelections();
     void showWorkTab();
--- a/mainwindow.cpp	Wed Mar 02 16:29:17 2011 +0000
+++ b/mainwindow.cpp	Fri Mar 11 18:08:35 2011 +0000
@@ -370,6 +370,16 @@
     }
 }
 
+void MainWindow::hgAnnotateFiles(QStringList files)
+{
+    QStringList params;
+    
+    if (!files.isEmpty()) {
+        params << "annotate" << "--" << files;
+        m_runner->requestAction(HgAction(ACT_ANNOTATE, m_workFolderPath, params));
+    }
+}
+
 void MainWindow::hgResolveList()
 {
     QStringList params;
@@ -380,12 +390,15 @@
 
 void MainWindow::hgAdd()
 {
-    QStringList params;
-
     // hgExplorer permitted adding "all" files -- I'm not sure
     // that one is a good idea, let's require the user to select
 
-    QStringList files = m_hgTabs->getSelectedAddableFiles();
+    hgAddFiles(m_hgTabs->getSelectedAddableFiles());
+}
+
+void MainWindow::hgAddFiles(QStringList files)
+{
+    QStringList params;
 
     if (!files.empty()) {
         params << "add" << "--" << files;
@@ -393,13 +406,15 @@
     }
 }
 
-
 void MainWindow::hgRemove()
 {
+    hgRemoveFiles(m_hgTabs->getSelectedRemovableFiles());
+}
+
+void MainWindow::hgRemoveFiles(QStringList files)
+{
     QStringList params;
 
-    QStringList files = m_hgTabs->getSelectedRemovableFiles();
-
     if (!files.empty()) {
         params << "remove" << "--after" << "--force" << "--" << files;
         m_runner->requestAction(HgAction(ACT_REMOVE, m_workFolderPath, params));
@@ -408,6 +423,11 @@
 
 void MainWindow::hgCommit()
 {
+    hgCommitFiles(QStringList());
+}
+
+void MainWindow::hgCommitFiles(QStringList files)
+{
     QStringList params;
     QString comment;
 
@@ -415,7 +435,6 @@
         comment = m_mergeCommitComment;
     }
 
-    QStringList files = m_hgTabs->getSelectedCommittableFiles();
     QStringList allFiles = m_hgTabs->getAllCommittableFiles();
     QStringList reportFiles = files;
     if (reportFiles.empty()) {
@@ -498,7 +517,6 @@
     }
 }
 
-
 void MainWindow::hgNoBranch()
 {
     if (m_currentParents.empty()) return;
@@ -511,7 +529,6 @@
     m_runner->requestAction(HgAction(ACT_NEW_BRANCH, m_workFolderPath, params));
 }
 
-
 void MainWindow::hgTag(QString id)
 {
     QStringList params;
@@ -533,7 +550,6 @@
     }
 }
 
-
 void MainWindow::hgIgnore()
 {
     QString hgIgnorePath;
@@ -568,6 +584,18 @@
     m_runner->requestAction(action);
 }
 
+void MainWindow::hgIgnoreFiles(QStringList files)
+{
+    //!!! not implemented yet
+    DEBUG << "MainWindow::hgIgnoreFiles: Not implemented" << endl;
+}
+
+void MainWindow::hgUnIgnoreFiles(QStringList files)
+{
+    //!!! not implemented yet
+    DEBUG << "MainWindow::hgUnIgnoreFiles: Not implemented" << endl;
+}
+
 QString MainWindow::getDiffBinaryName()
 {
     QSettings settings;
@@ -600,6 +628,11 @@
 
 void MainWindow::hgFolderDiff()
 {
+    hgDiffFiles(QStringList());
+}
+
+void MainWindow::hgDiffFiles(QStringList files)
+{
     QString diff = getDiffBinaryName();
     if (diff == "") return;
 
@@ -610,12 +643,11 @@
     params << "--config" << "extensions.extdiff=" << "extdiff";
     params << "--program" << diff;
 
-    params << m_hgTabs->getSelectedCommittableFiles(); // may be none: whole dir
+    params << files; // may be none: whole dir
 
     m_runner->requestAction(HgAction(ACT_FOLDERDIFF, m_workFolderPath, params));
 }
 
-
 void MainWindow::hgDiffToCurrent(QString id)
 {
     QString diff = getDiffBinaryName();
@@ -632,7 +664,6 @@
     m_runner->requestAction(HgAction(ACT_FOLDERDIFF, m_workFolderPath, params));
 }
 
-
 void MainWindow::hgDiffToParent(QString child, QString parent)
 {
     QString diff = getDiffBinaryName();
@@ -649,7 +680,7 @@
 
     m_runner->requestAction(HgAction(ACT_CHGSETDIFF, m_workFolderPath, params));
 }
-
+    
 
 void MainWindow::hgShowSummaryFor(Changeset *cs)
 {
@@ -687,11 +718,15 @@
 
 void MainWindow::hgRevert()
 {
+    hgRevertFiles(QStringList());
+}
+
+void MainWindow::hgRevertFiles(QStringList files)
+{
     QStringList params;
     QString comment;
     bool all = false;
 
-    QStringList files = m_hgTabs->getSelectedRevertableFiles();
     QStringList allFiles = m_hgTabs->getAllRevertableFiles();
     if (files.empty() || files == allFiles) {
         files = allFiles;
@@ -758,7 +793,7 @@
 }
 
 
-void MainWindow::hgMarkResolved(QStringList files)
+void MainWindow::hgMarkFilesResolved(QStringList files)
 {
     QStringList params;
 
@@ -774,7 +809,13 @@
 }
 
 
-void MainWindow::hgRetryMerge()
+void MainWindow::hgRedoMerge()
+{
+    hgRedoFileMerges(QStringList());
+}
+
+
+void MainWindow::hgRedoFileMerges(QStringList files)
 {
     QStringList params;
 
@@ -785,7 +826,6 @@
         params << "--tool" << merge;
     }
 
-    QStringList files = m_hgTabs->getSelectedUnresolvedFiles();
     if (files.empty()) {
         params << "--all";
     } else {
@@ -800,12 +840,12 @@
 
     m_mergeCommitComment = tr("Merge");
 }
-
+    
 
 void MainWindow::hgMerge()
 {
     if (m_hgTabs->canResolve()) {
-        hgRetryMerge();
+        hgRedoMerge();
         return;
     }
 
@@ -1805,6 +1845,13 @@
             reportNewRemoteHeads(output);
             return;
         }
+    case ACT_MERGE:
+    case ACT_RETRY_MERGE:
+        MoreInformationDialog::information
+            (this, tr("Merge"), tr("Merge failed"),
+             tr("Some files were not merged successfully.<p>You can Merge again to repeat the interactive merge; use Revert to abandon the merge entirely; or edit the files that are in conflict in an editor and, when you are happy with them, choose Mark Resolved in each file's right-button menu."),
+             output);
+        return;
     case ACT_STAT:
         break; // go on and report
     default:
@@ -1970,7 +2017,7 @@
         break;
 
     case ACT_REVERT:
-        hgMarkResolved(m_lastRevertedFiles);
+        hgMarkFilesResolved(m_lastRevertedFiles);
         m_justMerged = false;
         break;
         
@@ -2181,6 +2228,9 @@
 
 void MainWindow::connectTabsSignals()
 {
+    connect(m_hgTabs, SIGNAL(currentChanged(int)),
+            this, SLOT(enableDisableActions()));
+
     connect(m_hgTabs, SIGNAL(commit()),
             this, SLOT(hgCommit()));
     
@@ -2219,6 +2269,36 @@
 
     connect(m_hgTabs, SIGNAL(tag(QString)),
             this, SLOT(hgTag(QString)));
+
+    connect(m_hgTabs, SIGNAL(annotateFiles(QStringList)),
+            this, SLOT(hgAnnotateFiles(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(diffFiles(QStringList)),
+            this, SLOT(hgDiffFiles(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(commitFiles(QStringList)),
+            this, SLOT(hgCommitFiles(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(revertFiles(QStringList)),
+            this, SLOT(hgRevertFiles(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(addFiles(QStringList)),
+            this, SLOT(hgAddFiles(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(removeFiles(QStringList)),
+            this, SLOT(hgRemoveFiles(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(redoFileMerges(QStringList)),
+            this, SLOT(hgRedoFileMerges(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(markFilesResolved(QStringList)),
+            this, SLOT(hgMarkFilesResolved(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(ignoreFiles(QStringList)),
+            this, SLOT(hgIgnoreFiles(QStringList)));
+
+    connect(m_hgTabs, SIGNAL(unIgnoreFiles(QStringList)),
+            this, SLOT(hgUnIgnoreFiles(QStringList)));
 }    
 
 void MainWindow::enableDisableActions()
--- a/mainwindow.h	Wed Mar 02 16:29:17 2011 +0000
+++ b/mainwindow.h	Fri Mar 11 18:08:35 2011 +0000
@@ -78,8 +78,7 @@
     void hgUpdate();
     void hgRevert();
     void hgMerge();
-    void hgMarkResolved(QStringList);
-    void hgRetryMerge();
+    void hgRedoMerge();
     void hgCloneFromRemote();
     void hgInit();
     void hgIncoming();
@@ -95,6 +94,17 @@
     void hgServe();
     void hgIgnore();
 
+    void hgAnnotateFiles(QStringList);
+    void hgDiffFiles(QStringList);
+    void hgCommitFiles(QStringList);
+    void hgRevertFiles(QStringList);
+    void hgAddFiles(QStringList);
+    void hgRemoveFiles(QStringList);
+    void hgRedoFileMerges(QStringList);
+    void hgMarkFilesResolved(QStringList);
+    void hgIgnoreFiles(QStringList);
+    void hgUnIgnoreFiles(QStringList);
+
     void fsDirectoryChanged(QString);
     void fsFileChanged(QString);
     void checkFilesystem();