From 5acd01a0fd32213670f0b831a34b46eab53f614b Mon Sep 17 00:00:00 2001 From: kostas Date: Mon, 17 Oct 2011 14:58:09 +0000 Subject: [PATCH] Added Workspace Tree view Added Rename Folder action Minor GUI cleanups FossilOrigin-Name: 5fc925ac68148c69db66e88e23be196807ffec22 --- MainWindow.cpp | 544 ++++++++++++++++++++++++++++++++++++++++++++----- MainWindow.h | 62 +++++- MainWindow.ui | 204 ++++++++++++++----- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 705 insertions(+), 123 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index edeb1ed..7d0caed 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -24,14 +24,21 @@ QString EOL_MARK("\n"); #endif +#define PATH_SEP "/" + //----------------------------------------------------------------------------- enum { COLUMN_STATUS, - COLUMN_PATH, COLUMN_FILENAME, COLUMN_EXTENSION, - COLUMN_MODIFIED + COLUMN_MODIFIED, + COLUMN_PATH +}; + +enum +{ + REPODIRMODEL_ROLE_PATH = Qt::UserRole+1 }; //----------------------------------------------------------------------------- @@ -128,17 +135,40 @@ MainWindow::MainWindow(QWidget *parent) : ui(new Ui::MainWindow) { ui->setupUi(this); - ui->tableView->setModel(&itemModel); - itemModel.setHorizontalHeaderLabels(QStringList() << tr("S") << tr("Path") << tr("File") << tr("Ext") << tr("Modified") ); + + QAction *separator = new QAction(this); + separator->setSeparator(true); + + // TableView + ui->tableView->setModel(&repoFileModel); ui->tableView->addAction(ui->actionDiff); ui->tableView->addAction(ui->actionHistory); ui->tableView->addAction(ui->actionOpenFile); ui->tableView->addAction(ui->actionOpenContaining); + ui->tableView->addAction(separator); ui->tableView->addAction(ui->actionAdd); - ui->tableView->addAction(ui->actionDelete); + ui->tableView->addAction(ui->actionRevert); ui->tableView->addAction(ui->actionRename); + ui->tableView->addAction(ui->actionDelete); + // TreeView + ui->treeView->setModel(&repoDirModel); + connect( ui->treeView->selectionModel(), + SIGNAL( selectionChanged(const QItemSelection &, const QItemSelection &) ), + SLOT( on_treeView_selectionChanged(const QItemSelection &, const QItemSelection &) ), + Qt::DirectConnection ); + + ui->treeView->addAction(ui->actionCommit); + ui->treeView->addAction(ui->actionOpenFolder); + ui->treeView->addAction(ui->actionAdd); + ui->treeView->addAction(ui->actionRevert); + ui->treeView->addAction(ui->actionDelete); + ui->treeView->addAction(separator); + ui->treeView->addAction(ui->actionRenameFolder); + ui->treeView->addAction(ui->actionOpenFolder); + + // Recent Workspaces // Locate a sequence of two separator actions in file menu QList file_actions = ui->menuFile->actions(); QAction *recent_sep=0; @@ -156,7 +186,7 @@ MainWindow::MainWindow(QWidget *parent) : { recentWorkspaceActs[i] = new QAction(this); recentWorkspaceActs[i]->setVisible(false); - connect(recentWorkspaceActs[i], SIGNAL(triggered()), this, SLOT(onOpenRecent())); + connect(recentWorkspaceActs[i], SIGNAL(triggered()), this, SLOT(on_openRecent())); ui->menuFile->insertAction(recent_sep, recentWorkspaceActs[i]); } @@ -172,6 +202,8 @@ MainWindow::MainWindow(QWidget *parent) : a->setIconVisibleInMenu(false); #endif + viewMode = VIEWMODE_TREE; + loadSettings(); refresh(); rebuildRecent(); @@ -183,6 +215,11 @@ MainWindow::~MainWindow() { stopUI(); saveSettings(); + + // Dispose RepoFiles + for(filemap_t::iterator it = workspaceFiles.begin(); it!=workspaceFiles.end(); ++it) + delete *it; + delete ui; } //----------------------------------------------------------------------------- @@ -254,11 +291,12 @@ bool MainWindow::openWorkspace(const QString &dir) //------------------------------------------------------------------------------ void MainWindow::on_actionOpen_triggered() { - QString path = QFileDialog::getExistingDirectory(this, tr("Fossil Checkout"), QDir::currentPath()); + QString path = QFileDialog::getExistingDirectory(this, tr("Fossil Workspace"), QDir::currentPath()); if(!path.isNull()) openWorkspace(path); } + //------------------------------------------------------------------------------ void MainWindow::rebuildRecent() { @@ -279,7 +317,7 @@ void MainWindow::rebuildRecent() } //------------------------------------------------------------------------------ -void MainWindow::onOpenRecent() +void MainWindow::on_openRecent() { QAction *action = qobject_cast(sender()); if(!action) @@ -305,7 +343,7 @@ bool MainWindow::scanDirectory(QFileInfoList &entries, const QString& dirPath, c QString filename = info.fileName(); QString filepath = info.filePath(); QString rel_path = filepath; - rel_path.remove(baseDir+"/"); + rel_path.remove(baseDir+PATH_SEP); // Skip ignored files if(!ignoreSpec.isEmpty() && QDir::match(ignoreSpec, rel_path)) @@ -351,14 +389,14 @@ bool MainWindow::refresh() { setStatus(tr("No checkout detected.")); enableActions(false); - itemModel.removeRows(0, itemModel.rowCount()); + repoFileModel.removeRows(0, repoFileModel.rowCount()); return false; } else if(st==REPO_OLD_SCHEMA) { setStatus(tr("Old fossil schema detected. Consider running rebuild.")); enableActions(false); - itemModel.removeRows(0, itemModel.rowCount()); + repoFileModel.removeRows(0, repoFileModel.rowCount()); return true; } @@ -393,7 +431,13 @@ void MainWindow::scanWorkspace() setEnabled(false); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Dispose RepoFiles + for(filemap_t::iterator it = workspaceFiles.begin(); it!=workspaceFiles.end(); ++it) + delete *it; + workspaceFiles.clear(); + pathSet.clear(); + if(scan_files) { QCoreApplication::processEvents(); @@ -416,17 +460,20 @@ void MainWindow::scanWorkspace() if(filename == "_FOSSIL_" || (!repositoryFile.isEmpty() && it->absoluteFilePath()==repositoryFile)) continue; - RepoFile e; - e.set(*it, RepoFile::TYPE_UNKNOWN, wkdir); - workspaceFiles.insert(e.getFilename(), e); + RepoFile *rf = new RepoFile(*it, RepoFile::TYPE_UNKNOWN, wkdir); + workspaceFiles.insert(rf->getFilePath(), rf); + pathSet.insert(rf->getPath()); } } + setStatus(tr("Updating...")); QCoreApplication::processEvents(); - for(QStringList::iterator it=res.begin(); it!=res.end(); ++it) + // Update Files and Directories + + for(QStringList::iterator line_it=res.begin(); line_it!=res.end(); ++line_it) { - QString line = (*it).trimmed(); + QString line = (*line_it).trimmed(); if(line.length()==0) continue; @@ -467,26 +514,114 @@ void MainWindow::scanWorkspace() filemap_t::iterator it = workspaceFiles.find(fname); + RepoFile *rf = 0; if(add_missing && it==workspaceFiles.end()) { - RepoFile e; QFileInfo info(wkdir+QDir::separator()+fname); - e.set(info, type, wkdir); - workspaceFiles.insert(e.getFilename(), e); + rf = new RepoFile(info, type, wkdir); + workspaceFiles.insert(rf->getFilePath(), rf); } - it = workspaceFiles.find(fname); - Q_ASSERT(it!=workspaceFiles.end()); + if(!rf) + { + it = workspaceFiles.find(fname); + Q_ASSERT(it!=workspaceFiles.end()); + rf = *it; + } - it.value().setType(type); + rf->setType(type); + + QString path = rf->getPath(); + pathSet.insert(path); } - // Update the model + // Update the file item model + updateDirView(); + updateFileView(); + + setEnabled(true); + setStatus(""); + QApplication::restoreOverrideCursor(); +} + +//------------------------------------------------------------------------------ +static void addPathToTree(QStandardItem &root, const QString &path) +{ + QStringList dirs = path.split('/'); + QStandardItem *parent = &root; + + QString fullpath; + for(QStringList::iterator it = dirs.begin(); it!=dirs.end(); ++it) + { + const QString &dir = *it; + fullpath += dir; + + // Find the child that matches this subdir + bool found = false; + for(int r=0; rrowCount(); ++r) + { + QStandardItem *child = parent->child(r, 0); + Q_ASSERT(child); + if(child->text() == dir) + { + parent = child; + found = true; + } + } + + if(!found) // Generate it + { + QStandardItem *child = new QStandardItem(QIcon(":icons/icons/Folder-01.png"), dir); + child->setData(fullpath); // keep the full path to simplify selection + parent->appendRow(child); + parent = child; + } + fullpath += '/'; + } +} + +//------------------------------------------------------------------------------ +void MainWindow::updateDirView() +{ + if(viewMode == VIEWMODE_TREE) + { + // Directory View + repoDirModel.clear(); + QStandardItem *root = new QStandardItem(QIcon(":icons/icons/My Documents-01.png"), projectName); + root->setData(""); // Empty Path + root->setEditable(false); + QString aa = root->data().toString(); + repoDirModel.appendRow(root); + for(pathset_t::iterator it = pathSet.begin(); it!=pathSet.end(); ++it) + { + const QString &dir = *it; + if(dir.isEmpty()) + continue; + + addPathToTree(*root, dir); + } + ui->treeView->expandToDepth(0); + } +} + +//------------------------------------------------------------------------------ +void MainWindow::updateFileView() +{ + // File View // Clear all rows (except header) - itemModel.removeRows(0, itemModel.rowCount()); + repoFileModel.clear(); + + QStringList header; + header << tr("S") << tr("File") << tr("Ext") << tr("Modified"); + + if(viewMode==VIEWMODE_LIST) + header << tr("Path"); + + repoFileModel.setHorizontalHeaderLabels(header); struct { RepoFile::EntryType type; const char *tag; const char *tooltip; const char *icon; } - stats[] = { + stats[] = + { { RepoFile::TYPE_EDITTED, "E", "Editted", ":icons/icons/Button Blank Yellow-01.png" }, { RepoFile::TYPE_UNCHANGED, "U", "Unchanged", ":icons/icons/Button Blank Green-01.png" }, { RepoFile::TYPE_ADDED, "A", "Added", ":icons/icons/Button Add-01.png" }, @@ -495,13 +630,18 @@ void MainWindow::scanWorkspace() { RepoFile::TYPE_MISSING, "M", "Missing", ":icons/icons/Button Help-01.png" }, }; - size_t num_files = workspaceFiles.size(); - itemModel.insertRows(0, num_files); + //size_t num_files = workspaceFiles.size(); + //repoFileModel.insertRows(0, num_files); - size_t i=0; - for(filemap_t::iterator it = workspaceFiles.begin(); it!=workspaceFiles.end(); ++it, ++i) + size_t item_id=0; + for(filemap_t::iterator it = workspaceFiles.begin(); it!=workspaceFiles.end(); ++it) { - const RepoFile &e = it.value(); + const RepoFile &e = *it.value(); + QString path = e.getPath(); + + // In Tree mode, filter all items not included in the current dir + if(viewMode==VIEWMODE_TREE && path != viewDir) + continue; // Status Column const char *tag = "?"; // Default Tag @@ -521,26 +661,35 @@ void MainWindow::scanWorkspace() QStandardItem *status = new QStandardItem(QIcon(status_icon), tag); status->setToolTip(tooltip); - itemModel.setItem(i, COLUMN_STATUS, status); + repoFileModel.setItem(item_id, COLUMN_STATUS, status); - QString path = e.getFilename(); - path = path.left(path.indexOf(e.getFileInfo().fileName())); QFileInfo finfo = e.getFileInfo(); - itemModel.setItem(i, COLUMN_PATH, new QStandardItem(path)); - itemModel.setItem(i, COLUMN_FILENAME, new QStandardItem(e.getFilename())); - itemModel.setItem(i, COLUMN_EXTENSION, new QStandardItem(finfo .completeSuffix())); - itemModel.setItem(i, COLUMN_MODIFIED, new QStandardItem(finfo .lastModified().toString(Qt::SystemLocaleShortDate))); + QStandardItem *filename_item = 0; + if(viewMode==VIEWMODE_LIST) + { + repoFileModel.setItem(item_id, COLUMN_PATH, new QStandardItem(path)); + filename_item = new QStandardItem(e.getFilePath()); + } + else // In Tree mode the path is implicit so the file name is enough + filename_item = new QStandardItem(e.getFilename()); + + Q_ASSERT(filename_item); + // Keep the path in the user data + filename_item->setData(e.getFilePath()); + repoFileModel.setItem(item_id, COLUMN_FILENAME, filename_item); + + repoFileModel.setItem(item_id, COLUMN_EXTENSION, new QStandardItem(finfo .completeSuffix())); + repoFileModel.setItem(item_id, COLUMN_MODIFIED, new QStandardItem(finfo .lastModified().toString(Qt::SystemLocaleShortDate))); + + ++item_id; } ui->tableView->resizeColumnsToContents(); ui->tableView->resizeRowsToContents(); - - setEnabled(true); - setStatus(""); - QApplication::restoreOverrideCursor(); } + //------------------------------------------------------------------------------ MainWindow::RepoStatus MainWindow::getRepoStatus() { @@ -653,7 +802,7 @@ bool MainWindow::runFossilRaw(const QStringList &args, QStringList *output, int process.start(fossil, args); if(!process.waitForStarted()) { - log("Could not start fossil executable '" + fossil + "''\n"); + log(tr("Could not start fossil executable '") + fossil + "''\n"); return false; } @@ -668,7 +817,7 @@ bool MainWindow::runFossilRaw(const QStringList &args, QStringList *output, int { if(fossilAbort) { - log("\n* Terminated *\n"); + log("\n* "+tr("Terminated")+" *\n"); #ifdef Q_WS_WIN fossilUI.kill(); // QT on windows cannot terminate console processes with QProcess::terminate #else @@ -848,8 +997,9 @@ void MainWindow::loadSettings() if(qsettings.contains("ViewUnchanged")) ui->actionViewUnchanged->setChecked(qsettings.value("ViewUnchanged").toBool()); if(qsettings.contains("ViewIgnored")) - ui->actionViewUnchanged->setChecked(qsettings.value("ViewIgnored").toBool()); - + ui->actionViewIgnored->setChecked(qsettings.value("ViewIgnored").toBool()); + if(qsettings.contains("ViewAsList")) + ui->actionViewAsList->setChecked(qsettings.value("ViewAsList").toBool()); } //------------------------------------------------------------------------------ @@ -881,10 +1031,80 @@ void MainWindow::saveSettings() qsettings.setValue("ViewModified", ui->actionViewModified->isChecked()); qsettings.setValue("ViewUnchanged", ui->actionViewUnchanged->isChecked()); qsettings.setValue("ViewIgnored", ui->actionViewIgnored->isChecked()); + qsettings.setValue("ViewAsList", ui->actionViewAsList->isChecked()); } //------------------------------------------------------------------------------ void MainWindow::getSelectionFilenames(QStringList &filenames, int includeMask, bool allIfEmpty) +{ + if(QApplication::focusWidget() == ui->tableView) + getFileViewSelection(filenames, includeMask, allIfEmpty); + else if(QApplication::focusWidget() == ui->treeView) + getDirViewSelection(filenames, includeMask, allIfEmpty); +} + +//------------------------------------------------------------------------------ +void MainWindow::getSelectionPaths(pathset_t &paths) +{ + // Determine the directories selected + QModelIndexList selection = ui->treeView->selectionModel()->selectedIndexes(); + for(QModelIndexList::iterator mi_it = selection.begin(); mi_it!=selection.end(); ++mi_it) + { + const QModelIndex &mi = *mi_it; + QVariant data = repoDirModel.data(mi, REPODIRMODEL_ROLE_PATH); + paths.insert(data.toString()); + } +} +//------------------------------------------------------------------------------ +void MainWindow::getDirViewSelection(QStringList &filenames, int includeMask, bool allIfEmpty) +{ + // Determine the directories selected + pathset_t paths; + + QModelIndexList selection = ui->treeView->selectionModel()->selectedIndexes(); + if(!(selection.empty() && allIfEmpty)) + { + getSelectionPaths(paths); + } + + // Select the actual files form the selected directories + for(filemap_t::iterator it=workspaceFiles.begin(); it!=workspaceFiles.end(); ++it) + { + const RepoFile &e = *(*it); + + // Skip unwanted file types + if(!(includeMask & e.getType())) + continue; + + bool include = true; + + // If we have a limited set of paths to filter, check them + if(!paths.empty()) + include = false; + + for(pathset_t::iterator p_it=paths.begin(); p_it!=paths.end(); ++p_it) + { + const QString &path = *p_it; + // An empty path is the root folder, so it includes all files + // If the file's path starts with this, we include id + if(path.isEmpty() || e.getPath().indexOf(path)==0) + { + include = true; + break; + } + } + + if(!include) + continue; + + filenames.append(e.getFilePath()); + } + + qDebug() << filenames; +} + +//------------------------------------------------------------------------------ +void MainWindow::getFileViewSelection(QStringList &filenames, int includeMask, bool allIfEmpty) { QModelIndexList selection = ui->tableView->selectionModel()->selectedIndexes(); if(selection.empty() && allIfEmpty) @@ -903,11 +1123,11 @@ void MainWindow::getSelectionFilenames(QStringList &filenames, int includeMask, if(mi.column()!=COLUMN_FILENAME) continue; - QVariant data = itemModel.data(mi); + QVariant data = repoFileModel.data(mi, Qt::UserRole+1); QString filename = data.toString(); filemap_t::iterator e_it = workspaceFiles.find(filename); Q_ASSERT(e_it!=workspaceFiles.end()); - const RepoFile &e = e_it.value(); + const RepoFile &e = *e_it.value(); // Skip unwanted files if(!(includeMask & e.getType())) @@ -1036,7 +1256,7 @@ void MainWindow::on_actionHistory_triggered() for(QStringList::iterator it = selection.begin(); it!=selection.end(); ++it) { - QDesktopServices::openUrl(QUrl(getFossilHttpAddress()+"/finfo?name="+*it)); + QDesktopServices::openUrl(QUrl(getFossilHttpAddress()+"/finfo?name="+*it)); } } @@ -1229,7 +1449,7 @@ void MainWindow::on_actionRename_triggered() } //------------------------------------------------------------------------------ -void MainWindow::on_actionNew_triggered() +void MainWindow::on_actionNewRepository_triggered() { QString filter(tr("Fossil Repositories (*.fossil)")); @@ -1281,6 +1501,32 @@ void MainWindow::on_actionNew_triggered() refresh(); } +//------------------------------------------------------------------------------ +void MainWindow::on_actionOpenRepository_triggered() +{ +#if 0 + QString filter(tr("Fossil Repositories (*.fossil)")); + + QString path = QFileDialog::getOpenFileName( + this, + tr("Fossil Repository"), + QDir::currentPath(), + filter, + &filter); + + if(path.isEmpty()) + return; + + if(!QFile::exists(path)) + { + QMessageBox::critical(this, tr("Error"), tr("Repository file does not exist."), QMessageBox::Ok ); + return; + } + +#endif + +} + //------------------------------------------------------------------------------ void MainWindow::on_actionClone_triggered() { @@ -1478,10 +1724,210 @@ void MainWindow::on_actionViewIgnored_triggered() refresh(); } +//------------------------------------------------------------------------------ +void MainWindow::on_actionViewAsList_triggered() +{ + viewMode = ui->actionViewAsList->isChecked() ? VIEWMODE_LIST : VIEWMODE_TREE; + ui->treeView->setVisible(viewMode == VIEWMODE_TREE); + updateFileView(); +} + //------------------------------------------------------------------------------ QString MainWindow::getFossilHttpAddress() { return "http://127.0.0.1:"+fossilUIPort; } +//------------------------------------------------------------------------------ +void MainWindow::on_treeView_selectionChanged(const QItemSelection &selected, const QItemSelection &/*deselected*/) +{ + if(selected.indexes().count()!=1) + return; + + QModelIndex index = selected.indexes().at(0); + viewDir = repoDirModel.data(index, REPODIRMODEL_ROLE_PATH).toString(); + setStatus(viewDir); + updateFileView(); +} + +//------------------------------------------------------------------------------ +void MainWindow::on_actionOpenFolder_triggered() +{ + const QItemSelection &selection = ui->treeView->selectionModel()->selection(); + + if(selection.indexes().count()!=1) + return; + + QModelIndex index = selection.indexes().at(0); + QString target = repoDirModel.data(index, REPODIRMODEL_ROLE_PATH).toString(); + target = getCurrentWorkspace() + PATH_SEP + target; + + QUrl url = QUrl::fromLocalFile(target); + QDesktopServices::openUrl(url); +} + +//------------------------------------------------------------------------------ +void MainWindow::on_actionRenameFolder_triggered() +{ + pathset_t paths; + getSelectionPaths(paths); + + if(paths.size()!=1) + return; + + QString old_path = *paths.begin(); + + // Root Node? + if(old_path.isEmpty()) + { + // Cannot change the project name via command line + // so unsupported + return; + } + + int dir_start = old_path.lastIndexOf(PATH_SEP); + if(dir_start==-1) + dir_start = 0; + else + ++dir_start; + + QString old_name = old_path.mid(dir_start); + + bool ok = false; + QString new_name = QInputDialog::getText(this, tr("Rename Folder"), tr("Enter new name"), QLineEdit::Normal, old_name, &ok, Qt::Sheet); + if(!ok || old_name==new_name) + return; + + const char* invalid_tokens[] = { + "/", "\\", "\\\\", ":", ">", "<", "*", "?", "|", "\"", ".." + }; + + for(size_t i=0; igetPath().indexOf(old_path)!=0) + continue; + + files_to_move.append(r); + QString new_dir = new_path + r->getPath().mid(old_path.length()); + new_paths.append(new_dir); + QString new_file_path = new_dir + PATH_SEP + r->getFilename(); + operations.append(r->getFilePath() + " -> " + new_file_path); + } + + if(files_to_move.empty()) + return; + + bool move_local = false; + if(!FileActionDialog::run(this, tr("Rename Folder"), tr("Renaming folder '")+old_path+tr("' to '")+new_path + +tr("'\nThe following files will be moved in the repository. Are you sure?"), + operations, + tr("Also move the workspace files"), &move_local)) { + return; + } + + // Rename files in fossil + Q_ASSERT(files_to_move.length() == new_paths.length()); + for(int i=0; igetFilename(); + + if(!runFossil(QStringList() << "mv" << QuotePath(r->getFilePath()) << QuotePath(new_file_path))) + { + log(tr("Move aborted due to errors\n")); + goto _exit; + } + } + + if(!move_local) + goto _exit; + + // First ensure that the target directories exist, and if not make them + for(int i=0; igetFilename(); + + if(QFile::exists(new_file_path)) + { + QMessageBox::critical(this, tr("Error"), tr("Target file '")+new_file_path+tr("' exists already")); + goto _exit; + } + + log(tr("Copying file '")+r->getFilePath()+tr("' to '")+new_file_path+"'\n"); + + if(!QFile::copy(r->getFilePath(), new_file_path)) + { + QMessageBox::critical(this, tr("Error"), tr("Cannot copy file '")+r->getFilePath()+tr("' to '")+new_file_path+"'"); + goto _exit; + } + } + + // Finally delete old files + for(int i=0; igetFilePath()+"'\n"); + + if(!QFile::exists(r->getFilePath())) + { + QMessageBox::critical(this, tr("Error"), tr("Source file '")+r->getFilePath()+tr("' does not exist")); + goto _exit; + } + + if(!QFile::remove(r->getFilePath())) + { + QMessageBox::critical(this, tr("Error"), tr("Cannot remove file '")+r->getFilePath()+"'"); + goto _exit; + } + } + + log(tr("Folder renamed completed. Don't forget to commit!\n")); + +_exit: + refresh(); +} + diff --git a/MainWindow.h b/MainWindow.h index d6acd88..a070cea 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "SettingsDialog.h" namespace Ui { @@ -32,11 +33,17 @@ struct RepoFile TYPE_ALL = TYPE_UNKNOWN|TYPE_REPO }; - void set(QFileInfo &info, EntryType type, const QString &repoPath) + RepoFile(QFileInfo &info, EntryType type, const QString &repoPath) { FileInfo = info; Type = type; - Filename = getRelativeFilename(repoPath); + FilePath = getRelativeFilename(repoPath); + + if(FilePath.indexOf('/')!=-1) + { + Path = FilePath; + Path = Path.left(Path.indexOf(FileInfo.fileName())-1); + } } bool isType(EntryType t) const @@ -64,9 +71,19 @@ struct RepoFile return Type == TYPE_UNCHANGED || Type == TYPE_EDITTED; } - const QString &getFilename() const + const QString &getFilePath() const { - return Filename; + return FilePath; + } + + QString getFilename() const + { + return FileInfo.fileName(); + } + + const QString &getPath() const + { + return Path; } QString getRelativeFilename(const QString &path) @@ -84,7 +101,8 @@ struct RepoFile private: QFileInfo FileInfo; EntryType Type; - QString Filename; + QString FilePath; + QString Path; }; @@ -98,6 +116,9 @@ public: ~MainWindow(); bool diffFile(QString repoFile); +private: + typedef QSet pathset_t; + private: bool refresh(); void scanWorkspace(); @@ -111,6 +132,9 @@ private: void setStatus(const QString &text); bool uiRunning() const { return fossilUI.state() == QProcess::Running; } void getSelectionFilenames(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false); + void getFileViewSelection(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false); + void getDirViewSelection(QStringList &filenames, int includeMask=RepoFile::TYPE_ALL, bool allIfEmpty=false); + void getSelectionPaths(pathset_t &paths); bool startUI(); void stopUI(); void enableActions(bool on); @@ -121,6 +145,8 @@ private: QString getFossilPath(); QString getFossilHttpAddress(); bool scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec); + void updateDirView(); + void updateFileView(); enum RepoStatus { @@ -131,9 +157,16 @@ private: RepoStatus getRepoStatus(); + enum ViewMode + { + VIEWMODE_LIST, + VIEWMODE_TREE + }; + private slots: // Manual slots - void onOpenRecent(); + void on_openRecent(); + void on_treeView_selectionChanged(const class QItemSelection &selected, const class QItemSelection &deselected); // Designer slots void on_actionRefresh_triggered(); @@ -152,7 +185,6 @@ private slots: void on_actionAdd_triggered(); void on_actionDelete_triggered(); void on_actionRevert_triggered(); - void on_actionNew_triggered(); void on_actionClone_triggered(); void on_actionOpenContaining_triggered(); void on_actionRename_triggered(); @@ -164,8 +196,12 @@ private slots: void on_actionViewUnchanged_triggered(); void on_actionViewModified_triggered(); void on_actionViewUnknown_triggered(); - void on_actionViewIgnored_triggered(); + void on_actionViewAsList_triggered(); + void on_actionOpenFolder_triggered(); + void on_actionRenameFolder_triggered(); + void on_actionOpenRepository_triggered(); + void on_actionNewRepository_triggered(); private: enum @@ -174,7 +210,8 @@ private: }; Ui::MainWindow *ui; - QStandardItemModel itemModel; + QStandardItemModel repoFileModel; + QStandardItemModel repoDirModel; QProcess fossilUI; QString fossilUIPort; class QAction *recentWorkspaceActs[MAX_RECENT]; @@ -187,11 +224,14 @@ private: QStringList workspaceHistory; QString currentWorkspace; QStringList commitMessages; + ViewMode viewMode; + QString viewDir; // The directory selected in the tree // Repo State - typedef QMap filemap_t; + typedef QList filelist_t; + typedef QMap filemap_t; filemap_t workspaceFiles; - + pathset_t pathSet; }; #endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui index b9c89e7..7fac80a 100644 --- a/MainWindow.ui +++ b/MainWindow.ui @@ -26,47 +26,84 @@ 4 - + Qt::Vertical - + - + 0 80 - - Qt::ActionsContextMenu + + Qt::Horizontal - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::SelectRows - - - false - - - true - - - false - - - 20 - - - false - - - 30 - + + + + 20 + 0 + + + + Qt::ActionsContextMenu + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectItems + + + true + + + false + + + + + + 80 + 0 + + + + Qt::ActionsContextMenu + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SelectRows + + + false + + + true + + + false + + + 20 + + + false + + + 30 + + @@ -93,11 +130,12 @@ Workspace - - + + + @@ -117,6 +155,8 @@ + + @@ -240,6 +280,9 @@ Open a fossil checkout folder + + Open a fossil checkout folder + Ctrl+O @@ -294,6 +337,9 @@ Quit + + Quit + Ctrl+Q @@ -379,7 +425,7 @@ Ctrl+Return - + :/icons/icons/Book-01.png:/icons/icons/Book-01.png @@ -387,11 +433,25 @@ New Repository... + + Make a new Fossil repository + + + Make a new Fossil repository + Ctrl+N - + + + Open Repository... + + + Open Repository + + + :/icons/icons/Address Book-01.png:/icons/icons/Address Book-01.png @@ -432,6 +492,9 @@ About... + + About Fuel + @@ -456,6 +519,9 @@ Application Preferences + + Set application preferences + @@ -464,6 +530,9 @@ Set remote synchronization settings + + Set remote synchronization settings + @@ -475,6 +544,9 @@ Modified + + Show modifed files + @@ -486,6 +558,9 @@ Unchanged + + Show unchanged files + @@ -497,6 +572,9 @@ Unknown + + Show unknown files + @@ -505,28 +583,46 @@ Ignored + + Show ignored files + + + + + true + + + View as List + + + View workspace as a list of files + + + + + + :/icons/icons/My Documents-01.png:/icons/icons/My Documents-01.png + + + Open Folder + + + + + + :/icons/icons/Folder Open-01.png:/icons/icons/Folder Open-01.png + + + Rename Folder + + + Rename Folder + - - - tableView - customContextMenuRequested(QPoint) - MainWindow - deleteLater() - - - 432 - 278 - - - 432 - 319 - - - - + diff --git a/manifest b/manifest index 86d51e6..81a86b5 100644 --- a/manifest +++ b/manifest @@ -1,14 +1,14 @@ -C Added\sInstaller\sfor\sWin32 -D 2011-10-15T07:03:12.801 +C Added\sWorkspace\sTree\sview\nAdded\sRename\sFolder\saction\nMinor\sGUI\scleanups\n +D 2011-10-17T14:58:09.384 F CommitDialog.cpp 8965e52d077c300cf1acb1b16fb2dcca5c7070f8 F CommitDialog.h a9596d99865cf312b419d01d51334ffc916f5508 F CommitDialog.ui 5067623f6af6f5a42c87df903278e383e945e154 F FileActionDialog.cpp fcaebf9986f789b3440d5390b3458ad5f86fe0c8 F FileActionDialog.h 15db1650b3a13d70bc338371e4c033c66e3b79ce F FileActionDialog.ui c63644428579741aeb5fa052e237ba799ced9ad7 -F MainWindow.cpp 25d3453d37711c8bb1b01d329892ef4d58d223d8 -F MainWindow.h 55f90fe948661a6b7382470f3cdc80dc592e76c4 -F MainWindow.ui d22becfdb32d4b31ed9a6e6d68dd144870d9a957 +F MainWindow.cpp 1ca79369fdb5cbe87dcc38d3f2aebd8a007d64f0 +F MainWindow.h 6fdcb10c8a6da760c83dd05f1d07b69132ba3d4c +F MainWindow.ui 82a3d869e043314a0c9a4c0821de2469d565b782 F RepoDialog.cpp 8f20e1511526973555c774350ec413dcecf51c9e F RepoDialog.h a958c5f98f1e6882bf41dbdd2e4df3cb89700802 F RepoDialog.ui be7b18199c04a3003f3c7534a616cd7441b7bb0c @@ -175,7 +175,7 @@ F installer/fuel.iss 13b6a938bcdf273cbd3649d2549887baa1577214 F installer/license.txt 4cc77b90af91e615a64ae04893fdffa7939db84c F main.cpp f67a9b5c9ca0b634b19ef08e7136032372d37f93 F resources.qrc e98383ed205f4e37100c60057e0129c3b86dea53 -P 4e53a5ec6844f3a210ba2fbfc2185840d697b0bb -R 62600d2edbddf7a761b84fb85d9cbf33 +P a29a3fd0f7f4c26591e1ca168ad63f1f7530edbe +R 4d693a61ebb632ca6600c2150bca11d5 U kostas -Z eb7bffb75807cc22987af216d0cd4213 +Z a4c724920b064e72a32c87376626c0ab diff --git a/manifest.uuid b/manifest.uuid index e8de19e..984dbc7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a29a3fd0f7f4c26591e1ca168ad63f1f7530edbe \ No newline at end of file +5fc925ac68148c69db66e88e23be196807ffec22 \ No newline at end of file