comparison src/mainwindow.cpp @ 548:dca5bd5b2a06

Merge from branch "fswatcher"
author Chris Cannam
date Tue, 14 Feb 2012 17:55:39 +0000
parents 4edb47f8c3a3
children 06f7ae09015f
comparison
equal deleted inserted replaced
537:a4e699d32a9a 548:dca5bd5b2a06
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(checkFilesystem()));
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();
249 251
250 void MainWindow::hgStat() 252 void MainWindow::hgStat()
251 { 253 {
252 QStringList params; 254 QStringList params;
253 255
254 if (m_showAllFiles) { 256 // We always stat all files, regardless of whether we're showing
255 params << "stat" << "-A"; 257 // them all, because we need them for the filesystem monitor
256 } else { 258 params << "stat" << "-A";
257 params << "stat" << "-ardum";
258 }
259 259
260 m_lastStatOutput = ""; 260 m_lastStatOutput = "";
261
262 // We're about to do a stat, so we can silently bring ourselves
263 // up-to-date on any file changes to this point
264 (void)m_fsWatcher->getChangedPaths(m_fsWatcherToken);
261 265
262 m_runner->requestAction(HgAction(ACT_STAT, m_workFolderPath, params)); 266 m_runner->requestAction(HgAction(ACT_STAT, m_workFolderPath, params));
263 } 267 }
264 268
265 void MainWindow::hgQueryPaths() 269 void MainWindow::hgQueryPaths()
1299 m_lastRevertedFiles.clear(); 1303 m_lastRevertedFiles.clear();
1300 m_mergeTargetRevision = ""; 1304 m_mergeTargetRevision = "";
1301 m_mergeCommitComment = ""; 1305 m_mergeCommitComment = "";
1302 m_stateUnknown = true; 1306 m_stateUnknown = true;
1303 m_needNewLog = true; 1307 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 } 1308 }
1313 1309
1314 void MainWindow::hgServe() 1310 void MainWindow::hgServe()
1315 { 1311 {
1316 QStringList params; 1312 QStringList params;
1819 updateToolBarStyle(); 1815 updateToolBarStyle();
1820 hgRefresh(); 1816 hgRefresh();
1821 } 1817 }
1822 } 1818 }
1823 1819
1824 void MainWindow::updateFileSystemWatcher() 1820 void MainWindow::updateFsWatcher()
1825 { 1821 {
1826 bool justCreated = false; 1822 m_fsWatcher->setWorkDirPath(m_workFolderPath);
1827 if (!m_fsWatcher) { 1823 m_fsWatcher->setTrackedFilePaths(m_hgTabs->getFileStates().trackedFiles());
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 } 1824 }
1923 1825
1924 void MainWindow::checkFilesystem() 1826 void MainWindow::checkFilesystem()
1925 { 1827 {
1926 DEBUG << "MainWindow::checkFilesystem" << endl; 1828 DEBUG << "MainWindow::checkFilesystem" << endl;
1927 hgRefresh(); 1829 if (!m_commandSequenceInProgress) {
1928 } 1830 if (!m_fsWatcher->getChangedPaths(m_fsWatcherToken).empty()) {
1929 1831 hgRefresh();
1930 void MainWindow::fsDirectoryChanged(QString d) 1832 return;
1931 { 1833 }
1932 DEBUG << "MainWindow::fsDirectoryChanged " << d << endl; 1834 }
1933 if (!m_fsWatcherSuspended) { 1835 updateFsWatcher();
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 } 1836 }
1945 1837
1946 QString MainWindow::format1(QString head) 1838 QString MainWindow::format1(QString head)
1947 { 1839 {
1948 return QString("<qt><h3>%1</h3></qt>").arg(head); 1840 return QString("<qt><h3>%1</h3></qt>").arg(head);
2086 output); 1978 output);
2087 } 1979 }
2088 1980
2089 void MainWindow::commandStarting(HgAction action) 1981 void MainWindow::commandStarting(HgAction action)
2090 { 1982 {
2091 // Annoyingly, hg stat actually modifies the working directory -- 1983 m_commandSequenceInProgress = true;
2092 // it creates files called hg-checklink and hg-checkexec to test 1984 }
2093 // properties of the filesystem. For safety's sake, suspend the 1985
2094 // fs watcher while running commands, and restore it shortly after 1986 void MainWindow::commandFailed(HgAction action, QString stdErr, QString stdOut)
2095 // a command has finished.
2096
2097 if (action.action == ACT_STAT) {
2098 suspendFileSystemWatcher();
2099 }
2100 }
2101
2102 void MainWindow::commandFailed(HgAction action, QString stderr, QString stdout)
2103 { 1987 {
2104 DEBUG << "MainWindow::commandFailed" << endl; 1988 DEBUG << "MainWindow::commandFailed" << endl;
2105 restoreFileSystemWatcher(); 1989
1990 m_commandSequenceInProgress = false;
2106 1991
2107 QString setstr; 1992 QString setstr;
2108 #ifdef Q_OS_MAC 1993 #ifdef Q_OS_MAC
2109 setstr = tr("Preferences"); 1994 setstr = tr("Preferences");
2110 #else 1995 #else
2128 MoreInformationDialog::warning 2013 MoreInformationDialog::warning
2129 (this, 2014 (this,
2130 tr("Failed to run Mercurial"), 2015 tr("Failed to run Mercurial"),
2131 tr("Failed to run Mercurial"), 2016 tr("Failed to run Mercurial"),
2132 tr("The Mercurial program either could not be found or failed to run.<br><br>Check that the Mercurial program path is correct in %1.").arg(setstr), 2017 tr("The Mercurial program either could not be found or failed to run.<br><br>Check that the Mercurial program path is correct in %1.").arg(setstr),
2133 stderr); 2018 stdErr);
2134 settings(SettingsDialog::PathsTab); 2019 settings(SettingsDialog::PathsTab);
2135 return; 2020 return;
2136 case ACT_TEST_HG_EXT: 2021 case ACT_TEST_HG_EXT:
2137 MoreInformationDialog::warning 2022 MoreInformationDialog::warning
2138 (this, 2023 (this,
2139 tr("Failed to run Mercurial"), 2024 tr("Failed to run Mercurial"),
2140 tr("Failed to run Mercurial with extension enabled"), 2025 tr("Failed to run Mercurial with extension enabled"),
2141 tr("The Mercurial program failed to run with the EasyMercurial interaction extension enabled.<br>This may indicate an installation problem.<br><br>You may be able to continue working if you switch off &ldquo;Use EasyHg Mercurial Extension&rdquo; in %1. Note that remote repositories that require authentication might not work if you do this.").arg(setstr), 2026 tr("The Mercurial program failed to run with the EasyMercurial interaction extension enabled.<br>This may indicate an installation problem.<br><br>You may be able to continue working if you switch off &ldquo;Use EasyHg Mercurial Extension&rdquo; in %1. Note that remote repositories that require authentication might not work if you do this.").arg(setstr),
2142 stderr); 2027 stdErr);
2143 settings(SettingsDialog::PathsTab); 2028 settings(SettingsDialog::PathsTab);
2144 return; 2029 return;
2145 case ACT_CLONEFROMREMOTE: 2030 case ACT_CLONEFROMREMOTE:
2146 // if clone fails, we have no repo 2031 // if clone fails, we have no repo
2147 m_workFolderPath = ""; 2032 m_workFolderPath = "";
2148 enableDisableActions(); 2033 enableDisableActions();
2149 break; // go on to default report 2034 break; // go on to default report
2150 case ACT_INCOMING: 2035 case ACT_INCOMING:
2151 if (stderr.contains("authorization failed")) { 2036 if (stdErr.contains("authorization failed")) {
2152 reportAuthFailed(stderr); 2037 reportAuthFailed(stdErr);
2153 return; 2038 return;
2154 } else if (stderr.contains("entry cancelled")) { 2039 } else if (stdErr.contains("entry cancelled")) {
2155 // ignore this, user cancelled username or password dialog 2040 // ignore this, user cancelled username or password dialog
2156 return; 2041 return;
2157 } else { 2042 } else {
2158 // Incoming returns non-zero code and no stderr if the 2043 // Incoming returns non-zero code and no stdErr if the
2159 // check was successful but there are no changes 2044 // check was successful but there are no changes
2160 // pending. This is the only case where we need to remove 2045 // pending. This is the only case where we need to remove
2161 // warning messages, because it's the only case where a 2046 // warning messages, because it's the only case where a
2162 // non-zero code can be returned even though the command 2047 // non-zero code can be returned even though the command
2163 // has for our purposes succeeded 2048 // has for our purposes succeeded
2164 QString replaced = stderr; 2049 QString replaced = stdErr;
2165 while (1) { 2050 while (1) {
2166 QString r1 = replaced; 2051 QString r1 = replaced;
2167 r1.replace(QRegExp("warning: [^\\n]*"), ""); 2052 r1.replace(QRegExp("warning: [^\\n]*"), "");
2168 if (r1 == replaced) break; 2053 if (r1 == replaced) break;
2169 replaced = r1.trimmed(); 2054 replaced = r1.trimmed();
2173 return; 2058 return;
2174 } 2059 }
2175 } 2060 }
2176 break; // go on to default report 2061 break; // go on to default report
2177 case ACT_PULL: 2062 case ACT_PULL:
2178 if (stderr.contains("authorization failed")) { 2063 if (stdErr.contains("authorization failed")) {
2179 reportAuthFailed(stderr); 2064 reportAuthFailed(stdErr);
2180 return; 2065 return;
2181 } else if (stderr.contains("entry cancelled")) { 2066 } else if (stdErr.contains("entry cancelled")) {
2182 // ignore this, user cancelled username or password dialog 2067 // ignore this, user cancelled username or password dialog
2183 return; 2068 return;
2184 } else if (stderr.contains("no changes found") || stdout.contains("no changes found")) { 2069 } 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 2070 // success: hg 2.1 starts returning failure code for empty pull/push
2186 commandCompleted(action, stdout); 2071 m_commandSequenceInProgress = true; // there may be further commands
2072 commandCompleted(action, stdOut);
2187 return; 2073 return;
2188 } 2074 }
2189 break; // go on to default report 2075 break; // go on to default report
2190 case ACT_PUSH: 2076 case ACT_PUSH:
2191 if (stderr.contains("creates new remote head")) { 2077 if (stdErr.contains("creates new remote head")) {
2192 reportNewRemoteHeads(stderr); 2078 reportNewRemoteHeads(stdErr);
2193 return; 2079 return;
2194 } else if (stderr.contains("authorization failed")) { 2080 } else if (stdErr.contains("authorization failed")) {
2195 reportAuthFailed(stderr); 2081 reportAuthFailed(stdErr);
2196 return; 2082 return;
2197 } else if (stderr.contains("entry cancelled")) { 2083 } else if (stdErr.contains("entry cancelled")) {
2198 // ignore this, user cancelled username or password dialog 2084 // ignore this, user cancelled username or password dialog
2199 return; 2085 return;
2200 } else if (stderr.contains("no changes found") || stdout.contains("no changes found")) { 2086 } 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 2087 // success: hg 2.1 starts returning failure code for empty pull/push
2202 commandCompleted(action, stdout); 2088 m_commandSequenceInProgress = true; // there may be further commands
2089 commandCompleted(action, stdOut);
2203 return; 2090 return;
2204 } 2091 }
2205 break; // go on to default report 2092 break; // go on to default report
2206 case ACT_QUERY_HEADS_ACTIVE: 2093 case ACT_QUERY_HEADS_ACTIVE:
2207 case ACT_QUERY_HEADS: 2094 case ACT_QUERY_HEADS:
2208 // fails if repo is empty; we don't care (if there's a genuine 2095 // fails if repo is empty; we don't care (if there's a genuine
2209 // problem, something else will fail too). Pretend it 2096 // problem, something else will fail too). Pretend it
2210 // succeeded, so that any further actions that are contingent 2097 // succeeded, so that any further actions that are contingent
2211 // on the success of the heads query get carried out properly. 2098 // on the success of the heads query get carried out properly.
2099 m_commandSequenceInProgress = true; // there may be further commands
2212 commandCompleted(action, ""); 2100 commandCompleted(action, "");
2213 return; 2101 return;
2214 case ACT_FOLDERDIFF: 2102 case ACT_FOLDERDIFF:
2215 case ACT_CHGSETDIFF: 2103 case ACT_CHGSETDIFF:
2216 // external program, unlikely to be anything useful in stderr 2104 // external program, unlikely to be anything useful in stdErr
2217 // and some return with failure codes when something as basic 2105 // and some return with failure codes when something as basic
2218 // as the user closing the window via the wm happens 2106 // as the user closing the window via the wm happens
2219 return; 2107 return;
2220 case ACT_MERGE: 2108 case ACT_MERGE:
2221 if (stderr.contains("working directory ancestor")) { 2109 if (stdErr.contains("working directory ancestor")) {
2222 // arguably we should prevent this upfront, but that's 2110 // arguably we should prevent this upfront, but that's
2223 // trickier! 2111 // trickier!
2224 MoreInformationDialog::information 2112 MoreInformationDialog::information
2225 (this, tr("Merge"), tr("Merge has no effect"), 2113 (this, tr("Merge"), tr("Merge has no effect"),
2226 tr("You asked to merge a revision with one of its ancestors.<p>This has no effect, because the ancestor's changes already exist in both revisions."), 2114 tr("You asked to merge a revision with one of its ancestors.<p>This has no effect, because the ancestor's changes already exist in both revisions."),
2227 stderr); 2115 stdErr);
2228 return; 2116 return;
2229 } 2117 }
2230 // else fall through 2118 // else fall through
2231 case ACT_RETRY_MERGE: 2119 case ACT_RETRY_MERGE:
2232 MoreInformationDialog::information 2120 MoreInformationDialog::information
2233 (this, tr("Merge"), tr("Merge failed"), 2121 (this, tr("Merge"), tr("Merge failed"),
2234 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."), 2122 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."),
2235 stderr); 2123 stdErr);
2236 m_mergeCommitComment = ""; 2124 m_mergeCommitComment = "";
2237 return; 2125 return;
2238 case ACT_STAT: 2126 case ACT_STAT:
2239 break; // go on to default report 2127 break; // go on to default report
2240 default: 2128 default:
2249 2137
2250 MoreInformationDialog::warning 2138 MoreInformationDialog::warning
2251 (this, 2139 (this,
2252 tr("Command failed"), 2140 tr("Command failed"),
2253 tr("Command failed"), 2141 tr("Command failed"),
2254 (stderr == "" ? 2142 (stdErr == "" ?
2255 tr("A Mercurial command failed to run correctly. This may indicate an installation problem or some other problem with EasyMercurial.") : 2143 tr("A Mercurial command failed to run correctly. This may indicate an installation problem or some other problem with EasyMercurial.") :
2256 tr("A Mercurial command failed to run correctly. This may indicate an installation problem or some other problem with EasyMercurial.<br><br>See &ldquo;More Details&rdquo; for the command output.")), 2144 tr("A Mercurial command failed to run correctly. This may indicate an installation problem or some other problem with EasyMercurial.<br><br>See &ldquo;More Details&rdquo; for the command output.")),
2257 stderr); 2145 stdErr);
2258 } 2146 }
2259 2147
2260 void MainWindow::commandCompleted(HgAction completedAction, QString output) 2148 void MainWindow::commandCompleted(HgAction completedAction, QString output)
2261 { 2149 {
2262 // std::cerr << "commandCompleted: " << completedAction.action << std::endl; 2150 // std::cerr << "commandCompleted: " << completedAction.action << std::endl;
2263 2151
2264 restoreFileSystemWatcher();
2265 HGACTIONS action = completedAction.action; 2152 HGACTIONS action = completedAction.action;
2266 2153
2267 if (action == ACT_NONE) return; 2154 if (action == ACT_NONE) return;
2268 2155
2269 output.replace("\r\n", "\n"); 2156 output.replace("\r\n", "\n");
2318 m_currentBranch = output.trimmed(); 2205 m_currentBranch = output.trimmed();
2319 break; 2206 break;
2320 2207
2321 case ACT_STAT: 2208 case ACT_STAT:
2322 m_lastStatOutput = output; 2209 m_lastStatOutput = output;
2323 updateFileSystemWatcher();
2324 break; 2210 break;
2325 2211
2326 case ACT_RESOLVE_LIST: 2212 case ACT_RESOLVE_LIST:
2327 // This happens on every update, after the stat (above) 2213 // This happens on every update, after the stat (above)
2328 if (output != "") { 2214 if (output != "") {
2622 } 2508 }
2623 break; 2509 break;
2624 } 2510 }
2625 2511
2626 if (noMore) { 2512 if (noMore) {
2513 m_commandSequenceInProgress = false;
2627 m_stateUnknown = false; 2514 m_stateUnknown = false;
2628 enableDisableActions(); 2515 enableDisableActions();
2629 m_hgTabs->updateHistory(); 2516 m_hgTabs->updateHistory();
2630 updateRecentMenu(); 2517 updateRecentMenu();
2518 checkFilesystem();
2631 } 2519 }
2632 } 2520 }
2633 2521
2634 void MainWindow::connectActions() 2522 void MainWindow::connectActions()
2635 { 2523 {
2994 #endif 2882 #endif
2995 m_exitAct->setShortcuts(QKeySequence::Quit); 2883 m_exitAct->setShortcuts(QKeySequence::Quit);
2996 m_exitAct->setStatusTip(tr("Exit EasyMercurial")); 2884 m_exitAct->setStatusTip(tr("Exit EasyMercurial"));
2997 2885
2998 //Repository actions 2886 //Repository actions
2999 m_hgRefreshAct = new QAction(QIcon(":/images/status.png"), tr("&Refresh"), this); 2887 m_hgRefreshAct = new QAction(QIcon(":/images/status.png"), tr("&Re-Read Working Folder"), this);
3000 m_hgRefreshAct->setShortcut(tr("Ctrl+R")); 2888 m_hgRefreshAct->setShortcut(tr("Ctrl+R"));
3001 m_hgRefreshAct->setStatusTip(tr("Refresh the window to show the current state of the working folder")); 2889 m_hgRefreshAct->setStatusTip(tr("Refresh the window to show the current state of the working folder"));
3002 2890
3003 m_hgIncomingAct = new QAction(QIcon(":/images/incoming.png"), tr("Pre&view Incoming Changes"), this); 2891 m_hgIncomingAct = new QAction(QIcon(":/images/incoming.png"), tr("Pre&view Incoming Changes"), this);
3004 m_hgIncomingAct->setIconText(tr("Preview")); 2892 m_hgIncomingAct->setIconText(tr("Preview"));
3116 3004
3117 void MainWindow::createToolBars() 3005 void MainWindow::createToolBars()
3118 { 3006 {
3119 int sz = 32; 3007 int sz = 32;
3120 3008
3121 m_fileToolBar = addToolBar(tr("File")); 3009 bool spacingReqd = false;
3122 m_fileToolBar->setIconSize(QSize(sz, sz)); 3010 #ifndef Q_OS_MAC
3123 m_fileToolBar->addAction(m_openAct); 3011 spacingReqd = true;
3124 m_fileToolBar->addAction(m_hgRefreshAct); 3012 #endif
3125 m_fileToolBar->setMovable(false);
3126
3127 m_repoToolBar = addToolBar(tr("Remote"));
3128 m_repoToolBar->setIconSize(QSize(sz, sz));
3129 m_repoToolBar->addAction(m_hgIncomingAct);
3130 m_repoToolBar->addAction(m_hgPullAct);
3131 m_repoToolBar->addAction(m_hgPushAct);
3132 m_repoToolBar->setMovable(false);
3133 3013
3134 m_workFolderToolBar = addToolBar(tr("Work")); 3014 m_workFolderToolBar = addToolBar(tr("Work"));
3135 addToolBar(Qt::LeftToolBarArea, m_workFolderToolBar); 3015 addToolBar(Qt::LeftToolBarArea, m_workFolderToolBar);
3136 m_workFolderToolBar->setIconSize(QSize(sz, sz)); 3016 m_workFolderToolBar->setIconSize(QSize(sz, sz));
3017 if (spacingReqd) {
3018 QWidget *w = new QWidget;
3019 w->setFixedHeight(6);
3020 m_workFolderToolBar->addWidget(w);
3021 }
3137 m_workFolderToolBar->addAction(m_hgFolderDiffAct); 3022 m_workFolderToolBar->addAction(m_hgFolderDiffAct);
3138 m_workFolderToolBar->addSeparator(); 3023 m_workFolderToolBar->addSeparator();
3139 m_workFolderToolBar->addAction(m_hgRevertAct); 3024 m_workFolderToolBar->addAction(m_hgRevertAct);
3140 m_workFolderToolBar->addAction(m_hgUpdateAct); 3025 m_workFolderToolBar->addAction(m_hgUpdateAct);
3141 m_workFolderToolBar->addAction(m_hgCommitAct); 3026 m_workFolderToolBar->addAction(m_hgCommitAct);
3142 m_workFolderToolBar->addAction(m_hgMergeAct); 3027 m_workFolderToolBar->addAction(m_hgMergeAct);
3143 m_workFolderToolBar->addSeparator(); 3028 m_workFolderToolBar->addSeparator();
3144 m_workFolderToolBar->addAction(m_hgAddAct); 3029 m_workFolderToolBar->addAction(m_hgAddAct);
3145 m_workFolderToolBar->addAction(m_hgRemoveAct); 3030 m_workFolderToolBar->addAction(m_hgRemoveAct);
3146 m_workFolderToolBar->setMovable(false); 3031 m_workFolderToolBar->setMovable(false);
3032
3033 m_repoToolBar = addToolBar(tr("Remote"));
3034 m_repoToolBar->setIconSize(QSize(sz, sz));
3035 if (spacingReqd) m_repoToolBar->addWidget(new QLabel(" "));
3036 m_repoToolBar->addAction(m_openAct);
3037 if (spacingReqd) m_repoToolBar->addWidget(new QLabel(" "));
3038 m_repoToolBar->addSeparator();
3039 m_repoToolBar->addAction(m_hgIncomingAct);
3040 m_repoToolBar->addAction(m_hgPullAct);
3041 m_repoToolBar->addAction(m_hgPushAct);
3042 m_repoToolBar->setMovable(false);
3147 3043
3148 updateToolBarStyle(); 3044 updateToolBarStyle();
3149 } 3045 }
3150 3046
3151 void MainWindow::updateToolBarStyle() 3047 void MainWindow::updateToolBarStyle()