campo-sirio/include/controls.cpp

1794 lines
47 KiB
C++
Raw Normal View History

#if XVT_OS == XVT_OS_WIN
#define STRICT
#include <windows.h>
#endif
#include <colors.h>
#include <config.h>
#include <controls.h>
#include <mask.h>
#include <urldefid.h>
#include <window.h>
#ifndef STX_DATA
// Very deep hacking
typedef struct _stx_data
{
int cid;
WINDOW win;
RCT rct;
unsigned long attrib;
} STX_DATA;
#endif
short get_focus_id(WINDOW win)
{
XI_OBJ * itf = xi_get_itf(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->v.btn->type == XIBT_RADIOBTN))
obj = obj->parent;
return obj->cid;
}
// #define CAMPI_SCAVATI FALSE
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)
{
KEY key = xiev->v.chr.ch;
if (key < K_INS || key > K_HELP)
{
if (xiev->v.chr.shift && (key < ' ' || key >= K_UP)) key += K_SHIFT;
if (xiev->v.chr.control && key >= ' ') key += K_CTRL;
}
return key;
}
// @doc INTERNAL
///////////////////////////////////////////////////////////
// TPicture_array
///////////////////////////////////////////////////////////
class TPicture_array : public TArray
{
public:
TImage& add(short id, bool convert = FALSE);
const TImage& image(short id) const { return (const TImage&)operator[](id); }
bool exist(short id) const { return objptr(id) != NULL; }
TPicture_array() : TArray(128) {}
virtual ~TPicture_array() {}
};
TImage& TPicture_array::add(short id, bool convert)
{
TImage* i = (TImage*)objptr(id);
if (i == NULL)
{
i = new TImage(id);
TArray::add(i, id);
if (convert)
i->convert_transparent_color();
}
return *i;
}
///////////////////////////////////////////////////////////
// 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_USER, "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");
// Get true text size
#if XVT_OS == XVT_OS_WIN
TEXTMETRIC tm;
HDC hdc = (HDC)xvt_vobj_get_attr(TASK_WIN, ATTR_NATIVE_GRAPHIC_CONTEXT);
GetTextMetrics(hdc, &tm);
const int COLX = GetSystemMetrics(SM_CXSCREEN) / 80;
CHARX = tm.tmAveCharWidth+1;
if (CHARX > COLX) CHARX = COLX;
CHARY = tm.tmHeight;
BASEY = tm.tmAscent;
if (CHARY > ROWY-2) CHARY = ROWY-2;
#endif
}
return bold ? FAT_FONT : DEF_FONT;
}
static byte event_map[XIE_POST_NAVIGATION+1];
enum event_action { a_ignore, a_xvt, a_xvt_post, a_obj, a_child, a_update, a_select, a_post, a_debug };
void init_controls()
{
xi_set_font_id(xvt_default_font());
xi_set_pref(XI_PREF_NATIVE_CTRLS, FALSE);
xi_set_pref(XI_PREF_3D_LOOK, TRUE);
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);
// xi_set_pref(XI_PREF_AUTOSEL_ON_MOUSE, TRUE); // Da' problemi nelle ricerche
xi_set_pref(XI_PREF_CARET_WIDTH, 2);
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_ignore;
event_map[XIE_COL_SIZE] = a_ignore;
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);
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; xvt_vobj_get_client_rect(parent, &max);
if (parent == TASK_WIN)
max.bottom -= 26; // Non contare la status bar
if (dy <= 0)
height = max.bottom - top + dy * ROWY;
if (dx <= 0)
{
width = max.right - left + dx * CHARX;
if (!tag && dx == 0 && y > 0) // Aggiusta toolbar
top++;
}
if (x < 0) left = (max.right - width) >> 1;
if (y < 0) top = (max.bottom - height) >> 1;
}
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");
XI_OBJ_DEF* def = xi_create_itf_def(ITF_CID, xi_event_handler, &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->tab_on_enter = TRUE;
def->v.itf->win = win;
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)
{
RCT max; xvt_vobj_get_client_rect(TASK_WIN, &max);
xi_pu_to_fu(itf, (PNT*)&max, 2);
X_FU_MULTIPLE = max.right / 80;
Y_FU_MULTIPLE = max.bottom / 25;
PNT f1 = { XI_FU_MULTIPLE, XI_FU_MULTIPLE };
xi_fu_to_pu(itf, &f1, 1);
const int spacing = ROWY - f1.v;
if (spacing > 0)
xi_set_pref(XI_PREF_VERT_PIXEL_SPACING, spacing);
}
if (dx > 0)
{
xi_pu_to_fu(itf, (PNT*)&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, (PNT*)&r, 2);
xvt_vobj_move(win, &r);
}
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, DLG_PAGETAGS, &rct, XI_ATR_VISIBLE, 0, 0);
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 = 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_client_rect(win, &rc);
WINDOW wp = xvt_vobj_get_parent(win);
xvt_vobj_translate_points(win, wp, (PNT*)&rc, 2);
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, &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 = 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:
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, &b->v.btn->rct, &xiev->v.xvte.v.update.rct))
{
TPushbutton_control* p = (TPushbutton_control*)b->app_data;
p->update();
}
}
}
}
break;
case a_select:
if (xiev->v.xi_obj->type == XIT_LIST)
ctl = (TControl*)xi_get_app_data(xiev->v.xi_obj);
else
ctl = (TControl*)xi_get_app_data(xiev->v.xi_obj->parent);
break;
case a_xvt:
if (xiev->v.xvte.type == 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;
case a_xvt_post:
if (notify_xvt)
{
TWindow* w = (TWindow*)xi_get_app_data(itf);
CHECK(w, "Can't send XVT event to a null window");
w->handler(w->win(), &xiev->v.xvte);
}
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
///////////////////////////////////////////////////////////
// @doc INTERNAL
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(win);
CHECK(itf, "Can't get the interface from a window");
}
return itf;
}
void TControl::set_tab_cid(short cid)
{
CHECK(_obj, "Can't update tab_cid of a NULL control");
switch(_obj->type)
{
case XIT_BTN:
_obj->v.btn->tab_cid = cid; break;
case XIT_CONTAINER:
_obj->v.container->tab_cid = _obj->cid;
if (_obj->nbr_children > 0)
_obj->children[_obj->nbr_children-1]->v.btn->tab_cid = cid;
break;
case XIT_FIELD:
_obj->v.field->tab_cid = cid;
_obj->parent->v.form->tab_cid = _obj->parent->cid;
break;
case XIT_LIST:
_obj->v.list->tab_cid = cid; break;
default:
NFCHECK(0, "Can't set tab_cid to a static control: ", _obj->cid); break;
}
}
TControl& TControl::find_operable_ctl(bool forward) const
{
int num;
XI_OBJ** child = xi_get_member_list(get_interface(), &num);
const int first = forward ? 0 : num-2;
const int last = forward ? num-1 : -1;
const int delta = forward ? +1 : -1;
XI_OBJ* obj = NULL;
for (int c = first; c != last && obj == NULL; c += delta)
{
const XI_OBJ_TYPE t = child[c]->type;
if (t == XIT_FORM || t == XIT_BTN || t == XIT_LIST || t == XIT_CONTAINER)
obj = child[c];
}
TControl* ctl = obj == NULL ? (TControl*)this : (TControl*)xi_get_app_data(obj);
CHECK(ctl, "Can't update NULL control");
return *ctl;
}
void TControl::update_tab_cid()
{
TControl& lc = find_operable_ctl(FALSE);
lc.set_tab_cid(id());
const TControl& fc = find_operable_ctl(TRUE);
set_tab_cid(fc.id());
}
void TControl::coord2rct(WINDOW win, short x, short y, short dx, short dy, XI_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);
// 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 == DLG_PAGETAGS)
y++;
}
RCT max; xvt_vobj_get_client_rect(win, &max);
xi_pu_to_fu(itf, (PNT*)&max, 2);
const int& MAXX = max.right;
const int& MAXY = max.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;
}
RCT& TControl::get_rect(RCT& r) const
{
xi_get_rect(_obj, &r);
return r;
}
void TControl::set_rect(const RCT&)
{
CHECK(0, "Can't set_rect to generic TControl");
}
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(0, "Bad character after @ in prompt");
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);
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 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
{
// 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;
}
}
// 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);
}
// @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();
XI_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, &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;
}
///////////////////////////////////////////////////////////
// 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 = _obj->v.text->rct;
tr.top--; tr.bottom--;
XI_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, &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 = _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 + 1000;
XI_OBJ_DEF* frm_def = xi_add_form_def(NULL, fcid, fcid);
frm_def->app_data = (long)this;
XI_RCT rct; coord2rct(win, left, top, width, height, rct);
rct.right += XI_FU_MULTIPLE/4;
unsigned long attrib = flags2attr(flags) | 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->auto_tab = TRUE;
f->active_back_color = FOCUS_BACK_COLOR;
if (button)
{
f->button = TRUE;
f->pixel_button_distance = 1;
}
if (height > 1) // E' un multiline, quindi setto il rettangolo
f->xi_rct = rct;
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;
const int offset = _obj->v.field->btn_rct.left - stx->rct.right + 1;
_obj->v.field->btn_rct.left -= offset ;
_obj->v.field->btn_rct.right -= offset;
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;
if (f->button != on)
{
f->button = on;
xi_invalidate_rect(parent(), &f->btn_rct);
}
}
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_F1 && k <= K_CTRL+K_F12))
k = K_TAB;
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();
}
///////////////////////////////////////////////////////////
// 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.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);
const unsigned long attrib = flags2attr(flags);
XI_OBJ_DEF* def = xi_add_button_def(NULL, cid, &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 (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 = _obj->v.btn->rct;
r.top++; r.bottom++;
r.right += XI_FU_MULTIPLE / 2;
}
xi_dequeue();
xi_tree_free(def);
update_tab_cid();
}
XI_BTN_TYPE 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;
}
void TButton_control::set_rect(const RCT& r)
{
_obj->v.btn->rct = r;
}
bool TButton_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
{
bool ok = TRUE;
if (xiev->type == XIE_BUTTON)
{
if (xi_move_focus(_obj))
{
switch (_obj->v.btn->type)
{
case XIBT_CHECKBOX:
case XIBT_BUTTON_CHECKBOX:
toggle();
break;
default:
break;
}
ok = notify_key(K_SPACE);
}
else
ok = FALSE;
}
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)
{
if (bmp_up > 0)
text = "";
create(win, cid, left, top, width, height, flags, text, WC_PUSHBUTTON, NULL);
set_bmp(bmp_up, bmp_dn);
}
void TPushbutton_control::set_bmp(short bmp_up, short bmp_dn)
{
if (bmp_up > 0)
{
_obj->v.btn->drawable = TRUE;
_picture->add(_bmp_up = bmp_up, TRUE);
if (bmp_dn > 0)
_picture->add(_bmp_dn = bmp_dn, TRUE);
else
_bmp_dn = _bmp_up;
if (_obj->v.btn->text)
*_obj->v.btn->text = '\0';
}
else
{
_obj->v.btn->drawable = FALSE;
_bmp_up = _bmp_dn = 0;
}
}
void TPushbutton_control::update()
{
const long attrib = xi_get_attrib(_obj);
if (attrib & XI_ATR_VISIBLE)
{
const short bmp = (_bmp_dn > 0 && _obj->v.btn->down) ? _bmp_dn : _bmp_up;
if (bmp > 0)
{
const TImage& i = _picture->image(bmp);
const RCT& rct = _obj->v.btn->rct;
int x = rct.left + (rct.right - rct.left - i.width()) / 2;
int y = rct.top + (rct.bottom - rct.top - i.height()) / 2;
if (_obj->v.btn->down)
{
x += 2;
y += 2;
}
const WINDOW w = parent();
i.draw(w, x, y);
if (!(attrib & XI_ATR_ENABLED))
{
CPEN pen;
pen.width = 1;
pen.pat = PAT_SOLID;
pen.style = P_SOLID;
pen.color = MASK_BACK_COLOR;
xvt_dwin_set_cpen(w, &pen);
for (int py = y + i.height(); py >= y; py -= 2)
{
PNT p; p.v = py; p.h = x;
xvt_dwin_draw_set_pos(w, p);
p.h += i.width();
xvt_dwin_draw_line(w, p);
}
for (int px = x + i.width(); px >= x; px -= 2)
{
PNT p; p.v = y; p.h = px;
xvt_dwin_draw_set_pos(w, p);
p.v += i.height();
xvt_dwin_draw_line(w, p);
}
}
}
}
}
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;
XI_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;
}
XI_OBJ_DEF* def = xi_add_container_def(NULL, cid, &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);
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);
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::ddl_str_eh(XI_OBJ* itf, XI_EVENT* xiev)
{
TDropDownList* ddl = (TDropDownList*)xi_get_app_data(itf);
const char* row = NULL;
long rec = 0l;
if (!ddl->is_open()) return;
switch(xiev->type)
{
case XIE_CELL_REQUEST:
rec = xiev->v.cell_request.rec;
row = ddl->item((int)rec);
strncpy(xiev->v.cell_request.s, (char *)row, xiev->v.cell_request.len);
xiev->v.cell_request.s[xiev->v.cell_request.len - 1] = '\0';
break;
case XIE_GET_FIRST:
if (ddl->items() == 0)
xiev->refused = TRUE;
else
xiev->v.rec_request.data_rec = ddl->items() * xiev->v.rec_request.percent/100;
break;
case XIE_GET_LAST:
xiev->v.rec_request.data_rec = ddl->items() - 1;
break;
case XIE_GET_NEXT:
if (xiev->v.rec_request.spec_rec >= ddl->items()-1)
xiev->refused = TRUE;
else
xiev->v.rec_request.data_rec = xiev->v.rec_request.spec_rec + 1;
break;
case XIE_GET_PREV:
if (xiev->v.rec_request.spec_rec == 0)
xiev->refused = TRUE;
else
xiev->v.rec_request.data_rec = xiev->v.rec_request.spec_rec - 1;
break;
case XIE_GET_PERCENT:
if (ddl->items() > 1)
xiev->v.get_percent.percent = (int)((xiev->v.get_percent.record * 100l)/((long)ddl->items()-1l));
break;
case XIE_DBL_CELL:
ddl->select((int)(ddl->row2rec(xiev->v.xi_obj->v.cell.row)));
ddl->close();
break;
case XIE_ON_ROW:
if (ddl->displayed())
ddl->select((int)(ddl->row2rec(xiev->v.xi_obj->v.row)));
break;
case XIE_XVT_EVENT:
if ((xiev->v.xvte.type == E_FOCUS && xiev->v.xvte.v.active == FALSE) || xiev->v.xvte.type == E_MOUSE_UP)
ddl->close();
else 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(k);
}
break;
case XIE_CHAR_CELL: // TBI: questa e' copiata da edit
break;
default:
break;
}
}
void TDropDownList::close()
{
if (_open)
{
_open = _displayed = FALSE;
xvt_vobj_destroy(xi_get_window(_xi_lst->itf));
xvt_dwin_update (xi_get_window(_obj->itf));
}
}
bool TDropDownList::select(int i, bool force)
{
if (force || i != _selected)
{
_selected = i;
if (_obj->type == XIT_FIELD)
{
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 (is_open())
{
int r = rec2row(i);
if (r < 0)
{
xi_scroll_rec(_xi_lst, i, FOCUS_COLOR, XI_ATR_ENABLED, 0);
r = rec2row(i);
}
CHECKD(r >= 0, "Can't select listbox item ", r);
XI_OBJ cell; XI_MAKE_CELL(&cell, _xi_lst, r, 0);
xi_set_focus(&cell);
}
return TRUE;
}
return FALSE;
}
bool TDropDownList::select_by_initial(char c)
{
int next = _selected < _values.items() - 1 ? _selected + 1 : 0;
int first = -1;
for (int i = 0; i < _values.items(); i++)
if (toupper(*(_values.get(i))) == toupper(c))
{
first = i;
break;
}
if (first == -1)
return FALSE;
for (i = next; i < _values.items(); i++)
if (toupper(*(_values.get(i))) == toupper(c))
break;
if (i == _values.items() && first >= 0)
i = first;
return select(i);
}
bool TDropDownList::select_by_ofs(int i)
{
i += _selected;
if (i >= 0 && i < _values.items())
return select(i);
return FALSE;
}
TDropDownList::TDropDownList(XI_OBJ* o, const char* codes, const char* values, int width)
: _obj(o), _codes(codes), _values(values),
_open(FALSE), _xi_lst(NULL), _width(width),
_displayed(FALSE), _selected(0)
{}
void TDropDownList::set_values(const char* c, const char* v)
{
_codes = c;
_values = v;
}
void TDropDownList::open()
{
if (_open) return;
XI_OBJ_DEF* itfdef = xi_create_itf_def(ITF_CID, (XI_EVENT_HANDLER)ddl_str_eh, NULL, "",
(long)this);
// qui si vedra' se e cosa modificare
itfdef->v.itf->automatic_back_color = TRUE;
// compute size of field with button
RCT r; xi_get_rect(_obj, &r);
XI_RCT xr = r;
xi_pu_to_fu(_obj->itf, (PNT*)&xr, 2);
if (_width == 0) // usually in spreadsheet cells
_width = (xr.right - xr.left)/XI_FU_MULTIPLE;
if ((xr.right - xr.left) < (_width * XI_FU_MULTIPLE))
{
xr.right = xr.left + (_width * XI_FU_MULTIPLE);
r = xr;
xi_fu_to_pu(_obj->itf, (PNT*)&r, 2);
}
int hei = items() <= 1 ? 2*XI_FU_MULTIPLE + 1 : (min(6,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, NORMAL_COLOR, NORMAL_BACK_COLOR,
DISABLED_COLOR, DISABLED_BACK_COLOR, FOCUS_COLOR, 0);
lstdef->v.list->active_back_color = FOCUS_BACK_COLOR;
lstdef->v.list->scroll_bar = items() > 6;
lstdef->v.list->no_heading = TRUE;
lstdef->v.list->no_horz_lines = TRUE;
lstdef->v.list->no_vert_lines = TRUE;
lstdef->v.list->resize_with_window = TRUE;
/*
int len;
if (_in_cell)
len = items() <= 6 ? r.right - r.left - 2 : (_width-1) * XI_FU_MULTIPLE;
else
len = items() <= 6 ? _obj->v.field->rct.right - _obj->v.field->rct.left - 2 :
(_width-1) * XI_FU_MULTIPLE;
*/
int len = xr.right - xr.left - 2;
if (items() > 6) len = (_width-1) * XI_FU_MULTIPLE;
XI_OBJ_DEF* coldef = xi_add_column_def(lstdef, _obj->cid+2000,
XI_ATR_VISIBLE|XI_ATR_ENABLED|XI_ATR_READONLY,
0, len, 80, "");
RCT l; xi_get_def_rect(lstdef, &l);
PNT p; p.h = r.left;
WINDOW pwin = 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, &l);
wr.right = r.right; // wr.left + (l.right - l.left) -1;
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 = win;
itfdef->v.itf->rctp = &wr;
XI_OBJ* itfobj = xi_create(NULL, itfdef);
xi_tree_free(itfdef);
CHECK(itfobj != NULL, "Oggetto del cazzo!");
_xi_lst = xi_get_obj(itfobj, _obj->cid+1000);
CHECK(_xi_lst != NULL, "Lista del cazzo!");
_open = TRUE;
xi_cell_request(_xi_lst);
xvt_scr_set_focus_vobj(win);
xvt_vobj_raise(win);
_displayed = TRUE;
/*
xi_scroll_rec(_xi_lst, _selected, NORMAL_COLOR, XI_ATR_ENABLED, 0);
const int row = rec2row(_selected);
XI_OBJ cell;
XI_MAKE_CELL(&cell, _xi_lst, row, 0);
xi_set_focus(&cell);
*/
select(_selected, TRUE);
xi_dequeue();
}
// ------------------------------------------------------------------
bool TListbox_control::event_handler(XI_OBJ* itf, XI_EVENT* xiev)
{
bool ok = TRUE;
switch (xiev->type)
{
case XIE_DBL_FIELD:
case XIE_BUTTON:
_ddl->open();
break;
case XIE_CHAR_FIELD:
{
const KEY k = xiev_to_key(xiev);
if (k >= ' ' && k <= 'z')
_ddl->select_by_initial(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);
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 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);
}
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, width);
_ddl->select(0, TRUE);
}
TListbox_control::~TListbox_control()
{
delete _ddl;
}