Add support to select multiple-files in file tree view (SpartanJ/ecode#869).

This commit is contained in:
Martín Lucas Golini
2026-04-16 19:13:43 -03:00
parent ac811a38d9
commit 20f44697a0
8 changed files with 313 additions and 82 deletions

View File

@@ -539,7 +539,7 @@ UITableRow* UIAbstractTableView::createRow() {
}
} );
rowWidget->on( Event::MouseClick, [this]( const Event* event ) {
if ( !( event->asMouseEvent()->getFlags() & ( EE_BUTTON_LMASK | EE_BUTTON_RMASK ) ) ||
if ( !( event->asMouseEvent()->getFlags() & ( EE_BUTTON_LMASK ) ) ||
!isRowSelection() )
return;

View File

@@ -1378,7 +1378,7 @@ UITextView* App::getDocInfo() const {
return mDocInfo;
}
UITreeView* App::getProjectTreeView() const {
UITreeViewFS* App::getProjectTreeView() const {
return mProjectTreeView;
}
@@ -3778,7 +3778,7 @@ void App::initProjectViewEmptyCont() {
void App::initProjectTreeViewUI() {
initProjectViewEmptyCont();
mProjectTreeView = mUISceneNode->find<UITreeView>( "project_view" );
mProjectTreeView = mUISceneNode->find<UITreeViewFS>( "project_view" );
mProjectTreeView->setColumnsHidden(
{ FileSystemModel::Icon, FileSystemModel::Size, FileSystemModel::Group,
FileSystemModel::Inode, FileSystemModel::Owner, FileSystemModel::SymlinkTarget,
@@ -3809,8 +3809,9 @@ void App::initProjectTreeViewUI() {
} else {
tab->getTabWidget()->setTabSelected( tab );
}
} else { // ModelEventType::OpenMenu
mSettings->createProjectTreeMenu( FileInfo( path ) );
} else if ( !mProjectTreeView->getSelection().isEmpty() ) {
// ModelEventType::OpenMenu
mSettings->createProjectTreeMenu( mProjectTreeView->getSelectionsFileInfo() );
}
}
}
@@ -3824,7 +3825,10 @@ void App::initProjectTreeViewUI() {
if ( !mFileSystemModel )
return;
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
if ( keyEvent->getKeyCode() == KEY_F2 || keyEvent->getKeyCode() == KEY_DELETE ) {
if ( keyEvent->getKeyCode() == KEY_F2 ||
( ( keyEvent->getKeyCode() == KEY_DELETE ||
keyEvent->getKeyCode() == KEY_BACKSPACE ) &&
mProjectTreeView->getSelection().size() == 1 ) ) {
ModelIndex modelIndex = mProjectTreeView->getSelection().first();
if ( !modelIndex.isValid() )
return;
@@ -3833,10 +3837,15 @@ void App::initProjectTreeViewUI() {
FileInfo fileInfo( vPath.toString() );
if ( keyEvent->getKeyCode() == KEY_F2 ) {
renameFile( fileInfo );
} else {
} else if ( keyEvent->getKeyCode() == KEY_DELETE ||
keyEvent->getKeyCode() == KEY_BACKSPACE ) {
mSettings->deleteFileDialog( fileInfo );
}
}
} else if ( ( keyEvent->getKeyCode() == KEY_DELETE ||
keyEvent->getKeyCode() == KEY_BACKSPACE ) &&
!mProjectTreeView->getSelection().isEmpty() ) {
mProjectTreeView->asType<UITreeViewFS>()->deleteSelectedFiles();
}
} );
mProjectTreeView->setDisableCellClipping( true );

View File

@@ -32,6 +32,7 @@ class AutoCompletePlugin;
class LinterPlugin;
class FormatterPlugin;
class SettingsMenu;
class UITreeViewFS;
class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
public:
@@ -510,7 +511,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
UITextView* getDocInfo() const;
UITreeView* getProjectTreeView() const;
UITreeViewFS* getProjectTreeView() const;
void loadCurrentDirectory();
@@ -693,7 +694,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
Float mDisplayDPI{ 96 };
std::shared_ptr<ThreadPool> mThreadPool;
std::shared_ptr<ProjectDirectoryTree> mDirTree;
UITreeView* mProjectTreeView{ nullptr };
UITreeViewFS* mProjectTreeView{ nullptr };
UILinearLayout* mProjectViewEmptyCont{ nullptr };
std::shared_ptr<FileSystemModel> mFileSystemModel;
std::shared_ptr<GitIgnoreMatcher> mFileSystemMatcher;

View File

@@ -56,6 +56,7 @@ class NotificationCenter;
class ProjectDirectoryTree;
struct TerminalConfig;
class UIMainLayout;
class UITreeViewFS;
class PluginContextProvider {
public:
@@ -63,6 +64,8 @@ class PluginContextProvider {
virtual UISplitter* getMainSplitter() const = 0;
virtual UITreeViewFS* getProjectTreeView() const = 0;
virtual void hideGlobalSearchBar() = 0;
virtual void hideSearchBar() = 0;

View File

@@ -1,4 +1,5 @@
#include "settingsmenu.hpp"
#include "uitreeviewfs.hpp"
#include <filesystem>
namespace fs = std::filesystem;
@@ -2673,12 +2674,20 @@ static void fsRemoveAll( const std::string& fpath ) {
#endif
}
void SettingsMenu::createProjectTreeMenu( const FileInfo& file ) {
void SettingsMenu::createProjectTreeMenu( const std::vector<FileInfo>& files ) {
if ( mProjectTreeMenu && mProjectTreeMenu->isVisible() )
mProjectTreeMenu->close();
mProjectTreeMenu = UIPopUpMenu::New();
if ( file.isDirectory() ) {
bool allFiles = true;
for ( const auto& file : files ) {
if ( file.isDirectory() ) {
allFiles = false;
break;
}
}
if ( files.size() == 1 && files[0].isDirectory() ) {
mProjectTreeMenu->add( i18n( "new_file_ellipsis", "New File..." ), findIcon( "file-add" ) )
->setId( "new_file" );
mProjectTreeMenu
@@ -2702,7 +2711,7 @@ void SettingsMenu::createProjectTreeMenu( const FileInfo& file ) {
->add( i18n( "find_in_folder_ellipsis", "Find in Folder..." ),
findIcon( "file-search" ) )
->setId( "find_in_folder" );
} else {
} else if ( files.size() == 1 ) {
mProjectTreeMenu->add( i18n( "open_file", "Open File" ), findIcon( "document-open" ) )
->setId( "open_file" );
mProjectTreeMenu
@@ -2713,29 +2722,44 @@ void SettingsMenu::createProjectTreeMenu( const FileInfo& file ) {
->add( i18n( "new_file_in_directory_ellipsis", "New File in directory..." ),
findIcon( "file-add" ) )
->setId( "new_file_in_place" );
mProjectTreeMenu
->add( i18n( "new_folder_in_directory_ellipsis", "New Folder in directory..." ),
findIcon( "folder-add" ) )
->setId( "new_folder_in_place" );
mProjectTreeMenu
->add( i18n( "duplicate_file_ellipsis", "Duplicate File..." ), findIcon( "file-copy" ) )
->setId( "duplicate_file" );
} else if ( allFiles && files.size() > 1 ) {
mProjectTreeMenu->add( i18n( "open_files", "Open Files" ), findIcon( "document-open" ) )
->setId( "open_files" );
}
mProjectTreeMenu->add( i18n( "rename", "Rename" ), findIcon( "edit" ), "F2" )
->setId( "rename" );
if ( files.size() == 1 ) {
mProjectTreeMenu->add( i18n( "rename", "Rename" ), findIcon( "edit" ), "F2" )
->setId( "rename" );
}
mProjectTreeMenu
->add( i18n( "remove_ellipsis", "Remove..." ), findIcon( "delete-bin" ), "Delete" )
->setId( "remove" );
if ( file.isDirectory() || file.isExecutable() ) {
mProjectTreeMenu->addSeparator();
if ( files.size() == 1 ) {
auto& file = files[0];
if ( file.isDirectory() ) {
mProjectTreeMenu
->add( i18n( "execute_dir_in_terminal", "Open directory in terminal" ),
findIcon( "filetype-bash" ) )
->setId( "execute_dir_in_terminal" );
} else if ( file.isExecutable() ) {
mProjectTreeMenu
->add( i18n( "execute_in_terminal", "Execute in terminal" ),
findIcon( "filetype-bash" ) )
->setId( "execute_in_terminal" );
if ( file.isDirectory() || file.isExecutable() ) {
mProjectTreeMenu->addSeparator();
if ( file.isDirectory() ) {
mProjectTreeMenu
->add( i18n( "execute_dir_in_terminal", "Open directory in terminal" ),
findIcon( "filetype-bash" ) )
->setId( "execute_dir_in_terminal" );
} else if ( file.isExecutable() ) {
mProjectTreeMenu
->add( i18n( "execute_in_terminal", "Execute in terminal" ),
findIcon( "filetype-bash" ) )
->setId( "execute_in_terminal" );
}
}
}
@@ -2762,20 +2786,24 @@ void SettingsMenu::createProjectTreeMenu( const FileInfo& file ) {
->setId( "configure-ignore-files" );
}
mProjectTreeMenu->on( Event::OnItemClicked, [this, file]( const Event* event ) {
if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) )
mProjectTreeMenu->on( Event::OnItemClicked, [this, files]( const Event* event ) {
if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) || files.empty() )
return;
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
std::string id( item->getId() );
auto file = files[0];
if ( "new_file" == id || "new_file_in_place" == id ) {
mApp->newFile( file );
} else if ( "new_folder" == id ) {
} else if ( "new_folder" == id || "new_folder_in_place" == id ) {
mApp->newFolder( file );
} else if ( "open_file" == id ) {
mApp->openFileFromPath( file.getFilepath() );
} else if ( "open_files" == id ) {
for ( const auto& file : files )
mApp->openFileFromPath( file.getFilepath() );
} else if ( "remove" == id ) {
deleteFileDialog( file );
mApp->getProjectTreeView()->deleteSelectedFiles();
} else if ( "duplicate_file" == id ) {
UIMessageBox* msgBox = mApp->newInputMsgBox(
String::format( "%s \"%s\"",

View File

@@ -64,7 +64,7 @@ class SettingsMenu {
void createProjectTreeMenu();
void createProjectTreeMenu( const FileInfo& file );
void createProjectTreeMenu( const std::vector<FileInfo>& files );
void updateColorSchemeMenu();

View File

@@ -1,5 +1,6 @@
#include "uitreeviewfs.hpp"
#include "customwidgets.hpp"
#include "ecode.hpp"
#include "notificationcenter.hpp"
#include <eepp/system/filesystem.hpp>
@@ -7,6 +8,8 @@
#include <eepp/ui/uimessagebox.hpp>
#include <eepp/window/cursormanager.hpp>
#include <filesystem>
namespace ecode {
static const std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings() {
@@ -14,6 +17,10 @@ static const std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings(
{ { KEY_C, KeyMod::getDefaultModifier() }, "copy" },
{ { KEY_X, KeyMod::getDefaultModifier() }, "cut" },
{ { KEY_V, KeyMod::getDefaultModifier() }, "paste" },
{ { KEY_RETURN }, "open-selected-files" },
{ { KEY_KP_ENTER }, "open-selected-files" },
{ { KEY_DELETE }, "delete-selected-files" },
{ { KEY_BACKSPACE }, "delete-selected-files" },
};
}
@@ -81,7 +88,23 @@ class UITreeViewCellFS : public UITreeViewCell {
sDragTV->setVisible( false )->setEnabled( false );
sDragTV->setText( getTextView()->getText() );
setClass( "dragged" );
getTreeView()->setSourceDrag( getModel()->node( getCurIndex() ).fullPath() );
auto tvfs = getTreeView();
const auto& selection = tvfs->getSelection();
std::string dragPaths;
auto fsm = static_cast<const FileSystemModel*>( getModel() );
std::string rootPath = fsm->getRootPath();
tvfs->getSourceDragMultiplePaths().clear();
for ( int i = 0; i < selection.size(); ++i ) {
auto path = tvfs->getSelectionPathAtIndex( i );
std::string relPath( path );
FileSystem::filePathRemoveBasePath( rootPath, relPath );
dragPaths += relPath;
if ( i < selection.size() - 1 )
dragPaths += "\n";
tvfs->getSourceDragMultiplePaths().emplace_back( path );
}
sDragTV->setText( dragPaths );
return UITreeViewCell::onDragStart( position, flags );
}
@@ -111,8 +134,7 @@ class UITreeViewCellFS : public UITreeViewCell {
getTreeView()->execute( cmd );
return 1;
}
return 0;
return UITreeViewCell::onKeyDown( event );
}
const std::string& getCurrentPath() const {
@@ -129,30 +151,68 @@ UITreeViewFS::UITreeViewFS() : UITreeView(), mKeyBindings( getInput() ) {
mCommands["copy"] = [this] {
if ( getSelection().isEmpty() )
return;
mSrcCopy = getSelectionPath();
auto& selection = getSelection();
mSrcCopyMultiplePaths.clear();
mWasCut = false;
NotificationCenter::instance()->addNotification(
String::format( i18n( "copied_file", "Copied '%s'" ).toUtf8(),
FileSystem::fileNameFromPath( mSrcCopy ) ) );
for ( int i = 0; i < selection.size(); ++i )
mSrcCopyMultiplePaths.emplace_back( getSelectionPathAtIndex( i ) );
if ( selection.size() > 1 ) {
NotificationCenter::instance()->addNotification( String::format(
i18n( "copied_files", "Copied %d items" ).toUtf8(), selection.size() ) );
} else if ( selection.size() == 1 ) {
NotificationCenter::instance()->addNotification(
String::format( i18n( "copied_file", "Copied '%s'" ).toUtf8(),
FileSystem::fileNameFromPath( getSelectionPathAtIndex( 0 ) ) ) );
}
};
mCommands["cut"] = [this] {
if ( getSelection().isEmpty() )
return;
mSrcCopy = getSelectionPath();
auto& selection = getSelection();
mSrcCopyMultiplePaths.clear();
mWasCut = true;
NotificationCenter::instance()->addNotification( String::format(
i18n( "cut_file", "Cut '%s'" ).toUtf8(), FileSystem::fileNameFromPath( mSrcCopy ) ) );
for ( int i = 0; i < selection.size(); ++i )
mSrcCopyMultiplePaths.emplace_back( getSelectionPathAtIndex( i ) );
if ( selection.size() > 1 ) {
NotificationCenter::instance()->addNotification(
String::format( i18n( "cut_files", "Cut %d items" ).toUtf8(), selection.size() ) );
} else {
NotificationCenter::instance()->addNotification(
String::format( i18n( "cut_file", "Cut '%s'" ).toUtf8(),
FileSystem::fileNameFromPath( getSelectionPathAtIndex( 0 ) ) ) );
}
};
mCommands["paste"] = [this] {
if ( mWasCut )
moveFile( mSrcCopy, getSelectionPath() );
else
copyFile( mSrcCopy, getSelectionPath() );
auto& selection = getSelection();
std::string targetPath = selection.isEmpty() ? "" : getSelectionPath();
if ( targetPath.empty() )
return;
FileInfo targetInfo( targetPath );
if ( !targetInfo.isDirectory() ) {
targetInfo = FileInfo( targetInfo.getDirectoryPath() );
}
std::string dstPath( targetInfo.getFilepath() );
if ( mWasCut ) {
if ( !mSrcCopyMultiplePaths.empty() )
moveFiles( mSrcCopyMultiplePaths, dstPath );
mSrcCopyMultiplePaths.clear();
} else {
if ( !mSrcCopyMultiplePaths.empty() )
copyFiles( mSrcCopyMultiplePaths, dstPath );
mSrcCopyMultiplePaths.clear();
}
};
mCommands["open-selected-files"] = [this] { openSelectedFiles(); };
mCommands["delete-selected-files"] = [this] { deleteSelectedFiles(); };
mKeyBindings.addKeybinds( getDefaultKeybindings() );
setSelectionKind( SelectionKind::Multiple );
}
UIWidget* UITreeViewFS::createCell( UIWidget* rowWidget, const ModelIndex& index ) {
@@ -168,53 +228,84 @@ Uint32 UITreeViewFS::onMessage( const NodeMessage* msg ) {
if ( dropMsg->getSender()->isType( expectedType ) &&
dropMsg->getDroppedNode()->isType( expectedType ) ) {
auto dst = dropMsg->getSender()->asType<UITreeViewCellFS>()->getCurrentPath();
moveFile( mSrcDrag, dst );
moveFiles( mSrcDragMultiplePaths, dst );
return 1;
}
}
return UITreeView::onMessage( msg );
}
void UITreeViewFS::moveFile( const std::string& src, const std::string& dst ) {
FileInfo srcInfo( src );
if ( !srcInfo.exists() )
void UITreeViewFS::moveFiles( const std::vector<std::string>& paths, const std::string& dstDir ) {
if ( paths.empty() )
return;
FileInfo dstInfo( dst );
if ( !dstInfo.exists() )
return;
if ( srcInfo.getFilepath() != srcInfo.getRealPath() )
srcInfo = FileInfo( srcInfo.getRealPath() );
if ( !dstInfo.isDirectory() ) {
dstInfo = FileInfo( dstInfo.getDirectoryPath() );
if ( dstInfo.getFilepath() != dstInfo.getRealPath() )
dstInfo = FileInfo( dstInfo.getRealPath() );
}
if ( srcInfo.getDirectoryPath() == dstInfo.getFilepath() )
return;
std::string srcPath( srcInfo.getFilepath() );
std::string dstPath( dstInfo.getFilepath() );
FileSystem::dirAddSlashAtEnd( dstPath );
dstPath += srcInfo.getFileName();
std::string confirmMsg;
std::vector<std::pair<std::string, std::string>> validMoves;
auto fsm = static_cast<const FileSystemModel*>( getModel() );
std::string partialSrc( srcPath );
FileSystem::filePathRemoveBasePath( fsm->getRootPath(), partialSrc );
std::string partialDst( dstPath );
FileSystem::filePathRemoveBasePath( fsm->getRootPath(), partialDst );
auto confirmMsg( String::format(
i18n( "confirm_move_file_or_dir", "Are you sure you want to move:\n%s\ninto:\n%s" )
.toUtf8(),
partialSrc, partialDst ) );
for ( const auto& srcPath : paths ) {
FileInfo srcInfo( srcPath );
if ( !srcInfo.exists() )
continue;
FileInfo dstInfo( dstDir );
if ( !dstInfo.isDirectory() ) {
dstInfo = FileInfo( dstInfo.getDirectoryPath() );
}
if ( srcInfo.getDirectoryPath() == dstInfo.getFilepath() )
continue;
validMoves.emplace_back( srcPath, dstInfo.getFilepath() );
}
if ( validMoves.empty() )
return;
std::string partialDst( dstDir );
FileSystem::filePathRemoveBasePath( fsm->getRootPath(), partialDst );
if ( !FileSystem::isDirectory( partialDst ) )
partialDst = FileSystem::fileRemoveFileName( partialDst );
FileSystem::dirAddSlashAtEnd( partialDst );
if ( partialDst.empty() ) // If it's empty it's the root path
partialDst = fsm->getRootPath();
if ( validMoves.size() == 1 ) {
std::string partialSrc( validMoves[0].first );
FileSystem::filePathRemoveBasePath( fsm->getRootPath(), partialSrc );
confirmMsg = ( String::format(
i18n( "confirm_move_file_or_dir", "Are you sure you want to move:\n%s\ninto:\n%s" )
.toUtf8(),
partialSrc, partialDst ) );
} else {
std::string listFiles;
static constexpr auto MAX_LISTED_FILES = 10;
for ( size_t i = 0; i < validMoves.size() && i < MAX_LISTED_FILES; ++i ) {
std::string partialSrc( validMoves[i].first );
FileSystem::filePathRemoveBasePath( fsm->getRootPath(), partialSrc );
listFiles += partialSrc + "\n";
}
if ( validMoves.size() > MAX_LISTED_FILES ) {
listFiles +=
"... and " + String::format( "%d", validMoves.size() - MAX_LISTED_FILES ) + " more";
}
confirmMsg = String::format(
i18n( "confirm_move_multiple", "Are you sure you want to move:\n%s\ninto: %s" )
.toUtf8(),
listFiles, partialDst );
}
UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, confirmMsg );
msgBox->setTitle( "ecode" );
msgBox->center();
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->showWhenReady();
msgBox->on( Event::OnConfirm, [srcPath = std::move( srcPath ), dstPath = std::move( dstPath )](
auto ) { FileSystem::fileMove( srcPath, dstPath ); } );
msgBox->on( Event::OnConfirm, [validMoves]( const Event* ) {
for ( const auto& move : validMoves ) {
std::string dstPath( move.second );
FileSystem::dirAddSlashAtEnd( dstPath );
dstPath += FileInfo( move.first ).getFileName();
FileSystem::fileMove( move.first, dstPath );
}
} );
}
void UITreeViewFS::copyFile( const std::string& src, const std::string& dst ) {
@@ -241,12 +332,97 @@ void UITreeViewFS::copyFile( const std::string& src, const std::string& dst ) {
FileSystem::fileCopy( srcPath, dstPath );
}
void UITreeViewFS::copyFiles( const std::vector<std::string>& paths, const std::string& dstDir ) {
for ( const auto& srcPath : paths ) {
copyFile( srcPath, dstDir );
}
}
std::string UITreeViewFS::getSelectionPath() const {
return static_cast<const FileSystemModel*>( getModel() )
->node( getSelection().first() )
.fullPath();
}
std::string UITreeViewFS::getSelectionPathAtIndex( int index ) const {
auto& selection = getSelection();
if ( index < 0 || static_cast<size_t>( index ) >= static_cast<size_t>( selection.size() ) )
return "";
auto indexVec = selection.indexes();
return static_cast<const FileSystemModel*>( getModel() )
->node( indexVec[static_cast<size_t>( index )] )
.fullPath();
}
std::vector<FileInfo> UITreeViewFS::getSelectionsFileInfo() const {
std::vector<FileInfo> ret;
auto indexVec = getSelection().indexes();
auto model = static_cast<const FileSystemModel*>( getModel() );
for ( const auto& index : indexVec ) {
ret.emplace_back( FileInfo( model->node( index ).fullPath() ) );
}
return ret;
}
void UITreeViewFS::deleteSelectedFiles() {
auto& selection = getSelection();
if ( selection.isEmpty() )
return;
std::vector<std::string> paths;
for ( int i = 0; i < selection.size(); ++i )
paths.emplace_back( getSelectionPathAtIndex( i ) );
deleteItems( paths );
}
void UITreeViewFS::deleteItems( const std::vector<std::string>& paths ) {
if ( paths.empty() )
return;
auto fsm = static_cast<const FileSystemModel*>( getModel() );
std::string confirmMsg;
if ( paths.size() == 1 ) {
std::string partialPath( paths[0] );
FileSystem::filePathRemoveBasePath( fsm->getRootPath(), partialPath );
confirmMsg = String::format(
i18n( "confirm_delete_file_or_dir", "Are you sure you want to delete:\n%s" ).toUtf8(),
partialPath );
} else {
confirmMsg = String::format(
i18n( "confirm_delete_multiple", "Are you sure you want to delete %d items?" ).toUtf8(),
paths.size() );
}
UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, confirmMsg );
msgBox->setTitle( "ecode" );
msgBox->center();
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->showWhenReady();
msgBox->on( Event::OnConfirm, [paths]( const Event* ) {
for ( const auto& path : paths ) {
FileInfo info( path );
try {
if ( info.isDirectory() ) {
std::filesystem::remove_all( std::filesystem::path( path ) );
} else {
FileSystem::fileRemove( path );
}
} catch ( const std::filesystem::filesystem_error& ) {
}
}
} );
}
void UITreeViewFS::openSelectedFiles() {
auto selection = getSelection().indexes();
std::vector<FileInfo> paths;
paths.reserve( selection.size() );
for ( size_t i = 0; i < selection.size(); ++i )
paths.emplace_back( FileInfo( getSelectionPathAtIndex( i ) ) );
for ( const auto& path : paths ) {
if ( !path.isDirectory() )
App::instance()->openFileFromPath( path.getFilepath() );
}
}
void UITreeViewFS::execute( const std::string& cmd ) {
auto cmdIt = mCommands.find( cmd );
if ( cmdIt != mCommands.end() )

View File

@@ -1,7 +1,9 @@
#pragma once
#include <eepp/ui/keyboardshortcut.hpp>
#include <eepp/ui/models/modelindex.hpp>
#include <eepp/ui/uitreeview.hpp>
#include <vector>
using namespace EE::UI;
@@ -19,24 +21,36 @@ class UITreeViewFS : public UITreeView {
KeyBindings& getKeyBindings() { return mKeyBindings; }
void setSourceDrag( const std::string& src ) { mSrcDrag = src; }
std::vector<std::string>& getSourceDragMultiplePaths() { return mSrcDragMultiplePaths; }
void setSourceCopy( const std::string& src ) { mSrcCopy = src; }
std::string getSelectionPathAtIndex( int index ) const;
void deleteSelectedFiles();
void openSelectedFiles();
void execute( const std::string& cmd );
std::vector<FileInfo> getSelectionsFileInfo() const;
protected:
KeyBindings mKeyBindings;
UnorderedMap<std::string, std::function<void()>> mCommands;
std::string mSrcDrag;
std::string mSrcCopy;
std::vector<std::string> mSrcDragMultiplePaths;
std::vector<std::string> mSrcCopyMultiplePaths;
bool mWasCut{ false };
friend class UITreeViewCellFS;
std::string getSelectionPath() const;
void moveFile( const std::string& src, const std::string& dst );
void moveFiles( const std::vector<std::string>& paths, const std::string& dstDir );
void copyFile( const std::string& src, const std::string& dst );
void copyFiles( const std::vector<std::string>& paths, const std::string& dstDir );
void deleteItems( const std::vector<std::string>& paths );
};
} // namespace ecode