grafX2/src/engine.c
Thomas Bernard 77005eb780 use screen.h instead of sdlscreen.h, etc.
Create grafx2-win32 MSVC++ project
create keycode.h
various fixes
2018-07-02 11:38:51 +02:00

3735 lines
117 KiB
C

/* vim:expandtab:ts=2 sw=2:
*/
/* Grafx2 - The Ultimate 256-color bitmap paint program
Copyright 2007 Adrien Destugues
Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
Grafx2 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; version 2
of the License.
Grafx2 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 Grafx2; if not, see <http://www.gnu.org/licenses/>
*/
/// @file engine.c: Window engine and interface management
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "const.h"
#include "struct.h"
#include "global.h"
#include "graph.h"
#include "misc.h"
#include "special.h"
#include "buttons.h"
#include "operatio.h"
#include "shade.h"
#include "errors.h"
#include "screen.h"
#include "windows.h"
#include "brush.h"
#include "input.h"
#include "engine.h"
#include "pages.h"
#include "layers.h"
#include "factory.h"
#include "loadsave.h"
#include "io.h"
#include "pxsimple.h"
#include "oldies.h"
// we need this as global
short Old_MX = -1;
short Old_MY = -1;
//---------- Annuler les effets des modes de dessin (sauf la grille) ---------
// Variables mémorisants les anciens effets
byte Shade_mode_before_cancel;
byte Quick_shade_mode_before_cancel;
byte Stencil_mode_before_cancel;
byte Sieve_mode_before_cancel;
byte Colorize_mode_before_cancel;
byte Smooth_mode_before_cancel;
byte Tiling_mode_before_cancel;
Func_effect Effect_function_before_cancel;
///This table holds pointers to the saved window backgrounds. We can have up to 8 windows open at a time.
static byte* Window_background[8];
///Save a screen block (usually before erasing it with a new window or a dropdown menu)
static void Save_background(byte **buffer, int x_pos, int y_pos, int width, int height)
{
int index;
if(*buffer != NULL) DEBUG("WARNING : buffer already allocated !!!",0);
*buffer=(byte *) malloc(width*Menu_factor_X*height*Menu_factor_Y*Pixel_width);
if(*buffer==NULL) Error(0);
for (index=0; index<(height*Menu_factor_Y); index++)
Read_line(x_pos,y_pos+index,width*Menu_factor_X,(*buffer)+((int)index*width*Menu_factor_X*Pixel_width));
}
///Restores a screen block
static void Restore_background(byte **buffer, int x_pos, int y_pos, int width, int height)
{
int index;
if(*buffer==NULL) return;
for (index=0; index<height*Menu_factor_Y; index++)
Display_line_fast(x_pos,y_pos+index,width*Menu_factor_X,*buffer+((int)index*width*Menu_factor_X*Pixel_width));
free(*buffer);
*buffer = NULL;
}
///Draw a pixel in a saved screen block (when you sort colors in the palette, for example)
void Pixel_background(int x_pos, int y_pos, byte color)
{
int x_repetition=Pixel_width;
while (x_repetition--)
(Window_background[0][x_pos*Pixel_width+x_repetition+y_pos*Window_width*Pixel_width*Menu_factor_X])=color;
}
///Guess the number of the button that was just clicked
int Button_under_mouse(void)
{
int btn_number;
short x_pos;
short y_pos;
byte current_menu;
byte first_button;
x_pos = Mouse_X / Menu_factor_X;
// Find in which menubar we are
for (current_menu = 0; current_menu < MENUBAR_COUNT; current_menu ++)
{
if (Menu_bars[current_menu].Visible)
{
if (Mouse_Y >= Menu_Y+Menu_factor_Y*(Menu_bars[current_menu].Top) &&
Mouse_Y < Menu_Y+Menu_factor_Y*(Menu_bars[current_menu].Top + Menu_bars[current_menu].Height))
break;
}
}
if (current_menu==MENUBAR_COUNT)
return -1;
y_pos=(Mouse_Y - Menu_Y)/Menu_factor_Y - Menu_bars[current_menu].Top;
if (current_menu == 0) first_button = 0;
else first_button = Menu_bars[current_menu - 1].Last_button_index + 1;
for (btn_number=first_button;btn_number<=Menu_bars[current_menu].Last_button_index;btn_number++)
{
switch(Buttons_Pool[btn_number].Shape)
{
case BUTTON_SHAPE_NO_FRAME :
case BUTTON_SHAPE_RECTANGLE :
if ((x_pos>=Buttons_Pool[btn_number].X_offset) &&
(y_pos>=Buttons_Pool[btn_number].Y_offset) &&
(x_pos<=Buttons_Pool[btn_number].X_offset+Buttons_Pool[btn_number].Width) &&
(y_pos<=Buttons_Pool[btn_number].Y_offset+Buttons_Pool[btn_number].Height))
return btn_number;
break;
case BUTTON_SHAPE_TRIANGLE_TOP_LEFT:
if ((x_pos>=Buttons_Pool[btn_number].X_offset) &&
(y_pos>=Buttons_Pool[btn_number].Y_offset) &&
(x_pos+y_pos-(short)Buttons_Pool[btn_number].Y_offset-(short)Buttons_Pool[btn_number].X_offset<=Buttons_Pool[btn_number].Width))
return btn_number;
break;
case BUTTON_SHAPE_TRIANGLE_BOTTOM_RIGHT:
if ((x_pos<=Buttons_Pool[btn_number].X_offset+Buttons_Pool[btn_number].Width) &&
(y_pos<=Buttons_Pool[btn_number].Y_offset+Buttons_Pool[btn_number].Height) &&
(x_pos+y_pos-(short)Buttons_Pool[btn_number].Y_offset-(short)Buttons_Pool[btn_number].X_offset>=Buttons_Pool[btn_number].Width))
return btn_number;
break;
}
}
return -1;
}
///Draw a menu button, selected or not
void Draw_menu_button(byte btn_number,byte pressed)
{
word start_x;
word start_y;
word width;
word height;
byte * bitmap;
word bitmap_width;
word x_pos;
word y_pos;
byte current_menu;
byte color;
signed char icon;
// Find in which menu the button is
for (current_menu = 0; current_menu < MENUBAR_COUNT; current_menu++)
{
// We found the right bar !
if (Menu_bars[current_menu].Last_button_index >= btn_number &&
(current_menu==0 || Menu_bars[current_menu -1].Last_button_index < btn_number))
{
break;
}
}
start_x = Buttons_Pool[btn_number].X_offset;
start_y = Buttons_Pool[btn_number].Y_offset;
width = Buttons_Pool[btn_number].Width+1;
height = Buttons_Pool[btn_number].Height+1;
icon = Buttons_Pool[btn_number].Icon;
if (icon==-1)
{
// Standard button
bitmap_width = Menu_bars[current_menu].Skin_width;
bitmap=&(Menu_bars[current_menu].Skin[pressed][start_y*Menu_bars[current_menu].Skin_width+start_x]);
}
else
{
// From Menu_buttons
bitmap_width = MENU_SPRITE_WIDTH;
bitmap=Gfx->Menu_sprite[pressed][(byte)icon][0];
// For bottom right: offset +1,+1
if (Buttons_Pool[btn_number].Shape==BUTTON_SHAPE_TRIANGLE_BOTTOM_RIGHT)
bitmap += MENU_SPRITE_WIDTH+1;
}
switch(Buttons_Pool[btn_number].Shape)
{
case BUTTON_SHAPE_NO_FRAME :
break;
case BUTTON_SHAPE_RECTANGLE :
for (y_pos=0;y_pos<height;y_pos++)
for (x_pos=0;x_pos<width;x_pos++)
{
color=bitmap[x_pos+y_pos*bitmap_width];
Pixel_in_menu_and_skin(current_menu, start_x+x_pos, start_y+y_pos, color);
}
break;
case BUTTON_SHAPE_TRIANGLE_TOP_LEFT:
for (y_pos=0;y_pos<15;y_pos++)
for (x_pos=0;x_pos<15-y_pos;x_pos++)
{
color=bitmap[x_pos+y_pos*bitmap_width];
Pixel_in_menu_and_skin(current_menu, start_x+x_pos, start_y+y_pos, color);
}
break;
case BUTTON_SHAPE_TRIANGLE_BOTTOM_RIGHT:
for (y_pos=0;y_pos<15;y_pos++)
for (x_pos=14-y_pos;x_pos<15;x_pos++)
{
color=bitmap[(x_pos)+(y_pos)*bitmap_width];
Pixel_in_menu_and_skin(current_menu, start_x+x_pos, start_y+y_pos, color);
}
break;
}
// Special: Show specific shape
if (btn_number==BUTTON_PAINTBRUSHES
&& Paintbrush_shape!=PAINTBRUSH_SHAPE_COLOR_BRUSH
&& Paintbrush_shape!=PAINTBRUSH_SHAPE_MONO_BRUSH)
{
short x_pos,y_pos;
short start_x;
short menu_x_pos,menu_y_pos;
short menu_start_x;
// Print paintbrush shape
menu_start_x=8-Paintbrush_offset_X;
if (menu_start_x<1)
{
start_x=Paintbrush_offset_X-7;
menu_start_x=1;
}
else
start_x=0;
menu_y_pos=9-Paintbrush_offset_Y;
if (menu_y_pos<2)
{
y_pos=Paintbrush_offset_Y-7;
menu_y_pos=2;
}
else
y_pos=0;
for (;((y_pos<Paintbrush_height) && (menu_y_pos<16));menu_y_pos++,y_pos++)
for (menu_x_pos=menu_start_x,x_pos=start_x;((x_pos<Paintbrush_width) && (menu_x_pos<15));menu_x_pos++,x_pos++)
{
if (Paintbrush_sprite[(y_pos*MAX_PAINTBRUSH_SIZE)+x_pos])
Pixel_in_menu_and_skin(MENUBAR_TOOLS, menu_x_pos, menu_y_pos, MC_Black);
}
}
if (Menu_is_visible && Menu_bars[current_menu].Visible)
Update_rect(Menu_factor_X*(Buttons_Pool[btn_number].X_offset),
(Buttons_Pool[btn_number].Y_offset+Menu_bars[current_menu].Top)*Menu_factor_Y+Menu_Y,
width*Menu_factor_X,height*Menu_factor_Y);
/*
switch (pressed)
{
default:
case BUTTON_RELEASED:
color_top_left=MC_White;
color_bottom_right=MC_Dark;
color_diagonal=MC_Light;
break;
case BUTTON_PRESSED:
color_top_left=MC_Dark;
color_bottom_right=MC_Black;
color_diagonal=MC_Black;
break;
}
switch(Buttons_Pool[btn_number].Shape)
{
case BUTTON_SHAPE_NO_FRAME :
break;
case BUTTON_SHAPE_RECTANGLE :
// On colorie le point haut droit
Pixel_in_menu_and_skin(current_menu, end_x, start_y, color_diagonal);
// On colorie le point bas gauche
Pixel_in_menu_and_skin(current_menu, start_x, end_y, color_diagonal);
// On colorie la partie haute
for (x_pos=start_x;x_pos<=end_x-1;x_pos++)
{
Pixel_in_menu_and_skin(current_menu, x_pos, start_y, color_top_left);
}
for (y_pos=start_y+1;y_pos<=end_y-1;y_pos++)
{
// On colorie la partie gauche
Pixel_in_menu_and_skin(current_menu, start_x, y_pos, color_top_left);
// On colorie la partie droite
Pixel_in_menu_and_skin(current_menu, end_x, y_pos, color_bottom_right);
}
// On colorie la partie basse
for (x_pos=start_x+1;x_pos<=end_x;x_pos++)
{
Pixel_in_menu_and_skin(current_menu, x_pos, end_y, color_bottom_right);
}
break;
case BUTTON_SHAPE_TRIANGLE_TOP_LEFT:
// On colorie le point haut droit
Pixel_in_menu_and_skin(current_menu, end_x, start_y, color_top_left);
// On colorie le point bas gauche
Pixel_in_menu_and_skin(current_menu, start_x, end_y, color_top_left);
// On colorie le coin haut gauche
for (x_pos=0;x_pos<Buttons_Pool[btn_number].Width;x_pos++)
{
Pixel_in_menu_and_skin(current_menu, start_x+x_pos, start_y, color_top_left);
Pixel_in_menu_and_skin(current_menu, start_x, start_y+x_pos, color_top_left);
}
// On colorie la diagonale
for (x_pos=1;x_pos<Buttons_Pool[btn_number].Width;x_pos++)
{
Pixel_in_menu_and_skin(current_menu, start_x+x_pos, end_y-x_pos, color_bottom_right);
}
break;
case BUTTON_SHAPE_TRIANGLE_BOTTOM_RIGHT:
// On colorie le point haut droit
Pixel_in_menu_and_skin(current_menu, end_x, start_y, color_diagonal);
// On colorie le point bas gauche
Pixel_in_menu_and_skin(current_menu, start_x, end_y, color_diagonal);
// On colorie la diagonale
for (x_pos=1;x_pos<Buttons_Pool[btn_number].Width;x_pos++)
{
Pixel_in_menu_and_skin(current_menu, start_x+x_pos, end_y-x_pos, color_top_left);
}
// On colorie le coin bas droite
for (x_pos=0;x_pos<Buttons_Pool[btn_number].Width;x_pos++)
{
Pixel_in_menu_and_skin(current_menu, end_x-x_pos, end_y, color_bottom_right);
Pixel_in_menu_and_skin(current_menu, end_x, end_y-x_pos, color_bottom_right);
}
}
if (Menu_is_visible && Menu_bars[current_menu].Visible)
{
Update_rect(
start_x*Menu_factor_X,
(start_y+Menu_bars[current_menu].Top)*Menu_factor_Y + Menu_Y,
(end_x+1-start_x)*Menu_factor_X,
(end_y+1-start_y)*Menu_factor_Y);
}
*/
}
///Deselect a button
void Unselect_button(int btn_number)
{
if (Buttons_Pool[btn_number].Pressed)
{
// On considère que le bouton est relâché
Buttons_Pool[btn_number].Pressed=BUTTON_RELEASED;
// On affiche le cadre autour du bouton de façon à ce qu'il paraisse relâché
Draw_menu_button(btn_number,BUTTON_RELEASED);
// On appelle le désenclenchement particulier au bouton:
Buttons_Pool[btn_number].Unselect_action(btn_number);
}
}
///Select a button and disable all his family (for example, selecting "freehand" unselect "curves", "lines", ...)
void Select_button(int btn_number,byte click)
{
int family;
int b;
int icon;
Hide_cursor();
// Certains boutons ont deux icones
icon=-1;
switch(btn_number)
{
case BUTTON_POLYGONS:
case BUTTON_POLYFILL:
icon=MENU_SPRITE_POLYFORM;
break;
case BUTTON_FLOODFILL:
icon=MENU_SPRITE_REPLACE;
break;
}
if (icon!=-1)
{
// This changes the sprite number of both halves of a split button
Display_sprite_in_menu(btn_number,click==RIGHT_SIDE?icon:-1);
// Redraw the other half if Unselect_button() won't do it.
if (Buttons_Pool[btn_number].Shape==BUTTON_SHAPE_TRIANGLE_TOP_LEFT &&
!Buttons_Pool[btn_number+1].Pressed)
Draw_menu_button(btn_number+1, BUTTON_RELEASED);
else if (Buttons_Pool[btn_number].Shape==BUTTON_SHAPE_TRIANGLE_BOTTOM_RIGHT &&
!Buttons_Pool[btn_number-1].Pressed)
Draw_menu_button(btn_number-1, BUTTON_RELEASED);
}
// On note déjà la famille du bouton (La "Famiglia" c'est sacré)
family=Buttons_Pool[btn_number].Family;
switch (family)
{
case FAMILY_TOOLBAR: // On ne fait rien (on préserve les interruptions)
break;
case FAMILY_INTERRUPTION: // Petit cas spécial dans la famille "Interruption":
if ((btn_number!=BUTTON_MAGNIFIER) || (!Main.magnifier_mode))
// Pour chaque bouton:
for (b=0; b<NB_BUTTONS; b++)
// S'il est de la même famille
if (
(b!=btn_number) &&
(Buttons_Pool[b].Family==FAMILY_INTERRUPTION) &&
( (b!=BUTTON_MAGNIFIER) ||
((b==BUTTON_MAGNIFIER) && (!Main.magnifier_mode)) )
)
// Alors on désenclenche le bouton
Unselect_button(b);
break;
default:
// On désenclenche D'ABORD les interruptions
// Pour chaque bouton:
for (b=0; b<NB_BUTTONS; b++)
// S'il est de la famille interruption
if ( (b!=btn_number)
&& (Buttons_Pool[b].Family==FAMILY_INTERRUPTION)
// Et que ce n'est pas la loupe, ou alors qu'on n'est pas en mode loupe
&& (!(Main.magnifier_mode && (b==BUTTON_MAGNIFIER))) )
// Alors on désenclenche le bouton
Unselect_button(b);
// Right-clicking on Adjust opens a menu, so in this case we skip
// the unselection of all "Tool" buttons.
if (btn_number==BUTTON_ADJUST && click==RIGHT_SIDE)
break;
// Same case with the Grad. Rectangle button.
if (btn_number==BUTTON_GRADRECT && click==RIGHT_SIDE)
break;
// Pour chaque bouton:
for (b=0; b<NB_BUTTONS; b++)
// S'il est de la même famille
if ( (b!=btn_number)
&& (Buttons_Pool[b].Family==family) )
// Alors on désenclenche le bouton
Unselect_button(b);
}
// On considère que le bouton est enfoncé
Buttons_Pool[btn_number].Pressed=BUTTON_PRESSED;
// On affiche le cadre autour du bouton de façon à ce qu'il paraisse enfoncé
Draw_menu_button(btn_number, BUTTON_PRESSED);
Display_cursor();
if ((click==1 && !Buttons_Pool[btn_number].Left_instant)
||(click!=1 && !Buttons_Pool[btn_number].Right_instant))
{
// On attend ensuite que l'utilisateur lâche son bouton:
Wait_end_of_click();
}
// Puis on se contente d'appeler l'action correspondant au bouton:
if (click==1)
Buttons_Pool[btn_number].Left_action(btn_number);
else
Buttons_Pool[btn_number].Right_action(btn_number);
}
///Moves the splitbar between zoom and standard views
void Move_separator(void)
{
short old_main_separator_position=Main.separator_position;
short old_x_zoom=Main.X_zoom;
short offset=Main.X_zoom-Mouse_X;
byte old_cursor_shape=Cursor_shape;
short old_mouse_x=-1;
// Afficher la barre en XOR
Hide_cursor();
Windows_open=1;
Cursor_shape=CURSOR_SHAPE_HORIZONTAL;
Vertical_XOR_line(Main.separator_position,0,Menu_Y);
Vertical_XOR_line(Main.X_zoom-1,0,Menu_Y);
Display_cursor();
Update_rect(Main.separator_position,0,abs(Main.separator_position-Main.X_zoom)+1,Menu_Y);
while (Mouse_K)
{
if (Mouse_X!=old_mouse_x)
{
old_mouse_x=Mouse_X;
Main.separator_proportion=(float)(Mouse_X+offset)/Screen_width;
Compute_separator_data();
if (Main.X_zoom!=old_x_zoom)
{
Hide_cursor();
// Effacer la barre en XOR
Vertical_XOR_line(old_main_separator_position,0,Menu_Y);
Vertical_XOR_line(old_x_zoom-1,0,Menu_Y);
Update_rect(old_main_separator_position,0,abs(old_main_separator_position-old_x_zoom)+1,Menu_Y);
old_main_separator_position=Main.separator_position;
old_x_zoom=Main.X_zoom;
// Rafficher la barre en XOR
Vertical_XOR_line(Main.separator_position,0,Menu_Y);
Vertical_XOR_line(Main.X_zoom-1,0,Menu_Y);
Update_rect(Main.separator_position,0,abs(Main.separator_position-Main.X_zoom)+1,Menu_Y);
Display_cursor();
}
}
Get_input(20);
}
// Effacer la barre en XOR
Hide_cursor();
Vertical_XOR_line(Main.separator_position,0,Menu_Y);
Vertical_XOR_line(Main.X_zoom-1,0,Menu_Y);
Windows_open=0;
Cursor_shape=old_cursor_shape;
Compute_magnifier_data();
Position_screen_according_to_zoom();
Compute_limits();
Display_all_screen();
Display_cursor();
}
///
/// Updates the status bar line with a color number.
/// Used when hovering the menu palette.
void Status_print_palette_color(byte color)
{
char str[25];
int i;
strcpy(str,Buttons_Pool[BUTTON_CHOOSE_COL].Tooltip);
sprintf(str+strlen(str),"%d (%d,%d,%d)",color,Main.palette[color].R,Main.palette[color].G,Main.palette[color].B);
// Pad spaces
for (i=strlen(str); i<24; i++)
str[i]=' ';
str[24]='\0';
Print_in_menu(str,0);
}
void Layer_preview_on(int * preview_is_visible)
{
int x,y;
short layer;
short layercount = Main.backups->Pages->Nb_layers;
static int previewW=0, previewH=0;
if (! *preview_is_visible && layercount>1)
{
previewW = Min(Main.image_width/Menu_factor_X,Layer_button_width);
previewH = previewW * Main.image_height / Main.image_width * Menu_factor_X / Menu_factor_Y;
if (previewH > Screen_height/4)
{
previewH = Screen_height/4;
previewW = Main.image_width*previewH/Main.image_height*Menu_factor_Y/Menu_factor_X;
}
Open_popup((Buttons_Pool[BUTTON_LAYER_SELECT].X_offset + 2)*Menu_factor_X,
Menu_Y - previewH * Menu_factor_Y, Buttons_Pool[BUTTON_LAYER_SELECT].Width, previewH);
*preview_is_visible = 1;
// Make the system think the menu is visible (Open_popup hides it)
// so Button_under_mouse still works
Menu_is_visible=Menu_is_visible_before_window;
Menu_Y=Menu_Y_before_window;
Window_rectangle(0, 0, Window_width, Window_height, MC_Dark);
for(layer = 0; layer < layercount; ++layer)
{
int offset;
// Stop if the window is too small to show the
// layer button (ex: 320x200 can only display 12 layers)
if (layer*Layer_button_width+previewW > Window_width)
break;
offset=(Layer_button_width-previewW)/2;
for (y = 0; y < previewH*Pixel_height*Menu_factor_Y-1; y++)
for (x = 0; x < previewW*Pixel_width*Menu_factor_X-1; x++)
{
int imgx = x * Main.image_width / (previewW*Pixel_width*Menu_factor_X-1);
int imgy = y * Main.image_height / (previewH*Pixel_height*Menu_factor_Y-1);
// Use Pixel_simple() in order to get highest resolution
Pixel_simple(x+((layer*Layer_button_width+offset)*Menu_factor_X+Window_pos_X)*Pixel_width, y+Window_pos_Y*Pixel_height+1, *(Main.backups->Pages->Image[layer].Pixels
+ imgx + imgy * Main.image_width));
}
}
Update_window_area(0,0,Window_width, Window_height);
}
}
void Layer_preview_off(int * preview_is_visible)
{
if (*preview_is_visible)
{
int x = Mouse_K;
Close_popup();
Display_cursor();
Mouse_K = x; // Close_popup waits for end of click and resets Mouse_K...
*preview_is_visible = 0;
}
}
///Main handler for everything. This is the main loop of the program
void Main_handler(void)
{
static byte temp_color;
int button_index; // Numéro de bouton de menu en cours
int prev_button_number=0; // Numéro de bouton de menu sur lequel on était précédemment
byte blink; // L'opération demande un effacement du curseur
int key_index; // index du tableau de touches spéciales correspondant à la touche enfoncée
byte temp;
byte effect_modified;
byte action;
dword key_pressed;
int preview_is_visible=0;
// This is used for the layer preview
do
{
// Resize requested
if (Resize_width || Resize_height)
{
Hide_cursor();
Init_mode_video(Resize_width, Resize_height, 0, Pixel_ratio);
// Reset the variables that indicate a resize was requested.
Display_menu();
Reposition_palette();
Display_all_screen();
Display_cursor();
}
else if (Drop_file_name)
{
// A file was dragged into Grafx2's window
if (Main.image_is_modified && !Confirmation_box("Discard unsaved changes ?"))
{
// do nothing
}
else
{
T_IO_Context context;
char* flimit;
byte old_cursor_shape;
Upload_infos_page(&Main);
flimit = Find_last_separator(Drop_file_name);
*(flimit++) = '\0';
Hide_cursor();
old_cursor_shape=Cursor_shape;
Cursor_shape=CURSOR_SHAPE_HOURGLASS;
Display_cursor();
Init_context_layered_image(&context, flimit, Drop_file_name);
Load_image(&context);
if (File_error!=1)
{
Compute_limits();
Compute_paintbrush_coordinates();
Redraw_layered_image();
End_of_modification();
Main.image_is_modified=0;
}
Destroy_context(&context);
Compute_optimal_menu_colors(Main.palette);
Check_menu_mode();
Display_menu();
if (Config.Display_image_limits)
Display_image_limits();
Hide_cursor();
Cursor_shape=old_cursor_shape;
Display_all_screen();
Display_cursor();
}
free(Drop_file_name);
Drop_file_name=NULL;
}
if(Get_input(0))
{
action = 0;
// Inhibit all keys if a drawing operation is in progress.
// We make an exception for the freehand operations, but these will
// only accept a very limited number of shortcuts.
if (Operation_stack_size!=0 && !Allow_color_change_during_operation)
Key=0;
// Evenement de fermeture
if (Quit_is_required)
{
Quit_is_required=0;
Button_Quit(BUTTON_QUIT);
}
if (Pan_shortcut_pressed && Current_operation!=OPERATION_PAN_VIEW && Operation_stack_size==0)
{
Hide_cursor();
Start_operation_stack(OPERATION_PAN_VIEW);
Display_cursor();
action++;
}
else if (Key)
{
effect_modified = 0;
for (key_index=SPECIAL_CLICK_RIGHT+1;key_index<NB_SPECIAL_SHORTCUTS;key_index++)
{
if (Is_shortcut(Key,key_index))
{
Layer_preview_off(&preview_is_visible);
// Special keys (functions not hooked to a UI button)
switch(key_index)
{
case SPECIAL_NEXT_FORECOLOR : // Next foreground color
Special_next_forecolor();
action++;
break;
case SPECIAL_PREVIOUS_FORECOLOR : // Previous foreground color
Special_previous_forecolor();
action++;
break;
case SPECIAL_NEXT_BACKCOLOR : // Next background color
Special_next_backcolor();
action++;
break;
case SPECIAL_PREVIOUS_BACKCOLOR : // Previous background color
Special_previous_backcolor();
action++;
break;
case SPECIAL_SMALLER_PAINTBRUSH: // Rétrécir le pinceau
Smaller_paintbrush();
action++;
break;
case SPECIAL_BIGGER_PAINTBRUSH: // Grossir le pinceau
Bigger_paintbrush();
action++;
break;
case SPECIAL_NEXT_USER_FORECOLOR : // Next user-defined foreground color
Special_next_user_forecolor();
action++;
break;
case SPECIAL_PREVIOUS_USER_FORECOLOR : // Previous user-defined foreground color
Special_previous_user_forecolor();
action++;
break;
case SPECIAL_NEXT_USER_BACKCOLOR : // Next user-defined background color
Special_next_user_backcolor();
action++;
break;
case SPECIAL_PREVIOUS_USER_BACKCOLOR : // Previous user-defined background color
Special_previous_user_backcolor();
action++;
break;
}
// Other shortcuts are forbidden while an operation is in progress
if (Operation_stack_size!=0)
continue;
switch (key_index)
{
case SPECIAL_SCROLL_UP : // Scroll up
if (Main.magnifier_mode)
Scroll_magnifier(0,-(Main.magnifier_height>>2));
else
Scroll_screen(0,-(Screen_height>>3));
action++;
break;
case SPECIAL_SCROLL_DOWN : // Scroll down
if (Main.magnifier_mode)
Scroll_magnifier(0,(Main.magnifier_height>>2));
else
Scroll_screen(0,(Screen_height>>3));
action++;
break;
case SPECIAL_SCROLL_LEFT : // Scroll left
if (Main.magnifier_mode)
Scroll_magnifier(-(Main.magnifier_width>>2),0);
else
Scroll_screen(-(Screen_width>>3),0);
action++;
break;
case SPECIAL_SCROLL_RIGHT : // Scroll right
if (Main.magnifier_mode)
Scroll_magnifier((Main.magnifier_width>>2),0);
else
Scroll_screen((Screen_width>>3),0);
action++;
break;
case SPECIAL_SCROLL_UP_FAST : // Scroll up faster
if (Main.magnifier_mode)
Scroll_magnifier(0,-(Main.magnifier_height>>1));
else
Scroll_screen(0,-(Screen_height>>2));
action++;
break;
case SPECIAL_SCROLL_DOWN_FAST : // Scroll down faster
if (Main.magnifier_mode)
Scroll_magnifier(0,(Main.magnifier_height>>1));
else
Scroll_screen(0,(Screen_height>>2));
action++;
break;
case SPECIAL_SCROLL_LEFT_FAST : // Scroll left faster
if (Main.magnifier_mode)
Scroll_magnifier(-(Main.magnifier_width>>1),0);
else
Scroll_screen(-(Screen_width>>2),0);
action++;
break;
case SPECIAL_SCROLL_RIGHT_FAST : // Scroll right faster
if (Main.magnifier_mode)
Scroll_magnifier((Main.magnifier_width>>1),0);
else
Scroll_screen((Screen_width>>2),0);
action++;
break;
case SPECIAL_SCROLL_UP_SLOW : // Scroll up slower
if (Main.magnifier_mode)
Scroll_magnifier(0,-1);
else
Scroll_screen(0,-1);
action++;
break;
case SPECIAL_SCROLL_DOWN_SLOW : // Scroll down slower
if (Main.magnifier_mode)
Scroll_magnifier(0,1);
else
Scroll_screen(0,1);
action++;
break;
case SPECIAL_SCROLL_LEFT_SLOW : // Scroll left slower
if (Main.magnifier_mode)
Scroll_magnifier(-1,0);
else
Scroll_screen(-1,0);
action++;
break;
case SPECIAL_SCROLL_RIGHT_SLOW : // Scroll right slower
if (Main.magnifier_mode)
Scroll_magnifier(1,0);
else
Scroll_screen(1,0);
action++;
break;
case SPECIAL_SHOW_HIDE_CURSOR : // Show / Hide cursor
Hide_cursor();
Cursor_hidden=!Cursor_hidden;
Display_cursor();
action++;
break;
case SPECIAL_DOT_PAINTBRUSH : // Paintbrush = "."
Hide_cursor();
Paintbrush_shape=PAINTBRUSH_SHAPE_ROUND;
Set_paintbrush_size(1,1);
Change_paintbrush_shape(PAINTBRUSH_SHAPE_ROUND);
Display_cursor();
action++;
break;
case SPECIAL_CONTINUOUS_DRAW : // Continuous freehand drawing
Select_button(BUTTON_DRAW,LEFT_SIDE);
// ATTENTION CE TRUC EST MOCHE ET VA MERDER SI ON SE MET A UTILISER DES BOUTONS POPUPS
while (Current_operation!=OPERATION_CONTINUOUS_DRAW)
Select_button(BUTTON_DRAW,RIGHT_SIDE);
action++;
break;
case SPECIAL_FLIP_X : // Flip X
Hide_cursor();
Flip_X_lowlevel(Brush_original_pixels, Brush_width, Brush_height);
// Remap according to the last used remap table
Remap_general_lowlevel(Brush_colormap,Brush_original_pixels,Brush,Brush_width,Brush_height,Brush_width);
Display_cursor();
action++;
break;
case SPECIAL_FLIP_Y : // Flip Y
Hide_cursor();
Flip_Y_lowlevel(Brush_original_pixels, Brush_width, Brush_height);
// Remap according to the last used remap table
Remap_general_lowlevel(Brush_colormap,Brush_original_pixels,Brush,Brush_width,Brush_height,Brush_width);
Display_cursor();
action++;
break;
case SPECIAL_ROTATE_90 : // 90° brush rotation
Hide_cursor();
Rotate_90_deg();
Display_cursor();
action++;
break;
case SPECIAL_ROTATE_180 : // 180° brush rotation
Hide_cursor();
Rotate_180_deg_lowlevel(Brush_original_pixels, Brush_width, Brush_height);
// Remap according to the last used remap table
Remap_general_lowlevel(Brush_colormap,Brush_original_pixels,Brush,Brush_width,Brush_height,Brush_width);
Brush_offset_X=(Brush_width>>1);
Brush_offset_Y=(Brush_height>>1);
Display_cursor();
action++;
break;
case SPECIAL_STRETCH : // Stretch brush
Hide_cursor();
Start_operation_stack(OPERATION_STRETCH_BRUSH);
Display_cursor();
action++;
break;
case SPECIAL_DISTORT : // Distort brush
Hide_cursor();
Start_operation_stack(OPERATION_DISTORT_BRUSH);
Display_cursor();
action++;
break;
case SPECIAL_ROTATE_ANY_ANGLE : // Rotate brush by any angle
Hide_cursor();
Start_operation_stack(OPERATION_ROTATE_BRUSH);
Display_cursor();
action++;
break;
case SPECIAL_BRUSH_DOUBLE:
if (Paintbrush_shape==PAINTBRUSH_SHAPE_COLOR_BRUSH
|| Paintbrush_shape==PAINTBRUSH_SHAPE_MONO_BRUSH)
{
Hide_cursor();
Stretch_brush(1,1,Brush_width*2,Brush_height*2);
Display_cursor();
}
action++;
break;
case SPECIAL_BRUSH_DOUBLE_WIDTH:
if (Paintbrush_shape==PAINTBRUSH_SHAPE_COLOR_BRUSH
|| Paintbrush_shape==PAINTBRUSH_SHAPE_MONO_BRUSH)
{
Hide_cursor();
Stretch_brush(1,1,Brush_width*2,Brush_height);
Display_cursor();
}
action++;
break;
case SPECIAL_BRUSH_DOUBLE_HEIGHT:
if (Paintbrush_shape==PAINTBRUSH_SHAPE_COLOR_BRUSH
|| Paintbrush_shape==PAINTBRUSH_SHAPE_MONO_BRUSH)
{
Hide_cursor();
Stretch_brush(1,1,Brush_width,Brush_height*2);
Display_cursor();
}
action++;
break;
case SPECIAL_BRUSH_HALVE:
if (Paintbrush_shape==PAINTBRUSH_SHAPE_COLOR_BRUSH
|| Paintbrush_shape==PAINTBRUSH_SHAPE_MONO_BRUSH)
{
Hide_cursor();
Stretch_brush(1,1,Brush_width/2,Brush_height/2);
Display_cursor();
}
action++;
break;
case SPECIAL_OUTLINE : // Outline brush
Hide_cursor();
Outline_brush();
Display_cursor();
action++;
break;
case SPECIAL_NIBBLE : // Nibble brush
Hide_cursor();
Nibble_brush();
Display_cursor();
action++;
break;
case SPECIAL_GET_BRUSH_COLORS : // Get colors from brush
Get_colors_from_brush();
action++;
break;
case SPECIAL_RECOLORIZE_BRUSH : // Recolorize brush
Hide_cursor();
Remap_brush();
Display_cursor();
action++;
break;
case SPECIAL_LOAD_BRUSH :
Load_picture(CONTEXT_BRUSH);
action++;
break;
case SPECIAL_SAVE_BRUSH :
Save_picture(CONTEXT_BRUSH);
action++;
break;
case SPECIAL_ZOOM_IN : // Zoom in
Zoom(+1);
action++;
break;
case SPECIAL_ZOOM_IN_MORE :
Zoom(+3);
action++;
break;
case SPECIAL_ZOOM_OUT : // Zoom out
Zoom(-1);
action++;
break;
case SPECIAL_ZOOM_OUT_MORE :
Zoom(-3);
action++;
break;
case SPECIAL_CENTER_ATTACHMENT : // Center brush attachment
Hide_cursor();
Brush_offset_X=(Brush_width>>1);
Brush_offset_Y=(Brush_height>>1);
Display_cursor();
action++;
break;
case SPECIAL_TOP_LEFT_ATTACHMENT : // Top-left brush attachment
Hide_cursor();
Brush_offset_X=0;
Brush_offset_Y=0;
Display_cursor();
action++;
break;
case SPECIAL_TOP_RIGHT_ATTACHMENT : // Top-right brush attachment
Hide_cursor();
Brush_offset_X=(Brush_width-1);
Brush_offset_Y=0;
Display_cursor();
action++;
break;
case SPECIAL_BOTTOM_LEFT_ATTACHMENT : // Bottom-left brush attachment
Hide_cursor();
Brush_offset_X=0;
Brush_offset_Y=(Brush_height-1);
Display_cursor();
action++;
break;
case SPECIAL_BOTTOM_RIGHT_ATTACHMENT : // Bottom right brush attachment
Hide_cursor();
Brush_offset_X=(Brush_width-1);
Brush_offset_Y=(Brush_height-1);
Display_cursor();
action++;
break;
case SPECIAL_EXCLUDE_COLORS_MENU : // Exclude colors menu
Menu_tag_colors("Tag colors to exclude",Exclude_color,&temp,1, NULL, SPECIAL_EXCLUDE_COLORS_MENU);
action++;
break;
case SPECIAL_INVERT_SIEVE :
Invert_trame();
action++;
break;
case SPECIAL_SHADE_MODE :
Button_Shade_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_SHADE_MENU :
Button_Shade_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_QUICK_SHADE_MODE :
Button_Quick_shade_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_QUICK_SHADE_MENU :
Button_Quick_shade_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_STENCIL_MODE :
Button_Stencil_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_STENCIL_MENU :
Button_Stencil_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_MASK_MODE :
Button_Mask_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_MASK_MENU :
Button_Mask_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_GRID_MODE :
Button_Snap_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_GRID_MENU :
Button_Grid_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_SHOW_GRID :
Button_Show_grid();
effect_modified = 1;
action++;
break;
case SPECIAL_SIEVE_MODE :
Button_Sieve_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_SIEVE_MENU :
Button_Sieve_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_COLORIZE_MODE :
Button_Colorize_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_COLORIZE_MENU :
Button_Colorize_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_SMOOTH_MODE :
Button_Smooth_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_SMOOTH_MENU :
Button_Smooth_menu();
effect_modified = 1;
action++;
break;
case SPECIAL_SMEAR_MODE :
Button_Smear_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_TILING_MODE :
Button_Tiling_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_TILING_MENU :
effect_modified = 1;
Button_Tiling_menu();
action++;
break;
case SPECIAL_TILEMAP_MODE :
Button_Tilemap_mode();
effect_modified = 1;
action++;
break;
case SPECIAL_TILEMAP_MENU :
effect_modified = 1;
Button_Tilemap_menu();
action++;
break;
case SPECIAL_EFFECTS_OFF :
Effects_off();
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_1 :
Transparency_set(1);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_2 :
Transparency_set(2);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_3 :
Transparency_set(3);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_4 :
Transparency_set(4);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_5 :
Transparency_set(5);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_6 :
Transparency_set(6);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_7 :
Transparency_set(7);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_8 :
Transparency_set(8);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_9 :
Transparency_set(9);
effect_modified = 1;
action++;
break;
case SPECIAL_TRANSPARENCY_0 :
Transparency_set(0);
effect_modified = 1;
action++;
break;
case SPECIAL_ZOOM_1 :
Zoom_set(-1);
action++;
break;
case SPECIAL_ZOOM_2 :
Zoom_set(0);
action++;
break;
case SPECIAL_ZOOM_3 :
Zoom_set(1);
action++;
break;
case SPECIAL_ZOOM_4 :
Zoom_set(2);
action++;
break;
case SPECIAL_ZOOM_5 :
Zoom_set(3);
action++;
break;
case SPECIAL_ZOOM_6 :
Zoom_set(4);
action++;
break;
case SPECIAL_ZOOM_8 :
Zoom_set(5);
action++;
break;
case SPECIAL_ZOOM_10 :
Zoom_set(6);
action++;
break;
case SPECIAL_ZOOM_12 :
Zoom_set(7);
action++;
break;
case SPECIAL_ZOOM_14 :
Zoom_set(8);
action++;
break;
case SPECIAL_ZOOM_16 :
Zoom_set(9);
action++;
break;
case SPECIAL_ZOOM_18 :
Zoom_set(10);
action++;
break;
case SPECIAL_ZOOM_20 :
Zoom_set(11);
action++;
break;
case SPECIAL_LAYER1_SELECT:
case SPECIAL_LAYER2_SELECT:
case SPECIAL_LAYER3_SELECT:
case SPECIAL_LAYER4_SELECT:
case SPECIAL_LAYER5_SELECT:
case SPECIAL_LAYER6_SELECT:
case SPECIAL_LAYER7_SELECT:
case SPECIAL_LAYER8_SELECT:
Layer_activate((key_index-SPECIAL_LAYER1_SELECT)/2, LEFT_SIDE);
action++;
break;
case SPECIAL_LAYER1_TOGGLE:
case SPECIAL_LAYER2_TOGGLE:
case SPECIAL_LAYER3_TOGGLE:
case SPECIAL_LAYER4_TOGGLE:
case SPECIAL_LAYER5_TOGGLE:
case SPECIAL_LAYER6_TOGGLE:
case SPECIAL_LAYER7_TOGGLE:
case SPECIAL_LAYER8_TOGGLE:
Layer_activate((key_index-SPECIAL_LAYER1_TOGGLE)/2, RIGHT_SIDE);
action++;
break;
case SPECIAL_FORMAT_CHECKER:
C64_FLI_enforcer();
action++;
break;
case SPECIAL_REPEAT_SCRIPT:
#ifdef __ENABLE_LUA__
Repeat_script();
action++;
#endif
break;
case SPECIAL_RUN_SCRIPT_1:
case SPECIAL_RUN_SCRIPT_2:
case SPECIAL_RUN_SCRIPT_3:
case SPECIAL_RUN_SCRIPT_4:
case SPECIAL_RUN_SCRIPT_5:
case SPECIAL_RUN_SCRIPT_6:
case SPECIAL_RUN_SCRIPT_7:
case SPECIAL_RUN_SCRIPT_8:
case SPECIAL_RUN_SCRIPT_9:
case SPECIAL_RUN_SCRIPT_10:
#ifdef __ENABLE_LUA__
Run_numbered_script(key_index-SPECIAL_RUN_SCRIPT_1);
action++;
#endif
break;
case SPECIAL_CYCLE_MODE:
Cycling_mode= !Cycling_mode;
// Restore palette
if (!Cycling_mode)
Set_palette(Main.palette);
action++;
break;
case SPECIAL_HOLD_PAN:
// already handled by Pan_shortcut_pressed
break;
}
}
} // End of special keys
// Shortcut for clicks of Menu buttons.
// Disable all of them when an operation is in progress
if (Operation_stack_size==0)
{
// Some functions open windows that clear the Key variable,
// so we need to use a temporary replacement.
key_pressed = Key;
for (button_index=0;button_index<NB_BUTTONS;button_index++)
{
if (Is_shortcut(key_pressed,0x100+button_index))
{
Layer_preview_off(&preview_is_visible);
Select_button(button_index,LEFT_SIDE);
prev_button_number=-1;
action++;
}
else if (Is_shortcut(key_pressed,0x200+button_index))
{
Layer_preview_off(&preview_is_visible);
Select_button(button_index,RIGHT_SIDE);
prev_button_number=-1;
action++;
}
}
}
// Si on a modifié un effet, il faut rafficher le bouton des effets en
// conséquence.
if (effect_modified)
{
Hide_cursor();
Draw_menu_button(BUTTON_EFFECTS, Any_effect_active());
Display_cursor();
}
}
if (action)
Key=0;
}
else
{
// Removed all SDL_Delay() timing here: relying on Get_input()
SDL_Delay(10);
}
// Gestion de la souris
Cursor_in_menu=(Mouse_Y>=Menu_Y) ||
( (Main.magnifier_mode) && (Mouse_X>=Main.separator_position) &&
(Mouse_X<Main.X_zoom) );
if (Cursor_in_menu)
{
// Le curseur se trouve dans le menu
// On cherche sur quel bouton du menu se trouve la souris:
button_index=Button_under_mouse();
// Si le curseur vient de changer de zone
if ( (button_index!=prev_button_number)
|| (!Cursor_in_menu_previous)
|| (prev_button_number==BUTTON_CHOOSE_COL) )
{
// Si le curseur n'est pas sur un bouton
if (button_index==-1)
{
if (Menu_is_visible)
{
// On nettoie les coordonnées
Hide_cursor();
/*if (Gfx->Hover_effect && prev_button_number > -1 && !Buttons_Pool[prev_button_number].Pressed)
Draw_menu_button(prev_button_number, BUTTON_RELEASED);
*/
Block(18*Menu_factor_X,Menu_status_Y,192*Menu_factor_X,Menu_factor_Y<<3,MC_Light);
Update_rect(18*Menu_factor_X,Menu_status_Y,192*Menu_factor_X,Menu_factor_Y<<3);
Display_cursor();
}
}
else
{
if ( (prev_button_number!=BUTTON_CHOOSE_COL)
|| (temp_color!=First_color_in_palette)
|| (Old_MX!=Mouse_X) || (Old_MY!=Mouse_Y) )
{
// Le curseur est sur un nouveau bouton
if (button_index!=BUTTON_CHOOSE_COL)
{
Hide_cursor();
/*if (Gfx->Hover_effect && prev_button_number > -1 && !Buttons_Pool[prev_button_number].Pressed)
Draw_menu_button(prev_button_number, BUTTON_RELEASED);
*/
Print_in_menu(Buttons_Pool[button_index].Tooltip,0);
/*if (Gfx->Hover_effect && !Buttons_Pool[button_index].Pressed)
Draw_menu_button(button_index, BUTTON_HIGHLIGHTED);
*/
Display_cursor();
}
else
{ // Le curseur est-il sur une couleur de la palette?
int color;
if ((color=Pick_color_in_palette())!=-1)
{
Hide_cursor();
Status_print_palette_color(color);
Display_cursor();
}
else
{
if ( (Old_MX!=Mouse_X) || (Old_MY!=Mouse_Y) )
{
Hide_cursor();
Block(18*Menu_factor_X,Menu_status_Y,192*Menu_factor_X,Menu_factor_Y<<3,MC_Light);
Update_rect(18*Menu_factor_X,Menu_status_Y,192*Menu_factor_X,8*Menu_factor_Y);
Display_cursor();
}
}
}
}
}
}
prev_button_number=button_index;
// Gestion des clicks
if (Mouse_K)
{
if (Mouse_Y>=Menu_Y)
{
if (button_index>=0)
{
Layer_preview_off(&preview_is_visible);
Select_button(button_index,Mouse_K);
prev_button_number=-1;
}
}
else
if (Main.magnifier_mode) Move_separator();
}
if (button_index == BUTTON_LAYER_SELECT)
Layer_preview_on(&preview_is_visible);
else
Layer_preview_off(&preview_is_visible);
}
else // if (!Cursor_in_menu)
{
Layer_preview_off(&preview_is_visible);
}
// we need to refresh that one as we may come from a sub window
Cursor_in_menu=(Mouse_Y>=Menu_Y) ||
( (Main.magnifier_mode) && (Mouse_X>=Main.separator_position) &&
(Mouse_X<Main.X_zoom) );
// Le curseur se trouve dans l'image
if ( (!Cursor_in_menu) && (Menu_is_visible) && (Old_MY != Mouse_Y || Old_MX != Mouse_X || Key || Mouse_K)) // On ne met les coordonnées à jour que si l'utilisateur a fait un truc
{
if(Cursor_in_menu_previous)
{
Hide_cursor();
/*if (Gfx->Hover_effect && prev_button_number > -1 && !Buttons_Pool[prev_button_number].Pressed)
Draw_menu_button(prev_button_number, BUTTON_RELEASED);
*/
if ( (Current_operation!=OPERATION_COLORPICK) && (Current_operation!=OPERATION_REPLACE) )
{
Print_in_menu("X: Y: ",0);
}
else
{
Print_in_menu("X: Y: ( )",0);
}
Display_cursor();
Cursor_in_menu_previous = 0;
}
}
if(Cursor_in_menu)
{
Cursor_in_menu_previous = 1;
}
else
{
blink=Operation[Current_operation][Mouse_K_unique][Operation_stack_size].Hide_cursor;
if (blink) Hide_cursor();
Operation[Current_operation][Mouse_K_unique][Operation_stack_size].Action();
if (blink) Display_cursor();
}
Old_MX=Mouse_X;
Old_MY=Mouse_Y;
}
while (!Quitting);
}
//////////////////////////////////////////////////////////////////////////////
// différentes fonctions d'affichage utilisées dans les fenêtres //
//////////////////////////////////////////////////////////////////////////////
//----------------------- Tracer une fenêtre d'options -----------------------
void Open_window(word width,word height, const char * title)
// Lors de l'appel à cette procédure, la souris doit être affichée.
// En sortie de cette procedure, la souris est effacée.
{
//word i,j;
size_t title_length;
Hide_cursor();
/*if (Windows_open == 0 && Gfx->Hover_effect)
{
if (Cursor_in_menu)
{
int button_index=Button_under_mouse();
if (button_index > -1 && !Buttons_Pool[button_index].Pressed)
Draw_menu_button(button_index, BUTTON_RELEASED);
}
}*/
Windows_open++;
Window_width=width;
Window_height=height;
// Positionnement de la fenêtre
Window_pos_X=(Screen_width-(width*Menu_factor_X))>>1;
Window_pos_Y=(Screen_height-(height*Menu_factor_Y))>>1;
Window_draggable=1;
// Sauvegarde de ce que la fenêtre remplace
Save_background(&(Window_background[Windows_open-1]), Window_pos_X, Window_pos_Y, width, height);
// Fenêtre grise
Window_rectangle(2,2,width-4,height-4,MC_Window);
// -- Frame de la fenêtre ----- --- -- - -
// Frame noir puis en relief
Window_display_frame_mono(0,0,width,height,MC_Black);
Window_display_frame_out(1,1,width-2,height-2);
// Barre sous le titre
Window_rectangle(3,3,width-6,10,MC_White);
Window_rectangle(2,12,width-4,1,MC_Dark);
title_length = strlen(title);
if (title_length+2 > (size_t)(width/8))
title_length = width/8-2;
Print_in_window_limited((width-(title_length<<3))>>1,4,title,title_length,MC_Black,MC_White);
if (Windows_open == 1)
{
Menu_is_visible_before_window=Menu_is_visible;
Menu_is_visible=0;
Menu_Y_before_window=Menu_Y;
Menu_Y=Screen_height;
Cursor_shape_before_window=Cursor_shape;
Cursor_shape=CURSOR_SHAPE_ARROW;
Paintbrush_hidden_before_window=Paintbrush_hidden;
Paintbrush_hidden=1;
if (Allow_colorcycling)
{
Allow_colorcycling=0;
// Restore palette
Set_palette(Main.palette);
}
Allow_drag_and_drop(0);
}
// Initialisation des listes de boutons de la fenêtre
Window_normal_button_list =NULL;
Window_palette_button_list =NULL;
Window_scroller_button_list=NULL;
Window_special_button_list =NULL;
Window_dropdown_button_list=NULL;
Window_nb_buttons =0;
}
//----------------------- Fermer une fenêtre d'options -----------------------
void Close_window(void)
// Lors de l'appel à cette procedure, la souris doit être affichée.
// En sortie de cette procedure, la souris est effacée.
{
T_Normal_button * temp1;
T_Palette_button * temp2;
T_Scroller_button * temp3;
T_Special_button * temp4;
T_Dropdown_button * temp5;
T_List_button * temp6;
Hide_cursor();
while (Window_normal_button_list)
{
temp1=Window_normal_button_list->Next;
free(Window_normal_button_list);
Window_normal_button_list=temp1;
}
while (Window_palette_button_list)
{
temp2=Window_palette_button_list->Next;
free(Window_palette_button_list);
Window_palette_button_list=temp2;
}
while (Window_scroller_button_list)
{
temp3=Window_scroller_button_list->Next;
free(Window_scroller_button_list);
Window_scroller_button_list=temp3;
}
while (Window_special_button_list)
{
temp4=Window_special_button_list->Next;
free(Window_special_button_list);
Window_special_button_list=temp4;
}
while (Window_dropdown_button_list)
{
temp5=Window_dropdown_button_list->Next;
Window_dropdown_clear_items(Window_dropdown_button_list);
free(Window_dropdown_button_list);
Window_dropdown_button_list=temp5;
}
while (Window_list_button_list)
{
temp6=Window_list_button_list->Next;
free(Window_list_button_list);
Window_list_button_list=temp6;
}
if (Windows_open != 1)
{
// Restore de ce que la fenêtre cachait
Restore_background(&Window_background[Windows_open-1], Window_pos_X, Window_pos_Y, Window_width, Window_height);
Update_window_area(0,0,Window_width,Window_height);
Windows_open--;
}
else
{
free(Window_background[Windows_open-1]);
Window_background[Windows_open-1]=NULL;
Windows_open--;
Paintbrush_hidden=Paintbrush_hidden_before_window;
Compute_paintbrush_coordinates();
Menu_Y=Menu_Y_before_window;
Menu_is_visible=Menu_is_visible_before_window;
Cursor_shape=Cursor_shape_before_window;
Check_menu_mode();
Display_all_screen();
Display_menu();
Allow_colorcycling=1;
Allow_drag_and_drop(1);
}
Key=0;
Mouse_K=0;
Old_MX = -1;
Old_MY = -1;
}
//---------------- Dessiner un bouton normal dans une fenêtre ----------------
// undersc_letter is 0 for no underscore, 1-indexed array index otherwise
void Window_draw_normal_bouton(word x_pos,word y_pos,word width,word height,
const char * title,byte undersc_letter,byte clickable)
{
byte title_color;
word text_x_pos,text_y_pos;
if (clickable)
{
Window_display_frame_out(x_pos,y_pos,width,height);
Window_display_frame_generic(x_pos-1,y_pos-1,width+2,height+2,MC_Black,MC_Black,MC_Dark,MC_Dark,MC_Dark);
title_color=MC_Black;
}
else
{
Window_display_frame_out(x_pos,y_pos,width,height);
Window_display_frame_mono(x_pos-1,y_pos-1,width+2,height+2,MC_Light);
title_color=MC_Dark;
}
text_x_pos=x_pos+( (width-(strlen(title)<<3)+1) >>1 );
text_y_pos=y_pos+((height-7)>>1);
Print_in_window(text_x_pos,text_y_pos,title,title_color,MC_Light);
if (undersc_letter)
Window_rectangle(text_x_pos+((undersc_letter-1)<<3),
text_y_pos+8,8,1,MC_Dark);
}
// -- Button normal enfoncé dans la fenêtre --
void Window_select_normal_button(word x_pos,word y_pos,word width,word height)
{
Window_display_frame_generic(x_pos,y_pos,width,height,MC_Dark,MC_Black,MC_Dark,MC_Dark,MC_Black);
Update_window_area(x_pos, y_pos, width, height);
}
// -- Button normal désenfoncé dans la fenêtre --
void Window_unselect_normal_button(word x_pos,word y_pos,word width,word height)
{
Window_display_frame_out(x_pos,y_pos,width,height);
Update_window_area(x_pos, y_pos, width, height);
}
//--------------- Dessiner un bouton palette dans une fenêtre ----------------
void Window_draw_palette_bouton(word x_pos,word y_pos)
{
word color;
for (color=0; color<=255; color++)
Window_rectangle( ((color >> 4)*10)+x_pos+6,((color & 15)*5)+y_pos+3,5,5,color);
Window_display_frame(x_pos,y_pos,164,86);
}
// -------------------- Effacer les TAGs sur les palette ---------------------
// Cette fonct° ne sert plus que lorsqu'on efface les tags dans le menu Spray.
void Window_clear_tags(void)
{
word origin_x;
word origin_y;
word x_pos;
word window_x_pos;
//word window_y_pos;
origin_x=Window_palette_button_list->Pos_X+3;
origin_y=Window_palette_button_list->Pos_Y+3;
for (x_pos=0,window_x_pos=origin_x;x_pos<16;x_pos++,window_x_pos+=10)
Window_rectangle(window_x_pos,origin_y,3,80,MC_Light);
Update_window_area(origin_x,origin_y,160,80);
}
// ---- Tracer les TAGs sur les palettes du menu Palette ou du menu Shade ----
void Tag_color_range(byte start,byte end)
{
word origin_x;
word origin_y;
//word x_pos;
word y_pos;
//word window_x_pos;
word window_y_pos;
word index;
// On efface les anciens TAGs
for (index=0;index<=start;index++)
Window_rectangle(Window_palette_button_list->Pos_X+3+((index>>4)*10),
Window_palette_button_list->Pos_Y+3+((index&15)* 5),
3,5,MC_Light);
for (index=end;index<256;index++)
Window_rectangle(Window_palette_button_list->Pos_X+3+((index>>4)*10),
Window_palette_button_list->Pos_Y+3+((index&15)* 5),
3,5,MC_Light);
// On affiche le 1er TAG
origin_x=(Window_palette_button_list->Pos_X+3)+(start>>4)*10;
origin_y=(Window_palette_button_list->Pos_Y+3)+(start&15)* 5;
for (y_pos=0,window_y_pos=origin_y ;y_pos<5;y_pos++,window_y_pos++)
Pixel_in_window(origin_x ,window_y_pos,MC_Black);
for (y_pos=0,window_y_pos=origin_y+1;y_pos<3;y_pos++,window_y_pos++)
Pixel_in_window(origin_x+1,window_y_pos,MC_Black);
Pixel_in_window(origin_x+2,origin_y+2,MC_Black);
if (start!=end)
{
// On complète le 1er TAG
Pixel_in_window(origin_x+1,origin_y+4,MC_Black);
// On affiche le 2ème TAG
origin_x=(Window_palette_button_list->Pos_X+3)+(end>>4)*10;
origin_y=(Window_palette_button_list->Pos_Y+3)+(end&15)* 5;
for (y_pos=0,window_y_pos=origin_y; y_pos<5; y_pos++,window_y_pos++)
Pixel_in_window(origin_x ,window_y_pos,MC_Black);
for (y_pos=0,window_y_pos=origin_y; y_pos<4; y_pos++,window_y_pos++)
Pixel_in_window(origin_x+1,window_y_pos,MC_Black);
Pixel_in_window(origin_x+2,origin_y+2,MC_Black);
// On TAG toutes les couleurs intermédiaires
for (index=start+1;index<end;index++)
{
Window_rectangle(Window_palette_button_list->Pos_X+3+((index>>4)*10),
Window_palette_button_list->Pos_Y+3+((index&15)* 5),
2,5,MC_Black);
// On efface l'éventuelle pointe d'une ancienne extrémité de l'intervalle
Pixel_in_window(Window_palette_button_list->Pos_X+5+((index>>4)*10),
Window_palette_button_list->Pos_Y+5+((index&15)* 5),
MC_Light);
}
}
Update_window_area(Window_palette_button_list->Pos_X+3,Window_palette_button_list->Pos_Y+3,12*16,5*16);
}
//------------------ Dessiner un scroller dans une fenêtre -------------------
void Compute_slider_cursor_length(T_Scroller_button * button)
{
if (button->Nb_elements>button->Nb_visibles)
{
button->Cursor_length=(button->Nb_visibles*(button->Length-24))/button->Nb_elements;
if (!(button->Cursor_length))
button->Cursor_length=1;
}
else
{
button->Cursor_length=button->Length-24;
}
}
void Window_draw_slider(T_Scroller_button * button)
{
word slider_position;
if (button->Is_horizontal)
{
slider_position=button->Pos_X+12;
Window_rectangle(slider_position,
button->Pos_Y,
button->Length-24,11,MC_Black/*MC_Dark*/);
if (button->Nb_elements>button->Nb_visibles)
slider_position+=
((button->Length-24-button->Cursor_length)*(button->Position)+(button->Nb_elements-button->Nb_visibles)/2)/(button->Nb_elements-button->Nb_visibles);
Window_rectangle(slider_position,
button->Pos_Y,
button->Cursor_length,11,MC_OnBlack/*MC_White*/);
Update_window_area(button->Pos_X,
button->Pos_Y,
button->Length,11);
}
else
{
slider_position=button->Pos_Y+12;
Window_rectangle(button->Pos_X,
slider_position,
11,button->Length-24,MC_Black/*MC_Dark*/);
if (button->Nb_elements>button->Nb_visibles)
slider_position+=
((button->Length-24-button->Cursor_length)*(button->Position)+(button->Nb_elements-button->Nb_visibles)/2)/(button->Nb_elements-button->Nb_visibles);
//
//(button->Position*) / (button->Nb_elements-button->Nb_visibles));
Window_rectangle(button->Pos_X,
slider_position,
11,button->Cursor_length,MC_OnBlack/*MC_White*/);
Update_window_area(button->Pos_X,
button->Pos_Y,
11,button->Length);
}
}
void Window_draw_scroller_button(T_Scroller_button * button)
{
if (button->Is_horizontal)
{
Window_display_frame_generic(button->Pos_X-1,button->Pos_Y-1,button->Length+2,13,MC_Black,MC_Black,MC_Dark,MC_Dark,MC_Dark);
Window_display_frame_mono(button->Pos_X+11,button->Pos_Y-1,button->Length-22,13,MC_Black);
Window_display_frame_out(button->Pos_X,button->Pos_Y,11,11);
Window_display_frame_out(button->Pos_X+button->Length-11,button->Pos_Y,11,11);
Print_in_window(button->Pos_X+2,button->Pos_Y+2,"\033",MC_Black,MC_Light);
Print_in_window(button->Pos_X+button->Length-9,button->Pos_Y+2,"\032",MC_Black,MC_Light);
}
else
{
Window_display_frame_generic(button->Pos_X-1,button->Pos_Y-1,13,button->Length+2,MC_Black,MC_Black,MC_Dark,MC_Dark,MC_Dark);
Window_display_frame_mono(button->Pos_X-1,button->Pos_Y+11,13,button->Length-22,MC_Black);
Window_display_frame_out(button->Pos_X,button->Pos_Y,11,11);
Window_display_frame_out(button->Pos_X,button->Pos_Y+button->Length-11,11,11);
Print_in_window(button->Pos_X+2,button->Pos_Y+2,"\030",MC_Black,MC_Light);
Print_in_window(button->Pos_X+2,button->Pos_Y+button->Length-9,"\031",MC_Black,MC_Light);
}
Window_draw_slider(button);
}
//--------------- Dessiner une zone de saisie dans une fenêtre ---------------
void Window_draw_input_bouton(word x_pos,word y_pos,word width_in_characters)
{
Window_display_frame_in(x_pos,y_pos,(width_in_characters<<3)+3,11);
}
//------------ Modifier le contenu (caption) d'une zone de saisie ------------
void Window_input_content(T_Special_button * button, const char * content)
{
Print_in_window_limited(button->Pos_X+2,button->Pos_Y+2,content,button->Width/8,MC_Black,MC_Light);
}
//------------ Effacer le contenu (caption) d'une zone de saisie ------------
void Window_clear_input_button(T_Special_button * button)
{
Window_rectangle(button->Pos_X+2,button->Pos_Y+2,(button->Width/8)*8,8,MC_Light);
Update_window_area(button->Pos_X+2,button->Pos_Y+2,button->Width/8*8,8);
}
//------ Rajout d'un bouton à la liste de ceux présents dans la fenêtre ------
// undersc_letter is 0 for no underscore, 1-indexed array index otherwise
T_Normal_button * Window_set_normal_button(word x_pos, word y_pos,
word width, word height,
const char * title, byte undersc_letter,
byte clickable, word shortcut)
{
T_Normal_button * temp=NULL;
Window_nb_buttons++;
if (clickable)
{
temp=(T_Normal_button *)malloc(sizeof(T_Normal_button));
temp->Number =Window_nb_buttons;
temp->Pos_X =x_pos;
temp->Pos_Y =y_pos;
temp->Width =width;
temp->Height =height;
temp->Clickable=clickable;
temp->Shortcut =shortcut;
temp->Repeatable=0;
temp->Next=Window_normal_button_list;
Window_normal_button_list=temp;
}
Window_draw_normal_bouton(x_pos,y_pos,width,height,title,undersc_letter,clickable);
return temp;
}
//------ Rajout d'un bouton à la liste de ceux présents dans la fenêtre ------
// undersc_letter is 0 for no underscore, 1-indexed array index otherwise
T_Normal_button * Window_set_repeatable_button(word x_pos, word y_pos,
word width, word height,
const char * title, byte undersc_letter,
byte clickable, word shortcut)
{
T_Normal_button * temp=NULL;
Window_nb_buttons++;
if (clickable)
{
temp=(T_Normal_button *)malloc(sizeof(T_Normal_button));
temp->Number =Window_nb_buttons;
temp->Pos_X =x_pos;
temp->Pos_Y =y_pos;
temp->Width =width;
temp->Height =height;
temp->Shortcut=shortcut;
temp->Repeatable=1;
temp->Next=Window_normal_button_list;
Window_normal_button_list=temp;
}
Window_draw_normal_bouton(x_pos,y_pos,width,height,title,undersc_letter,clickable);
return temp;
}
T_Palette_button * Window_set_palette_button(word x_pos, word y_pos)
{
T_Palette_button * temp;
temp=(T_Palette_button *)malloc(sizeof(T_Palette_button));
temp->Number =++Window_nb_buttons;
temp->Pos_X =x_pos;
temp->Pos_Y =y_pos;
temp->Next=Window_palette_button_list;
Window_palette_button_list=temp;
Window_draw_palette_bouton(x_pos,y_pos);
return temp;
}
T_Scroller_button * Window_set_scroller_button(word x_pos, word y_pos,
word height,
word nb_elements,
word nb_elements_visible,
word initial_position)
{
T_Scroller_button * temp;
temp=(T_Scroller_button *)malloc(sizeof(T_Scroller_button));
temp->Number =++Window_nb_buttons;
temp->Is_horizontal =0;
temp->Pos_X =x_pos;
temp->Pos_Y =y_pos;
temp->Length =height;
temp->Nb_elements =nb_elements;
temp->Nb_visibles =nb_elements_visible;
temp->Position =initial_position;
Compute_slider_cursor_length(temp);
temp->Next=Window_scroller_button_list;
Window_scroller_button_list=temp;
Window_draw_scroller_button(temp);
return temp;
}
T_Scroller_button * Window_set_horizontal_scroller_button(word x_pos, word y_pos,
word width,
word nb_elements,
word nb_elements_visible,
word initial_position)
{
T_Scroller_button * temp;
temp=(T_Scroller_button *)malloc(sizeof(T_Scroller_button));
temp->Number =++Window_nb_buttons;
temp->Is_horizontal =1;
temp->Pos_X =x_pos;
temp->Pos_Y =y_pos;
temp->Length =width;
temp->Nb_elements =nb_elements;
temp->Nb_visibles =nb_elements_visible;
temp->Position =initial_position;
Compute_slider_cursor_length(temp);
temp->Next=Window_scroller_button_list;
Window_scroller_button_list=temp;
Window_draw_scroller_button(temp);
return temp;
}
T_Special_button * Window_set_special_button(word x_pos,word y_pos,word width,word height, word shortcut)
{
T_Special_button * temp;
temp=(T_Special_button *)malloc(sizeof(T_Special_button));
temp->Number =++Window_nb_buttons;
temp->Pos_X =x_pos;
temp->Pos_Y =y_pos;
temp->Width =width;
temp->Height =height;
temp->Shortcut =shortcut;
temp->Next=Window_special_button_list;
Window_special_button_list=temp;
return temp;
}
T_Special_button * Window_set_input_button_s(word x_pos,word y_pos,word width_in_characters, word shortcut)
{
T_Special_button *temp;
temp=Window_set_special_button(x_pos,y_pos,(width_in_characters<<3)+3,11,shortcut);
Window_draw_input_bouton(x_pos,y_pos,width_in_characters);
return temp;
}
T_Special_button * Window_set_input_button(word x_pos,word y_pos,word width_in_characters)
{
return Window_set_input_button_s(x_pos, y_pos, width_in_characters, 0);
}
T_Dropdown_button * Window_set_dropdown_button(word x_pos,word y_pos,word width,word height,word dropdown_width,const char *label,byte display_choice,byte display_centered,byte display_arrow,byte active_button, byte bottom_up)
{
T_Dropdown_button *temp;
temp=(T_Dropdown_button *)malloc(sizeof(T_Dropdown_button));
temp->Number =++Window_nb_buttons;
temp->Pos_X =x_pos;
temp->Pos_Y =y_pos;
temp->Width =width;
temp->Height =height;
temp->Display_choice =display_choice;
temp->First_item=NULL;
temp->Dropdown_width=dropdown_width?dropdown_width:width;
temp->Display_centered=display_centered;
temp->Display_arrow=display_arrow;
temp->Active_button=active_button;
temp->Bottom_up=bottom_up;
temp->Next=Window_dropdown_button_list;
Window_dropdown_button_list=temp;
Window_draw_normal_bouton(x_pos,y_pos,width,height,"",0,1);
if (label && label[0])
Print_in_window(temp->Pos_X+2,temp->Pos_Y+(temp->Height-7)/2,label,MC_Black,MC_Light);
if (display_arrow)
Window_display_icon_sprite(temp->Pos_X+temp->Width-10,temp->Pos_Y+(temp->Height-7)/2,ICON_DROPDOWN);
return temp;
}
// Ajoute un choix à une dropdown. Le libellé est seulement référencé,
// il doit pointer sur une zone qui doit être encore valide à la fermeture
// de la fenêtre (comprise).
void Window_dropdown_add_item(T_Dropdown_button * dropdown, word btn_number, const char *label)
{
T_Dropdown_choice *temp;
T_Dropdown_choice *last;
temp=(T_Dropdown_choice *)malloc(sizeof(T_Dropdown_choice));
temp->Number =btn_number;
temp->Label=label;
temp->Next=NULL;
last=dropdown->First_item;
if (last)
{
// On cherche le dernier élément
for (;last->Next;last=last->Next)
;
last->Next=temp;
}
else
{
dropdown->First_item=temp;
}
}
// ------------- Suppression de tous les choix d'une dropdown ---------
void Window_dropdown_clear_items(T_Dropdown_button * dropdown)
{
T_Dropdown_choice * next_choice;
while (dropdown->First_item)
{
next_choice=dropdown->First_item->Next;
free(dropdown->First_item);
dropdown->First_item=next_choice;
}
}
//----------------------- Create a List control -----------------------
// These controls are special. They work over two controls previously created:
// - entry_button is the textual area where the list values will be printed.
// - scroller is a scroller button attached to it
T_List_button * Window_set_list_button(T_Special_button * entry_button, T_Scroller_button * scroller, Func_draw_list_item draw_list_item, byte color_index)
{
T_List_button *temp;
temp=(T_List_button *)malloc(sizeof(T_List_button));
temp->Number =++Window_nb_buttons;
temp->List_start = 0;
temp->Cursor_position = 0;
temp->Entry_button = entry_button;
temp->Scroller = scroller;
temp->Draw_list_item = draw_list_item;
temp->Color_index = color_index;
temp->Next=Window_list_button_list;
Window_list_button_list=temp;
return temp;
}
void Window_redraw_list(T_List_button * list)
{
int i;
for (i=Min(list->Scroller->Nb_visibles-1, list->Scroller->Nb_elements-1); i>=0; i--)
{
list->Draw_list_item(
list->Entry_button->Pos_X,
list->Entry_button->Pos_Y + i * 8,
list->List_start + i,
i == list->Cursor_position);
}
// Remaining rectangle under list
i=list->Scroller->Nb_visibles-list->Scroller->Nb_elements;
if (i>0)
{
byte color;
color = list->Color_index == 0 ? MC_Black :
(list->Color_index == 1 ? MC_Dark :
(list->Color_index == 2 ? MC_Light : MC_White));
Window_rectangle(
list->Entry_button->Pos_X,
list->Entry_button->Pos_Y+list->Scroller->Nb_elements*8,
list->Entry_button->Width,
i*8,
color);
}
}
//----------------------- Ouverture d'un pop-up -----------------------
void Open_popup(word x_pos, word y_pos, word width,word height)
// Lors de l'appel à cette procédure, la souris doit être affichée.
// En sortie de cette procedure, la souris est effacée.
// Note : les pop-ups sont gérés comme s'ils étaient des sous-fenêtres, ils ont donc leur propre boucle d'évènements et tout, on peut ajouter des widgets dedans, ...
// Les différences sont surtout graphiques :
// -Possibilité de préciser la position XY
// -Pas de titre
// -Pas de cadre en relief mais seulement un plat, et il est blanc au lieu de noir.
{
Windows_open++;
if (height > Screen_height)
height = Screen_height;
if (y_pos + height > Screen_height) // fix dropdown that would get bellow the screen
y_pos = Screen_height - height;
Window_width=width;
Window_height=height;
Window_pos_X=x_pos;
Window_pos_Y=y_pos;
Window_draggable=0;
// Sauvegarde de ce que la fenêtre remplace
Save_background(&(Window_background[Windows_open-1]), Window_pos_X, Window_pos_Y, width, height);
/*
// Fenêtre grise
Window_rectangle(1,1,width-2,height-2,MC_Light);
// Frame noir puis en relief
Window_display_frame_mono(0,0,width,height,MC_White);
*/
if (Windows_open == 1)
{
Menu_is_visible_before_window=Menu_is_visible;
Menu_is_visible=0;
Menu_Y_before_window=Menu_Y;
Menu_Y=Screen_height;
Cursor_shape_before_window=Cursor_shape;
Cursor_shape=CURSOR_SHAPE_ARROW;
Paintbrush_hidden_before_window=Paintbrush_hidden;
Paintbrush_hidden=1;
}
// Initialisation des listes de boutons de la fenêtre
Window_normal_button_list =NULL;
Window_palette_button_list =NULL;
Window_scroller_button_list=NULL;
Window_special_button_list =NULL;
Window_dropdown_button_list =NULL;
Window_nb_buttons =0;
}
//----------------------- Fermer une fenêtre d'options -----------------------
void Close_popup(void)
// Lors de l'appel à cette procedure, la souris doit être affichée.
// En sortie de cette procedure, la souris est effacée.
{
T_Normal_button * temp1;
T_Palette_button * temp2;
T_Scroller_button * temp3;
T_Special_button * temp4;
T_Dropdown_button * temp5;
T_List_button * temp6;
Hide_cursor();
while (Window_normal_button_list)
{
temp1=Window_normal_button_list->Next;
free(Window_normal_button_list);
Window_normal_button_list=temp1;
}
while (Window_palette_button_list)
{
temp2=Window_palette_button_list->Next;
free(Window_palette_button_list);
Window_palette_button_list=temp2;
}
while (Window_scroller_button_list)
{
temp3=Window_scroller_button_list->Next;
free(Window_scroller_button_list);
Window_scroller_button_list=temp3;
}
while (Window_special_button_list)
{
temp4=Window_special_button_list->Next;
free(Window_special_button_list);
Window_special_button_list=temp4;
}
while (Window_dropdown_button_list)
{
Window_dropdown_clear_items(Window_dropdown_button_list);
temp5=Window_dropdown_button_list->Next;
free(Window_dropdown_button_list);
Window_dropdown_button_list=temp5;
}
while (Window_list_button_list)
{
temp6=Window_list_button_list->Next;
free(Window_list_button_list);
Window_list_button_list=temp6;
}
if (Windows_open != 1)
{
// Restore de ce que la fenêtre cachait
Restore_background(&Window_background[Windows_open-1], Window_pos_X, Window_pos_Y, Window_width, Window_height);
Update_window_area(0,0,Window_width,Window_height);
Windows_open--;
}
else
{
free(Window_background[Windows_open-1]);
Window_background[Windows_open-1] = NULL;
Windows_open--;
Paintbrush_hidden=Paintbrush_hidden_before_window;
Compute_paintbrush_coordinates();
Menu_Y=Menu_Y_before_window;
Menu_is_visible=Menu_is_visible_before_window;
Cursor_shape=Cursor_shape_before_window;
Display_all_screen();
Display_menu();
}
Key=0;
Mouse_K=0;
Old_MX = -1;
Old_MY = -1;
}
//////////////////////////////////////////////////////////////////////////////
// //
// Mini-MOTEUR utilisé dans les fenêtres (menus des boutons...) //
// //
//////////////////////////////////////////////////////////////////////////////
// -- Indique si on a cliqué dans une zone définie par deux points extremes --
byte Window_click_in_rectangle(short start_x,short start_y,short end_x,short end_y)
{
short x_pos,y_pos;
x_pos=((short)Mouse_X-Window_pos_X)/Menu_factor_X;
y_pos=((short)Mouse_Y-Window_pos_Y)/Menu_factor_Y;
return ((x_pos>=start_x) &&
(y_pos>=start_y) &&
(x_pos<=end_x) &&
(y_pos<=end_y));
}
// --- Attend que l'on clique dans la palette pour renvoyer la couleur choisie
// ou bien renvoie -1 si on a annulé l'action pas click-droit ou Escape ------
short Wait_click_in_palette(T_Palette_button * button)
{
short start_x=button->Pos_X+5;
short start_y=button->Pos_Y+3;
short end_x =button->Pos_X+160;
short end_y =button->Pos_Y+82;
byte selected_color;
byte old_hide_cursor;
byte old_main_magnifier_mode;
Hide_cursor();
old_hide_cursor=Cursor_hidden;
old_main_magnifier_mode=Main.magnifier_mode;
Main.magnifier_mode=0;
Cursor_hidden=0;
Cursor_shape=CURSOR_SHAPE_TARGET;
Display_cursor();
for (;;)
{
while (Get_input(20))
;
if (Mouse_K==LEFT_SIDE)
{
if (Window_click_in_rectangle(start_x,start_y,end_x,end_y))
{
Hide_cursor();
selected_color=(((Mouse_X-Window_pos_X)/Menu_factor_X)-(button->Pos_X+2)) / 10 * 16 +
(((Mouse_Y-Window_pos_Y)/Menu_factor_Y)-(button->Pos_Y+3)) / 5;
Cursor_shape=CURSOR_SHAPE_ARROW;
Cursor_hidden=old_hide_cursor;
Main.magnifier_mode=old_main_magnifier_mode;
Display_cursor();
return selected_color;
}
if ((Mouse_X<Window_pos_X) || (Mouse_Y<Window_pos_Y) ||
(Mouse_X>=Window_pos_X+(Window_width*Menu_factor_X)) ||
(Mouse_Y>=Window_pos_Y+(Window_height*Menu_factor_Y)) )
{
Hide_cursor();
selected_color=Read_pixel(Mouse_X,Mouse_Y);
Cursor_shape=CURSOR_SHAPE_ARROW;
Cursor_hidden=old_hide_cursor;
Main.magnifier_mode=old_main_magnifier_mode;
Display_cursor();
return selected_color;
}
}
if ((Mouse_K==RIGHT_SIDE) || (Key==KEY_ESC))
{
Hide_cursor();
Cursor_shape=CURSOR_SHAPE_ARROW;
Cursor_hidden=old_hide_cursor;
Main.magnifier_mode=old_main_magnifier_mode;
Display_cursor();
return -1;
}
}
}
// -------------- Récupération d'une couleur derrière un menu ----------------
void Get_color_behind_window(byte * color, byte * click)
{
short old_x=-1;
short old_y=-1;
short index;
short a,b,c,d; // Variables temporaires et multitâches...
byte * buffer = NULL;
char str[25];
byte cursor_was_hidden;
Hide_cursor();
cursor_was_hidden=Cursor_hidden;
Cursor_hidden=0;
Save_background(&buffer,Window_pos_X,Window_pos_Y,Window_width,Window_height);
a=Menu_Y;
Menu_Y=Menu_Y_before_window;
b=Menu_is_visible;
Menu_is_visible=Menu_is_visible_before_window;
Display_all_screen();
Display_menu();
Menu_Y=a;
Menu_is_visible=b;
Cursor_shape=CURSOR_SHAPE_COLORPICKER;
b=Paintbrush_hidden;
Paintbrush_hidden=1;
c=-1; // color pointée: au début aucune, comme ça on initialise tout
if (Menu_is_visible_before_window)
Print_in_menu(Buttons_Pool[BUTTON_CHOOSE_COL].Tooltip,0);
Display_cursor();
do
{
Get_input(20);
if ((Mouse_X!=old_x) || (Mouse_Y!=old_y))
{
Hide_cursor();
a=Read_pixel(Mouse_X,Mouse_Y);
if (a!=c)
{
c=a; // Mise à jour de la couleur pointée
if (Menu_is_visible_before_window)
{
sprintf(str,"%d",a);
d=strlen(str);
strcat(str," (");
sprintf(str+strlen(str),"%d",Main.palette[a].R);
strcat(str,",");
sprintf(str+strlen(str),"%d",Main.palette[a].G);
strcat(str,",");
sprintf(str+strlen(str),"%d",Main.palette[a].B);
strcat(str,")");
a=24-d;
for (index=strlen(str); index<a; index++)
str[index]=' ';
str[a]=0;
Print_in_menu(str,strlen(Buttons_Pool[BUTTON_CHOOSE_COL].Tooltip));
Print_general((26+((d+strlen(Buttons_Pool[BUTTON_CHOOSE_COL].Tooltip))<<3))*Menu_factor_X,
Menu_status_Y," ",0,c);
}
}
Display_cursor();
}
old_x=Mouse_X;
old_y=Mouse_Y;
} while (!(Mouse_K || (Key==KEY_ESC)));
if (Mouse_K)
{
Hide_cursor();
*click=Mouse_K;
*color=Read_pixel(Mouse_X,Mouse_Y);
}
else
{
*click=0;
Hide_cursor();
}
Restore_background(&buffer,Window_pos_X,Window_pos_Y,Window_width,Window_height);
Update_window_area(0, 0, Window_width, Window_height);
Cursor_shape=CURSOR_SHAPE_ARROW;
Paintbrush_hidden=b;
Cursor_hidden=cursor_was_hidden;
Display_cursor();
}
// ------------ Opération de déplacement de la fenêtre à l'écran -------------
void Move_window(short dx, short dy)
{
short new_x=Mouse_X-dx;
short new_y=Mouse_Y-dy;
short old_x;
short old_y;
short width=Window_width*Menu_factor_X;
short height=Window_height*Menu_factor_Y;
short a;
byte b;
byte *buffer=NULL;
Hide_cursor();
Horizontal_XOR_line(new_x,new_y,width);
Vertical_XOR_line(new_x,new_y+1,height-2);
Vertical_XOR_line(new_x+width-1,new_y+1,height-2);
Horizontal_XOR_line(new_x,new_y+height-1,width);
Update_rect(new_x,new_y,width,height);
Cursor_shape=CURSOR_SHAPE_MULTIDIRECTIONAL;
Display_cursor();
while (Mouse_K)
{
old_x=new_x;
old_y=new_y;
do
{
Get_input(20);
} while(Mouse_K && new_x==Mouse_X-dx && new_y==Mouse_Y-dy);
new_x=Mouse_X-dx;
if (new_x<0)
{
new_x=0;
dx = Mouse_X;
}
if (new_x>Screen_width-width)
{
new_x=Screen_width-width;
dx = Mouse_X - new_x;
}
new_y=Mouse_Y-dy;
if (new_y<0)
{
new_y=0;
dy = Mouse_Y;
}
if (new_y>Screen_height-height)
{
new_y=Screen_height-height;
dy = Mouse_Y - new_y;
}
if ((new_x!=old_x) || (new_y!=old_y))
{
Hide_cursor();
Horizontal_XOR_line(old_x,old_y,width);
Vertical_XOR_line(old_x,old_y+1,height-2);
Vertical_XOR_line(old_x+width-1,old_y+1,height-2);
Horizontal_XOR_line(old_x,old_y+height-1,width);
Horizontal_XOR_line(new_x,new_y,width);
Vertical_XOR_line(new_x,new_y+1,height-2);
Vertical_XOR_line(new_x+width-1,new_y+1,height-2);
Horizontal_XOR_line(new_x,new_y+height-1,width);
Display_cursor();
Update_rect(old_x,old_y,width,height);
Update_rect(new_x,new_y,width,height);
}
}
Hide_cursor();
Horizontal_XOR_line(new_x,new_y,width);
Vertical_XOR_line(new_x,new_y+1,height-2);
Vertical_XOR_line(new_x+width-1,new_y+1,height-2);
Horizontal_XOR_line(new_x,new_y+height-1,width);
if ((new_x!=Window_pos_X)
|| (new_y!=Window_pos_Y))
{
a=Menu_Y;
Menu_Y=Menu_Y_before_window;
b=Menu_is_visible;
Menu_is_visible=Menu_is_visible_before_window;
//Display_all_screen();
//Display_menu();
Menu_Y=a;
Menu_is_visible=b;
// Sauvegarde du contenu actuel de la fenêtre
Save_background(&buffer, Window_pos_X, Window_pos_Y, Window_width, Window_height);
// Restore de ce que la fenêtre cachait
Restore_background(&Window_background[Windows_open-1], Window_pos_X, Window_pos_Y, Window_width, Window_height);
// Sauvegarde de ce que la fenêtre remplace
Save_background(&(Window_background[Windows_open-1]), new_x, new_y, Window_width, Window_height);
// Raffichage de la fenêtre
Restore_background(&buffer, new_x, new_y, Window_width, Window_height);
// Mise à jour du rectangle englobant
Update_rect(
(new_x>Window_pos_X)?Window_pos_X:new_x,
(new_y>Window_pos_Y)?Window_pos_Y:new_y,
((new_x>Window_pos_X)?(new_x-Window_pos_X):(Window_pos_X-new_x)) + Window_width*Menu_factor_X,
((new_y>Window_pos_Y)?(new_y-Window_pos_Y):(Window_pos_Y-new_y)) + Window_height*Menu_factor_Y);
Window_pos_X=new_x;
Window_pos_Y=new_y;
}
else
{
// Update pour effacer le rectangle XOR
Update_window_area(0, 0, Window_width, Window_height);
}
Cursor_shape=CURSOR_SHAPE_ARROW;
Display_cursor();
}
///
/// Displays a dropped-down menu and handles the UI logic until the user
/// releases a mouse button.
/// This function then clears the dropdown and returns the selected item,
/// or NULL if the user wasn't highlighting an item when he closed.
T_Dropdown_choice * Dropdown_activate(T_Dropdown_button *button, short off_x, short off_y)
{
short nb_choices;
short choice_index;
short selected_index;
short old_selected_index;
short box_height;
T_Dropdown_choice *item;
// Taille de l'ombre portée (en plus des dimensions normales)
#define SHADOW_RIGHT 3
#define SHADOW_BOTTOM 4
// Comptage des items pour calculer la taille
nb_choices=0;
for (item=button->First_item; item!=NULL; item=item->Next)
{
nb_choices++;
}
box_height=3+nb_choices*8+1;
// Open a new stacked "window" to serve as drawing area.
Open_popup(
off_x+(button->Pos_X)*Menu_factor_X,
off_y+(button->Pos_Y+(button->Bottom_up?-box_height:button->Height))*Menu_factor_Y,
button->Dropdown_width+SHADOW_RIGHT,
box_height+SHADOW_BOTTOM);
// Dessin de la boite
// Bord gauche
Window_rectangle(0,0,1,box_height,MC_Black);
// Frame fonce et blanc
Window_display_frame_out(1,0,button->Dropdown_width-1,box_height);
// Ombre portée
if (SHADOW_BOTTOM)
{
Window_rectangle(SHADOW_RIGHT,
box_height,
button->Dropdown_width,
SHADOW_BOTTOM,
MC_Black);
Window_rectangle(0,
box_height,
SHADOW_RIGHT,
1,
MC_Black);
}
if (SHADOW_RIGHT)
{
Window_rectangle(button->Dropdown_width,
SHADOW_BOTTOM,
SHADOW_RIGHT,
box_height-SHADOW_BOTTOM,
MC_Black);
Window_rectangle(button->Dropdown_width,
1,
1,
SHADOW_BOTTOM,
MC_Black);
}
selected_index=-1;
while (1)
{
old_selected_index = selected_index;
// Fenêtre grise
Window_rectangle(2,1,button->Dropdown_width-3,box_height-2,MC_Light);
// Affichage des items
for(item=button->First_item,choice_index=0; item!=NULL; item=item->Next,choice_index++)
{
byte color_1;
byte color_2;
if (choice_index==selected_index)
{
color_1=MC_White;
color_2=MC_Dark;
Window_rectangle(3,2+choice_index*8,
(button->Dropdown_width-5),8,MC_Dark);
}
else
{
color_1=MC_Black;
color_2=MC_Light;
}
Print_in_window(3,2+choice_index*8,item->Label,color_1,color_2);
}
Update_window_area(0,0,Window_width,Window_height);
Display_cursor();
do
{
// Attente
Get_input(20);
// Mise à jour du survol
selected_index=Window_click_in_rectangle(2,2,button->Dropdown_width-2,box_height-1)?
(((Mouse_Y-Window_pos_Y)/Menu_factor_Y-2)>>3) : -1;
} while (Mouse_K && selected_index==old_selected_index);
if (!Mouse_K)
break;
Hide_cursor();
}
Close_popup();
if (selected_index>=0 && selected_index<nb_choices)
{
for(item=button->First_item; selected_index; item=item->Next,selected_index--)
;
return item;
}
return NULL;
}
// Gestion des dropdown
short Window_dropdown_on_click(T_Dropdown_button *button)
{
T_Dropdown_choice * item;
// Highlight the button
Hide_cursor();
Window_select_normal_button(button->Pos_X,button->Pos_Y,button->Width,button->Height);
// Handle the dropdown's logic
item = Dropdown_activate(button, Window_pos_X, Window_pos_Y);
// Unhighlight the button
Window_unselect_normal_button(button->Pos_X,button->Pos_Y,button->Width,button->Height);
Display_cursor();
if (item == NULL)
{
Window_attribute2=-1;
return 0;
}
if (button->Display_choice)
{
// Automatically update the label of the dropdown list.
int text_length = (button->Width-4-(button->Display_arrow?8:0))/8;
// Clear original label area
Window_rectangle(button->Pos_X+2,button->Pos_Y+(button->Height-7)/2,text_length*8,8,MC_Light);
Print_in_window_limited(button->Pos_X+2,button->Pos_Y+(button->Height-7)/2,item->Label,text_length ,MC_Black,MC_Light);
}
Window_attribute2=item->Number;
return button->Number;
}
// --- Fonction de clic sur un bouton a peu près ordinaire:
// Attend que l'on relache le bouton, et renvoie le numero du bouton si on
// est resté dessus, 0 si on a annulé en sortant du bouton.
short Window_normal_button_onclick(word x_pos, word y_pos, word width, word height, short btn_number)
{
while(1)
{
Hide_cursor();
Window_select_normal_button(x_pos,y_pos,width,height);
Display_cursor();
while (Window_click_in_rectangle(x_pos,y_pos,x_pos+width-1,y_pos+height-1))
{
Get_input(20);
if (!Mouse_K)
{
Hide_cursor();
Window_unselect_normal_button(x_pos,y_pos,width,height);
Display_cursor();
return btn_number;
}
}
Hide_cursor();
Window_unselect_normal_button(x_pos,y_pos,width,height);
Display_cursor();
while (!(Window_click_in_rectangle(x_pos,y_pos,x_pos+width-1,y_pos+height-1)))
{
Get_input(20);
if (!Mouse_K)
return 0;
}
}
}
// --- Returns the number of the clicked button (-1:out of the window, 0:none) ---
short Window_get_clicked_button(void)
{
T_Normal_button * temp1;
T_Palette_button * temp2;
T_Scroller_button * temp3;
T_Special_button * temp4;
T_Dropdown_button * temp5;
Window_attribute1=Mouse_K;
// Test click on normal buttons
for (temp1=Window_normal_button_list; temp1; temp1=temp1->Next)
{
if ((Input_sticky_control == 0 || Input_sticky_control == temp1->Number)
&& Window_click_in_rectangle(temp1->Pos_X,temp1->Pos_Y,temp1->Pos_X+temp1->Width-1,temp1->Pos_Y+temp1->Height-1))
{
Input_sticky_control = temp1->Number;
if (temp1->Repeatable)
{
Hide_cursor();
Window_select_normal_button(temp1->Pos_X,temp1->Pos_Y,temp1->Width,temp1->Height);
Display_cursor();
Delay_with_active_mouse((Mouse_K==1)? Config.Delay_left_click_on_slider : Config.Delay_right_click_on_slider);
Hide_cursor();
Window_unselect_normal_button(temp1->Pos_X,temp1->Pos_Y,temp1->Width,temp1->Height);
Display_cursor();
return temp1->Number;
}
return Window_normal_button_onclick(temp1->Pos_X,temp1->Pos_Y,temp1->Width,temp1->Height,temp1->Number);
}
}
// Test click on "Palette" buttons
for (temp2=Window_palette_button_list; temp2; temp2=temp2->Next)
{
if ((Input_sticky_control == 0 || Input_sticky_control == temp2->Number)
&& Window_click_in_rectangle(temp2->Pos_X+5,temp2->Pos_Y+3,temp2->Pos_X+160,temp2->Pos_Y+82))
{
Input_sticky_control = temp2->Number;
// We store the clicked color in Attribute2
Window_attribute2 = (((Mouse_X-Window_pos_X)/Menu_factor_X)-(temp2->Pos_X+2)) / 10 * 16 +
(((Mouse_Y-Window_pos_Y)/Menu_factor_Y)-(temp2->Pos_Y+3)) / 5;
return temp2->Number;
}
}
// Test click on slider/scroller bars
for (temp3=Window_scroller_button_list; temp3; temp3=temp3->Next)
{
// Button Up arrow
if ((Input_sticky_control == 0 || Input_sticky_control == (temp3->Number|1024))
&& Window_click_in_rectangle(temp3->Pos_X,temp3->Pos_Y,temp3->Pos_X+10,temp3->Pos_Y+10))
{
Input_sticky_control = temp3->Number | 1024;
Hide_cursor();
Window_select_normal_button(temp3->Pos_X,temp3->Pos_Y,11,11);
if (temp3->Position)
{
temp3->Position--;
Window_attribute1=1;
Window_attribute2=temp3->Position;
Window_draw_slider(temp3);
}
else
Window_attribute1=0;
Display_cursor();
Delay_with_active_mouse((Mouse_K==1)? Config.Delay_left_click_on_slider : Config.Delay_right_click_on_slider);
Hide_cursor();
Window_unselect_normal_button(temp3->Pos_X,temp3->Pos_Y,11,11);
Display_cursor();
return (Window_attribute1)? temp3->Number : 0;
}
// Button Down arrow
if ((Input_sticky_control == 0 || Input_sticky_control == (temp3->Number|2048))
&& ((temp3->Is_horizontal && Window_click_in_rectangle(temp3->Pos_X+temp3->Length-11,temp3->Pos_Y,temp3->Pos_X+temp3->Length-1,temp3->Pos_Y+10))
|| (!temp3->Is_horizontal && Window_click_in_rectangle(temp3->Pos_X,temp3->Pos_Y+temp3->Length-11,temp3->Pos_X+10,temp3->Pos_Y+temp3->Length-1))))
{
Input_sticky_control = temp3->Number | 2048;
Hide_cursor();
if (temp3->Is_horizontal)
Window_select_normal_button(temp3->Pos_X+temp3->Length-11,temp3->Pos_Y,11,11);
else
Window_select_normal_button(temp3->Pos_X,temp3->Pos_Y+temp3->Length-11,11,11);
if (temp3->Position+temp3->Nb_visibles<temp3->Nb_elements)
{
temp3->Position++;
Window_attribute1=2;
Window_attribute2=temp3->Position;
Window_draw_slider(temp3);
}
else
Window_attribute1=0;
Display_cursor();
Delay_with_active_mouse((Mouse_K==1)? Config.Delay_left_click_on_slider : Config.Delay_right_click_on_slider);
Hide_cursor();
if (temp3->Is_horizontal)
Window_unselect_normal_button(temp3->Pos_X+temp3->Length-11,temp3->Pos_Y,11,11);
else
Window_unselect_normal_button(temp3->Pos_X,temp3->Pos_Y+temp3->Length-11,11,11);
Display_cursor();
return (Window_attribute1)? temp3->Number : 0;
}
// Middle slider
if ((Input_sticky_control == temp3->Number) || (Input_sticky_control==0 &&
((!temp3->Is_horizontal && Window_click_in_rectangle(temp3->Pos_X,temp3->Pos_Y+12,temp3->Pos_X+10,temp3->Pos_Y+temp3->Length-13))
||(temp3->Is_horizontal && Window_click_in_rectangle(temp3->Pos_X+12,temp3->Pos_Y,temp3->Pos_X+temp3->Length-13,temp3->Pos_Y+10)))))
{
Input_sticky_control = temp3->Number;
if (temp3->Nb_elements>temp3->Nb_visibles)
{
// If there is enough room to make the cursor move:
long mouse_pos;
long origin;
// Window_attribute2 receives the position of the cursor.
if (temp3->Is_horizontal)
mouse_pos =(Mouse_X-Window_pos_X) / Menu_factor_X - (temp3->Pos_X+12);
else
mouse_pos =(Mouse_Y-Window_pos_Y) / Menu_factor_Y - (temp3->Pos_Y+12);
// The following formula is wicked. The issue is that you want two
// different behaviors:
// *) If the range is bigger than the pixel precision, the last pixel
// should map to max value, exactly.
// *) Otherwise, the possible cursor positions are separated by
// at least one full pixel, so we should find the valid position
// closest to the center of the mouse cursor position pixel.
origin = (temp3->Nb_visibles-1)*(temp3->Length-24)/temp3->Nb_elements/2;
Window_attribute2 = (mouse_pos - origin) * (temp3->Nb_elements-(temp3->Cursor_length>1?0:1)) / (temp3->Length-24-1);
if (Window_attribute2<0)
Window_attribute2=0;
else if (Window_attribute2+temp3->Nb_visibles>temp3->Nb_elements)
Window_attribute2=temp3->Nb_elements-temp3->Nb_visibles;
// If the cursor moved
if (temp3->Position!=Window_attribute2)
{
temp3->Position=Window_attribute2;
Window_attribute1=3;
Hide_cursor();
Window_draw_slider(temp3);
Display_cursor();
}
else
// If the cursor moved
Window_attribute1=0;
}
else
// If there's not enough room to make the cursor move:
Window_attribute1=0;
return (Window_attribute1)? temp3->Number : 0;
}
}
// Test click on a special button
for (temp4=Window_special_button_list; temp4; temp4=temp4->Next)
{
if ((Input_sticky_control == 0 || Input_sticky_control == temp4->Number)
&& Window_click_in_rectangle(temp4->Pos_X,temp4->Pos_Y,temp4->Pos_X+temp4->Width-1,temp4->Pos_Y+temp4->Height-1))
{
Input_sticky_control = temp4->Number;
return temp4->Number;
}
}
// Test click on a dropdown box
for (temp5=Window_dropdown_button_list; temp5; temp5=temp5->Next)
{
if ((Input_sticky_control == 0 || Input_sticky_control == temp5->Number)
&& Window_click_in_rectangle(temp5->Pos_X,temp5->Pos_Y,temp5->Pos_X+temp5->Width-1,temp5->Pos_Y+temp5->Height-1))
{
Input_sticky_control = temp5->Number;
if (Mouse_K & temp5->Active_button)
return Window_dropdown_on_click(temp5);
else
{
Window_attribute2=-1;
return Window_normal_button_onclick(temp5->Pos_X,temp5->Pos_Y,temp5->Width,temp5->Height,temp5->Number);
}
}
}
return 0;
}
short Window_get_button_shortcut(void)
{
T_Normal_button * temp;
T_Special_button * temp2;
if (Key & MOD_SHIFT)
Window_attribute1=RIGHT_SIDE;
else
Window_attribute1=LEFT_SIDE;
// On fait une première recherche
temp=Window_normal_button_list;
while (temp!=NULL)
{
if (temp->Shortcut==Key)
{
Hide_cursor();
Window_select_normal_button(temp->Pos_X,temp->Pos_Y,temp->Width,temp->Height);
Display_cursor();
Delay_with_active_mouse(Config.Delay_right_click_on_slider);
Hide_cursor();
Window_unselect_normal_button(temp->Pos_X,temp->Pos_Y,temp->Width,temp->Height);
Display_cursor();
return temp->Number;
}
temp=temp->Next;
}
// Scan for shortcut in special button list
temp2=Window_special_button_list;
while (temp2!=NULL)
{
if (temp2->Shortcut==Key)
{
return temp2->Number;
}
temp2=temp2->Next;
}
// Si la recherche n'a pas été fructueuse ET que l'utilisateur appuyait sur
// <Shift>, on regarde si un bouton ne pourrait pas réagir comme si <Shift>
// n'était pas appuyé.
if (Window_attribute1==RIGHT_SIDE)
{
temp=Window_normal_button_list;
while (temp!=NULL)
{
if (temp->Shortcut==(Key&0x0FFF))
return temp->Number;
temp=temp->Next;
}
}
// Handle arrow keys, end/home, and mouse wheel that have
// a certain behavior if a list control is present.
if (Window_list_button_list)
{
T_List_button *list = Window_list_button_list;
// If there's more than one of such control, only capture
// events if the mouse cursor is over it.
if (list->Next)
{
// to do
}
}
return 0;
}
short Window_clicked_button(void)
{
short Button;
byte old_mouse_k;
old_mouse_k=Mouse_K;
Get_input(20);
// Handle clicks
if (Mouse_K)
{
if ((Mouse_X<Window_pos_X) || (Mouse_Y<Window_pos_Y)
|| (Mouse_X>=Window_pos_X+(Window_width*Menu_factor_X))
|| (Mouse_Y>=Window_pos_Y+(Window_height*Menu_factor_Y)))
{
if (Input_sticky_control == 0 || Input_sticky_control == -1)
{
Input_sticky_control = -1;
return -1;
}
else
{
return 0;
}
}
if (!Input_sticky_control && Window_draggable && Mouse_Y < Window_pos_Y+(12*Menu_factor_Y))
{
Move_window(Mouse_X-Window_pos_X,Mouse_Y-Window_pos_Y);
}
else
{
short clicked_button;
T_List_button * list;
static dword time_last_click = 0;
static int last_list_number = -1;
dword time_now;
// Check which controls was clicked (by rectangular area)
clicked_button = Window_get_clicked_button();
// Check if it's part of a list control
for (list=Window_list_button_list; list!=NULL; list=list->Next)
{
if (list->Entry_button->Number == clicked_button)
{
// Click in the textual part of a list.
short clicked_line;
clicked_line = (((Mouse_Y-Window_pos_Y)/Menu_factor_Y)-list->Entry_button->Pos_Y)>>3;
if (clicked_line >= list->Scroller->Nb_elements) // Below last line
return 0;
time_now = GFX2_GetTicks();
if (clicked_line == list->Cursor_position)
{
// Double click check
if (old_mouse_k==0 && last_list_number==list->Number && time_now - time_last_click < Config.Double_click_speed)
{
time_last_click = time_now;
Input_sticky_control=0;
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the "special button" that covers the list.
return list->Entry_button->Number;
}
time_last_click = time_now;
last_list_number=list->Number;
// Already selected : don't activate anything
return 0;
}
Hide_cursor();
// Redraw one item as disabled
if (list->Cursor_position>=0 && list->Cursor_position<list->Scroller->Nb_visibles)
list->Draw_list_item(
list->Entry_button->Pos_X,
list->Entry_button->Pos_Y + list->Cursor_position * 8,
list->List_start + list->Cursor_position,
0);
list->Cursor_position = clicked_line;
// Redraw one item as enabled
if (list->Cursor_position>=0 && list->Cursor_position<list->Scroller->Nb_visibles)
list->Draw_list_item(
list->Entry_button->Pos_X,
list->Entry_button->Pos_Y + list->Cursor_position * 8,
list->List_start + list->Cursor_position,
1);
Display_cursor();
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the list.
return list->Number;
}
else if (list->Scroller->Number == clicked_button)
{
// Click in the scroller part of a list
if (list->List_start == list->Scroller->Position)
return 0; // Didn't actually move
// Update scroller indices
list->Cursor_position += list->List_start;
list->List_start = list->Scroller->Position;
list->Cursor_position -= list->List_start;
// Need to redraw all
Hide_cursor();
Window_redraw_list(list);
Display_cursor();
}
}
return clicked_button;
}
}
// Intercept keys
if (Key)
{
T_List_button * list;
Button=Window_get_button_shortcut();
if (Button)
{
Key=0;
return Button;
}
// Check if there's a list control and the keys can control it
for (list=Window_list_button_list; list!=NULL; list=list->Next)
{
// FIXME: Make only one list have the keyboard focus.
if (1)
{
if (Key==SDLK_UP && (list->Cursor_position+list->List_start)>0)
{
Key=0;
Hide_cursor();
list->Cursor_position--;
if (list->Cursor_position<0)
{
list->List_start=list->List_start+list->Cursor_position;
list->Cursor_position=0;
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
}
Window_redraw_list(list);
Display_cursor();
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the list.
return list->Number;
}
if (Key==SDLK_DOWN && (list->Cursor_position+list->List_start)<(list->Scroller->Nb_elements-1))
{
Key=0;
Hide_cursor();
list->Cursor_position++;
if (list->Cursor_position>(list->Scroller->Nb_visibles-1))
{
list->List_start=list->List_start+list->Cursor_position-(list->Scroller->Nb_visibles-1);
list->Cursor_position=(list->Scroller->Nb_visibles-1);
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
}
Window_redraw_list(list);
Display_cursor();
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the list.
return list->Number;
}
if (Key==SDLK_HOME && (list->Cursor_position!=0 || list->List_start!=0))
{
Key=0;
Hide_cursor();
list->Cursor_position=0;
list->List_start=0;
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
Window_redraw_list(list);
Display_cursor();
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the list.
return list->Number;
}
if (Key==SDLK_END && (list->Cursor_position+list->List_start)<(list->Scroller->Nb_elements-1))
{
Key=0;
Hide_cursor();
list->Cursor_position=(list->Scroller->Nb_elements-1)-list->List_start;
if (list->Cursor_position>(list->Scroller->Nb_visibles-1))
{
list->List_start=list->List_start+list->Cursor_position-(list->Scroller->Nb_visibles-1);
list->Cursor_position=(list->Scroller->Nb_visibles-1);
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
}
Window_redraw_list(list);
Display_cursor();
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the list.
return list->Number;
}
if (Key==SDLK_PAGEDOWN && (list->Cursor_position+list->List_start)<(list->Scroller->Nb_elements-1))
{
Key=0;
Hide_cursor();
if (list->Scroller->Nb_elements<list->Scroller->Nb_visibles)
{
list->Cursor_position=list->Scroller->Nb_elements-1;
}
else if(list->Cursor_position!=list->Scroller->Nb_visibles-1)
{
list->Cursor_position=list->Scroller->Nb_visibles-1;
}
else
{
list->List_start+=list->Scroller->Nb_visibles;
if (list->List_start+list->Scroller->Nb_visibles>list->Scroller->Nb_elements)
{
list->List_start=list->Scroller->Nb_elements-list->Scroller->Nb_visibles;
}
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
}
Window_redraw_list(list);
Display_cursor();
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the list.
return list->Number;
}
if (Key==SDLK_PAGEUP && (list->Cursor_position+list->List_start)>0)
{
Key=0;
Hide_cursor();
if(list->Cursor_position!=0)
{
list->Cursor_position=0;
}
else
{
list->List_start-=list->Scroller->Nb_visibles;
if (list->List_start<0)
{
list->List_start=0;
}
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
}
Window_redraw_list(list);
Display_cursor();
// Store the selected value as attribute2
Window_attribute2=list->List_start + list->Cursor_position;
// Return the control ID of the list.
return list->Number;
}
if (Key == KEY_MOUSEWHEELUP && list->List_start>0)
{
list->Cursor_position+=list->List_start;
if (list->List_start>=3)
list->List_start-=3;
else
list->List_start=0;
list->Cursor_position-=list->List_start;
// On affiche à nouveau la liste
Hide_cursor();
Window_redraw_list(list);
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
Display_cursor();
}
if (Key==KEY_MOUSEWHEELDOWN && list->List_start<list->Scroller->Nb_elements-list->Scroller->Nb_visibles)
{
list->Cursor_position+=list->List_start;
list->List_start+=3;
if (list->List_start+list->Scroller->Nb_visibles>list->Scroller->Nb_elements)
{
list->List_start=list->Scroller->Nb_elements-list->Scroller->Nb_visibles;
}
list->Cursor_position-=list->List_start;
// On affiche à nouveau la liste
Hide_cursor();
Window_redraw_list(list);
// Mise à jour du scroller
list->Scroller->Position=list->List_start;
Window_draw_slider(list->Scroller);
Display_cursor();
}
}
}
}
return 0;
}
// Fonction qui sert à remapper les parties sauvegardées derriere les
// fenetres ouvertes. C'est utilisé par exemple par la fenetre de palette
// Qui remappe des couleurs, afin de propager les changements.
void Remap_window_backgrounds(const byte * conversion_table, int Min_Y, int Max_Y)
{
int window_index;
byte* EDI;
int dx,cx;
for (window_index=0; window_index<Windows_open; window_index++)
{
EDI = Window_background[window_index];
// Pour chaque ligne
for(dx=0; dx<Window_stack[window_index].Height*Menu_factor_Y;dx++)
{
if (dx+Window_stack[window_index].Pos_Y>Max_Y)
return;
if (dx+Window_stack[window_index].Pos_Y<Min_Y)
EDI += Window_stack[window_index].Width*Menu_factor_X*Pixel_width;
else
{ // Pour chaque pixel
for(cx=Window_stack[window_index].Width*Menu_factor_X*Pixel_width;cx>0;cx--)
{
*EDI = conversion_table[*EDI];
EDI ++;
}
}
}
}
}
void Remap_UI_in_window_backgrounds(const byte * conversion_table)
{
int i, j;
int pos_y;
// all window backgrounds
for (i = 0; i < Windows_open; i++)
{
// remap pixels of background that are either Menu or another window below
byte * p = Window_background[i];
for (pos_y = Window_stack[i].Pos_Y;
pos_y < (Window_stack[i].Pos_Y + Window_stack[i].Height*Menu_factor_Y);
pos_y++)
{
// for each line of the background
int min_x = 0xffff;
int max_x = 0;
if (Menu_is_visible_before_window && pos_y >= Menu_Y_before_window)
{
min_x = 0;
max_x = Screen_width;
}
else for(j = 0; j < i; j++)
{
// check all windows below
if (pos_y < Window_stack[j].Pos_Y
|| pos_y >= (Window_stack[j].Pos_Y + Window_stack[j].Height*Menu_factor_Y) )
continue; // this window doesn't occupy this screen row
if (min_x > Window_stack[j].Pos_X)
min_x = Window_stack[j].Pos_X;
if (max_x < (Window_stack[j].Pos_X + Window_stack[j].Width*Menu_factor_X))
max_x = Window_stack[j].Pos_X + Window_stack[j].Width*Menu_factor_X;
}
if (min_x < Window_stack[i].Pos_X)
min_x = Window_stack[i].Pos_X;
if (max_x > Window_stack[i].Pos_X + Window_stack[i].Width*Menu_factor_X)
max_x = Window_stack[i].Pos_X + Window_stack[i].Width*Menu_factor_X;
if (max_x > min_x)
{
int x;
min_x -= Window_stack[i].Pos_X;
max_x -= Window_stack[i].Pos_X;
// do the conversion
for (x = min_x; x < max_x; x++)
{
p[x] = conversion_table[p[x]];
}
}
p += Window_stack[i].Width*Menu_factor_X*Pixel_width;
}
}
}
void Delay_with_active_mouse(int speed)
{
dword end;
byte original_mouse_k = Mouse_K;
end = GFX2_GetTicks()+speed*10;
do
{
Get_input(20);
} while (Mouse_K == original_mouse_k && GFX2_GetTicks()<end);
}
///
/// Based on which toolbars are visible, updates their offsets and
/// computes ::Menu_height and ::Menu_Y
void Compute_menu_offsets(void)
{
int i;
int offset;
// Recompute all offsets
offset=0;
Menu_height=0;
for (i = MENUBAR_COUNT-1; i >=0; i--)
{
Menu_bars[i].Top = offset;
if(Menu_bars[i].Visible)
{
offset += Menu_bars[i].Height;
Menu_height += Menu_bars[i].Height;
}
}
// Update global menu coordinates
Menu_Y = Screen_height - Menu_height * Menu_factor_Y;
}
///
/// Shows or hides a tolbar from the menu.
/// If with_redraw is set to zero, the caller should
/// redraw itself using Display_menu() and Display_all_screen().
void Set_bar_visibility(word bar, int visible, int with_redraw)
{
if (!visible && Menu_bars[bar].Visible)
{
// Hide it
Menu_bars[bar].Visible=0;
Compute_menu_offsets();
if (Main.magnifier_mode)
{
Compute_magnifier_data();
}
// On repositionne le décalage de l'image pour qu'il n'y ait pas d'in-
// -cohérences lorsqu'on sortira du mode Loupe.
if (Main.offset_Y+Screen_height>Main.image_height)
{
if (Screen_height>Main.image_height)
Main.offset_Y=0;
else
Main.offset_Y=Main.image_height-Screen_height;
}
// On fait pareil pour le brouillon
if (Spare.offset_Y+Screen_height>Spare.image_height)
{
if (Screen_height>Spare.image_height)
Spare.offset_Y=0;
else
Spare.offset_Y=Spare.image_height-Screen_height;
}
Compute_magnifier_data();
if (Main.magnifier_mode)
Position_screen_according_to_zoom();
Compute_limits();
Compute_paintbrush_coordinates();
if (with_redraw)
{
Display_menu();
Display_all_screen();
}
}
else if (visible && !Menu_bars[bar].Visible)
{
// Show it
Menu_bars[bar].Visible = 1;
Compute_menu_offsets();
Compute_magnifier_data();
if (Main.magnifier_mode)
Position_screen_according_to_zoom();
Compute_limits();
Compute_paintbrush_coordinates();
if (with_redraw)
{
Display_menu();
if (Main.magnifier_mode)
Display_all_screen();
}
}
}
///
/// Checks if the current menu toolbars suit the current image type :
/// layered vs anim. If they don't fit, swap the toolbars and return 1:
/// The caller is then responsible for refreshing the screen by calling
/// Display_menu() and Display_all_screen()
int Check_menu_mode(void)
{
if (Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
if (Menu_bars[MENUBAR_LAYERS].Visible)
{
Set_bar_visibility(MENUBAR_LAYERS, 0, 0);
Set_bar_visibility(MENUBAR_ANIMATION, 1, 0);
return 1;
}
}
else
{
if (Menu_bars[MENUBAR_ANIMATION].Visible)
{
Set_bar_visibility(MENUBAR_ANIMATION, 0, 0);
Set_bar_visibility(MENUBAR_LAYERS, 1, 0);
return 1;
}
}
return 0;
}