22 #include <libgtkcrnmm_config.h>
30 using namespace GtkCRN;
32 const crn::String SelectionBox::reorderKey(U
"GtkCRN::SelectionBox::Reorder");
33 const int SelectionBox::reorderId(9);
34 const int SelectionBox::dropId(4);
45 first_drop_zone(sigc::mem_fun(this, &
SelectionBox::drop), this, nullptr),
46 last_drop_zone(sigc::mem_fun(this, &
SelectionBox::drop), this, (Element*)(-1)),
53 signal_key_press_event().connect(sigc::mem_fun(
this, &SelectionBox::keyevents),
true);
54 signal_key_release_event().connect(sigc::mem_fun(
this, &SelectionBox::keyevents));
55 add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
56 signal_enter_notify_event().connect(sigc::mem_fun(
this, &SelectionBox::cursor_cross));
57 signal_leave_notify_event().connect(sigc::mem_fun(
this, &SelectionBox::cursor_cross));
63 throw Glib::OptionError(Glib::OptionError::BAD_VALUE,
_(
"GtkCRN::SelectionBox: Invalid orientation."));
65 box = Gtk::manage(
new Gtk::VBox());
67 box = Gtk::manage(
new Gtk::HBox());
70 sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
73 box->pack_start(first_drop_zone,
false,
true, 0);
74 box->pack_end(last_drop_zone,
true,
true, 0);
76 std::vector<Gtk::TargetEntry> tl;
77 tl.push_back(Gtk::TargetEntry(
"text/plain"));
78 tl.push_back(Gtk::TargetEntry(
"text/uri-list"));
79 drag_dest_set(tl, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
80 signal_drag_motion().connect(sigc::mem_fun(
this, &SelectionBox::drag_motion));
82 set_size_request(50, 50);
83 Glib::signal_timeout().connect(sigc::mem_fun(
this, &SelectionBox::autoscroll), 100);
103 std::shared_ptr<Element> vel(std::make_shared<Element>(child,
this));
104 content.push_back(vel);
105 box->pack_start(*vel, options, padding);
119 std::shared_ptr<Element> vel(std::make_shared<Element>(child,
this));
120 content.push_back(vel);
121 box->pack_start(*vel, expand, fill, padding);
134 std::shared_ptr<Element> vel(std::make_shared<Element>(child,
this));
135 content.push_back(vel);
136 box->pack_end(*vel, options, padding);
150 std::shared_ptr<Element> vel(std::make_shared<Element>(child,
this));
151 content.push_back(vel);
152 box->pack_end(*vel, expand, fill, padding);
158 box->foreach(sigc::mem_fun(*box, &Gtk::Box::remove));
159 box->pack_start(first_drop_zone,
false,
true, 0);
160 box->pack_end(last_drop_zone,
true,
true, 0);
163 last_selected = std::weak_ptr<Element>();
173 std::vector<Gtk::Widget*> sel;
174 for (std::shared_ptr<Element> el : content)
176 if (selection.find(el) != selection.end())
178 sel.push_back(&el->GetWidget());
190 std::vector<Gtk::Widget*> sel;
191 for (std::shared_ptr<Element> el : content)
193 sel.push_back(&el->GetWidget());
204 if (last_selected.expired())
210 return const_cast<Gtk::Widget*
>(&last_selected.lock()->GetWidget());
218 bool SelectionBox::keyevents(GdkEventKey *ev)
220 #ifdef CRN_USING_GTKMM3
221 if ((ev->keyval == GDK_KEY_Shift_L) || (ev->keyval == GDK_KEY_Shift_R))
223 if ((ev->keyval == GDK_Shift_L) || (ev->keyval == GDK_Shift_R))
226 if (ev->type == GDK_KEY_PRESS)
235 #ifdef CRN_USING_GTKMM3
236 if ((ev->keyval == GDK_KEY_Control_L) || (ev->keyval == GDK_KEY_Control_R))
238 if ((ev->keyval == GDK_Control_L) || (ev->keyval == GDK_Control_R))
241 if (ev->type == GDK_KEY_PRESS)
253 #ifdef CRN_USING_GTKMM3
260 void SelectionBox::dodrop(Element *dropon,
const Gtk::SelectionData *selection_data,
int info)
268 void SelectionBox::dodrop(Element *dropon,
const GtkSelectionData *selection_data,
int info)
276 if (dropon ==
nullptr)
280 else if (dropon == (Element*)(-1))
282 pos = int(content.size()) - 1;
285 pos = int(std::find_if(content.begin(), content.end(),
286 [dropon](
const std::shared_ptr<Element>&el) {
return el.get() == dropon; } ) - content.begin());
288 if (info == reorderId)
293 std::vector<size_t> move_to(content.size());
294 std::vector<size_t> moved_from(content.size());
297 for (
int tmp = 0; tmp <= pos; ++tmp)
299 if (selection.find(content[tmp]) == selection.end())
302 moved_from[to] = tmp;
307 std::set<size_t> selpos;
308 for (
const std::shared_ptr<Element> &el : selection)
310 size_t elpos = std::find(content.begin(), content.end(), el) - content.begin();
311 selpos.insert(elpos);
313 for (
size_t elpos : selpos)
316 moved_from[to] = elpos;
320 for (
int tmp = pos + 1; tmp < int(content.size()); ++tmp)
322 if (selection.find(content[tmp]) == selection.end())
325 moved_from[to] = tmp;
332 box->remove(last_drop_zone);
333 std::vector<size_t> move(moved_from);
334 for (
int tmp =
int(move.size()) - 1; tmp >= 0; --tmp)
336 #ifdef CRN_USING_GTKMM3
337 box->reorder_child(*box->get_children()[move[tmp]], 1);
339 Gtk::Box_Helpers::BoxList::iterator it(box->children().begin()), bit(box->children().begin());
340 std::advance(it, move[tmp] + 1);
341 std::advance(bit, 1);
342 box->children().reorder(it, bit);
344 for (
size_t &p : move)
350 box->pack_end(last_drop_zone,
true,
true, 0);
352 std::vector<std::shared_ptr<Element> > nc;
353 for (
size_t tmp = 0; tmp < moved_from.size(); ++tmp)
354 nc.push_back(content[moved_from[tmp]]);
359 for (
size_t tmp = 0; tmp < move_to.size(); ++tmp)
361 if (move_to[tmp] != tmp)
363 moved.emit(move_to, moved_from);
368 else if (info == dropId)
370 #ifdef CRN_USING_GTKMM3
389 droppedin.emit(pos, dat);
393 void SelectionBox::drop(Element *dropon,
const Glib::RefPtr<Gdk::DragContext>& context,
int,
int,
const Gtk::SelectionData& selection_data, guint info, guint time)
395 #ifdef CRN_USING_GTKMM3
396 dodrop(dropon, &selection_data, info);
398 dodrop(dropon, selection_data.gobj(), info);
402 bool SelectionBox::drag_motion(
const Glib::RefPtr<Gdk::DragContext>& context,
int x,
int y, guint time)
412 else if (y > get_height() - 10)
416 else if (y > get_height() - 20)
433 else if (x > get_width() - 10)
437 else if (x > get_width() - 20)
448 bool SelectionBox::autoscroll()
450 #ifdef CRN_USING_GTKMM3
451 auto vad = sw.get_vadjustment();
453 Gtk::Adjustment *vad = sw.get_vadjustment();
456 vad->set_value(
crn::Max(0.0, vad->get_value() - vad->get_page_increment() / -vscrolldiv));
457 else if (vscrolldiv > 0)
458 vad->set_value(
crn::Min(vad->get_upper() - vad->get_page_size(), vad->get_value() + vad->get_page_increment() / vscrolldiv));
460 #ifdef CRN_USING_GTKMM3
461 auto had = sw.get_hadjustment();
463 Gtk::Adjustment *had = sw.get_hadjustment();
466 had->set_value(
crn::Max(0.0, had->get_value() - had->get_page_increment() / -hscrolldiv));
467 else if (hscrolldiv > 0)
468 had->set_value(
crn::Min(had->get_upper() - had->get_page_size(), had->get_value() + had->get_page_increment() / hscrolldiv));
472 SelectionBox::DropZone::DropZone(
const sigc::slot7<
void, Element*,
const Glib::RefPtr<Gdk::DragContext>&,
int,
int,
const Gtk::SelectionData&, guint, guint> &dropfun,
SelectionBox *sb, Element *el):in(
false)
476 std::vector<Gtk::TargetEntry> tl;
477 tl.push_back(Gtk::TargetEntry(
"text/plain", Gtk::TARGET_SAME_APP, reorderId));
478 tl.push_back(Gtk::TargetEntry(
"text/uri-list", Gtk::TargetFlags(0), dropId));
479 drag_dest_set(tl, Gtk::DEST_DEFAULT_MOTION|Gtk::DEST_DEFAULT_DROP, Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
482 signal_drag_data_received().connect(sigc::bind<0>(dropfun, el));
483 signal_drag_motion().connect(sigc::mem_fun(
this, &DropZone::drag_motion));
484 signal_drag_leave().connect(sigc::mem_fun(
this, &DropZone::drag_leave));
485 #ifdef CRN_USING_GTKMM3
486 signal_draw().connect(sigc::mem_fun(
this, &DropZone::expose));
488 signal_expose_event().connect(sigc::mem_fun(
this, &DropZone::expose));
491 set_size_request(10, 10);
494 bool SelectionBox::DropZone::drag_motion(
const Glib::RefPtr<Gdk::DragContext>& context,
int x,
int y, guint time)
499 #ifdef CRN_USING_GTKMM3
500 get_style_property(
"theme-fg-color", bg);
502 get_window()->set_background(get_style()->get_fg(get_state()));
509 void SelectionBox::DropZone::drag_leave(
const Glib::RefPtr<Gdk::DragContext>& context, guint time)
512 #ifdef CRN_USING_GTKMM3
513 get_style_property(
"theme-bg-color", bg);
515 get_window()->set_background(get_style()->get_bg(get_state()));
520 #ifdef CRN_USING_GTKMM3
521 bool SelectionBox::DropZone::expose(
const Cairo::RefPtr<Cairo::Context>&)
523 bool SelectionBox::DropZone::expose(GdkEventExpose *ev)
526 #ifdef CRN_USING_GTKMM3
527 auto cr = get_window()->create_cairo_context();
528 cr->set_source_rgb(bg.get_red(), bg.get_green(), bg.get_blue());
531 get_window()->clear();
536 SelectionBox::Element::Element(Gtk::Widget &w,
SelectionBox *
const sb):
541 set_shadow_type(Gtk::SHADOW_NONE);
544 box = std::make_shared<Gtk::HBox>();
546 box = std::make_shared<Gtk::VBox>();
551 #ifdef CRN_USING_GTKMM3
553 get_style_property(
"theme-bg-color", bgcol);
558 crn::String colors(Gtk::Settings::get_default()->property_gtk_color_scheme().get_value().c_str());
559 if (colors.IsNotEmpty())
562 size_t pos = colors.Find(bgsel);
564 size_t endpos = colors.Find(U
"\n", pos);
565 colors.Crop(pos, endpos - pos);
566 tb.modify_bg(Gtk::STATE_ACTIVE, Gdk::Color(Glib::ustring(colors.CStr())));
567 tb.modify_bg(Gtk::STATE_PRELIGHT, Gdk::Color(Glib::ustring(colors.CStr())));
572 tb.signal_toggled().connect(sigc::bind(sigc::mem_fun(*
this, &Element::toggled), sb));
575 std::vector<Gtk::TargetEntry> tl;
576 tl.push_back(Gtk::TargetEntry(
"text/plain", Gtk::TARGET_SAME_APP));
577 tb.drag_source_set(tl, Gdk::MODIFIER_MASK, Gdk::ACTION_MOVE);
578 tb.signal_drag_begin().connect(sigc::mem_fun(*
this, &Element::drag_begin));
579 tb.signal_drag_data_get().connect(sigc::mem_fun(*
this, &Element::drag_data_get));
581 box->pack_start(tb,
false,
true, 0);
584 box->pack_start(dz,
false,
true, 0);
592 void SelectionBox::Element::toggled(
SelectionBox *
const sb)
594 std::shared_ptr<Element> shthis(*std::find_if(sb->content.begin(), sb->content.end(),
595 [
this](
const std::shared_ptr<Element>&el) {
return el.get() ==
this; } ));
598 if (sb->shift_key && !sb->last_selected.expired() && !sb->selecting)
600 sb->selecting =
true;
601 std::vector<std::shared_ptr<Element> >::iterator last, cur;
602 last = std::find(sb->content.begin(), sb->content.end(), sb->last_selected.lock());
603 cur = std::find_if(sb->content.begin(), sb->content.end(),
604 [
this](
const std::shared_ptr<Element>& el){
return el.get() ==
this; });
607 for (std::vector<std::shared_ptr<Element> >::iterator it = cur + 1; it != last; ++it)
614 for (std::vector<std::shared_ptr<Element> >::iterator it = last + 1; it != cur; ++it)
619 sb->selecting =
false;
621 if (!sb->shift_key && !sb->control_key && !sb->selecting)
623 sb->selecting =
true;
624 for (std::shared_ptr<Element> el : sb->content)
629 sb->selecting =
false;
633 sb->selection.insert(shthis);
634 sb->last_selected = shthis;
638 if (sb->shift_key && !sb->last_selected.expired() && !sb->selecting)
640 sb->selecting =
true;
641 std::vector<std::shared_ptr<Element> >::iterator last, cur;
642 last = std::find(sb->content.begin(), sb->content.end(), sb->last_selected.lock());
643 cur = std::find(sb->content.begin(), sb->content.end(), shthis);
646 for (std::vector<std::shared_ptr<Element> >::iterator it = cur + 1; it != last + 1; ++it)
653 for (std::vector<std::shared_ptr<Element> >::iterator it = last; it != cur; ++it)
658 sb->selecting =
false;
660 if (!sb->control_key && !sb->shift_key && !sb->selecting)
662 sb->selecting =
true;
663 for (std::shared_ptr<Element> el : sb->content)
667 sb->selecting =
false;
672 sb->selection.erase(shthis);
673 if (sb->last_selected.lock() == shthis)
675 if (sb->selection.size() == 0)
676 sb->last_selected = std::weak_ptr<Element>();
678 sb->last_selected = *sb->selection.rbegin();
684 sb->selection_changed.emit(sb->last_selected.expired() ?
nullptr : &sb->last_selected.lock()->GetWidget(), sb->
get_selection());
687 bool SelectionBox::cursor_cross(GdkEventCrossing *ev)
689 if (ev->type == GDK_ENTER_NOTIFY)
691 control_key = ev->state & GDK_CONTROL_MASK;
692 shift_key = ev->state & GDK_SHIFT_MASK;
707 if (index >= content.size())
708 throw Glib::OptionError(Glib::OptionError::BAD_VALUE, Glib::ustring(
"void SelectionBox::set_selection(size_t index): ") +
_(
"index out of bounds."));
709 std::shared_ptr<Element> el(content[index]);
723 if (index >= content.size())
724 throw Glib::OptionError(Glib::OptionError::BAD_VALUE, Glib::ustring(
"void SelectionBox::set_selected(size_t index, bool selected, bool silent): ") +
_(
"index out of bounds."));
725 bool oldctrl = control_key;
727 bool oldshift = shift_key;
728 std::shared_ptr<Element> el(content[index]);
729 bool oldselecting = selecting;
734 if (selection.find(el) == selection.end())
741 if (selection.find(el) != selection.end())
747 selecting = oldselecting;
748 control_key = oldctrl;
749 shift_key = oldshift;
761 if (index >= content.size())
762 throw Glib::OptionError(Glib::OptionError::BAD_VALUE, Glib::ustring(
"bool SelectionBox::is_selected(size_t index): ") +
_(
"index out of bounds."));
763 std::shared_ptr<Element> el(content[index]);
764 return selection.find(el) != selection.end();
772 if (content.size() > 1)
776 content.front()->Select();
780 bool old_shift = shift_key;
782 content.back()->Select();
783 shift_key = old_shift;
790 if (content.size() > 1)
794 content.front()->Deselect();
798 bool old_shift = shift_key;
800 content.back()->Deselect();
801 shift_key = old_shift;
809 for (
size_t tmp = 0; tmp < content.size(); ++tmp)
813 content[tmp]->Select();
814 last_selected = content[tmp];
817 content[tmp]->Deselect();
820 selection_changed.emit(last_selected.expired() ?
nullptr : &last_selected.lock()->GetWidget(),
get_selection());
828 for (
size_t tmp = 0; tmp < content.size(); ++tmp)
831 content[tmp]->Deselect();
834 content[tmp]->Select();
835 last_selected = content[tmp];
839 selection_changed.emit(last_selected.expired() ?
nullptr : &last_selected.lock()->GetWidget(),
get_selection());
847 for (
size_t tmp = 0; tmp < content.size() - 1; ++tmp)
859 if (content.size() == 0)
861 switch (selection.size())
865 std::shared_ptr<Element> el(*selection.begin());
870 content.front()->Select();
874 std::vector<std::shared_ptr<Element> >::reverse_iterator lsel(std::find(content.rbegin(), content.rend(), last_selected.lock()));
875 std::vector<std::shared_ptr<Element> >::reverse_iterator nsel(content.rend());
876 for (std::vector<std::shared_ptr<Element> >::reverse_iterator it(lsel); it != content.rend(); ++it)
878 if (selection.find(*it) != selection.end())
881 if ((nsel != content.rend()) && (nsel != lsel))
883 last_selected = *nsel;
884 selection_changed.emit(&last_selected.lock()->GetWidget(),
get_selection());
893 if (content.size() == 0)
895 switch (selection.size())
899 content.front()->Select();
903 std::vector<std::shared_ptr<Element> >::reverse_iterator lsel(std::find(content.rbegin(), content.rend(), last_selected.lock()));
904 std::vector<std::shared_ptr<Element> >::reverse_iterator nsel(lsel);
906 if (nsel != content.rend())
908 std::shared_ptr<Element>(*lsel)->Deselect();
909 std::shared_ptr<Element>(*nsel)->Select();
915 std::vector<std::shared_ptr<Element> >::reverse_iterator lsel(std::find(content.rbegin(), content.rend(), last_selected.lock()));
916 std::vector<std::shared_ptr<Element> >::reverse_iterator nsel(content.rend());
917 for (std::vector<std::shared_ptr<Element> >::reverse_iterator it(lsel + 1); it != content.rend(); ++it)
919 if (selection.find(*it) != selection.end())
925 if (nsel != content.rend())
927 last_selected = *nsel;
928 selection_changed.emit(&last_selected.lock()->GetWidget(),
get_selection());
937 if (content.size() == 0)
939 switch (selection.size())
943 content.front()->Select();
947 std::vector<std::shared_ptr<Element> >::iterator lsel(std::find(content.begin(), content.end(), last_selected.lock()));
948 std::vector<std::shared_ptr<Element> >::iterator nsel(lsel);
950 if (nsel != content.end())
952 std::shared_ptr<Element>(*lsel)->Deselect();
953 std::shared_ptr<Element>(*nsel)->Select();
959 std::vector<std::shared_ptr<Element> >::iterator lsel(std::find(content.begin(), content.end(), last_selected.lock()));
960 std::vector<std::shared_ptr<Element> >::iterator nsel(content.end());
961 for (std::vector<std::shared_ptr<Element> >::iterator it(lsel + 1); it != content.end(); ++it)
963 if (selection.find(*it) != selection.end())
969 if (nsel != content.end())
971 last_selected = *nsel;
972 selection_changed.emit(&last_selected.lock()->GetWidget(),
get_selection());
981 if (content.size() == 0)
983 switch (selection.size())
987 std::shared_ptr<Element> el(*selection.begin());
992 content.back()->Select();
996 std::vector<std::shared_ptr<Element> >::iterator lsel(std::find(content.begin(), content.end(), last_selected.lock()));
997 std::vector<std::shared_ptr<Element> >::iterator nsel(content.end());
998 for (std::vector<std::shared_ptr<Element> >::iterator it(lsel); it != content.end(); ++it)
1000 if (selection.find(*it) != selection.end())
1003 if ((nsel != content.end()) && (nsel != lsel))
1005 last_selected = *nsel;
1006 selection_changed.emit(&last_selected.lock()->GetWidget(),
get_selection());
void set_selection(size_t index)
Sets the selection (one element)
void set_selected(size_t index, bool selected=true, bool silent=false)
Adds or remove an element from the selection.
void select_next()
If selection size is 0, the select the first element, if selection size is 1, then select the next it...
Orientation
An enumeration of orientations.
void select_even()
Selects odd elements (2nd, 4th…)
Gtk::Widget * get_last_selected() const
Returns the last widget that was selected.
const T & Max(const T &a, const T &b)
Returns the max of two values.
virtual ~SelectionBox() override
Destructor.
A UTF32 character string class.
A Gtk::Box-like widget with multiple selection, reordering and drag'n drop features.
void select_odd()
Selects even elements (1st, 3rd…)
void pack_end(Gtk::Widget &child, Gtk::PackOptions options=Gtk::PACK_EXPAND_WIDGET, guint padding=0)
Right/bottom side insert a widget to a box.
void select_first()
If selection size is <= 1, then select the first item, else move last_selected to the first selected ...
void pack_start(Gtk::Widget &child, Gtk::PackOptions options=Gtk::PACK_EXPAND_WIDGET, guint padding=0)
Left/top side insert a widget to a box.
void clear()
Erase all elements.
void invert_selection()
Inverts the view selection.
std::vector< Gtk::Widget * > get_selection() const
Returns the list of selected widgets.
SelectionBox(crn::Orientation ori)
Constructor.
const T & Min(const T &a, const T &b)
Returns the min of two values.
bool is_selected(size_t index)
Is an element selected?
void select_last()
If selection size is <= 1, then select the last item, else move last_selected to the last selected el...
void select_previous()
If selection size is 0, the select the first element, if selection size is 1, then select the previou...
A character string class.
void select_all()
Selects all elements.
std::vector< Gtk::Widget * > get_content() const
Returns the list of widgets inside the box.
void deselect_all()
Deselects all elements.
crn::Orientation get_orientation() const
Returns the orientation of the box.