Ardour  8.12
dndvbox.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009-2015 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2013-2017 Robin Gareus <robin@gareus.org>
5  * Copyright (C) 2014 John Emmas <john@creativepost.co.uk>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #pragma once
23 
24 #include <gtkmm/window.h>
25 #include <gtkmm/box.h>
26 
27 #include "gtkmm2ext/visibility.h"
28 #include "gtkmm2ext/widget_state.h"
29 
30 namespace Gtkmm2ext {
31 
33 class /*LIBGTKMM2EXT_API*/ DnDVBoxChild
34 {
35 public:
36  virtual ~DnDVBoxChild () {}
37 
39  virtual Gtk::Widget& widget () = 0;
40 
42  virtual Gtk::EventBox& action_widget () = 0;
43 
45  virtual std::string drag_text () const = 0;
46 
48  virtual void set_visual_state (VisualState, bool onoff) = 0;
49 
51  virtual bool is_selectable () const = 0;
52 
53  virtual bool drag_data_get (Glib::RefPtr<Gdk::DragContext> const, Gtk::SelectionData &) { return false; }
54 
55  virtual bool can_copy_state (DnDVBoxChild*) const = 0;
56 };
57 
59 template <class T>
60 class /*LIBGTKMM2EXT_API*/ DnDVBox : public Gtk::EventBox
61 {
62 public:
63  DnDVBox (std::list<Gtk::TargetEntry> targets)
64  : _targets (targets)
65  , _active (0)
66  , _drag_icon (0)
68  , _placeholder (0)
69  , _drag_child (0)
70  {
71 
73  add_events (
77  );
78 
79  signal_button_press_event().connect (sigc::bind (mem_fun (*this, &DnDVBox::button_press), (T *) 0));
80  signal_button_release_event().connect (sigc::bind (mem_fun (*this, &DnDVBox::button_release), (T *) 0));
81  signal_drag_motion().connect (mem_fun (*this, &DnDVBox::drag_motion));
82  signal_drag_leave().connect (mem_fun (*this, &DnDVBox::drag_leave));
83 
85 
87  signal_drag_data_received().connect (mem_fun (*this, &DnDVBox::drag_data_received));
88  }
89 
90  virtual ~DnDVBox ()
91  {
92  clear ();
93 
94  delete _drag_icon;
95  }
96 
98  void add_child (T* child, std::list<Gtk::TargetEntry> targets = std::list<Gtk::TargetEntry>())
99  {
100  if (targets.empty ()) {
101  child->action_widget().drag_source_set (_targets);
102  } else {
103  child->action_widget().drag_source_set (targets);
104  }
105  child->action_widget().signal_drag_begin().connect (sigc::bind (mem_fun (*this, &DnDVBox::drag_begin), child));
106  child->action_widget().signal_drag_data_get().connect (sigc::bind (mem_fun (*this, &DnDVBox::drag_data_get), child));
107  child->action_widget().signal_drag_end().connect (sigc::bind (mem_fun (*this, &DnDVBox::drag_end), child));
108  child->action_widget().signal_button_press_event().connect (sigc::bind (mem_fun (*this, &DnDVBox::button_press), child));
109  child->action_widget().signal_button_release_event().connect (sigc::bind (mem_fun (*this, &DnDVBox::button_release), child));
110 
111  _internal_vbox.pack_start (child->widget(), false, false);
112 
113  _children.push_back (child);
114  child->widget().show ();
115  }
116 
118  std::list<T*> children ()
119  {
120  std::list<T*> sorted_children;
121 
122  std::list<Gtk::Widget*> widget_children = _internal_vbox.get_children ();
123  for (std::list<Gtk::Widget*>::iterator i = widget_children.begin(); i != widget_children.end(); ++i) {
124  T* c = child_from_widget (*i);
125 
126  if (c) {
127  sorted_children.push_back (c);
128  }
129  }
130 
131  return sorted_children;
132  }
133 
135  std::list<T*> selection (bool sorted = false) const {
136  if (!sorted) {
137  return _selection;
138  } else {
139  /* simple insertion-sort */
140  std::list<T*> rv;
141  for (typename std::list<T*>::const_iterator i = _children.begin(); i != _children.end(); ++i) {
142  if (selected (*i)) {
143  rv.push_back (*i);
144  }
145  }
146  return rv;
147  }
148  }
149 
154  void set_active (T* c) {
155  T* old_active = _active;
156  _active = c;
157  if (old_active) {
158  setup_child_state (old_active);
159  }
160  if (_active) {
162  }
163  }
164 
168  bool selected (T* child) const {
169  return (find (_selection.begin(), _selection.end(), child) != _selection.end());
170  }
171 
173  void clear ()
174  {
175  _selection.clear ();
176 
177  for (typename std::list<T*>::iterator i = _children.begin(); i != _children.end(); ++i) {
178  _internal_vbox.remove ((*i)->widget());
179  delete *i;
180  }
181 
182  _children.clear ();
183  _active = 0;
184  }
185 
186  void select_all ()
187  {
188  clear_selection ();
189  for (typename std::list<T*>::iterator i = _children.begin(); i != _children.end(); ++i) {
190  add_to_selection (*i);
191  }
192 
193  SelectionChanged (); /* EMIT SIGNAL */
194  }
195 
196  void select_none ()
197  {
198  clear_selection ();
199 
200  SelectionChanged (); /* EMIT SIGNAL */
201  }
202 
206  std::pair<T*, double> get_child_at_position (int y) const
207  {
208  T* before;
209  T* after;
210 
211  std::pair<T*, double> r;
212 
213  r.second = get_children_around_position (y, &before, &r.first, &after);
214 
215  return r;
216  }
217 
218  void set_spacing (int s) {
220  }
221 
223  {
224  if (_placeholder) {
226  _placeholder = 0;
227  }
228  }
229 
234  int add_placeholder (double y)
235  {
237  }
238 
240  sigc::signal<void> Reordered;
241 
243  sigc::signal<bool, GdkEventButton*, T*> ButtonPress;
244 
246  sigc::signal<bool, GdkEventButton*, T*> ButtonRelease;
247 
251  sigc::signal<void, DnDVBox*, T*, Glib::RefPtr<Gdk::DragContext> const & > DropFromAnotherBox;
252  sigc::signal<void, Gtk::SelectionData const &, T*, Glib::RefPtr<Gdk::DragContext> const & > DropFromExternal;
253  sigc::signal<void> SelectionChanged;
254  sigc::signal<void,T&> SelectionAdded;
255 
256 private:
257 
261  double bottom_of_child_ignoring_placeholder (T* child) const
262  {
263  Gtk::Allocation const a = child->widget().get_allocation ();
264  double bottom = a.get_y() + a.get_height();
265 
266  if (_placeholder) {
268  if (b.get_y() < a.get_y()) {
269  bottom -= (b.get_height () + _internal_vbox.get_spacing ());
270  }
271  }
272 
273  return bottom;
274  }
275 
283  double get_children_around_position (int y, T** before, T** at, T** after) const
284  {
285  if (_children.empty()) {
286  *before = *at = *after = 0;
287  return -1;
288  }
289 
290  *before = 0;
291 
292  typename std::list<T*>::const_iterator j = _children.begin ();
293 
294  /* index of current child */
295  int i = 0;
296  /* top of current child */
297  double top = 0;
298  /* bottom of current child */
299  double bottom = bottom_of_child_ignoring_placeholder (*j);
300 
301  while (y >= bottom && j != _children.end()) {
302 
303  top = bottom;
304 
305  *before = *j;
306  ++i;
307  ++j;
308 
309  if (j != _children.end()) {
311  }
312  }
313 
314  if (j == _children.end()) {
315  *at = 0;
316  *after = 0;
317  return -1;
318  }
319 
320  *at = *j;
321 
322  ++j;
323  *after = j != _children.end() ? *j : 0;
324 
325  return i + ((y - top) / (bottom - top));
326  }
327 
328  void drag_begin (Glib::RefPtr<Gdk::DragContext> const & context, T* child)
329  {
330  _drag_child = child;
331 
332  /* make up an icon for the drag */
334 
335  Gtk::Allocation a = child->action_widget().get_allocation ();
337 
338  _drag_icon->signal_expose_event().connect (sigc::mem_fun (*this, &DnDVBox::icon_expose));
340 
341  /* make the icon transparent if possible */
342  Glib::RefPtr<Gdk::Screen const> s = _drag_icon->get_screen ();
343  Glib::RefPtr<Gdk::Colormap const> c = s->get_rgba_colormap ();
344  if (c) {
346  }
347 
348  int w, h;
349  _drag_icon->get_size (w, h);
350  _drag_icon->drag_set_as_icon (context, w / 2, h / 2);
351 
352  _drag_source = this;
353  }
354 
355  /* Draw the drag icon */
357  {
358  /* Just grab the child's widget and use that */
359 
360  int w, h;
361  _drag_icon->get_size (w, h);
362 
363  cairo_t* cr = gdk_cairo_create (_drag_icon->get_window()->gobj ());
364 
365  Glib::RefPtr<Gdk::Pixmap> p = _drag_child->action_widget().get_snapshot();
366  gdk_cairo_set_source_pixmap (cr, p->gobj(), 0, 0);
367  cairo_rectangle (cr, 0, 0, w, h);
368  cairo_fill (cr);
369  cairo_destroy (cr);
370 
371  return false;
372  }
373 
374  void drag_data_get (Glib::RefPtr<Gdk::DragContext> const &context, Gtk::SelectionData & selection_data, guint, guint, T* child)
375  {
376  if (!child->drag_data_get(context, selection_data)) {
377  selection_data.set (selection_data.get_target(), 8, (const guchar *) &child, sizeof (&child));
378  }
379  }
380 
382  Glib::RefPtr<Gdk::DragContext> const & context, int /*x*/, int y, Gtk::SelectionData const & selection_data, guint /*info*/, guint time
383  )
384  {
385  /* work out where it was dropped */
386  std::pair<T*, double> const drop = get_child_at_position (y);
387 
388  if (selection_data.get_target () != _targets.front ().get_target ()) {
389  DropFromExternal (selection_data, drop.first, context);
390  context->drag_finish (false, false, time);
391  return;
392  }
393 
394  if (_drag_source == this) {
395 
396  /* dropped from ourselves onto ourselves */
397 
398  T* child = *((T * const *) selection_data.get_data());
399 
400  if (drop.first == 0) {
401  _internal_vbox.reorder_child (child->widget(), -1);
402  } else {
403 
404  /* where in the list this child should be dropped */
405  int target = drop.second + 0.5;
406 
407  /* find out whether the child was `picked up' from before the drop position */
408  int n = 0;
409  typename std::list<T*>::const_iterator i = _children.begin ();
410  while (i != _children.end() && *i != child && n < target) {
411  ++i;
412  ++n;
413  }
414 
415  /* if so, adjust the drop position to account for this */
416  if (n < target) {
417  --target;
418  }
419 
420  _internal_vbox.reorder_child (child->widget(), target);
421  }
422 
423  } else {
424 
425  /* drag started in another DnDVBox; raise a signal to say what happened */
426  DropFromAnotherBox (_drag_source, drop.first, context);
427  }
428 
429  context->drag_finish (false, false, time);
430  }
431 
432  void drag_end (Glib::RefPtr<Gdk::DragContext> const &, T *)
433  {
434  delete _drag_icon;
435  _drag_icon = 0;
436  _drag_source = 0;
437 
438  _drag_child = 0;
440 
441  Reordered (); /* EMIT SIGNAL */
442  }
443 
449  {
450  if (_placeholder == 0) {
451  _placeholder = manage (new Gtk::Label (""));
452  _internal_vbox.pack_start (*_placeholder, false, false);
453  _placeholder->show ();
454  }
455 
456  /* round up the index, unless we're off the end of the children */
457  int const n = c < 0 ? -1 : int (c + 0.5);
459  return n;
460  }
461 
462  bool drag_motion (Glib::RefPtr<Gdk::DragContext> const & ctx, int /*x*/, int y, guint tme)
463  {
464  if (_children.empty ()) {
465  return false;
466  }
467 
468  T* before;
469  T* at = NULL;
470  T* after;
471 
472  /* decide where we currently are */
473  double const c = get_children_around_position (y, &before, &at, &after);
474 
475  /* whether we're in the top or bottom half of the child that we're over */
476  bool top_half = (c - int (c)) < .5;
477  bool bottom_half = !top_half;
478 
479  if (_drag_source != this /* re-order */
480  && _drag_source && at
482  && _drag_source->selection ().size () == 1
483  && at != _drag_source->_drag_child // can't happen or can it?
484  && at->can_copy_state (_drag_source->_drag_child))
485  {
486  top_half = (c - int (c)) < 0.33;
487  bottom_half = (c - int (c)) > 0.8; // increase area >> 0.66; plugin below will move, or there's space
488  }
489 
490  /* Note that when checking on whether to remove a placeholder, we never do
491  so if _drag_child is 0 as this means that the child being dragged is
492  coming from a different DnDVBox, so it will never be the same as any
493  of our children.
494  */
495 
496  if (top_half && _drag_child && (before == _drag_child || at == _drag_child)) {
497  /* dropping here would have no effect, so remove the visual cue */
499  return false;
500  }
501 
502  if (bottom_half && _drag_child && (at == _drag_child || after == _drag_child)) {
503  /* dropping here would have no effect, so remove the visual cue */
505  return false;
506  }
507 
508  if (top_half || bottom_half) {
510  if (_drag_source == this /* re-order */) {
511  ctx->drag_status (Gdk::ACTION_MOVE, tme);
512  } else {
513  ctx->drag_status (Gdk::ACTION_COPY, tme);
514  }
515  } else {
516  ctx->drag_status (Gdk::ACTION_LINK, tme);
518  }
519  return true;
520  }
521 
522  void drag_leave (Glib::RefPtr<Gdk::DragContext> const &, guint)
523  {
525  }
526 
527  bool button_press (GdkEventButton* ev, T* child)
528  {
529  if (_expecting_unwanted_button_event == true && child == 0) {
531  return true;
532  }
533 
534  if (child) {
536  }
537 
538  if (ev->button == 1 || ev->button == 3) {
539 
540  if (!selected (child)) {
541 
542  if ((ev->state & Gdk::SHIFT_MASK) && !_selection.empty()) {
543 
544  /* Shift-click; select all between the clicked child and any existing selections */
545 
546  bool selecting = false;
547  bool done = false;
548  for (typename std::list<T*>::const_iterator i = _children.begin(); i != _children.end(); ++i) {
549 
550  bool const was_selected = selected (*i);
551 
552  if (selecting && !was_selected) {
553  add_to_selection (*i);
554  }
555 
556  if (!selecting && !done) {
557  if (selected (*i)) {
558  selecting = true;
559  } else if (*i == child) {
560  selecting = true;
561  add_to_selection (child);
562  }
563  } else if (selecting) {
564  if (was_selected || *i == child) {
565  selecting = false;
566  done = true;
567  }
568  }
569  }
570 
571  } else {
572 
573  if ((ev->state & Gdk::CONTROL_MASK) == 0) {
574  clear_selection ();
575  }
576 
577  if (child) {
578  add_to_selection (child);
579  }
580 
581  }
582 
583  SelectionChanged (); /* EMIT SIGNAL */
584 
585  } else {
586  /* XXX THIS NEEDS GENERALIZING FOR OS X */
587  if (ev->button == 1 && (ev->state & Gdk::CONTROL_MASK)) {
588  if (child && selected (child)) {
589  remove_from_selection (child);
590  SelectionChanged (); /* EMIT SIGNAL */
591  }
592  }
593  }
594  }
595 
596  return ButtonPress (ev, child); /* EMIT SIGNAL */
597  }
598 
599  bool button_release (GdkEventButton* ev, T* child)
600  {
601  if (_expecting_unwanted_button_event == true && child == 0) {
603  return true;
604  }
605 
606  if (child) {
608  }
609 
610  return ButtonRelease (ev, child); /* EMIT SIGNAL */
611  }
612 
614  void setup_child_state (T* c)
615  {
616  assert (c);
617  c->set_visual_state (Selected, (selected (c) || (_active == c)));
618  }
619 
621  {
622  std::list<T*> old_selection = _selection;
623  _selection.clear ();
624  for (typename std::list<T*>::iterator i = old_selection.begin(); i != old_selection.end(); ++i) {
625  setup_child_state (*i);
626  }
627  }
628 
629  void add_to_selection (T* child)
630  {
631  if (!child->is_selectable()) {
632  return;
633  }
634  _selection.push_back (child);
635  setup_child_state (child);
636  SelectionAdded (*child); /* EMIT SIGNAL */
637  }
638 
639  void remove_from_selection (T* child)
640  {
641  typename std::list<T*>::iterator x = find (_selection.begin(), _selection.end(), child);
642  if (x != _selection.end()) {
643  T* c = *x;
644  _selection.erase (x);
645  setup_child_state (c);
646  }
647  }
648 
649  T* child_from_widget (Gtk::Widget const * w) const
650  {
651  typename std::list<T*>::const_iterator i = _children.begin();
652  while (i != _children.end() && &(*i)->widget() != w) {
653  ++i;
654  }
655 
656  if (i == _children.end()) {
657  return 0;
658  }
659 
660  return *i;
661  }
662 
664  std::list<Gtk::TargetEntry> _targets;
665  std::list<T*> _children;
666  std::list<T*> _selection;
676 
678 
679 };
680 
681 template <class T>
683 
684 }
int get_width() const
int get_y() const
int get_height() const
void pack_start(Widget &child, bool expand, bool fill, guint padding=0)
void set_spacing(int spacing)
void reorder_child(Widget &child, int pos)
int get_spacing() const
void remove(Widget &widget)
Glib::ListHandle< Widget * > get_children()
(internal) Operate on contained items (see foreach())
virtual void add(Widget &widget)
const guchar * get_data() const
std::string get_target() const
void set(int format, const guint8 *data, int length)
Glib::SignalProxy6< void, const Glib::RefPtr< Gdk::DragContext > &, int, int, const SelectionData &, guint, guint > signal_drag_data_received()
void set_name(const Glib::ustring &name)
void add_events(Gdk::EventMask events)
Glib::RefPtr< Gdk::Window > get_window()
Glib::SignalProxy1< bool, GdkEventButton * > signal_button_release_event()
void set_size_request(int width=-1, int height=-1)
void drag_set_as_icon(const Glib::RefPtr< Gdk::DragContext > &context, int hot_x, int hot_y)
Glib::SignalProxy1< bool, GdkEventExpose * > signal_expose_event()
Event triggered by window requiring a refresh.
Glib::SignalProxy2< void, const Glib::RefPtr< Gdk::DragContext > &, guint > signal_drag_leave()
Glib::SignalProxy1< bool, GdkEventButton * > signal_button_press_event()
Allocation get_allocation() const
Glib::ustring get_name() const
Glib::SignalProxy4< bool, const Glib::RefPtr< Gdk::DragContext > &, int, int, guint > signal_drag_motion()
void set_colormap(const Glib::RefPtr< const Gdk::Colormap > &colormap)
void drag_dest_set(DestDefaults flags=DestDefaults(0), Gdk::DragAction actions=Gdk::DragAction(0))
Glib::RefPtr< Gdk::Screen > get_screen()
void get_size(int &width, int &height) const
virtual bool drag_data_get(Glib::RefPtr< Gdk::DragContext > const, Gtk::SelectionData &)
Definition: dndvbox.h:53
virtual Gtk::Widget & widget()=0
virtual Gtk::EventBox & action_widget()=0
virtual void set_visual_state(VisualState, bool onoff)=0
virtual bool can_copy_state(DnDVBoxChild *) const =0
virtual ~DnDVBoxChild()
Definition: dndvbox.h:36
virtual std::string drag_text() const =0
virtual bool is_selectable() const =0
sigc::signal< void, DnDVBox *, T *, Glib::RefPtr< Gdk::DragContext > const & > DropFromAnotherBox
Definition: dndvbox.h:251
std::list< Gtk::TargetEntry > _targets
Definition: dndvbox.h:664
sigc::signal< void > SelectionChanged
Definition: dndvbox.h:253
std::list< T * > children()
Definition: dndvbox.h:118
void add_child(T *child, std::list< Gtk::TargetEntry > targets=std::list< Gtk::TargetEntry >())
Definition: dndvbox.h:98
static DnDVBox * _drag_source
Definition: dndvbox.h:677
bool icon_expose(GdkEventExpose *)
Definition: dndvbox.h:356
sigc::signal< void, T & > SelectionAdded
Definition: dndvbox.h:254
void clear_selection()
Definition: dndvbox.h:620
sigc::signal< void > Reordered
Definition: dndvbox.h:240
double bottom_of_child_ignoring_placeholder(T *child) const
Definition: dndvbox.h:261
bool _expecting_unwanted_button_event
Definition: dndvbox.h:669
sigc::signal< bool, GdkEventButton *, T * > ButtonPress
Definition: dndvbox.h:243
void remove_placeholder()
Definition: dndvbox.h:222
DnDVBox(std::list< Gtk::TargetEntry > targets)
Definition: dndvbox.h:63
std::list< T * > _selection
Definition: dndvbox.h:666
bool selected(T *child) const
Definition: dndvbox.h:168
virtual ~DnDVBox()
Definition: dndvbox.h:90
bool button_release(GdkEventButton *ev, T *child)
Definition: dndvbox.h:599
void add_to_selection(T *child)
Definition: dndvbox.h:629
sigc::signal< bool, GdkEventButton *, T * > ButtonRelease
Definition: dndvbox.h:246
std::pair< T *, double > get_child_at_position(int y) const
Definition: dndvbox.h:206
void setup_child_state(T *c)
Definition: dndvbox.h:614
int add_placeholder(double y)
Definition: dndvbox.h:234
std::list< T * > _children
Definition: dndvbox.h:665
double get_children_around_position(int y, T **before, T **at, T **after) const
Definition: dndvbox.h:283
void select_none()
Definition: dndvbox.h:196
bool button_press(GdkEventButton *ev, T *child)
Definition: dndvbox.h:527
void drag_begin(Glib::RefPtr< Gdk::DragContext > const &context, T *child)
Definition: dndvbox.h:328
void drag_data_received(Glib::RefPtr< Gdk::DragContext > const &context, int, int y, Gtk::SelectionData const &selection_data, guint, guint time)
Definition: dndvbox.h:381
T * child_from_widget(Gtk::Widget const *w) const
Definition: dndvbox.h:649
void drag_leave(Glib::RefPtr< Gdk::DragContext > const &, guint)
Definition: dndvbox.h:522
void set_active(T *c)
Definition: dndvbox.h:154
void drag_data_get(Glib::RefPtr< Gdk::DragContext > const &context, Gtk::SelectionData &selection_data, guint, guint, T *child)
Definition: dndvbox.h:374
int create_or_update_placeholder(double c)
Definition: dndvbox.h:448
void remove_from_selection(T *child)
Definition: dndvbox.h:639
Gtk::Window * _drag_icon
Definition: dndvbox.h:668
void drag_end(Glib::RefPtr< Gdk::DragContext > const &, T *)
Definition: dndvbox.h:432
std::list< T * > selection(bool sorted=false) const
Definition: dndvbox.h:135
bool drag_motion(Glib::RefPtr< Gdk::DragContext > const &ctx, int, int y, guint tme)
Definition: dndvbox.h:462
Gtk::VBox _internal_vbox
Definition: dndvbox.h:663
void set_spacing(int s)
Definition: dndvbox.h:218
sigc::signal< void, Gtk::SelectionData const &, T *, Glib::RefPtr< Gdk::DragContext > const & > DropFromExternal
Definition: dndvbox.h:252
Gtk::Label * _placeholder
Definition: dndvbox.h:673
void select_all()
Definition: dndvbox.h:186
G_BEGIN_DECLS cairo_t * gdk_cairo_create(GdkDrawable *drawable)
void gdk_cairo_set_source_pixmap(cairo_t *cr, GdkPixmap *pixmap, double pixmap_x, double pixmap_y)
@ ACTION_LINK
Definition: dragcontext.h:72
@ ACTION_MOVE
Definition: dragcontext.h:71
@ ACTION_COPY
Definition: dragcontext.h:70
T * manage(T *obj)
Definition: object.h:58