implemented search
[belltower] / belltower.c
1 /*
2  * belltower
3  * an app to find belltowers under Maemo 5
4  *
5  * Copyright (c) 2009 Thomas Thurman <tthurman@gnome.org>
6  * Released under the GPL
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <glib.h>
13 #include <hildon/hildon.h>
14 #include <gtk/gtk.h>
15 #include <location/location-gps-device.h>
16 #include <location/location-distance-utils.h>
17 #include <dbus/dbus-glib.h>
18
19 #define MAX_FIELDS 50
20
21 GtkWidget *window;
22
23 typedef enum {
24   /** stop scanning the database */
25   FILTER_STOP,
26   /** ignore this one */
27   FILTER_IGNORE,
28   /** add this one to the list */
29   FILTER_ACCEPT
30 } FilterResult;
31
32 /*
33   FIXME:
34   We should really do this by looking at the header row of the table.
35   They might decide to put in new columns some day.
36 */
37 typedef enum {
38   FieldPrimaryKey,
39   FieldNationalGrid,
40   FieldAccRef,
41   FieldSNLat,
42   FieldSNLong,
43   FieldPostcode,
44   FieldTowerBase,
45   FieldCounty,
46   FieldCountry,
47   FieldDiocese,
48   FieldPlace,
49   FieldPlace2,
50   FieldPlaceCL,
51   FieldDedication,
52   FieldBells,
53   FieldWt,
54   FieldApp,
55   FieldNote,
56   FieldHz,
57   FieldDetails,
58   FieldGF,
59   FieldToilet,
60   FieldUR,
61   FieldPDNo,
62   FieldPracticeNight,
63   FieldPSt,
64   FieldPrXF,
65   FieldOvhaulYr,
66   FieldContractor,
67   FieldExtraInfo,
68   FieldWebPage,
69   FieldUpdated,
70   FieldAffiliations,
71   FieldAltName,
72   FieldLat,
73   FieldLong,
74   FieldSimulator
75 } field;
76
77 typedef struct {
78   int serial;
79
80   /* the raw data */
81
82   char* fields[MAX_FIELDS];
83   int n_fields;
84 } tower;
85
86 /*
87  * we're going to pretend you're in Helsinki
88  * until I get the GPS working
89  */
90 double current_lat = 60.161790;
91 double current_long = 23.924902;
92
93 static void
94 show_message (char *message)
95 {
96   HildonNote* note = HILDON_NOTE
97     (hildon_note_new_information (GTK_WINDOW (window),
98                                   message?message:
99                                   "Some message was supposed to be here."));
100   gtk_dialog_run (GTK_DIALOG (note));
101   gtk_widget_destroy (GTK_WIDGET (note));
102 }
103
104 static gchar*
105 distance_to_tower (tower *details)
106 {
107   char *endptr;
108   double tower_lat;
109   double tower_long;
110   double km_distance;
111   const double km_to_miles = 1.609344;
112
113   tower_lat = strtod(details->fields[FieldLat], &endptr);
114   if (*endptr) return g_strdup ("unknown");
115   tower_long = strtod(details->fields[FieldLong], &endptr);
116   if (*endptr) return g_strdup ("unknown");
117
118   km_distance = location_distance_between (current_lat,
119                                            current_long,
120                                            tower_lat,
121                                            tower_long);
122
123   return g_strdup_printf("%dmi", (int) (km_distance / km_to_miles));
124 }
125
126 static void
127 call_dbus (DBusBusType type,
128            char *name,
129            char *path,
130            char *interface,
131            char *method,
132            char *parameter)
133 {
134   DBusGConnection *connection;
135   GError *error = NULL;
136
137   DBusGProxy *proxy;
138
139   connection = dbus_g_bus_get (type,
140                                &error);
141   if (connection == NULL)
142     {
143       show_message (error->message);
144       g_error_free (error);
145       return;
146     }
147
148   proxy = dbus_g_proxy_new_for_name (connection, name, path, interface);
149
150   error = NULL;
151   if (!dbus_g_proxy_call (proxy, method, &error,
152                           G_TYPE_STRING, parameter,
153                           G_TYPE_INVALID,
154                           G_TYPE_INVALID))
155     {
156       show_message (error->message);
157       g_error_free (error);
158     }
159 }
160
161 static void
162 show_browser (gchar *url)
163 {
164   call_dbus (DBUS_BUS_SESSION,
165              "com.nokia.osso_browser",
166              "/com/nokia/osso_browser/request",
167              "com.nokia.osso_browser",
168              "load_url",
169              url);
170 }
171
172 typedef FilterResult (*ParseDoveCallback)(tower *details, gpointer data);
173 typedef void (*ButtonCallback)(void);
174
175 GtkWidget *tower_window, *buttons, *tower_table;
176 HildonAppMenu *menu;
177
178 static void
179 add_table_field (char *name,
180                  char *value)
181 {
182   int row;
183   GtkLabel *label;
184   gchar *str;
185
186   g_object_get(tower_table, "n-rows", &row);
187
188   row++;
189
190   gtk_table_resize (GTK_TABLE (tower_table), row, 2);
191
192   label = GTK_LABEL (gtk_label_new (NULL));
193   str = g_strdup_printf("<b>%s</b>", name);
194   gtk_label_set_markup (label, str);
195   g_free (str);
196   gtk_label_set_justify (label, GTK_JUSTIFY_RIGHT);
197   gtk_table_attach_defaults (GTK_TABLE (tower_table),
198                              GTK_WIDGET (label),
199                              0, 1, row, row+1);
200
201   label = GTK_LABEL (gtk_label_new (value));
202   gtk_label_set_justify (label, GTK_JUSTIFY_LEFT);
203   gtk_table_attach_defaults (GTK_TABLE (tower_table),
204                              GTK_WIDGET (label),
205                              1, 2, row, row+1);
206 }
207
208 static void
209 add_button (char *label,
210             ButtonCallback callback)
211 {
212   GtkWidget *button;
213
214   button = gtk_button_new_with_label (label);
215   g_signal_connect (button, "clicked", G_CALLBACK (callback), NULL);
216   hildon_app_menu_append (menu, GTK_BUTTON (button));
217   button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
218                                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
219                                         label, NULL);
220   g_signal_connect (button, "clicked", G_CALLBACK (callback), NULL);
221   gtk_box_pack_end (GTK_BOX (buttons), button, FALSE, FALSE, 0);
222 }
223
224
225 static void
226 bookmark_toggled (GtkButton *button,
227                   gpointer dummy)
228 {
229   show_message ("Bookmarks are not yet implemented.");
230 }
231
232 char *tower_website = NULL;
233 char *tower_map = NULL;
234 char *tower_directions = NULL;
235 char *peals_list = NULL;
236
237 static void
238 show_tower_website (void)
239 {
240   show_browser (tower_website);
241 }
242
243 static void
244 show_tower_map (void)
245 {
246   show_browser (tower_map);
247 }
248
249 static void
250 show_peals_list (void)
251 {
252   show_browser (peals_list);
253 }
254
255 static FilterResult
256 get_countries_cb (tower *details,
257                   gpointer data)
258 {
259   GHashTable *hash = (GHashTable *)data;
260
261   if (details->serial==0)
262     return TRUE; /* header row */
263
264   if (!g_hash_table_lookup_extended (hash,
265                                     details->fields[FieldCountry],
266                                      NULL, NULL))
267     {
268       g_hash_table_insert (hash,
269                            g_strdup(details->fields[FieldCountry]),
270                            g_strdup (details->fields[FieldCountry]));
271     }
272
273   return FILTER_IGNORE;
274 }
275
276 typedef struct {
277   GHashTable *hash;
278   gchar *country_name;
279 } country_cb_data;
280
281 typedef struct {
282   char *country;
283   char *county;
284 } country_and_county;
285
286 static FilterResult
287 get_counties_cb (tower *details,
288                  gpointer data)
289 {
290   country_cb_data *d = (country_cb_data *)data;
291
292   if (details->serial==0)
293     return FILTER_IGNORE; /* header row */
294
295   if (strcmp(details->fields[FieldCountry], d->country_name)!=0)
296     return FILTER_IGNORE; /* wrong country */
297
298   if (!g_hash_table_lookup_extended (d->hash,
299                                     details->fields[FieldCounty],
300                                      NULL, NULL))
301     {
302       g_hash_table_insert (d->hash,
303                            g_strdup(details->fields[FieldCounty]),
304                            g_strdup (details->fields[FieldCounty]));
305     }
306
307   return FILTER_IGNORE;
308 }
309
310 static FilterResult
311 get_towers_by_county_cb (tower *details,
312                          gpointer data)
313 {
314   country_and_county *cac = (country_and_county *) data;
315
316   if ((!cac->county || strcmp (cac->county, details->fields[FieldCounty])==0) &&
317       (!cac->country || strcmp (cac->country, details->fields[FieldCountry])==0))
318     {
319       return FILTER_ACCEPT;
320     }
321   else
322     {
323       return FILTER_IGNORE;
324     }
325 }
326
327 static FilterResult
328 get_towers_by_search_cb (tower *details,
329                          gpointer data)
330 {
331   char *s = (char *) data;
332
333   if (strcasestr(details->fields[FieldCountry], s) ||
334       strcasestr(details->fields[FieldCounty], s) ||
335       strcasestr(details->fields[FieldDedication], s) ||
336       strcasestr(details->fields[FieldPlace], s))
337     {
338       return FILTER_ACCEPT;
339     }
340   else
341     {
342       return FILTER_IGNORE;
343     }
344 }
345
346 static FilterResult
347 single_tower_cb (tower *details,
348                  gpointer data)
349 {
350
351   GtkWidget *hbox, *button;
352   gchar *str;
353   gint tenor_weight;
354   gchar *primary_key = (gchar*) data;
355   gchar *miles;
356
357   if (strcmp(details->fields[FieldPrimaryKey], primary_key)!=0)
358     {
359       /* not this one; keep going */
360       return FILTER_IGNORE;
361     }
362
363   tower_window = hildon_stackable_window_new ();
364
365   if (g_str_has_prefix (details->fields[FieldDedication],
366                         "S "))
367     {
368       /* FIXME: This needs to be cleverer, because we can have
369        * e.g. "S Peter and S Paul".
370        * May have to use regexps.
371        * Reallocation in general even when unchanged is okay,
372        * because it's the common case (most towers are S Something)
373        */
374       
375       /* FIXME: Since we're passing this in as markup,
376        * we need to escape the strings.
377        */
378
379       str = g_strdup_printf("S<sup>t</sup> %s, %s",
380                               details->fields[FieldDedication]+2,
381                               details->fields[FieldPlace]);
382
383     }
384   else
385     {
386       str = g_strdup_printf("%s, %s",
387                               details->fields[FieldDedication],
388                               details->fields[FieldPlace]);
389     }
390
391   hildon_window_set_markup (HILDON_WINDOW (tower_window),
392                             str);
393   g_free (str);
394
395   hbox = gtk_hbox_new (FALSE, 0);
396   tower_table = gtk_table_new (0, 2, FALSE);
397   buttons = gtk_vbox_new (TRUE, 0);
398   menu = HILDON_APP_MENU (hildon_app_menu_new ());
399
400   miles = distance_to_tower(details);
401
402   add_table_field ("Distance", miles);
403   add_table_field ("Postcode", details->fields[FieldPostcode]);
404   add_table_field ("County", details->fields[FieldCounty]);
405   add_table_field ("Country", details->fields[FieldCountry]);
406   add_table_field ("Diocese", details->fields[FieldDiocese]);
407   add_table_field ("Practice night", details->fields[FieldPracticeNight]);
408   add_table_field ("Bells", details->fields[FieldBells]);
409
410   g_free (miles);
411
412   tenor_weight = atoi (details->fields[FieldWt]);
413   str = g_strdup_printf("%dcwt %dqr %dlb in %s",
414                         tenor_weight/112,
415                         (tenor_weight % 112)/28,
416                         tenor_weight % 28,
417                         details->fields[FieldNote]
418                         );
419   add_table_field ("Tenor", str);
420   g_free (str);
421
422   add_button ("Tower website", show_tower_website);
423   add_button ("Peals", show_peals_list);
424   add_button ("Map", show_tower_map);
425   add_button ("Directions", NULL);
426
427   /* don't use a toggle button: it looks stupid */
428   button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_FINGER_HEIGHT,
429                                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
430                                         "Bookmark", NULL);
431   g_signal_connect (button, "clicked", G_CALLBACK (bookmark_toggled), NULL);
432   gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
433
434   gtk_widget_show_all (GTK_WIDGET (menu));
435   hildon_window_set_app_menu (HILDON_WINDOW (tower_window), menu);
436
437   gtk_box_pack_end (GTK_BOX (hbox), buttons, TRUE, TRUE, 0);
438   gtk_box_pack_end (GTK_BOX (hbox), tower_table, TRUE, TRUE, 0);
439
440   gtk_container_add (GTK_CONTAINER (tower_window), hbox);
441
442   g_free (tower_website);
443   tower_website = g_strdup_printf ("http://%s", details->fields[FieldWebPage]);
444   g_free (peals_list);
445   peals_list = g_strdup_printf ("http://www.pealbase.ismysite.co.uk/felstead/tbid.php?tid=%s",
446        details->fields[FieldTowerBase]);
447   g_free (tower_map);
448   tower_map = g_strdup_printf ("http://maps.google.com/maps?q=%s,%s",
449         details->fields[FieldLat],
450         details->fields[FieldLong]);
451   gtk_widget_show_all (GTK_WIDGET (tower_window));
452
453   return FILTER_STOP;
454 }
455
456 /**
457  * A tower that was accepted by a filter.
458  */
459 typedef struct {
460   char *sortkey;
461   char *primarykey;
462   char *displayname;
463 } FoundTower;
464
465 static FoundTower *
466 found_tower_new (tower *basis)
467 {
468   FoundTower* result = g_new (FoundTower, 1);
469   gchar *distance = distance_to_tower (basis);
470
471   result->sortkey = g_strdup (basis->fields[FieldPrimaryKey]);
472   result->primarykey = g_strdup (basis->fields[FieldPrimaryKey]);
473   result->displayname = g_strdup_printf ("%s, %s (%s, %s) (%s)",
474                                          basis->fields[FieldDedication],
475                                          basis->fields[FieldPlace],
476                                          basis->fields[FieldBells],
477                                          basis->fields[FieldPracticeNight],
478                                          distance);
479
480   g_free (distance);
481
482   return result;
483 }
484
485 static void
486 found_tower_free (FoundTower *tower)
487 {
488   g_free (tower->sortkey);
489   g_free (tower->primarykey);
490   g_free (tower->displayname);
491   g_free (tower);
492 }
493
494 static void
495 parse_dove (ParseDoveCallback callback,
496             GSList **filter_results,
497             gpointer data)
498 {
499   FILE *dove = fopen("/usr/share/belltower/dove.txt", "r");
500   char tower_rec[4096];
501   tower result;
502   char *i;
503   gboolean seen_newline;
504
505   if (!dove)
506     {
507       show_message ("Cannot open Dove database!");
508       exit (255);
509     }
510
511   result.serial = 0;
512
513   while (fgets (tower_rec, sizeof (tower_rec), dove))
514     {
515       seen_newline = FALSE;
516       result.fields[0] = tower_rec;
517       result.n_fields = 0;
518       for (i=tower_rec; *i; i++) {
519         if (*i=='\n')
520           {
521             seen_newline = TRUE;
522           }
523         if (*i=='\\' || *i=='\n')
524           {
525             *i = 0;
526             result.n_fields++;
527             result.fields[result.n_fields] = i+1;
528           }
529       }
530
531       if (!seen_newline)
532         {
533           /* keep it simple, stupid */
534           show_message ("Line too long, cannot continue.");
535           exit (255);
536         }
537
538       if (strcmp (result.fields[FieldCountry], "")==0)
539         {
540           result.fields[FieldCountry] = "England";
541         }
542
543       switch (callback (&result, data))
544         {
545         case FILTER_IGNORE:
546           /* nothing */
547           break;
548
549         case FILTER_STOP:
550           fclose (dove);
551           return;
552
553         case FILTER_ACCEPT:
554           if (filter_results)
555             {
556               *filter_results = g_slist_append (*filter_results,
557                                                 found_tower_new (&result));
558             }
559         }
560
561       result.serial++;
562     }
563
564   fclose (dove);
565 }
566
567 static void
568 nearby_towers (void)
569 {
570   char buffer[4096];
571   LocationGPSDevice *device;
572   device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
573
574   sprintf(buffer, "%f %f %x",
575       device->fix->latitude,
576       device->fix->longitude,
577       device->fix->fields);
578   show_message (buffer);
579
580   if (device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET)
581     {
582       show_message ("I know where you are!");
583     }
584   else
585     {
586       show_message ("I don't know where you are!");
587     }
588
589   g_object_unref (device);
590 }
591
592 static void
593 show_tower (char *primary_key)
594 {
595   parse_dove (single_tower_cb, NULL, primary_key);
596 }
597
598 static void
599 show_towers_from_list (GSList *list)
600 {
601   GtkWidget *dialog;
602   GtkWidget *selector;
603   gint result = -1;
604   GSList *cursor;
605   gchar foo[2048];
606
607   if (!list)
608     {
609       hildon_banner_show_information(window,
610                                      NULL,
611                                      "No towers found.");
612       return;
613     }
614
615   if (!list->next)
616     {
617       /* only one; don't bother showing the list */
618       FoundTower* found = (FoundTower*) list->data;
619
620       hildon_banner_show_information(window,
621                                      NULL,
622                                      "One tower found.");
623       show_tower (found->primarykey);
624
625       /* FIXME: and free the list */
626       return;
627     }
628
629   dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
630   selector = hildon_touch_selector_new_text ();
631
632   for (cursor=list; cursor; cursor=cursor->next)
633     {
634       FoundTower* found = (FoundTower*) cursor->data;
635       hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
636                                          found->displayname);
637     }
638
639   hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog),
640                                      HILDON_TOUCH_SELECTOR (selector));
641
642   gtk_widget_show_all (GTK_WIDGET (dialog));
643
644   if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
645     {
646       GList *rows = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (selector),
647                                                              0);
648       GtkTreePath *path = (GtkTreePath*) rows->data;
649       gint *indices = gtk_tree_path_get_indices (path);
650
651       result = *indices;
652     }
653
654   gtk_widget_destroy (GTK_WIDGET (dialog));
655
656   if (result!=-1)
657     {
658       FoundTower *found = (FoundTower *) g_slist_nth_data (list, result);
659       show_tower (found->primarykey);
660     }
661
662   /* FIXME: and free the list */
663 }
664
665 static gint strcmp_f (gconstpointer a,
666                       gconstpointer b)
667 {
668   return strcmp ((char*)a, (char*)b);
669 }
670
671 static void
672 put_areas_into_list (gpointer key,
673                      gpointer value,
674                      gpointer data)
675 {
676   GSList **list = (GSList **)data;
677   *list = g_slist_insert_sorted (*list,
678                                  value,
679                                  strcmp_f);
680 }
681
682 static void
683 towers_by_subarea (gchar *area)
684 {
685   GtkWidget *dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
686   GtkWidget *selector = hildon_touch_selector_new_text ();
687   GHashTable *hash = g_hash_table_new_full (g_str_hash,
688                                             g_str_equal,
689                                             g_free,
690                                             g_free);
691   GSList *list=NULL, *cursor;
692   gchar *title = g_strdup_printf ("Areas of %s", area);
693   country_cb_data d = { hash, area };
694   country_and_county cac = { area, NULL };
695
696   gtk_window_set_title (GTK_WINDOW (dialog), title);
697   g_free (title);
698
699   parse_dove (get_counties_cb, NULL, &d);
700
701   g_hash_table_foreach (hash,
702                         put_areas_into_list,
703                         &list);
704
705   for (cursor=list; cursor; cursor=cursor->next)
706     {
707       hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
708                                          cursor->data);
709     }
710
711   hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog),
712                                      HILDON_TOUCH_SELECTOR (selector));
713
714   gtk_widget_show_all (GTK_WIDGET (dialog));
715
716   if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
717     {
718       GSList *matches = NULL;
719       cac.county = strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector)));
720
721       parse_dove (get_towers_by_county_cb,
722                   &matches,
723                   &cac);
724       g_free (cac.county);
725
726       show_towers_from_list (matches);
727     }
728   g_hash_table_unref (hash);
729   gtk_widget_destroy (GTK_WIDGET (dialog));
730 }
731
732 static void
733 towers_by_area (void)
734 {
735   GtkWidget *dialog = hildon_picker_dialog_new (GTK_WINDOW (window));
736   GtkWidget *selector = hildon_touch_selector_new_text ();
737   GHashTable *hash = g_hash_table_new_full (g_str_hash,
738                                             g_str_equal,
739                                             g_free,
740                                             g_free);
741   GSList *list = NULL, *cursor;
742   gchar *result = NULL;
743
744   gtk_window_set_title (GTK_WINDOW (dialog), "Areas of the world");
745
746   parse_dove (get_countries_cb, NULL, hash);
747
748   g_hash_table_foreach (hash,
749                         put_areas_into_list,
750                         &list);
751
752   for (cursor=list; cursor; cursor=cursor->next)
753     {
754       hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
755                                          cursor->data);
756     }
757
758   hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog),
759                                      HILDON_TOUCH_SELECTOR (selector));
760
761   gtk_widget_show_all (GTK_WIDGET (dialog));
762
763   if (gtk_dialog_run (GTK_DIALOG (dialog))==GTK_RESPONSE_OK)
764     {
765       result = g_strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector)));
766     }
767   g_hash_table_unref (hash);
768   gtk_widget_destroy (GTK_WIDGET (dialog));
769
770   if (result)
771     {
772       towers_by_subarea (result);
773       g_free (result);
774     }
775 }
776
777 static void
778 show_bookmarks (void)
779 {
780 }
781
782 static void
783 tower_search (void)
784 {
785   GtkWidget *terms = gtk_dialog_new_with_buttons ("What are you looking for?",
786                                                   GTK_WINDOW (window),
787                                                   GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
788                                                   "Search",
789                                                   GTK_RESPONSE_OK,
790                                                   NULL);
791   GtkWidget *entry = gtk_entry_new ();
792   GSList *matches = NULL;
793
794   gtk_box_pack_end (GTK_BOX (GTK_DIALOG (terms)->vbox),
795                     entry, TRUE, TRUE, 0);
796
797   gtk_widget_show_all (GTK_WIDGET (terms));
798
799   if (gtk_dialog_run (GTK_DIALOG (terms))==GTK_RESPONSE_OK)
800     {
801       GSList *matches = NULL;
802
803       parse_dove (get_towers_by_search_cb,
804                   &matches,
805                   (char*) gtk_entry_get_text (GTK_ENTRY (entry)));
806
807       show_towers_from_list (matches);
808     }
809
810   gtk_widget_destroy (GTK_WIDGET (terms));
811 }
812
813 static void
814 recent_towers (void)
815 {
816 }
817
818 int
819 main(int argc, char **argv)
820 {
821   GtkWidget *bell, *button, *hbox;
822   GdkPixbuf *bell_picture;
823
824   gtk_init (&argc, &argv);
825   g_set_application_name ("Belltower");
826
827   window = hildon_stackable_window_new ();
828   gtk_window_set_title (GTK_WINDOW (window), "Belltower");
829   g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
830
831   bell_picture = gdk_pixbuf_new_from_file ("/usr/share/belltower/bells1.jpg", NULL);
832
833   buttons = gtk_vbox_new (TRUE, 0);
834   menu = HILDON_APP_MENU (hildon_app_menu_new ());
835
836   add_button ("Nearby", nearby_towers);
837   add_button ("Recent", recent_towers);
838   add_button ("Bookmarks", show_bookmarks);
839   add_button ("By area", towers_by_area);
840   add_button ("Search", tower_search);
841
842   /* extra buttons for the app menu */
843   button = gtk_button_new_with_label ("Credits");
844   hildon_app_menu_append (menu, GTK_BUTTON (button));
845   hildon_app_menu_append (menu, GTK_BUTTON (button));
846
847   gtk_widget_show_all (GTK_WIDGET (menu));
848   hildon_window_set_app_menu (HILDON_WINDOW (window), menu);
849
850   hbox = gtk_hbox_new (FALSE, 0);
851   gtk_box_pack_end (GTK_BOX (hbox), buttons, TRUE, TRUE, 0);
852   gtk_box_pack_end (GTK_BOX (hbox),
853                     gtk_image_new_from_pixbuf (bell_picture),
854                     TRUE, TRUE, 0);
855
856   gtk_container_add (GTK_CONTAINER (window), hbox);
857   gtk_widget_show_all (GTK_WIDGET (window));
858
859   gtk_main ();
860
861   return EXIT_SUCCESS;
862 }