#if XVT_OS == XVT_OS_WIN || XVT_OS_WIN32 #define STRICT #define XVT_INCL_NATIVE #endif #define XI_INTERNAL #include #include #include #include #include #include #include COLOR MASK_BACK_COLOR = COLOR_DKCYAN; COLOR MASK_LIGHT_COLOR = COLOR_CYAN; 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_LTGRAY; COLOR DISABLED_COLOR = COLOR_GRAY; COLOR DISABLED_BACK_COLOR = MASK_BACK_COLOR; COLOR FOCUS_COLOR = NORMAL_COLOR; COLOR FOCUS_BACK_COLOR = COLOR_CYAN; BOOLEAN CAMPI_SCAVATI = FALSE; BOOLEAN AUTOSELECT = FALSE; #ifndef STX_DATA // Very deep hacking typedef struct _stx_data { int cid; WINDOW win; RCT rct; #ifdef XI_R4 RCT edit_rct; #endif unsigned long attrib; } STX_DATA; extern "C" { #include } #endif HIDDEN bool _button_blocked = FALSE; HIDDEN int _last_mouse_button = 0; 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->parent != obj->itf)) 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) { 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_F1 && k <= K_F12) || isalnum(k))) key += K_CTRL; } return key; } // @doc INTERNAL /////////////////////////////////////////////////////////// // TPicture_array /////////////////////////////////////////////////////////// class TPicture_array : public TArray { public: TImage& 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() {} }; TImage& TPicture_array::add(short id) { TImage* i = (TImage*)objptr(id); if (i == NULL) { i = new TImage(id); i->convert_transparent_color(BTN_BACK_COLOR); TArray::add(i, id); } return *i; } void TPicture_array::reload() { for (int id = last(); id >= 0; id--) { TImage* i = (TImage*)objptr(id); if (i) { i->load(id); i->convert_transparent_color(BTN_BACK_COLOR); } } } /////////////////////////////////////////////////////////// // 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 || XVT_OS_WIN32 TEXTMETRIC tm; HDC hdc = (HDC)xvt_vobj_get_attr(TASK_WIN, ATTR_NATIVE_GRAPHIC_CONTEXT); GetTextMetrics(hdc, &tm); RCT pc; xvt_vobj_get_client_rect(TASK_WIN, &pc); ROWY = (pc.bottom - pc.top) / 23; const int COLX = (pc.right - pc.left) / 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; } 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); 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 }; HIDDEN void set_default_palette(WINDOW win) { COLOR colori[12] = { MASK_BACK_COLOR, MASK_LIGHT_COLOR, MASK_DARK_COLOR, NORMAL_COLOR, NORMAL_BACK_COLOR, FOCUS_COLOR, FOCUS_BACK_COLOR, DISABLED_COLOR, DISABLED_BACK_COLOR, BTN_BACK_COLOR, BTN_LIGHT_COLOR, BTN_DARK_COLOR }; XVT_PALETTE wp = xvt_vobj_get_palet(win); if (wp != NULL) { XVT_PALETTE up = xvt_palet_create(XVT_PALETTE_USER, NULL); if (up != NULL) { xvt_palet_set_tolerance(up, xvt_palet_get_tolerance(wp)); const int MAXCOL = 256; COLOR colors[MAXCOL]; const int num = xvt_palet_get_colors(wp, colors, MAXCOL); xvt_palet_add_colors(up, colors, num); const int add = xvt_palet_add_colors(up, colori, 12); if (add > 0) { xvt_vobj_set_palet(win, up); xvt_palet_destroy(wp); } else xvt_palet_destroy(up); } } } // @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 { TConfig colors(CONFIG_USER, "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); 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, (bool)CAMPI_SCAVATI); AUTOSELECT = colors.get_bool("AutoSelect", NULL, -1, (bool)AUTOSELECT); 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_COMBO_ICON, COMBO_ICON); 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); set_default_palette(TASK_WIN); 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_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_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); #define MASK_RECT_ID -883 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"); set_default_palette(win); 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 = win; def->v.itf->edit_menu = TRUE; // Update edit menu items def->v.itf->menu_win = TASK_WIN; // Window that owns the menu 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, (XinPoint *) &max, 2); X_FU_MULTIPLE = max.right / 80; Y_FU_MULTIPLE = max.bottom / 25; } 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); } XI_RCT rct; xi_get_xi_rct(itf, &rct); if (tag) rct.top += Y_FU_MULTIPLE; // Lascia lo spazio per i Bottoni di cambio pagina def = xi_add_rect_def(NULL, tag ? MASK_RECT_ID : -1, &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 = (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_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, (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 = 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, (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: 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: switch (xiev->v.xvte.type) { case E_MOUSE_DOWN: _last_mouse_button = xiev->v.xvte.v.mouse.button; 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) { 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); 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(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* first = find_operable(_obj->itf, TRUE, TRUE); XI_OBJ* last = find_operable(_obj->itf, FALSE, TRUE); int num; XI_OBJ** child = xi_get_member_list(_obj, &num); set_tab_cid(child[num-1], first->cid); set_tab_cid(last, child[0]->cid); } else if (_obj->parent != _obj->itf) { XI_OBJ* first = find_operable(_obj->itf, TRUE, FALSE); XI_OBJ* last = find_operable(_obj->itf, FALSE, FALSE); set_tab_cid(_obj->parent, first->cid); set_tab_cid(last, _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); // 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; xvt_vobj_get_client_rect(win, &max); xi_pu_to_fu(itf, (XinPoint *) &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; } 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&) { 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); 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 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; } } // Usa tranquillamente il metodo ortodosso #endif 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(); 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 XinFont * &f = _obj->v.text->font; if (bold && f == NULL) XinFontCopy(&f, xi_get_system_font()); if (f != NULL && XinFontBoldGet(f) != bold) XinFontBoldSet(f, 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); 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->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.top = rct.top; f->xi_rct.bottom = rct.bottom; f->xi_rct.right = rct.right; f->xi_rct.left = rct.left; } 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; if (f->button != on) { f->button = on; xi_invalidate_rect(parent(), &f->btn_rct); } } void TField_control::set_rect(const RCT& r) { _obj->v.field->rct.top = r.top; _obj->v.field->rct.bottom = r.bottom; _obj->v.field->rct.right = r.right; _obj->v.field->rct.left = r.left; } 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(); } /////////////////////////////////////////////////////////// // 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, (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 (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; } void TButton_control::set_rect(const RCT& r) { _obj->v.btn->rct.top = r.top; _obj->v.btn->rct.bottom = r.bottom; _obj->v.btn->rct.left = r.left; _obj->v.btn->rct.right = r.right; } 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), _hicon(0) { bool bold; COLOR color; _caption = parse_caption(text, bold, color); _underscore = _caption.find('~'); if (_underscore < 0) _underscore = _caption.find('&'); if (_underscore >= 0) _caption.strip("~&"); create(win, cid, left, top, width, height, flags, text, WC_PUSHBUTTON, NULL); _obj->v.btn->drawable = TRUE; *_obj->v.btn->text = '\0'; set_bmp(bmp_up, bmp_dn); } TPushbutton_control::~TPushbutton_control() { if (_hicon) DestroyIcon((HICON)_hicon); } void TPushbutton_control::set_icon(word hicon) { if (_hicon) { DestroyIcon((HICON)_hicon); _hicon = NULL; } if (hicon) { _hicon = hicon; set_bmp(0, 0); } } void TPushbutton_control::set_bmp(short bmp_up, short bmp_dn) { if (bmp_up > 0) { _picture->add(_bmp_up = bmp_up); if (bmp_dn > 0) _picture->add(_bmp_dn = bmp_dn); else _bmp_dn = _bmp_up; set_icon(NULL); } else { _bmp_up = _bmp_dn = 0; } } void TPushbutton_control::update() { const long attrib = xi_get_attrib(_obj); const WINDOW win = parent(); if (attrib & XI_ATR_VISIBLE) { if (_hicon) { HDC hdc = (HDC)xvt_vobj_get_attr(win, ATTR_NATIVE_GRAPHIC_CONTEXT); const RCT& rct = (RCT &) _obj->v.btn->rct; int x = rct.left + (rct.right - rct.left - 32) / 2; int y = rct.top + (rct.bottom - rct.top - 32) / 2; DrawIcon(hdc, x, y, (HICON)_hicon); return; } 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 = (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; } #ifdef XI_R4 XinWindow win = xi_get_window(_obj->itf); xi_set_clip( win, (XinRect *) &rct ); #endif i.draw(win, x, y); #ifdef XI_R4 xi_set_clip( win, NULL ); #endif 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 { RCT& r = (RCT &) _obj->v.btn->rct; xvt_dwin_set_font(win, xvt_default_font()); int ascent, descent; xvt_dwin_get_font_metrics(win, NULL, &ascent, &descent); const int w = xvt_dwin_get_text_width(win, (char*)(const char*)_caption, -1); int x = r.left + (r.right - r.left - w) / 2; int y = r.top + (r.bottom - r.top + ascent - (descent+1)) / 2; if (_obj->v.btn->down) { x += 2; y += 2; } DRAW_CTOOLS ct; xvt_dwin_get_draw_ctools(win, &ct); ct.opaque_text = FALSE; ct.fore_color = (attrib & XI_ATR_ENABLED) ? _obj->v.btn->fore_color : DISABLED_COLOR; xvt_dwin_set_draw_ctools(win, &ct); xvt_dwin_draw_text(win, x, y, (char*)(const char*)_caption, -1); if (_underscore >= 0) { TString80 cap = _caption; cap[_underscore] = '_'; xvt_dwin_draw_text(win, x, y, (char*)(const char*)cap, _underscore+1); } } } } 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); 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; ddl->update_selection(xiev); } break; case XIE_GET_LAST: xiev->v.rec_request.data_rec = ddl->items() - 1; ddl->update_selection(xiev); 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; ddl->update_selection(xiev); } 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; ddl->update_selection(xiev); } break; case XIE_GET_PERCENT: if (ddl->items() > 1) xiev->v.get_percent.percent = int((xiev->v.get_percent.record * 100L)/(ddl->items()-1)); 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_FOCUS && xiev->v.xvte.v.active == FALSE) || xiev->v.xvte.type == E_MOUSE_UP) { ddl->close(); #ifndef XI_R4 if (xiev->v.xvte.type == E_FOCUS) _button_blocked = TRUE; #endif } 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) { if (is_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 && is_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; } TDropDownList::TDropDownList(XI_OBJ* o, const char* codes, const char* values) : _obj(o), _codes(codes), _values(values), _open(FALSE), _xi_lst(NULL), _displayed(FALSE), _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; } } void TDropDownList::set_values(const char* c, const char* v) { _codes = c; _values = v; } 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 * XI_FU_MULTIPLE; } 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); itfdef->v.itf->automatic_back_color = TRUE; const 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, 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() > 6; 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; } // 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 = 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.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 = (XinRect *) ≀ 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; 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: 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(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 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); _ddl->select(0, TRUE); } TListbox_control::~TListbox_control() { delete _ddl; }