Advertisement
Guest User

Untitled

a guest
Mar 28th, 2020
277
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 28.02 KB | None | 0 0
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26.  
  27. #include "DirectoryView.h"
  28. #include "FileUtils.h"
  29. #include "PropertiesDialog.h"
  30. #include <AK/FileSystemPath.h>
  31. #include <AK/StringBuilder.h>
  32. #include <AK/URL.h>
  33. #include <LibCore/ConfigFile.h>
  34. #include <LibCore/MimeData.h>
  35. #include <LibCore/UserInfo.h>
  36. #include <LibGUI/AboutDialog.h>
  37. #include <LibGUI/Action.h>
  38. #include <LibGUI/ActionGroup.h>
  39. #include <LibGUI/Application.h>
  40. #include <LibGUI/BoxLayout.h>
  41. #include <LibGUI/Clipboard.h>
  42. #include <LibGUI/FileSystemModel.h>
  43. #include <LibGUI/InputBox.h>
  44. #include <LibGUI/Label.h>
  45. #include <LibGUI/Menu.h>
  46. #include <LibGUI/MenuBar.h>
  47. #include <LibGUI/MessageBox.h>
  48. #include <LibGUI/ProgressBar.h>
  49. #include <LibGUI/Splitter.h>
  50. #include <LibGUI/StatusBar.h>
  51. #include <LibGUI/TextEditor.h>
  52. #include <LibGUI/ToolBar.h>
  53. #include <LibGUI/TreeView.h>
  54. #include <LibGUI/Widget.h>
  55. #include <LibGUI/Window.h>
  56. #include <signal.h>
  57. #include <stdio.h>
  58. #include <string.h>
  59. #include <unistd.h>
  60.  
  61. int main(int argc, char** argv)
  62. {
  63. if (pledge("stdio thread shared_buffer accept unix cpath rpath wpath fattr proc exec", nullptr) < 0) {
  64. perror("pledge");
  65. return 1;
  66. }
  67.  
  68. struct sigaction act;
  69. memset(&act, 0, sizeof(act));
  70. act.sa_flags = SA_NOCLDWAIT;
  71. act.sa_handler = SIG_IGN;
  72. int rc = sigaction(SIGCHLD, &act, nullptr);
  73. if (rc < 0) {
  74. perror("sigaction");
  75. return 1;
  76. }
  77.  
  78. RefPtr<Core::ConfigFile> config = Core::ConfigFile::get_for_app("FileManager");
  79.  
  80. GUI::Application app(argc, argv);
  81.  
  82. if (pledge("stdio thread shared_buffer accept cpath rpath wpath fattr proc exec", nullptr) < 0) {
  83. perror("pledge");
  84. return 1;
  85. }
  86.  
  87. auto window = GUI::Window::construct();
  88. window->set_title("File Manager");
  89.  
  90. auto left = config->read_num_entry("Window", "Left", 150);
  91. auto top = config->read_num_entry("Window", "Top", 75);
  92. auto width = config->read_num_entry("Window", "Width", 640);
  93. auto heigth = config->read_num_entry("Window", "Heigth", 480);
  94. window->set_rect({ left, top, width, heigth });
  95.  
  96. auto& widget = window->set_main_widget<GUI::Widget>();
  97. widget.set_layout<GUI::VerticalBoxLayout>();
  98. widget.layout()->set_spacing(0);
  99.  
  100. auto& main_toolbar = widget.add<GUI::ToolBar>();
  101. auto& location_toolbar = widget.add<GUI::ToolBar>();
  102. location_toolbar.layout()->set_margins({ 6, 3, 6, 3 });
  103. location_toolbar.set_preferred_size(0, 25);
  104.  
  105. auto& location_label = location_toolbar.add<GUI::Label>("Location: ");
  106. location_label.size_to_fit();
  107.  
  108. auto& location_textbox = location_toolbar.add<GUI::TextBox>();
  109.  
  110. auto& splitter = widget.add<GUI::HorizontalSplitter>();
  111. auto& tree_view = splitter.add<GUI::TreeView>();
  112. auto directories_model = GUI::FileSystemModel::create("/", GUI::FileSystemModel::Mode::DirectoriesOnly);
  113. tree_view.set_model(directories_model);
  114. tree_view.set_column_hidden(GUI::FileSystemModel::Column::Icon, true);
  115. tree_view.set_column_hidden(GUI::FileSystemModel::Column::Size, true);
  116. tree_view.set_column_hidden(GUI::FileSystemModel::Column::Owner, true);
  117. tree_view.set_column_hidden(GUI::FileSystemModel::Column::Group, true);
  118. tree_view.set_column_hidden(GUI::FileSystemModel::Column::Permissions, true);
  119. tree_view.set_column_hidden(GUI::FileSystemModel::Column::ModificationTime, true);
  120. tree_view.set_column_hidden(GUI::FileSystemModel::Column::Inode, true);
  121. tree_view.set_column_hidden(GUI::FileSystemModel::Column::SymlinkTarget, true);
  122. tree_view.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
  123. tree_view.set_preferred_size(150, 0);
  124. auto& directory_view = splitter.add<DirectoryView>();
  125.  
  126. auto& statusbar = widget.add<GUI::StatusBar>();
  127.  
  128. auto& progressbar = statusbar.add<GUI::ProgressBar>();
  129. progressbar.set_caption("Generating thumbnails: ");
  130. progressbar.set_format(GUI::ProgressBar::Format::ValueSlashMax);
  131. progressbar.set_visible(false);
  132. progressbar.set_frame_shape(Gfx::FrameShape::Panel);
  133. progressbar.set_frame_shadow(Gfx::FrameShadow::Sunken);
  134. progressbar.set_frame_thickness(1);
  135.  
  136. location_textbox.on_return_pressed = [&] {
  137. directory_view.open(location_textbox.text());
  138. };
  139.  
  140. auto refresh_tree_view = [&] {
  141. directories_model->update();
  142.  
  143. auto current_path = directory_view.path();
  144.  
  145. struct stat st;
  146. // If the directory no longer exists, we find a parent that does.
  147. while (stat(current_path.characters(), &st) != 0) {
  148. directory_view.open_parent_directory();
  149. current_path = directory_view.path();
  150. if (current_path == directories_model->root_path()) {
  151. break;
  152. }
  153. }
  154.  
  155. // Reselect the existing folder in the tree.
  156. auto new_index = directories_model->index(current_path, GUI::FileSystemModel::Column::Name);
  157. tree_view.selection().set(new_index);
  158. tree_view.scroll_into_view(new_index, Orientation::Vertical);
  159. tree_view.update();
  160.  
  161. directory_view.refresh();
  162. };
  163.  
  164. auto directory_context_menu = GUI::Menu::construct("Directory View Directory");
  165. auto file_context_menu = GUI::Menu::construct("Directory View File");
  166. auto directory_view_context_menu = GUI::Menu::construct("Directory View");
  167. auto tree_view_directory_context_menu = GUI::Menu::construct("Tree View Directory");
  168. auto tree_view_context_menu = GUI::Menu::construct("Tree View");
  169.  
  170. auto open_parent_directory_action = GUI::Action::create("Open parent directory", { Mod_Alt, Key_Up }, Gfx::Bitmap::load_from_file("/res/icons/16x16/open-parent-directory.png"), [&](const GUI::Action&) {
  171. directory_view.open_parent_directory();
  172. });
  173.  
  174. auto mkdir_action = GUI::Action::create("New directory...", { Mod_Ctrl | Mod_Shift, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/mkdir.png"), [&](const GUI::Action&) {
  175. auto input_box = GUI::InputBox::construct("Enter name:", "New directory", window);
  176. if (input_box->exec() == GUI::InputBox::ExecOK && !input_box->text_value().is_empty()) {
  177. auto new_dir_path = canonicalized_path(
  178. String::format("%s/%s",
  179. directory_view.path().characters(),
  180. input_box->text_value().characters()));
  181. int rc = mkdir(new_dir_path.characters(), 0777);
  182. if (rc < 0) {
  183. GUI::MessageBox::show(String::format("mkdir(\"%s\") failed: %s", new_dir_path.characters(), strerror(errno)), "Error", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, window);
  184. } else {
  185. refresh_tree_view();
  186. }
  187. }
  188. });
  189.  
  190. auto open_terminal_action = GUI::Action::create("Open Terminal here...", Gfx::Bitmap::load_from_file("/res/icons/16x16/app-terminal.png"), [&](const GUI::Action&) {
  191. if (!fork()) {
  192. if (chdir(directory_view.path().characters()) < 0) {
  193. perror("chdir");
  194. exit(1);
  195. }
  196.  
  197. if (execl("/bin/Terminal", "Terminal", nullptr) < 0)
  198. perror("execl");
  199. exit(1);
  200. }
  201. });
  202.  
  203. RefPtr<GUI::Action> view_as_table_action;
  204. RefPtr<GUI::Action> view_as_icons_action;
  205. RefPtr<GUI::Action> view_as_columns_action;
  206.  
  207. view_as_table_action = GUI::Action::create(
  208. "Table view", { Mod_Ctrl, KeyCode::Key_L }, Gfx::Bitmap::load_from_file("/res/icons/16x16/table-view.png"), [&](const GUI::Action&) {
  209. directory_view.set_view_mode(DirectoryView::ViewMode::List);
  210. view_as_table_action->set_checked(true);
  211.  
  212. config->write_entry("DirectoryView", "ViewMode", "List");
  213. config->sync();
  214. },
  215. window);
  216. view_as_table_action->set_checkable(true);
  217.  
  218. view_as_icons_action = GUI::Action::create(
  219. "Icon view", { Mod_Ctrl, KeyCode::Key_I }, Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GUI::Action&) {
  220. directory_view.set_view_mode(DirectoryView::ViewMode::Icon);
  221. view_as_icons_action->set_checked(true);
  222.  
  223. config->write_entry("DirectoryView", "ViewMode", "Icon");
  224. config->sync();
  225. },
  226. window);
  227. view_as_icons_action->set_checkable(true);
  228.  
  229. view_as_columns_action = GUI::Action::create(
  230. "Columns view", Gfx::Bitmap::load_from_file("/res/icons/16x16/columns-view.png"), [&](const GUI::Action&) {
  231. directory_view.set_view_mode(DirectoryView::ViewMode::Columns);
  232. view_as_columns_action->set_checked(true);
  233.  
  234. config->write_entry("DirectoryView", "ViewMode", "Columns");
  235. config->sync();
  236. },
  237. window);
  238. view_as_columns_action->set_checkable(true);
  239.  
  240. auto view_type_action_group = make<GUI::ActionGroup>();
  241. view_type_action_group->set_exclusive(true);
  242. view_type_action_group->add_action(*view_as_table_action);
  243. view_type_action_group->add_action(*view_as_icons_action);
  244. view_type_action_group->add_action(*view_as_columns_action);
  245.  
  246. auto selected_file_paths = [&] {
  247. Vector<String> paths;
  248. auto& view = directory_view.current_view();
  249. auto& model = *view.model();
  250. view.selection().for_each_index([&](const GUI::ModelIndex& index) {
  251. auto parent_index = model.parent_index(index);
  252. auto name_index = model.index(index.row(), GUI::FileSystemModel::Column::Name, parent_index);
  253. auto path = model.data(name_index, GUI::Model::Role::Custom).to_string();
  254. paths.append(path);
  255. });
  256. return paths;
  257. };
  258.  
  259. auto tree_view_selected_file_paths = [&] {
  260. Vector<String> paths;
  261. auto& view = tree_view;
  262. view.selection().for_each_index([&](const GUI::ModelIndex& index) {
  263. paths.append(directories_model->full_path(index));
  264. });
  265. return paths;
  266. };
  267.  
  268. auto select_all_action = GUI::Action::create("Select all", { Mod_Ctrl, KeyCode::Key_A }, [&](const GUI::Action&) {
  269. directory_view.current_view().select_all();
  270. });
  271.  
  272. auto copy_action = GUI::CommonActions::make_copy_action(
  273. [&](const GUI::Action& action) {
  274. Vector<String> paths;
  275. if (action.activator() == directory_context_menu || directory_view.active_widget()->is_focused()) {
  276. paths = selected_file_paths();
  277. } else {
  278. paths = tree_view_selected_file_paths();
  279. }
  280. if (paths.is_empty())
  281. return;
  282. StringBuilder copy_text;
  283. for (auto& path : paths) {
  284. copy_text.appendf("%s\n", path.characters());
  285. }
  286. GUI::Clipboard::the().set_data(copy_text.build(), "file-list");
  287. },
  288. window);
  289. copy_action->set_enabled(false);
  290.  
  291. auto paste_action = GUI::CommonActions::make_paste_action(
  292. [&](const GUI::Action&) {
  293. auto data_and_type = GUI::Clipboard::the().data_and_type();
  294. if (data_and_type.type != "file-list") {
  295. dbg() << "Cannot paste clipboard type " << data_and_type.type;
  296. return;
  297. }
  298. auto copied_lines = data_and_type.data.split('\n');
  299. if (copied_lines.is_empty()) {
  300. dbg() << "No files to paste";
  301. return;
  302. }
  303. for (auto& current_path : copied_lines) {
  304. if (current_path.is_empty())
  305. continue;
  306. auto current_directory = directory_view.path();
  307. auto new_path = String::format("%s/%s",
  308. current_directory.characters(),
  309. FileSystemPath(current_path).basename().characters());
  310. if (!FileUtils::copy_file_or_directory(current_path, new_path)) {
  311. auto error_message = String::format("Could not paste %s.",
  312. current_path.characters());
  313. GUI::MessageBox::show(error_message, "File Manager", GUI::MessageBox::Type::Error);
  314. } else {
  315. refresh_tree_view();
  316. }
  317. }
  318. },
  319. window);
  320. paste_action->set_enabled(GUI::Clipboard::the().type() == "file-list");
  321.  
  322. GUI::Clipboard::the().on_content_change = [&](const String& data_type) {
  323. paste_action->set_enabled(data_type == "file-list");
  324. };
  325.  
  326. auto properties_action
  327. = GUI::Action::create(
  328. "Properties...", { Mod_Alt, Key_Return }, Gfx::Bitmap::load_from_file("/res/icons/16x16/properties.png"), [&](const GUI::Action& action) {
  329. auto& model = directory_view.model();
  330. String path;
  331. Vector<String> selected;
  332. if (action.activator() == directory_context_menu || directory_view.active_widget()->is_focused()) {
  333. path = directory_view.path();
  334. selected = selected_file_paths();
  335. } else {
  336. path = directories_model->full_path(tree_view.selection().first());
  337. selected = tree_view_selected_file_paths();
  338. }
  339.  
  340. RefPtr<PropertiesDialog> properties;
  341. if (selected.is_empty()) {
  342. properties = window->add<PropertiesDialog>(model, path, true);
  343. } else {
  344. properties = window->add<PropertiesDialog>(model, selected.first(), false);
  345. }
  346.  
  347. properties->exec();
  348. },
  349. window);
  350.  
  351. enum class ConfirmBeforeDelete {
  352. No,
  353. Yes
  354. };
  355.  
  356. auto do_delete = [&](ConfirmBeforeDelete confirm, const GUI::Action& action) {
  357. Vector<String> paths;
  358. if (action.activator() == directory_context_menu || directory_view.active_widget()->is_focused()) {
  359. paths = selected_file_paths();
  360. } else {
  361. paths = tree_view_selected_file_paths();
  362. }
  363. if (paths.is_empty())
  364. return;
  365. {
  366. String message;
  367. if (paths.size() == 1) {
  368. message = String::format("Really delete %s?", FileSystemPath(paths[0]).basename().characters());
  369. } else {
  370. message = String::format("Really delete %d files?", paths.size());
  371. }
  372.  
  373. if (confirm == ConfirmBeforeDelete::Yes) {
  374. auto result = GUI::MessageBox::show(
  375. message,
  376. "Confirm deletion",
  377. GUI::MessageBox::Type::Warning,
  378. GUI::MessageBox::InputType::OKCancel,
  379. window);
  380. if (result == GUI::MessageBox::ExecCancel)
  381. return;
  382. }
  383. }
  384.  
  385. for (auto& path : paths) {
  386. struct stat st;
  387. if (lstat(path.characters(), &st)) {
  388. GUI::MessageBox::show(
  389. String::format("lstat(%s) failed: %s", path.characters(), strerror(errno)),
  390. "Delete failed",
  391. GUI::MessageBox::Type::Error,
  392. GUI::MessageBox::InputType::OK,
  393. window);
  394. break;
  395. } else {
  396. refresh_tree_view();
  397. }
  398.  
  399. if (S_ISDIR(st.st_mode)) {
  400. String error_path;
  401. int error = FileUtils::delete_directory(path, error_path);
  402.  
  403. if (error) {
  404. GUI::MessageBox::show(
  405. String::format("Failed to delete directory \"%s\": %s", error_path.characters(), strerror(error)),
  406. "Delete failed",
  407. GUI::MessageBox::Type::Error,
  408. GUI::MessageBox::InputType::OK,
  409. window);
  410. break;
  411. } else {
  412. refresh_tree_view();
  413. }
  414. } else if (unlink(path.characters()) < 0) {
  415. int saved_errno = errno;
  416. GUI::MessageBox::show(
  417. String::format("unlink(%s) failed: %s", path.characters(), strerror(saved_errno)),
  418. "Delete failed",
  419. GUI::MessageBox::Type::Error,
  420. GUI::MessageBox::InputType::OK,
  421. window);
  422. break;
  423. }
  424. }
  425. };
  426.  
  427. auto force_delete_action = GUI::Action::create(
  428. "Delete without confirmation", { Mod_Shift, Key_Delete }, [&](const GUI::Action& action) {
  429. do_delete(ConfirmBeforeDelete::No, action);
  430. },
  431. window);
  432.  
  433. auto delete_action = GUI::CommonActions::make_delete_action(
  434. [&](const GUI::Action& action) {
  435. do_delete(ConfirmBeforeDelete::Yes, action);
  436. },
  437. window);
  438. delete_action->set_enabled(false);
  439.  
  440. auto go_back_action = GUI::CommonActions::make_go_back_action(
  441. [&](auto&) {
  442. directory_view.open_previous_directory();
  443. },
  444. window);
  445.  
  446. auto go_forward_action = GUI::CommonActions::make_go_forward_action(
  447. [&](auto&) {
  448. directory_view.open_next_directory();
  449. },
  450. window);
  451.  
  452. auto go_home_action = GUI::CommonActions::make_go_home_action(
  453. [&](auto&) {
  454. directory_view.open(get_current_user_home_path());
  455. },
  456. window);
  457.  
  458. auto menubar = make<GUI::MenuBar>();
  459.  
  460. auto app_menu = GUI::Menu::construct("File Manager");
  461. app_menu->add_action(mkdir_action);
  462. app_menu->add_action(copy_action);
  463. app_menu->add_action(paste_action);
  464. app_menu->add_action(delete_action);
  465. app_menu->add_separator();
  466. app_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) {
  467. GUI::Application::the().quit(0);
  468. }));
  469. menubar->add_menu(move(app_menu));
  470.  
  471. auto view_menu = GUI::Menu::construct("View");
  472. view_menu->add_action(*view_as_icons_action);
  473. view_menu->add_action(*view_as_table_action);
  474. view_menu->add_action(*view_as_columns_action);
  475. menubar->add_menu(move(view_menu));
  476.  
  477. auto go_menu = GUI::Menu::construct("Go");
  478. go_menu->add_action(go_back_action);
  479. go_menu->add_action(go_forward_action);
  480. go_menu->add_action(open_parent_directory_action);
  481. go_menu->add_action(go_home_action);
  482. menubar->add_menu(move(go_menu));
  483.  
  484. auto help_menu = GUI::Menu::construct("Help");
  485. help_menu->add_action(GUI::Action::create("About", [&](const GUI::Action&) {
  486. GUI::AboutDialog::show("File Manager", Gfx::Bitmap::load_from_file("/res/icons/32x32/filetype-folder.png"), window);
  487. }));
  488. menubar->add_menu(move(help_menu));
  489.  
  490. app.set_menubar(move(menubar));
  491.  
  492. main_toolbar.add_action(go_back_action);
  493. main_toolbar.add_action(go_forward_action);
  494. main_toolbar.add_action(open_parent_directory_action);
  495. main_toolbar.add_action(go_home_action);
  496.  
  497. main_toolbar.add_separator();
  498. main_toolbar.add_action(mkdir_action);
  499. main_toolbar.add_action(copy_action);
  500. main_toolbar.add_action(paste_action);
  501. main_toolbar.add_action(delete_action);
  502.  
  503. main_toolbar.add_separator();
  504. main_toolbar.add_action(*view_as_icons_action);
  505. main_toolbar.add_action(*view_as_table_action);
  506. main_toolbar.add_action(*view_as_columns_action);
  507.  
  508. directory_view.on_path_change = [&](const String& new_path) {
  509. window->set_title(String::format("%s - File Manager", new_path.characters()));
  510. location_textbox.set_text(new_path);
  511. auto new_index = directories_model->index(new_path, GUI::FileSystemModel::Column::Name);
  512. if (new_index.is_valid()) {
  513. tree_view.selection().set(new_index);
  514. tree_view.scroll_into_view(new_index, Orientation::Vertical);
  515. tree_view.update();
  516. }
  517.  
  518. go_forward_action->set_enabled(directory_view.path_history_position()
  519. < directory_view.path_history_size() - 1);
  520. go_back_action->set_enabled(directory_view.path_history_position() > 0);
  521. };
  522.  
  523. directory_view.on_status_message = [&](const StringView& message) {
  524. statusbar.set_text(message);
  525. };
  526.  
  527. directory_view.on_thumbnail_progress = [&](int done, int total) {
  528. if (done == total) {
  529. progressbar.set_visible(false);
  530. return;
  531. }
  532. progressbar.set_range(0, total);
  533. progressbar.set_value(done);
  534. progressbar.set_visible(true);
  535. };
  536.  
  537. directory_view.on_selection_change = [&](GUI::AbstractView& view) {
  538. // FIXME: Figure out how we can enable/disable the paste action, based on clipboard contents.
  539. copy_action->set_enabled(!view.selection().is_empty());
  540. delete_action->set_enabled(!view.selection().is_empty());
  541. };
  542.  
  543. auto open_in_text_editor_action = GUI::Action::create("Open in TextEditor...", Gfx::Bitmap::load_from_file("/res/icons/TextEditor16.png"), [&](auto&) {
  544. for (auto& path : selected_file_paths()) {
  545. if (!fork()) {
  546. int rc = execl("/bin/TextEditor", "TextEditor", path.characters(), nullptr);
  547. if (rc < 0)
  548. perror("execl");
  549. exit(1);
  550. }
  551. }
  552. });
  553.  
  554. directory_context_menu->add_action(copy_action);
  555. directory_context_menu->add_action(paste_action);
  556. directory_context_menu->add_action(delete_action);
  557. directory_context_menu->add_separator();
  558. directory_context_menu->add_action(properties_action);
  559.  
  560. file_context_menu->add_action(copy_action);
  561. file_context_menu->add_action(paste_action);
  562. file_context_menu->add_action(delete_action);
  563. file_context_menu->add_separator();
  564. file_context_menu->add_action(open_in_text_editor_action);
  565. file_context_menu->add_separator();
  566. file_context_menu->add_action(properties_action);
  567.  
  568. directory_view_context_menu->add_action(mkdir_action);
  569. directory_view_context_menu->add_action(open_terminal_action);
  570.  
  571. tree_view_directory_context_menu->add_action(copy_action);
  572. tree_view_directory_context_menu->add_action(paste_action);
  573. tree_view_directory_context_menu->add_action(delete_action);
  574. tree_view_directory_context_menu->add_separator();
  575. tree_view_directory_context_menu->add_action(properties_action);
  576. tree_view_directory_context_menu->add_separator();
  577. tree_view_directory_context_menu->add_action(mkdir_action);
  578.  
  579. directory_view.on_context_menu_request = [&](const GUI::AbstractView&, const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) {
  580. if (index.is_valid()) {
  581. auto& node = directory_view.model().node(index);
  582.  
  583. if (node.is_directory())
  584. directory_context_menu->popup(event.screen_position());
  585. else
  586. file_context_menu->popup(event.screen_position());
  587. } else {
  588. directory_view_context_menu->popup(event.screen_position());
  589. }
  590. };
  591.  
  592. directory_view.on_drop = [&](const GUI::AbstractView&, const GUI::ModelIndex& index, const GUI::DropEvent& event) {
  593. if (!index.is_valid())
  594. return;
  595. if (!event.mime_data().has_urls())
  596. return;
  597. auto urls = event.mime_data().urls();
  598. if (urls.is_empty()) {
  599. dbg() << "No files to drop";
  600. return;
  601. }
  602.  
  603. auto& target_node = directory_view.model().node(index);
  604. if (!target_node.is_directory())
  605. return;
  606.  
  607. for (auto& url_to_copy : urls) {
  608. if (!url_to_copy.is_valid())
  609. continue;
  610. auto new_path = String::format("%s/%s",
  611. target_node.full_path(directory_view.model()).characters(),
  612. FileSystemPath(url_to_copy.path()).basename().characters());
  613.  
  614. if (!FileUtils::copy_file_or_directory(url_to_copy.path(), new_path)) {
  615. auto error_message = String::format("Could not copy %s into %s.",
  616. url_to_copy.to_string().characters(),
  617. new_path.characters());
  618. GUI::MessageBox::show(error_message, "File Manager", GUI::MessageBox::Type::Error);
  619. } else {
  620. refresh_tree_view();
  621. }
  622. }
  623. };
  624.  
  625. tree_view.on_selection_change = [&] {
  626. auto path = directories_model->full_path(tree_view.selection().first());
  627. if (directory_view.path() == path)
  628. return;
  629. directory_view.open(path);
  630. copy_action->set_enabled(!tree_view.selection().is_empty());
  631. delete_action->set_enabled(!tree_view.selection().is_empty());
  632. };
  633.  
  634. tree_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) {
  635. if (index.is_valid()) {
  636. tree_view_directory_context_menu->popup(event.screen_position());
  637. }
  638. };
  639.  
  640. // our initial location is defined as, in order of precedence:
  641. // 1. the first command-line argument (e.g. FileManager /bin)
  642. // 2. the user's home directory
  643. // 3. the root directory
  644.  
  645. String initial_location;
  646.  
  647. if (argc >= 2)
  648. initial_location = argv[1];
  649.  
  650. if (initial_location.is_empty())
  651. initial_location = get_current_user_home_path();
  652.  
  653. if (initial_location.is_empty())
  654. initial_location = "/";
  655.  
  656. directory_view.open(initial_location);
  657. directory_view.set_focus(true);
  658.  
  659. window->show();
  660.  
  661. window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-folder.png"));
  662.  
  663. // Read direcory read mode from config.
  664. auto dir_view_mode = config->read_entry("DirectoryView", "ViewMode", "Icon");
  665.  
  666. if (dir_view_mode.contains("List")) {
  667. directory_view.set_view_mode(DirectoryView::ViewMode::List);
  668. view_as_table_action->set_checked(true);
  669. } else if (dir_view_mode.contains("Columns")) {
  670. directory_view.set_view_mode(DirectoryView::ViewMode::Columns);
  671. view_as_columns_action->set_checked(true);
  672. } else {
  673. directory_view.set_view_mode(DirectoryView::ViewMode::Icon);
  674. view_as_icons_action->set_checked(true);
  675. }
  676.  
  677. if (directory_view.is_focused()) {
  678. // i cant figure out how to use this to get the key pressed at the moment
  679. directory_view.action_for_key_event();
  680. }
  681.  
  682. // Write window position to config file on close request.
  683. window->on_close_request = [&] {
  684. config->write_num_entry("Window", "Left", window->x());
  685. config->write_num_entry("Window", "Top", window->y());
  686. config->write_num_entry("Window", "Width", window->width());
  687. config->write_num_entry("Window", "Heigth", window->height());
  688. config->sync();
  689.  
  690. return GUI::Window::CloseRequestDecision::Close;
  691. };
  692.  
  693. return app.exec();
  694. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement