source: trunk/src/gtk/glmapview.cpp @ 166

Last change on this file since 166 was 166, checked in by sam, 10 years ago

Keyboard scrolling in the editor.

  • Property svn:keywords set to Id
File size: 8.4 KB
Line 
1//
2// Deus Hax (working title)
3// Copyright (c) 2010 Sam Hocevar <sam@hocevar.net>
4//
5
6#if defined HAVE_CONFIG_H
7#   include "config.h"
8#endif
9
10#include <gtk/gtk.h>
11#include <gtkgl/gtkglarea.h>
12#include <gdk/gdkkeysyms.h>
13
14#include "core.h"
15#include "glmapview.h"
16
17static float const FPS = 30.0f;
18
19GlMapView::GlMapView(GtkBuilder *builder)
20  : hadj(GTK_ADJUSTMENT(gtk_builder_get_object(builder, "gl_hadj"))),
21    vadj(GTK_ADJUSTMENT(gtk_builder_get_object(builder, "gl_vadj"))),
22    ticking(FALSE), panning(FALSE),
23    mapviewer(0),
24    xpan(0.0), ypan(0.0)
25{
26    /* Create new OpenGL widget */
27    int attrlist[] =
28    {
29        GDK_GL_RGBA,
30        GDK_GL_RED_SIZE, 1,
31        GDK_GL_GREEN_SIZE, 1,
32        GDK_GL_BLUE_SIZE, 1,
33        GDK_GL_DOUBLEBUFFER,
34        GDK_GL_NONE
35    };
36
37    glarea = gtk_gl_area_new(attrlist);
38    gtk_widget_set_usize(glarea, 400, 300);
39    gtk_widget_set_events(glarea, GDK_EXPOSURE_MASK |
40                                  GDK_POINTER_MOTION_MASK |
41                                  GDK_BUTTON_PRESS_MASK |
42                                  GDK_BUTTON_RELEASE_MASK);
43    gtk_widget_set_can_focus(glarea, TRUE);
44
45    GtkContainer *cont = GTK_CONTAINER(gtk_builder_get_object(builder,
46                                                              "gl_container"));
47    gtk_container_add(cont, glarea);
48
49    /* We tick from the idle function instead of a timeout to avoid
50     * stealing time from the GTK loop when the callback time exceeds
51     * the timeout value. */
52    g_idle_add((GSourceFunc)IdleTickSignal, this);
53
54    gtk_signal_connect(GTK_OBJECT(glarea), "realize",
55                       GTK_SIGNAL_FUNC(SetupSignal), this);
56    gtk_signal_connect(GTK_OBJECT(glarea), "destroy",
57                       GTK_SIGNAL_FUNC(DestroySignal), this);
58    gtk_signal_connect(GTK_OBJECT(glarea), "expose_event",
59                       GTK_SIGNAL_FUNC(DrawSignal), this);
60    gtk_signal_connect(GTK_OBJECT(glarea), "configure_event",
61                       GTK_SIGNAL_FUNC(ReshapeSignal), this);
62
63    gtk_signal_connect(GTK_OBJECT(glarea), "button_press_event",
64                       GTK_SIGNAL_FUNC(MouseButtonSignal), this);
65    gtk_signal_connect(GTK_OBJECT(glarea), "button_release_event",
66                       GTK_SIGNAL_FUNC(MouseButtonSignal), this);
67    gtk_signal_connect(GTK_OBJECT(glarea), "motion_notify_event",
68                       GTK_SIGNAL_FUNC(MouseMotionSignal), this);
69
70    gtk_signal_connect(GTK_OBJECT(glarea), "key_press_event",
71                       GTK_SIGNAL_FUNC(KeyPressSignal), this);
72}
73
74void GlMapView::LoadMap(char const *path)
75{
76    // FIXME: detect when the map viewer is killed
77    mapviewer = new MapViewer(path);
78
79    UpdateAdjustments();
80}
81
82void GlMapView::SetFocus()
83{
84    gtk_widget_grab_focus(glarea);
85}
86
87gboolean GlMapView::IdleTick()
88{
89    // FIXME: do not do anything if the previous tick was too recent?
90    ticking = TRUE;
91
92    if (mapviewer)
93        mapviewer->SetPOV(gtk_adjustment_get_value(hadj),
94                          gtk_adjustment_get_value(vadj));
95
96    /* Tick the game */
97    Ticker::TickGame();
98
99    gtk_widget_draw(GTK_WIDGET(glarea), NULL);
100
101    return TRUE;
102}
103
104gboolean GlMapView::Setup()
105{
106    /* Set up display */
107    if (gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
108        Video::Setup(glarea->allocation.width, glarea->allocation.height);
109
110    UpdateAdjustments();
111
112    return TRUE;
113}
114
115gboolean GlMapView::Destroy()
116{
117    g_idle_remove_by_data(this);
118    return TRUE;
119}
120
121gboolean GlMapView::Draw(GdkEventExpose *event)
122{
123    if (event->count > 0)
124        return TRUE;
125
126    /* OpenGL functions can be called only if make_current returns true */
127    if (ticking && gtk_gl_area_make_current(GTK_GL_AREA(glarea)))
128    {
129        ticking = FALSE;
130
131        /* Clear the screen, tick the renderer, show the frame and
132         * clamp to desired framerate */
133        Video::Clear();
134        Ticker::TickDraw();
135        gtk_gl_area_swapbuffers(GTK_GL_AREA(glarea));
136        while (g_main_context_iteration(NULL, FALSE))
137            ;
138        Ticker::ClampFps(1000.0f / FPS);
139    }
140
141    return TRUE;
142}
143
144gboolean GlMapView::UpdateAdjustments()
145{
146    float w = mapviewer ? mapviewer->GetWidth() : glarea->allocation.width;
147    float h = mapviewer ? mapviewer->GetHeight() : glarea->allocation.height;
148
149    /* Manage adjustments */
150    struct { GtkAdjustment *adj; float map_size, sw_size; } s[2] =
151    {
152        { hadj, w, glarea->allocation.width },
153        { vadj, h, glarea->allocation.height },
154    };
155
156    for (int i = 0; i < 2; i++)
157    {
158        gtk_adjustment_set_lower(s[i].adj, 0);
159        gtk_adjustment_set_upper(s[i].adj, s[i].map_size);
160        gtk_adjustment_set_step_increment(s[i].adj, 1);
161        gtk_adjustment_set_page_increment(s[i].adj, s[i].sw_size);
162        gtk_adjustment_set_page_size(s[i].adj, s[i].sw_size);
163
164        float val = gtk_adjustment_get_value(s[i].adj);
165        if (val + s[i].sw_size > s[i].map_size)
166        {
167            gtk_adjustment_set_value(s[i].adj,
168                                     s[i].map_size - s[i].sw_size);
169            gtk_adjustment_value_changed(s[i].adj);
170        }
171    }
172
173    return TRUE;
174}
175
176gboolean GlMapView::MoveAdjustments(double dx, double dy)
177{
178    if (dx)
179        gtk_adjustment_set_value(hadj, gtk_adjustment_get_value(hadj) + dx);
180
181    if (dy)
182        gtk_adjustment_set_value(vadj, gtk_adjustment_get_value(vadj) + dy);
183
184    UpdateAdjustments();
185
186    return TRUE;
187}
188
189gboolean GlMapView::MouseButton(GdkEventButton *event)
190{
191    if (event->type == GDK_BUTTON_PRESS && event->button == 2)
192    {
193        panning = TRUE;
194        xpan = event->x;
195        ypan = event->y;
196        GdkCursor *cursor = gdk_cursor_new(GDK_HAND1);
197        gdk_window_set_cursor(glarea->window, cursor);
198        gdk_cursor_unref(cursor);
199        return FALSE;
200    }
201    else if (event->type == GDK_BUTTON_RELEASE && event->button == 2)
202    {
203        panning = FALSE;
204        gdk_window_set_cursor(glarea->window, NULL);
205        return FALSE;
206    }
207
208    return TRUE;
209}
210
211gboolean GlMapView::MouseMotion(GdkEventMotion *event)
212{
213    if (panning)
214    {
215        if (event->x != xpan)
216        {
217            double val = gtk_adjustment_get_value(hadj);
218            int map_width = mapviewer->GetWidth();
219            val += xpan - event->x;
220            xpan = event->x;
221            if (val + glarea->allocation.width > map_width)
222                val = map_width - glarea->allocation.width;
223            gtk_adjustment_set_value(hadj, val);
224            gtk_adjustment_value_changed(hadj);
225        }
226
227        if (event->y != ypan)
228        {
229            double val = gtk_adjustment_get_value(vadj);
230            int map_height = mapviewer->GetHeight();
231            val += ypan - event->y;
232            ypan = event->y;
233            if (val + glarea->allocation.height > map_height)
234                val = map_height - glarea->allocation.height;
235            gtk_adjustment_set_value(vadj, val);
236            gtk_adjustment_value_changed(vadj);
237        }
238    }
239
240    return TRUE;
241}
242
243gboolean GlMapView::KeyPress(GdkEventKey *event)
244{
245    switch (event->keyval)
246    {
247    case GDK_Up:    MoveAdjustments(  0.0, -10.0); break;
248    case GDK_Down:  MoveAdjustments(  0.0,  10.0); break;
249    case GDK_Left:  MoveAdjustments(-10.0,  0.0); break;
250    case GDK_Right: MoveAdjustments( 10.0,  0.0); break;
251    default: return FALSE;
252    }
253
254    return TRUE;
255}
256
257/* Private signal slots */
258gboolean GlMapView::IdleTickSignal(GlMapView *that)
259{
260    return that->IdleTick();
261}
262
263gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that)
264{
265    (void)w;
266    return that->Setup();
267}
268
269gboolean GlMapView::DestroySignal(GtkWidget *w, GlMapView *that)
270{
271    (void)w;
272    return that->Destroy();
273}
274
275gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *event,
276                               GlMapView *that)
277{
278    (void)w;
279    return that->Draw(event);
280}
281
282gboolean GlMapView::ReshapeSignal(GtkWidget *w, GdkEventConfigure *event,
283                                  GlMapView *that)
284{
285    (void)w;
286    (void)event;
287    return that->Setup();
288}
289
290gboolean GlMapView::MouseButtonSignal(GtkWidget *w, GdkEventButton *event,
291                                      GlMapView *that)
292{
293    (void)w;
294    return that->MouseButton(event);
295}
296
297gboolean GlMapView::MouseMotionSignal(GtkWidget *w, GdkEventMotion *event,
298                                      GlMapView *that)
299{
300    (void)w;
301    return that->MouseMotion(event);
302}
303
304gboolean GlMapView::KeyPressSignal(GtkWidget *w, GdkEventKey *event,
305                                   GlMapView *that)
306{
307    (void)w;
308    return that->KeyPress(event);
309}
310
Note: See TracBrowser for help on using the repository browser.