campo-sirio/include/controls.cpp
guy 6bac6177c4 Patch level : 2.0 nopatch
Files correlati     :
Ricompilazione Demo : [ ]
Commento            :

Aggiunte utili per gestione dizionario
assoc.cpp    Aggiunta possibilita' di selezionare un elemento causale di un assoc
controls.cpp Corretta gestione scrollbar dei listbox
diction.cpp  Migliorata selezione caratteri da trimmare dalle traduzioni
isam.cpp     Corretto azzeramento dei memo (non azzerava il TString_array corrispondente)
msksheet.cpp Resa personalizzabile la larghezza della colonna col numero di riga
netsock.cpp  Migliorata gestione "a capo" in protocollo soap
progind.cpp  Corretto posizionamento progind sovrapposte
relapp.cpp   Cambiato un messaggio di richiesta annullamento


git-svn-id: svn://10.65.10.50/trunk@11651 c028cbd2-c16b-5b4b-a496-9718f37d4682
2003-12-03 09:41:16 +00:00

2348 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())
font_ser_desc = "01\\Courier\\0\\10\\WIN01/-13/0/0/0/400/0/0/0/0/1/2/1/49/Courier";
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_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, NULL, 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;
for (const char* 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);
for (int 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);
for (int 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;
const char* row = NULL;
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:
if (xiev->v.xvte.type == 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;
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_OBJ_DEF* coldef = 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 *) &wr;
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 (!_ddl->is_open() && !_button_blocked)
_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;
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), _codes(codes), _values(values),
_open(FALSE), _xi_lst(NULL), _selected(0)
{
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;
}