mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Add support to select multiple-files in file tree view (SpartanJ/ecode#869).
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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\"",
|
||||
|
||||
@@ -64,7 +64,7 @@ class SettingsMenu {
|
||||
|
||||
void createProjectTreeMenu();
|
||||
|
||||
void createProjectTreeMenu( const FileInfo& file );
|
||||
void createProjectTreeMenu( const std::vector<FileInfo>& files );
|
||||
|
||||
void updateColorSchemeMenu();
|
||||
|
||||
|
||||
@@ -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() )
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user