BrowserCellRendererVariant.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * Copyright (C) 2011 Igalia S.L.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
  14. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  15. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
  17. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  23. * THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "BrowserCellRendererVariant.h"
  26. #include "BrowserMarshal.h"
  27. #include <errno.h>
  28. enum {
  29. PROP_0,
  30. PROP_VALUE,
  31. PROP_ADJUSTMENT
  32. };
  33. enum {
  34. CHANGED,
  35. LAST_SIGNAL
  36. };
  37. struct _BrowserCellRendererVariant {
  38. GtkCellRenderer parent;
  39. GValue *value;
  40. GtkCellRenderer *textRenderer;
  41. GtkCellRenderer *toggleRenderer;
  42. GtkCellRenderer *spinRenderer;
  43. };
  44. struct _BrowserCellRendererVariantClass {
  45. GtkCellRendererClass parent;
  46. };
  47. static guint signals[LAST_SIGNAL] = { 0 };
  48. G_DEFINE_TYPE(BrowserCellRendererVariant, browser_cell_renderer_variant, GTK_TYPE_CELL_RENDERER)
  49. static void browserCellRendererVariantFinalize(GObject *object)
  50. {
  51. BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
  52. g_object_unref(renderer->toggleRenderer);
  53. g_object_unref(renderer->spinRenderer);
  54. g_object_unref(renderer->textRenderer);
  55. if (renderer->value)
  56. g_boxed_free(G_TYPE_VALUE, renderer->value);
  57. G_OBJECT_CLASS(browser_cell_renderer_variant_parent_class)->finalize(object);
  58. }
  59. static void browserCellRendererVariantGetProperty(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
  60. {
  61. BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
  62. switch (propId) {
  63. case PROP_VALUE:
  64. g_value_set_boxed(value, renderer->value);
  65. break;
  66. case PROP_ADJUSTMENT: {
  67. GtkAdjustment *adjustment = NULL;
  68. g_object_get(G_OBJECT(renderer->spinRenderer), "adjustment", &adjustment, NULL);
  69. if (adjustment) {
  70. g_value_set_object(value, adjustment);
  71. g_object_unref(adjustment);
  72. }
  73. break;
  74. }
  75. default:
  76. G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
  77. }
  78. }
  79. static void browserCellRendererVariantSetModeForValue(BrowserCellRendererVariant *renderer)
  80. {
  81. if (!renderer->value)
  82. return;
  83. GtkCellRendererMode mode;
  84. if (G_VALUE_HOLDS_BOOLEAN(renderer->value))
  85. mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
  86. else if (G_VALUE_HOLDS_STRING(renderer->value) || G_VALUE_HOLDS_UINT(renderer->value))
  87. mode = GTK_CELL_RENDERER_MODE_EDITABLE;
  88. else
  89. return;
  90. g_object_set(G_OBJECT(renderer), "mode", mode, NULL);
  91. }
  92. static void browserCellRendererVariantSetProperty(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
  93. {
  94. BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(object);
  95. switch (propId) {
  96. case PROP_VALUE:
  97. if (renderer->value)
  98. g_boxed_free(G_TYPE_VALUE, renderer->value);
  99. renderer->value = g_value_dup_boxed(value);
  100. browserCellRendererVariantSetModeForValue(renderer);
  101. break;
  102. case PROP_ADJUSTMENT:
  103. g_object_set(G_OBJECT(renderer->spinRenderer), "adjustment", g_value_get_object(value), NULL);
  104. break;
  105. default:
  106. G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
  107. }
  108. }
  109. static GtkCellRenderer *browserCellRendererVariantGetRendererForValue(BrowserCellRendererVariant *renderer)
  110. {
  111. if (!renderer->value)
  112. return NULL;
  113. if (G_VALUE_HOLDS_BOOLEAN(renderer->value)) {
  114. g_object_set(G_OBJECT(renderer->toggleRenderer),
  115. "active", g_value_get_boolean(renderer->value),
  116. NULL);
  117. return renderer->toggleRenderer;
  118. }
  119. if (G_VALUE_HOLDS_STRING(renderer->value)) {
  120. g_object_set(G_OBJECT(renderer->textRenderer),
  121. "text", g_value_get_string(renderer->value),
  122. NULL);
  123. return renderer->textRenderer;
  124. }
  125. if (G_VALUE_HOLDS_UINT(renderer->value)) {
  126. gchar *text = g_strdup_printf("%u", g_value_get_uint(renderer->value));
  127. g_object_set(G_OBJECT(renderer->spinRenderer), "text", text, NULL);
  128. g_free(text);
  129. return renderer->spinRenderer;
  130. }
  131. return NULL;
  132. }
  133. static void browserCellRendererVariantCellRendererTextEdited(BrowserCellRendererVariant *renderer, const gchar *path, const gchar *newText)
  134. {
  135. if (!renderer->value)
  136. return;
  137. if (!G_VALUE_HOLDS_STRING(renderer->value))
  138. return;
  139. g_value_set_string(renderer->value, newText);
  140. g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
  141. }
  142. static void browserCellRendererVariantCellRendererSpinEdited(BrowserCellRendererVariant *renderer, const gchar *path, const gchar *newText)
  143. {
  144. if (!renderer->value)
  145. return;
  146. if (!G_VALUE_HOLDS_UINT(renderer->value))
  147. return;
  148. GtkAdjustment *adjustment;
  149. g_object_get(G_OBJECT(renderer->spinRenderer), "adjustment", &adjustment, NULL);
  150. if (!adjustment)
  151. return;
  152. errno = 0;
  153. gchar *endPtr;
  154. gdouble value = g_strtod(newText, &endPtr);
  155. if (errno || value > gtk_adjustment_get_upper(adjustment) || value < gtk_adjustment_get_lower(adjustment) || endPtr == newText) {
  156. g_warning("Invalid input for cell: %s\n", newText);
  157. return;
  158. }
  159. g_value_set_uint(renderer->value, (guint)value);
  160. g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
  161. }
  162. static gboolean browserCellRendererVariantCellRendererActivate(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
  163. {
  164. BrowserCellRendererVariant *renderer = BROWSER_CELL_RENDERER_VARIANT(cell);
  165. if (!renderer->value)
  166. return TRUE;
  167. if (!G_VALUE_HOLDS_BOOLEAN(renderer->value))
  168. return TRUE;
  169. g_value_set_boolean(renderer->value, !g_value_get_boolean(renderer->value));
  170. g_signal_emit(renderer, signals[CHANGED], 0, path, renderer->value);
  171. return TRUE;
  172. }
  173. static void browserCellRendererVariantCellRendererRender(GtkCellRenderer *cell, cairo_t *cr, GtkWidget *widget, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
  174. {
  175. GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
  176. if (!renderer)
  177. return;
  178. GTK_CELL_RENDERER_GET_CLASS(renderer)->render(renderer, cr, widget, bgArea, cellArea, flags);
  179. }
  180. static GtkCellEditable *browserCellRendererVariantCellRendererStartEditing(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *bgArea, const GdkRectangle *cellArea, GtkCellRendererState flags)
  181. {
  182. GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
  183. if (!renderer)
  184. return NULL;
  185. if (!GTK_CELL_RENDERER_GET_CLASS(renderer)->start_editing)
  186. return NULL;
  187. return GTK_CELL_RENDERER_GET_CLASS(renderer)->start_editing(renderer, event, widget, path, bgArea, cellArea, flags);
  188. }
  189. static void browserCellRendererVariantCellRendererGetPreferredWidth(GtkCellRenderer *cell, GtkWidget *widget, gint *minimumWidth, gint *naturalWidth)
  190. {
  191. GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
  192. if (!renderer)
  193. return;
  194. GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_width(renderer, widget, minimumWidth, naturalWidth);
  195. }
  196. static void browserCellRendererVariantCellRendererGetPreferredHeight(GtkCellRenderer *cell, GtkWidget *widget, gint *minimumHeight, gint *naturalHeight)
  197. {
  198. GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
  199. if (!renderer)
  200. return;
  201. GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_height(renderer, widget, minimumHeight, naturalHeight);
  202. }
  203. static void browserCellRendererVariantCellRendererGetPreferredWidthForHeight(GtkCellRenderer *cell, GtkWidget *widget, gint height, gint *minimumWidth, gint *naturalWidth)
  204. {
  205. GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
  206. if (!renderer)
  207. return;
  208. GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_width_for_height(renderer, widget, height, minimumWidth, naturalWidth);
  209. }
  210. static void browserCellRendererVariantCellRendererGetPreferredHeightForWidth(GtkCellRenderer *cell, GtkWidget *widget, gint width, gint *minimumHeight, gint *naturalHeight)
  211. {
  212. GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
  213. if (!renderer)
  214. return;
  215. GTK_CELL_RENDERER_GET_CLASS(renderer)->get_preferred_height_for_width(renderer, widget, width, minimumHeight, naturalHeight);
  216. }
  217. static void browserCellRendererVariantCellRendererGetAlignedArea(GtkCellRenderer *cell, GtkWidget *widget, GtkCellRendererState flags, const GdkRectangle *cellArea, GdkRectangle *alignedArea)
  218. {
  219. GtkCellRenderer *renderer = browserCellRendererVariantGetRendererForValue(BROWSER_CELL_RENDERER_VARIANT(cell));
  220. if (!renderer)
  221. return;
  222. GTK_CELL_RENDERER_GET_CLASS(renderer)->get_aligned_area(renderer, widget, flags, cellArea, alignedArea);
  223. }
  224. static void browser_cell_renderer_variant_init(BrowserCellRendererVariant *renderer)
  225. {
  226. g_object_set(renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
  227. renderer->toggleRenderer = gtk_cell_renderer_toggle_new();
  228. g_object_set(G_OBJECT(renderer->toggleRenderer), "xalign", 0.0, NULL);
  229. g_object_ref_sink(renderer->toggleRenderer);
  230. renderer->textRenderer = gtk_cell_renderer_text_new();
  231. g_signal_connect_swapped(renderer->textRenderer, "edited",
  232. G_CALLBACK(browserCellRendererVariantCellRendererTextEdited), renderer);
  233. g_object_set(G_OBJECT(renderer->textRenderer), "editable", TRUE, NULL);
  234. g_object_ref_sink(renderer->textRenderer);
  235. renderer->spinRenderer = gtk_cell_renderer_spin_new();
  236. g_signal_connect_swapped(renderer->spinRenderer, "edited",
  237. G_CALLBACK(browserCellRendererVariantCellRendererSpinEdited), renderer);
  238. g_object_set(G_OBJECT(renderer->spinRenderer), "editable", TRUE, NULL);
  239. }
  240. static void browser_cell_renderer_variant_class_init(BrowserCellRendererVariantClass *klass)
  241. {
  242. GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
  243. GtkCellRendererClass *cellRendererClass = GTK_CELL_RENDERER_CLASS(klass);
  244. gobjectClass->get_property = browserCellRendererVariantGetProperty;
  245. gobjectClass->set_property = browserCellRendererVariantSetProperty;
  246. gobjectClass->finalize = browserCellRendererVariantFinalize;
  247. cellRendererClass->activate = browserCellRendererVariantCellRendererActivate;
  248. cellRendererClass->render = browserCellRendererVariantCellRendererRender;
  249. cellRendererClass->start_editing = browserCellRendererVariantCellRendererStartEditing;
  250. cellRendererClass->get_preferred_width = browserCellRendererVariantCellRendererGetPreferredWidth;
  251. cellRendererClass->get_preferred_height = browserCellRendererVariantCellRendererGetPreferredHeight;
  252. cellRendererClass->get_preferred_width_for_height = browserCellRendererVariantCellRendererGetPreferredWidthForHeight;
  253. cellRendererClass->get_preferred_height_for_width = browserCellRendererVariantCellRendererGetPreferredHeightForWidth;
  254. cellRendererClass->get_aligned_area = browserCellRendererVariantCellRendererGetAlignedArea;
  255. g_object_class_install_property(gobjectClass,
  256. PROP_VALUE,
  257. g_param_spec_boxed("value",
  258. "Value",
  259. "The cell renderer value",
  260. G_TYPE_VALUE,
  261. G_PARAM_READWRITE));
  262. g_object_class_install_property(gobjectClass,
  263. PROP_ADJUSTMENT,
  264. g_param_spec_object("adjustment",
  265. "Adjustment",
  266. "The adjustment that holds the value of the spin button",
  267. GTK_TYPE_ADJUSTMENT,
  268. G_PARAM_READWRITE));
  269. signals[CHANGED] =
  270. g_signal_new("changed",
  271. G_TYPE_FROM_CLASS(gobjectClass),
  272. G_SIGNAL_RUN_LAST,
  273. 0, NULL, NULL,
  274. browser_marshal_VOID__STRING_BOXED,
  275. G_TYPE_NONE, 2,
  276. G_TYPE_STRING, G_TYPE_VALUE);
  277. }
  278. GtkCellRenderer *browser_cell_renderer_variant_new(void)
  279. {
  280. return GTK_CELL_RENDERER(g_object_new(BROWSER_TYPE_CELL_RENDERER_VARIANT, NULL));
  281. }