libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GtkCRNSelectionBox.cpp
Go to the documentation of this file.
1 /* Copyright 2010-2016 CoReNum, INSA-Lyon, ENS-Lyon
2  *
3  * This file is part of libgtkcrnmm.
4  *
5  * libgtkcrnmm is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libgtkcrnmm is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libgtkcrnmm. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: GtkCRNSelectionBox.cpp
19  * \author Yann LEYDIER
20  */
21 
22 #include <libgtkcrnmm_config.h>
23 #include <CRNi18n.h>
24 #include <GtkCRNSelectionBox.h>
25 #include <CRN.h>
26 #include <CRNIO/CRNIO.h>
27 #include <CRNMath/CRNMath.h>
28 #include <CRNData/CRNForeach.h>
29 
30 using namespace GtkCRN;
31 
32 const crn::String SelectionBox::reorderKey(U"GtkCRN::SelectionBox::Reorder");
33 const int SelectionBox::reorderId(9);
34 const int SelectionBox::dropId(4);
35 
43  orientation(ori),
44  can_reorder(true),
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)),
47  shift_key(false),
48  control_key(false),
49  selecting(false),
50  vscrolldiv(0),
51  hscrolldiv(0)
52 {
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));
58 
59  sw.show();
60  add(sw);
61 
62  if (ori == crn::Orientation::INVALID)
63  throw Glib::OptionError(Glib::OptionError::BAD_VALUE, _("GtkCRN::SelectionBox: Invalid orientation."));
64  if (ori == crn::Orientation::VERTICAL)
65  box = Gtk::manage(new Gtk::VBox());
66  else
67  box = Gtk::manage(new Gtk::HBox());
68  box->show();
69 
70  sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
71  sw.add(*box);
72 
73  box->pack_start(first_drop_zone, false, true, 0);
74  box->pack_end(last_drop_zone, true, true, 0);
75 
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));
81 
82  set_size_request(50, 50);
83  Glib::signal_timeout().connect(sigc::mem_fun(this, &SelectionBox::autoscroll), 100);
84 
85  set_can_focus(true);
86 }
87 
90 {
91 }
92 
101 void SelectionBox::pack_start(Gtk::Widget& child, Gtk::PackOptions options, guint padding)
102 {
103  std::shared_ptr<Element> vel(std::make_shared<Element>(child, this));
104  content.push_back(vel);
105  box->pack_start(*vel, options, padding);
106 }
107 
117 void SelectionBox::pack_start(Gtk::Widget& child, bool expand, bool fill, guint padding)
118 {
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);
122 }
123 
132 void SelectionBox::pack_end(Gtk::Widget& child, Gtk::PackOptions options, guint padding)
133 {
134  std::shared_ptr<Element> vel(std::make_shared<Element>(child, this));
135  content.push_back(vel);
136  box->pack_end(*vel, options, padding);
137 }
138 
148 void SelectionBox::pack_end(Gtk::Widget& child, bool expand, bool fill, guint padding)
149 {
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);
153 }
154 
157 {
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);
161  content.clear();
162  selection.clear();
163  last_selected = std::weak_ptr<Element>();
164  selection_changed.emit(nullptr, get_selection());
165 }
166 
171 std::vector<Gtk::Widget*> SelectionBox::get_selection() const
172 {
173  std::vector<Gtk::Widget*> sel;
174  for (std::shared_ptr<Element> el : content)
175  {
176  if (selection.find(el) != selection.end())
177  {
178  sel.push_back(&el->GetWidget());
179  }
180  }
181  return sel;
182 }
183 
188 std::vector<Gtk::Widget*> SelectionBox::get_content() const
189 {
190  std::vector<Gtk::Widget*> sel;
191  for (std::shared_ptr<Element> el : content)
192  {
193  sel.push_back(&el->GetWidget());
194  }
195  return sel;
196 }
197 
203 {
204  if (last_selected.expired())
205  return nullptr;
206  /*std::shared_ptr<Element> el(last_selected);
207  if (!el)
208  return nullptr;
209  return &el->GetWidget();*/
210  return const_cast<Gtk::Widget*>(&last_selected.lock()->GetWidget());
211 }
212 
218 bool SelectionBox::keyevents(GdkEventKey *ev)
219 {
220 #ifdef CRN_USING_GTKMM3
221  if ((ev->keyval == GDK_KEY_Shift_L) || (ev->keyval == GDK_KEY_Shift_R))
222 #else /* CRN_USING_GTKMM3 */
223  if ((ev->keyval == GDK_Shift_L) || (ev->keyval == GDK_Shift_R))
224 #endif /* CRN_USING_GTKMM3 */
225  {
226  if (ev->type == GDK_KEY_PRESS)
227  {
228  shift_key = true;
229  }
230  else
231  {
232  shift_key = false;
233  }
234  }
235 #ifdef CRN_USING_GTKMM3
236  if ((ev->keyval == GDK_KEY_Control_L) || (ev->keyval == GDK_KEY_Control_R))
237 #else /* CRN_USING_GTKMM3 */
238  if ((ev->keyval == GDK_Control_L) || (ev->keyval == GDK_Control_R))
239 #endif /* CRN_USING_GTKMM3 */
240  {
241  if (ev->type == GDK_KEY_PRESS)
242  {
243  control_key = true;
244  }
245  else
246  {
247  control_key = false;
248  }
249  }
250  return false;
251 }
252 
253 #ifdef CRN_USING_GTKMM3
254 
260 void SelectionBox::dodrop(Element *dropon, const Gtk::SelectionData *selection_data, int info)
261 #else /* CRN_USING_GTKMM3 */
262 
268 void SelectionBox::dodrop(Element *dropon, const GtkSelectionData *selection_data, int info)
269 #endif /* CRN_USING_GTKMM3 */
270 {
271  vscrolldiv = 0;
272  hscrolldiv = 0;
273 
274  // pos is the position in the "content" list. However as there is a drop zone at the beginning of the box, the position in the box is pos+1.
275  int pos;
276  if (dropon == nullptr)
277  {
278  pos = -1;
279  }
280  else if (dropon == (Element*)(-1))
281  {
282  pos = int(content.size()) - 1;
283  }
284  else
285  pos = int(std::find_if(content.begin(), content.end(),
286  [dropon](const std::shared_ptr<Element>&el) { return el.get() == dropon; } ) - content.begin());
287 
288  if (info == reorderId)
289  {
290  if (!can_reorder)
291  return;
292 
293  std::vector<size_t> move_to(content.size());
294  std::vector<size_t> moved_from(content.size());
295  int to = 0;
296  // copy non selected elements up to the position
297  for (int tmp = 0; tmp <= pos; ++tmp)
298  {
299  if (selection.find(content[tmp]) == selection.end())
300  {
301  move_to[tmp] = to;
302  moved_from[to] = tmp;
303  to += 1;
304  }
305  }
306  // copy selected elements
307  std::set<size_t> selpos;
308  for (const std::shared_ptr<Element> &el : selection)
309  {
310  size_t elpos = std::find(content.begin(), content.end(), el) - content.begin();
311  selpos.insert(elpos);
312  }
313  for (size_t elpos : selpos)
314  {
315  move_to[elpos] = to;
316  moved_from[to] = elpos;
317  to += 1;
318  }
319  // copy the remaining non selected elements
320  for (int tmp = pos + 1; tmp < int(content.size()); ++tmp)
321  {
322  if (selection.find(content[tmp]) == selection.end())
323  {
324  move_to[tmp] = to;
325  moved_from[to] = tmp;
326  to += 1;
327  }
328  }
329 
330  // now, we reorder the box
331  // to do that, we move all the element to the top from last to first in the new order
332  box->remove(last_drop_zone); // remove the last drop zone because it messes up with the reordering algo
333  std::vector<size_t> move(moved_from);
334  for (int tmp = int(move.size()) - 1; tmp >= 0; --tmp)
335  {
336 #ifdef CRN_USING_GTKMM3
337  box->reorder_child(*box->get_children()[move[tmp]], 1);
338 #else
339  Gtk::Box_Helpers::BoxList::iterator it(box->children().begin()), bit(box->children().begin());
340  std::advance(it, move[tmp] + 1); // +1 because the first element is a DropZone
341  std::advance(bit, 1); // idem
342  box->children().reorder(it, bit);
343 #endif /* CRN_USING_GTKMM3 */
344  for (size_t &p : move)
345  { // as an element was moved, all elements previously before it are shifted
346  if (p < move[tmp])
347  p += 1;
348  }
349  }
350  box->pack_end(last_drop_zone, true, true, 0); // put back the last drop zone
351  // reorder the content vector
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]]);
355  content.swap(nc);
356 
357  // emit the signal only if the order was really changed
358  // (the order may not be changed if the drop zone is inside the selection)
359  for (size_t tmp = 0; tmp < move_to.size(); ++tmp)
360  {
361  if (move_to[tmp] != tmp)
362  {
363  moved.emit(move_to, moved_from);
364  break;
365  }
366  }
367  }
368  else if (info == dropId)
369  { // dropped from outside
370 #ifdef CRN_USING_GTKMM3
371  crn::StringUTF8 dat(selection_data->get_text().c_str());
372 #else /* CRN_USING_GTKMM3 */
373  crn::StringUTF8 dat((char*)selection_data->data);
374 #endif /* CRN_USING_GTKMM3 */
375  //CRNVerbose(dat);
376  /*
377  std::vector<Glib::ustring> file_list;
378  file_list = selection_data.get_uris();
379  crn::StringUTF8 li;
380  CRNVerbose(file_list.size());
381  for (int tmp = 0; tmp < file_list.size(); ++tmp)
382  {
383  CRNVerbose(file_list[tmp].c_str());
384  li += file_list[tmp].c_str();
385  li += "\n";
386  }
387  droppedin.emit(pos, li);
388  */
389  droppedin.emit(pos, dat);
390  }
391 }
392 
393 void SelectionBox::drop(Element *dropon, const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint info, guint time)
394 {
395 #ifdef CRN_USING_GTKMM3
396  dodrop(dropon, &selection_data, info);
397 #else /* CRN_USING_GTKMM3 */
398  dodrop(dropon, selection_data.gobj(), info);
399 #endif /* CRN_USING_GTKMM3 */
400 }
401 
402 bool SelectionBox::drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time)
403 {
404  if (y < 10)
405  {
406  vscrolldiv = -5;
407  }
408  else if (y < 20)
409  {
410  vscrolldiv = -10;
411  }
412  else if (y > get_height() - 10)
413  {
414  vscrolldiv = 5;
415  }
416  else if (y > get_height() - 20)
417  {
418  vscrolldiv = 10;
419  }
420  else
421  {
422  vscrolldiv = 0;
423  }
424 
425  if (x < 10)
426  {
427  hscrolldiv = -5;
428  }
429  else if (x < 20)
430  {
431  hscrolldiv = -10;
432  }
433  else if (x > get_width() - 10)
434  {
435  hscrolldiv = 5;
436  }
437  else if (x > get_width() - 20)
438  {
439  hscrolldiv = 10;
440  }
441  else
442  {
443  hscrolldiv = 0;
444  }
445  return true;
446 }
447 
448 bool SelectionBox::autoscroll()
449 {
450 #ifdef CRN_USING_GTKMM3
451  auto vad = sw.get_vadjustment();
452 #else /* CRN_USING_GTKMM3 */
453  Gtk::Adjustment *vad = sw.get_vadjustment();
454 #endif /* CRN_USING_GTKMM3 */
455  if (vscrolldiv < 0)
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));
459 
460 #ifdef CRN_USING_GTKMM3
461  auto had = sw.get_hadjustment();
462 #else /* CRN_USING_GTKMM3 */
463  Gtk::Adjustment *had = sw.get_hadjustment();
464 #endif /* CRN_USING_GTKMM3 */
465  if (hscrolldiv < 0)
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));
469  return true;
470 }
471 
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)
473 {
474  show();
475 
476  std::vector<Gtk::TargetEntry> tl;
477  tl.push_back(Gtk::TargetEntry("text/plain", Gtk::TARGET_SAME_APP, reorderId)); // reorder
478  tl.push_back(Gtk::TargetEntry("text/uri-list", Gtk::TargetFlags(0), dropId)); // add from outside
479  drag_dest_set(tl, Gtk::DEST_DEFAULT_MOTION|Gtk::DEST_DEFAULT_DROP, Gdk::ACTION_COPY|Gdk::ACTION_MOVE);
480  //drag_dest_add_uri_targets();
481  //drag_dest_add_text_targets();
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));
487 #else /* CRN_USING_GTKMM3 */
488  signal_expose_event().connect(sigc::mem_fun(this, &DropZone::expose));
489 #endif /* CRN_USING_GTKMM3 */
490 
491  set_size_request(10, 10);
492 }
493 
494 bool SelectionBox::DropZone::drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time)
495 {
496  if (!in)
497  {
498  in = true;
499 #ifdef CRN_USING_GTKMM3
500  get_style_property("theme-fg-color", bg);
501 #else /* CRN_USING_GTKMM3 */
502  get_window()->set_background(get_style()->get_fg(get_state()));
503 #endif /* CRN_USING_GTKMM3 */
504  queue_draw();
505  }
506  return true;
507 }
508 
509 void SelectionBox::DropZone::drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint time)
510 {
511  in = false;
512 #ifdef CRN_USING_GTKMM3
513  get_style_property("theme-bg-color", bg);
514 #else /* CRN_USING_GTKMM3 */
515  get_window()->set_background(get_style()->get_bg(get_state()));
516 #endif /* CRN_USING_GTKMM3 */
517  queue_draw();
518 }
519 
520 #ifdef CRN_USING_GTKMM3
521 bool SelectionBox::DropZone::expose(const Cairo::RefPtr<Cairo::Context>&)
522 #else /* CRN_USING_GTKMM3 */
523 bool SelectionBox::DropZone::expose(GdkEventExpose *ev)
524 #endif /* CRN_USING_GTKMM3 */
525 {
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());
529  cr->paint();
530 #else /* CRN_USING_GTKMM3 */
531  get_window()->clear();
532 #endif /* CRN_USING_GTKMM3 */
533  return true;
534 }
535 
536 SelectionBox::Element::Element(Gtk::Widget &w, SelectionBox *const sb):
537  widget(w),
538  dz(sigc::mem_fun(sb, &SelectionBox::drop), sb, this)
539 {
540  unset_label();
541  set_shadow_type(Gtk::SHADOW_NONE);
542 
544  box = std::make_shared<Gtk::HBox>();
545  else
546  box = std::make_shared<Gtk::VBox>();
547 
548  w.show();
549 
550  // make the color of the button more visible when selected
551 #ifdef CRN_USING_GTKMM3
552  Gdk::RGBA bgcol;
553  get_style_property("theme-bg-color", bgcol);
554  // TODO
555  //tb.override_background_color(bgcol, Gtk::STATE_FLAG_ACTIVE);
556  //tb.override_background_color(bgcol, Gtk::STATE_FLAG_PRELIGHT);
557 #else
558  crn::String colors(Gtk::Settings::get_default()->property_gtk_color_scheme().get_value().c_str());
559  if (colors.IsNotEmpty())
560  {
561  crn::String bgsel(U"selected_bg_color: ");
562  size_t pos = colors.Find(bgsel);
563  pos += bgsel.Size();
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())));
568  }
569 #endif /* CRN_USING_GTKMM3 */
570 
571  tb.show();
572  tb.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &Element::toggled), sb));
573  tb.add(w);
574 
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));
580 
581  box->pack_start(tb, false, true, 0);
582 
583  dz.show();
584  box->pack_start(dz, false, true, 0);
585 
586  box->show();
587  add(*box);
588 
589  show();
590 }
591 
592 void SelectionBox::Element::toggled(SelectionBox *const sb)
593 {
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; } ));
596  if (tb.get_active())
597  { // an element was selected
598  if (sb->shift_key && !sb->last_selected.expired() && !sb->selecting)
599  { // must select all elements between last click and this element
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; });
605  if (last >= cur)
606  {
607  for (std::vector<std::shared_ptr<Element> >::iterator it = cur + 1; it != last; ++it)
608  {
609  (*it)->Select();
610  }
611  }
612  else
613  {
614  for (std::vector<std::shared_ptr<Element> >::iterator it = last + 1; it != cur; ++it)
615  {
616  (*it)->Select();
617  }
618  }
619  sb->selecting = false;
620  }
621  if (!sb->shift_key && !sb->control_key && !sb->selecting)
622  { // nor shift nor control was hold -> single selection: we must deselect all
623  sb->selecting = true;
624  for (std::shared_ptr<Element> el : sb->content)
625  {
626  if (el != shthis)
627  el->Deselect();
628  }
629  sb->selecting = false;
630  }
631  // select this and update last selected
632  //sb->selection.insert(this);
633  sb->selection.insert(shthis);
634  sb->last_selected = shthis;
635  }
636  else
637  { // an element was deselected
638  if (sb->shift_key && !sb->last_selected.expired() && !sb->selecting)
639  { // must deselect all elements between last click and this element
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);
644  if (last >= cur)
645  {
646  for (std::vector<std::shared_ptr<Element> >::iterator it = cur + 1; it != last + 1; ++it)
647  {
648  (*it)->Deselect();
649  }
650  }
651  else
652  {
653  for (std::vector<std::shared_ptr<Element> >::iterator it = last; it != cur; ++it)
654  {
655  (*it)->Deselect();
656  }
657  }
658  sb->selecting = false;
659  }
660  if (!sb->control_key && !sb->shift_key && !sb->selecting)
661  { // nor control nor shift key is hold -> deselect all and reselect this
662  sb->selecting = true;
663  for (std::shared_ptr<Element> el : sb->content)
664  {
665  el->Deselect();
666  }
667  sb->selecting = false;
668  this->Select();
669  }
670  else
671  { // deselect this and update last_selected
672  sb->selection.erase(shthis);
673  if (sb->last_selected.lock() == shthis)
674  {
675  if (sb->selection.size() == 0)
676  sb->last_selected = std::weak_ptr<Element>();
677  else
678  sb->last_selected = *sb->selection.rbegin();
679  }
680  }
681  }
682 
683  if (!sb->selecting)
684  sb->selection_changed.emit(sb->last_selected.expired() ? nullptr : &sb->last_selected.lock()->GetWidget(), sb->get_selection());
685 }
686 
687 bool SelectionBox::cursor_cross(GdkEventCrossing *ev)
688 {
689  if (ev->type == GDK_ENTER_NOTIFY)
690  {
691  control_key = ev->state & GDK_CONTROL_MASK;
692  shift_key = ev->state & GDK_SHIFT_MASK;
693  }
694  grab_focus();
695  return false;
696 }
697 
704 void SelectionBox::set_selection(size_t index)
705 {
706  deselect_all();
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]);
710  el->Select();
711 }
712 
721 void SelectionBox::set_selected(size_t index, bool selected, bool silent)
722 {
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;
726  control_key = true;
727  bool oldshift = shift_key;
728  std::shared_ptr<Element> el(content[index]);
729  bool oldselecting = selecting;
730  if (silent)
731  selecting = true;
732  if (selected)
733  {
734  if (selection.find(el) == selection.end())
735  {
736  el->Select();
737  }
738  }
739  else
740  {
741  if (selection.find(el) != selection.end())
742  {
743  el->Deselect();
744  }
745  }
746  if (silent)
747  selecting = oldselecting;
748  control_key = oldctrl;
749  shift_key = oldshift;
750 }
751 
759 bool SelectionBox::is_selected(size_t index)
760 {
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();
765 }
766 
767 
769 {
770  if (content.empty())
771  return;
772  if (content.size() > 1)
773  {
774  // inert select the first element
775  selecting = true;
776  content.front()->Select();
777  selecting = false;
778  }
779  // shift select the last element
780  bool old_shift = shift_key;
781  shift_key = true;
782  content.back()->Select();
783  shift_key = old_shift;
784 }
785 
787 {
788  if (content.empty())
789  return;
790  if (content.size() > 1)
791  {
792  // inert deselect the first element
793  selecting = true;
794  content.front()->Deselect();
795  selecting = false;
796  }
797  // shift deselect the last element
798  bool old_shift = shift_key;
799  shift_key = true;
800  content.back()->Deselect();
801  shift_key = old_shift;
802 }
803 
805 {
806  if (content.empty())
807  return;
808  selecting = true;
809  for (size_t tmp = 0; tmp < content.size(); ++tmp)
810  {
811  if (tmp & 0x01) // this is an odd number, however, as the first index is 0, this is an even element
812  {
813  content[tmp]->Select();
814  last_selected = content[tmp];
815  }
816  else
817  content[tmp]->Deselect();
818  }
819  selecting = false;
820  selection_changed.emit(last_selected.expired() ? nullptr : &last_selected.lock()->GetWidget(), get_selection());
821 }
822 
824 {
825  if (content.empty())
826  return;
827  selecting = true;
828  for (size_t tmp = 0; tmp < content.size(); ++tmp)
829  {
830  if (tmp & 0x01) // this is an odd number, however, as the first index is 0, this is an even element
831  content[tmp]->Deselect();
832  else
833  {
834  content[tmp]->Select();
835  last_selected = content[tmp];
836  }
837  }
838  selecting = false;
839  selection_changed.emit(last_selected.expired() ? nullptr : &last_selected.lock()->GetWidget(), get_selection());
840 }
841 
843 {
844  if (content.empty())
845  return;
846  selecting = true;
847  for (size_t tmp = 0; tmp < content.size() - 1; ++tmp)
848  {
849  set_selected(tmp, !is_selected(tmp));
850  }
851  selecting = false;
852  set_selected(content.size() - 1, !is_selected(content.size() - 1));
853 }
854 
855 
858 {
859  if (content.size() == 0)
860  return;
861  switch (selection.size())
862  {
863  case 1:
864  { // deselect the selected element and goto 0
865  std::shared_ptr<Element> el(*selection.begin());
866  el->Deselect();
867  }
868  case 0:
869  // select the first element
870  content.front()->Select();
871  break;
872  default:
873  { // look for the first selected element
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)
877  {
878  if (selection.find(*it) != selection.end())
879  nsel = it;
880  }
881  if ((nsel != content.rend()) && (nsel != lsel))
882  {
883  last_selected = *nsel;
884  selection_changed.emit(&last_selected.lock()->GetWidget(), get_selection());
885  }
886  }
887  }
888 }
889 
892 {
893  if (content.size() == 0)
894  return;
895  switch (selection.size())
896  {
897  case 0:
898  // select the first element
899  content.front()->Select();
900  break;
901  case 1:
902  { // if there is an element before the selected element, select it
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);
905  ++nsel;
906  if (nsel != content.rend())
907  {
908  std::shared_ptr<Element>(*lsel)->Deselect();
909  std::shared_ptr<Element>(*nsel)->Select();
910  }
911  }
912  break;
913  default:
914  { // look for the select element before last_selected
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)
918  {
919  if (selection.find(*it) != selection.end())
920  {
921  nsel = it;
922  break;
923  }
924  }
925  if (nsel != content.rend())
926  {
927  last_selected = *nsel;
928  selection_changed.emit(&last_selected.lock()->GetWidget(), get_selection());
929  }
930  }
931  }
932 }
933 
936 {
937  if (content.size() == 0)
938  return;
939  switch (selection.size())
940  {
941  case 0:
942  // select the first element
943  content.front()->Select();
944  break;
945  case 1:
946  { // if there is an element after the selected element, select it
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);
949  ++nsel;
950  if (nsel != content.end())
951  {
952  std::shared_ptr<Element>(*lsel)->Deselect();
953  std::shared_ptr<Element>(*nsel)->Select();
954  }
955  }
956  break;
957  default:
958  { // look for the select element after last_selected
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)
962  {
963  if (selection.find(*it) != selection.end())
964  {
965  nsel = it;
966  break;
967  }
968  }
969  if (nsel != content.end())
970  {
971  last_selected = *nsel;
972  selection_changed.emit(&last_selected.lock()->GetWidget(), get_selection());
973  }
974  }
975  }
976 }
977 
980 {
981  if (content.size() == 0)
982  return;
983  switch (selection.size())
984  {
985  case 1:
986  { // deselect the selected element and goto 0
987  std::shared_ptr<Element> el(*selection.begin());
988  el->Deselect();
989  }
990  case 0:
991  // select the first element
992  content.back()->Select();
993  break;
994  default:
995  { // look for the first selected element
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)
999  {
1000  if (selection.find(*it) != selection.end())
1001  nsel = it;
1002  }
1003  if ((nsel != content.end()) && (nsel != lsel))
1004  {
1005  last_selected = *nsel;
1006  selection_changed.emit(&last_selected.lock()->GetWidget(), get_selection());
1007  }
1008  }
1009  }
1010 }
1011 
1012 
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.
Definition: CRNMath.h:152
void select_even()
Selects odd elements (2nd, 4th…)
Gtk::Widget * get_last_selected() const
Returns the last widget that was selected.
#define _(String)
Definition: CRNi18n.h:51
const T & Max(const T &a, const T &b)
Returns the max of two values.
Definition: CRNMath.h:47
virtual ~SelectionBox() override
Destructor.
A UTF32 character string class.
Definition: CRNString.h:61
#define false
Definition: ConvertUTF.cpp:56
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.
#define true
Definition: ConvertUTF.cpp:57
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.
Definition: CRNMath.h:49
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.
Definition: CRNStringUTF8.h:49
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.