shoes 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (275) hide show
  1. data/.gitignore +12 -0
  2. data/CHANGELOG +29 -0
  3. data/COPYING +30 -0
  4. data/README.md +76 -0
  5. data/README.old +117 -0
  6. data/Rakefile +130 -0
  7. data/Rakefile.bk +651 -0
  8. data/app.yaml +7 -0
  9. data/bin/main.skel +104 -0
  10. data/bugs/issue-012e9468ddc7b0cb7e1503413a8a11c4f8707b67.yaml +21 -0
  11. data/bugs/issue-0711ff8b67baa63586f11d0096fee9dd4436bf58.yaml +23 -0
  12. data/bugs/issue-07f87998d93eb401e22395c11085676389b935c4.yaml +22 -0
  13. data/bugs/issue-0bff2d80008803cbc7efbcdacdc60ef2163664d2.yaml +22 -0
  14. data/bugs/issue-0c66f223d19efbca7b692f3b91961f407ad7abec.yaml +24 -0
  15. data/bugs/issue-183bc3c7a8f575aff2b24e0bf22aa0dfcc8e4fc2.yaml +29 -0
  16. data/bugs/issue-1bad1e60a04cb3adb6a8f3ded128af2e16b56ffe.yaml +18 -0
  17. data/bugs/issue-1f1d43f76bf4de79d7f94adbae6dd506a9d81633.yaml +41 -0
  18. data/bugs/issue-320924117cadb045cc91f2a6fca922b4d81d4bb5.yaml +20 -0
  19. data/bugs/issue-33e5c8355fbf65252ea9e939714651df8bd8cb3b.yaml +25 -0
  20. data/bugs/issue-36f7c8262a72bcd5e28cfa4ed29328b8237ea79b.yaml +20 -0
  21. data/bugs/issue-39bf04ee50c3bef67b89b8e9beb28657805587ce.yaml +18 -0
  22. data/bugs/issue-3e28ba7754f73d02ef416eef989af707a8c00f01.yaml +23 -0
  23. data/bugs/issue-3f638cc03f41f9c3fdf217355321f1563f8fee45.yaml +22 -0
  24. data/bugs/issue-41e48e14f1ef4f6deb69be68165d7dd3ffd99156.yaml +22 -0
  25. data/bugs/issue-4c9408166a2aa5dd333bbb05520deebf4ded08e0.yaml +22 -0
  26. data/bugs/issue-55c687e108ecd5aeb8e0d0aeb4294984f84acd3e.yaml +18 -0
  27. data/bugs/issue-5811d34692cc292717a58ec1df1f8948cf30d826.yaml +28 -0
  28. data/bugs/issue-5a7c1f59a0526f9cea7fb366867cf1e6ed8ef69d.yaml +22 -0
  29. data/bugs/issue-688a2c2566bef6a03efa57a4ae1f7cfa8fc74444.yaml +21 -0
  30. data/bugs/issue-78b16c7988ade2ef96e716fa8cb9a004bd4ced65.yaml +33 -0
  31. data/bugs/issue-804ee49f9800154eddf0650690c7a9bb5626253f.yaml +27 -0
  32. data/bugs/issue-807dd1c610b2e941a5d454e26b2eac27e89a0ab4.yaml +22 -0
  33. data/bugs/issue-822ee33ec11ebd25f90ea86e8438de7891e63aee.yaml +18 -0
  34. data/bugs/issue-82ff7152cebb8a1cb065b864cb4fe22de2328146.yaml +29 -0
  35. data/bugs/issue-856afbdcdd4970ad54b8ce4a6c017fbaab45f49d.yaml +31 -0
  36. data/bugs/issue-8d49e32fb21522651c67490adb4d80076dbb14e1.yaml +24 -0
  37. data/bugs/issue-91583359653a9d530c1e32be72d874d847a306e7.yaml +21 -0
  38. data/bugs/issue-af41a5229613172764e7d3c98431172db337053d.yaml +20 -0
  39. data/bugs/issue-bd9856b3788dd429c998a5af1b2aae8221cb80b7.yaml +28 -0
  40. data/bugs/issue-be22aef5c564fb7b5e9938343136a9cd6a2edb7b.yaml +22 -0
  41. data/bugs/issue-bf8697523c77c326e7e35fc82cf3dd739ae92310.yaml +27 -0
  42. data/bugs/issue-cdd4b4795f34d6ff46e56ece10f8216a4a5456b2.yaml +25 -0
  43. data/bugs/issue-d9bfb5581d745ef9deb3b9b2e08ef74d1cd43082.yaml +20 -0
  44. data/bugs/issue-e0ce2f687cc096f35715d28b0af88589f4ab6cc6.yaml +18 -0
  45. data/bugs/issue-e4b0aca4a10a574b333aeaecaaf221dbc5ed22c6.yaml +38 -0
  46. data/bugs/issue-f263746594b95ba778455730478ee8df60ee639d.yaml +22 -0
  47. data/bugs/project.yaml +72 -0
  48. data/builddeps.sh +102 -0
  49. data/fonts/Coolvetica.ttf +0 -0
  50. data/fonts/Lacuna.ttf +0 -0
  51. data/gemlib/shoes.rb +1 -0
  52. data/lib/shoes.rb +548 -0
  53. data/lib/shoes/cache.rb +54 -0
  54. data/lib/shoes/chipmunk.rb +35 -0
  55. data/lib/shoes/data.rb +39 -0
  56. data/lib/shoes/help.rb +468 -0
  57. data/lib/shoes/image.rb +25 -0
  58. data/lib/shoes/inspect.rb +128 -0
  59. data/lib/shoes/log.rb +48 -0
  60. data/lib/shoes/minitar.rb +986 -0
  61. data/lib/shoes/override.rb +38 -0
  62. data/lib/shoes/pack.rb +543 -0
  63. data/lib/shoes/search.rb +46 -0
  64. data/lib/shoes/setup.rb +329 -0
  65. data/lib/shoes/shy.rb +131 -0
  66. data/lib/shoes/shybuilder.rb +44 -0
  67. data/lib/shoes/version.rb +3 -0
  68. data/make/darwin/deps.vlc +12 -0
  69. data/make/darwin/dylibs.shoes +22 -0
  70. data/make/darwin/dylibs.video +11 -0
  71. data/make/darwin/env.rb +81 -0
  72. data/make/darwin/tasks.rb +105 -0
  73. data/make/linux/env.rb +65 -0
  74. data/make/linux/tasks.rb +61 -0
  75. data/make/make.rb +85 -0
  76. data/make/mingw/dlls +19 -0
  77. data/make/mingw/env.rb +69 -0
  78. data/make/mingw/tasks.rb +70 -0
  79. data/make/rakefile_common.rb +8 -0
  80. data/manual-snapshots/class-book.png +0 -0
  81. data/manual-snapshots/expert-definr.png +0 -0
  82. data/manual-snapshots/expert-funnies.png +0 -0
  83. data/manual-snapshots/expert-irb.png +0 -0
  84. data/manual-snapshots/expert-minesweeper.png +0 -0
  85. data/manual-snapshots/expert-othello.png +0 -0
  86. data/manual-snapshots/expert-pong.png +0 -0
  87. data/manual-snapshots/expert-tankspank.png +0 -0
  88. data/manual-snapshots/good-arc.png +0 -0
  89. data/manual-snapshots/good-clock.png +0 -0
  90. data/manual-snapshots/good-follow.png +0 -0
  91. data/manual-snapshots/good-reminder.png +0 -0
  92. data/manual-snapshots/good-vjot.png +0 -0
  93. data/manual-snapshots/simple-accordion.png +0 -0
  94. data/manual-snapshots/simple-anim-shapes.png +0 -0
  95. data/manual-snapshots/simple-anim-text.png +0 -0
  96. data/manual-snapshots/simple-arc.png +0 -0
  97. data/manual-snapshots/simple-bounce.png +0 -0
  98. data/manual-snapshots/simple-calc.png +0 -0
  99. data/manual-snapshots/simple-chipmunk.png +0 -0
  100. data/manual-snapshots/simple-control-sizes.png +0 -0
  101. data/manual-snapshots/simple-curve.png +0 -0
  102. data/manual-snapshots/simple-dialogs.png +0 -0
  103. data/manual-snapshots/simple-downloader.png +0 -0
  104. data/manual-snapshots/simple-draw.png +0 -0
  105. data/manual-snapshots/simple-editor.png +0 -0
  106. data/manual-snapshots/simple-form.png +0 -0
  107. data/manual-snapshots/simple-mask.png +0 -0
  108. data/manual-snapshots/simple-menu.png +0 -0
  109. data/manual-snapshots/simple-menu1.png +0 -0
  110. data/manual-snapshots/simple-rubygems.png +0 -0
  111. data/manual-snapshots/simple-slide.png +0 -0
  112. data/manual-snapshots/simple-sphere.png +0 -0
  113. data/manual-snapshots/simple-sqlite3.png +0 -0
  114. data/manual-snapshots/simple-timer.png +0 -0
  115. data/manual-snapshots/simple-video.png +0 -0
  116. data/platform/mac/Info.plist +55 -0
  117. data/platform/mac/build-deps.sh +658 -0
  118. data/platform/mac/command-manual.rb +1 -0
  119. data/platform/mac/deps-osx.patch +159 -0
  120. data/platform/mac/dmg_ds_store +0 -0
  121. data/platform/mac/pangorc +2 -0
  122. data/platform/mac/pkg-dmg +1447 -0
  123. data/platform/mac/shoes +31 -0
  124. data/platform/mac/shoes-launch +7 -0
  125. data/platform/mac/stub.m +178 -0
  126. data/platform/mac/version.plist +14 -0
  127. data/platform/msw/base.nsi +644 -0
  128. data/platform/msw/installer-1.bmp +0 -0
  129. data/platform/msw/installer-2.bmp +0 -0
  130. data/platform/msw/shoes.exe.manifest +17 -0
  131. data/platform/msw/shoes.ico +0 -0
  132. data/platform/msw/stub-inject.c +59 -0
  133. data/platform/msw/stub.c +271 -0
  134. data/platform/msw/stub32.h +14 -0
  135. data/platform/msw/stub32.rc +16 -0
  136. data/platform/nix/INSTALL +56 -0
  137. data/platform/nix/Makefile +144 -0
  138. data/platform/nix/shoes.launch +20 -0
  139. data/platform/skel.rb +27 -0
  140. data/rakefile_darwin.rb +7 -0
  141. data/rakefile_linux.rb +3 -0
  142. data/rakefile_mingw.rb +7 -0
  143. data/samples/class-book.rb +43 -0
  144. data/samples/class-book.yaml +387 -0
  145. data/samples/expert-definr.rb +23 -0
  146. data/samples/expert-funnies.rb +51 -0
  147. data/samples/expert-irb.rb +112 -0
  148. data/samples/expert-minesweeper.rb +267 -0
  149. data/samples/expert-othello.rb +319 -0
  150. data/samples/expert-pong.rb +62 -0
  151. data/samples/expert-tankspank.rb +385 -0
  152. data/samples/good-arc.rb +37 -0
  153. data/samples/good-clock.rb +51 -0
  154. data/samples/good-follow.rb +26 -0
  155. data/samples/good-reminder.rb +174 -0
  156. data/samples/good-vjot.rb +56 -0
  157. data/samples/simple-accordion.rb +75 -0
  158. data/samples/simple-anim-shapes.rb +17 -0
  159. data/samples/simple-anim-text.rb +13 -0
  160. data/samples/simple-arc.rb +23 -0
  161. data/samples/simple-bounce.rb +24 -0
  162. data/samples/simple-calc.rb +70 -0
  163. data/samples/simple-chipmunk.rb +26 -0
  164. data/samples/simple-control-sizes.rb +24 -0
  165. data/samples/simple-curve.rb +26 -0
  166. data/samples/simple-dialogs.rb +29 -0
  167. data/samples/simple-downloader.rb +27 -0
  168. data/samples/simple-draw.rb +13 -0
  169. data/samples/simple-editor.rb +28 -0
  170. data/samples/simple-form.rb +28 -0
  171. data/samples/simple-form.shy +0 -0
  172. data/samples/simple-mask.rb +21 -0
  173. data/samples/simple-menu.rb +31 -0
  174. data/samples/simple-menu1.rb +35 -0
  175. data/samples/simple-rubygems.rb +29 -0
  176. data/samples/simple-slide.rb +45 -0
  177. data/samples/simple-sphere.rb +28 -0
  178. data/samples/simple-sqlite3.rb +13 -0
  179. data/samples/simple-timer.rb +13 -0
  180. data/samples/simple-video.rb +13 -0
  181. data/shoes.gemspec +21 -0
  182. data/shoes/app.c +591 -0
  183. data/shoes/app.h +110 -0
  184. data/shoes/appwin32.h +13 -0
  185. data/shoes/appwin32.rc +28 -0
  186. data/shoes/canvas.c +2202 -0
  187. data/shoes/canvas.h +682 -0
  188. data/shoes/code.h +14 -0
  189. data/shoes/config.h +232 -0
  190. data/shoes/effects.c +243 -0
  191. data/shoes/effects.h +7 -0
  192. data/shoes/http.h +44 -0
  193. data/shoes/http/common.h +86 -0
  194. data/shoes/http/curl.c +259 -0
  195. data/shoes/http/nsurl.m +274 -0
  196. data/shoes/http/windownload.c +114 -0
  197. data/shoes/http/winhttp.c +216 -0
  198. data/shoes/http/winhttp.h +19 -0
  199. data/shoes/image.c +1020 -0
  200. data/shoes/internal.c +46 -0
  201. data/shoes/internal.h +63 -0
  202. data/shoes/native.h +110 -0
  203. data/shoes/native/cocoa.h +105 -0
  204. data/shoes/native/cocoa.m +1557 -0
  205. data/shoes/native/gtk.c +1257 -0
  206. data/shoes/native/windows.c +2392 -0
  207. data/shoes/ruby.c +5221 -0
  208. data/shoes/ruby.h +299 -0
  209. data/shoes/world.c +243 -0
  210. data/shoes/world.h +63 -0
  211. data/static/Shoes.icns +0 -0
  212. data/static/avatar.png +0 -0
  213. data/static/code_highlighter.js +188 -0
  214. data/static/code_highlighter_ruby.js +26 -0
  215. data/static/icon-debug.png +0 -0
  216. data/static/icon-error.png +0 -0
  217. data/static/icon-info.png +0 -0
  218. data/static/icon-warn.png +0 -0
  219. data/static/listbox_button1.png +0 -0
  220. data/static/listbox_button2.png +0 -0
  221. data/static/man-app.png +0 -0
  222. data/static/man-builds.png +0 -0
  223. data/static/man-builds1.png +0 -0
  224. data/static/man-editor-notepad.png +0 -0
  225. data/static/man-editor-osx.png +0 -0
  226. data/static/man-ele-background.png +0 -0
  227. data/static/man-ele-border.png +0 -0
  228. data/static/man-ele-button.png +0 -0
  229. data/static/man-ele-check.png +0 -0
  230. data/static/man-ele-editbox.png +0 -0
  231. data/static/man-ele-editline.png +0 -0
  232. data/static/man-ele-image.png +0 -0
  233. data/static/man-ele-listbox.png +0 -0
  234. data/static/man-ele-progress.png +0 -0
  235. data/static/man-ele-radio.png +0 -0
  236. data/static/man-ele-shape.png +0 -0
  237. data/static/man-ele-textblock.png +0 -0
  238. data/static/man-ele-video.png +0 -0
  239. data/static/man-intro-dmg.png +0 -0
  240. data/static/man-intro-exe.png +0 -0
  241. data/static/man-look-tiger.png +0 -0
  242. data/static/man-look-ubuntu.png +0 -0
  243. data/static/man-look-vista.png +0 -0
  244. data/static/man-run-osx.png +0 -0
  245. data/static/man-run-vista.png +0 -0
  246. data/static/man-run-xp.png +0 -0
  247. data/static/man-shot1.png +0 -0
  248. data/static/manual-en.txt +3531 -0
  249. data/static/manual-ja.txt +2825 -0
  250. data/static/manual.css +167 -0
  251. data/static/menu-corner1.png +0 -0
  252. data/static/menu-corner2.png +0 -0
  253. data/static/menu-gray.png +0 -0
  254. data/static/menu-left.png +0 -0
  255. data/static/menu-right.png +0 -0
  256. data/static/menu-top.png +0 -0
  257. data/static/shoes-dmg.jpg +0 -0
  258. data/static/shoes-icon-blue.png +0 -0
  259. data/static/shoes-icon.png +0 -0
  260. data/static/shoes-manual-apps.gif +0 -0
  261. data/static/shoes_main_window.png +0 -0
  262. data/static/stripe.png +0 -0
  263. data/static/stubs/blank.exe +0 -0
  264. data/static/stubs/blank.hfz +0 -0
  265. data/static/stubs/blank.run +375 -0
  266. data/static/stubs/cocoa-install +0 -0
  267. data/static/stubs/sh-install +49 -0
  268. data/static/stubs/shoes-stub-inject.exe +0 -0
  269. data/static/stubs/shoes-stub.exe +0 -0
  270. data/static/tutor-back.png +0 -0
  271. data/test/shoes_test.rb +8 -0
  272. data/test/test_helper.rb +25 -0
  273. data/use-deps +12 -0
  274. data/use-tmp-dep +8 -0
  275. metadata +509 -0
@@ -0,0 +1,1257 @@
1
+ //
2
+ // shoes/native-gtk.c
3
+ // GTK+ code for Shoes.
4
+ //
5
+ #include "shoes/app.h"
6
+ #include "shoes/ruby.h"
7
+ #include "shoes/config.h"
8
+ #include "shoes/world.h"
9
+ #include "shoes/native.h"
10
+ #include "shoes/internal.h"
11
+
12
+ #include <fontconfig/fontconfig.h>
13
+ #include <curl/curl.h>
14
+ #include <pthread.h>
15
+
16
+ #define GTK_CHILD(child, ptr) \
17
+ GList *children = gtk_container_get_children(GTK_CONTAINER(ptr)); \
18
+ child = children->data
19
+
20
+ #define HEIGHT_PAD 0
21
+
22
+ #define SHOES_GTK_INVISIBLE_CHAR (gunichar)0x2022
23
+
24
+ static VALUE
25
+ shoes_make_font_list(FcFontSet *fonts, VALUE ary)
26
+ {
27
+ int i = 0;
28
+ for (i = 0; i < fonts->nfont; i++)
29
+ {
30
+ FcValue val;
31
+ FcPattern *p = fonts->fonts[i];
32
+ if (FcPatternGet(p, FC_FAMILY, 0, &val) == FcResultMatch)
33
+ rb_ary_push(ary, rb_str_new2((char *)val.u.s));
34
+ }
35
+ rb_funcall(ary, rb_intern("uniq!"), 0);
36
+ rb_funcall(ary, rb_intern("sort!"), 0);
37
+ return ary;
38
+ }
39
+
40
+ VALUE
41
+ shoes_font_list()
42
+ {
43
+ VALUE ary = rb_ary_new();
44
+ FcConfig *fc = FcConfigGetCurrent();
45
+ FcFontSet *fonts = FcConfigGetFonts(fc, FcSetApplication);
46
+ if (fonts) shoes_make_font_list(fonts, ary);
47
+ fonts = FcConfigGetFonts(fc, FcSetSystem);
48
+ if (fonts) shoes_make_font_list(fonts, ary);
49
+ return ary;
50
+ }
51
+
52
+ VALUE
53
+ shoes_load_font(const char *filename)
54
+ {
55
+ FcConfig *fc = FcConfigGetCurrent();
56
+ FcFontSet *fonts = FcFontSetCreate();
57
+ if (!FcFileScan(fonts, NULL, NULL, NULL, (const FcChar8 *)filename, FcTrue))
58
+ return Qnil;
59
+
60
+ VALUE ary = rb_ary_new();
61
+ shoes_make_font_list(fonts, ary);
62
+ FcFontSetDestroy(fonts);
63
+
64
+ if (!FcConfigAppFontAddFile(fc, (const FcChar8 *)filename))
65
+ return Qnil;
66
+
67
+ // refresh the FONTS list
68
+ shoes_update_fonts(shoes_font_list());
69
+ return ary;
70
+ }
71
+
72
+ void shoes_native_init()
73
+ {
74
+ curl_global_init(CURL_GLOBAL_ALL);
75
+ gtk_init(NULL, NULL);
76
+ }
77
+
78
+ void shoes_native_cleanup(shoes_world_t *world)
79
+ {
80
+ curl_global_cleanup();
81
+ }
82
+
83
+ void shoes_native_quit()
84
+ {
85
+ gtk_main_quit();
86
+ }
87
+
88
+ void shoes_get_time(SHOES_TIME *ts)
89
+ {
90
+ clock_gettime(CLOCK_REALTIME, ts);
91
+ }
92
+
93
+ unsigned long shoes_diff_time(SHOES_TIME *start, SHOES_TIME *end)
94
+ {
95
+ unsigned long usec;
96
+ if ((end->tv_nsec-start->tv_nsec)<0) {
97
+ usec = (end->tv_sec-start->tv_sec - 1) * 1000;
98
+ usec += (1000000000 + end->tv_nsec - start->tv_nsec) / 1000000;
99
+ } else {
100
+ usec = (end->tv_sec - start->tv_sec) * 1000;
101
+ usec += (end->tv_nsec - start->tv_nsec) / 1000000;
102
+ }
103
+ return usec;
104
+ }
105
+
106
+ typedef struct {
107
+ unsigned int name;
108
+ VALUE obj;
109
+ void *data;
110
+ pthread_mutex_t mutex;
111
+ pthread_cond_t cond;
112
+ int ret;
113
+ } shoes_gtk_msg;
114
+
115
+ static gboolean
116
+ shoes_gtk_catch_message(gpointer user) {
117
+ shoes_gtk_msg *msg = (shoes_gtk_msg *)user;
118
+ pthread_mutex_lock(&msg->mutex);
119
+ msg->ret = shoes_catch_message(msg->name, msg->obj, msg->data);
120
+ pthread_cond_signal(&msg->cond);
121
+ pthread_mutex_unlock(&msg->mutex);
122
+ return FALSE;
123
+ }
124
+
125
+ int shoes_throw_message(unsigned int name, VALUE obj, void *data)
126
+ {
127
+ int ret;
128
+ shoes_gtk_msg *msg = SHOE_ALLOC(shoes_gtk_msg);
129
+ msg->name = name;
130
+ msg->obj = obj;
131
+ msg->data = data;
132
+ pthread_mutex_init(&msg->mutex, NULL);
133
+ pthread_cond_init(&msg->cond, NULL);
134
+ msg->ret = 0;
135
+
136
+ pthread_mutex_lock(&msg->mutex);
137
+ g_idle_add_full(G_PRIORITY_DEFAULT, shoes_gtk_catch_message, msg, NULL);
138
+ pthread_cond_wait(&msg->cond, &msg->mutex);
139
+ ret = msg->ret;
140
+ pthread_mutex_unlock(&msg->mutex);
141
+
142
+ free(msg);
143
+ return ret;
144
+ }
145
+
146
+ void shoes_native_slot_mark(SHOES_SLOT_OS *slot) {}
147
+ void shoes_native_slot_reset(SHOES_SLOT_OS *slot) {}
148
+ void shoes_native_slot_clear(shoes_canvas *canvas)
149
+ {
150
+ if (canvas->slot->vscroll)
151
+ {
152
+ GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(canvas->slot->vscroll));
153
+ gtk_adjustment_set_value(adj, adj->lower);
154
+ }
155
+ }
156
+
157
+ void shoes_native_slot_paint(SHOES_SLOT_OS *slot)
158
+ {
159
+ gtk_widget_queue_draw(slot->canvas);
160
+ }
161
+
162
+ void shoes_native_slot_lengthen(SHOES_SLOT_OS *slot, int height, int endy)
163
+ {
164
+ if (slot->vscroll)
165
+ {
166
+ GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(slot->vscroll));
167
+ if (adj->upper != (gdouble)endy)
168
+ {
169
+ gtk_range_set_range(GTK_RANGE(slot->vscroll), 0., (gdouble)endy);
170
+ if (adj->page_size >= adj->upper)
171
+ gtk_widget_hide(slot->vscroll);
172
+ else
173
+ gtk_widget_show(slot->vscroll);
174
+ }
175
+ }
176
+ }
177
+
178
+ void shoes_native_slot_scroll_top(SHOES_SLOT_OS *slot)
179
+ {
180
+ if (slot->vscroll)
181
+ gtk_range_set_value(GTK_RANGE(slot->vscroll), slot->scrolly);
182
+ }
183
+
184
+ int shoes_native_slot_gutter(SHOES_SLOT_OS *slot)
185
+ {
186
+ if (slot->vscroll)
187
+ {
188
+ GtkRequisition req;
189
+ gtk_widget_size_request(slot->vscroll, &req);
190
+ return req.width;
191
+ }
192
+ return 0;
193
+ }
194
+
195
+ void shoes_native_remove_item(SHOES_SLOT_OS *slot, VALUE item, char c)
196
+ {
197
+ }
198
+
199
+ //
200
+ // Window-level events
201
+ //
202
+ static gboolean
203
+ shoes_app_gtk_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data)
204
+ {
205
+ GdkModifierType state;
206
+ shoes_app *app = (shoes_app *)data;
207
+ if (!event->is_hint)
208
+ {
209
+ shoes_canvas *canvas;
210
+ Data_Get_Struct(app->canvas, shoes_canvas, canvas);
211
+ state = (GdkModifierType)event->state;
212
+ shoes_app_motion(app, (int)event->x, (int)event->y + canvas->slot->scrolly);
213
+ }
214
+ return TRUE;
215
+ }
216
+
217
+ static gboolean
218
+ shoes_app_gtk_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
219
+ {
220
+ shoes_app *app = (shoes_app *)data;
221
+ shoes_canvas *canvas;
222
+ Data_Get_Struct(app->canvas, shoes_canvas, canvas);
223
+ if (event->type == GDK_BUTTON_PRESS)
224
+ {
225
+ shoes_app_click(app, event->button, event->x, event->y + canvas->slot->scrolly);
226
+ }
227
+ else if (event->type == GDK_BUTTON_RELEASE)
228
+ {
229
+ shoes_app_release(app, event->button, event->x, event->y + canvas->slot->scrolly);
230
+ }
231
+ return TRUE;
232
+ }
233
+
234
+ static gboolean
235
+ shoes_app_gtk_wheel(GtkWidget *widget, GdkEventScroll *event, gpointer data)
236
+ {
237
+ ID wheel;
238
+ shoes_app *app = (shoes_app *)data;
239
+ switch (event->direction)
240
+ {
241
+ case GDK_SCROLL_UP: wheel = s_up; break;
242
+ case GDK_SCROLL_DOWN: wheel = s_down; break;
243
+ case GDK_SCROLL_LEFT: wheel = s_left; break;
244
+ case GDK_SCROLL_RIGHT: wheel = s_right; break;
245
+ default: return TRUE;
246
+ }
247
+
248
+ shoes_app_wheel(app, wheel, event->x, event->y);
249
+ return TRUE;
250
+ }
251
+
252
+ static void
253
+ shoes_app_gtk_paint (GtkWidget *widget, GdkEventExpose *event, gpointer data)
254
+ {
255
+ shoes_app *app = (shoes_app *)data;
256
+ gtk_window_get_size(GTK_WINDOW(app->os.window), &app->width, &app->height);
257
+ shoes_canvas_size(app->canvas, app->width, app->height);
258
+ }
259
+
260
+ #define KEY_SYM(name, sym) \
261
+ else if (event->keyval == GDK_##name) \
262
+ v = ID2SYM(rb_intern("" # sym))
263
+
264
+ static gboolean
265
+ shoes_app_gtk_keypress (GtkWidget *widget, GdkEventKey *event, gpointer data)
266
+ {
267
+ VALUE v = Qnil;
268
+ guint modifiers = event->state;
269
+ shoes_app *app = (shoes_app *)data;
270
+ if (event->keyval == GDK_Return)
271
+ {
272
+ v = rb_str_new2("\n");
273
+ }
274
+ KEY_SYM(Escape, escape);
275
+ else if (event->length > 0)
276
+ {
277
+ if ((event->state & GDK_CONTROL_MASK) || (event->state & GDK_MOD1_MASK))
278
+ {
279
+ gint len;
280
+ gunichar ch;
281
+ char chbuf[7] = {0};
282
+
283
+ ch = gdk_keyval_to_unicode(event->keyval);
284
+ len = g_unichar_to_utf8(ch, chbuf);
285
+ chbuf[len] = '\0';
286
+
287
+ v = ID2SYM(rb_intern(chbuf));
288
+ if (modifiers & GDK_SHIFT_MASK) modifiers ^= GDK_SHIFT_MASK;
289
+ }
290
+ else
291
+ {
292
+ if (event->string[0] == '\r' && event->length == 1)
293
+ v = rb_str_new2("\n");
294
+ else
295
+ v = rb_str_new(event->string, event->length);
296
+ }
297
+ }
298
+ KEY_SYM(Insert, insert);
299
+ KEY_SYM(Delete, delete);
300
+ KEY_SYM(BackSpace, backspace);
301
+ KEY_SYM(Tab, tab);
302
+ KEY_SYM(ISO_Left_Tab, tab);
303
+ KEY_SYM(Page_Up, page_up);
304
+ KEY_SYM(Page_Down, page_down);
305
+ KEY_SYM(Home, home);
306
+ KEY_SYM(End, end);
307
+ KEY_SYM(Left, left);
308
+ KEY_SYM(Up, up);
309
+ KEY_SYM(Right, right);
310
+ KEY_SYM(Down, down);
311
+ KEY_SYM(F1, f1);
312
+ KEY_SYM(F2, f2);
313
+ KEY_SYM(F3, f3);
314
+ KEY_SYM(F4, f4);
315
+ KEY_SYM(F5, f5);
316
+ KEY_SYM(F6, f6);
317
+ KEY_SYM(F7, f7);
318
+ KEY_SYM(F8, f8);
319
+ KEY_SYM(F9, f9);
320
+ KEY_SYM(F10, f10);
321
+ KEY_SYM(F11, f11);
322
+ KEY_SYM(F12, f12);
323
+
324
+ if (v != Qnil) {
325
+ if (event->type == GDK_KEY_PRESS) {
326
+ shoes_app_keydown(app, v);
327
+
328
+ if (event->keyval == GDK_Return)
329
+ if ((event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) != 0)
330
+ v = ID2SYM(rb_intern("enter"));
331
+
332
+ if (SYMBOL_P(v))
333
+ {
334
+ if (modifiers & GDK_MOD1_MASK)
335
+ KEY_STATE(alt);
336
+ if (modifiers & GDK_SHIFT_MASK)
337
+ KEY_STATE(shift);
338
+ if (modifiers & GDK_CONTROL_MASK)
339
+ KEY_STATE(control);
340
+ }
341
+
342
+ shoes_app_keypress(app, v);
343
+ } else {
344
+ shoes_app_keyup(app, v);
345
+ }
346
+ }
347
+
348
+ return FALSE;
349
+ }
350
+
351
+ static gboolean
352
+ shoes_app_gtk_quit(GtkWidget *widget, GdkEvent *event, gpointer data)
353
+ {
354
+ shoes_app *app = (shoes_app *)data;
355
+ if (shoes_app_remove(app))
356
+ gtk_main_quit();
357
+ return FALSE;
358
+ }
359
+
360
+ static void
361
+ shoes_canvas_gtk_paint_children(GtkWidget *widget, gpointer data)
362
+ {
363
+ shoes_canvas *canvas = (shoes_canvas *)data;
364
+ gtk_container_propagate_expose(GTK_CONTAINER(canvas->slot->canvas), widget, canvas->slot->expose);
365
+ }
366
+
367
+ static void
368
+ shoes_canvas_gtk_paint(GtkWidget *widget, GdkEventExpose *event, gpointer data)
369
+ {
370
+ VALUE c = (VALUE)data;
371
+ shoes_canvas *canvas;
372
+ INFO("EXPOSE: (%d, %d) (%d, %d) %lu, %d, %d\n", event->area.x, event->area.y,
373
+ event->area.width, event->area.height, c, (int)event->send_event, event->count);
374
+ Data_Get_Struct(c, shoes_canvas, canvas);
375
+
376
+ //
377
+ // Since I'm using a GtkFixed container, I need to force it to be clipped on its boundaries.
378
+ // This could be done by using a whole lot of gdk_window_begin_paint_region calls, but that
379
+ // would also mean masking every region for every element... This approach is simple. Clip
380
+ // the expose region and pass it on.
381
+ //
382
+ canvas->slot->expose = event;
383
+ GdkRegion *region = event->region;
384
+ GdkRectangle rect = event->area;
385
+ event->region = gdk_region_rectangle(&canvas->slot->canvas->allocation);
386
+ gdk_region_intersect(event->region, region);
387
+ gdk_region_get_clipbox(event->region, &event->area);
388
+
389
+ shoes_canvas_paint(c);
390
+ gtk_container_forall(GTK_CONTAINER(widget), shoes_canvas_gtk_paint_children, canvas);
391
+
392
+ //
393
+ // Restore the full region to the event.
394
+ //
395
+ gdk_region_destroy(event->region);
396
+ event->region = region;
397
+ event->area = rect;
398
+ canvas->slot->expose = NULL;
399
+ }
400
+
401
+ static void
402
+ shoes_canvas_gtk_size(GtkWidget *widget, GtkAllocation *size, gpointer data)
403
+ {
404
+ VALUE c = (VALUE)data;
405
+ shoes_canvas *canvas;
406
+ Data_Get_Struct(c, shoes_canvas, canvas);
407
+ if (canvas->slot->vscroll &&
408
+ (size->height != canvas->slot->scrollh || size->width != canvas->slot->scrollw))
409
+ {
410
+ GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(canvas->slot->vscroll));
411
+ gtk_widget_set_size_request(canvas->slot->vscroll, -1, size->height);
412
+ gtk_fixed_move(GTK_FIXED(canvas->slot->canvas), canvas->slot->vscroll,
413
+ size->width - canvas->slot->vscroll->allocation.width, 0);
414
+ adj->page_size = size->height;
415
+ adj->page_increment = size->height - 32;
416
+ if (adj->page_size >= adj->upper)
417
+ gtk_widget_hide(canvas->slot->vscroll);
418
+ else
419
+ gtk_widget_show(canvas->slot->vscroll);
420
+ canvas->slot->scrollh = size->height;
421
+ canvas->slot->scrollw = size->width;
422
+ }
423
+ }
424
+
425
+ static void
426
+ shoes_canvas_gtk_scroll(GtkRange *r, gpointer data)
427
+ {
428
+ VALUE c = (VALUE)data;
429
+ shoes_canvas *canvas;
430
+ Data_Get_Struct(c, shoes_canvas, canvas);
431
+ canvas->slot->scrolly = (int)gtk_range_get_value(r);
432
+ shoes_slot_repaint(canvas->app->slot);
433
+ }
434
+
435
+ static gint
436
+ shoes_app_g_poll (GPollFD *fds, guint nfds, gint timeout)
437
+ {
438
+ struct timeval tv;
439
+ fd_set rset, wset, xset;
440
+ GPollFD *f;
441
+ int ready;
442
+ int maxfd = 0;
443
+
444
+ FD_ZERO (&rset);
445
+ FD_ZERO (&wset);
446
+ FD_ZERO (&xset);
447
+
448
+ for (f = fds; f < &fds[nfds]; ++f)
449
+ if (f->fd >= 0)
450
+ {
451
+ if (f->events & G_IO_IN)
452
+ FD_SET (f->fd, &rset);
453
+ if (f->events & G_IO_OUT)
454
+ FD_SET (f->fd, &wset);
455
+ if (f->events & G_IO_PRI)
456
+ FD_SET (f->fd, &xset);
457
+ if (f->fd > maxfd && (f->events & (G_IO_IN|G_IO_OUT|G_IO_PRI)))
458
+ maxfd = f->fd;
459
+ }
460
+
461
+ //
462
+ // If we poll indefinitely, then the window updates will
463
+ // pile up for as long as Ruby is churning away.
464
+ //
465
+ // Give Ruby half-seconds in which to work, in order to
466
+ // keep it from completely blocking the GUI.
467
+ //
468
+ if (timeout == -1 || timeout > 500)
469
+ timeout = 500;
470
+
471
+ tv.tv_sec = timeout / 1000;
472
+ tv.tv_usec = (timeout % 1000) * 1000;
473
+
474
+ ready = rb_thread_select (maxfd + 1, &rset, &wset, &xset, &tv);
475
+ if (ready > 0)
476
+ for (f = fds; f < &fds[nfds]; ++f)
477
+ {
478
+ f->revents = 0;
479
+ if (f->fd >= 0)
480
+ {
481
+ if (FD_ISSET (f->fd, &rset))
482
+ f->revents |= G_IO_IN;
483
+ if (FD_ISSET (f->fd, &wset))
484
+ f->revents |= G_IO_OUT;
485
+ if (FD_ISSET (f->fd, &xset))
486
+ f->revents |= G_IO_PRI;
487
+ }
488
+ }
489
+
490
+ return ready;
491
+ }
492
+
493
+ shoes_code
494
+ shoes_app_cursor(shoes_app *app, ID cursor)
495
+ {
496
+ if (app->os.window == NULL || app->os.window->window == NULL || app->cursor == cursor)
497
+ goto done;
498
+
499
+ GdkCursor *c;
500
+ if (cursor == s_hand || cursor == s_link)
501
+ {
502
+ c = gdk_cursor_new(GDK_HAND2);
503
+ }
504
+ else if (cursor == s_arrow)
505
+ {
506
+ c = gdk_cursor_new(GDK_ARROW);
507
+ }
508
+ else if (cursor == s_text)
509
+ {
510
+ c = gdk_cursor_new(GDK_XTERM);
511
+ }
512
+ else
513
+ goto done;
514
+
515
+ gdk_window_set_cursor(app->os.window->window, c);
516
+
517
+ app->cursor = cursor;
518
+
519
+ done:
520
+ return SHOES_OK;
521
+ }
522
+
523
+ void
524
+ shoes_native_app_resized(shoes_app *app)
525
+ {
526
+ if (app->os.window != NULL)
527
+ gtk_widget_set_size_request(app->os.window, app->width, app->height);
528
+ }
529
+
530
+ void
531
+ shoes_native_app_title(shoes_app *app, char *msg)
532
+ {
533
+ gtk_window_set_title(GTK_WINDOW(app->os.window), _(msg));
534
+ }
535
+
536
+ void
537
+ shoes_native_app_fullscreen(shoes_app *app, char yn)
538
+ {
539
+ gtk_window_set_keep_above(GTK_WINDOW(app->os.window), (gboolean)yn);
540
+ if (yn)
541
+ gtk_window_fullscreen(GTK_WINDOW(app->os.window));
542
+ else
543
+ gtk_window_unfullscreen(GTK_WINDOW(app->os.window));
544
+ }
545
+
546
+ shoes_code
547
+ shoes_native_app_open(shoes_app *app, char *path, int dialog)
548
+ {
549
+ char icon_path[SHOES_BUFSIZE];
550
+ shoes_app_gtk *gk = &app->os;
551
+
552
+ sprintf(icon_path, "%s/static/app-icon.png", shoes_world->path);
553
+ gtk_window_set_default_icon_from_file(icon_path, NULL);
554
+ gk->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
555
+ gtk_window_set_position(GTK_WINDOW(gk->window), GTK_WIN_POS_CENTER);
556
+ if (app->minwidth < app->width || app->minheight < app->height)
557
+ {
558
+ GdkGeometry hints;
559
+ hints.min_width = app->minwidth;
560
+ hints.min_height = app->minheight;
561
+ gtk_window_set_geometry_hints(GTK_WINDOW(gk->window), NULL,
562
+ &hints, GDK_HINT_MIN_SIZE);
563
+ }
564
+ if (!app->resizable)
565
+ gtk_window_set_resizable(GTK_WINDOW(gk->window), FALSE);
566
+ if (app->fullscreen)
567
+ shoes_native_app_fullscreen(app, 1);
568
+ gtk_widget_set_events(gk->window, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
569
+ g_signal_connect(G_OBJECT(gk->window), "size-allocate",
570
+ G_CALLBACK(shoes_app_gtk_paint), app);
571
+ g_signal_connect(G_OBJECT(gk->window), "motion-notify-event",
572
+ G_CALLBACK(shoes_app_gtk_motion), app);
573
+ g_signal_connect(G_OBJECT(gk->window), "button-press-event",
574
+ G_CALLBACK(shoes_app_gtk_button), app);
575
+ g_signal_connect(G_OBJECT(gk->window), "button-release-event",
576
+ G_CALLBACK(shoes_app_gtk_button), app);
577
+ g_signal_connect(G_OBJECT(gk->window), "scroll-event",
578
+ G_CALLBACK(shoes_app_gtk_wheel), app);
579
+ g_signal_connect(G_OBJECT(gk->window), "key-press-event",
580
+ G_CALLBACK(shoes_app_gtk_keypress), app);
581
+ g_signal_connect(G_OBJECT(gk->window), "key-release-event",
582
+ G_CALLBACK(shoes_app_gtk_keypress), app);
583
+ g_signal_connect(G_OBJECT(gk->window), "delete-event",
584
+ G_CALLBACK(shoes_app_gtk_quit), app);
585
+ app->slot->canvas = gk->window;
586
+ return SHOES_OK;
587
+ }
588
+
589
+ void
590
+ shoes_native_app_show(shoes_app *app)
591
+ {
592
+ gtk_widget_show_all(app->os.window);
593
+ }
594
+
595
+ void
596
+ shoes_native_loop()
597
+ {
598
+ g_main_set_poll_func(shoes_app_g_poll);
599
+ gtk_main();
600
+ }
601
+
602
+ void
603
+ shoes_native_app_close(shoes_app *app)
604
+ {
605
+ shoes_app_gtk_quit(app->os.window, NULL, (gpointer)app);
606
+ gtk_widget_destroy(app->os.window);
607
+ app->os.window = NULL;
608
+ }
609
+
610
+ void
611
+ shoes_browser_open(char *url)
612
+ {
613
+ VALUE browser = rb_str_new2("/etc/alternatives/x-www-browser '");
614
+ rb_str_cat2(browser, url);
615
+ rb_str_cat2(browser, "' 2>/dev/null &");
616
+ shoes_sys(RSTRING_PTR(browser), 1);
617
+ }
618
+
619
+ void
620
+ shoes_slot_init(VALUE c, SHOES_SLOT_OS *parent, int x, int y, int width, int height, int scrolls, int toplevel)
621
+ {
622
+ shoes_canvas *canvas;
623
+ SHOES_SLOT_OS *slot;
624
+ Data_Get_Struct(c, shoes_canvas, canvas);
625
+
626
+ slot = shoes_slot_alloc(canvas, parent, toplevel);
627
+ slot->canvas = gtk_fixed_new();
628
+ INFO("shoes_slot_init(%lu)\n", c);
629
+ g_signal_connect(G_OBJECT(slot->canvas), "expose-event",
630
+ G_CALLBACK(shoes_canvas_gtk_paint), (gpointer)c);
631
+ g_signal_connect(G_OBJECT(slot->canvas), "size-allocate",
632
+ G_CALLBACK(shoes_canvas_gtk_size), (gpointer)c);
633
+ if (toplevel)
634
+ gtk_container_add(GTK_CONTAINER(parent->canvas), slot->canvas);
635
+ else
636
+ gtk_fixed_put(GTK_FIXED(parent->canvas), slot->canvas, x, y);
637
+
638
+ slot->scrollh = slot->scrollw = 0;
639
+ slot->vscroll = NULL;
640
+ if (scrolls)
641
+ {
642
+ slot->vscroll = gtk_vscrollbar_new(NULL);
643
+ gtk_range_get_adjustment(GTK_RANGE(slot->vscroll))->step_increment = 16;
644
+ gtk_range_get_adjustment(GTK_RANGE(slot->vscroll))->page_increment = height - 32;
645
+ g_signal_connect(G_OBJECT(slot->vscroll), "value-changed",
646
+ G_CALLBACK(shoes_canvas_gtk_scroll), (gpointer)c);
647
+ gtk_fixed_put(GTK_FIXED(slot->canvas), slot->vscroll, -100, -100);
648
+ }
649
+
650
+ gtk_widget_set_size_request(slot->canvas, width, height);
651
+ slot->expose = NULL;
652
+ if (toplevel) shoes_canvas_size(c, width, height);
653
+ else
654
+ {
655
+ gtk_widget_show_all(slot->canvas);
656
+ canvas->width = 100;
657
+ canvas->height = 100;
658
+ }
659
+ }
660
+
661
+ void
662
+ shoes_slot_destroy(shoes_canvas *canvas, shoes_canvas *pc)
663
+ {
664
+ if (canvas->slot->vscroll)
665
+ gtk_container_remove(GTK_CONTAINER(canvas->slot->canvas), canvas->slot->vscroll);
666
+ gtk_container_remove(GTK_CONTAINER(pc->slot->canvas), canvas->slot->canvas);
667
+ }
668
+
669
+ cairo_t *
670
+ shoes_cairo_create(shoes_canvas *canvas)
671
+ {
672
+ cairo_t *cr = gdk_cairo_create(canvas->slot->canvas->window);
673
+ if (canvas->slot->expose != NULL)
674
+ {
675
+ GdkRegion *region = gdk_region_rectangle(&canvas->slot->canvas->allocation);
676
+ gdk_region_intersect(region, canvas->slot->expose->region);
677
+ gdk_cairo_region(cr, region);
678
+ gdk_region_destroy(region);
679
+ cairo_clip(cr);
680
+ cairo_translate(cr, canvas->slot->canvas->allocation.x, canvas->slot->canvas->allocation.y - canvas->slot->scrolly);
681
+ }
682
+ return cr;
683
+ }
684
+
685
+ void
686
+ shoes_cairo_destroy(shoes_canvas *canvas)
687
+ {
688
+ }
689
+
690
+ void
691
+ shoes_group_clear(SHOES_GROUP_OS *group)
692
+ {
693
+ group->radios = NULL;
694
+ group->layout = NULL;
695
+ }
696
+
697
+ void
698
+ shoes_native_canvas_place(shoes_canvas *self_t, shoes_canvas *pc)
699
+ {
700
+ int x, y, newy;
701
+ GtkAllocation *a = &self_t->slot->canvas->allocation;
702
+ gtk_widget_translate_coordinates(self_t->slot->canvas, pc->slot->canvas, 0, 0, &x, &y);
703
+ newy = (self_t->place.iy + self_t->place.dy) - pc->slot->scrolly;
704
+
705
+ if (x != self_t->place.ix + self_t->place.dx || y != newy)
706
+ gtk_fixed_move(GTK_FIXED(pc->slot->canvas), self_t->slot->canvas,
707
+ self_t->place.ix + self_t->place.dx, newy);
708
+
709
+ if (a->width != self_t->place.iw || a->height != self_t->place.ih)
710
+ gtk_widget_set_size_request(self_t->slot->canvas, self_t->place.iw, self_t->place.ih);
711
+ }
712
+
713
+ void
714
+ shoes_native_canvas_resize(shoes_canvas *canvas)
715
+ {
716
+ }
717
+
718
+ static void
719
+ shoes_widget_changed(GtkWidget *ref, gpointer data)
720
+ {
721
+ VALUE self = (VALUE)data;
722
+ shoes_control_send(self, s_change);
723
+ }
724
+
725
+ void
726
+ shoes_native_control_hide(SHOES_CONTROL_REF ref)
727
+ {
728
+ gtk_widget_hide(ref);
729
+ }
730
+
731
+ void
732
+ shoes_native_control_show(SHOES_CONTROL_REF ref)
733
+ {
734
+ gtk_widget_show(ref);
735
+ }
736
+
737
+ void
738
+ shoes_native_control_position(SHOES_CONTROL_REF ref, shoes_place *p1, VALUE self,
739
+ shoes_canvas *canvas, shoes_place *p2)
740
+ {
741
+ PLACE_COORDS();
742
+ gtk_widget_set_size_request(ref, p2->iw, p2->ih);
743
+ gtk_fixed_put(GTK_FIXED(canvas->slot->canvas),
744
+ ref, p2->ix + p2->dx, p2->iy + p2->dy);
745
+ gtk_widget_show_all(ref);
746
+ }
747
+
748
+ void
749
+ shoes_native_control_repaint(SHOES_CONTROL_REF ref, shoes_place *p1,
750
+ shoes_canvas *canvas, shoes_place *p2)
751
+ {
752
+ p2->iy -= canvas->slot->scrolly;
753
+ if (CHANGED_COORDS()) {
754
+ PLACE_COORDS();
755
+ gtk_fixed_move(GTK_FIXED(canvas->slot->canvas),
756
+ ref, p2->ix + p2->dx, p2->iy + p2->dy);
757
+ gtk_widget_set_size_request(ref, p2->iw, p2->ih);
758
+ }
759
+ p2->iy += canvas->slot->scrolly;
760
+ }
761
+
762
+ void
763
+ shoes_native_control_focus(SHOES_CONTROL_REF ref)
764
+ {
765
+ if (GTK_WIDGET_CAN_FOCUS(ref)) gtk_widget_grab_focus(ref);
766
+ }
767
+
768
+ void
769
+ shoes_native_control_state(SHOES_CONTROL_REF ref, gboolean sensitive, gboolean setting)
770
+ {
771
+ gtk_widget_set_sensitive(ref, sensitive);
772
+ if (GTK_IS_EDITABLE(ref))
773
+ gtk_editable_set_editable(GTK_EDITABLE(ref), setting);
774
+ else if (GTK_IS_SCROLLED_WINDOW(ref))
775
+ {
776
+ GtkWidget *textview;
777
+ GTK_CHILD(textview, ref);
778
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), setting);
779
+ }
780
+ }
781
+
782
+ void
783
+ shoes_native_control_remove(SHOES_CONTROL_REF ref, shoes_canvas *canvas)
784
+ {
785
+ gtk_container_remove(GTK_CONTAINER(canvas->slot->canvas), ref);
786
+ }
787
+
788
+ void
789
+ shoes_native_control_free(SHOES_CONTROL_REF ref)
790
+ {
791
+ //
792
+ // no need to free gtk widgets, since gtk seems
793
+ // to garbage collect them fine. and memory
794
+ // addresses often get reused.
795
+ //
796
+ }
797
+
798
+ SHOES_SURFACE_REF
799
+ shoes_native_surface_new(shoes_canvas *canvas, VALUE self, shoes_place *place)
800
+ {
801
+ return gtk_drawing_area_new();
802
+ }
803
+
804
+ void
805
+ shoes_native_surface_position(SHOES_SURFACE_REF ref, shoes_place *p1,
806
+ VALUE self, shoes_canvas *canvas, shoes_place *p2)
807
+ {
808
+ shoes_native_control_position(ref, p1, self, canvas, p2);
809
+ }
810
+
811
+ void
812
+ shoes_native_surface_hide(SHOES_SURFACE_REF ref)
813
+ {
814
+ shoes_native_control_hide(ref);
815
+ }
816
+
817
+ void
818
+ shoes_native_surface_show(SHOES_SURFACE_REF ref)
819
+ {
820
+ shoes_native_control_show(ref);
821
+ }
822
+
823
+ void
824
+ shoes_native_surface_remove(shoes_canvas *canvas, SHOES_SURFACE_REF ref)
825
+ {
826
+ gtk_container_remove(GTK_CONTAINER(canvas->slot->canvas), ref);
827
+ }
828
+
829
+ static gboolean
830
+ shoes_button_gtk_clicked(GtkButton *button, gpointer data)
831
+ {
832
+ VALUE self = (VALUE)data;
833
+ shoes_control_send(self, s_click);
834
+ return TRUE;
835
+ }
836
+
837
+ SHOES_CONTROL_REF
838
+ shoes_native_button(VALUE self, shoes_canvas *canvas, shoes_place *place, char *msg)
839
+ {
840
+ SHOES_CONTROL_REF ref = gtk_button_new_with_label(_(msg));
841
+ g_signal_connect(G_OBJECT(ref), "clicked",
842
+ G_CALLBACK(shoes_button_gtk_clicked),
843
+ (gpointer)self);
844
+ return ref;
845
+ }
846
+
847
+ void
848
+ shoes_native_secrecy(SHOES_CONTROL_REF ref)
849
+ {
850
+ gtk_entry_set_visibility(GTK_ENTRY(ref), FALSE);
851
+ gtk_entry_set_invisible_char(GTK_ENTRY(ref), SHOES_GTK_INVISIBLE_CHAR);
852
+ }
853
+
854
+ SHOES_CONTROL_REF
855
+ shoes_native_edit_line(VALUE self, shoes_canvas *canvas, shoes_place *place, VALUE attr, char *msg)
856
+ {
857
+ SHOES_CONTROL_REF ref = gtk_entry_new();
858
+ if (RTEST(ATTR(attr, secret))) shoes_native_secrecy(ref);
859
+ gtk_entry_set_text(GTK_ENTRY(ref), _(msg));
860
+ g_signal_connect(G_OBJECT(ref), "changed",
861
+ G_CALLBACK(shoes_widget_changed),
862
+ (gpointer)self);
863
+ return ref;
864
+ }
865
+
866
+ VALUE
867
+ shoes_native_edit_line_get_text(SHOES_CONTROL_REF ref)
868
+ {
869
+ return rb_str_new2(gtk_entry_get_text(GTK_ENTRY(ref)));
870
+ }
871
+
872
+ void
873
+ shoes_native_edit_line_set_text(SHOES_CONTROL_REF ref, char *msg)
874
+ {
875
+ gtk_entry_set_text(GTK_ENTRY(ref), _(msg));
876
+ }
877
+
878
+ SHOES_CONTROL_REF
879
+ shoes_native_edit_box(VALUE self, shoes_canvas *canvas, shoes_place *place, VALUE attr, char *msg)
880
+ {
881
+ GtkTextBuffer *buffer;
882
+ GtkWidget* textview = gtk_text_view_new();
883
+ SHOES_CONTROL_REF ref = gtk_scrolled_window_new(NULL, NULL);
884
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD);
885
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
886
+ gtk_text_buffer_set_text(buffer, _(msg), -1);
887
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ref),
888
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
889
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ref), GTK_SHADOW_IN);
890
+ gtk_container_add(GTK_CONTAINER(ref), textview);
891
+ g_signal_connect(G_OBJECT(buffer), "changed",
892
+ G_CALLBACK(shoes_widget_changed),
893
+ (gpointer)self);
894
+ return ref;
895
+ }
896
+
897
+ VALUE
898
+ shoes_native_edit_box_get_text(SHOES_CONTROL_REF ref)
899
+ {
900
+ GtkWidget *textview;
901
+ GTK_CHILD(textview, ref);
902
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
903
+ GtkTextIter begin, end;
904
+ gtk_text_buffer_get_bounds(buffer, &begin, &end);
905
+ return rb_str_new2(gtk_text_buffer_get_text(buffer, &begin, &end, TRUE));
906
+ }
907
+
908
+ void
909
+ shoes_native_edit_box_set_text(SHOES_CONTROL_REF ref, char *msg)
910
+ {
911
+ GtkWidget *textview;
912
+ GTK_CHILD(textview, ref);
913
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
914
+ gtk_text_buffer_set_text(buffer, _(msg), -1);
915
+ }
916
+
917
+ SHOES_CONTROL_REF
918
+ shoes_native_list_box(VALUE self, shoes_canvas *canvas, shoes_place *place, VALUE attr, char *msg)
919
+ {
920
+ SHOES_CONTROL_REF ref = gtk_combo_box_new_text();
921
+ g_signal_connect(G_OBJECT(ref), "changed",
922
+ G_CALLBACK(shoes_widget_changed),
923
+ (gpointer)self);
924
+ return ref;
925
+ }
926
+
927
+ void
928
+ shoes_native_list_box_update(SHOES_CONTROL_REF combo, VALUE ary)
929
+ {
930
+ long i;
931
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))));
932
+ for (i = 0; i < RARRAY_LEN(ary); i++)
933
+ {
934
+ VALUE msg = shoes_native_to_s(rb_ary_entry(ary, i));
935
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _(RSTRING_PTR(msg)));
936
+ }
937
+ }
938
+
939
+ VALUE
940
+ shoes_native_list_box_get_active(SHOES_CONTROL_REF ref, VALUE items)
941
+ {
942
+ int sel = gtk_combo_box_get_active(GTK_COMBO_BOX(ref));
943
+ if (sel >= 0)
944
+ return rb_ary_entry(items, sel);
945
+ return Qnil;
946
+ }
947
+
948
+ void
949
+ shoes_native_list_box_set_active(SHOES_CONTROL_REF combo, VALUE ary, VALUE item)
950
+ {
951
+ int idx = rb_ary_index_of(ary, item);
952
+ if (idx < 0) return;
953
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo), idx);
954
+ }
955
+
956
+ SHOES_CONTROL_REF
957
+ shoes_native_progress(VALUE self, shoes_canvas *canvas, shoes_place *place, VALUE attr, char *msg)
958
+ {
959
+ SHOES_CONTROL_REF ref = gtk_progress_bar_new();
960
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ref), _(msg));
961
+ return ref;
962
+ }
963
+
964
+ double
965
+ shoes_native_progress_get_fraction(SHOES_CONTROL_REF ref)
966
+ {
967
+ return gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(ref));
968
+ }
969
+
970
+ void
971
+ shoes_native_progress_set_fraction(SHOES_CONTROL_REF ref, double perc)
972
+ {
973
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ref), perc);
974
+ }
975
+
976
+ SHOES_CONTROL_REF
977
+ shoes_native_slider(VALUE self, shoes_canvas *canvas, shoes_place *place, VALUE attr, char *msg)
978
+ {
979
+ SHOES_CONTROL_REF ref = gtk_hscale_new_with_range(0., 1., 0.01);
980
+ gtk_scale_set_draw_value(GTK_SCALE(ref), FALSE);
981
+ g_signal_connect(G_OBJECT(ref), "value-changed",
982
+ G_CALLBACK(shoes_widget_changed), (gpointer)self);
983
+ return ref;
984
+ }
985
+
986
+ double
987
+ shoes_native_slider_get_fraction(SHOES_CONTROL_REF ref)
988
+ {
989
+ return gtk_range_get_value(GTK_RANGE(ref));
990
+ }
991
+
992
+ void
993
+ shoes_native_slider_set_fraction(SHOES_CONTROL_REF ref, double perc)
994
+ {
995
+ gtk_range_set_value(GTK_RANGE(ref), perc);
996
+ }
997
+
998
+ SHOES_CONTROL_REF
999
+ shoes_native_check(VALUE self, shoes_canvas *canvas, shoes_place *place, VALUE attr, char *msg)
1000
+ {
1001
+ SHOES_CONTROL_REF ref = gtk_check_button_new();
1002
+ g_signal_connect(G_OBJECT(ref), "clicked",
1003
+ G_CALLBACK(shoes_button_gtk_clicked),
1004
+ (gpointer)self);
1005
+ return ref;
1006
+ }
1007
+
1008
+ VALUE
1009
+ shoes_native_check_get(SHOES_CONTROL_REF ref)
1010
+ {
1011
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ref)) ? Qtrue : Qfalse;
1012
+ }
1013
+
1014
+ void
1015
+ shoes_native_check_set(SHOES_CONTROL_REF ref, int on)
1016
+ {
1017
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ref), on ? TRUE : FALSE);
1018
+ }
1019
+
1020
+ SHOES_CONTROL_REF
1021
+ shoes_native_radio(VALUE self, shoes_canvas *canvas, shoes_place *place, VALUE attr, VALUE group)
1022
+ {
1023
+ SHOES_CONTROL_REF ref;
1024
+ GSList *list = NULL;
1025
+ if (!NIL_P(group))
1026
+ {
1027
+ shoes_control *lctrl;
1028
+ VALUE leader = rb_ary_entry(group, 0);
1029
+ Data_Get_Struct(leader, shoes_control, lctrl);
1030
+ list = gtk_radio_button_get_group(GTK_RADIO_BUTTON(lctrl->ref));
1031
+ }
1032
+ ref = gtk_radio_button_new(list);
1033
+ g_signal_connect(G_OBJECT(ref), "clicked",
1034
+ G_CALLBACK(shoes_button_gtk_clicked),
1035
+ (gpointer)self);
1036
+ return ref;
1037
+ }
1038
+
1039
+ static gboolean
1040
+ shoes_gtk_animate(gpointer data)
1041
+ {
1042
+ VALUE timer = (VALUE)data;
1043
+ shoes_timer *self_t;
1044
+ Data_Get_Struct(timer, shoes_timer, self_t);
1045
+ if (self_t->started == ANIM_STARTED)
1046
+ shoes_timer_call(timer);
1047
+ return self_t->started == ANIM_STARTED;
1048
+ }
1049
+
1050
+ void
1051
+ shoes_native_timer_remove(shoes_canvas *canvas, SHOES_TIMER_REF ref)
1052
+ {
1053
+ g_source_remove(ref);
1054
+ }
1055
+
1056
+ SHOES_TIMER_REF
1057
+ shoes_native_timer_start(VALUE self, shoes_canvas *canvas, unsigned int interval)
1058
+ {
1059
+ return g_timeout_add(interval, shoes_gtk_animate, (gpointer)self);
1060
+ }
1061
+
1062
+ VALUE
1063
+ shoes_native_clipboard_get(shoes_app *app)
1064
+ {
1065
+ GtkClipboard *primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1066
+ if (gtk_clipboard_wait_is_text_available(primary))
1067
+ {
1068
+ gchar *string = gtk_clipboard_wait_for_text(primary);
1069
+ return rb_str_new2(string);
1070
+ }
1071
+ return Qnil;
1072
+ }
1073
+
1074
+ void
1075
+ shoes_native_clipboard_set(shoes_app *app, VALUE string)
1076
+ {
1077
+ GtkClipboard *primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1078
+ gtk_clipboard_set_text(primary, RSTRING_PTR(string), RSTRING_LEN(string));
1079
+ }
1080
+
1081
+ VALUE
1082
+ shoes_native_to_s(VALUE text)
1083
+ {
1084
+ text = rb_funcall(text, s_to_s, 0);
1085
+ return text;
1086
+ }
1087
+
1088
+ VALUE
1089
+ shoes_native_window_color(shoes_app *app)
1090
+ {
1091
+ GtkStyle *style = gtk_widget_get_style(GTK_WIDGET(APP_WINDOW(app)));
1092
+ GdkColor bg = style->bg[GTK_STATE_NORMAL];
1093
+ return shoes_color_new(bg.red / 257, bg.green / 257, bg.blue / 257 , SHOES_COLOR_OPAQUE);
1094
+ }
1095
+
1096
+ VALUE
1097
+ shoes_native_dialog_color(shoes_app *app)
1098
+ {
1099
+ GtkStyle *style = gtk_widget_get_style(GTK_WIDGET(APP_WINDOW(app)));
1100
+ GdkColor bg = style->bg[GTK_STATE_NORMAL];
1101
+ return shoes_color_new(bg.red / 257, bg.green / 257, bg.blue / 257 , SHOES_COLOR_OPAQUE);
1102
+ }
1103
+
1104
+ VALUE
1105
+ shoes_dialog_alert(VALUE self, VALUE msg)
1106
+ {
1107
+ GLOBAL_APP(app);
1108
+ msg = shoes_native_to_s(msg);
1109
+ GtkWidget *dialog = gtk_message_dialog_new_with_markup(
1110
+ APP_WINDOW(app), GTK_DIALOG_MODAL,
1111
+ GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "<span size='larger'>%s</span>\n\n%s",
1112
+ _(dialog_title_says), RSTRING_PTR(msg));
1113
+ gtk_dialog_run(GTK_DIALOG(dialog));
1114
+ gtk_widget_destroy(dialog);
1115
+ return Qnil;
1116
+ }
1117
+
1118
+ VALUE
1119
+ shoes_dialog_ask(int argc, VALUE *argv, VALUE self)
1120
+ {
1121
+ rb_arg_list args;
1122
+ VALUE answer = Qnil;
1123
+ rb_parse_args(argc, argv, "s|h", &args);
1124
+ GLOBAL_APP(app);
1125
+ GtkWidget *dialog = gtk_dialog_new_with_buttons(_(dialog_title),
1126
+ APP_WINDOW(app), GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1127
+ gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
1128
+ gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 6);
1129
+ GtkWidget *question = gtk_label_new(RSTRING_PTR(args.a[0]));
1130
+ gtk_misc_set_alignment(GTK_MISC(question), 0, 0);
1131
+ GtkWidget *_answer = gtk_entry_new();
1132
+ if (RTEST(ATTR(args.a[1], secret))) shoes_native_secrecy(_answer);
1133
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), question, FALSE, FALSE, 3);
1134
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), _answer, FALSE, TRUE, 3);
1135
+ gtk_widget_show_all(dialog);
1136
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
1137
+ if (result == GTK_RESPONSE_OK)
1138
+ {
1139
+ const gchar *txt = gtk_entry_get_text(GTK_ENTRY(_answer));
1140
+ answer = rb_str_new2(txt);
1141
+ }
1142
+ gtk_widget_destroy(dialog);
1143
+ return answer;
1144
+ }
1145
+
1146
+ VALUE
1147
+ shoes_dialog_confirm(VALUE self, VALUE quiz)
1148
+ {
1149
+ VALUE answer = Qfalse;
1150
+ GLOBAL_APP(app);
1151
+ quiz = shoes_native_to_s(quiz);
1152
+ GtkWidget *dialog = gtk_dialog_new_with_buttons(_(dialog_title),
1153
+ APP_WINDOW(app), GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1154
+ gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
1155
+ gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 6);
1156
+ GtkWidget *question = gtk_label_new(RSTRING_PTR(quiz));
1157
+ gtk_misc_set_alignment(GTK_MISC(question), 0, 0);
1158
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), question, FALSE, FALSE, 3);
1159
+ gtk_widget_show_all(dialog);
1160
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
1161
+ if (result == GTK_RESPONSE_OK)
1162
+ answer = Qtrue;
1163
+ gtk_widget_destroy(dialog);
1164
+ return answer;
1165
+ }
1166
+
1167
+ VALUE
1168
+ shoes_dialog_color(VALUE self, VALUE title)
1169
+ {
1170
+ VALUE color = Qnil;
1171
+ GLOBAL_APP(app);
1172
+ title = shoes_native_to_s(title);
1173
+ GtkWidget *dialog = gtk_color_selection_dialog_new(RSTRING_PTR(title));
1174
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
1175
+ if (result == GTK_RESPONSE_OK)
1176
+ {
1177
+ GdkColor _color;
1178
+ gtk_color_selection_get_current_color(
1179
+ GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel),
1180
+ &_color);
1181
+ color = shoes_color_new(_color.red/256, _color.green/256, _color.blue/256, SHOES_COLOR_OPAQUE);
1182
+ }
1183
+ gtk_widget_destroy(dialog);
1184
+ return color;
1185
+ }
1186
+
1187
+ static VALUE
1188
+ shoes_dialog_chooser(VALUE self, char *title, GtkFileChooserAction act, const gchar *button, VALUE attr)
1189
+ {
1190
+ VALUE path = Qnil;
1191
+ GLOBAL_APP(app);
1192
+ GtkWidget *dialog = gtk_file_chooser_dialog_new(title, APP_WINDOW(app),
1193
+ act, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, button, GTK_RESPONSE_ACCEPT, NULL);
1194
+ if (act == GTK_FILE_CHOOSER_ACTION_SAVE)
1195
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1196
+ if(RTEST(shoes_hash_get(attr, rb_intern("save"))))
1197
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
1198
+ RSTRING_PTR(shoes_hash_get(attr, rb_intern("save"))));
1199
+ if(RTEST(shoes_hash_get(attr, rb_intern("types"))) && TYPE(shoes_hash_get(attr, rb_intern("types"))) == T_HASH) {
1200
+ VALUE hsh = shoes_hash_get(attr, rb_intern("types"));
1201
+ VALUE keys = rb_funcall(hsh, s_keys, 0);
1202
+ int i;
1203
+ for(i = 0; i < RARRAY_LEN(keys); i++) {
1204
+ VALUE key = rb_ary_entry(keys, i);
1205
+ VALUE val = rb_hash_aref(hsh, key);
1206
+ GtkFileFilter *ff = gtk_file_filter_new();
1207
+ gtk_file_filter_set_name(ff, RSTRING_PTR(key));
1208
+ gtk_file_filter_add_pattern(ff, RSTRING_PTR(val));
1209
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), ff);
1210
+ }
1211
+ }
1212
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
1213
+ if (result == GTK_RESPONSE_ACCEPT)
1214
+ {
1215
+ char *filename;
1216
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1217
+ path = rb_str_new2(filename);
1218
+ }
1219
+ gtk_widget_destroy(dialog);
1220
+ return path;
1221
+ }
1222
+
1223
+ VALUE
1224
+ shoes_dialog_open(int argc, VALUE *argv, VALUE self)
1225
+ {
1226
+ rb_arg_list args;
1227
+ rb_parse_args(argc, argv, "|h", &args);
1228
+ return shoes_dialog_chooser(self, "Open file...", GTK_FILE_CHOOSER_ACTION_OPEN,
1229
+ GTK_STOCK_OPEN, args.a[0]);
1230
+ }
1231
+
1232
+ VALUE
1233
+ shoes_dialog_save(int argc, VALUE *argv, VALUE self)
1234
+ {
1235
+ rb_arg_list args;
1236
+ rb_parse_args(argc, argv, "|h", &args);
1237
+ return shoes_dialog_chooser(self, "Save file...", GTK_FILE_CHOOSER_ACTION_SAVE,
1238
+ GTK_STOCK_SAVE, args.a[0]);
1239
+ }
1240
+
1241
+ VALUE
1242
+ shoes_dialog_open_folder(int argc, VALUE *argv, VALUE self)
1243
+ {
1244
+ rb_arg_list args;
1245
+ rb_parse_args(argc, argv, "|h", &args);
1246
+ return shoes_dialog_chooser(self, "Open folder...", GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1247
+ GTK_STOCK_OPEN, args.a[0]);
1248
+ }
1249
+
1250
+ VALUE
1251
+ shoes_dialog_save_folder(int argc, VALUE *argv, VALUE self)
1252
+ {
1253
+ rb_arg_list args;
1254
+ rb_parse_args(argc, argv, "|h", &args);
1255
+ return shoes_dialog_chooser(self, "Save folder...", GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
1256
+ GTK_STOCK_SAVE, args.a[0]);
1257
+ }