comparison src/mainwindow.cpp @ 540:fc2df97920e8 fswatcher

Introduce FsWatcher into main window (though it isn't fully rigged up yet)
author Chris Cannam
date Mon, 13 Feb 2012 17:29:06 +0000
parents a4e699d32a9a
children 0a16db274f2c
comparison
equal deleted inserted replaced
539:3935a7e621ca 540:fc2df97920e8
50 #include "annotatedialog.h" 50 #include "annotatedialog.h"
51 #include "version.h" 51 #include "version.h"
52 #include "workstatuswidget.h" 52 #include "workstatuswidget.h"
53 #include "hgignoredialog.h" 53 #include "hgignoredialog.h"
54 #include "versiontester.h" 54 #include "versiontester.h"
55 #include "fswatcher.h"
55 56
56 57
57 MainWindow::MainWindow(QString myDirPath) : 58 MainWindow::MainWindow(QString myDirPath) :
58 m_myDirPath(myDirPath), 59 m_myDirPath(myDirPath),
59 m_helpDialog(0), 60 m_helpDialog(0)
60 m_fsWatcherGeneralTimer(0),
61 m_fsWatcherRestoreTimer(0),
62 m_fsWatcherSuspended(false)
63 { 61 {
64 setWindowIcon(QIcon(":images/easyhg-icon.png")); 62 setWindowIcon(QIcon(":images/easyhg-icon.png"));
65 63
66 QString wndTitle; 64 QString wndTitle;
67 65
68 m_showAllFiles = false; 66 m_showAllFiles = false;
69 67
70 m_fsWatcher = 0; 68 m_fsWatcher = new FsWatcher();
69 m_fsWatcherToken = m_fsWatcher->getNewToken();
70 m_commandSequenceInProgress = false;
71 connect(m_fsWatcher, SIGNAL(changed()), this, SLOT(fsWatcherChanged()));
72
71 m_commitsSincePush = 0; 73 m_commitsSincePush = 0;
72 m_shouldHgStat = true; 74 m_shouldHgStat = true;
73 75
74 createActions(); 76 createActions();
75 createMenus(); 77 createMenus();
1299 m_lastRevertedFiles.clear(); 1301 m_lastRevertedFiles.clear();
1300 m_mergeTargetRevision = ""; 1302 m_mergeTargetRevision = "";
1301 m_mergeCommitComment = ""; 1303 m_mergeCommitComment = "";
1302 m_stateUnknown = true; 1304 m_stateUnknown = true;
1303 m_needNewLog = true; 1305 m_needNewLog = true;
1304 if (m_fsWatcher) {
1305 delete m_fsWatcherGeneralTimer;
1306 m_fsWatcherGeneralTimer = 0;
1307 delete m_fsWatcherRestoreTimer;
1308 m_fsWatcherRestoreTimer = 0;
1309 delete m_fsWatcher;
1310 m_fsWatcher = 0;
1311 }
1312 } 1306 }
1313 1307
1314 void MainWindow::hgServe() 1308 void MainWindow::hgServe()
1315 { 1309 {
1316 QStringList params; 1310 QStringList params;
1819 updateToolBarStyle(); 1813 updateToolBarStyle();
1820 hgRefresh(); 1814 hgRefresh();
1821 } 1815 }
1822 } 1816 }
1823 1817
1824 void MainWindow::updateFileSystemWatcher()
1825 {
1826 bool justCreated = false;
1827 if (!m_fsWatcher) {
1828 m_fsWatcher = new QFileSystemWatcher();
1829 justCreated = true;
1830 }
1831
1832 // QFileSystemWatcher will refuse to add a file or directory to
1833 // its watch list that it is already watching -- fine, that's what
1834 // we want -- but it prints a warning when this happens, which is
1835 // annoying because it would be the normal case for us. So we'll
1836 // check for duplicates ourselves.
1837 QSet<QString> alreadyWatched;
1838 QStringList dl(m_fsWatcher->directories());
1839 foreach (QString d, dl) alreadyWatched.insert(d);
1840
1841 std::deque<QString> pending;
1842 pending.push_back(m_workFolderPath);
1843
1844 while (!pending.empty()) {
1845
1846 QString path = pending.front();
1847 pending.pop_front();
1848 if (!alreadyWatched.contains(path)) {
1849 m_fsWatcher->addPath(path);
1850 DEBUG << "Added to file system watcher: " << path << endl;
1851 }
1852
1853 QDir d(path);
1854 if (d.exists()) {
1855 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot |
1856 QDir::Readable | QDir::NoSymLinks);
1857 foreach (QString entry, d.entryList()) {
1858 if (entry.startsWith('.')) continue;
1859 QString entryPath = d.absoluteFilePath(entry);
1860 pending.push_back(entryPath);
1861 }
1862 }
1863 }
1864
1865 // The general timer isn't really related to the fs watcher
1866 // object, it just does something similar -- every now and then we
1867 // do a refresh just to update the history dates etc
1868
1869 m_fsWatcherGeneralTimer = new QTimer(this);
1870 connect(m_fsWatcherGeneralTimer, SIGNAL(timeout()),
1871 this, SLOT(checkFilesystem()));
1872 m_fsWatcherGeneralTimer->setInterval(30 * 60 * 1000); // half an hour
1873 m_fsWatcherGeneralTimer->start();
1874
1875 if (justCreated) {
1876 connect(m_fsWatcher, SIGNAL(directoryChanged(QString)),
1877 this, SLOT(fsDirectoryChanged(QString)));
1878 connect(m_fsWatcher, SIGNAL(fileChanged(QString)),
1879 this, SLOT(fsFileChanged(QString)));
1880 }
1881 }
1882
1883 void MainWindow::suspendFileSystemWatcher()
1884 {
1885 DEBUG << "MainWindow::suspendFileSystemWatcher" << endl;
1886 if (m_fsWatcher) {
1887 m_fsWatcherSuspended = true;
1888 if (m_fsWatcherRestoreTimer) {
1889 delete m_fsWatcherRestoreTimer;
1890 m_fsWatcherRestoreTimer = 0;
1891 }
1892 m_fsWatcherGeneralTimer->stop();
1893 }
1894 }
1895
1896 void MainWindow::restoreFileSystemWatcher()
1897 {
1898 DEBUG << "MainWindow::restoreFileSystemWatcher" << endl;
1899 if (m_fsWatcherRestoreTimer) delete m_fsWatcherRestoreTimer;
1900
1901 // The restore timer is used to leave a polite interval between
1902 // being asked to restore the watcher and actually doing so. It's
1903 // a single shot timer each time it's used, but we don't use
1904 // QTimer::singleShot because we want to stop the previous one if
1905 // it's running (via deleting it)
1906
1907 m_fsWatcherRestoreTimer = new QTimer(this);
1908 connect(m_fsWatcherRestoreTimer, SIGNAL(timeout()),
1909 this, SLOT(actuallyRestoreFileSystemWatcher()));
1910 m_fsWatcherRestoreTimer->setInterval(1000);
1911 m_fsWatcherRestoreTimer->setSingleShot(true);
1912 m_fsWatcherRestoreTimer->start();
1913 }
1914
1915 void MainWindow::actuallyRestoreFileSystemWatcher()
1916 {
1917 DEBUG << "MainWindow::actuallyRestoreFileSystemWatcher" << endl;
1918 if (m_fsWatcher) {
1919 m_fsWatcherSuspended = false;
1920 m_fsWatcherGeneralTimer->start();
1921 }
1922 }
1923
1924 void MainWindow::checkFilesystem() 1818 void MainWindow::checkFilesystem()
1925 { 1819 {
1926 DEBUG << "MainWindow::checkFilesystem" << endl; 1820 DEBUG << "MainWindow::checkFilesystem" << endl;
1927 hgRefresh(); 1821 hgRefresh();
1928 } 1822 }
1929 1823
1930 void MainWindow::fsDirectoryChanged(QString d) 1824 void MainWindow::fsWatcherChanged()
1931 { 1825 {
1932 DEBUG << "MainWindow::fsDirectoryChanged " << d << endl; 1826 DEBUG << "MainWindow::fsWatcherChanged" << endl;
1933 if (!m_fsWatcherSuspended) { 1827
1934 hgStat();
1935 }
1936 }
1937
1938 void MainWindow::fsFileChanged(QString f)
1939 {
1940 DEBUG << "MainWindow::fsFileChanged " << f << endl;
1941 if (!m_fsWatcherSuspended) {
1942 hgStat();
1943 }
1944 } 1828 }
1945 1829
1946 QString MainWindow::format1(QString head) 1830 QString MainWindow::format1(QString head)
1947 { 1831 {
1948 return QString("<qt><h3>%1</h3></qt>").arg(head); 1832 return QString("<qt><h3>%1</h3></qt>").arg(head);
2086 output); 1970 output);
2087 } 1971 }
2088 1972
2089 void MainWindow::commandStarting(HgAction action) 1973 void MainWindow::commandStarting(HgAction action)
2090 { 1974 {
2091 // Annoyingly, hg stat actually modifies the working directory -- 1975 m_commandSequenceInProgress = true;
2092 // it creates files called hg-checklink and hg-checkexec to test
2093 // properties of the filesystem. For safety's sake, suspend the
2094 // fs watcher while running commands, and restore it shortly after
2095 // a command has finished.
2096
2097 if (action.action == ACT_STAT) {
2098 suspendFileSystemWatcher();
2099 }
2100 } 1976 }
2101 1977
2102 void MainWindow::commandFailed(HgAction action, QString stderr, QString stdout) 1978 void MainWindow::commandFailed(HgAction action, QString stderr, QString stdout)
2103 { 1979 {
2104 DEBUG << "MainWindow::commandFailed" << endl; 1980 DEBUG << "MainWindow::commandFailed" << endl;
2105 restoreFileSystemWatcher(); 1981
1982 m_commandSequenceInProgress = false;
2106 1983
2107 QString setstr; 1984 QString setstr;
2108 #ifdef Q_OS_MAC 1985 #ifdef Q_OS_MAC
2109 setstr = tr("Preferences"); 1986 setstr = tr("Preferences");
2110 #else 1987 #else
2181 } else if (stderr.contains("entry cancelled")) { 2058 } else if (stderr.contains("entry cancelled")) {
2182 // ignore this, user cancelled username or password dialog 2059 // ignore this, user cancelled username or password dialog
2183 return; 2060 return;
2184 } else if (stderr.contains("no changes found") || stdout.contains("no changes found")) { 2061 } else if (stderr.contains("no changes found") || stdout.contains("no changes found")) {
2185 // success: hg 2.1 starts returning failure code for empty pull/push 2062 // success: hg 2.1 starts returning failure code for empty pull/push
2063 m_commandSequenceInProgress = true; // there may be further commands
2186 commandCompleted(action, stdout); 2064 commandCompleted(action, stdout);
2187 return; 2065 return;
2188 } 2066 }
2189 break; // go on to default report 2067 break; // go on to default report
2190 case ACT_PUSH: 2068 case ACT_PUSH:
2197 } else if (stderr.contains("entry cancelled")) { 2075 } else if (stderr.contains("entry cancelled")) {
2198 // ignore this, user cancelled username or password dialog 2076 // ignore this, user cancelled username or password dialog
2199 return; 2077 return;
2200 } else if (stderr.contains("no changes found") || stdout.contains("no changes found")) { 2078 } else if (stderr.contains("no changes found") || stdout.contains("no changes found")) {
2201 // success: hg 2.1 starts returning failure code for empty pull/push 2079 // success: hg 2.1 starts returning failure code for empty pull/push
2080 m_commandSequenceInProgress = true; // there may be further commands
2202 commandCompleted(action, stdout); 2081 commandCompleted(action, stdout);
2203 return; 2082 return;
2204 } 2083 }
2205 break; // go on to default report 2084 break; // go on to default report
2206 case ACT_QUERY_HEADS_ACTIVE: 2085 case ACT_QUERY_HEADS_ACTIVE:
2207 case ACT_QUERY_HEADS: 2086 case ACT_QUERY_HEADS:
2208 // fails if repo is empty; we don't care (if there's a genuine 2087 // fails if repo is empty; we don't care (if there's a genuine
2209 // problem, something else will fail too). Pretend it 2088 // problem, something else will fail too). Pretend it
2210 // succeeded, so that any further actions that are contingent 2089 // succeeded, so that any further actions that are contingent
2211 // on the success of the heads query get carried out properly. 2090 // on the success of the heads query get carried out properly.
2091 m_commandSequenceInProgress = true; // there may be further commands
2212 commandCompleted(action, ""); 2092 commandCompleted(action, "");
2213 return; 2093 return;
2214 case ACT_FOLDERDIFF: 2094 case ACT_FOLDERDIFF:
2215 case ACT_CHGSETDIFF: 2095 case ACT_CHGSETDIFF:
2216 // external program, unlikely to be anything useful in stderr 2096 // external program, unlikely to be anything useful in stderr
2259 2139
2260 void MainWindow::commandCompleted(HgAction completedAction, QString output) 2140 void MainWindow::commandCompleted(HgAction completedAction, QString output)
2261 { 2141 {
2262 // std::cerr << "commandCompleted: " << completedAction.action << std::endl; 2142 // std::cerr << "commandCompleted: " << completedAction.action << std::endl;
2263 2143
2264 restoreFileSystemWatcher();
2265 HGACTIONS action = completedAction.action; 2144 HGACTIONS action = completedAction.action;
2266 2145
2267 if (action == ACT_NONE) return; 2146 if (action == ACT_NONE) return;
2268 2147
2269 output.replace("\r\n", "\n"); 2148 output.replace("\r\n", "\n");
2318 m_currentBranch = output.trimmed(); 2197 m_currentBranch = output.trimmed();
2319 break; 2198 break;
2320 2199
2321 case ACT_STAT: 2200 case ACT_STAT:
2322 m_lastStatOutput = output; 2201 m_lastStatOutput = output;
2323 updateFileSystemWatcher();
2324 break; 2202 break;
2325 2203
2326 case ACT_RESOLVE_LIST: 2204 case ACT_RESOLVE_LIST:
2327 // This happens on every update, after the stat (above) 2205 // This happens on every update, after the stat (above)
2328 if (output != "") { 2206 if (output != "") {
2622 } 2500 }
2623 break; 2501 break;
2624 } 2502 }
2625 2503
2626 if (noMore) { 2504 if (noMore) {
2505 m_commandSequenceInProgress = false;
2627 m_stateUnknown = false; 2506 m_stateUnknown = false;
2628 enableDisableActions(); 2507 enableDisableActions();
2629 m_hgTabs->updateHistory(); 2508 m_hgTabs->updateHistory();
2630 updateRecentMenu(); 2509 updateRecentMenu();
2631 } 2510 }