geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

gb.c
Go to the documentation of this file.
1/*
2 * gb.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2005 The Geany contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/*
22 * A small Pong-like.
23 */
24
25#include "utils.h"
26
27#include <gtk/gtk.h>
28
29
30#define AREA_SIZE 300
31#define BALL_SIZE 4
32#define BORDER_THIKNESS 4
33#define HANDLE_THIKNESS 4
34#define HANDLE_SHRINK 3
35
36
37#define GEANY_TYPE_PONG (geany_pong_get_type())
38#define GEANY_PONG(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GEANY_TYPE_PONG, GeanyPong))
39#define GEANY_IS_PONG(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GEANY_TYPE_PONG))
40
41
42typedef struct _GeanyPong GeanyPong;
44
46{
47 GtkDialog parent;
48 /* no need for private data as the whole thing is private */
49
50 GtkWidget *score_label;
51 GtkWidget *area;
52
55
57 gdouble ball_pos[2];
58 gdouble ball_vec[2];
61
62 guint score;
63
64 guint source_id;
65};
66
68{
69 GtkDialogClass parent_class;
70};
71
72
73static void geany_pong_finalize(GObject *obj);
74static void geany_pong_response(GtkDialog *self, gint response);
75static GType geany_pong_get_type(void) G_GNUC_CONST;
76
77
78G_DEFINE_TYPE(GeanyPong, geany_pong, GTK_TYPE_DIALOG)
79
80
81static void geany_pong_set_cairo_source_color(cairo_t *cr, GdkRGBA *c, gdouble a)
82{
83 cairo_set_source_rgba(cr, c->red, c->green, c->blue, MIN(c->alpha, a));
84}
85
86
87static gboolean geany_pong_area_draw(GtkWidget *area, cairo_t *cr, GeanyPong *self)
88{
89 /* we use the window style context because the area one has a transparent
90 * background and we want something to paint for the overlay */
91 GtkStyleContext *ctx = gtk_widget_get_style_context(GTK_WIDGET(self));
92 GtkStateFlags state = gtk_style_context_get_state(ctx);
93 GdkRGBA fg, bg;
94
95 gtk_style_context_get_color(ctx, state, &fg);
96 gtk_style_context_get_background_color(ctx, state, &bg);
97
98 self->area_width = gtk_widget_get_allocated_width(area);
99 self->area_height = gtk_widget_get_allocated_height(area);
100
101 cairo_set_line_width(cr, BORDER_THIKNESS);
102
103 /* draw the border */
104 cairo_rectangle(cr, BORDER_THIKNESS/2, BORDER_THIKNESS/2,
105 self->area_width - BORDER_THIKNESS, self->area_height /* we don't wanna see the bottom */);
106 geany_pong_set_cairo_source_color(cr, &fg, 1.0);
107 cairo_stroke(cr);
108
109 /* draw the handle */
110 cairo_rectangle(cr, self->handle_pos - self->handle_width/2, self->area_height - HANDLE_THIKNESS,
112 cairo_fill(cr);
113
114 /* draw the ball */
115 cairo_arc(cr, self->ball_pos[0], self->ball_pos[1], BALL_SIZE, 0, 2*G_PI);
116 cairo_fill(cr);
117
118 /* if not running, add an info */
119 if (! self->source_id || self->handle_width < 1)
120 {
121 PangoLayout *layout;
122 gint pw, ph;
123 gdouble scale;
124 PangoFontDescription *font = NULL;
125
126 geany_pong_set_cairo_source_color(cr, &bg, 0.8);
127 cairo_rectangle(cr, 0, 0, self->area_width, self->area_height);
128 cairo_paint(cr);
129
130 geany_pong_set_cairo_source_color(cr, &fg, 1.0);
131 layout = pango_cairo_create_layout(cr);
132
133 gtk_style_context_get(ctx, state, GTK_STYLE_PROPERTY_FONT, &font, NULL);
134 if (font)
135 {
136 pango_layout_set_font_description(layout, font);
137 pango_font_description_free(font);
138 }
139
140 if (! self->handle_width)
141 pango_layout_set_markup(layout, "<b>You won!</b>\n<small>OK, go back to work now.</small>", -1);
142 else
143 pango_layout_set_text(layout, "Click to Play", -1);
144 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
145 pango_layout_get_pixel_size(layout, &pw, &ph);
146
147 scale = MIN(0.9 * self->area_width / pw, 0.9 * self->area_height / ph);
148 cairo_move_to(cr, (self->area_width - pw * scale) / 2, (self->area_height - ph * scale) / 2);
149 cairo_scale(cr, scale, scale);
150 pango_cairo_show_layout(cr, layout);
151
152 g_object_unref(layout);
153 }
154
155 return TRUE;
156}
157
158
160{
161 self->ball_speed = 5;
162 self->ball_pos[0] = self->area_width / 2;
163 self->ball_pos[1] = self->area_height / 2;
164 self->ball_vec[0] = g_random_double_range(.2, .8);
165 self->ball_vec[1] = 1.0 - self->ball_vec[0];
166 if (g_random_boolean())
167 self->ball_vec[0] *= -1;
168}
169
170
172{
173 gchar buf[16];
174
175 g_snprintf(buf, sizeof buf, "%u", self->score);
176 gtk_label_set_text(GTK_LABEL(self->score_label), buf);
177}
178
179
180static gboolean geany_pong_area_timeout(gpointer data)
181{
182 GeanyPong *self = data;
183 const gdouble x = BORDER_THIKNESS + BALL_SIZE/2;
184 const gdouble y = BORDER_THIKNESS + BALL_SIZE/2;
185 const gdouble w = self->area_width - BORDER_THIKNESS - BALL_SIZE/2;
186 const gdouble h = self->area_height - HANDLE_THIKNESS - BALL_SIZE/2;
187 const gdouble old_ball_pos[2] = { self->ball_pos[0], self->ball_pos[1] };
188 const gdouble step[2] = { self->ball_speed * self->ball_vec[0],
189 self->ball_speed * self->ball_vec[1] };
190
191 /* left & right */
192 if (self->ball_pos[0] + step[0] >= w ||
193 self->ball_pos[0] + step[0] <= x)
194 self->ball_vec[0] = -self->ball_vec[0];
195 /* top */
196 if (self->ball_pos[1] + step[1] <= y)
197 self->ball_vec[1] = -self->ball_vec[1];
198 /* bottom */
199 if (self->ball_pos[1] + step[1] >= h)
200 {
201 if (self->ball_pos[0] + step[0] >= self->handle_pos - self->handle_width/2 &&
202 self->ball_pos[0] + step[0] <= self->handle_pos + self->handle_width/2 &&
203 /* only bounce *above* the handle, not below */
204 self->ball_pos[1] <= h)
205 {
206 self->score += self->ball_speed * 2;
208
209 self->ball_vec[1] = -self->ball_vec[1];
210 self->ball_speed++;
212 /* we don't allow a handle smaller than a shrink step */
213 if (self->handle_width < HANDLE_SHRINK)
214 {
215 self->handle_width = 0;
216 self->source_id = 0;
217 }
218 }
219 /* let the ball fall completely off before losing */
220 else if (self->ball_pos[1] + step[1] >= self->area_height + BALL_SIZE)
221 { /* lost! */
222 self->source_id = 0;
224 }
225 }
226
227 if (self->source_id)
228 {
229 self->ball_pos[0] += self->ball_speed * self->ball_vec[0];
230 self->ball_pos[1] += self->ball_speed * self->ball_vec[1];
231 }
232
233 if (! self->source_id)
234 {
235 /* we will draw a text all over, just invalidate everything */
236 gtk_widget_queue_draw(self->area);
237 }
238 else
239 {
240 /* compute the rough bounding box to redraw the ball */
241 const gint bb[4] = {
242 (gint) MIN(self->ball_pos[0], old_ball_pos[0]) - BALL_SIZE - 1,
243 (gint) MIN(self->ball_pos[1], old_ball_pos[1]) - BALL_SIZE - 1,
244 (gint) MAX(self->ball_pos[0], old_ball_pos[0]) + BALL_SIZE + 1,
245 (gint) MAX(self->ball_pos[1], old_ball_pos[1]) + BALL_SIZE + 1
246 };
247
248 gtk_widget_queue_draw_area(self->area, bb[0], bb[1], bb[2] - bb[0], bb[3] - bb[1]);
249 /* always redraw the handle in case it has moved */
250 gtk_widget_queue_draw_area(self->area,
253 }
254
255 return self->source_id != 0;
256}
257
258
259static gboolean geany_pong_area_button_press(GtkWidget *area, GdkEventButton *event, GeanyPong *self)
260{
261 if (event->type == GDK_BUTTON_PRESS &&
262 self->handle_width > 0)
263 {
264 if (! self->source_id)
265 self->source_id = g_timeout_add(1000/60, geany_pong_area_timeout, self);
266 else
267 {
268 g_source_remove(self->source_id);
269 self->source_id = 0;
270 }
271 gtk_widget_queue_draw(area);
272 return TRUE;
273 }
274
275 return FALSE;
276}
277
278
279static gboolean geany_pong_area_motion_notify(GtkWidget *area, GdkEventMotion *event, GeanyPong *self)
280{
281 self->handle_pos = (gint) event->x;
282 /* clamp so the handle is always fully in */
283 if (self->handle_pos < self->handle_width/2 + BORDER_THIKNESS)
284 self->handle_pos = self->handle_width/2 + BORDER_THIKNESS;
285 else if (self->handle_pos > self->area_width - self->handle_width/2 - BORDER_THIKNESS)
286 self->handle_pos = self->area_width - self->handle_width/2 - BORDER_THIKNESS;
287
288 return TRUE;
289}
290
291
293{
294 GObjectClass *object_class = G_OBJECT_CLASS(klass);
295 GtkDialogClass *dialog_class = GTK_DIALOG_CLASS(klass);
296
297 object_class->finalize = geany_pong_finalize;
298 dialog_class->response = geany_pong_response;
299}
300
301
302static void geany_pong_init(GeanyPong *self)
303{
304 GtkWidget *vbox;
305 GtkWidget *hbox;
306 GtkWidget *label;
307
308 self->score = 0;
309 self->source_id = 0;
310 self->area_height = AREA_SIZE;
311 self->area_width = AREA_SIZE;
312 self->handle_width = self->area_width / 2;
313 self->handle_pos = self->area_width / 2;
315
316 gtk_window_set_title(GTK_WINDOW(self), "Happy Easter!");
317 gtk_window_set_position(GTK_WINDOW(self), GTK_WIN_POS_CENTER_ON_PARENT);
318 gtk_window_set_destroy_with_parent(GTK_WINDOW(self), TRUE);
319 gtk_window_set_modal(GTK_WINDOW(self), TRUE);
320 gtk_window_set_skip_pager_hint(GTK_WINDOW(self), TRUE);
321 gtk_window_set_resizable(GTK_WINDOW(self), FALSE);
322
323 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
324 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
325 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self))), vbox, TRUE, TRUE, 0);
326
327 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
328 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
329
330 label = gtk_label_new("Score:");
331 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
332 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
333
334 self->score_label = gtk_label_new("0");
335 gtk_box_pack_start(GTK_BOX(hbox), self->score_label, FALSE, FALSE, 0);
336
337 self->area = gtk_drawing_area_new();
338 gtk_widget_add_events(self->area, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
339 g_signal_connect(self->area, "draw", G_CALLBACK(geany_pong_area_draw), self);
340 g_signal_connect(self->area, "button-press-event", G_CALLBACK(geany_pong_area_button_press), self);
341 g_signal_connect(self->area, "motion-notify-event", G_CALLBACK(geany_pong_area_motion_notify), self);
342 gtk_widget_set_size_request(self->area, AREA_SIZE, AREA_SIZE);
343 gtk_box_pack_start(GTK_BOX(vbox), self->area, TRUE, TRUE, 0);
344
345 gtk_dialog_add_buttons(GTK_DIALOG(self),
346 GTK_STOCK_HELP, GTK_RESPONSE_HELP,
347 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
348 NULL);
349 gtk_dialog_set_default_response(GTK_DIALOG(self), GTK_RESPONSE_HELP);
350 gtk_widget_grab_focus(gtk_dialog_get_widget_for_response(GTK_DIALOG(self), GTK_RESPONSE_HELP));
351
353}
354
355
356static void geany_pong_finalize(GObject *obj)
357{
358 GeanyPong *self = GEANY_PONG(obj);
359
360 if (self->source_id)
361 g_source_remove(self->source_id);
362
363 G_OBJECT_CLASS(geany_pong_parent_class)->finalize(obj);
364}
365
366
367static void geany_pong_help(GeanyPong *self)
368{
369 GtkWidget *dialog;
370 GtkWidget *vbox;
371 GtkWidget *scrolledwindow;
372 GtkWidget *textview;
373 GtkTextBuffer *buffer;
374
375 dialog = gtk_dialog_new_with_buttons("Help", GTK_WINDOW(self),
376 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
377 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
378 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
379 gtk_container_set_border_width(GTK_CONTAINER(dialog), 1);
380 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
381
382 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
383
384 scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
385 gtk_box_pack_start(GTK_BOX(vbox), scrolledwindow, TRUE, TRUE, 0);
386 gtk_container_set_border_width(GTK_CONTAINER(scrolledwindow), 5);
387 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
388 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_SHADOW_IN);
389
390 textview = gtk_text_view_new();
391 gtk_container_add(GTK_CONTAINER(scrolledwindow), textview);
392 gtk_widget_set_size_request(textview, 450, -1);
393 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
394 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD);
395 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview), FALSE);
396 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 2);
397 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 2);
398
399 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
400 gtk_text_buffer_set_text(buffer,
401 "A small Pong-like\n"
402 "\n"
403 "Click to start, and then bounce the ball off the walls without it "
404 "falling down the bottom edge. You control the bottom handle with "
405 "the mouse, but beware! the ball goes faster and faster and the "
406 "handle grows smaller and smaller!", -1);
407
409 gtk_dialog_run(GTK_DIALOG(dialog));
410 gtk_widget_destroy(dialog);
411}
412
413
414static void geany_pong_response(GtkDialog *self, gint response)
415{
416 g_return_if_fail(GEANY_IS_PONG(self));
417
418 switch (response)
419 {
420 case GTK_RESPONSE_HELP:
422 break;
423
424 default:
425 gtk_widget_destroy(GTK_WIDGET(self));
426 }
427}
428
429
430static GtkWidget *geany_pong_new(GtkWindow *parent)
431{
432 return g_object_new(GEANY_TYPE_PONG, "transient-for", parent, NULL);
433}
434
435
436static gboolean gb_on_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
437{
438 static gchar text[] = "geany";
439
440 if (event->keyval < 0x80)
441 {
442 memmove (text, &text[1], G_N_ELEMENTS(text) - 1);
443 text[G_N_ELEMENTS(text) - 2] = (gchar) event->keyval;
444
445 if (utils_str_equal(text, "geany"))
446 {
447 GtkWidget *pong = geany_pong_new(GTK_WINDOW(widget));
448 gtk_widget_show(pong);
449 return TRUE;
450 }
451 }
452
453 return FALSE;
454}
const gchar * label
Definition: build.c:2676
gchar * text
Definition: editor.c:83
static void geany_pong_reset_ball(GeanyPong *self)
Definition: gb.c:159
static void geany_pong_init(GeanyPong *self)
Definition: gb.c:302
static void geany_pong_help(GeanyPong *self)
Definition: gb.c:367
static gboolean geany_pong_area_button_press(GtkWidget *area, GdkEventButton *event, GeanyPong *self)
Definition: gb.c:259
#define BALL_SIZE
Definition: gb.c:31
#define GEANY_PONG(o)
Definition: gb.c:38
static GType geany_pong_get_type(void)
Definition: gb.c:75
static gboolean geany_pong_area_motion_notify(GtkWidget *area, GdkEventMotion *event, GeanyPong *self)
Definition: gb.c:279
#define HANDLE_SHRINK
Definition: gb.c:34
static void geany_pong_class_init(GeanyPongClass *klass)
Definition: gb.c:292
static GtkWidget * geany_pong_new(GtkWindow *parent)
Definition: gb.c:430
#define GEANY_IS_PONG(o)
Definition: gb.c:39
static void geany_pong_finalize(GObject *obj)
Definition: gb.c:356
#define HANDLE_THIKNESS
Definition: gb.c:33
#define GEANY_TYPE_PONG
Definition: gb.c:37
static gboolean gb_on_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
Definition: gb.c:436
static void geany_pong_update_score(GeanyPong *self)
Definition: gb.c:171
#define BORDER_THIKNESS
Definition: gb.c:32
static void geany_pong_response(GtkDialog *self, gint response)
Definition: gb.c:414
static gboolean geany_pong_area_timeout(gpointer data)
Definition: gb.c:180
#define AREA_SIZE
Definition: gb.c:30
static gboolean geany_pong_area_draw(GtkWidget *area, cairo_t *cr, GeanyPong *self)
Definition: gb.c:87
#define MAX(a, b)
Definition: mio.c:93
#define NULL
Definition: rbtree.h:150
if(!stash_group_load_from_file(group, filename)) g_warning(_("Could not load keyfile %s!")
GtkWidget * dialog
gtk_container_add(GTK_CONTAINER(dialog->vbox), check_button)
gtk_widget_show_all(dialog)
GtkDialogClass parent_class
Definition: gb.c:69
Definition: gb.c:46
gdouble ball_pos[2]
Definition: gb.c:57
gint area_width
Definition: gb.c:54
guint ball_speed
Definition: gb.c:56
gdouble ball_vec[2]
Definition: gb.c:58
GtkDialog parent
Definition: gb.c:47
GtkWidget * area
Definition: gb.c:51
gint handle_pos
Definition: gb.c:60
gint handle_width
Definition: gb.c:59
gint area_height
Definition: gb.c:53
GtkWidget * score_label
Definition: gb.c:50
guint score
Definition: gb.c:62
guint source_id
Definition: gb.c:64
gboolean utils_str_equal(const gchar *a, const gchar *b)
NULL-safe string comparison.
Definition: utils.c:599
General utility functions, non-GTK related.