2376 lines
61 KiB
C++
Executable File
2376 lines
61 KiB
C++
Executable File
#define XI_INTERNAL
|
|
#include <xinclude.h>
|
|
|
|
#ifndef STX_DATA
|
|
extern "C"
|
|
{
|
|
#include <xitext.h>
|
|
#include <xistx.h>
|
|
#include <xiutils.h>
|
|
}
|
|
#endif
|
|
|
|
#include <colors.h>
|
|
#include <config.h>
|
|
#include <controls.h>
|
|
#include <image.h>
|
|
#include <mask.h>
|
|
#include <urldefid.h>
|
|
|
|
#include <statbar.h>
|
|
|
|
COLOR MASK_BACK_COLOR = MAKE_COLOR(201,194,188); // COLOR_LTGRAY;
|
|
COLOR MASK_LIGHT_COLOR = COLOR_WHITE;
|
|
COLOR MASK_DARK_COLOR = COLOR_GRAY;
|
|
COLOR BTN_BACK_COLOR = COLOR_LTGRAY;
|
|
COLOR BTN_LIGHT_COLOR = COLOR_WHITE;
|
|
COLOR BTN_DARK_COLOR = COLOR_GRAY;
|
|
COLOR TOOL_BACK_COLOR = COLOR_GRAY;
|
|
COLOR NORMAL_COLOR = COLOR_BLACK;
|
|
COLOR NORMAL_BACK_COLOR = COLOR_WHITE;
|
|
COLOR DISABLED_COLOR = COLOR_DKGRAY;
|
|
COLOR DISABLED_BACK_COLOR = MASK_BACK_COLOR;
|
|
COLOR FOCUS_COLOR = NORMAL_COLOR;
|
|
COLOR FOCUS_BACK_COLOR = COLOR_YELLOW;
|
|
COLOR REQUIRED_BACK_COLOR = MAKE_COLOR(255,255,156);
|
|
|
|
bool CAMPI_SCAVATI = TRUE;
|
|
bool AUTOSELECT = FALSE;
|
|
bool ADVANCED_GRAPHICS = TRUE;
|
|
bool SMALL_ICONS=FALSE;
|
|
|
|
HIDDEN bool _button_blocked = FALSE;
|
|
HIDDEN int _last_mouse_button = 0;
|
|
HIDDEN TDropDownList* _cur_ddl = NULL;
|
|
|
|
|
|
short low_get_focus_id(WINDOW win)
|
|
{
|
|
XI_OBJ * itf = xi_get_itf((XinWindow)win);
|
|
XI_OBJ * obj = xi_get_focus(itf);
|
|
|
|
if (obj == NULL || obj->type == XIT_ITF)
|
|
return -1;
|
|
if (obj->type == XIT_CELL || (obj->type == XIT_BTN && obj->parent != obj->itf))
|
|
obj = obj->parent;
|
|
|
|
return obj->cid;
|
|
}
|
|
|
|
HIDDEN int X_FU_MULTIPLE = 0;
|
|
HIDDEN int Y_FU_MULTIPLE = 0;
|
|
HIDDEN const int ITF_CID = 30000;
|
|
|
|
KEY TControl::xiev_to_key(const XI_EVENT* xiev)
|
|
{
|
|
const int k = xiev->v.chr.ch;
|
|
KEY key = k;
|
|
if (key < K_INS || key > K_HELP)
|
|
{
|
|
if (xiev->v.chr.shift && (k < ' ' || k >= K_UP)) key += K_SHIFT;
|
|
//if (xiev->v.chr.control && k >= ' ') key += K_CTRL;
|
|
// Correzione per gestire i tasti AltGr sulle tastiere non U.S.A.
|
|
if (xiev->v.chr.control && (k > K_SHIFT || (k >= K_F1 && k <= K_F12) || isalnum(k) || strchr("\r+-*/",k) != NULL))
|
|
key += K_CTRL;
|
|
}
|
|
return key;
|
|
}
|
|
|
|
|
|
// @doc INTERNAL
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TPicture_array
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TPicture_array : public TArray
|
|
{
|
|
public:
|
|
bool add(short id);
|
|
|
|
const TImage& image(short id) const { return (const TImage&)operator[](id); }
|
|
bool exist(short id) const { return objptr(id) != NULL; }
|
|
void reload();
|
|
|
|
TPicture_array() : TArray(128) {}
|
|
virtual ~TPicture_array() {}
|
|
};
|
|
|
|
bool TPicture_array::add(short id)
|
|
{
|
|
TImage* i = (TImage*)objptr(id);
|
|
if (i == NULL)
|
|
{
|
|
if (SMALL_ICONS)
|
|
{
|
|
ignore_xvt_errors(TRUE);
|
|
i = new TImage(10000+id);
|
|
ignore_xvt_errors(FALSE);
|
|
if (!i->ok())
|
|
i->load(id);
|
|
}
|
|
else
|
|
i = new TImage(id);
|
|
|
|
if (i->ok())
|
|
{
|
|
i->convert_transparent_color(BTN_BACK_COLOR);
|
|
TArray::add(i, id);
|
|
}
|
|
else
|
|
{
|
|
delete i;
|
|
i = NULL;
|
|
}
|
|
}
|
|
return i != NULL;
|
|
}
|
|
|
|
void TPicture_array::reload()
|
|
{
|
|
for (short id = last(); id > 0; id--) if (exist(id))
|
|
{
|
|
destroy(id);
|
|
add(id);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Utility functions
|
|
///////////////////////////////////////////////////////////
|
|
|
|
HIDDEN TPicture_array* _picture = NULL;
|
|
HIDDEN XVT_FNTID DEF_FONT = NULL;
|
|
HIDDEN XVT_FNTID FAT_FONT = NULL;
|
|
|
|
XVT_FNTID xvt_default_font(bool bold)
|
|
{
|
|
if (DEF_FONT == NULL)
|
|
{
|
|
DEF_FONT = xvt_dwin_get_font(TASK_WIN);
|
|
|
|
TConfig font(CONFIG_GUI, "Font");
|
|
TString font_ser_desc(font.get("FontDesc"));
|
|
if (font_ser_desc.empty())
|
|
#ifdef WIN32
|
|
font_ser_desc = "01\\Courier\\0\\10\\WIN01/-13/0/0/0/400/0/0/0/0/1/2/1/49/Courier";
|
|
#else
|
|
font_ser_desc = "01\\Courier\\0\\10\\WIN01/10/0/0/0/400/0/0/0/0/1/2/1/49/Courier";
|
|
#endif
|
|
xvt_font_deserialize(DEF_FONT, (char *)(const char *) font_ser_desc);
|
|
xvt_font_map_using_default(DEF_FONT);
|
|
CHECK(xvt_font_is_mapped(DEF_FONT), "Can't map native font");
|
|
|
|
xvt_dwin_set_font(TASK_WIN, DEF_FONT);
|
|
xvt_menu_set_font_sel(TASK_WIN, DEF_FONT);
|
|
|
|
FAT_FONT = xvt_font_create();
|
|
xvt_font_copy(FAT_FONT, DEF_FONT, XVT_FA_ALL);
|
|
xvt_font_set_style(FAT_FONT, XVT_FS_BOLD);
|
|
xvt_font_map_using_default(FAT_FONT);
|
|
CHECK(xvt_font_is_mapped(FAT_FONT), "Can't map native font");
|
|
|
|
// Compute true text size
|
|
|
|
RCT pc; xvt_vobj_get_client_rect(TASK_WIN, &pc);
|
|
|
|
ROWY = (pc.bottom - pc.top) / 25;
|
|
const int COLX = (pc.right - pc.left) / 80;
|
|
|
|
TString str(80, 'M');
|
|
CHARX = xvt_dwin_get_text_width(TASK_WIN, str.get_buffer(), str.size()) / str.size();
|
|
|
|
int leading, ascent, descent;
|
|
xvt_dwin_get_font_metrics(TASK_WIN, &leading, &ascent, &descent);
|
|
CHARY = ascent + descent + 1;
|
|
BASEY = ascent;
|
|
|
|
if (CHARX > COLX)
|
|
CHARX = COLX;
|
|
|
|
if (CHARY > ROWY-2)
|
|
CHARY = ROWY-2;
|
|
}
|
|
|
|
return bold ? FAT_FONT : DEF_FONT;
|
|
}
|
|
|
|
XVT_FNTID xvt_load_default_font()
|
|
{
|
|
if (DEF_FONT)
|
|
{
|
|
xvt_font_destroy(DEF_FONT);
|
|
DEF_FONT = NULL;
|
|
}
|
|
if (FAT_FONT)
|
|
{
|
|
xvt_font_destroy(FAT_FONT);
|
|
FAT_FONT = NULL;
|
|
}
|
|
|
|
X_FU_MULTIPLE = Y_FU_MULTIPLE = 0;
|
|
XVT_FNTID font = xvt_default_font(FALSE);
|
|
xi_set_font_id(font);
|
|
xi_init_sysvals(); // Ricalcola i FU units
|
|
|
|
statbar_set_fontid(TASK_WIN, font);
|
|
|
|
return font;
|
|
}
|
|
|
|
#ifdef XI_R4
|
|
static byte event_map[XIE_XVT_POST_EVENT+1];
|
|
#else
|
|
static byte event_map[XIE_POST_NAVIGATION+1];
|
|
#endif
|
|
|
|
enum event_action { a_ignore, a_xvt, a_xvt_post, a_obj, a_child, a_update, a_select, a_post, a_debug };
|
|
|
|
|
|
// @func Permette di aggiornare i colori attivi del programma
|
|
void customize_colors()
|
|
|
|
// @comm Legge dal file di configurazione i colori scelti dall'utente e
|
|
// ne setta le variabili in modo da poter personalizzare i colori
|
|
//
|
|
// @xref <c TConfig>
|
|
|
|
{
|
|
TConfig colors(CONFIG_GUI, "Colors");
|
|
|
|
MASK_BACK_COLOR = colors.get_color("MaskBack", NULL, -1, MASK_BACK_COLOR);
|
|
MASK_LIGHT_COLOR = colors.get_color("MaskLight", NULL, -1, MASK_LIGHT_COLOR);
|
|
MASK_DARK_COLOR = colors.get_color("MaskDark", NULL, -1, MASK_DARK_COLOR);
|
|
NORMAL_COLOR = colors.get_color("Normal", NULL, -1, NORMAL_COLOR);
|
|
NORMAL_BACK_COLOR = colors.get_color("NormalBack", NULL, -1, NORMAL_BACK_COLOR);
|
|
REQUIRED_BACK_COLOR = colors.get_color("RequiredBack", NULL, -1, REQUIRED_BACK_COLOR);
|
|
DISABLED_COLOR = colors.get_color("Disabled", NULL, -1, DISABLED_COLOR);
|
|
DISABLED_BACK_COLOR = colors.get_color("DisabledBack", NULL, -1, DISABLED_BACK_COLOR);
|
|
FOCUS_COLOR = colors.get_color("Focus", NULL, -1, FOCUS_COLOR);
|
|
FOCUS_BACK_COLOR = colors.get_color("FocusBack", NULL, -1, FOCUS_BACK_COLOR);
|
|
TOOL_BACK_COLOR = colors.get_color("ToolBack", NULL, -1, MASK_DARK_COLOR);
|
|
CAMPI_SCAVATI = colors.get_bool("Campi3D", NULL, -1, CAMPI_SCAVATI);
|
|
AUTOSELECT = colors.get_bool("AutoSelect", NULL, -1, AUTOSELECT);
|
|
ADVANCED_GRAPHICS = colors.get_bool("AdvancedGraphics", NULL, -1, ADVANCED_GRAPHICS);
|
|
SMALL_ICONS = colors.get_bool("SmallIcons", NULL, -1, SMALL_ICONS);
|
|
const int SPEECH_MODE = colors.get_int("SpeechMode", NULL, -1, 0);
|
|
xvt_vobj_set_attr(NULL_WIN, ATTR_SPEECH_MODE, SPEECH_MODE);
|
|
|
|
xi_set_pref(XI_PREF_COLOR_LIGHT, MASK_LIGHT_COLOR);
|
|
xi_set_pref(XI_PREF_COLOR_CTRL, MASK_BACK_COLOR);
|
|
xi_set_pref(XI_PREF_COLOR_DARK, MASK_DARK_COLOR);
|
|
xi_set_pref(XI_PREF_COLOR_DISABLED, DISABLED_COLOR);
|
|
|
|
BTN_BACK_COLOR = colors.get_color("ButtonBack", NULL, -1, BTN_BACK_COLOR);
|
|
aga_set_pref(AGA_PREF_BTN_COLOR_CTRL, BTN_BACK_COLOR);
|
|
|
|
BTN_LIGHT_COLOR = colors.get_color("ButtonLight", NULL, -1, BTN_LIGHT_COLOR);
|
|
aga_set_pref(AGA_PREF_BTN_COLOR_LIGHT, BTN_LIGHT_COLOR);
|
|
|
|
BTN_DARK_COLOR = colors.get_color("ButtonDark", NULL, -1, BTN_DARK_COLOR);
|
|
aga_set_pref(AGA_PREF_BTN_COLOR_DARK, BTN_DARK_COLOR);
|
|
|
|
if (_picture)
|
|
_picture->reload();
|
|
|
|
xvt_load_default_font(); // Aggiorna anche il font!
|
|
}
|
|
|
|
void init_controls()
|
|
{
|
|
xi_set_font_id(xvt_load_default_font());
|
|
|
|
xi_set_pref(XI_PREF_NATIVE_CTRLS, FALSE);
|
|
xi_set_pref(XI_PREF_3D_LOOK, TRUE);
|
|
xi_set_pref(XI_PREF_CARET_WIDTH, 2);
|
|
#ifdef XI_R4
|
|
xi_set_pref(XI_PREF_COMBO_ICON, ICO_COMBO);
|
|
xi_set_pref(XI_PREF_OPTIMIZE_CELL_REQUESTS, TRUE);
|
|
#else
|
|
xi_set_pref(XI_PREF_COMBO_ICON, COMBO_ICON);
|
|
#endif
|
|
|
|
xi_init();
|
|
|
|
event_map[XIE_CHAR_FIELD] = a_obj;
|
|
event_map[XIE_DBL_FIELD] = a_obj;
|
|
event_map[XIE_CHG_FIELD] = a_obj;
|
|
event_map[XIE_OFF_FIELD] = a_obj;
|
|
event_map[XIE_ON_FIELD] = a_obj;
|
|
event_map[XIE_OFF_GROUP] = a_ignore;
|
|
event_map[XIE_ON_GROUP] = a_ignore;
|
|
event_map[XIE_OFF_FORM] = a_ignore;
|
|
event_map[XIE_ON_FORM] = a_ignore;
|
|
event_map[XIE_VIR_PAN] = a_ignore;
|
|
event_map[XIE_XVT_EVENT] = a_xvt;
|
|
event_map[XIE_XVT_POST_EVENT] = a_xvt_post;
|
|
event_map[XIE_INIT] = a_ignore;
|
|
event_map[XIE_BUTTON] = a_obj;
|
|
event_map[XIE_CHAR_CELL] = a_child;
|
|
event_map[XIE_CLEANUP] = a_ignore;
|
|
event_map[XIE_CLOSE] = a_ignore;
|
|
event_map[XIE_COMMAND] = a_ignore;
|
|
event_map[XIE_DBL_CELL] = a_child;
|
|
event_map[XIE_GET_FIRST] = a_obj;
|
|
event_map[XIE_GET_LAST] = a_obj;
|
|
event_map[XIE_GET_NEXT] = a_obj;
|
|
event_map[XIE_GET_PERCENT] = a_obj;
|
|
event_map[XIE_GET_PREV] = a_obj;
|
|
event_map[XIE_CELL_REQUEST] = a_obj;
|
|
event_map[XIE_CHG_CELL] = a_child;
|
|
event_map[XIE_OFF_CELL] = a_child;
|
|
event_map[XIE_ON_CELL] = a_child;
|
|
event_map[XIE_OFF_ROW] = a_child;
|
|
event_map[XIE_ON_ROW] = a_child;
|
|
event_map[XIE_OFF_COLUMN] = a_ignore;
|
|
event_map[XIE_ON_COLUMN] = a_ignore;
|
|
event_map[XIE_OFF_LIST] = a_obj;
|
|
event_map[XIE_ON_LIST] = a_obj;
|
|
event_map[XIE_REC_ALLOCATE] = a_ignore;
|
|
event_map[XIE_REC_FREE] = a_ignore;
|
|
event_map[XIE_ROW_SIZE] = a_ignore;
|
|
event_map[XIE_SELECT] = a_select;
|
|
event_map[XIE_UPDATE] = a_update;
|
|
event_map[XIE_COL_DELETE] = a_ignore;
|
|
event_map[XIE_COL_MOVE] = a_obj;
|
|
event_map[XIE_COL_SIZE] = a_obj;
|
|
event_map[XIE_POST_NAVIGATION]= a_post;
|
|
|
|
if (_picture == NULL)
|
|
_picture = new TPicture_array;
|
|
}
|
|
|
|
void free_controls()
|
|
{
|
|
if (_picture)
|
|
{
|
|
delete _picture;
|
|
_picture = NULL;
|
|
}
|
|
if (DEF_FONT)
|
|
{
|
|
xvt_font_destroy(DEF_FONT);
|
|
DEF_FONT = NULL;
|
|
}
|
|
if (FAT_FONT)
|
|
{
|
|
xvt_font_destroy(FAT_FONT);
|
|
FAT_FONT = NULL;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Interface creation
|
|
///////////////////////////////////////////////////////////
|
|
|
|
HIDDEN void xi_event_handler(XI_OBJ *itf, XI_EVENT *xiev);
|
|
|
|
HIDDEN void init_fu_multiple(XI_OBJ* itf)
|
|
{
|
|
RCT max_rct; xvt_vobj_get_client_rect(TASK_WIN, &max_rct);
|
|
xi_pu_to_fu(itf, (XinPoint *) &max_rct, 2);
|
|
X_FU_MULTIPLE = max_rct.right / 80;
|
|
Y_FU_MULTIPLE = max_rct.bottom / 25;
|
|
}
|
|
|
|
#define MASK_RECT_ID -883
|
|
|
|
HIDDEN XI_BITMAP* get_background_bitmap()
|
|
{
|
|
static bool checked = FALSE;
|
|
static XI_BITMAP* bmp = NULL;
|
|
|
|
if (ADVANCED_GRAPHICS)
|
|
{
|
|
if (bmp == NULL && !checked)
|
|
{
|
|
TConfig ini(CONFIG_GUI, "Colors");
|
|
TFilename back = ini.get("Tile");
|
|
if (back.custom_path())
|
|
bmp = xi_bitmap_create(back.get_buffer(), XI_BITMAP_TILE);
|
|
checked = TRUE;
|
|
}
|
|
return bmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
WINDOW create_interface(WINDOW parent, short x, short y, short dx, short dy,
|
|
const char* caption, TWindow* msk, bool tag)
|
|
{
|
|
if (parent == NULL_WIN)
|
|
parent = TASK_WIN;
|
|
|
|
short left = x * CHARX;
|
|
short top = y * ROWY;
|
|
short width = dx * CHARX;
|
|
short height = dy * ROWY;
|
|
|
|
if (x <= 0 || y <= 0 || dx <= 0 || dy <= 0)
|
|
{
|
|
RCT max_rct; xvt_vobj_get_client_rect(parent, &max_rct);
|
|
if (dy <= 0)
|
|
{
|
|
if (y < -1)
|
|
{
|
|
height = -top;
|
|
top += max_rct.bottom;
|
|
y = (max_rct.bottom-height)/ROWY; // 22 - height/ROWY;
|
|
}
|
|
else
|
|
height = max_rct.bottom - top + dy * ROWY;
|
|
}
|
|
|
|
if (dx <= 0)
|
|
{
|
|
width = max_rct.right + dx * CHARX;
|
|
if (left > 0)
|
|
width -= left;
|
|
}
|
|
|
|
if (x < 0) left = (max_rct.right - width) / 2;
|
|
if (y < 0) top = (max_rct.bottom - height) / 2;
|
|
}
|
|
RCT r; xvt_rect_set(&r, left, top, left+width, top+height);
|
|
|
|
const WIN_TYPE wt = (dx == 0) ? W_PLAIN : W_DOC;
|
|
long wsf = WSF_INVISIBLE | WSF_NO_MENUBAR;
|
|
|
|
WINDOW win = xvt_win_create(wt, &r, (char*)caption, 0L, parent, wsf,
|
|
EM_ALL, (EVENT_HANDLER)xi_event, (long)msk);
|
|
CHECK(win, "Can't create an XVT window for an interface");
|
|
xvt_set_font(win, NULL, 0, 0);
|
|
|
|
XI_OBJ_DEF* def = xi_create_itf_def(ITF_CID, xi_event_handler, (XinRect *) &r, (char*)caption, (long)msk);
|
|
CHECK(def, "Can't define an interface");
|
|
|
|
def->v.itf->automatic_back_color = FALSE;
|
|
def->v.itf->back_color = MASK_BACK_COLOR;
|
|
def->v.itf->font_id = xvt_default_font(FALSE);
|
|
def->v.itf->tab_on_enter = TRUE;
|
|
def->v.itf->win = (XinWindow)win;
|
|
def->v.itf->edit_menu = TRUE; // Update edit menu items
|
|
def->v.itf->menu_win = (XinWindow)TASK_WIN; // Window that owns the menu
|
|
|
|
XI_BITMAP* bmpback = get_background_bitmap();
|
|
if (bmpback != NULL && !tag)
|
|
{
|
|
def->v.itf->back_color = 0;
|
|
def->v.itf->bitmap = bmpback;
|
|
}
|
|
|
|
XI_OBJ* itf = xi_create(NULL, def);
|
|
CHECK(itf, "Can't create an interface");
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(def);
|
|
|
|
if (Y_FU_MULTIPLE == 0)
|
|
init_fu_multiple(itf);
|
|
|
|
if (dx > 0)
|
|
{
|
|
xi_pu_to_fu(itf, (XinPoint *) &r, 2);
|
|
r.right = r.left + (dx+1) * XI_FU_MULTIPLE;
|
|
r.bottom = r.top + (dy+1) * Y_FU_MULTIPLE;
|
|
xi_fu_to_pu(itf, (XinPoint *) &r, 2);
|
|
xvt_vobj_move(win, &r);
|
|
}
|
|
|
|
// Aggiunge rettangolo di cornice per pagine con tag buttons
|
|
if (tag)
|
|
{
|
|
XI_RCT rct; xi_get_xi_rct(itf, &rct);
|
|
rct.top += Y_FU_MULTIPLE; // Lascia lo spazio per i Bottoni di cambio pagina
|
|
|
|
def = xi_add_rect_def(NULL, MASK_RECT_ID, &rct, XI_ATR_VISIBLE, 0, 0);
|
|
if (bmpback != NULL)
|
|
{
|
|
def->v.rect->back_color = 0;
|
|
def->v.rect->bitmap = bmpback;
|
|
}
|
|
|
|
XI_OBJ* obj = xi_create(itf, def);
|
|
CHECK(obj, "Can't create page rectangle");
|
|
|
|
// Fa coincidere esattamente il rettangolo con la finestra che lo contiene
|
|
RCT& rect = (RCT &) obj->v.rect->rct;
|
|
const int oldy = rect.top;
|
|
xvt_vobj_get_client_rect(win, &rect);
|
|
rect.top = oldy;
|
|
xi_dequeue();
|
|
xi_tree_free(def);
|
|
}
|
|
|
|
return win;
|
|
}
|
|
|
|
void attach_interface(WINDOW win, COLOR back)
|
|
{
|
|
xvt_win_set_handler(win, (EVENT_HANDLER)xi_event);
|
|
|
|
RCT rc; xvt_vobj_get_outer_rect(win, &rc);
|
|
|
|
char caption[80]; xvt_vobj_get_title(win, caption, 80);
|
|
TWindow* parent = (TWindow*)xvt_vobj_get_data(win);
|
|
XI_OBJ_DEF* def = xi_create_itf_def(ITF_CID, xi_event_handler, (XinRect *) &rc, caption, (long)parent);
|
|
CHECK(def, "Can't define an interface");
|
|
|
|
def->v.itf->automatic_back_color = FALSE;
|
|
def->v.itf->back_color = back;
|
|
def->v.itf->tab_on_enter = TRUE;
|
|
def->v.itf->win = (XinWindow)win;
|
|
|
|
XI_OBJ* itf = xi_create(NULL, def);
|
|
CHECK(itf, "Can't create an interface");
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(def);
|
|
}
|
|
|
|
HIDDEN void xi_event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
static bool notify_xvt = TRUE;
|
|
|
|
TControl* ctl = NULL;
|
|
|
|
switch (event_map[xiev->type])
|
|
{
|
|
case a_obj:
|
|
if (xiev->v.xi_obj)
|
|
ctl = (TControl*)xi_get_app_data(xiev->v.xi_obj);
|
|
break;
|
|
case a_child:
|
|
ctl = (TControl*)xi_get_app_data(xiev->v.xi_obj->parent);
|
|
break;
|
|
case a_update:
|
|
{
|
|
int num;
|
|
XI_OBJ** button = xi_get_member_list(itf, &num);
|
|
for (int i = num-1; i >= 0; i--)
|
|
{
|
|
XI_OBJ* b = button[i];
|
|
if (b->type == XIT_BTN && b->v.btn->drawable)
|
|
{
|
|
if (xvt_rect_intersect(NULL, (RCT *) &b->v.btn->rct, &xiev->v.xvte.v.update.rct))
|
|
{
|
|
TPushbutton_control* p = (TPushbutton_control*)b->app_data;
|
|
p->update();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case a_select:
|
|
switch(xiev->v.xi_obj->type)
|
|
{
|
|
case XIT_LIST: ctl = (TControl*)xi_get_app_data(xiev->v.xi_obj); break;
|
|
case XIT_ROW :
|
|
case XIT_CELL: ctl = (TControl*)xi_get_app_data(xiev->v.xi_obj->parent); break;
|
|
default : break;
|
|
}
|
|
break;
|
|
case a_xvt:
|
|
switch (xiev->v.xvte.type)
|
|
{
|
|
case E_MOUSE_DOWN:
|
|
_last_mouse_button = xiev->v.xvte.v.mouse.button;
|
|
if (_cur_ddl != NULL)
|
|
_cur_ddl->on_mouse_down(xiev->v.xvte.v.mouse.where);
|
|
break;
|
|
case E_CHAR:
|
|
{
|
|
XI_OBJ* fo = xi_get_focus(itf);
|
|
if (fo != NULL && fo->type == XIT_CELL)
|
|
ctl = (TControl*)xi_get_app_data(fo->parent);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case a_xvt_post:
|
|
if (notify_xvt && xiev->v.xvte.type != 0)
|
|
{
|
|
TWindow* w = (TWindow*)xi_get_app_data(itf);
|
|
if (w != NULL && w->win() != NULL_WIN)
|
|
w->handler(w->win(), &xiev->v.xvte);
|
|
if (xiev->v.xvte.type == E_MOUSE_UP)
|
|
_button_blocked = FALSE;
|
|
}
|
|
else
|
|
notify_xvt = TRUE;
|
|
break;
|
|
case a_post:
|
|
{
|
|
XI_OBJ * obj = xi_get_focus(itf);
|
|
|
|
if ((obj != NULL && obj->type != XIT_ITF))
|
|
{
|
|
if ((obj->type == XIT_CELL || (obj->type == XIT_BTN && obj->v.btn->type == XIBT_RADIOBTN)))
|
|
obj = obj->parent;
|
|
CHECK(obj != NULL, "Unexpected null obj");
|
|
ctl = (TControl*)xi_get_app_data(obj);
|
|
}
|
|
}
|
|
break;
|
|
case a_debug:
|
|
break; // Put your breakpoint here
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ctl != NULL)
|
|
{
|
|
const bool ok = ctl->event_handler(itf, xiev);
|
|
if (!ok)
|
|
{
|
|
xiev->refused = TRUE;
|
|
if (xiev->type == XIE_CHAR_FIELD || xiev->type == XIE_CHAR_CELL ||
|
|
xiev->type == XIE_XVT_EVENT)
|
|
notify_xvt = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TControl
|
|
///////////////////////////////////////////////////////////
|
|
|
|
TControl::TControl()
|
|
: _obj(NULL), _fld(NULL)
|
|
{}
|
|
|
|
// Virtual destructor needed to make derived descrutors live!
|
|
TControl::~TControl()
|
|
{}
|
|
|
|
XI_OBJ* TControl::get_interface(WINDOW win) const
|
|
{
|
|
XI_OBJ* itf;
|
|
if (win == NULL_WIN)
|
|
{
|
|
CHECK(_obj, "Can't get the interface from NULL XI object");
|
|
itf = _obj->itf;
|
|
}
|
|
else
|
|
{
|
|
itf = xi_get_itf((XinWindow)win);
|
|
CHECK(itf, "Can't get the interface from a window");
|
|
}
|
|
return itf;
|
|
}
|
|
|
|
void TControl::set_tab_cid(XI_OBJ* obj, short cid) const
|
|
{
|
|
CHECK(obj, "Can't set tab cid to null control");
|
|
|
|
switch(obj->type)
|
|
{
|
|
case XIT_BTN:
|
|
obj->v.btn->tab_cid = cid;
|
|
break;
|
|
case XIT_CONTAINER:
|
|
obj->v.container->tab_cid = cid;
|
|
break;
|
|
case XIT_FIELD:
|
|
obj->v.field->tab_cid = cid;
|
|
break;
|
|
case XIT_FORM:
|
|
obj->v.form->tab_cid = cid;
|
|
break;
|
|
case XIT_LIST:
|
|
obj->v.list->tab_cid = cid;
|
|
break;
|
|
case XIT_COLUMN:
|
|
obj->parent->v.list->tab_cid = cid;
|
|
break;
|
|
default:
|
|
NFCHECK(0, "Can't set tab_cid to a static control: ", _obj->cid); break;
|
|
}
|
|
}
|
|
|
|
HIDDEN bool is_container(const XI_OBJ* obj)
|
|
{
|
|
return obj->type == XIT_CONTAINER || obj->type == XIT_FORM || obj->type == XIT_GROUP;
|
|
}
|
|
|
|
XI_OBJ* TControl::find_operable(XI_OBJ* container, bool forward, bool normal) const
|
|
{
|
|
int num;
|
|
XI_OBJ** child = xi_get_member_list(container, &num);
|
|
CHECK(num > 0, "Container too empty");
|
|
|
|
const int first = forward ? 0 : num-2;
|
|
const int last = forward ? num-1 : -1;
|
|
const int delta = forward ? +1 : -1;
|
|
|
|
XI_OBJ* found = NULL;
|
|
for (int c = first; c != last && found == NULL; c += delta)
|
|
{
|
|
XI_OBJ* obj = child[c];
|
|
switch (obj->type)
|
|
{
|
|
case XIT_LIST:
|
|
case XIT_BTN:
|
|
case XIT_FIELD:
|
|
if (normal && obj->cid > 0)
|
|
found = obj;
|
|
break;
|
|
case XIT_CONTAINER:
|
|
case XIT_FORM:
|
|
case XIT_GROUP:
|
|
if (obj->cid > 0)
|
|
{
|
|
if (normal)
|
|
{
|
|
int num;
|
|
XI_OBJ** child = xi_get_member_list(obj, &num);
|
|
found = forward ? child[0] : child[num-1];
|
|
}
|
|
else
|
|
found = obj;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found == NULL)
|
|
{
|
|
found = child[num-1];
|
|
if (is_container(found) && normal)
|
|
{
|
|
int num;
|
|
XI_OBJ** child = xi_get_member_list(found, &num);
|
|
found = forward ? child[0] : child[num-1];
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
void TControl::update_tab_cid()
|
|
{
|
|
const bool is_cont = is_container(_obj);
|
|
|
|
XI_OBJ *first = find_operable(_obj->itf, TRUE, !is_cont);
|
|
set_tab_cid(_obj, first->cid);
|
|
|
|
XI_OBJ *last = find_operable(_obj->itf, FALSE, !is_cont);
|
|
set_tab_cid(last, _obj->cid);
|
|
|
|
if (is_cont)
|
|
{
|
|
XI_OBJ* fi = find_operable(_obj->itf, TRUE, TRUE);
|
|
XI_OBJ* la = find_operable(_obj->itf, FALSE, TRUE);
|
|
int num;
|
|
XI_OBJ** child = xi_get_member_list(_obj, &num);
|
|
set_tab_cid(child[num-1], fi->cid);
|
|
set_tab_cid(la, child[0]->cid);
|
|
} else
|
|
if (_obj->parent != _obj->itf)
|
|
{
|
|
XI_OBJ* fi = find_operable(_obj->itf, TRUE, FALSE);
|
|
XI_OBJ* la = find_operable(_obj->itf, FALSE, FALSE);
|
|
set_tab_cid(_obj->parent, fi->cid);
|
|
set_tab_cid(la, _obj->parent->cid);
|
|
}
|
|
}
|
|
|
|
void TControl::coord2rct(WINDOW win, short x, short y, short dx, short dy, RCT & rct) const
|
|
{
|
|
// Spazio da lasciare prima di toccare i bordi
|
|
const int X_FU_DELTA = XI_FU_MULTIPLE / 4;
|
|
const int Y_FU_DELTA = XI_FU_MULTIPLE / 8;
|
|
|
|
XI_OBJ* itf = get_interface(win);
|
|
|
|
if (Y_FU_MULTIPLE == 0) // Puo' succedere che non sia ancora inizializzato
|
|
init_fu_multiple(itf);
|
|
|
|
// Se ci sono i tab controls sulla pagina salta una riga
|
|
if (y >= 0)
|
|
{
|
|
int num;
|
|
XI_OBJ** child = xi_get_member_list(itf, &num);
|
|
if (num > 0 && child[0]->cid == MASK_RECT_ID)
|
|
y++;
|
|
}
|
|
|
|
RCT max_rct; xvt_vobj_get_client_rect(win, &max_rct);
|
|
xi_pu_to_fu(itf, (XinPoint *)&max_rct, 2);
|
|
const int MAXX = max_rct.right;
|
|
const int MAXY = max_rct.bottom;
|
|
|
|
int width = XI_FU_MULTIPLE;
|
|
if (dx > 0)
|
|
width = dx * XI_FU_MULTIPLE;
|
|
|
|
int height = XI_FU_MULTIPLE;
|
|
if (dy > 1)
|
|
height += (dy-1) * Y_FU_MULTIPLE;
|
|
|
|
if (x < 0)
|
|
{
|
|
x = -x;
|
|
if (x > 10)
|
|
{
|
|
const int num = x / 10 -1;
|
|
const int tot = x % 10;
|
|
const int spc = (MAXX - tot*width) / (tot+1);
|
|
rct.left = spc + num * (spc+width);
|
|
}
|
|
else
|
|
rct.left = MAXX - width - x * XI_FU_MULTIPLE;
|
|
}
|
|
else
|
|
{
|
|
rct.left = x * XI_FU_MULTIPLE + X_FU_DELTA;
|
|
|
|
if (dx > 0 && MAXX > 80 * XI_FU_MULTIPLE)
|
|
rct.left += (MAXX - 80 * XI_FU_MULTIPLE) / 2;
|
|
}
|
|
|
|
if (y < 0)
|
|
{
|
|
y = -y;
|
|
if (y > 10)
|
|
{
|
|
const int num = y / 10 -1;
|
|
const int tot = y % 10;
|
|
const int spc = (MAXY - tot*height) / (tot+1);
|
|
rct.top = spc + num * (spc+height);
|
|
}
|
|
else
|
|
rct.top = MAXY - height - (y-1) * Y_FU_MULTIPLE - Y_FU_DELTA;
|
|
}
|
|
else
|
|
rct.top = y * Y_FU_MULTIPLE + Y_FU_DELTA;
|
|
|
|
if (dx > 0)
|
|
rct.right = rct.left + width;
|
|
else
|
|
rct.right = MAXX + (dx-1) * XI_FU_MULTIPLE - X_FU_DELTA;
|
|
|
|
if (dy > 0)
|
|
rct.bottom = rct.top + height;
|
|
else
|
|
rct.bottom = MAXY + dy * Y_FU_MULTIPLE - X_FU_DELTA;
|
|
}
|
|
|
|
short TControl::id() const
|
|
{ return _obj->cid; }
|
|
|
|
int TControl::type() const
|
|
{ return _obj->type; }
|
|
|
|
RCT& TControl::get_rect(RCT& r) const
|
|
{
|
|
xi_get_rect(_obj, (XinRect *) &r);
|
|
return r;
|
|
}
|
|
|
|
void TControl::set_rect(const RCT& r)
|
|
{
|
|
xi_set_rect(_obj, (XinRect*)&r, FALSE);
|
|
}
|
|
|
|
unsigned long TControl::flags2attr(const char* flags) const
|
|
{
|
|
unsigned long attrib = XI_ATR_VISIBLE | XI_ATR_ENABLED;
|
|
for (const char* f = flags; *f; f++)
|
|
{
|
|
switch(toupper(*f))
|
|
{
|
|
case '*': attrib |= XI_ATR_PASSWORD; break;
|
|
case 'C': attrib |= XI_ATR_HCENTER; break;
|
|
case 'D': attrib &= ~XI_ATR_ENABLED; break;
|
|
case 'H': attrib &= ~XI_ATR_VISIBLE; break;
|
|
case 'R': attrib |= XI_ATR_RJUST; break;
|
|
default : break;
|
|
}
|
|
}
|
|
return attrib;
|
|
}
|
|
|
|
const char* TControl::parse_caption(const char* cap, bool& bold, COLOR& color) const
|
|
{
|
|
bold = FALSE;
|
|
color = NORMAL_COLOR;
|
|
|
|
const char* t;
|
|
|
|
for (t = cap; *t == '@' || *t == '$'; t++)
|
|
{
|
|
if (*t == '@')
|
|
{
|
|
const char code = toupper(*(t+1));
|
|
if (code == 'B')
|
|
{
|
|
bold = TRUE;
|
|
t++;
|
|
}
|
|
else
|
|
{
|
|
NFCHECK("Bad character (%c) after @ in %s", code, cap);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*(t+1) == '[')
|
|
{
|
|
t += 2; // Skip [
|
|
if (isalpha(*t))
|
|
color = trans_color(*t);
|
|
else
|
|
{
|
|
int r = 0, g = 0, b = 0;
|
|
sscanf(t, "%d,%d,%d", &r, &g, &b);
|
|
if (r == 0 && g == 0 && b == 0)
|
|
color = COLOR_BLACK;
|
|
else
|
|
color = MAKE_COLOR(r, g, b);
|
|
}
|
|
while (*t && *t != ']') t++;
|
|
}
|
|
else
|
|
{
|
|
NFCHECK(0, "Bad character after $ in prompt");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
WINDOW TControl::parent() const
|
|
{
|
|
return (WINDOW)xi_get_window(_obj->itf);
|
|
}
|
|
|
|
void TControl::set_focus() const
|
|
{
|
|
xi_set_focus(_obj);
|
|
}
|
|
|
|
bool TControl::notify_key(KEY k)
|
|
{
|
|
bool ok = TRUE;
|
|
|
|
if (_fld == NULL)
|
|
{
|
|
TWindow* msk = (TWindow*)xi_get_app_data(get_interface());
|
|
CHECK(msk, "Can't get TMask from XI interface");
|
|
if (msk->is_kind_of(CLASS_MASK))
|
|
{
|
|
const TMask& m = (TMask&)*msk;
|
|
const int pos = m.id2pos(id());
|
|
if (pos >= 0)
|
|
_fld = &m.fld(pos);
|
|
}
|
|
}
|
|
if (_fld)
|
|
ok = _fld->on_key(k);
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool TControl::is_edit_key(KEY k) const
|
|
{
|
|
return k >= ' ' && k < 255 && k != K_DEL;
|
|
}
|
|
|
|
bool TControl::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
bool ok = TRUE;
|
|
return ok;
|
|
}
|
|
|
|
void TControl::change_attrib(unsigned long mask, bool on, XI_OBJ* obj)
|
|
{
|
|
if (obj == NULL) obj = _obj;
|
|
const unsigned long old_attr = xi_get_attrib(obj);
|
|
unsigned long attr = old_attr;
|
|
if (on) attr |= mask;
|
|
else attr &= ~mask;
|
|
if (attr != old_attr) // C'e' un vero cambiamento d'attributo
|
|
{
|
|
#ifndef XI_R4
|
|
// Se la finestra e' chiusa e il campo e un bottone o e' editabile allora cambia l'attributo
|
|
// senza ridisegnare il campo, il che provocherebbe problemi di funzionamento degli sheet
|
|
if (_obj->type == XIT_BTN || _obj->type == XIT_FIELD)
|
|
{
|
|
const TWindow* w = (const TWindow*)_obj->itf->app_data;
|
|
if (!w->is_open())
|
|
{
|
|
if (_obj->type == XIT_FIELD)
|
|
((STX_DATA*)_obj->v.field->stx)->attrib = attr;
|
|
else
|
|
_obj->v.btn->attrib = attr;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
// Usa tranquillamente il metodo ortodosso
|
|
xi_set_attrib(obj, attr);
|
|
}
|
|
}
|
|
|
|
// @doc INTERNAL
|
|
|
|
// @mfunc Mostra/Nasconde il controllo
|
|
void TControl::show(
|
|
bool on) // @parm Operazione da svolgere sul controllo:
|
|
// @flag TRUE | Il controllo viene mostrato
|
|
// @flag FALSE | Il controllo viene nascosto
|
|
{
|
|
change_attrib(XI_ATR_VISIBLE, on);
|
|
}
|
|
|
|
// @doc INTERNAL
|
|
|
|
// @mfunc Abilita/disabilita il controllo
|
|
void TControl::enable(
|
|
bool on) // @parm Operazione da svolgere sul controllo:
|
|
// @flag TRUE | Il controllo viene abilitato
|
|
// @flag FALSE | Il controllo viene disabilitato
|
|
{
|
|
change_attrib(XI_ATR_ENABLED, on);
|
|
}
|
|
|
|
// @mfunc Abilita/disabilita l'autoselezione del testo
|
|
void TControl::autoselect(bool on)
|
|
{
|
|
change_attrib(XI_ATR_AUTOSELECT, on);
|
|
}
|
|
|
|
// @mfunc Allineamento a destra del testo
|
|
void TControl::set_rjust(bool on)
|
|
{
|
|
change_attrib(XI_ATR_RJUST, on);
|
|
}
|
|
|
|
// @doc INTERNAL
|
|
|
|
const char* TControl::caption() const
|
|
{
|
|
const char* c = xi_get_text(_obj, NULL, 512);
|
|
return c;
|
|
}
|
|
|
|
void TControl::set_caption(const char* c)
|
|
{
|
|
xi_set_text(_obj, (char*)c);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TText
|
|
///////////////////////////////////////////////////////////
|
|
|
|
TText_control::TText_control(WINDOW win, short cid,
|
|
short left, short top, short width, short height,
|
|
const char* flags, const char* text)
|
|
{
|
|
bool bold;
|
|
COLOR color;
|
|
TString t = parse_caption(text, bold, color);
|
|
t.rtrim();
|
|
if (width <= 0)
|
|
width = t.len();
|
|
|
|
RCT rct; coord2rct(win, left, top, width, height, rct);
|
|
rct.right += bold ? (width*XI_FU_MULTIPLE/4) : XI_FU_MULTIPLE;
|
|
|
|
const unsigned long attrib = flags2attr(flags);
|
|
XI_OBJ_DEF* def = xi_add_text_def(NULL, cid, (XinRect *) &rct, attrib, (char*)(const char*)t);
|
|
CHECKS(def, "Can't create the definition of TText_control:", text);
|
|
def->v.text->fore_color = color;
|
|
if (bold)
|
|
def->v.text->font_id = xvt_default_font(TRUE);
|
|
_obj = xi_create(get_interface(win), def);
|
|
CHECKS(_obj, "Can't create TText_control ", text);
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(def);
|
|
}
|
|
|
|
void TText_control::set_caption(const char* text)
|
|
{
|
|
bool bold;
|
|
COLOR color;
|
|
const char* c = parse_caption(text, bold, color);
|
|
xi_set_text(_obj, (char*)c);
|
|
_obj->v.text->fore_color = color;
|
|
|
|
#ifdef XI_R4
|
|
xi_set_obj_font_id(_obj, xvt_default_font(bold));
|
|
#else
|
|
FONT_OBJ* &font = _obj->v.text->font;
|
|
|
|
if (font == NULL)
|
|
font = (FONT_OBJ*)xi_tree_malloc(sizeof(FONT_OBJ), _obj->v.text);
|
|
*font = xi_create_copy_font_id(get_interface(), xvt_default_font(bold), NULL);
|
|
RCT rct; get_rect(rct);
|
|
|
|
if (bold)
|
|
{
|
|
const int estimated_size = strlen(c) * (XI_FU_MULTIPLE+2);
|
|
if (estimated_size > (rct.right-rct.left))
|
|
{
|
|
rct.right = rct.left + estimated_size;
|
|
set_rect(rct);
|
|
}
|
|
}
|
|
xi_invalidate_rect(parent(), &rct);
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TGroupbox_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
TGroupbox_control::TGroupbox_control(WINDOW win, short cid,
|
|
short left, short top, short width, short height,
|
|
const char* flags, const char* text)
|
|
: TText_control(win, cid, left, top, width, 1, flags, text)
|
|
{
|
|
// Alza di un pixel il rettangolo per non coprire il rettangolo
|
|
RCT& tr = (RCT &) _obj->v.text->rct;
|
|
tr.top--; tr.bottom--;
|
|
|
|
RCT rct; coord2rct(win, left, top, width, height, rct);
|
|
rct.top += XI_FU_MULTIPLE - 2;
|
|
rct.bottom -= Y_FU_MULTIPLE / 2;
|
|
|
|
XI_OBJ_DEF* def = xi_add_rect_def(NULL, cid, (XinRect *) &rct, XI_ATR_VISIBLE, 0, 0); // Ignore colors
|
|
CHECKS(def, "Can't create the definition of TGroupbox_control ", text);
|
|
def->v.rect->hilight_color = MASK_LIGHT_COLOR;
|
|
def->v.rect->back_color = MASK_BACK_COLOR;
|
|
def->v.rect->shadow_color = MASK_DARK_COLOR;
|
|
|
|
const bool erre = strchr(flags, 'R') != NULL;
|
|
if (erre)
|
|
{
|
|
def->v.rect->well = TRUE; // Mette in rilievo il rettangolo
|
|
change_attrib(XI_ATR_RJUST, FALSE, _obj); // Toglie l'erroneo allineamento a destra del titolo
|
|
}
|
|
_rct = xi_create(get_interface(win), def);
|
|
CHECKD(_rct, "Can't create Groupbox_control ", cid);
|
|
|
|
RCT& rt = (RCT &) _obj->v.text->rct;
|
|
rt.top--; rt.bottom--;
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(def);
|
|
}
|
|
|
|
void TGroupbox_control::show(bool on)
|
|
{
|
|
TText_control::show(on);
|
|
change_attrib(XI_ATR_VISIBLE, on, _rct);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TField
|
|
///////////////////////////////////////////////////////////
|
|
|
|
static bool in_create = FALSE;
|
|
|
|
TField_control::TField_control(WINDOW win, short cid,
|
|
short left, short top, short width, short maxlen,
|
|
const char* flags, const char* text)
|
|
{
|
|
const bool button = strchr(flags, 'B') != NULL;
|
|
create(win, cid, left, top, width, 1, maxlen, flags, text, button);
|
|
}
|
|
|
|
void TField_control::create(WINDOW win, short cid,
|
|
short left, short top, short width, short height, short maxlen,
|
|
const char* flags, const char* text, bool button)
|
|
{
|
|
in_create = TRUE;
|
|
|
|
const short fcid = cid > 0 ? cid + 2000 : cid - 2000;
|
|
XI_OBJ_DEF* frm_def = xi_add_form_def(NULL, fcid, fcid);
|
|
frm_def->app_data = (long)this;
|
|
|
|
RCT rct; coord2rct(win, left, top, width, height, rct);
|
|
rct.right += XI_FU_MULTIPLE/4;
|
|
|
|
unsigned long attrib = flags2attr(flags) | XI_ATR_EDITMENU;
|
|
if (AUTOSELECT)
|
|
attrib |= XI_ATR_AUTOSELECT;
|
|
if (!CAMPI_SCAVATI)
|
|
attrib |= XI_ATR_BORDER;
|
|
if (maxlen > width)
|
|
attrib |= XI_ATR_AUTOSCROLL;
|
|
|
|
XI_OBJ_DEF* def = xi_add_field_def(frm_def, cid,
|
|
rct.top, rct.left,
|
|
rct.right - rct.left,
|
|
attrib, cid, maxlen+1,
|
|
NORMAL_COLOR, NORMAL_BACK_COLOR,
|
|
NORMAL_COLOR, DISABLED_BACK_COLOR,
|
|
FOCUS_COLOR);
|
|
|
|
def->app_data = (long)this;
|
|
XI_FIELD_DEF* f = def->v.field;
|
|
f->well = CAMPI_SCAVATI;
|
|
f->active_back_color = FOCUS_BACK_COLOR;
|
|
|
|
if (height == 1)
|
|
{
|
|
f->auto_tab = TRUE;
|
|
if (button)
|
|
{
|
|
f->button = TRUE;
|
|
f->pixel_button_distance = 1;
|
|
}
|
|
}
|
|
else // E' un multiline, quindi setto il rettangolo
|
|
{
|
|
f->xi_rct.top = rct.top;
|
|
f->xi_rct.bottom = rct.bottom;
|
|
f->xi_rct.right = rct.right;
|
|
f->xi_rct.left = rct.left;
|
|
f->cr_ok = TRUE;
|
|
f->var_len_text = TRUE;
|
|
}
|
|
|
|
XI_OBJ* itf = get_interface(win);
|
|
XI_OBJ* form = xi_create(itf, frm_def);
|
|
CHECKD(form, "Can't create the form for field ", cid);
|
|
_obj = xi_get_obj(form, cid);
|
|
CHECKD(_obj, "Can't create field ", cid);
|
|
STX_DATA* stx = (STX_DATA*)_obj->v.field->stx;
|
|
CHECKD(stx, "NULL stx for field ", cid);
|
|
|
|
if (button)
|
|
{
|
|
// Aggiusta il rettangolo del bottone in modo da allinearlo al testo
|
|
RCT& br = (RCT&)_obj->v.field->btn_rct;
|
|
const int offset = stx->rct.right - br.left - 1;
|
|
br.left += offset;
|
|
br.right += offset;
|
|
br.bottom = stx->rct.bottom;
|
|
}
|
|
|
|
update_tab_cid();
|
|
xi_dequeue();
|
|
xi_tree_free(frm_def);
|
|
|
|
in_create = FALSE;
|
|
}
|
|
|
|
void TField_control::show_button(bool on)
|
|
{
|
|
XI_FIELD_DATA* f = _obj->v.field;
|
|
bool has_button = f->button != 0;
|
|
if (has_button != on)
|
|
{
|
|
f->button = on;
|
|
xi_invalidate_rect((XinWindow)parent(), &f->btn_rct);
|
|
}
|
|
}
|
|
|
|
bool TField_control::read_only() const
|
|
{
|
|
return bool((xi_get_attrib(_obj) & XI_ATR_READONLY) != 0);
|
|
}
|
|
|
|
void TField_control::set_read_only(bool on)
|
|
{
|
|
change_attrib(XI_ATR_READONLY, on);
|
|
}
|
|
|
|
bool TField_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
if (in_create)
|
|
return FALSE;
|
|
|
|
bool ok = TRUE;
|
|
|
|
switch(xiev->type)
|
|
{
|
|
case XIE_OFF_FIELD:
|
|
ok = notify_key(K_TAB);
|
|
break;
|
|
case XIE_ON_FIELD:
|
|
notify_key(K_CTRL+K_TAB);
|
|
break;
|
|
case XIE_DBL_FIELD:
|
|
notify_key(K_F9);
|
|
break;
|
|
case XIE_CHAR_FIELD:
|
|
{
|
|
KEY k = xiev_to_key(xiev);
|
|
if (k == K_PREV || k == K_NEXT || k > K_CTRL)
|
|
{
|
|
k = K_TAB;
|
|
xiev->refused = TRUE;
|
|
}
|
|
if (k == K_TAB || is_edit_key(k) || (k > K_F1 && k < K_F12))
|
|
ok = notify_key(k);
|
|
}
|
|
break;
|
|
case XIE_CHG_FIELD:
|
|
notify_key(K_CTRL+K_SPACE);
|
|
break;
|
|
case XIE_BUTTON:
|
|
notify_key(K_F9);
|
|
break;
|
|
default:
|
|
ok = TControl::event_handler(itf, xiev);
|
|
break;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
void TField_control::set_focus() const
|
|
{
|
|
TControl::set_focus();
|
|
}
|
|
|
|
void TField_control::set_back_color(COLOR col)
|
|
{
|
|
xi_set_color(_obj, XIC_BACK, col);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TMultiline_control
|
|
///////////////////////////////////////////////////////////
|
|
TMultiline_control::TMultiline_control(WINDOW win, short cid,
|
|
short left, short top,
|
|
short width, short height, short maxlen,
|
|
const char* flags, const char* text)
|
|
{
|
|
create(win, cid, left, top, width, height, maxlen, flags, text, FALSE);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TButton_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
void TButton_control::create(WINDOW win, short cid,
|
|
short left, short top, short width, short height,
|
|
const char* flags, const char* text,
|
|
WIN_TYPE wc, XI_OBJ* container)
|
|
{
|
|
bool bold;
|
|
COLOR color;
|
|
TString txt(text); txt.trim();
|
|
|
|
char mnemonic = '\0';
|
|
int underscore = -1;
|
|
if (wc == WC_PUSHBUTTON)
|
|
{
|
|
underscore = txt.find('~');
|
|
if (underscore < 0)
|
|
underscore = txt.find('&');
|
|
if (underscore >= 0)
|
|
mnemonic = txt[underscore+1];
|
|
txt.strip("&~");
|
|
}
|
|
const char* t = parse_caption(txt, bold, color);
|
|
if (width <= 0) width = strlen(t)+3;
|
|
|
|
RCT rct; coord2rct(win, left, top, width, height, rct);
|
|
if (wc == WC_PUSHBUTTON && height == 1) // Troppo bassi!
|
|
{
|
|
rct.top--;
|
|
// rct.bottom++;
|
|
}
|
|
|
|
const unsigned long attrib = flags2attr(flags);
|
|
XI_OBJ_DEF* def = xi_add_button_def(NULL, cid, (XinRect *) &rct, attrib, (char*)t, cid);
|
|
CHECKD(def, "Can't create the interface of TButton_control ", cid);
|
|
def->v.btn->fore_color = color;
|
|
def->app_data = (long)this;
|
|
switch (wc)
|
|
{
|
|
case WC_PUSHBUTTON : def->v.btn->type = XIBT_BUTTON; break;
|
|
case WC_CHECKBOX : def->v.btn->type = XIBT_CHECKBOX; break;
|
|
case WC_RADIOBUTTON: def->v.btn->type = XIBT_RADIOBTN; break;
|
|
default : def->v.btn->type = XIBT_TABBTN; break;
|
|
}
|
|
|
|
if (mnemonic)
|
|
{
|
|
int mnemonic_instance = 1;
|
|
for (int c = underscore-1; c >= 0; c--)
|
|
if (t[c] == mnemonic)
|
|
mnemonic_instance++;
|
|
def->v.btn->mnemonic = mnemonic;
|
|
def->v.btn->mnemonic_instance = mnemonic_instance;
|
|
}
|
|
|
|
if (container == NULL)
|
|
container = get_interface(win);
|
|
_obj = xi_create(container, def);
|
|
CHECKD(_obj, "Can't create TButton_control ", cid);
|
|
|
|
// Aggiusta bottoni con icona a sinistra
|
|
if (wc == WC_CHECKBOX || wc == WC_RADIOBUTTON)
|
|
{
|
|
RCT& r = (RCT &) _obj->v.btn->rct;
|
|
r.top++; r.bottom-=2;
|
|
r.right += XI_FU_MULTIPLE / 2;
|
|
}
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(def);
|
|
|
|
update_tab_cid();
|
|
}
|
|
|
|
int TButton_control::button_type() const
|
|
{
|
|
return _obj->v.btn->type;
|
|
}
|
|
|
|
XI_OBJ* TButton_control::container() const
|
|
{
|
|
return _obj->parent;
|
|
}
|
|
|
|
void TButton_control::check(bool on)
|
|
{
|
|
xi_check(_obj, on);
|
|
}
|
|
|
|
bool TButton_control::checked() const
|
|
{
|
|
return xi_is_checked(_obj) ? TRUE : FALSE;
|
|
}
|
|
|
|
bool TButton_control::toggle()
|
|
{
|
|
const bool on = !checked();
|
|
check(on);
|
|
return on;
|
|
}
|
|
|
|
bool TButton_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
bool ok = TRUE;
|
|
|
|
if (xiev->type == XIE_BUTTON)
|
|
{
|
|
if (_last_mouse_button == 0)
|
|
{
|
|
if (id() != DLG_CANCEL && id() != DLG_QUIT)
|
|
ok = xi_move_focus(_obj) ? TRUE : FALSE;
|
|
|
|
if (ok)
|
|
{
|
|
switch (_obj->v.btn->type)
|
|
{
|
|
case XIBT_CHECKBOX:
|
|
case XIBT_BUTTON_CHECKBOX:
|
|
toggle();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ok = notify_key(K_SPACE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (xiev->type == XIE_POST_NAVIGATION)
|
|
ok = notify_key(K_CTRL + K_TAB);
|
|
else
|
|
ok = TControl::event_handler(itf, xiev);
|
|
|
|
return ok;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TPushbutton_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
TPushbutton_control::TPushbutton_control(WINDOW win, short cid,
|
|
short left, short top, short width, short height,
|
|
const char* flags, const char* text,
|
|
short bmp_up, short bmp_dn)
|
|
: _bmp_up(bmp_up), _bmp_dn(bmp_dn)
|
|
|
|
{
|
|
create(win, cid, left, top, width, height, flags, text, WC_PUSHBUTTON, NULL);
|
|
set_bmp(bmp_up, bmp_dn);
|
|
}
|
|
|
|
TPushbutton_control::~TPushbutton_control()
|
|
{
|
|
}
|
|
|
|
char TPushbutton_control::mnemonic() const
|
|
{
|
|
return _obj->v.btn->mnemonic;
|
|
}
|
|
|
|
void TPushbutton_control::set_caption(const char* c)
|
|
{
|
|
bool bold;
|
|
COLOR color;
|
|
const char* cap = parse_caption(c, bold, color);
|
|
if (*cap)
|
|
{
|
|
TControl::set_caption(cap);
|
|
set_central_icon(0);
|
|
set_bmp(0, 0);
|
|
_obj->v.btn->drawable = FALSE;
|
|
_obj->v.btn->fore_color = color;
|
|
}
|
|
}
|
|
|
|
void TPushbutton_control::set_central_icon(unsigned int hicon)
|
|
{
|
|
xi_set_icon(_obj, hicon, hicon);
|
|
_obj->v.btn->icon_x = (_obj->v.btn->rct.right - _obj->v.btn->rct.left - 32) / 2 - 5;
|
|
_obj->v.btn->icon_y = (_obj->v.btn->rct.bottom - _obj->v.btn->rct.top - 32) / 2 - 5;
|
|
_obj->v.btn->drawable = FALSE;
|
|
}
|
|
|
|
void TPushbutton_control::set_bmp(short bmp_up, short bmp_dn)
|
|
{
|
|
if (bmp_up > 0)
|
|
{
|
|
_bmp_up = (bmp_up > 0 && _picture->add(bmp_up)) ? bmp_up : 0;
|
|
_bmp_dn = (bmp_dn > 0 && _picture->add(bmp_dn)) ? bmp_dn : _bmp_up;
|
|
set_central_icon(0);
|
|
}
|
|
else
|
|
_bmp_up = _bmp_dn = 0;
|
|
_obj->v.btn->drawable = _bmp_up > 0;
|
|
}
|
|
|
|
void TPushbutton_control::update()
|
|
{
|
|
const long attrib = xi_get_attrib(_obj);
|
|
|
|
if (attrib & XI_ATR_VISIBLE)
|
|
{
|
|
RCT rct = (RCT&) _obj->v.btn->rct;
|
|
rct.left += 5; rct.top += 5; rct.right -= 5; rct.bottom -= 5;
|
|
#ifdef XI_R4
|
|
XinWindow xiwin = xi_get_window(_obj->itf);
|
|
xi_set_clip(xiwin, (XinRect *) &rct );
|
|
#endif
|
|
|
|
// Cancello il testo sottostante
|
|
const WINDOW win = parent();
|
|
CBRUSH brush = { PAT_SOLID, BTN_BACK_COLOR };
|
|
xvt_dwin_set_cbrush(win, &brush);
|
|
xvt_dwin_set_std_cpen(win, TL_PEN_HOLLOW);
|
|
xvt_dwin_draw_rect(win, &rct);
|
|
|
|
const short bmp = (_bmp_dn > 0 && _obj->v.btn->down) ? _bmp_dn : _bmp_up;
|
|
if (bmp > 0)
|
|
{
|
|
const TImage& i = _picture->image(bmp);
|
|
if (!SMALL_ICONS || i.width() > i.height()*2)
|
|
{
|
|
int x = (rct.right + rct.left - i.width()) / 2;
|
|
int y = (rct.bottom + rct.top - i.height()) / 2;
|
|
if (_obj->v.btn->down)
|
|
{
|
|
x += 2;
|
|
y += 2;
|
|
}
|
|
|
|
i.draw(win, x, y);
|
|
|
|
if (!(attrib & XI_ATR_ENABLED))
|
|
{
|
|
CPEN pen;
|
|
pen.width = 1;
|
|
pen.pat = PAT_SOLID;
|
|
pen.style = P_SOLID;
|
|
pen.color = BTN_BACK_COLOR;
|
|
xvt_dwin_set_cpen(win, &pen);
|
|
for (int py = y + i.height(); py >= y; py -= 2)
|
|
{
|
|
PNT p; p.v = py; p.h = x;
|
|
xvt_dwin_draw_set_pos(win, p);
|
|
p.h += i.width();
|
|
xvt_dwin_draw_line(win, p);
|
|
}
|
|
for (int px = x + i.width(); px >= x; px -= 2)
|
|
{
|
|
PNT p; p.v = y; p.h = px;
|
|
xvt_dwin_draw_set_pos(win, p);
|
|
p.v += i.height();
|
|
xvt_dwin_draw_line(win, p);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const char* cap = caption();
|
|
int width = i.width();
|
|
if (*cap)
|
|
width += xvt_dwin_get_text_width(win, cap, -1) + 1;
|
|
|
|
int x = (rct.right + rct.left - width) / 2 + _obj->v.btn->down;
|
|
int y = (rct.bottom + rct.top - i.height()) / 2 + _obj->v.btn->down;
|
|
i.draw(win, x, y);
|
|
|
|
if (*cap)
|
|
{
|
|
const int height = xvt_dwin_get_font_size_mapped(win);
|
|
x += i.width()+1;
|
|
y = (rct.top + rct.bottom + height) / 2 + _obj->v.btn->down;
|
|
xvt_dwin_set_fore_color(win, (attrib & XI_ATR_ENABLED) ? NORMAL_COLOR : DISABLED_COLOR);
|
|
xvt_dwin_draw_text(win, x, y, cap, -1);
|
|
}
|
|
}
|
|
}
|
|
#ifdef XI_R4
|
|
xi_set_clip(xiwin, NULL);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool TPushbutton_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
bool ok = TButton_control::event_handler(itf, xiev);
|
|
|
|
switch (xiev->type)
|
|
{
|
|
case XIE_BUTTON:
|
|
{
|
|
TWindow* w = (TWindow*)xi_get_app_data(get_interface());
|
|
w->on_button(id());
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TCheckbox_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
TCheckbox_control::TCheckbox_control(WINDOW win, short cid,
|
|
short left, short top, short width,
|
|
const char* flags, const char* text)
|
|
{ create(win, cid, left, top, width, 1, flags, text, WC_CHECKBOX, NULL); }
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TRadiobutton_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
TRadiobutton_control::TRadiobutton_control(WINDOW win, short cid,
|
|
short left, short top, short width, short height,
|
|
const char* flags, const char* text)
|
|
{
|
|
TToken_string testo(text); testo.strip("&~");
|
|
const int tot = testo.items();
|
|
|
|
const XI_CONTAINER_ORIENTATION orient = height == 1 ? XI_STACK_HORIZONTAL : XI_STACK_VERTICAL;
|
|
RCT rct; coord2rct(win, left, top, width, height, rct);
|
|
|
|
if (height > 1)
|
|
{
|
|
// Aggiusta rettangolo in modo da centrare i bottoni
|
|
const int extra = rct.bottom - rct.top - tot * XI_FU_MULTIPLE;
|
|
if (extra > 0)
|
|
rct.top += extra / 2;
|
|
else
|
|
if (extra < 0)
|
|
rct.bottom -= extra;
|
|
rct.left -= XI_FU_MULTIPLE / 4;
|
|
rct.right += XI_FU_MULTIPLE / 4;
|
|
}
|
|
XI_OBJ_DEF* def = xi_add_container_def(NULL, cid, (XinRect *) &rct, orient, cid);
|
|
def->app_data = (long)this;
|
|
|
|
const unsigned long attrib = flags2attr(flags);
|
|
for (int b = 1; b <= tot; b++)
|
|
{
|
|
bool bold;
|
|
COLOR color;
|
|
const char* t = parse_caption(testo.get(), bold, color);
|
|
|
|
const int next = cid + (b+1)*1000;
|
|
XI_OBJ_DEF* btn_def = xi_add_button_def(def, cid + b*1000, NULL, attrib, (char*)t, next);
|
|
CHECKD(btn_def, "Can't create definition for radio-button ", cid);
|
|
btn_def->app_data = (long)this;
|
|
|
|
XI_BTN_DEF* btn = btn_def->v.btn;
|
|
btn->type = XIBT_RADIOBTN;
|
|
btn->fore_color = color;
|
|
btn->checked = b == 1;
|
|
}
|
|
|
|
XI_OBJ* itf = get_interface(win);
|
|
_obj = xi_create(itf, def);
|
|
CHECKD(_obj, "Can't create radio-button container ", cid);
|
|
|
|
update_tab_cid();
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(def);
|
|
}
|
|
|
|
void TRadiobutton_control::show(bool on)
|
|
{
|
|
int children;
|
|
XI_OBJ** child = xi_get_member_list(_obj, &children);
|
|
for (int c = children-1; c >= 0; c--)
|
|
change_attrib(XI_ATR_VISIBLE, on, child[c]);
|
|
}
|
|
|
|
void TRadiobutton_control::enable(bool on)
|
|
{
|
|
int children;
|
|
XI_OBJ** child = xi_get_member_list(_obj, &children);
|
|
for (int c = children-1; c >= 0; c--)
|
|
change_attrib(XI_ATR_ENABLED, on, child[c]);
|
|
}
|
|
|
|
byte TRadiobutton_control::get_checked() const
|
|
{
|
|
int children;
|
|
XI_OBJ** child = xi_get_member_list(_obj, &children);
|
|
int c;
|
|
|
|
for (c = children-1; c > 0; c--)
|
|
if (xi_is_checked(child[c])) break;
|
|
return (byte)c;
|
|
}
|
|
|
|
void TRadiobutton_control::check_button(byte c)
|
|
{
|
|
int children;
|
|
XI_OBJ** child = xi_get_member_list(_obj, &children);
|
|
CHECKD(c < children, "This radio is rather old, it doesn't have button ", c);
|
|
if (!xi_is_checked(child[c]))
|
|
xi_check(child[c], TRUE);
|
|
}
|
|
|
|
void TRadiobutton_control::show_button(byte c, bool on)
|
|
{
|
|
int children;
|
|
XI_OBJ** child = xi_get_member_list(_obj, &children);
|
|
CHECKD(c < children, "This radio is rather old, it doesn't have button ", c);
|
|
change_attrib(XI_ATR_VISIBLE, on, child[c]);
|
|
}
|
|
|
|
bool TRadiobutton_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
bool ok = TRUE;
|
|
if (xiev->type == XIE_BUTTON)
|
|
{
|
|
XI_OBJ* obj = xiev->v.xi_obj; // Elemento del gruppo di radio buttons da premere
|
|
if (xi_move_focus(obj)) // Tenta di dargli il focus
|
|
{
|
|
if (!xi_is_checked(obj)) // Se non e' gia' premuto ...
|
|
{
|
|
xi_check(obj, TRUE); // ... allora premilo e ...
|
|
ok = notify_key(K_SPACE); // ... avverti il mask_field proprietario
|
|
}
|
|
}
|
|
else
|
|
ok = FALSE;
|
|
}
|
|
else
|
|
ok = TButton_control::event_handler(itf, xiev);
|
|
return ok;
|
|
}
|
|
|
|
void TRadiobutton_control::set_focus() const
|
|
{
|
|
int children;
|
|
XI_OBJ** child = xi_get_member_list(_obj, &children);
|
|
int c;
|
|
|
|
for (c = children-1; c > 0; c--)
|
|
if (xi_is_checked(child[c])) break;
|
|
xi_set_focus(child[c]);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TTagbutton_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
TTagbutton_control::TTagbutton_control(WINDOW win, short cid,
|
|
short left, short top, short width, short height,
|
|
const char* flags, const char* text, int tag)
|
|
{
|
|
XI_OBJ* itf = get_interface(win);
|
|
XI_RCT rct; xi_get_xi_rct(itf, &rct);
|
|
rct.bottom = rct.top + Y_FU_MULTIPLE;
|
|
XI_OBJ_DEF* cnt_def = xi_add_container_def(NULL, cid, &rct, XI_STACK_HORIZONTAL, cid);
|
|
cnt_def->app_data = (long)this;
|
|
|
|
const unsigned long attrib = flags2attr(flags);
|
|
|
|
TToken_string titolo(text);
|
|
const int tot = titolo.items();
|
|
|
|
for (int b = 1; b <= tot; b++)
|
|
{
|
|
bool bold;
|
|
COLOR color;
|
|
const char* t = parse_caption(titolo.get(), bold, color);
|
|
|
|
const int next_bid = b == tot ? cid+1 : cid+b+1;
|
|
XI_OBJ_DEF* btn_def = xi_add_button_def(cnt_def, cid+b, NULL, attrib, (char*)t, next_bid);
|
|
btn_def->v.btn->type = XIBT_TABBTN;
|
|
btn_def->v.btn->fore_color = color;
|
|
btn_def->v.btn->checked = tag == b-1;
|
|
btn_def->app_data = (long)this;
|
|
}
|
|
|
|
_obj = xi_create(itf, cnt_def);
|
|
CHECKD(_obj, "Can't create tab-button container ", cid);
|
|
|
|
update_tab_cid();
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(cnt_def);
|
|
}
|
|
|
|
bool TTagbutton_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
bool ok = TRUE;
|
|
if (xiev->type == XIE_BUTTON)
|
|
{
|
|
XI_OBJ* obj = xiev->v.xi_obj; // Elemento del gruppo di radio buttons da premere
|
|
if (!xi_is_checked(obj)) // Se e' gia' selezionato ignoralo completamente
|
|
{
|
|
if (xi_move_focus(itf)) // Controlla se si puo' cambiare pagina
|
|
{
|
|
TWindow* w = (TWindow*)xi_get_app_data(itf);
|
|
const KEY k = K_CTRL + K_F1 + xiev->v.xi_obj->cid - id() - 1;
|
|
w->on_key(k);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ok = TRadiobutton_control::event_handler(itf, xiev);
|
|
return ok;
|
|
}
|
|
|
|
void TTagbutton_control::set_caption(const char* text)
|
|
{
|
|
TToken_string cap = text;
|
|
int num;
|
|
XI_OBJ** tag = xi_get_member_list(_obj, &num);
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
text = cap.get();
|
|
if (text == NULL)
|
|
text = cap.get(0);
|
|
xi_set_text(tag[i], (char*)text);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TListbox_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
long TDropDownList::row2rec(int row) const
|
|
{
|
|
int rows;
|
|
const long* handle = xi_get_list_info(_xi_lst, &rows);
|
|
|
|
if (row < 0) row = 0;
|
|
else
|
|
if (row >= rows)
|
|
row = rows-1;
|
|
const long r = handle[row];
|
|
CHECKD(r >= 0 && r < items(), "Sheet line out of range: ", row);
|
|
|
|
return r;
|
|
}
|
|
|
|
int TDropDownList::rec2row(long rec) const
|
|
{
|
|
int rows;
|
|
const long* handle = xi_get_list_info(_xi_lst, &rows);
|
|
|
|
int r = int(rec - handle[0]);
|
|
if (r < 0 || r >= rows)
|
|
r = -1;
|
|
|
|
return r;
|
|
}
|
|
|
|
void TDropDownList::update_selection(XI_EVENT* xiev) const
|
|
{
|
|
if (xiev->v.rec_request.data_rec == selected())
|
|
xiev->v.rec_request.attrib |= XI_ATR_SELECTED;
|
|
else
|
|
xiev->v.rec_request.attrib &= ~XI_ATR_SELECTED;
|
|
}
|
|
|
|
void TDropDownList::ddl_str_eh(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
TDropDownList* ddl = (TDropDownList*)xi_get_app_data(itf);
|
|
if (!ddl->is_open())
|
|
return;
|
|
|
|
switch(xiev->type)
|
|
{
|
|
case XIE_GET_FIRST:
|
|
if (ddl->items() > 0)
|
|
{
|
|
// GUY
|
|
// xiev->v.rec_request.data_rec = ddl->items() * xiev->v.rec_request.percent/100;
|
|
// ddl->update_selection(xiev);
|
|
long n = ddl->items() * (long)xiev->v.rec_request.percent / 100L;
|
|
if (n < 0L) n = 0L;
|
|
xiev->v.rec_request.data_rec = n;
|
|
}
|
|
else
|
|
xiev->refused = TRUE;
|
|
break;
|
|
case XIE_GET_LAST:
|
|
xiev->v.rec_request.data_rec = ddl->items() - 1;
|
|
// GUY ddl->update_selection(xiev);
|
|
break;
|
|
case XIE_GET_PREV:
|
|
case XIE_GET_NEXT:
|
|
{
|
|
const long n = xiev->v.rec_request.spec_rec + (xiev->type == XIE_GET_NEXT ? +1 : -1) ;
|
|
if (n >= 0 && n < ddl->items())
|
|
xiev->v.rec_request.data_rec = n;
|
|
else
|
|
xiev->refused = TRUE;
|
|
}
|
|
break;
|
|
case XIE_CELL_REQUEST:
|
|
{
|
|
const long rec = xiev->v.cell_request.rec;
|
|
if (rec >= 0 && rec < ddl->items())
|
|
{
|
|
const char* row = ddl->item(xiev->v.cell_request.rec);
|
|
const int len = xiev->v.cell_request.len;
|
|
strncpy(xiev->v.cell_request.s, (char*)row, len);
|
|
xiev->v.cell_request.s[len - 1] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
case XIE_GET_PERCENT:
|
|
{
|
|
const long rec = xiev->v.get_percent.record;
|
|
long n = ddl->items(); if (n <= 0) n = 1;
|
|
xiev->v.get_percent.percent = int(rec * 100L / n);
|
|
}
|
|
break;
|
|
case XIE_DBL_CELL:
|
|
ddl->select((int)(ddl->row2rec(xiev->v.xi_obj->v.cell.row)));
|
|
ddl->close();
|
|
break;
|
|
case XIE_SELECT:
|
|
if (xiev->v.xi_obj->type == XIT_ROW)
|
|
{
|
|
const long rec = ddl->row2rec(xiev->v.xi_obj->v.row);
|
|
ddl->select(int(rec));
|
|
}
|
|
break;
|
|
case XIE_XVT_EVENT:
|
|
switch (xiev->v.xvte.type)
|
|
{
|
|
case E_CHAR:
|
|
{
|
|
const KEY k = xiev->v.xvte.v.chr.ch;
|
|
if (k == K_ENTER || k == K_ESC)
|
|
ddl->close();
|
|
else
|
|
{
|
|
if (k >= ' ' && k <= 'z')
|
|
ddl->select_by_initial(char(k));
|
|
}
|
|
}
|
|
break;
|
|
case E_MOUSE_DBL:
|
|
ddl->close();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TDropDownList::close()
|
|
{
|
|
if (_open)
|
|
{
|
|
_open = FALSE;
|
|
if (_xi_lst != NULL)
|
|
xvt_vobj_set_visible((WINDOW)xi_get_window(_xi_lst->itf), FALSE);
|
|
_cur_ddl = NULL;
|
|
}
|
|
}
|
|
|
|
void TDropDownList::destroy()
|
|
{
|
|
close();
|
|
if (_xi_lst != NULL)
|
|
{
|
|
xvt_vobj_destroy((WINDOW)xi_get_window(_xi_lst->itf));
|
|
_xi_lst = NULL;
|
|
}
|
|
}
|
|
|
|
bool TDropDownList::select(int i, bool force)
|
|
{
|
|
if (force || i != _selected)
|
|
{
|
|
_selected = i;
|
|
|
|
if (_obj->type == XIT_FIELD)
|
|
{
|
|
if (_open) xi_set_text(_obj, ""); // Altrimenti a volte ridisegna male!
|
|
xi_set_text(_obj, (char*)_values.get(i));
|
|
TListbox_control* listbox = (TListbox_control *)xi_get_app_data(_obj);
|
|
listbox->notify_key(K_SPACE);
|
|
}
|
|
else
|
|
xi_set_text(_obj, (char*)_codes.get(i));
|
|
|
|
if (force && _open)
|
|
xi_scroll_rec(_xi_lst, i, FOCUS_COLOR, XI_ATR_ENABLED | XI_ATR_SELECTED, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
bool TDropDownList::select_by_initial(char c)
|
|
{
|
|
const int tot = _values.items();
|
|
int i = _selected;
|
|
c = toupper(c);
|
|
while(TRUE)
|
|
{
|
|
i++;
|
|
if (i >= tot)
|
|
i = 0;
|
|
if (i == _selected)
|
|
break;
|
|
const char p = toupper(*(_values.get(i)));
|
|
if (p == c)
|
|
break;
|
|
}
|
|
|
|
bool ok = FALSE;
|
|
if (i != selected())
|
|
{
|
|
if (is_open())
|
|
{
|
|
const int oldsel = selected();
|
|
|
|
// Scrolla alla selezione corrente
|
|
xi_scroll_rec(_xi_lst, i, FOCUS_COLOR, XI_ATR_SELECTED | XI_ATR_ENABLED, 0);
|
|
|
|
const int r = rec2row(oldsel);
|
|
if (r >= 0)
|
|
{
|
|
XI_OBJ riga; XI_MAKE_ROW(&riga, _xi_lst, r);
|
|
long attr = xi_get_attrib(&riga);
|
|
attr &= ~XI_ATR_SELECTED;
|
|
xi_set_attrib(&riga, attr);
|
|
xi_dequeue();
|
|
}
|
|
|
|
}
|
|
ok = select(i);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool TDropDownList::select_by_ofs(int i)
|
|
{
|
|
i += _selected;
|
|
if (i >= 0 && i < _values.items())
|
|
return select(i);
|
|
return FALSE;
|
|
}
|
|
|
|
void TDropDownList::set_values(const char* c, const char* v)
|
|
{
|
|
const int old_items = _codes.items();
|
|
|
|
_codes = c;
|
|
_values = v;
|
|
|
|
const int new_items = _codes.items();
|
|
if (new_items != old_items && _xi_lst != NULL)
|
|
destroy();
|
|
}
|
|
|
|
int TDropDownList::calc_min_width()
|
|
{
|
|
int len = 1;
|
|
for (const char* v = _values.get(0); v; v = _values.get())
|
|
{
|
|
const int l = strlen(v);
|
|
if (l > len) len = l;
|
|
}
|
|
|
|
return (len+2) * XI_FU_MULTIPLE;
|
|
}
|
|
|
|
void TDropDownList::create()
|
|
{
|
|
if (_xi_lst != NULL)
|
|
return;
|
|
|
|
XI_OBJ_DEF* itfdef = xi_create_itf_def(ITF_CID+1, (XI_EVENT_HANDLER)ddl_str_eh, NULL, "",
|
|
(long)this);
|
|
itfdef->v.itf->automatic_back_color = TRUE;
|
|
|
|
const int hei = items() <= 1 ? 2*XI_FU_MULTIPLE + 1 : (min(8,items()) * XI_FU_MULTIPLE) + 1;
|
|
XI_OBJ_DEF* lstdef = xi_add_list_def(itfdef, _obj->cid+1000, 0, 0, hei,
|
|
XI_ATR_ENABLED|XI_ATR_VISIBLE,
|
|
FOCUS_COLOR, FOCUS_BACK_COLOR,
|
|
DISABLED_COLOR, DISABLED_BACK_COLOR,
|
|
FOCUS_COLOR, 0);
|
|
XI_LIST_DEF* ld = lstdef->v.list;
|
|
ld->active_back_color = FOCUS_BACK_COLOR;
|
|
ld->scroll_bar = items() > 8;
|
|
ld->no_heading = TRUE;
|
|
ld->no_horz_lines = TRUE;
|
|
ld->no_vert_lines = TRUE;
|
|
ld->resize_with_window = TRUE;
|
|
ld->single_select = TRUE;
|
|
|
|
// compute size in pixel of field (with button enclosed)
|
|
RCT r; xi_get_rect(_obj, (XinRect *) &r);
|
|
|
|
// Larghezza in pixel dell'edit field
|
|
int len = r.right - r.left;
|
|
if (lstdef->v.list->scroll_bar)
|
|
{
|
|
// Larghezza in pixel del bottone
|
|
const int bw = int(_obj->itf->v.itf->fu_height * XI_FU_MULTIPLE / _obj->itf->v.itf->fu_width);
|
|
len -= bw;
|
|
len -= 2; // Don't ask me why
|
|
}
|
|
else
|
|
len -= 6; // Don't ask me why
|
|
|
|
// Larghezza in form units dell'edit field
|
|
len = len * XI_FU_MULTIPLE / _obj->itf->v.itf->fu_width - 2;
|
|
|
|
if (_obj->type == XIT_CELL)
|
|
{
|
|
const int m = calc_min_width();
|
|
if (len < m)
|
|
{
|
|
len = m;
|
|
r.right = r.left + m * _obj->itf->v.itf->fu_width / XI_FU_MULTIPLE;
|
|
}
|
|
}
|
|
|
|
xi_add_column_def(lstdef, _obj->cid+2000,
|
|
XI_ATR_VISIBLE|XI_ATR_ENABLED|XI_ATR_READONLY|XI_ATR_SELECTABLE,
|
|
0, len, 80, "");
|
|
|
|
RCT l; xi_get_def_rect(lstdef, (XinRect *) &l);
|
|
PNT p; p.h = r.left;
|
|
WINDOW pwin = (WINDOW)xi_get_window(_obj->itf);
|
|
RCT w; xvt_vobj_get_client_rect(pwin, &w);
|
|
// place rectangle
|
|
if (r.bottom + l.bottom - l.top <= w.bottom)
|
|
p.v = r.bottom;
|
|
else
|
|
{
|
|
p.v = r.top - l.bottom - l.top;
|
|
if (p.v < w.top) p.v = w.top;
|
|
}
|
|
RCT wr;
|
|
wr.left = p.h; wr.top = p.v;
|
|
// xi_get_def_rect(lstdef, (XinRect *) &l);
|
|
wr.right = r.right;
|
|
wr.bottom = wr.top + l.bottom - l.top;
|
|
const int delta_x = _obj->itf->v.itf->delta_x;
|
|
const int delta_y = _obj->itf->v.itf->delta_y;
|
|
wr.left -= delta_x;
|
|
wr.right -= delta_x;
|
|
wr.top -= delta_y;
|
|
wr.bottom -= delta_y;
|
|
|
|
WINDOW win = xvt_win_create(W_PLAIN, &wr, "", 0, pwin, WSF_NO_MENUBAR, EM_ALL,
|
|
(EVENT_HANDLER)xi_event, 0L);
|
|
itfdef->v.itf->win = (XinWindow)win;
|
|
itfdef->v.itf->rctp = (XinRect *) ≀
|
|
XI_OBJ* itfobj = xi_create(NULL, itfdef);
|
|
CHECK(itfobj, "Can't create listbox");
|
|
|
|
xi_dequeue();
|
|
xi_tree_free(itfdef);
|
|
|
|
_xi_lst = xi_get_obj(itfobj, _obj->cid+1000);
|
|
CHECK(_xi_lst, "Can't create listbox");
|
|
}
|
|
|
|
void TDropDownList::open()
|
|
{
|
|
if (_open)
|
|
return;
|
|
|
|
if (_xi_lst != NULL)
|
|
xvt_vobj_set_visible((WINDOW)xi_get_window(_xi_lst->itf), TRUE);
|
|
else
|
|
create();
|
|
|
|
_open = TRUE;
|
|
xi_cell_request(_xi_lst);
|
|
|
|
WINDOW win = (WINDOW)xi_get_window(_xi_lst->itf);
|
|
xvt_scr_set_focus_vobj(win);
|
|
xvt_vobj_raise(win);
|
|
|
|
select(_selected, TRUE);
|
|
|
|
xi_dequeue();
|
|
_cur_ddl = this;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
bool TListbox_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
|
|
{
|
|
bool ok = TRUE;
|
|
switch (xiev->type)
|
|
{
|
|
case XIE_DBL_FIELD:
|
|
case XIE_BUTTON:
|
|
if (!_button_blocked)
|
|
{
|
|
if (_ddl->is_open())
|
|
_ddl->close();
|
|
else
|
|
_ddl->open();
|
|
}
|
|
break;
|
|
case XIE_CHAR_FIELD:
|
|
{
|
|
const KEY k = xiev_to_key(xiev);
|
|
if (k >= ' ' && k <= 'z')
|
|
_ddl->select_by_initial(char(k));
|
|
else if (k == K_F9)
|
|
_ddl->open();
|
|
else if (k == K_RIGHT) // poor man's substitute for down arrow
|
|
_ddl->select_by_ofs(1);
|
|
else if (k == K_LEFT) // poor man's substitute for up arrow
|
|
_ddl->select_by_ofs(-1);
|
|
else if (k == K_F2 || k == K_F11 || k == K_F12)
|
|
notify_key(k);
|
|
xiev->refused = TRUE;
|
|
}
|
|
break;
|
|
case XIE_CHG_FIELD:
|
|
// Ignore changes: they are notified by the drop-down list
|
|
break;
|
|
default:
|
|
ok = TField_control::event_handler(itf, xiev);
|
|
break;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
void TDropDownList::on_mouse_down(const PNT& pt)
|
|
{
|
|
if (_open)
|
|
{
|
|
RCT rct;
|
|
|
|
xi_get_rect(_obj, (XinRect*)&rct);
|
|
if (xvt_rect_has_point(&rct, pt))
|
|
return; // E' nel campo di testo proprietario della lista
|
|
|
|
xvt_vobj_get_outer_rect((WINDOW)xi_get_window(_xi_lst->itf), &rct);
|
|
if (!xvt_rect_has_point(&rct, pt))
|
|
close();
|
|
}
|
|
}
|
|
|
|
TDropDownList::TDropDownList(XI_OBJ* o, const char* codes, const char* values)
|
|
: _obj(o), _xi_lst(NULL), _codes(codes),
|
|
_values(values), _selected(0), _open(FALSE)
|
|
{
|
|
if (o->type == XIT_CELL)
|
|
{
|
|
const char* val = xi_get_text(o, NULL, -1);
|
|
_selected = _codes.get_pos(val);
|
|
if (_selected < 0)
|
|
_selected = 0;
|
|
}
|
|
}
|
|
|
|
TDropDownList::~TDropDownList()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TListbox_control
|
|
///////////////////////////////////////////////////////////
|
|
|
|
void TListbox_control::set_values(const char* cod, const char* val)
|
|
{
|
|
_ddl->set_values(cod, val);
|
|
if (selected() >= items())
|
|
_ddl->select(0, TRUE);
|
|
else
|
|
_ddl->select(selected(), TRUE);
|
|
}
|
|
|
|
int TListbox_control::items() const
|
|
{
|
|
return _ddl->items();
|
|
}
|
|
|
|
bool TListbox_control::select(int i)
|
|
{
|
|
return _ddl->select(i);
|
|
}
|
|
|
|
bool TListbox_control::select_by_initial(char c)
|
|
{
|
|
return _ddl->select_by_initial(c);
|
|
}
|
|
|
|
bool TListbox_control::select_by_ofs(int i)
|
|
{
|
|
return _ddl->select_by_ofs(i);
|
|
}
|
|
|
|
int TListbox_control::selected() const
|
|
{
|
|
return _ddl->selected();
|
|
}
|
|
|
|
TListbox_control::TListbox_control(WINDOW win, short cid,
|
|
short left, short top, short width,
|
|
const char* flags, const char* text,
|
|
const char* codes, const char* values)
|
|
{
|
|
create(win, cid, left, top, width , 1, width, flags, text, TRUE);
|
|
_ddl = new TDropDownList(_obj, codes, values);
|
|
_ddl->select(0, TRUE);
|
|
}
|
|
|
|
TListbox_control::~TListbox_control()
|
|
{
|
|
delete _ddl;
|
|
}
|