X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/c7efa5beeca8a783cfac8d2055ba89b35df8ef21..2fa6c78d8dbf5402a6d2d79c3be9e208fd425f4a:/gtk.c diff --git a/gtk.c b/gtk.c index 97b426e..bfc06d7 100644 --- a/gtk.c +++ b/gtk.c @@ -134,9 +134,10 @@ struct frontend { #ifdef OLD_FILESEL char *filesel_name; #endif - int npresets; - GtkWidget **preset_bullets; - GtkWidget *preset_custom_bullet; + GSList *preset_radio; + int n_preset_menu_items; + int preset_threaded; + GtkWidget *preset_custom; GtkWidget *copy_menu_item; }; @@ -498,6 +499,17 @@ void gtk_end_draw(void *handle) } } +#ifdef USE_PANGO +char *gtk_text_fallback(void *handle, const char *const *strings, int nstrings) +{ + /* + * We assume Pango can cope with any UTF-8 likely to be emitted + * by a puzzle. + */ + return dupstr(strings[0]); +} +#endif + const struct drawing_api gtk_drawing = { gtk_draw_text, gtk_draw_rect, @@ -515,7 +527,12 @@ const struct drawing_api gtk_drawing = { gtk_blitter_save, gtk_blitter_load, NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */ - NULL, /* line_width */ + NULL, NULL, /* line_width, line_dotted */ +#ifdef USE_PANGO + gtk_text_fallback, +#else + NULL, +#endif }; static void destroy(GtkWidget *widget, gpointer data) @@ -646,6 +663,11 @@ static gint motion_event(GtkWidget *widget, GdkEventMotion *event, if (!midend_process_key(fe->me, event->x - fe->ox, event->y - fe->oy, button)) gtk_widget_destroy(fe->window); +#if GTK_CHECK_VERSION(2,12,0) + gdk_event_request_motions(event); +#else + gdk_window_get_pointer(widget->window, NULL, NULL, NULL); +#endif return TRUE; } @@ -1121,15 +1143,6 @@ static void get_size(frontend *fe, int *px, int *py) gdk_window_resize(GTK_WIDGET(win)->window, x, y) #endif -static void update_menuitem_bullet(GtkWidget *label, int visible) -{ - if (visible) { - gtk_label_set_text(GTK_LABEL(label), "\xE2\x80\xA2"); - } else { - gtk_label_set_text(GTK_LABEL(label), ""); - } -} - /* * Called when any other code in this file has changed the * selected game parameters. @@ -1137,18 +1150,30 @@ static void update_menuitem_bullet(GtkWidget *label, int visible) static void changed_preset(frontend *fe) { int n = midend_which_preset(fe->me); - int i; - /* - * Update the tick mark in the Type menu. - */ - if (fe->preset_bullets) { - for (i = 0; i < fe->npresets; i++) - update_menuitem_bullet(fe->preset_bullets[i], n == i); - } - if (fe->preset_custom_bullet) { - update_menuitem_bullet(fe->preset_custom_bullet, n < 0); + fe->preset_threaded = TRUE; + if (n < 0 && fe->preset_custom) { + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(fe->preset_custom), + TRUE); + } else { + GSList *gs = fe->preset_radio; + int i = fe->n_preset_menu_items - 1 - n; + if (fe->preset_custom) + gs = gs->next; + while (i && gs) { + i--; + gs = gs->next; + } + if (gs) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data), + TRUE); + } else for (gs = fe->preset_radio; gs; gs = gs->next) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data), + FALSE); + } } + fe->preset_threaded = FALSE; /* * Update the greying on the Copy menu option. @@ -1186,6 +1211,10 @@ static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) game_params *params = (game_params *)gtk_object_get_data(GTK_OBJECT(menuitem), "user-data"); + if (fe->preset_threaded || + (GTK_IS_CHECK_MENU_ITEM(menuitem) && + !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))) + return; midend_set_params(fe->me, params); midend_new_game(fe->me); changed_preset(fe); @@ -1195,77 +1224,40 @@ static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) GdkAtom compound_text_atom, utf8_string_atom; int paste_initialised = FALSE; -void init_paste() +static void set_selection(frontend *fe, GdkAtom selection) { - unsigned char empty[] = { 0 }; - - if (paste_initialised) - return; - - if (!compound_text_atom) - compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE); - if (!utf8_string_atom) - utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE); + if (!paste_initialised) { + compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE); + utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE); + paste_initialised = TRUE; + } /* - * Ensure that all the cut buffers exist - according to the - * ICCCM, we must do this before we start using cut buffers. + * For this simple application we can safely assume that the + * data passed to this function is pure ASCII, which means we + * can return precisely the same stuff for types STRING, + * COMPOUND_TEXT or UTF8_STRING. */ - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0); - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0); - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0); - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0); - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0); - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0); - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0); - XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0); -} - -/* Store data in a cut-buffer. */ -void store_cutbuffer(char *ptr, int len) -{ - /* ICCCM says we must rotate the buffers before storing to buffer 0. */ - XRotateBuffers(GDK_DISPLAY(), 1); - XStoreBytes(GDK_DISPLAY(), ptr, len); + + if (gtk_selection_owner_set(fe->area, selection, CurrentTime)) { + gtk_selection_clear_targets(fe->area, selection); + gtk_selection_add_target(fe->area, selection, + GDK_SELECTION_TYPE_STRING, 1); + gtk_selection_add_target(fe->area, selection, compound_text_atom, 1); + gtk_selection_add_target(fe->area, selection, utf8_string_atom, 1); + } } void write_clip(frontend *fe, char *data) { - init_paste(); - if (fe->paste_data) sfree(fe->paste_data); - /* - * For this simple application we can safely assume that the - * data passed to this function is pure ASCII, which means we - * can return precisely the same stuff for types STRING, - * COMPOUND_TEXT or UTF8_STRING. - */ - fe->paste_data = data; fe->paste_data_len = strlen(data); - store_cutbuffer(fe->paste_data, fe->paste_data_len); - - if (gtk_selection_owner_set(fe->area, GDK_SELECTION_PRIMARY, - CurrentTime)) { - gtk_selection_clear_targets(fe->area, GDK_SELECTION_PRIMARY); - gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY, - GDK_SELECTION_TYPE_STRING, 1); - gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY, - compound_text_atom, 1); - gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY, - utf8_string_atom, 1); - } + set_selection(fe, GDK_SELECTION_PRIMARY); + set_selection(fe, GDK_SELECTION_CLIPBOARD); } void selection_get(GtkWidget *widget, GtkSelectionData *seldata, @@ -1496,6 +1488,11 @@ static void menu_config_event(GtkMenuItem *menuitem, gpointer data) int which = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(menuitem), "user-data")); + if (fe->preset_threaded || + (GTK_IS_CHECK_MENU_ITEM(menuitem) && + !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))) + return; + changed_preset(fe); /* Put the old preset back! */ if (!get_config(fe, which)) return; @@ -1558,38 +1555,10 @@ static void add_menu_separator(GtkContainer *cont) enum { ARG_EITHER, ARG_SAVE, ARG_ID }; /* for argtype */ -static GtkWidget *make_preset_menuitem(GtkWidget **bulletlabel, - const char *name) -{ - GtkWidget *hbox, *lab1, *lab2, *menuitem; - GtkRequisition req; - - hbox = gtk_hbox_new(FALSE, 0); - gtk_widget_show(hbox); - lab1 = gtk_label_new("\xE2\x80\xA2 "); - gtk_widget_show(lab1); - gtk_box_pack_start(GTK_BOX(hbox), lab1, FALSE, FALSE, 0); - gtk_misc_set_alignment(GTK_MISC(lab1), 0.0, 0.0); - lab2 = gtk_label_new(name); - gtk_widget_show(lab2); - gtk_box_pack_start(GTK_BOX(hbox), lab2, TRUE, TRUE, 0); - gtk_misc_set_alignment(GTK_MISC(lab2), 0.0, 0.0); - - gtk_widget_size_request(lab1, &req); - gtk_widget_set_usize(lab1, req.width, -1); - gtk_label_set_text(GTK_LABEL(lab1), ""); - - menuitem = gtk_menu_item_new(); - gtk_container_add(GTK_CONTAINER(menuitem), hbox); - - *bulletlabel = lab1; - return menuitem; -} - static frontend *new_window(char *arg, int argtype, char **error) { frontend *fe; - GtkBox *vbox; + GtkBox *vbox, *hbox; GtkWidget *menubar, *menu, *menuitem; GdkPixmap *iconpm; GList *iconlist; @@ -1675,8 +1644,12 @@ static frontend *new_window(char *arg, int argtype, char **error) fe->accelgroup = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(fe->window), fe->accelgroup); + hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); + gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0); + gtk_widget_show(GTK_WIDGET(hbox)); + menubar = gtk_menu_bar_new(); - gtk_box_pack_start(vbox, menubar, FALSE, FALSE, 0); + gtk_box_pack_start(hbox, menubar, TRUE, TRUE, 0); gtk_widget_show(menubar); menuitem = gtk_menu_item_new_with_mnemonic("_Game"); @@ -1710,6 +1683,10 @@ static frontend *new_window(char *arg, int argtype, char **error) GTK_SIGNAL_FUNC(menu_config_event), fe); gtk_widget_show(menuitem); + fe->preset_radio = NULL; + fe->preset_custom = NULL; + fe->n_preset_menu_items = 0; + fe->preset_threaded = FALSE; if ((n = midend_num_presets(fe->me)) > 0 || thegame.can_configure) { GtkWidget *submenu; int i; @@ -1721,17 +1698,17 @@ static frontend *new_window(char *arg, int argtype, char **error) submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - fe->npresets = n; - fe->preset_bullets = snewn(n, GtkWidget *); - for (i = 0; i < n; i++) { char *name; game_params *params; midend_fetch_preset(fe->me, i, &name, ¶ms); - menuitem = make_preset_menuitem(&fe->preset_bullets[i], name); - + menuitem = + gtk_radio_menu_item_new_with_label(fe->preset_radio, name); + fe->preset_radio = + gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem)); + fe->n_preset_menu_items++; gtk_container_add(GTK_CONTAINER(submenu), menuitem); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", params); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", @@ -1740,22 +1717,19 @@ static frontend *new_window(char *arg, int argtype, char **error) } if (thegame.can_configure) { - menuitem = make_preset_menuitem(&fe->preset_custom_bullet, - "Custom..."); - + menuitem = fe->preset_custom = + gtk_radio_menu_item_new_with_label(fe->preset_radio, + "Custom..."); + fe->preset_radio = + gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem)); gtk_container_add(GTK_CONTAINER(submenu), menuitem); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GPOINTER_TO_INT(CFG_SETTINGS)); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_config_event), fe); gtk_widget_show(menuitem); - } else - fe->preset_custom_bullet = NULL; + } - } else { - fe->npresets = 0; - fe->preset_bullets = NULL; - fe->preset_custom_bullet = NULL; } add_menu_separator(GTK_CONTAINER(menu)); @@ -1769,9 +1743,11 @@ static frontend *new_window(char *arg, int argtype, char **error) gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_save_event), fe); gtk_widget_show(menuitem); +#ifndef STYLUS_BASED add_menu_separator(GTK_CONTAINER(menu)); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u'); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", 'r'); +#endif if (thegame.can_format_as_text_ever) { add_menu_separator(GTK_CONTAINER(menu)); menuitem = gtk_menu_item_new_with_label("Copy"); @@ -1807,6 +1783,42 @@ static frontend *new_window(char *arg, int argtype, char **error) GTK_SIGNAL_FUNC(menu_about_event), fe); gtk_widget_show(menuitem); +#ifdef STYLUS_BASED + menuitem=gtk_button_new_with_mnemonic("_Redo"); + gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", + GINT_TO_POINTER((int)('r'))); + gtk_signal_connect(GTK_OBJECT(menuitem), "clicked", + GTK_SIGNAL_FUNC(menu_key_event), fe); + gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0); + gtk_widget_show(menuitem); + + menuitem=gtk_button_new_with_mnemonic("_Undo"); + gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", + GINT_TO_POINTER((int)('u'))); + gtk_signal_connect(GTK_OBJECT(menuitem), "clicked", + GTK_SIGNAL_FUNC(menu_key_event), fe); + gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0); + gtk_widget_show(menuitem); + + if (thegame.flags & REQUIRE_NUMPAD) { + hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); + gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0); + gtk_widget_show(GTK_WIDGET(hbox)); + + *((int*)errbuf)=0; + errbuf[1]='\0'; + for(errbuf[0]='0';errbuf[0]<='9';errbuf[0]++) { + menuitem=gtk_button_new_with_label(errbuf); + gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", + GINT_TO_POINTER((int)(errbuf[0]))); + gtk_signal_connect(GTK_OBJECT(menuitem), "clicked", + GTK_SIGNAL_FUNC(menu_key_event), fe); + gtk_box_pack_start(hbox, menuitem, TRUE, TRUE, 0); + gtk_widget_show(menuitem); + } + } +#endif /* STYLUS_BASED */ + changed_preset(fe); { @@ -1898,7 +1910,8 @@ static frontend *new_window(char *arg, int argtype, char **error) gtk_widget_add_events(GTK_WIDGET(fe->area), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_MOTION_MASK); + GDK_BUTTON_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK); if (n_xpm_icons) { gtk_widget_realize(fe->window);