/* otherwindow.c Copyright (C) 2004-2007 Mark Tyler and Dmitry Groshev This file is part of mtPaint. mtPaint is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. mtPaint is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mtPaint in the file COPYING. */ #include #include #include #if GTK_MAJOR_VERSION == 1 #include #endif #include "global.h" #include "memory.h" #include "otherwindow.h" #include "mygtk.h" #include "png.h" #include "mainwindow.h" #include "viewer.h" #include "inifile.h" #include "canvas.h" #include "quantizer.h" #include "layer.h" #include "wu.h" #include "ani.h" #include "channels.h" #include "toolbar.h" #include "csel.h" /// NEW IMAGE WINDOW int im_type, new_window_type = 0; GtkWidget *new_window; GtkWidget *spinbutton_height, *spinbutton_width, *spinbutton_cols; gint delete_new( GtkWidget *widget, GdkEvent *event, gpointer data ) { gtk_widget_destroy(new_window); return FALSE; } void reset_tools() { float old_zoom = can_zoom; init_istate(); init_pal(); can_zoom = -1; if ( inifile_get_gboolean("zoomToggle", FALSE) ) align_size(1); // Always start at 100% else align_size(old_zoom); pressed_opacity( 255 ); // Set opacity to 100% to start with update_menus(); } void do_new_chores() { reset_tools(); set_new_filename( _("Untitled") ); update_all_views(); gtk_widget_queue_draw(drawing_col_prev); } int do_new_one(int nw, int nh, int nc, int nt, int bpp) { int res; if ( nt != 1) mem_pal_copy( mem_pal, mem_pal_def ); #ifdef U_GUADALINEX else mem_scale_pal( 0, 255,255,255, nc-1, 0,0,0 ); #else else mem_scale_pal( 0, 0,0,0, nc-1, 255,255,255 ); #endif mtMIN( nw, nw, MAX_WIDTH ) mtMAX( nw, nw, MIN_WIDTH ) mtMIN( nh, nh, MAX_HEIGHT ) mtMAX( nh, nh, MIN_HEIGHT ) mtMAX( nc, nc, 2 ) mem_cols = nc; res = mem_new( nw, nh, bpp, CMASK_IMAGE ); if ( res!= 0 ) // Not enough memory! { memory_errors(1); } do_new_chores(); return res; } static void create_new(GtkWidget *widget) { int nw, nh, nc, bpp = 1, err=0; nw = read_spin(spinbutton_width); nh = read_spin(spinbutton_height); nc = read_spin(spinbutton_cols); if (im_type == 0) bpp = 3; if (im_type == 3) // Grab Screenshot { #if GTK_MAJOR_VERSION == 1 gdk_window_lower( main_window->window ); gdk_window_lower( new_window->window ); gdk_flush(); while (gtk_events_pending()) gtk_main_iteration(); // Wait for minimize sleep(1); // Wait a second for screen to redraw grab_screen(); do_new_chores(); notify_changed(); gdk_window_raise( main_window->window ); #endif #if GTK_MAJOR_VERSION == 2 gtk_window_set_transient_for( GTK_WINDOW(new_window), NULL ); gdk_window_iconify( new_window->window ); gdk_window_iconify( main_window->window ); gdk_flush(); while (gtk_events_pending()) gtk_main_iteration(); // Wait for minimize g_usleep(400000); // Wait 0.4 of a second for screen to redraw grab_screen(); do_new_chores(); notify_changed(); gdk_window_deiconify( main_window->window ); gdk_window_raise( main_window->window ); #endif } if ((im_type < 3) && (new_window_type == 0)) // New image { err = do_new_one( nw, nh, nc, im_type, bpp ); if ( err>0 ) // System was unable to allocate memory for image, using 8x8 instead { nw = mem_width; nh = mem_height; } if ( layers_total>0 ) layers_notify_changed(); inifile_set_gint32("lastnewWidth", nw ); inifile_set_gint32("lastnewHeight", nh ); inifile_set_gint32("lastnewCols", nc ); inifile_set_gint32("lastnewType", im_type ); /* Lose a selection marquee */ pressed_select_none(NULL, NULL); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(icon_buttons[DEFAULT_TOOL_ICON]), TRUE ); } if (new_window_type == 1) layer_new(nw, nh, 3 - im_type, nc, CMASK_IMAGE); else { gtk_adjustment_value_changed( gtk_scrolled_window_get_hadjustment( GTK_SCROLLED_WINDOW(scrolledwindow_canvas) ) ); gtk_adjustment_value_changed( gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(scrolledwindow_canvas) ) ); // These 2 are needed to synchronize the scrollbars & image view } gtk_widget_destroy(new_window); } void generic_new_window(int type) // 0=New image, 1=New layer { char *rad_txt[] = {_("24 bit RGB"), _("Greyscale"), _("Indexed Palette"), _("Grab Screenshot")}, *title_txt[] = {_("New Image"), _("New Layer")}; int w = mem_width, h = mem_height, c = mem_cols; GtkWidget *vbox1, *hbox3; GtkWidget *table1; new_window_type = type; if ( type == 0 && check_for_changes() == 1 ) return; new_window = add_a_window( GTK_WINDOW_TOPLEVEL, title_txt[type], GTK_WIN_POS_CENTER, TRUE ); vbox1 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox1); gtk_container_add (GTK_CONTAINER (new_window), vbox1); table1 = add_a_table( 3, 2, 5, vbox1 ); if ( type == 0 ) { w = inifile_get_gint32("lastnewWidth", DEFAULT_WIDTH); h = inifile_get_gint32("lastnewHeight", DEFAULT_HEIGHT); c = inifile_get_gint32("lastnewCols", 256); im_type = inifile_get_gint32("lastnewType", 2); if ( im_type<0 || im_type>2 ) im_type = 0; } else im_type = 3 - mem_img_bpp; spinbutton_width = spin_to_table(table1, 0, 1, 5, w, MIN_WIDTH, MAX_WIDTH); spinbutton_height = spin_to_table(table1, 1, 1, 5, h, MIN_WIDTH, MAX_HEIGHT); spinbutton_cols = spin_to_table(table1, 2, 1, 5, c, 2, 256); add_to_table( _("Width"), table1, 0, 0, 5 ); add_to_table( _("Height"), table1, 1, 0, 5 ); add_to_table( _("Colours"), table1, 2, 0, 5 ); hbox3 = wj_radio_pack(rad_txt, type ? 3 : 4, 0, im_type, &im_type, NULL); gtk_box_pack_start(GTK_BOX(vbox1), hbox3, TRUE, TRUE, 0); add_hseparator( vbox1, 200, 10 ); hbox3 = OK_box(5, new_window, _("Create"), GTK_SIGNAL_FUNC(create_new), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); gtk_box_pack_start(GTK_BOX(vbox1), hbox3, FALSE, FALSE, 0); gtk_window_set_transient_for( GTK_WINDOW(new_window), GTK_WINDOW(main_window) ); gtk_widget_show (new_window); } void pressed_new( GtkMenuItem *menu_item, gpointer user_data ) { generic_new_window(0); } /// PATTERN & BRUSH CHOOSER WINDOW static GtkWidget *pat_window, *draw_pat; static int pat_brush; static unsigned char *mem_patch = NULL; static gint delete_pat( GtkWidget *widget, GdkEvent *event, gpointer data ) { if ( pat_brush == 0 ) { if ( mem_patch != NULL ) free(mem_patch); mem_patch = NULL; } gtk_widget_destroy(pat_window); return FALSE; } static gint key_pat( GtkWidget *widget, GdkEventKey *event ) { /* xine-ui sends bogus keypresses so don't delete on this */ if (!XINE_FAKERY(event->keyval)) delete_pat( widget, NULL, NULL ); return FALSE; } static gint click_pat( GtkWidget *widget, GdkEventButton *event ) { int pat_no = 0, mx, my; mx = event->x; my = event->y; pat_no = mx / (PATCH_WIDTH/9) + 9*( my / (PATCH_HEIGHT/9) ); mtMAX(pat_no, pat_no, 0) mtMIN(pat_no, pat_no, 80) if ( event->button == 1 ) { if ( pat_brush == 0 ) { tool_pat = pat_no; mem_pat_update(); // Update memory gtk_widget_queue_draw( drawing_col_prev ); // Update widget if ( marq_status >= MARQUEE_PASTE && text_paste ) { render_text( drawing_col_prev ); check_marquee(); gtk_widget_queue_draw( drawing_canvas ); } } else { mem_set_brush(pat_no); brush_tool_type = tool_type; toolbar_update_settings(); // Update spin buttons gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(icon_buttons[PAINT_TOOL_ICON]), TRUE ); // Update toolbar set_cursor(); } } return FALSE; } static gint expose_pat( GtkWidget *widget, GdkEventExpose *event ) { gdk_draw_rgb_image( draw_pat->window, draw_pat->style->black_gc, event->area.x, event->area.y, event->area.width, event->area.height, GDK_RGB_DITHER_NONE, mem_patch + 3*( event->area.x + PATCH_WIDTH*event->area.y ), PATCH_WIDTH*3 ); return FALSE; } void choose_pattern(int typ) // Bring up pattern chooser (0) or brush (1) { int pattern, pixel, r, g, b, row, column, sx, sy, ex, ey; pat_brush = typ; if ( typ == 0 ) { mem_patch = grab_memory( 3*PATCH_WIDTH*PATCH_HEIGHT, 0 ); for ( pattern = 0; pattern < 81; pattern++ ) { sy = 2 + (pattern / 9) * 36; // Start y pixel on main image sx = 2 + (pattern % 9) * 36; // Start x pixel on main image for ( column = 0; column < 8; column++ ) { for ( row = 0; row < 8; row++ ) { pixel = mem_patterns[pattern][column][row]; if ( pixel == 1 ) { r = mem_col_A24.red; g = mem_col_A24.green; b = mem_col_A24.blue; } else { r = mem_col_B24.red; g = mem_col_B24.green; b = mem_col_B24.blue; } for ( ey=0; ey<4; ey++ ) { for ( ex=0; ex<4; ex++ ) { mem_patch[ 0+3*( sx+row+8*ex + PATCH_WIDTH*(sy+column+8*ey) ) ] = r; mem_patch[ 1+3*( sx+row+8*ex + PATCH_WIDTH*(sy+column+8*ey) ) ] = g; mem_patch[ 2+3*( sx+row+8*ex + PATCH_WIDTH*(sy+column+8*ey) ) ] = b; } } } } } } else { mem_patch = mem_brushes; } pat_window = add_a_window( GTK_WINDOW_POPUP, _("Pattern Chooser"), GTK_WIN_POS_MOUSE, TRUE ); gtk_container_set_border_width (GTK_CONTAINER (pat_window), 4); draw_pat = gtk_drawing_area_new (); gtk_widget_set_usize( draw_pat, PATCH_WIDTH, PATCH_HEIGHT ); gtk_container_add (GTK_CONTAINER (pat_window), draw_pat); gtk_widget_show( draw_pat ); gtk_signal_connect_object( GTK_OBJECT(draw_pat), "expose_event", GTK_SIGNAL_FUNC (expose_pat), GTK_OBJECT(draw_pat) ); gtk_signal_connect_object( GTK_OBJECT(draw_pat), "button_press_event", GTK_SIGNAL_FUNC (click_pat), GTK_OBJECT(draw_pat) ); gtk_signal_connect_object( GTK_OBJECT(draw_pat), "button_release_event", GTK_SIGNAL_FUNC (delete_pat), NULL ); gtk_signal_connect_object (GTK_OBJECT (pat_window), "key_press_event", GTK_SIGNAL_FUNC (key_pat), NULL); gtk_widget_set_events (draw_pat, GDK_ALL_EVENTS_MASK); gtk_widget_show (pat_window); } /// ADD COLOURS TO PALETTE WINDOW GtkWidget *add_col_window; GtkWidget *spinbutton_col_add; static void click_col_add_ok(GtkWidget *widget) { int i, to_add; to_add = read_spin(spinbutton_col_add); if ( to_add != mem_cols ) { spot_undo(UNDO_PAL); if ( to_add>mem_cols ) for ( i=mem_cols; i= mem_cols ) mem_col_A = 0; if ( mem_col_B >= mem_cols ) mem_col_B = 0; } init_pal(); } gtk_widget_destroy(add_col_window); } void pressed_add_cols( GtkMenuItem *menu_item, gpointer user_data ) { GtkWidget *vbox5, *hbox6; add_col_window = add_a_window( GTK_WINDOW_TOPLEVEL, _("Set Palette Size"), GTK_WIN_POS_CENTER, TRUE ); gtk_widget_set_usize (add_col_window, 320, -2); vbox5 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox5); gtk_container_add (GTK_CONTAINER (add_col_window), vbox5); add_hseparator( vbox5, -2, 10 ); spinbutton_col_add = add_a_spin( 256, 2, 256 ); gtk_box_pack_start (GTK_BOX (vbox5), spinbutton_col_add, FALSE, FALSE, 5); add_hseparator( vbox5, -2, 10 ); hbox6 = OK_box(0, add_col_window, _("OK"), GTK_SIGNAL_FUNC(click_col_add_ok), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); gtk_box_pack_start(GTK_BOX(vbox5), hbox6, FALSE, FALSE, 0); gtk_widget_show (add_col_window); } void pal_refresher() { update_all_views(); init_pal(); gtk_widget_queue_draw(drawing_col_prev); } /* Generic code to handle UI needs of common image transform tasks */ GtkWidget *filter_win, *filter_cont; filter_hook filter_func; gpointer filter_data; void run_filter(GtkButton *button, gpointer user_data) { if (filter_func(filter_cont, filter_data)) gtk_widget_destroy(filter_win); update_all_views(); } void filter_window(gchar *title, GtkWidget *content, filter_hook filt, gpointer fdata, int istool) { GtkWidget *hbox7, *vbox6; GtkWindowPosition pos = istool && !inifile_get_gboolean("centerSettings", TRUE) ? GTK_WIN_POS_MOUSE : GTK_WIN_POS_CENTER; filter_cont = content; filter_func = filt; filter_data = fdata; filter_win = add_a_window(GTK_WINDOW_TOPLEVEL, title, pos, TRUE); gtk_window_set_default_size(GTK_WINDOW(filter_win), 300, -1); vbox6 = gtk_vbox_new(FALSE, 0); gtk_widget_show(vbox6); gtk_container_add(GTK_CONTAINER(filter_win), vbox6); add_hseparator(vbox6, -2, 10); gtk_box_pack_start(GTK_BOX(vbox6), content, FALSE, FALSE, 5); add_hseparator(vbox6, -2, 10); hbox7 = OK_box(0, filter_win, _("Apply"), GTK_SIGNAL_FUNC(run_filter), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); gtk_box_pack_start(GTK_BOX(vbox6), hbox7, FALSE, FALSE, 0); gtk_widget_show(filter_win); } /// BACTERIA EFFECT int do_bacteria(GtkWidget *spin, gpointer fdata) { int i; i = read_spin(spin); spot_undo(UNDO_FILT); mem_bacteria(i); return FALSE; } void pressed_bacteria(GtkMenuItem *menu_item, gpointer user_data) { GtkWidget *spin = add_a_spin(10, 1, 100); filter_window(_("Bacteria Effect"), spin, do_bacteria, NULL, FALSE); } /// SORT PALETTE COLOURS static GtkWidget *spal_window, *spal_spins[2], *spal_rev; static int spal_mode; gint click_spal_apply( GtkWidget *widget, GdkEvent *event, gpointer data ) { int index1 = 0, index2 = 1; gboolean reverse; inifile_set_gint32("lastspalType", spal_mode); reverse = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(spal_rev)); inifile_set_gboolean( "palrevSort", reverse ); index1 = read_spin(spal_spins[0]); index2 = read_spin(spal_spins[1]); if ( index1 == index2 ) return FALSE; spot_undo(UNDO_XPAL); mem_pal_sort(spal_mode, index1, index2, reverse); init_pal(); update_all_views(); gtk_widget_queue_draw( drawing_col_prev ); return FALSE; } gint click_spal_ok( GtkWidget *widget, GdkEvent *event, gpointer data ) { click_spal_apply( NULL, NULL, NULL ); gtk_widget_destroy(spal_window); return FALSE; } void pressed_sort_pal( GtkMenuItem *menu_item, gpointer user_data ) { char *rad_txt[] = { _("Hue"), _("Saturation"), _("Luminance"), _("Distance to A"), _("Distance to A+B"), _("Red"), _("Green"), _("Blue"), _("Projection to A->B"), _("Frequency")}; GtkWidget *vbox1, *hbox3, *table1; spal_window = add_a_window( GTK_WINDOW_TOPLEVEL, _("Sort Palette Colours"), GTK_WIN_POS_CENTER, TRUE ); vbox1 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox1); gtk_container_add (GTK_CONTAINER (spal_window), vbox1); table1 = add_a_table(2, 2, 5, vbox1); spal_spins[0] = spin_to_table(table1, 0, 1, 5, 0, 0, mem_cols - 1); spal_spins[1] = spin_to_table(table1, 1, 1, 5, mem_cols - 1, 0, mem_cols - 1); add_to_table( _("Start Index"), table1, 0, 0, 5 ); add_to_table( _("End Index"), table1, 1, 0, 5 ); table1 = wj_radio_pack(rad_txt, mem_img_bpp == 3 ? 9 : 10, 5, inifile_get_gint32("lastspalType", 2), &spal_mode, NULL); gtk_box_pack_start(GTK_BOX(vbox1), table1, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(table1), 5); spal_rev = add_a_toggle(_("Reverse Order"), vbox1, inifile_get_gboolean("palrevSort", FALSE)); add_hseparator( vbox1, 200, 10 ); /* Cancel / Apply / OK */ hbox3 = OK_box(5, spal_window, _("OK"), GTK_SIGNAL_FUNC(click_spal_ok), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); OK_box_add(hbox3, _("Apply"), GTK_SIGNAL_FUNC(click_spal_apply), 1); gtk_box_pack_start (GTK_BOX (vbox1), hbox3, FALSE, TRUE, 0); gtk_widget_show (spal_window); } /// BRIGHTNESS-CONTRAST-SATURATION WINDOW #define BRCOSA_ITEMS 6 static GtkWidget *brcosa_window, *brcosa_toggles[6], *brcosa_spins[BRCOSA_ITEMS+2], // Extra 2 used for palette limits *brcosa_buttons[5]; static int brcosa_values[BRCOSA_ITEMS], brcosa_pal_lim[2], brcosa_values_default[BRCOSA_ITEMS] = {0, 0, 0, 8, 100, 0}; png_color brcosa_pal[256]; static void brcosa_buttons_sensitive() // Set 4 brcosa button as sensitive if the user has assigned changes { int i; gboolean state = FALSE; if ( brcosa_buttons[0] == NULL ) return; for ( i=0; i3; i-- ) { brcosa_toggles[i] = add_a_toggle( tog_txt[i], hbox, TRUE ); gtk_signal_connect(GTK_OBJECT(brcosa_toggles[i]), "clicked", GTK_SIGNAL_FUNC(click_brcosa_RGB_toggle), NULL); } } brcosa_pal_lim[0] = 0; brcosa_pal_lim[1] = mem_cols-1; brcosa_spins[BRCOSA_ITEMS] = add_a_spin( 0, 0, mem_cols-1 ); brcosa_spins[BRCOSA_ITEMS+1] = add_a_spin( mem_cols-1, 0, mem_cols-1 ); gtk_box_pack_start (GTK_BOX (hbox), brcosa_spins[BRCOSA_ITEMS], FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), brcosa_spins[BRCOSA_ITEMS+1], FALSE, FALSE, 0); for ( i=0; i<2; i++ ) { #if GTK_MAJOR_VERSION == 2 gtk_signal_connect( GTK_OBJECT( >K_SPIN_BUTTON(brcosa_spins[BRCOSA_ITEMS+i])->entry ), "value_changed", GTK_SIGNAL_FUNC(brcosa_pal_lim_change), NULL); #else gtk_signal_connect( GTK_OBJECT( >K_SPIN_BUTTON(brcosa_spins[BRCOSA_ITEMS+i])->entry ), "changed", GTK_SIGNAL_FUNC(brcosa_pal_lim_change), NULL); #endif } hbox = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (vbox5), hbox, FALSE, FALSE, 0); for ( i=0; i<4; i++ ) { brcosa_toggles[i] = add_a_toggle( tog_txt[i], hbox, TRUE ); if ( i == 0 ) gtk_signal_connect(GTK_OBJECT(brcosa_toggles[i]), "clicked", GTK_SIGNAL_FUNC(click_brcosa_preview_toggle), NULL); if ( i>0 ) gtk_signal_connect(GTK_OBJECT(brcosa_toggles[i]), "clicked", GTK_SIGNAL_FUNC(click_brcosa_preview), NULL); } if ( mem_img_bpp == 3 ) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( brcosa_toggles[4] ), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( brcosa_toggles[0] ), inifile_get_gboolean("autopreviewToggle", TRUE)); /// BOTTOM AREA add_hseparator( vbox, -2, 10 ); hbox = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); button = add_a_button(_("Cancel"), 4, hbox, TRUE); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_brcosa_cancel), NULL); gtk_signal_connect_object (GTK_OBJECT (brcosa_window), "delete_event", GTK_SIGNAL_FUNC (click_brcosa_cancel), NULL); gtk_widget_add_accelerator (button, "clicked", ag, GDK_Escape, 0, (GtkAccelFlags) 0); brcosa_buttons[0] = button; button = add_a_button(_("Preview"), 4, hbox, TRUE); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_brcosa_preview), NULL); brcosa_buttons[1] = button; button = add_a_button(_("Reset"), 4, hbox, TRUE); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_brcosa_reset), NULL); brcosa_buttons[2] = button; button = add_a_button(_("Apply"), 4, hbox, TRUE); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_brcosa_apply), NULL); brcosa_buttons[3] = button; button = add_a_button(_("OK"), 4, hbox, TRUE); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_brcosa_ok), NULL); gtk_widget_add_accelerator (button, "clicked", ag, GDK_KP_Enter, 0, (GtkAccelFlags) 0); gtk_widget_add_accelerator (button, "clicked", ag, GDK_Return, 0, (GtkAccelFlags) 0); brcosa_buttons[4] = button; gtk_widget_show (brcosa_window); gtk_window_add_accel_group(GTK_WINDOW (brcosa_window), ag); #if GTK_MAJOR_VERSION == 1 gtk_widget_queue_resize(brcosa_window); /* Reset shortened sliders */ #endif click_brcosa_preview_toggle( NULL, NULL, NULL ); // Show/hide preview button brcosa_buttons_sensitive(); // Disable buttons gtk_window_set_transient_for( GTK_WINDOW(brcosa_window), GTK_WINDOW(main_window) ); } /// RESIZE/RESCALE WINDOWS GtkWidget *sisca_window, *sisca_table; GtkWidget *sisca_spins[6], *sisca_toggles[2], *sisca_gc; gboolean sisca_scale; static void sisca_moved(GtkAdjustment *adjustment, gpointer user_data) { int w, h, nw, idx = (int)user_data; if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sisca_toggles[0]))) return; w = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sisca_spins[0])); h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sisca_spins[1])); if (idx) { nw = rint(h * mem_width / (float)mem_height); nw = nw < 1 ? 1 : nw > MAX_WIDTH ? MAX_WIDTH : nw; if (nw == w) return; } else { nw = rint(w * mem_height / (float)mem_width); nw = nw < 1 ? 1 : nw > MAX_HEIGHT ? MAX_HEIGHT : nw; if (nw == h) return; } idx ^= 1; /* Other coordinate */ gtk_spin_button_update(GTK_SPIN_BUTTON(sisca_spins[idx])); gtk_spin_button_set_value(GTK_SPIN_BUTTON(sisca_spins[idx]), nw); } static int scale_mode = 7; static int resize_mode = 0; static void click_sisca_ok(GtkWidget *widget, gpointer user_data) { int nw, nh, ox = 0, oy = 0, res = 1, scale_type = 0, gcor = FALSE; nw = read_spin(sisca_spins[0]); nh = read_spin(sisca_spins[1]); if (!sisca_scale) { ox = read_spin(sisca_spins[2]); oy = read_spin(sisca_spins[3]); } if ((nw == mem_width) && (nh == mem_height) && !ox && !oy) { alert_box(_("Error"), _("New geometry is the same as now - nothing to do."), _("OK"), NULL, NULL); return; } if (sisca_scale) { if (mem_img_bpp == 3) scale_type = scale_mode; res = mem_image_scale(nw, nh, scale_type, gcor); } else res = mem_image_resize(nw, nh, ox, oy, resize_mode); if (!res) { canvas_undo_chores(); // Needed in Windows to stop GTK+ lowering the main window below window underneath gtk_window_set_transient_for( GTK_WINDOW(sisca_window), NULL ); gtk_widget_destroy(sisca_window); } else memory_errors(res); } void memory_errors(int type) { if ( type == 1 ) alert_box(_("Error"), _("The operating system cannot allocate the memory for this operation."), _("OK"), NULL, NULL); if ( type == 2 ) alert_box(_("Error"), _("You have not allocated enough memory in the Preferences window for this operation."), _("OK"), NULL, NULL); } gint click_sisca_centre( GtkWidget *widget, GdkEvent *event, gpointer data ) { int nw = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(sisca_spins[0]) ); int nh = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(sisca_spins[1]) ); nw = (nw - mem_width) / 2; nh = (nh - mem_height) / 2; gtk_spin_button_set_value( GTK_SPIN_BUTTON(sisca_spins[2]), nw ); gtk_spin_button_set_value( GTK_SPIN_BUTTON(sisca_spins[3]), nh ); return FALSE; } void sisca_init( char *title ) { gchar* scale_fnames[] = { _("Nearest Neighbour"), _("Bilinear / Area Mapping"), _("Bicubic"), _("Bicubic edged"), _("Bicubic better"), _("Bicubic sharper"), _("Lanczos3"), _("Blackman-Harris"), NULL }; gchar* resize_modes[] = { _("Clear"), _("Tile"), _("Mirror tile"), NULL }; GtkWidget *button_centre, *sisca_vbox, *sisca_hbox; sisca_window = add_a_window( GTK_WINDOW_TOPLEVEL, title, GTK_WIN_POS_CENTER, TRUE ); sisca_vbox = gtk_vbox_new(FALSE, 0); gtk_widget_show(sisca_vbox); gtk_container_add(GTK_CONTAINER (sisca_window), sisca_vbox); sisca_table = add_a_table(3, 3, 5, sisca_vbox); add_to_table( _("Width "), sisca_table, 0, 1, 0 ); add_to_table( _("Height "), sisca_table, 0, 2, 0 ); add_to_table( _("Original "), sisca_table, 1, 0, 0); sisca_spins[0] = spin_to_table(sisca_table, 1, 1, 5, mem_width, mem_width, mem_width); sisca_spins[1] = spin_to_table(sisca_table, 1, 2, 5, mem_height, mem_height, mem_height); GTK_WIDGET_UNSET_FLAGS (sisca_spins[0], GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS (sisca_spins[1], GTK_CAN_FOCUS); add_to_table( _("New"), sisca_table, 2, 0, 0 ); sisca_spins[0] = spin_to_table(sisca_table, 2, 1, 5, mem_width, 1, MAX_WIDTH); sisca_spins[1] = spin_to_table(sisca_table, 2, 2, 5, mem_height, 1, MAX_HEIGHT); gtk_signal_connect(GTK_OBJECT(gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON(sisca_spins[0]))), "value_changed", GTK_SIGNAL_FUNC(sisca_moved), (gpointer)0); gtk_signal_connect(GTK_OBJECT(gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON(sisca_spins[1]))), "value_changed", GTK_SIGNAL_FUNC(sisca_moved), (gpointer)1); if ( !sisca_scale ) { add_to_table( _("Offset"), sisca_table, 3, 0, 0 ); sisca_spins[2] = spin_to_table(sisca_table, 3, 1, 5, 0, -MAX_WIDTH, MAX_WIDTH); sisca_spins[3] = spin_to_table(sisca_table, 3, 2, 5, 0, -MAX_HEIGHT, MAX_HEIGHT); button_centre = gtk_button_new_with_label(_("Centre")); gtk_widget_show(button_centre); gtk_table_attach (GTK_TABLE (sisca_table), button_centre, 0, 1, 4, 5, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 5, 5); gtk_signal_connect(GTK_OBJECT(button_centre), "clicked", GTK_SIGNAL_FUNC(click_sisca_centre), NULL); } add_hseparator( sisca_vbox, -2, 10 ); sisca_toggles[0] = add_a_toggle( _("Fix Aspect Ratio"), sisca_vbox, TRUE ); gtk_signal_connect(GTK_OBJECT(sisca_toggles[0]), "toggled", GTK_SIGNAL_FUNC(sisca_moved), (gpointer)0); sisca_hbox = sisca_gc = NULL; /* Resize */ if (!sisca_scale) sisca_hbox = wj_radio_pack(resize_modes, -1, 0, resize_mode, &resize_mode, NULL); /* RGB rescale */ else if (mem_img_bpp == 3) { sisca_gc = add_a_toggle(_("Gamma corrected"), sisca_vbox, inifile_get_gboolean("defaultGamma", FALSE)); sisca_hbox = wj_radio_pack(scale_fnames, -1, 0, scale_mode, &scale_mode, NULL); } if (sisca_hbox) { add_hseparator(sisca_vbox, -2, 10); gtk_box_pack_start(GTK_BOX(sisca_vbox), sisca_hbox, TRUE, TRUE, 0); } add_hseparator( sisca_vbox, -2, 10 ); sisca_hbox = OK_box(5, sisca_window, _("OK"), GTK_SIGNAL_FUNC(click_sisca_ok), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); gtk_box_pack_start(GTK_BOX(sisca_vbox), sisca_hbox, FALSE, FALSE, 0); gtk_window_set_transient_for( GTK_WINDOW(sisca_window), GTK_WINDOW(main_window) ); gtk_widget_show (sisca_window); } void pressed_scale( GtkMenuItem *menu_item, gpointer user_data ) { sisca_scale = TRUE; sisca_init(_("Scale Canvas")); } void pressed_size( GtkMenuItem *menu_item, gpointer user_data ) { sisca_scale = FALSE; sisca_init(_("Resize Canvas")); } /// PALETTE EDITOR WINDOW typedef struct { guint16 r, g, b, a; } RGBA16; static GtkWidget *allcol_window, *allcol_list; static RGBA16 *ctable; static int allcol_idx; static colour_hook allcol_hook; static void allcol_ok(colour_hook chook) { chook(2); gtk_widget_destroy(allcol_window); free(ctable); } static void allcol_preview(colour_hook chook) { chook(1); } static gboolean allcol_cancel(colour_hook chook) { chook(0); gtk_widget_destroy(allcol_window); free(ctable); return (FALSE); } static void color_refresh() { gtk_list_select_item(GTK_LIST(allcol_list), allcol_idx); /* Stupid GTK+ does nothing for gtk_widget_queue_draw(allcol_list) */ gtk_container_foreach(GTK_CONTAINER(allcol_list), (GtkCallback)gtk_widget_queue_draw, NULL); } static gboolean color_expose( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) { RGBA16 *cc = user_data; unsigned char r = cc->r / 257, g = cc->g / 257, b = cc->b / 257, *rgb; int x = event->area.x, y = event->area.y, w = event->area.width, h = event->area.height; int i, j = w * h * 3; rgb = malloc(j); if (rgb) { for (i = 0; i < j; i += 3) { rgb[i] = r; rgb[i + 1] = g; rgb[i + 2] = b; } gdk_draw_rgb_image( widget->window, widget->style->black_gc, x, y, w, h, GDK_RGB_DITHER_NONE, rgb, w*3 ); free(rgb); } return FALSE; } static void color_set( GtkColorSelection *selection, gpointer user_data ) { gdouble color[4]; GtkWidget *widget; RGBA16 *cc; GdkColor c; gtk_color_selection_get_color( selection, color ); widget = GTK_WIDGET(gtk_object_get_user_data(GTK_OBJECT(selection))); cc = gtk_object_get_user_data(GTK_OBJECT(widget)); cc->r = rint(color[0] * 65535.0); cc->g = rint(color[1] * 65535.0); cc->b = rint(color[2] * 65535.0); cc->a = rint(color[3] * 65535.0); c.pixel = 0; c.red = cc->r; c.green = cc->g; c.blue = cc->b; gdk_colormap_alloc_color( gdk_colormap_get_system(), &c, FALSE, TRUE ); gtk_widget_queue_draw(widget); allcol_hook(4); } static void color_select( GtkList *list, GtkWidget *widget, gpointer user_data ) { GtkColorSelection *cs = GTK_COLOR_SELECTION(user_data); RGBA16 *cc = gtk_object_get_user_data(GTK_OBJECT(widget)); gdouble color[4]; #if GTK_MAJOR_VERSION == 2 GdkColor c = {0, cc->r, cc->g, cc->b}; #endif gtk_object_set_user_data( GTK_OBJECT(cs), widget ); color[0] = ((gdouble)(cc->r)) / 65535.0; color[1] = ((gdouble)(cc->g)) / 65535.0; color[2] = ((gdouble)(cc->b)) / 65535.0; color[3] = ((gdouble)(cc->a)) / 65535.0; gtk_signal_disconnect_by_func(GTK_OBJECT(cs), GTK_SIGNAL_FUNC(color_set), NULL); gtk_color_selection_set_color( cs, color ); #if GTK_MAJOR_VERSION == 1 gtk_color_selection_set_color( cs, color); #endif #if GTK_MAJOR_VERSION == 2 gtk_color_selection_set_previous_color(cs, &c); gtk_color_selection_set_previous_alpha(cs, cc->a); #endif allcol_idx = cc - ctable; gtk_signal_connect( GTK_OBJECT(cs), "color_changed", GTK_SIGNAL_FUNC(color_set), NULL ); allcol_hook(3); } static void colour_window(GtkWidget *win, GtkWidget *extbox, int cnt, char **cnames, int alpha, colour_hook chook, GtkSignalFunc lhook) { GtkWidget *vbox, *hbox, *hbut, *button_ok, *button_preview, *button_cancel; GtkWidget *col_list, *l_item, *hbox2, *label, *drw, *swindow, *viewport; GtkWidget *cs; char txt[64], *tmp = txt; int i; GtkAccelGroup* ag = gtk_accel_group_new(); cs = gtk_color_selection_new(); #if GTK_MAJOR_VERSION == 1 gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(cs), alpha); #endif #if GTK_MAJOR_VERSION == 2 gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(cs), alpha); gtk_color_selection_set_has_palette (GTK_COLOR_SELECTION(cs), TRUE); #endif gtk_signal_connect( GTK_OBJECT(cs), "color_changed", GTK_SIGNAL_FUNC(color_set), NULL ); allcol_window = win; allcol_hook = chook; gtk_signal_connect_object(GTK_OBJECT(allcol_window), "delete_event", GTK_SIGNAL_FUNC(allcol_cancel), (gpointer)chook); vbox = gtk_vbox_new( FALSE, 5 ); gtk_widget_show( vbox ); gtk_container_set_border_width( GTK_CONTAINER(vbox), 5 ); gtk_container_add( GTK_CONTAINER(allcol_window), vbox ); hbox = gtk_hbox_new( FALSE, 10 ); gtk_widget_show( hbox ); gtk_box_pack_start( GTK_BOX(vbox), hbox, TRUE, TRUE, 0); swindow = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (swindow); gtk_box_pack_start (GTK_BOX (hbox), swindow, FALSE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); viewport = gtk_viewport_new (NULL, NULL); gtk_widget_show(viewport); gtk_container_add (GTK_CONTAINER (swindow), viewport); allcol_idx = 0; allcol_list = col_list = gtk_list_new(); gtk_signal_connect(GTK_OBJECT(col_list), "select_child", GTK_SIGNAL_FUNC(color_select), cs); gtk_list_set_selection_mode( GTK_LIST(col_list), GTK_SELECTION_BROWSE ); gtk_container_add ( GTK_CONTAINER(viewport), col_list ); gtk_widget_show( col_list ); for (i = 0; i < cnt; i++) { l_item = gtk_list_item_new(); gtk_object_set_user_data( GTK_OBJECT(l_item), (gpointer)(&ctable[i])); if (lhook) gtk_signal_connect(GTK_OBJECT(l_item), "button_press_event", lhook, (gpointer)i); gtk_container_add( GTK_CONTAINER(col_list), l_item ); gtk_widget_show( l_item ); hbox2 = gtk_hbox_new( FALSE, 3 ); gtk_widget_show( hbox2 ); gtk_container_set_border_width( GTK_CONTAINER(hbox2), 3 ); gtk_container_add( GTK_CONTAINER(l_item), hbox2 ); drw = gtk_drawing_area_new(); gtk_drawing_area_size( GTK_DRAWING_AREA(drw), 20, 20 ); gtk_signal_connect(GTK_OBJECT(drw), "expose_event", GTK_SIGNAL_FUNC(color_expose), (gpointer)(&ctable[i])); gtk_box_pack_start( GTK_BOX(hbox2), drw, FALSE, FALSE, 0 ); gtk_widget_show( drw ); if (cnames) tmp = cnames[i]; else sprintf(txt, "%i", i); label = gtk_label_new(tmp); gtk_widget_show( label ); gtk_misc_set_alignment( GTK_MISC(label), 0.0, 1.0 ); gtk_box_pack_start( GTK_BOX(hbox2), label, TRUE, TRUE, 0 ); } gtk_box_pack_start( GTK_BOX(hbox), cs, TRUE, TRUE, 0 ); if (extbox) gtk_box_pack_start(GTK_BOX(vbox), extbox, FALSE, FALSE, 0); hbut = gtk_hbox_new(FALSE, 3); gtk_widget_show( hbut ); gtk_box_pack_start( GTK_BOX(vbox), hbut, FALSE, FALSE, 0 ); button_ok = gtk_button_new_with_label(_("OK")); gtk_widget_show( button_ok ); gtk_widget_add_accelerator (button_ok, "clicked", ag, GDK_KP_Enter, 0, (GtkAccelFlags) 0); gtk_widget_add_accelerator (button_ok, "clicked", ag, GDK_Return, 0, (GtkAccelFlags) 0); gtk_signal_connect_object(GTK_OBJECT(button_ok), "clicked", GTK_SIGNAL_FUNC(allcol_ok), (gpointer)chook); gtk_box_pack_end( GTK_BOX(hbut), button_ok, FALSE, FALSE, 5 ); gtk_widget_set_usize(button_ok, 80, -2); button_preview = gtk_button_new_with_label(_("Preview")); gtk_widget_show( button_preview ); gtk_signal_connect_object(GTK_OBJECT(button_preview), "clicked", GTK_SIGNAL_FUNC(allcol_preview), (gpointer)chook); gtk_box_pack_end( GTK_BOX(hbut), button_preview, FALSE, FALSE, 5 ); gtk_widget_set_usize(button_preview, 80, -2); button_cancel = gtk_button_new_with_label(_("Cancel")); gtk_widget_show( button_cancel ); gtk_widget_add_accelerator (button_cancel, "clicked", ag, GDK_Escape, 0, (GtkAccelFlags) 0); gtk_signal_connect_object(GTK_OBJECT(button_cancel), "clicked", GTK_SIGNAL_FUNC(allcol_cancel), (gpointer)chook); gtk_box_pack_end( GTK_BOX(hbut), button_cancel, FALSE, FALSE, 5 ); gtk_widget_set_usize(button_cancel, 80, -2); gtk_widget_show( cs ); gtk_window_set_transient_for( GTK_WINDOW(allcol_window), GTK_WINDOW(main_window) ); gtk_widget_show( allcol_window ); gtk_window_add_accel_group( GTK_WINDOW(allcol_window), ag ); #if GTK_MAJOR_VERSION == 1 while (gtk_events_pending()) gtk_main_iteration(); gtk_list_select_item( GTK_LIST(col_list), 0 ); // grubby hack needed to start with proper opacity in GTK+1 gtk_widget_queue_resize(allcol_window); /* Reset shortened sliders */ #endif } static void do_allcol() { int i; for (i = 0; i < mem_cols; i++) { mem_pal[i].red = (ctable[i].r + 128) / 257; mem_pal[i].green = (ctable[i].g + 128) / 257; mem_pal[i].blue = (ctable[i].b + 128) / 257; } init_pal(); update_all_views(); gtk_widget_queue_draw( drawing_col_prev ); } static void do_allover() { int i; for (i = 0; i < NUM_CHANNELS; i++) { channel_rgb[i][0] = (ctable[i].r + 128) / 257; channel_rgb[i][1] = (ctable[i].g + 128) / 257; channel_rgb[i][2] = (ctable[i].b + 128) / 257; channel_opacity[i] = (ctable[i].a + 128) / 257; } update_all_views(); } static void do_AB(int idx) { png_color *A0, *B0; A0 = mem_img_bpp == 1 ? &mem_pal[mem_col_A] : &mem_col_A24; B0 = mem_img_bpp == 1 ? &mem_pal[mem_col_B] : &mem_col_B24; A0->red = (ctable[idx].r + 128) / 257; A0->green = (ctable[idx].g + 128) / 257; A0->blue = (ctable[idx].b + 128) / 257; idx++; B0->red = (ctable[idx].r + 128) / 257; B0->green = (ctable[idx].g + 128) / 257; B0->blue = (ctable[idx].b + 128) / 257; pal_refresher(); } static void set_csel() { csel_data->center = RGB_2_INT((ctable[0].r + 128) / 257, (ctable[0].g + 128) / 257, (ctable[0].b + 128) / 257); csel_data->center_a = (ctable[0].a + 128) / 257; csel_data->limit = RGB_2_INT((ctable[1].r + 128) / 257, (ctable[1].g + 128) / 257, (ctable[1].b + 128) / 257); csel_data->limit_a = (ctable[1].a + 128) / 257; csel_preview = RGB_2_INT((ctable[2].r + 128) / 257, (ctable[2].g + 128) / 257, (ctable[2].b + 128) / 257); /* !!! Alpha is disabled for now !!! */ // csel_preview_a = (ctable[2].a + 128) / 257; } GtkWidget *range_spins[2]; static void select_colour(int what) { switch (what) { case 0: /* Cancel */ mem_pal_copy(mem_pal, brcosa_pal); pal_refresher(); break; case 1: /* Preview */ do_allcol(); break; case 2: /* OK */ mem_pal_copy(mem_pal, brcosa_pal); pal_refresher(); spot_undo(UNDO_PAL); do_allcol(); break; } } static gint click_colour(GtkWidget *widget, GdkEventButton *event, gpointer user_data) { /* Middle click sets "from" */ if ((event->type == GDK_BUTTON_PRESS) && (event->button == 2)) gtk_spin_button_set_value(GTK_SPIN_BUTTON(range_spins[0]), (int)user_data); /* Right click sets "to" */ if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) gtk_spin_button_set_value(GTK_SPIN_BUTTON(range_spins[1]), (int)user_data); /* Let click processing continue */ return (FALSE); } static void set_range_spin(GtkButton *button, GtkWidget *spin) { gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), allcol_idx); } static void make_cscale(GtkButton *button, gpointer user_data) { int i, start, stop, start0, mode; unsigned char rgb[6]; double d; mode = wj_option_menu_get_history(GTK_WIDGET(user_data)); start = start0 = read_spin(range_spins[0]); stop = read_spin(range_spins[1]); if (mode <= 1) /* RGB/HSV */ { if (start > stop) { i = start; start = stop; stop = i; } if (stop < start + 2) return; } else if (stop == start) return; /* Gradient */ rgb[0] = (ctable[start].r + 128) / 257; rgb[1] = (ctable[start].g + 128) / 257; rgb[2] = (ctable[start].b + 128) / 257; rgb[3] = (ctable[stop].r + 128) / 257; rgb[4] = (ctable[stop].g + 128) / 257; rgb[5] = (ctable[stop].b + 128) / 257; d = stop - start; if (mode == 0) /* RGB */ { double r0, g0, b0, dr, dg, db; dr = ((int)rgb[3] - rgb[0]) / d; r0 = rgb[0] - dr * start; dg = ((int)rgb[4] - rgb[1]) / d; g0 = rgb[1] - dg * start; db = ((int)rgb[5] - rgb[2]) / d; b0 = rgb[2] - db * start; for (i = start + 1; i < stop; i++) { ctable[i].r = rint(r0 + dr * i) * 257; ctable[i].g = rint(g0 + dg * i) * 257; ctable[i].b = rint(b0 + db * i) * 257; } } else if (mode == 1) /* HSV */ { int t; double h0, dh, s0, ds, v0, dv, hsv[6], hh, ss, vv; rgb2hsv(rgb + 0, hsv + 0); rgb2hsv(rgb + 3, hsv + 3); /* Grey has no hue */ if (hsv[1] == 0.0) hsv[0] = hsv[3]; if (hsv[4] == 0.0) hsv[3] = hsv[0]; /* Always go from 1st to 2nd hue in ascending order */ t = start == start0 ? 0 : 3; if (hsv[t] > hsv[t ^ 3]) hsv[t] -= 6.0; dh = (hsv[3] - hsv[0]) / d; h0 = hsv[0] - dh * start; ds = (hsv[4] - hsv[1]) / d; s0 = hsv[1] - ds * start; dv = (hsv[5] - hsv[2]) / d; v0 = hsv[2] - dv * start; for (i = start + 1; i < stop; i++) { vv = v0 + dv * i; ss = vv - vv * (s0 + ds * i); hh = h0 + dh * i; if (hh < 0.0) hh += 6.0; t = hh; hh = (hh - t) * (vv - ss); if (t & 1) { vv -= hh; hh += vv; } else hh += ss; t >>= 1; rgb[t] = rint(vv); rgb[(t + 1) % 3] = rint(hh); rgb[(t + 2) % 3] = rint(ss); ctable[i].r = rgb[0] * 257; ctable[i].g = rgb[1] * 257; ctable[i].b = rgb[2] * 257; } } else /* Gradient */ { int j, op, c[NUM_CHANNELS + 3]; j = start < stop ? 1 : -1; for (i = start; i != stop + j; i += j) { op = grad_value(c, 0, (i - start) / d); /* Zero opacity - empty slot */ if (!op) ctable[i].r = ctable[i].g = ctable[i].b = 0; else { ctable[i].r = ((c[0] + 128) >> 8) * 257; ctable[i].g = ((c[1] + 128) >> 8) * 257; ctable[i].b = ((c[2] + 128) >> 8) * 257; } } } color_refresh(); } static void select_overlay(int what) { char txt[64]; int i, j; switch (what) { case 0: /* Cancel */ for (i = 0; i < NUM_CHANNELS; i++) // Restore original values { ctable[i] = ctable[i + NUM_CHANNELS]; } case 1: /* Preview */ do_allover(); break; case 2: /* OK */ do_allover(); for (i = 0; i < NUM_CHANNELS; i++) // Save all settings to ini file { for (j = 0; j < 4; j++) { sprintf(txt, "overlay%i%i", i, j); inifile_set_gint32(txt, j < 3 ? channel_rgb[i][j] : channel_opacity[i]); } } break; } } static GtkWidget *AB_spins[NUM_CHANNELS]; static int temp_AB[NUM_CHANNELS][2]; static void select_AB(int what) { int i; switch (what) { case 0: /* Cancel */ do_AB(2); break; case 2: /* OK */ do_AB(2); spot_undo(UNDO_PAL); for (i = CHN_ALPHA; i < NUM_CHANNELS; i++) { channel_col_A[i] = temp_AB[i][0]; channel_col_B[i] = temp_AB[i][1]; } case 1: /* Preview */ do_AB(0); break; case 3: /* Select */ for (i = CHN_ALPHA; i < NUM_CHANNELS; i++) { mt_spinslide_set_value(AB_spins[i], temp_AB[i][allcol_idx]); } break; } } static void AB_spin_moved(GtkAdjustment *adj, gpointer user_data) { temp_AB[(int)user_data][allcol_idx] = ADJ2INT(adj); } static void posterize_AB(GtkButton *button, gpointer user_data) { static int posm[8] = {0, 0xFF00, 0x5500, 0x2480, 0x1100, 0x0840, 0x0410, 0x0204}; int i, pm, ps; ps = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(user_data)); inifile_set_gint32("posterizeInt", ps); if (ps >= 8) return; pm = posm[ps]; ps = 8 - ps; i = (ctable[0].r + 128) / 257; ctable[0].r = (((i >> ps) * pm) >> 8) * 257; i = (ctable[0].g + 128) / 257; ctable[0].g = (((i >> ps) * pm) >> 8) * 257; i = (ctable[0].b + 128) / 257; ctable[0].b = (((i >> ps) * pm) >> 8) * 257; i = (ctable[1].r + 128) / 257; ctable[1].r = (((i >> ps) * pm) >> 8) * 257; i = (ctable[1].g + 128) / 257; ctable[1].g = (((i >> ps) * pm) >> 8) * 257; i = (ctable[1].b + 128) / 257; ctable[1].b = (((i >> ps) * pm) >> 8) * 257; color_refresh(); } static GtkWidget *csel_spin, *csel_toggle; static unsigned char csel_save[CSEL_SVSIZE]; static int csel_preview0, csel_preview_a0; static void select_csel(int what) { int old_over = csel_overlay; switch (what) { case 0: /* Cancel */ csel_overlay = 0; memcpy(csel_data, csel_save, CSEL_SVSIZE); csel_preview = csel_preview0; csel_preview_a = csel_preview_a0; csel_reset(csel_data); break; case 1: /* Preview */ case 2: /* OK */ csel_overlay = 0; gtk_spin_button_update(GTK_SPIN_BUTTON(csel_spin)); csel_data->range = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(csel_spin)); csel_data->invert = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(csel_toggle)); set_csel(); csel_reset(csel_data); if (what != 1) break; old_over = 0; csel_overlay = 1; break; case 4: /* Set */ csel_overlay = 0; if (allcol_idx != 1) break; /* Only for limit */ set_csel(); csel_data->range = csel_eval(csel_data->mode, csel_data->center, csel_data->limit); gtk_spin_button_set_value(GTK_SPIN_BUTTON(csel_spin), csel_data->range); break; } if ((old_over != csel_overlay) && drawing_canvas) gtk_widget_queue_draw(drawing_canvas); } static void csel_mode_changed(GtkToggleButton *widget, gpointer user_data) { int old_over; if (!gtk_toggle_button_get_active(widget)) return; if (csel_data->mode == (int)user_data) return; old_over = csel_overlay; csel_overlay = 0; csel_data->mode = (int)user_data; set_csel(); csel_data->range = csel_eval(csel_data->mode, csel_data->center, csel_data->limit); gtk_spin_button_set_value(GTK_SPIN_BUTTON(csel_spin), csel_data->range); if (old_over && drawing_canvas) gtk_widget_queue_draw(drawing_canvas); } void colour_selector( int cs_type ) // Bring up GTK+ colour wheel { GtkWidget *win, *extbox, *button, *spin, *opt; int i, j; if (cs_type == COLSEL_EDIT_ALL) { char *fromto[] = { _("From"), _("To") }; char *scales[] = { "RGB", "HSV", _("Gradient") }; mem_pal_copy( brcosa_pal, mem_pal ); // Remember old settings ctable = malloc(mem_cols * sizeof(RGBA16)); if (!ctable) return; for (i = 0; i < mem_cols; i++) { ctable[i].r = mem_pal[i].red * 257; ctable[i].g = mem_pal[i].green * 257; ctable[i].b = mem_pal[i].blue * 257; ctable[i].a = 65535; } /* Prepare range controls */ extbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(extbox), gtk_label_new(_("Scale")), FALSE, FALSE, 0); for (i = 0; i < 2; i++) { button = add_a_button(fromto[i], 4, extbox, TRUE); spin = range_spins[i] = add_a_spin(0, 0, 255); gtk_box_pack_start(GTK_BOX(extbox), spin, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(set_range_spin), spin); } opt = wj_option_menu(scales, 3, 0, NULL, NULL); gtk_container_set_border_width(GTK_CONTAINER(opt), 4); gtk_box_pack_start(GTK_BOX(extbox), opt, TRUE, TRUE, 0); button = add_a_button(_("Create"), 4, extbox, TRUE); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(make_cscale), (gpointer)opt); /* Protect sliders from option menu in GTK1 */ #if GTK_MAJOR_VERSION == 1 extbox = widget_align_minsize(extbox, 0, 0); #endif gtk_widget_show_all(extbox); win = add_a_window(GTK_WINDOW_TOPLEVEL, _("Palette Editor"), GTK_WIN_POS_MOUSE, TRUE); colour_window(win, extbox, mem_cols, NULL, FALSE, select_colour, GTK_SIGNAL_FUNC(click_colour)); /* Protect sliders from option menu in GTK1 */ #if GTK_MAJOR_VERSION == 1 gtk_container_set_resize_mode(GTK_CONTAINER(extbox), GTK_RESIZE_QUEUE); #endif } if (cs_type == COLSEL_OVERLAYS) { ctable = malloc(NUM_CHANNELS * 2 * sizeof(RGBA16)); if (!ctable) return; for (i = 0; i < NUM_CHANNELS; i++) { ctable[i].r = channel_rgb[i][0] * 257; ctable[i].g = channel_rgb[i][1] * 257; ctable[i].b = channel_rgb[i][2] * 257; ctable[i].a = channel_opacity[i] * 257; /* Save old value in the same array */ ctable[i + NUM_CHANNELS] = ctable[i]; } win = add_a_window(GTK_WINDOW_TOPLEVEL, _("Configure Overlays"), GTK_WIN_POS_CENTER, TRUE); colour_window(win, NULL, NUM_CHANNELS, allchannames, TRUE, select_overlay, NULL); } if (cs_type == COLSEL_EDIT_AB) { static char *AB_txt[] = { "A", "B" }; png_color *A0, *B0; A0 = mem_img_bpp == 1 ? &mem_pal[mem_col_A] : &mem_col_A24; B0 = mem_img_bpp == 1 ? &mem_pal[mem_col_B] : &mem_col_B24; ctable = malloc(4 * sizeof(RGBA16)); if (!ctable) return; ctable[0].r = A0->red * 257; ctable[0].g = A0->green * 257; ctable[0].b = A0->blue * 257; ctable[0].a = 65535; ctable[1].r = B0->red * 257; ctable[1].g = B0->green * 257; ctable[1].b = B0->blue * 257; ctable[1].a = 65535; /* Save previous values right here */ ctable[2] = ctable[0]; ctable[3] = ctable[1]; /* Prepare posterize controls */ extbox = gtk_table_new(3, 3, FALSE); win = gtk_hbox_new(FALSE, 0); gtk_table_attach(GTK_TABLE(extbox), win, 0, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); button = add_a_button(_("Posterize"), 4, win, TRUE); spin = add_a_spin(inifile_get_gint32("posterizeInt", 1), 1, 8); gtk_box_pack_start(GTK_BOX(win), spin, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(posterize_AB), spin); /* Prepare channel A/B controls */ for (i = CHN_ALPHA; i < NUM_CHANNELS; i++) { j = i - CHN_ALPHA; spin = gtk_label_new(allchannames[i]); gtk_table_attach(GTK_TABLE(extbox), spin, j, j + 1, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0); gtk_misc_set_alignment(GTK_MISC(spin), 1.0 / 3.0, 0.5); spin = AB_spins[i] = mt_spinslide_new(-1, -1); gtk_table_attach(GTK_TABLE(extbox), spin, j, j + 1, 2, 3, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); mt_spinslide_set_range(spin, 0, 255); mt_spinslide_set_value(spin, channel_col_A[i]); mt_spinslide_connect(spin, GTK_SIGNAL_FUNC(AB_spin_moved), (gpointer)i); temp_AB[i][0] = channel_col_A[i]; temp_AB[i][1] = channel_col_B[i]; } gtk_widget_show_all(extbox); win = add_a_window(GTK_WINDOW_TOPLEVEL, _("Colour Editor"), GTK_WIN_POS_MOUSE, TRUE); colour_window(win, extbox, 2, AB_txt, FALSE, select_AB, NULL); } if (cs_type == COLSEL_EDIT_CSEL) { char *csel_txt[] = { _("Centre"), _("Limit"), _("Preview") }; char *csel_modes[] = { _("Sphere"), _("Angle"), _("Cube"), NULL }; if (!csel_data) { csel_init(); if (!csel_data) return; } /* Save previous values */ memcpy(csel_save, csel_data, CSEL_SVSIZE); csel_preview0 = csel_preview; csel_preview_a0 = csel_preview_a; ctable = malloc(3 * sizeof(RGBA16)); if (!ctable) return; ctable[0].r = INT_2_R(csel_data->center) * 257; ctable[0].g = INT_2_G(csel_data->center) * 257; ctable[0].b = INT_2_B(csel_data->center) * 257; ctable[0].a = csel_data->center_a * 257; ctable[1].r = INT_2_R(csel_data->limit) * 257; ctable[1].g = INT_2_G(csel_data->limit) * 257; ctable[1].b = INT_2_B(csel_data->limit) * 257; ctable[1].a = csel_data->limit_a * 257; ctable[2].r = INT_2_R(csel_preview) * 257; ctable[2].g = INT_2_G(csel_preview) * 257; ctable[2].b = INT_2_B(csel_preview) * 257; ctable[2].a = csel_preview_a * 257; /* Prepare extra controls */ extbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(extbox), gtk_label_new(_("Range")), FALSE, FALSE, 0); spin = csel_spin = add_a_spin(0, 0, 765); gtk_box_pack_start(GTK_BOX(extbox), spin, FALSE, FALSE, 0); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 2); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), csel_data->range); csel_toggle = add_a_toggle(_("Inverse"), extbox, csel_data->invert); win = wj_radio_pack(csel_modes, -1, 1, csel_data->mode, NULL, GTK_SIGNAL_FUNC(csel_mode_changed)); gtk_box_pack_start(GTK_BOX(extbox), win, FALSE, FALSE, 0); gtk_widget_show_all(extbox); win = add_a_window(GTK_WINDOW_TOPLEVEL, _("Colour-Selective Mode"), GTK_WIN_POS_CENTER, TRUE); /* !!! Alpha ranges not implemented yet !!! */ // colour_window(win, extbox, 3, csel_txt, TRUE, select_csel, NULL); colour_window(win, extbox, 3, csel_txt, FALSE, select_csel, NULL); } } void pressed_allcol( GtkMenuItem *menu_item, gpointer user_data ) { colour_selector( COLSEL_EDIT_ALL ); } void choose_colours() { colour_selector(COLSEL_EDIT_AB); } /// QUANTIZE WINDOW #define DITH_NONE 0 #define DITH_FS 1 #define DITH_STUCKI 2 #define DITH_ORDERED 3 #define DITH_OLDFS 4 #define DITH_OLDDITHER 5 #define DITH_OLDSCATTER 6 #define DITH_MAX 7 GtkWidget *quantize_window, *quantize_spin, *quantize_dither; GtkWidget *dither_serpent, *dither_spin; int quantize_cols, quantize_mode, dither_mode; /* Dither settings - persistent */ int dither_cspace = 1, dither_dist = 2, dither_limit = 0; int dither_scan = TRUE, dither_sel = 0; double dither_fract[2] = {1.0, 0.0}; static void click_quantize_radio(GtkWidget *widget, gpointer data) { if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) return; quantize_mode = (int)data; /* No dither for exact conversion */ gtk_widget_set_sensitive(quantize_dither, quantize_mode != 0); } static void click_quantize_ok(GtkWidget *widget, gpointer data) { int i, dither, new_cols, err = 0; unsigned char *old_image = mem_img[CHN_IMAGE], newpal[3][256]; double efrac; /* Dithering filters */ /* Floyd-Steinberg dither */ static short fs_dither[16] = { 16, 0, 0, 0, 7, 0, 0, 3, 5, 1, 0, 0, 0, 0, 0, 0 }; /* Stucki dither */ static short s_dither[16] = { 42, 0, 0, 0, 8, 4, 2, 4, 8, 4, 2, 1, 2, 4, 2, 1 }; dither = quantize_mode ? dither_mode : DITH_NONE; new_cols = read_spin(quantize_spin); new_cols = new_cols < 1 ? 1 : new_cols > 256 ? 256 : new_cols; dither_scan = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dither_serpent)); efrac = 0.01 * read_spin(dither_spin); dither_fract[dither_sel ? 1 : 0] = efrac; gtk_widget_destroy(quantize_window); /* Paranoia */ if ((quantize_mode >= 5) || (dither >= DITH_MAX)) return; i = undo_next_core(UC_NOCOPY, mem_width, mem_height, 1, CMASK_IMAGE); if (i) { memory_errors(2); return; } switch (quantize_mode) { case 0: /* Use image colours */ new_cols = quantize_cols; err = mem_convert_indexed(); dither = DITH_MAX; break; default: case 1: /* Use current palette */ for (i = 0; i < new_cols; i++) { newpal[0][i] = mem_pal[i].red; newpal[1][i] = mem_pal[i].green; newpal[2][i] = mem_pal[i].blue; } break; case 2: /* DL1 quantizer */ err = dl1quant(old_image, mem_width, mem_height, new_cols, newpal); break; case 3: /* DL3 quantizer */ err = dl3quant(old_image, mem_width, mem_height, new_cols, newpal); break; case 4: /* Wu quantizer */ err = wu_quant(old_image, mem_width, mem_height, new_cols, newpal); break; } if (quantize_mode > 1) { for (i = 0; i < new_cols; i++) { mem_pal[i].red = newpal[0][i]; mem_pal[i].green = newpal[1][i]; mem_pal[i].blue = newpal[2][i]; } } if (err) dither = DITH_MAX; switch (dither) { case DITH_NONE: case DITH_FS: case DITH_STUCKI: mem_dither(old_image, new_cols, dither == DITH_NONE ? NULL : dither == DITH_FS ? fs_dither : s_dither, dither_cspace, dither_dist, dither_limit, dither_sel, dither_scan, efrac); break; // !!! No code yet - temporarily disabled !!! // case DITH_ORDERED: case DITH_OLDFS: err = dl3floste(old_image, mem_img[CHN_IMAGE], mem_width, mem_height, new_cols, 1, newpal); break; case DITH_OLDDITHER: err = mem_quantize(old_image, new_cols, 2); break; case DITH_OLDSCATTER: err = mem_quantize(old_image, new_cols, 3); break; default: case DITH_MAX: break; } // If the user is pasting or smudging, lose it! if ((tool_type == TOOL_SELECT) && (marq_status >= MARQUEE_PASTE)) pressed_select_none(NULL, NULL); if (tool_type == TOOL_SMUDGE) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(icon_buttons[DEFAULT_TOOL_ICON]), TRUE); mem_cols = new_cols; mem_col_A = mem_cols > 1 ? 1 : 0; mem_col_B = 0; update_menus(); init_pal(); update_all_views(); gtk_widget_queue_draw(drawing_col_prev); } static void toggle_quantize(GtkToggleButton *button, GtkNotebook *book) { int i = gtk_toggle_button_get_active(button); gtk_notebook_set_page(book, i ? 1 : 0); } static void toggle_selective(GtkWidget *btn, gpointer user_data) { if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn))) return; /* Selectivity state toggled */ if ((dither_sel == 0) ^ ((int)user_data == 0)) { dither_fract[dither_sel ? 1 : 0] = 0.01 * read_spin(dither_spin); gtk_spin_button_set_value(GTK_SPIN_BUTTON(dither_spin), 100.0 * ((int)user_data ? dither_fract[1] : dither_fract[0])); } dither_sel = (int)user_data; } void pressed_quantize(GtkMenuItem *menu_item, gpointer user_data) { GtkWidget *mainbox, *topbox, *notebook, *page0, *page1, *button; GtkWidget *label, *vbox, *hbox; char *rad_txt[] = {_("Exact Conversion"), _("Use Current Palette"), _("DL1 Quantize (fastest)"), _("DL3 Quantize (very slow, better quality)"), _("Wu Quantize (best method for small palettes)"), NULL }; char *rad_txt2[] = {_("None"), _("Floyd-Steinberg"), _("Stucki"), // !!! "Ordered" not done yet !!! /* _("Ordered") */ "", _("Floyd-Steinberg (quick)"), _("Dithered (effect)"), _("Scattered (effect)"), NULL }; char *dist_txt[] = {_("Largest (Linf)"), _("Sum (L1)"), _("Euclidean (L2)")}; char *clamp_txt[] = {_("Gamut"), _("Weakly"), _("Strongly")}; char *err_txt[] = {_("Off"), _("Separate/Sum"), _("Separate/Split"), _("Length/Sum"), _("Length/Split"), NULL}; static char *csp_txt[] = {"RGB", "sRGB", "LXN"}; quantize_cols = mem_cols_used(257); quantize_window = add_a_window(GTK_WINDOW_TOPLEVEL, _("Convert To Indexed"), GTK_WIN_POS_CENTER, TRUE); mainbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(quantize_window), mainbox); /* Colours spin */ topbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(mainbox), topbox, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(topbox), 10); label = gtk_label_new(_("Indexed Colours To Use")); gtk_box_pack_start(GTK_BOX(topbox), label, FALSE, FALSE, 0); quantize_spin = add_a_spin(quantize_cols > 256 ? mem_cols : quantize_cols, 1, 256); gtk_box_pack_start(GTK_BOX(topbox), quantize_spin, TRUE, TRUE, 0); /* Notebook */ notebook = gtk_notebook_new(); gtk_box_pack_start(GTK_BOX(mainbox), notebook, TRUE, TRUE, 0); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); page0 = gtk_vbox_new(FALSE, 0); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page0, NULL); page1 = gtk_vbox_new(FALSE, 0); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page1, NULL); button = gtk_toggle_button_new_with_label(_("Settings")); gtk_box_pack_end(GTK_BOX(topbox), button, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(button), 5); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); gtk_signal_connect(GTK_OBJECT(button), "toggled", GTK_SIGNAL_FUNC(toggle_quantize), GTK_NOTEBOOK(notebook)); /* Main page - Palette frame */ /* No exact transfer if too many colours */ if (quantize_cols > 256) rad_txt[0] = ""; vbox = wj_radio_pack(rad_txt, -1, 0, quantize_cols > 256 ? 4 : 0, &quantize_mode, GTK_SIGNAL_FUNC(click_quantize_radio)); add_with_frame(page0, _("Palette"), vbox, 5); /* Main page - Dither frame */ hbox = wj_radio_pack(rad_txt2, -1, 6, quantize_cols > 256 ? DITH_OLDFS : DITH_NONE, &dither_mode, NULL); quantize_dither = add_with_frame(page0, _("Dither"), hbox, 5); if (quantize_cols <= 256) gtk_widget_set_sensitive(quantize_dither, FALSE); /* Settings page */ hbox = wj_radio_pack(csp_txt, 3, 1, dither_cspace, &dither_cspace, NULL); add_with_frame(page1, _("Colour space"), hbox, 5); hbox = wj_radio_pack(dist_txt, 3, 1, dither_dist, &dither_dist, NULL); add_with_frame(page1, _("Difference measure"), hbox, 5); hbox = wj_radio_pack(clamp_txt, 3, 1, dither_limit, &dither_limit, NULL); add_with_frame(page1, _("Reduce colour bleed"), hbox, 5); dither_serpent = add_a_toggle(_("Serpentine scan"), page1, dither_scan); hbox = gtk_hbox_new(FALSE, 5); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(page1), hbox, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); label = gtk_label_new(_("Error propagation, %")); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); dither_spin = add_a_spin(100, 0, 100); gtk_box_pack_start(GTK_BOX(hbox), dither_spin, TRUE, TRUE, 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(dither_spin), (dither_sel ? dither_fract[1] : dither_fract[0]) * 100.0); hbox = wj_radio_pack(err_txt, -1, 3, dither_sel, &dither_sel, GTK_SIGNAL_FUNC(toggle_selective)); add_with_frame(page1, _("Selective error propagation"), hbox, 5); /* OK / Cancel */ hbox = OK_box(0, quantize_window, _("OK"), GTK_SIGNAL_FUNC(click_quantize_ok), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); gtk_box_pack_start(GTK_BOX(mainbox), hbox, FALSE, FALSE, 0); gtk_window_set_transient_for(GTK_WINDOW(quantize_window), GTK_WINDOW(main_window)); gtk_widget_show_all(quantize_window); } /// GRADIENT WINDOW static int grad_channel; static grad_info grad_temps[NUM_CHANNELS]; static grad_map grad_tmaps[NUM_CHANNELS + 1]; static grad_store grad_tbytes; static GtkWidget *grad_window; static GtkWidget *grad_spin_len, *grad_spin_rep, *grad_spin_ofs; static GtkWidget *grad_opt_type, *grad_opt_bound; static GtkWidget *grad_ss_pre; static GtkWidget *grad_opt_gtype, *grad_opt_otype; static GtkWidget *grad_check_grev, *grad_check_orev; static unsigned char grad_pad[GRAD_POINTS * 3], grad_mpad[GRAD_POINTS]; static int grad_cnt, grad_ofs, grad_slot, grad_mode; static GtkWidget *grad_ed_cs, *grad_ed_opt, *grad_ed_ss, *grad_ed_tog; static GtkWidget *grad_ed_len, *grad_ed_bar[16], *grad_ed_left, *grad_ed_right; #define SLOT_SIZE 15 #define PPAD_SLOT 11 #define PPAD_XSZ 32 #define PPAD_YSZ 8 #define PPAD_WIDTH(X) (PPAD_XSZ * (X) + 1) #define PPAD_HEIGHT(X) (PPAD_YSZ * (X) + 1) static gboolean palette_pad_draw(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data) { unsigned char *rgb, *tmp; int i, j, k, w, h, col, row, cellsize = PPAD_SLOT; int channel = (int)user_data; GdkPixmap *pmap = gtk_object_get_user_data(GTK_OBJECT(widget)); if (!pmap) return (TRUE); w = PPAD_WIDTH(cellsize); h = PPAD_HEIGHT(cellsize); row = w * 3; rgb = malloc(h * row); if (!rgb) return (TRUE); memset(rgb, 0, h * row); for (col = 0 , i = 1; i < h - 1; i += cellsize) { tmp = rgb + i * row; for (j = 3; j < row - 3; j += cellsize * 3 , col++) { if (channel == CHN_IMAGE) /* Palette as such */ { if (col < mem_cols) /* Draw only existing colors */ { tmp[j + 0] = mem_pal[col].red; tmp[j + 1] = mem_pal[col].green; tmp[j + 2] = mem_pal[col].blue; } } else if (channel >= 0) /* Utility */ { k = channel_rgb[channel][0] * col; tmp[j + 0] = (k + (k >> 8) + 1) >> 8; k = channel_rgb[channel][1] * col; tmp[j + 1] = (k + (k >> 8) + 1) >> 8; k = channel_rgb[channel][2] * col; tmp[j + 2] = (k + (k >> 8) + 1) >> 8; } else /* Opacity */ { tmp[j + 0] = tmp[j + 1] = tmp[j + 2] = col; } for (k = j + 3; k < j + cellsize * 3 - 3; k++) tmp[k] = tmp[k - 3]; } for (j = i + 1; j < i + cellsize - 1; j++) memcpy(rgb + j * row, tmp, row); } gdk_draw_rgb_image(pmap, widget->style->black_gc, 0, 0, w, h, GDK_RGB_DITHER_NONE, rgb, row); free(rgb); return (TRUE); } static void grad_edit_set_rgb(GtkColorSelection *selection, gpointer user_data); static gboolean palette_pad_click(GtkWidget *widget, GdkEventButton *event, gpointer user_data) { int x, y, i; /* Only single clicks */ if (event->type != GDK_BUTTON_PRESS) return (TRUE); x = event->x / PPAD_SLOT; y = event->y / PPAD_SLOT; if ((x < 0) || (x >= PPAD_XSZ) || (y < 0) || (y >= PPAD_YSZ)) return (TRUE); i = y * PPAD_XSZ + x; /* Indexed / utility / opacity */ if (!(int)user_data) mt_spinslide_set_value(grad_ed_ss, i); else if (i < mem_cols) /* Valid RGB */ { gdouble color[4] = { (gdouble)mem_pal[i].red / 255.0, (gdouble)mem_pal[i].green / 255.0, (gdouble)mem_pal[i].blue / 255.0, 1.0}; #if GTK_MAJOR_VERSION == 1 GtkColorSelection *cs = GTK_COLOR_SELECTION(grad_ed_cs); gdouble oldcolor[4]; /* Preserve old color, invoke "color_changed" handler */ memcpy(oldcolor, cs->old_values + 3, sizeof(oldcolor)); gtk_color_selection_set_color(cs, oldcolor); gtk_color_selection_set_color(cs, color); grad_edit_set_rgb(cs, NULL); #else gtk_color_selection_set_color(GTK_COLOR_SELECTION(grad_ed_cs), color); #endif } return (TRUE); } static void click_grad_edit_ok(GtkWidget *widget) { int idx = (grad_channel == CHN_IMAGE) && (mem_img_bpp == 3) ? 0 : grad_channel + 1; if (grad_mode < 0) /* Opacity */ { memcpy(grad_tbytes + GRAD_CUSTOM_OPAC(idx), grad_pad, GRAD_POINTS); memcpy(grad_tbytes + GRAD_CUSTOM_OMAP(idx), grad_mpad, GRAD_POINTS); grad_tmaps[idx].coplen = grad_cnt; } else /* Gradient */ { memcpy(grad_tbytes + GRAD_CUSTOM_DATA(idx), grad_pad, idx ? GRAD_POINTS : GRAD_POINTS * 3); memcpy(grad_tbytes + GRAD_CUSTOM_DMAP(idx), grad_mpad, GRAD_POINTS); grad_tmaps[idx].cvslen = grad_cnt; } gtk_widget_destroy(widget); } static void grad_load_slot(int slot) { gdouble color[4]; int i; #if GTK_MAJOR_VERSION == 2 GdkColor c; #endif if (slot >= grad_cnt) /* Empty slot */ { grad_slot = slot; return; } grad_slot = -1; /* Block circular signal calls */ if (!grad_mode) /* RGB */ { GtkColorSelection *cs = GTK_COLOR_SELECTION(grad_ed_cs); for (i = 0; i < 3; i++) color[i] = (gdouble)grad_pad[slot * 3 + i] / 255.0; color[3] = 1.0; gtk_color_selection_set_color(cs, color); #if GTK_MAJOR_VERSION == 1 gtk_color_selection_set_color(cs, color); #endif #if GTK_MAJOR_VERSION == 2 c.red = grad_pad[slot * 3 + 0] * 257; c.green = grad_pad[slot * 3 + 1] * 257; c.blue = grad_pad[slot * 3 + 2] * 257; gtk_color_selection_set_previous_color(cs, &c); #endif gtk_option_menu_set_history(GTK_OPTION_MENU(grad_ed_opt), grad_mpad[slot]); } else /* Indexed / utility / opacity */ { mt_spinslide_set_value(grad_ed_ss, grad_pad[slot]); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grad_ed_tog), grad_mpad[slot] == GRAD_TYPE_CONST); } grad_slot = slot; } static void grad_redraw_slots(int idx0, int idx1) { if (idx0 < grad_ofs) idx0 = grad_ofs; if (++idx1 > grad_ofs + 16) idx1 = grad_ofs + 16; for (; idx0 < idx1; idx0++) gtk_widget_queue_draw(GTK_BIN(grad_ed_bar[idx0 - grad_ofs])->child); } static void grad_edit_set_mode() { int mode, ix0 = grad_slot; if (grad_slot < 0) return; /* Blocked */ if (!grad_mode) mode = wj_option_menu_get_history(grad_ed_opt); else mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grad_ed_tog)) ? GRAD_TYPE_CONST : GRAD_TYPE_RGB; grad_mpad[grad_slot] = mode; if (grad_cnt <= grad_slot) { ix0 = grad_cnt; grad_cnt = grad_slot + 1; gtk_spin_button_set_value(GTK_SPIN_BUTTON(grad_ed_len), grad_cnt); } grad_redraw_slots(ix0, grad_slot); } static void grad_edit_set_rgb(GtkColorSelection *selection, gpointer user_data) { gdouble color[4]; int i; if (grad_slot < 0) return; /* Blocked */ gtk_color_selection_get_color(selection, color); for (i = 0; i < 3; i++) grad_pad[grad_slot * 3 + i] = rint(color[i] * 255.0); grad_edit_set_mode(); } static void grad_edit_move_slide(GtkAdjustment *adj, gpointer user_data) { if (grad_slot < 0) return; /* Blocked */ grad_pad[grad_slot] = ADJ2INT(adj); grad_edit_set_mode(); } static void grad_edit_length(GtkAdjustment *adj, gpointer user_data) { int l0 = grad_cnt; grad_cnt = ADJ2INT(adj); if (l0 < grad_cnt) grad_redraw_slots(l0, grad_cnt - 1); else if (l0 > grad_cnt) grad_redraw_slots(grad_cnt, l0 - 1); } static void grad_edit_scroll(GtkButton *button, gpointer user_data) { int dir = (int)user_data; grad_ofs += dir; gtk_widget_set_sensitive(grad_ed_left, !!grad_ofs); gtk_widget_set_sensitive(grad_ed_right, grad_ofs < GRAD_POINTS - 16); grad_redraw_slots(0, GRAD_POINTS); } static void grad_edit_slot(GtkWidget *btn, gpointer user_data) { if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn))) return; grad_load_slot((int)user_data + grad_ofs); } static gboolean grad_draw_slot(GtkWidget *widget, GdkEventExpose *event, gpointer idx) { unsigned char rgb[SLOT_SIZE * 2 * 3]; int i, n = (int)idx + grad_ofs, mode = n >= grad_cnt ? -2 : grad_mode; switch (mode) { default: /* Error */ case -2: /* Empty */ rgb[0] = rgb[1] = rgb[2] = 178; break; case -1: /* Opacity */ rgb[0] = rgb[1] = rgb[2] = grad_pad[n]; break; case 0: /* RGB */ rgb[0] = grad_pad[n * 3 + 0]; rgb[1] = grad_pad[n * 3 + 1]; rgb[2] = grad_pad[n * 3 + 2]; break; case CHN_IMAGE + 1: /* Indexed */ i = grad_pad[n]; if (i >= mem_cols) rgb[0] = rgb[1] = rgb[2] = 0; else { rgb[0] = mem_pal[i].red; rgb[1] = mem_pal[i].green; rgb[2] = mem_pal[i].blue; } break; case CHN_ALPHA + 1: /* Alpha */ case CHN_SEL + 1: /* Selection */ case CHN_MASK + 1: /* Mask */ i = channel_rgb[mode - 1][0] * grad_pad[n]; rgb[0] = (i + (i >> 8) + 1) >> 8; i = channel_rgb[mode - 1][1] * grad_pad[n]; rgb[1] = (i + (i >> 8) + 1) >> 8; i = channel_rgb[mode - 1][2] * grad_pad[n]; rgb[2] = (i + (i >> 8) + 1) >> 8; break; } for (i = 3; i < SLOT_SIZE * 2 * 3; i++) rgb[i] = rgb[i - 3]; if (mode == -2) /* Empty slot - show that */ memset(rgb, 128, SLOT_SIZE * 3); gdk_draw_rgb_image(widget->window, widget->style->black_gc, 0, 0, SLOT_SIZE, SLOT_SIZE, GDK_RGB_DITHER_NONE, rgb + SLOT_SIZE * 3, -3); return (TRUE); } static void grad_edit(GtkWidget *widget, gpointer user_data) { GtkWidget *win, *mainbox, *hbox, *hbox2, *pix, *cs, *ss, *sw, *btn; int i, idx, opac = (int)user_data != 0; idx = (grad_channel == CHN_IMAGE) && (mem_img_bpp == 3) ? 0 : grad_channel + 1; /* Copy to temp */ if (opac) { memcpy(grad_pad, grad_tbytes + GRAD_CUSTOM_OPAC(idx), GRAD_POINTS); memcpy(grad_mpad, grad_tbytes + GRAD_CUSTOM_OMAP(idx), GRAD_POINTS); grad_cnt = grad_tmaps[idx].coplen; } else { memcpy(grad_pad, grad_tbytes + GRAD_CUSTOM_DATA(idx), idx ? GRAD_POINTS : GRAD_POINTS * 3); memcpy(grad_mpad, grad_tbytes + GRAD_CUSTOM_DMAP(idx), GRAD_POINTS); grad_cnt = grad_tmaps[idx].cvslen; } if (grad_cnt < 2) grad_cnt = 2; grad_ofs = grad_slot = 0; grad_mode = opac ? -1 : idx; win = add_a_window(GTK_WINDOW_TOPLEVEL, _("Edit Gradient"), GTK_WIN_POS_CENTER, TRUE); mainbox = gtk_vbox_new(FALSE, 5); gtk_container_set_border_width(GTK_CONTAINER(mainbox), 5); gtk_container_add(GTK_CONTAINER(win), mainbox); /* Palette pad */ pix = wj_pixmap(PPAD_WIDTH(PPAD_SLOT), PPAD_HEIGHT(PPAD_SLOT)); sw = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); gtk_container_add(GTK_CONTAINER(sw), pix); gtk_box_pack_start(GTK_BOX(mainbox), sw, FALSE, FALSE, 0); gtk_signal_connect(GTK_OBJECT(pix), "configure_event", GTK_SIGNAL_FUNC(palette_pad_draw), (gpointer)(opac ? -1 : grad_channel)); gtk_signal_connect(GTK_OBJECT(pix), "button_press_event", GTK_SIGNAL_FUNC(palette_pad_click), (gpointer)!grad_mode); add_hseparator(mainbox, -2, 10); /* Editor widgets */ if (!grad_mode) /* RGB */ { char *interp[] = {"RGB", "HSV", _("Backward HSV"), _("Constant")}; grad_ed_cs = cs = gtk_color_selection_new(); gtk_box_pack_start(GTK_BOX(mainbox), cs, FALSE, FALSE, 0); #if GTK_MAJOR_VERSION == 1 gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(cs), FALSE); #endif #if GTK_MAJOR_VERSION == 2 gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(cs), FALSE); gtk_color_selection_set_has_palette(GTK_COLOR_SELECTION(cs), TRUE); #endif gtk_signal_connect(GTK_OBJECT(cs), "color_changed", GTK_SIGNAL_FUNC(grad_edit_set_rgb), NULL); grad_ed_opt = sw = wj_option_menu(interp, 4, 0, NULL, GTK_SIGNAL_FUNC(grad_edit_set_mode)); /* Protect sliders from option menu in GTK1 */ #if GTK_MAJOR_VERSION == 1 sw = widget_align_minsize(sw, 0, 0); #endif } else /* Indexed / utility / opacity */ { grad_ed_ss = ss = mt_spinslide_new(-2, -2); gtk_box_pack_start(GTK_BOX(mainbox), ss, FALSE, FALSE, 0); mt_spinslide_set_range(ss, 0, grad_mode == CHN_IMAGE + 1 ? mem_cols - 1 : 255); mt_spinslide_connect(ss, GTK_SIGNAL_FUNC(grad_edit_move_slide), NULL); grad_ed_tog = sw = sig_toggle(_("Constant"), FALSE, NULL, GTK_SIGNAL_FUNC(grad_edit_set_mode)); } hbox = gtk_hbox_new(TRUE, 5); gtk_box_pack_start(GTK_BOX(mainbox), hbox, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0); hbox2 = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox2), gtk_label_new(_("Points:")), FALSE, FALSE, 0); grad_ed_len = sw = add_a_spin(grad_cnt, 2, GRAD_POINTS); gtk_box_pack_start(GTK_BOX(hbox2), sw, FALSE, FALSE, 0); spin_connect(sw, GTK_SIGNAL_FUNC(grad_edit_length), NULL); /* Gradient bar */ hbox2 = gtk_hbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(mainbox), hbox2, FALSE, FALSE, 0); grad_ed_left = btn = gtk_button_new(); gtk_box_pack_start(GTK_BOX(hbox2), btn, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(btn), gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE)); gtk_widget_set_sensitive(btn, FALSE); gtk_signal_connect(GTK_OBJECT(btn), "clicked", GTK_SIGNAL_FUNC(grad_edit_scroll), (gpointer)(-1)); btn = NULL; for (i = 0; i < 16; i++) { grad_ed_bar[i] = btn = gtk_radio_button_new_from_widget( GTK_RADIO_BUTTON_0(btn)); gtk_box_pack_start(GTK_BOX(hbox2), btn, TRUE, TRUE, 0); gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(btn), FALSE); gtk_signal_connect(GTK_OBJECT(btn), "toggled", GTK_SIGNAL_FUNC(grad_edit_slot), (gpointer)i); sw = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(btn), sw); gtk_widget_set_usize(sw, SLOT_SIZE, SLOT_SIZE); gtk_signal_connect(GTK_OBJECT(sw), "expose_event", GTK_SIGNAL_FUNC(grad_draw_slot), (gpointer)i); } grad_ed_right = btn = gtk_button_new(); gtk_box_pack_start(GTK_BOX(hbox2), btn, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(btn), gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE)); gtk_signal_connect(GTK_OBJECT(btn), "clicked", GTK_SIGNAL_FUNC(grad_edit_scroll), (gpointer)1); hbox2 = OK_box(0, win, _("OK"), GTK_SIGNAL_FUNC(click_grad_edit_ok), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); gtk_box_pack_start(GTK_BOX(mainbox), hbox2, FALSE, FALSE, 0); grad_load_slot(0); gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(grad_window)); gtk_widget_show_all(win); /* Protect sliders from option menu in GTK1 */ #if GTK_MAJOR_VERSION == 1 gtk_container_set_resize_mode(GTK_CONTAINER(hbox), GTK_RESIZE_QUEUE); #endif } #define NUM_GTYPES 6 #define NUM_OTYPES 3 static const char gtmap[NUM_GTYPES * 2] = {GRAD_TYPE_RGB, 1, GRAD_TYPE_RGB, 2, GRAD_TYPE_HSV, 2, GRAD_TYPE_BK_HSV, 2, GRAD_TYPE_CONST, 3, GRAD_TYPE_CUSTOM, 3}; static const char opmap[NUM_OTYPES] = {GRAD_TYPE_RGB, GRAD_TYPE_CONST, GRAD_TYPE_CUSTOM}; static void grad_reset_menu(int mode, int bpp) { GList *items = GTK_MENU_SHELL(gtk_option_menu_get_menu( GTK_OPTION_MENU(grad_opt_gtype)))->children; char f = bpp == 1 ? 1 : 2; int i, j; for (j = NUM_GTYPES - 1; j >= 0; j--) { if ((gtmap[j * 2] == mode) && (gtmap[j * 2 + 1] & f)) break; } for (; items; items = items->next) { i = (int)gtk_object_get_user_data(GTK_OBJECT(items->data)); (gtmap[2 * i + 1] & f ? gtk_widget_show : gtk_widget_hide) (GTK_WIDGET(items->data)); } gtk_option_menu_set_history(GTK_OPTION_MENU(grad_opt_gtype), j); } static void store_channel_gradient(int channel) { grad_info *grad = grad_temps + channel; grad_map *gmap = grad_tmaps + channel + 1; if (channel < 0) return; if ((channel == CHN_IMAGE) && (mem_img_bpp == 3)) gmap = grad_tmaps; grad->len = read_spin(grad_spin_len); grad->gmode = wj_option_menu_get_history(grad_opt_type) + 1; grad->rep = read_spin(grad_spin_rep); grad->rmode = wj_option_menu_get_history(grad_opt_bound); grad->ofs = read_spin(grad_spin_ofs); gmap->gtype = gtmap[2 * wj_option_menu_get_history(grad_opt_gtype)]; gmap->grev = !!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grad_check_grev)); gmap->otype = opmap[wj_option_menu_get_history(grad_opt_otype)]; gmap->orev = !!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grad_check_orev)); } static void show_channel_gradient(int channel) { grad_info *grad = grad_temps + channel; grad_map *gmap; int i, idx = channel + 1, bpp = BPP(channel); if (bpp == 3) --idx; gmap = grad_tmaps + idx; gtk_spin_button_set_value(GTK_SPIN_BUTTON(grad_spin_len), grad->len); gtk_option_menu_set_history(GTK_OPTION_MENU(grad_opt_type), grad->gmode - 1); gtk_spin_button_set_value(GTK_SPIN_BUTTON(grad_spin_rep), grad->rep); gtk_option_menu_set_history(GTK_OPTION_MENU(grad_opt_bound), grad->rmode); gtk_spin_button_set_value(GTK_SPIN_BUTTON(grad_spin_ofs), grad->ofs); grad_reset_menu(gmap->gtype, bpp); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grad_check_grev), gmap->grev); for (i = NUM_OTYPES - 1; (i >= 0) && (opmap[i] != gmap->otype); i--); gtk_option_menu_set_history(GTK_OPTION_MENU(grad_opt_otype), i); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grad_check_orev), gmap->orev); } static void click_grad_apply(GtkWidget *widget) { int i; store_channel_gradient(grad_channel); memcpy(gradient, grad_temps, sizeof(grad_temps)); memcpy(graddata, grad_tmaps, sizeof(grad_tmaps)); memcpy(gradbytes, grad_tbytes, sizeof(grad_tbytes)); grad_opacity = mt_spinslide_get_value(grad_ss_pre); inifile_set_gint32("gradientOpacity", grad_opacity); for (i = 0; i < NUM_CHANNELS; i++) grad_update(gradient + i); for (i = 0; i <= NUM_CHANNELS; i++) gmap_setup(graddata + i, gradbytes, i); grad_def_update(); toolbar_update_settings(); if ((tool_type == TOOL_GRADIENT) && grad_opacity) gtk_widget_queue_draw(drawing_canvas); } static void click_grad_ok(GtkWidget *widget) { click_grad_apply(widget); gtk_widget_destroy(widget); } static void grad_channel_changed(GtkToggleButton *widget, gpointer user_data) { if ((int)user_data == grad_channel) return; if (!gtk_toggle_button_get_active(widget)) return; store_channel_gradient(grad_channel); grad_channel = -1; show_channel_gradient((int)user_data); grad_channel = (int)user_data; } static void grad_selector_box(GtkWidget *box, char **mtext, int op) { GtkWidget *vbox, *hbox, *menu, *rev, *btn; vbox = gtk_vbox_new(FALSE, 0); add_with_frame_x(box, op ? _("Opacity") : _("Gradient"), vbox, 5, TRUE); menu = wj_option_menu(mtext, op ? NUM_OTYPES : NUM_GTYPES, 0, NULL, NULL); gtk_container_set_border_width(GTK_CONTAINER(menu), 5); gtk_box_pack_start(GTK_BOX(vbox), menu, FALSE, FALSE, 0); hbox = gtk_hbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); rev = add_a_toggle(_("Reverse"), hbox, FALSE); btn = add_a_button(_("Edit Custom"), 5, hbox, TRUE); gtk_signal_connect(GTK_OBJECT(btn), "clicked", GTK_SIGNAL_FUNC(grad_edit), (gpointer)op); if (op) { grad_opt_otype = menu; grad_check_orev = rev; } else { grad_opt_gtype = menu; grad_check_grev = rev; } } void gradient_setup(int mode) { char *gtypes[] = {_("Linear"), _("Bilinear"), _("Radial"), _("Square")}; char *rtypes[] = {_("None"), _("Level"), _("Repeat"), _("Mirror")}; char *gradtypes[] = {_("A to B"), _("A to B (RGB)"), _("A to B (HSV)"), _("A to B (backward HSV)"), _("A only"), _("Custom")}; char *optypes[] = {_("Current to 0"), _("Current only"), _("Custom")}; GtkWidget *win, *mainbox, *hbox, *table, *align; GtkWindowPosition pos = !mode && !inifile_get_gboolean("centerSettings", TRUE) ? GTK_WIN_POS_MOUSE : GTK_WIN_POS_CENTER; #if GTK_MAJOR_VERSION == 1 GtkWidget *hbox1, *hbox2; #endif memcpy(grad_temps, gradient, sizeof(grad_temps)); memcpy(grad_tmaps, graddata, sizeof(grad_tmaps)); memcpy(grad_tbytes, gradbytes, sizeof(grad_tbytes)); grad_channel = mem_channel; grad_window = win = add_a_window(GTK_WINDOW_TOPLEVEL, _("Configure Gradient"), pos, TRUE); mainbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(win), mainbox); /* Channel box */ hbox = wj_radio_pack(allchannames, 4, 1, mem_channel, NULL, GTK_SIGNAL_FUNC(grad_channel_changed)); add_with_frame(mainbox, _("Channel"), hbox, 5); /* Setup block */ table = add_a_table(3, 4, 5, mainbox); add_to_table(_("Length"), table, 0, 0, 5); grad_spin_len = spin_to_table(table, 0, 1, 5, 0, 0, MAX_GRAD); add_to_table(_("Repeat length"), table, 1, 0, 5); grad_spin_rep = spin_to_table(table, 1, 1, 5, 0, 0, MAX_GRAD); add_to_table(_("Offset"), table, 2, 0, 5); grad_spin_ofs = spin_to_table(table, 2, 1, 5, 0, -MAX_GRAD, MAX_GRAD); add_to_table(_("Gradient type"), table, 0, 2, 5); grad_opt_type = wj_option_menu(gtypes, 4, 0, NULL, NULL); add_to_table(_("Extension type"), table, 1, 2, 5); grad_opt_bound = wj_option_menu(rtypes, 4, 0, NULL, NULL); /* Protect spinslider from option menus in GTK1 */ #if GTK_MAJOR_VERSION == 1 align = widget_align_minsize(grad_opt_type, 0, 0); hbox1 = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox1), align, TRUE, TRUE, 0); gtk_table_attach(GTK_TABLE(table), hbox1, 3, 4, 0, 1, GTK_EXPAND | GTK_FILL, 0, 0, 5); align = widget_align_minsize(grad_opt_bound, 0, 0); hbox2 = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox2), align, TRUE, TRUE, 0); gtk_table_attach(GTK_TABLE(table), hbox2, 3, 4, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 5); #else gtk_table_attach(GTK_TABLE(table), grad_opt_type, 3, 4, 0, 1, GTK_EXPAND | GTK_FILL, 0, 0, 5); gtk_table_attach(GTK_TABLE(table), grad_opt_bound, 3, 4, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 5); #endif add_to_table(_("Preview opacity"), table, 2, 2, 5); grad_ss_pre = mt_spinslide_new(-1, -1); mt_spinslide_set_range(grad_ss_pre, 0, 255); mt_spinslide_set_value(grad_ss_pre, grad_opacity); /* !!! Box derivatives can't have their "natural" size set directly */ align = widget_align_minsize(grad_ss_pre, 200, -2); gtk_table_attach(GTK_TABLE(table), align, 3, 4, 2, 3, GTK_EXPAND | GTK_FILL, 0, 0, 5); /* Select page */ hbox = gtk_hbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(mainbox), hbox, FALSE, FALSE, 0); grad_selector_box(hbox, gradtypes, 0); grad_selector_box(hbox, optypes, 1); /* Cancel / Apply / OK */ align = OK_box(0, win, _("OK"), GTK_SIGNAL_FUNC(click_grad_ok), _("Cancel"), GTK_SIGNAL_FUNC(gtk_widget_destroy)); OK_box_add(align, _("Apply"), GTK_SIGNAL_FUNC(click_grad_apply), 1); gtk_box_pack_start(GTK_BOX(mainbox), align, FALSE, FALSE, 0); /* Fill in values */ gtk_widget_show_all(mainbox); show_channel_gradient(mem_channel); gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(main_window)); gtk_widget_show(win); /* Properly set up slider & option menus in GTK1 */ #if GTK_MAJOR_VERSION == 1 gtk_widget_queue_resize(win); gtk_container_set_resize_mode(GTK_CONTAINER(hbox), GTK_RESIZE_QUEUE); gtk_container_set_resize_mode(GTK_CONTAINER(hbox1), GTK_RESIZE_QUEUE); gtk_container_set_resize_mode(GTK_CONTAINER(hbox2), GTK_RESIZE_QUEUE); #endif }