1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2012 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 #include "gtkcolorswatchprivate.h"
22 #include "gtkcolorchooserprivate.h"
23 #include "gtkroundedboxprivate.h"
24 #include "gtkthemingbackgroundprivate.h"
26 #include "gtkicontheme.h"
29 #include "gtkmenuitem.h"
30 #include "gtkmenushell.h"
31 #include "gtkpressandholdprivate.h"
32 #include "gtkprivate.h"
34 #include "a11y/gtkcolorswatchaccessibleprivate.h"
37 struct _GtkColorSwatchPrivate
43 guint contains_pointer : 1;
47 GdkWindow *event_window;
49 GtkPressAndHold *press_and_hold;
66 static guint signals[LAST_SIGNAL];
68 G_DEFINE_TYPE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
71 gtk_color_swatch_init (GtkColorSwatch *swatch)
73 swatch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swatch,
74 GTK_TYPE_COLOR_SWATCH,
75 GtkColorSwatchPrivate);
77 gtk_widget_set_can_focus (GTK_WIDGET (swatch), TRUE);
78 gtk_widget_set_has_window (GTK_WIDGET (swatch), FALSE);
80 swatch->priv->use_alpha = TRUE;
81 swatch->priv->selectable = TRUE;
84 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
85 #define ACTIVE_BADGE_RADIUS 10
88 swatch_draw (GtkWidget *widget,
91 GtkColorSwatch *swatch = (GtkColorSwatch*)widget;
92 GtkThemingBackground background;
93 gdouble width, height;
94 GtkStyleContext *context;
97 GtkIconInfo *icon_info = NULL;
99 theme = gtk_icon_theme_get_default ();
100 context = gtk_widget_get_style_context (widget);
101 state = gtk_widget_get_state_flags (widget);
102 width = gtk_widget_get_allocated_width (widget);
103 height = gtk_widget_get_allocated_height (widget);
107 gtk_style_context_save (context);
108 gtk_style_context_set_state (context, state);
110 _gtk_theming_background_init_from_context (&background, context,
114 if (swatch->priv->has_color)
116 cairo_pattern_t *pattern;
117 cairo_matrix_t matrix;
119 if (swatch->priv->use_alpha)
123 _gtk_rounded_box_path (&background.padding_box, cr);
124 cairo_clip_preserve (cr);
126 cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
127 cairo_fill_preserve (cr);
129 pattern = _gtk_color_chooser_get_checkered_pattern ();
130 cairo_matrix_init_scale (&matrix, 0.125, 0.125);
131 cairo_pattern_set_matrix (pattern, &matrix);
133 cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
134 cairo_mask (cr, pattern);
135 cairo_pattern_destroy (pattern);
139 background.bg_color = swatch->priv->color;
143 background.bg_color = swatch->priv->color;
144 background.bg_color.alpha = 1.0;
147 _gtk_theming_background_render (&background, cr);
150 _gtk_theming_background_render (&background, cr);
152 gtk_render_frame (context, cr,
153 0, 0, width, height);
155 if (gtk_widget_has_visible_focus (widget))
157 cairo_set_line_width (cr, 2);
158 if (swatch->priv->has_color && INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) < 0.5)
159 cairo_set_source_rgba (cr, 1., 1., 1., 0.4);
161 cairo_set_source_rgba (cr, 0., 0., 0., 0.4);
162 _gtk_rounded_box_shrink (&background.padding_box, 3, 3, 3, 3);
163 _gtk_rounded_box_path (&background.padding_box, cr);
167 if (swatch->priv->icon)
169 icon_info = gtk_icon_theme_lookup_icon (theme, swatch->priv->icon, 16,
170 GTK_ICON_LOOKUP_GENERIC_FALLBACK
171 | GTK_ICON_LOOKUP_USE_BUILTIN);
173 else if ((state & GTK_STATE_FLAG_SELECTED) != 0)
176 GtkBorder border_width;
179 gtk_style_context_add_class (context, "color-active-badge");
180 _gtk_theming_background_init_from_context (&background, context,
181 (width - 2 * ACTIVE_BADGE_RADIUS) / 2, (height - 2 * ACTIVE_BADGE_RADIUS) / 2,
182 2 * ACTIVE_BADGE_RADIUS, 2* ACTIVE_BADGE_RADIUS,
185 if (_gtk_theming_background_has_background_image (&background))
187 _gtk_theming_background_render (&background, cr);
191 gtk_style_context_get_background_color (context, state, &bg);
192 gtk_style_context_get_border_color (context, state, &border);
193 gtk_style_context_get_border (context, state, &border_width);
195 cairo_new_sub_path (cr);
196 cairo_arc (cr, width / 2, height / 2, ACTIVE_BADGE_RADIUS, 0, 2 * G_PI);
197 cairo_close_path (cr);
198 gdk_cairo_set_source_rgba (cr, &bg);
199 cairo_fill_preserve (cr);
201 gdk_cairo_set_source_rgba (cr, &border);
202 cairo_set_line_width (cr, border_width.left);
205 gicon = g_themed_icon_new ("object-select-symbolic");
206 /* fallback for themes that don't have object-select-symbolic */
207 g_themed_icon_append_name (G_THEMED_ICON (gicon), "gtk-apply");
209 icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, 16,
210 GTK_ICON_LOOKUP_GENERIC_FALLBACK
211 | GTK_ICON_LOOKUP_USE_BUILTIN);
212 g_object_unref (gicon);
216 if (icon_info != NULL)
220 pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, context,
225 gtk_render_icon (context, cr, pixbuf,
226 (width - gdk_pixbuf_get_width (pixbuf)) / 2,
227 (height - gdk_pixbuf_get_height (pixbuf)) / 2);
228 g_object_unref (pixbuf);
231 g_object_unref (icon_info);
235 gtk_style_context_restore (context);
241 drag_set_color_icon (GdkDragContext *context,
242 const GdkRGBA *color)
244 cairo_surface_t *surface;
247 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 48, 32);
248 cr = cairo_create (surface);
249 gdk_cairo_set_source_rgba (cr, color);
252 cairo_surface_set_device_offset (surface, -4, -4);
253 gtk_drag_set_icon_surface (context, surface);
256 cairo_surface_destroy (surface);
260 swatch_drag_begin (GtkWidget *widget,
261 GdkDragContext *context)
263 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
266 gtk_color_swatch_get_rgba (swatch, &color);
267 drag_set_color_icon (context, &color);
271 swatch_drag_data_get (GtkWidget *widget,
272 GdkDragContext *context,
273 GtkSelectionData *selection_data,
277 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
281 gtk_color_swatch_get_rgba (swatch, &color);
283 vals[0] = color.red * 0xffff;
284 vals[1] = color.green * 0xffff;
285 vals[2] = color.blue * 0xffff;
286 vals[3] = color.alpha * 0xffff;
288 gtk_selection_data_set (selection_data,
289 gdk_atom_intern_static_string ("application/x-color"),
290 16, (guchar *)vals, 8);
294 swatch_drag_data_received (GtkWidget *widget,
295 GdkDragContext *context,
298 GtkSelectionData *selection_data,
306 length = gtk_selection_data_get_length (selection_data);
311 /* We accept drops with the wrong format, since the KDE color
312 * chooser incorrectly drops application/x-color with format 8.
316 g_warning ("Received invalid color data\n");
320 vals = (guint16 *) gtk_selection_data_get_data (selection_data);
322 color.red = (gdouble)vals[0] / 0xffff;
323 color.green = (gdouble)vals[1] / 0xffff;
324 color.blue = (gdouble)vals[2] / 0xffff;
325 color.alpha = (gdouble)vals[3] / 0xffff;
327 gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
331 swatch_get_preferred_width (GtkWidget *widget,
339 swatch_get_preferred_height (GtkWidget *widget,
347 swatch_key_press (GtkWidget *widget,
350 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
352 if (event->keyval == GDK_KEY_space ||
353 event->keyval == GDK_KEY_Return ||
354 event->keyval == GDK_KEY_ISO_Enter||
355 event->keyval == GDK_KEY_KP_Enter ||
356 event->keyval == GDK_KEY_KP_Space)
358 if (swatch->priv->has_color &&
359 swatch->priv->selectable &&
360 (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_SELECTED) == 0)
361 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
363 g_signal_emit (swatch, signals[ACTIVATE], 0);
367 if (GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->key_press_event (widget, event))
374 swatch_enter_notify (GtkWidget *widget,
375 GdkEventCrossing *event)
377 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
378 swatch->priv->contains_pointer = TRUE;
379 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
385 swatch_leave_notify (GtkWidget *widget,
386 GdkEventCrossing *event)
388 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
389 swatch->priv->contains_pointer = FALSE;
390 gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
396 emit_customize (GtkColorSwatch *swatch)
398 g_signal_emit (swatch, signals[CUSTOMIZE], 0);
402 popup_position_func (GtkMenu *menu,
413 GdkRectangle monitor;
416 widget = GTK_WIDGET (user_data);
417 g_return_if_fail (gtk_widget_get_realized (widget));
418 window = gtk_widget_get_window (widget);
420 screen = gtk_widget_get_screen (widget);
421 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
424 gtk_menu_set_monitor (menu, monitor_num);
426 gdk_window_get_origin (window, &root_x, &root_y);
427 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
429 /* Put corner of menu centered on swatch */
430 *x = root_x + gtk_widget_get_allocated_width (widget) / 2;
431 *y = root_y + gtk_widget_get_allocated_height (widget) / 2;
434 gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
435 *x = CLAMP (*x, monitor.x, MAX (monitor.x, monitor.width - req.width));
436 *y = CLAMP (*y, monitor.y, MAX (monitor.y, monitor.height - req.height));
440 do_popup (GtkWidget *swatch,
441 GdkEventButton *event)
446 menu = gtk_menu_new ();
447 item = gtk_menu_item_new_with_mnemonic (_("_Customize"));
448 gtk_menu_attach_to_widget (GTK_MENU (menu), swatch, NULL);
450 g_signal_connect_swapped (item, "activate",
451 G_CALLBACK (emit_customize), swatch);
453 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
455 gtk_widget_show_all (item);
458 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
459 NULL, NULL, event->button, event->time);
461 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
462 popup_position_func, swatch,
463 0, gtk_get_current_event_time ());
467 swatch_button_press (GtkWidget *widget,
468 GdkEventButton *event)
470 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
472 gtk_widget_grab_focus (widget);
474 if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
475 swatch->priv->has_color)
477 do_popup (widget, event);
480 else if (event->type == GDK_2BUTTON_PRESS &&
481 event->button == GDK_BUTTON_PRIMARY)
483 g_signal_emit (swatch, signals[ACTIVATE], 0);
491 swatch_primary_action (GtkColorSwatch *swatch)
493 GtkWidget *widget = (GtkWidget *)swatch;
496 flags = gtk_widget_get_state_flags (widget);
497 if (!swatch->priv->has_color)
499 g_signal_emit (swatch, signals[ACTIVATE], 0);
502 else if (swatch->priv->selectable &&
503 (flags & GTK_STATE_FLAG_SELECTED) == 0)
505 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
513 swatch_button_release (GtkWidget *widget,
514 GdkEventButton *event)
516 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
518 if (event->button == GDK_BUTTON_PRIMARY &&
519 swatch->priv->contains_pointer)
520 return swatch_primary_action (swatch);
526 hold_action (GtkPressAndHold *pah,
529 GtkColorSwatch *swatch)
531 emit_customize (swatch);
535 tap_action (GtkPressAndHold *pah,
538 GtkColorSwatch *swatch)
540 swatch_primary_action (swatch);
544 swatch_touch (GtkWidget *widget,
545 GdkEventTouch *event)
547 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
549 if (!swatch->priv->press_and_hold)
553 g_object_get (gtk_widget_get_settings (widget),
554 "gtk-dnd-drag-threshold", &drag_threshold,
557 swatch->priv->press_and_hold = gtk_press_and_hold_new ();
559 g_object_set (swatch->priv->press_and_hold,
560 "drag-threshold", drag_threshold,
564 g_signal_connect (swatch->priv->press_and_hold, "hold",
565 G_CALLBACK (hold_action), swatch);
566 g_signal_connect (swatch->priv->press_and_hold, "tap",
567 G_CALLBACK (tap_action), swatch);
570 gtk_press_and_hold_process_event (swatch->priv->press_and_hold, (GdkEvent *)event);
576 swatch_map (GtkWidget *widget)
578 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
580 GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->map (widget);
582 if (swatch->priv->event_window)
583 gdk_window_show (swatch->priv->event_window);
587 swatch_unmap (GtkWidget *widget)
589 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
591 if (swatch->priv->event_window)
592 gdk_window_hide (swatch->priv->event_window);
594 GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unmap (widget);
598 swatch_realize (GtkWidget *widget)
600 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
601 GtkAllocation allocation;
603 GdkWindowAttr attributes;
604 gint attributes_mask;
606 gtk_widget_get_allocation (widget, &allocation);
607 gtk_widget_set_realized (widget, TRUE);
609 attributes.window_type = GDK_WINDOW_CHILD;
610 attributes.x = allocation.x;
611 attributes.y = allocation.y;
612 attributes.width = allocation.width;
613 attributes.height = allocation.height;
614 attributes.wclass = GDK_INPUT_ONLY;
615 attributes.event_mask = gtk_widget_get_events (widget);
616 attributes.event_mask |= GDK_BUTTON_PRESS_MASK
617 | GDK_BUTTON_RELEASE_MASK
618 | GDK_ENTER_NOTIFY_MASK
619 | GDK_LEAVE_NOTIFY_MASK
622 attributes_mask = GDK_WA_X | GDK_WA_Y;
624 window = gtk_widget_get_parent_window (widget);
625 gtk_widget_set_window (widget, window);
626 g_object_ref (window);
628 swatch->priv->event_window =
629 gdk_window_new (window,
630 &attributes, attributes_mask);
631 gtk_widget_register_window (widget, swatch->priv->event_window);
635 swatch_unrealize (GtkWidget *widget)
637 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
639 if (swatch->priv->event_window)
641 gtk_widget_unregister_window (widget, swatch->priv->event_window);
642 gdk_window_destroy (swatch->priv->event_window);
643 swatch->priv->event_window = NULL;
646 GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unrealize (widget);
650 swatch_size_allocate (GtkWidget *widget,
651 GtkAllocation *allocation)
653 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
655 gtk_widget_set_allocation (widget, allocation);
657 if (gtk_widget_get_realized (widget))
658 gdk_window_move_resize (swatch->priv->event_window,
666 swatch_popup_menu (GtkWidget *swatch)
668 do_popup (swatch, NULL);
672 /* GObject implementation {{{1 */
675 swatch_get_property (GObject *object,
680 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
686 gtk_color_swatch_get_rgba (swatch, &color);
687 g_value_set_boxed (value, &color);
689 case PROP_SELECTABLE:
690 g_value_set_boolean (value, gtk_color_swatch_get_selectable (swatch));
693 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
699 swatch_set_property (GObject *object,
704 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
709 gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
711 case PROP_SELECTABLE:
712 gtk_color_swatch_set_selectable (swatch, g_value_get_boolean (value));
715 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
721 swatch_finalize (GObject *object)
723 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
725 g_free (swatch->priv->icon);
726 g_clear_object (&swatch->priv->press_and_hold);
728 G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
732 gtk_color_swatch_class_init (GtkColorSwatchClass *class)
734 GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
735 GObjectClass *object_class = (GObjectClass *)class;
737 object_class->get_property = swatch_get_property;
738 object_class->set_property = swatch_set_property;
739 object_class->finalize = swatch_finalize;
741 widget_class->get_preferred_width = swatch_get_preferred_width;
742 widget_class->get_preferred_height = swatch_get_preferred_height;
743 widget_class->draw = swatch_draw;
744 widget_class->drag_begin = swatch_drag_begin;
745 widget_class->drag_data_get = swatch_drag_data_get;
746 widget_class->drag_data_received = swatch_drag_data_received;
747 widget_class->key_press_event = swatch_key_press;
748 widget_class->popup_menu = swatch_popup_menu;
749 widget_class->button_press_event = swatch_button_press;
750 widget_class->button_release_event = swatch_button_release;
751 widget_class->enter_notify_event = swatch_enter_notify;
752 widget_class->leave_notify_event = swatch_leave_notify;
753 widget_class->realize = swatch_realize;
754 widget_class->unrealize = swatch_unrealize;
755 widget_class->map = swatch_map;
756 widget_class->unmap = swatch_unmap;
757 widget_class->size_allocate = swatch_size_allocate;
758 widget_class->touch_event = swatch_touch;
761 g_signal_new ("activate",
762 GTK_TYPE_COLOR_SWATCH,
764 G_STRUCT_OFFSET (GtkColorSwatchClass, activate),
765 NULL, NULL, NULL, G_TYPE_NONE, 0);
768 g_signal_new ("customize",
769 GTK_TYPE_COLOR_SWATCH,
771 G_STRUCT_OFFSET (GtkColorSwatchClass, customize),
772 NULL, NULL, NULL, G_TYPE_NONE, 0);
774 g_object_class_install_property (object_class, PROP_RGBA,
775 g_param_spec_boxed ("rgba", P_("RGBA Color"), P_("Color as RGBA"),
776 GDK_TYPE_RGBA, GTK_PARAM_READWRITE));
777 g_object_class_install_property (object_class, PROP_SELECTABLE,
778 g_param_spec_boolean ("selectable", P_("Selectable"), P_("Whether the swatch is selectable"),
779 TRUE, GTK_PARAM_READWRITE));
781 g_type_class_add_private (object_class, sizeof (GtkColorSwatchPrivate));
783 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COLOR_SWATCH_ACCESSIBLE);
786 /* Public API {{{1 */
789 gtk_color_swatch_new (void)
791 return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_SWATCH, NULL);
794 static const GtkTargetEntry dnd_targets[] = {
795 { "application/x-color", 0 }
799 gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
800 const GdkRGBA *color)
802 GtkStyleContext *context;
804 context = gtk_widget_get_style_context (GTK_WIDGET (swatch));
806 if (!swatch->priv->has_color)
808 gtk_drag_source_set (GTK_WIDGET (swatch),
809 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
810 dnd_targets, G_N_ELEMENTS (dnd_targets),
811 GDK_ACTION_COPY | GDK_ACTION_MOVE);
815 gtk_style_context_remove_class (context, "color-light");
816 gtk_style_context_remove_class (context, "color-dark");
819 swatch->priv->has_color = TRUE;
820 swatch->priv->color = *color;
822 if (INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) > 0.5)
823 gtk_style_context_add_class (context, "color-light");
825 gtk_style_context_add_class (context, "color-dark");
827 gtk_widget_queue_draw (GTK_WIDGET (swatch));
828 g_object_notify (G_OBJECT (swatch), "rgba");
832 gtk_color_swatch_get_rgba (GtkColorSwatch *swatch,
835 if (swatch->priv->has_color)
837 color->red = swatch->priv->color.red;
838 color->green = swatch->priv->color.green;
839 color->blue = swatch->priv->color.blue;
840 color->alpha = swatch->priv->color.alpha;
854 gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
857 swatch->priv->icon = g_strdup (icon);
858 gtk_widget_queue_draw (GTK_WIDGET (swatch));
862 gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
867 gtk_drag_dest_set (GTK_WIDGET (swatch),
868 GTK_DEST_DEFAULT_HIGHLIGHT |
869 GTK_DEST_DEFAULT_MOTION |
870 GTK_DEST_DEFAULT_DROP,
871 dnd_targets, G_N_ELEMENTS (dnd_targets),
876 gtk_drag_dest_unset (GTK_WIDGET (swatch));
881 gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
884 swatch->priv->use_alpha = use_alpha;
885 gtk_widget_queue_draw (GTK_WIDGET (swatch));
889 gtk_color_swatch_set_selectable (GtkColorSwatch *swatch,
892 if (selectable == swatch->priv->selectable)
895 swatch->priv->selectable = selectable;
896 g_object_notify (G_OBJECT (swatch), "selectable");
900 gtk_color_swatch_get_selectable (GtkColorSwatch *swatch)
902 return swatch->priv->selectable;
905 /* vim:set foldmethod=marker: */