diff --git a/rtgui/toollocationpref.cc b/rtgui/toollocationpref.cc index d0a8cdf34..2330759a4 100644 --- a/rtgui/toollocationpref.cc +++ b/rtgui/toollocationpref.cc @@ -20,6 +20,7 @@ #include "guiutils.h" #include "options.h" +#include "rtimage.h" #include "toollocationpref.h" #include "toolpanelcoord.h" @@ -291,7 +292,28 @@ Glib::ustring getToolTitleKey(Tool tool) return ""; } -} +class ListEditButtons : public Gtk::Box +{ +private: + Gtk::TreeView &list; + Glib::RefPtr listStore; + Gtk::Button buttonUp; + Gtk::Button buttonDown; + Gtk::Button buttonRemove; + + sigc::signal> signalRowsPreErase; + + void onButtonDownPressed(); + void onButtonRemovePressed(); + void onButtonUpPressed(); + void onListSelectionChanged(); + void updateButtonSensitivity(); + +public: + explicit ListEditButtons(Gtk::TreeView &list, Glib::RefPtr listStore); + + sigc::signal> getSignalRowsPreErase(); +}; class FavoritesColumns : public Gtk::TreeModelColumnRecord { @@ -323,6 +345,148 @@ public: } }; +ListEditButtons::ListEditButtons(Gtk::TreeView &list, Glib::RefPtr listStore) : + Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL), + list(list), + listStore(listStore) +{ + assert(list.get_model() == listStore); + + // Set button images. + RTImage *image_button_up = Gtk::manage(new RTImage("arrow-up-small.png")); + RTImage *image_button_down = Gtk::manage(new RTImage("arrow-down-small.png")); + RTImage *image_button_remove = Gtk::manage(new RTImage("remove-small.png")); + buttonUp.set_image(*image_button_up); + buttonDown.set_image(*image_button_down); + buttonRemove.set_image(*image_button_remove); + + // Connect signals for changing button sensitivity. + const auto on_list_sel_changed_fun = sigc::mem_fun( + *this, &ListEditButtons::onListSelectionChanged); + const auto on_row_deleted_fun = sigc::hide(on_list_sel_changed_fun); + const auto on_row_inserted_fun = sigc::hide(on_row_deleted_fun); + list.get_selection()->signal_changed().connect(on_list_sel_changed_fun); + listStore->signal_row_deleted().connect(on_row_deleted_fun); + listStore->signal_row_inserted().connect(on_row_inserted_fun); + + // Connect signals for buttons. + buttonUp.signal_pressed().connect(sigc::mem_fun( + *this, &ListEditButtons::onButtonUpPressed)); + buttonDown.signal_pressed().connect(sigc::mem_fun( + *this, &ListEditButtons::onButtonDownPressed)); + buttonRemove.signal_pressed().connect(sigc::mem_fun( + *this, &ListEditButtons::onButtonRemovePressed)); + + updateButtonSensitivity(); + + add(buttonUp); + add(buttonDown); + add(buttonRemove); +} + +void ListEditButtons::onButtonDownPressed() +{ + const auto list_children = listStore->children(); + const std::vector selected = + list.get_selection()->get_selected_rows(); + + if (selected.size() != 1) { // Only one can be selected. + return; + } + + auto selected_row_iter = listStore->get_iter(selected[0]); + auto next_row_iter = selected_row_iter; + next_row_iter++; + + if (next_row_iter == list_children.end()) { // Can't be last row. + return; + } + + listStore->iter_swap(selected_row_iter, next_row_iter); + updateButtonSensitivity(); +} + +void ListEditButtons::onButtonRemovePressed() +{ + const std::vector selected_paths = + list.get_selection()->get_selected_rows(); + std::vector selected; + + // Get row references, which are valid until the row is removed. + for (const auto & row_path : selected_paths) { + selected.push_back(Gtk::TreeModel::RowReference(listStore, row_path)); + } + + signalRowsPreErase.emit(selected_paths); + + for (const auto & row_ref : selected) { + const auto row_path = row_ref.get_path(); + if (row_path) { + listStore->erase(listStore->get_iter(row_path)); + } + } + + updateButtonSensitivity(); +} + +void ListEditButtons::onButtonUpPressed() +{ + const auto list_children = listStore->children(); + const std::vector selected = + list.get_selection()->get_selected_rows(); + + if (selected.size() != 1) { // Only one can be selected. + return; + } + + auto selected_row_iter = listStore->get_iter(selected[0]); + + if (selected_row_iter == list_children.begin()) { // Can't be first row. + return; + } + + auto prev_row_iter = selected_row_iter; + prev_row_iter--; + listStore->iter_swap(selected_row_iter, prev_row_iter); + updateButtonSensitivity(); +} + +void ListEditButtons::onListSelectionChanged() +{ + updateButtonSensitivity(); +} + +void ListEditButtons::updateButtonSensitivity() +{ + assert(list.get_model() == listStore); + + const std::vector selected = + list.get_selection()->get_selected_rows(); + + // Update sensitivity of the move up/down buttons. + if (selected.size() != 1) { + // Items can only be moved if one row is selected. + buttonDown.set_sensitive(false); + buttonUp.set_sensitive(false); + } else { + auto selected_row_iter = list.get_model()->get_iter(selected[0]); + const auto list_children = listStore->children(); + buttonUp.set_sensitive(!selected_row_iter->equal(list_children.begin())); + buttonDown.set_sensitive(!(++selected_row_iter)->equal(list_children.end())); + } + + // Update sensitivity of the remove button. + buttonRemove.set_sensitive(selected.size() > 0); +} + +sigc::signal> +ListEditButtons::getSignalRowsPreErase() +{ + return signalRowsPreErase; +} + +} + struct ToolLocationPreference::Impl { static std::unordered_map toolNamesReverseMap; @@ -336,6 +500,7 @@ struct ToolLocationPreference::Impl { Gtk::TreeViewColumn toolListViewColumnFavorite; Gtk::TreeViewColumn toolListViewColumnToolName; Gtk::TreeView *toolListViewPtr; + std::unordered_map toolListToolToRowIterMap; // Favorites list. FavoritesColumns favoritesColumns; @@ -343,6 +508,7 @@ struct ToolLocationPreference::Impl { Gtk::CellRendererText favoritesCellRendererToolName; Gtk::TreeViewColumn favoritesViewColumnToolName; Gtk::TreeView *favoritesViewPtr; + ListEditButtons favoritesListEditButtons; explicit Impl(Options &options); @@ -354,6 +520,7 @@ struct ToolLocationPreference::Impl { Tool getToolFromName(const std::string &name) const; void initFavoritesRows(const std::vector &favorites); void initToolListRows(const std::vector &favorites); + void onFavoritesRowsPreRemove(const std::vector paths); std::vector toolNamesToTools( const std::vector &tool_names) const; void updateOptions(); @@ -375,7 +542,8 @@ ToolLocationPreference::Impl::Impl(Options &options) : favoritesModelPtr(Gtk::ListStore::create(favoritesColumns)), favoritesViewColumnToolName( Gtk::TreeViewColumn(M("PREFERENCES_TOOLPANEL_TOOL"))), - favoritesViewPtr(Gtk::manage(new Gtk::TreeView(favoritesModelPtr))) + favoritesViewPtr(Gtk::manage(new Gtk::TreeView(favoritesModelPtr))), + favoritesListEditButtons(*favoritesViewPtr, favoritesModelPtr) { const std::vector favorites = toolNamesToTools(options.favorites); @@ -403,6 +571,10 @@ ToolLocationPreference::Impl::Impl(Options &options) : favoritesViewColumnToolName.pack_start(favoritesCellRendererToolName); favoritesViewColumnToolName.set_renderer( favoritesCellRendererToolName, favoritesColumns.toolName); + favoritesListEditButtons.getSignalRowsPreErase().connect(sigc::mem_fun( + *this, &ToolLocationPreference::Impl::onFavoritesRowsPreRemove)); + favoritesViewPtr->get_selection()->set_mode( + Gtk::SelectionMode::SELECTION_MULTIPLE); initFavoritesRows(favorites); } @@ -497,6 +669,7 @@ void ToolLocationPreference::Impl::addToolListRowGroup( tool_row_iter->set_value( toolListColumns.isEditable, ToolPanelCoordinator::isFavoritable(tool.id)); + toolListToolToRowIterMap[tool.id] = tool_row_iter; addToolListRowGroup(tool.children, tool_row_iter, favorites); } }; @@ -527,6 +700,18 @@ void ToolLocationPreference::Impl::initToolListRows(const std::vector &fav } } +void ToolLocationPreference::Impl::onFavoritesRowsPreRemove( + const std::vector paths) +{ + // Unset the favorite column in the tools list for tools about to be removed + // from the favorites list. + for (const auto &path : paths) { + const auto &row_iter = toolListToolToRowIterMap.at( + favoritesModelPtr->get_iter(path)->get_value(favoritesColumns.tool)); + row_iter->set_value(toolListColumns.isFavorite, false); + } +} + std::vector ToolLocationPreference::Impl::toolNamesToTools( const std::vector &tool_names) const { @@ -579,11 +764,14 @@ ToolLocationPreference::ToolLocationPreference(Options &options) : // Favorites list. Gtk::Frame *favorites_frame = Gtk::manage(new Gtk::Frame( M("PREFERENCES_TOOLPANEL_FAVORITESPANEL"))); + Gtk::Box *favorites_box = Gtk::manage(new Gtk::Box()); Gtk::ScrolledWindow *favorites_list_scrolled_window = Gtk::manage(new Gtk::ScrolledWindow()); favorites_list_scrolled_window->set_min_content_width(400); layout_grid->attach_next_to(*favorites_frame, Gtk::PositionType::POS_RIGHT, 1, 1); - favorites_frame->add(*favorites_list_scrolled_window); + favorites_box->pack_start(*favorites_list_scrolled_window, false, false); + favorites_box->pack_start(impl->favoritesListEditButtons, false, false); + favorites_frame->add(*favorites_box); favorites_list_scrolled_window->add(*impl->favoritesViewPtr); setExpandAlignProperties( favorites_frame, false, true, Gtk::ALIGN_START, Gtk::ALIGN_FILL);