To: vim_dev@googlegroups.com Subject: Patch 8.0.0918 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.0918 Problem: Cannot get terminal window cursor shape or attributes. Solution: Support cursor shape, attributes and color. Files: src/terminal.c, runtime/doc/eval.txt, src/libvterm/include/vterm.h, src/libvterm/src/state.c, src/libvterm/src/vterm.c, src/feature.h, src/ui.c, src/proto/ui.pro, src/term.c, src/proto/term.pro, src/option.c, src/term.h *** ../vim-8.0.0917/src/terminal.c 2017-08-12 16:39:01.377461483 +0200 --- src/terminal.c 2017-08-12 19:45:56.893223753 +0200 *************** *** 36,44 **** * that buffer, attributes come from the scrollback buffer tl_scrollback. * * TODO: ! * - support different cursor shapes, colors and attributes ! * - make term_getcursor() return type (none/block/bar/underline) and ! * attributes (color, blink, etc.) * - Make argument list work on MS-Windows. #1954 * - MS-Windows: no redraw for 'updatetime' #1915 * - To set BS correctly, check get_stty(); Pass the fd of the pty. --- 36,42 ---- * that buffer, attributes come from the scrollback buffer tl_scrollback. * * TODO: ! * - cursor shape/color/blink in the GUI * - Make argument list work on MS-Windows. #1954 * - MS-Windows: no redraw for 'updatetime' #1915 * - To set BS correctly, check get_stty(); Pass the fd of the pty. *************** *** 143,148 **** --- 141,149 ---- VTermPos tl_cursor_pos; int tl_cursor_visible; + int tl_cursor_blink; + int tl_cursor_shape; /* 1: block, 2: underline, 3: bar */ + char_u *tl_cursor_color; /* NULL or allocated */ int tl_using_altscreen; }; *************** *** 155,160 **** --- 156,163 ---- */ static term_T *first_term = NULL; + /* Terminal active in terminal_loop(). */ + static term_T *in_terminal_loop = NULL; #define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */ #define KEY_BUF_LEN 200 *************** *** 256,261 **** --- 259,265 ---- return; term->tl_dirty_row_end = MAX_ROW; term->tl_cursor_visible = TRUE; + term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK; term->tl_finish = opt->jo_term_finish; ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300); *************** *** 517,522 **** --- 521,527 ---- vim_free(term->tl_title); vim_free(term->tl_status_text); vim_free(term->tl_opencmd); + vim_free(term->tl_cursor_color); vim_free(term); buf->b_term = NULL; } *************** *** 1158,1163 **** --- 1163,1197 ---- } } + static int did_change_cursor = FALSE; + + static void + may_set_cursor_props(term_T *term) + { + if (in_terminal_loop == term) + { + if (term->tl_cursor_color != NULL) + term_cursor_color(term->tl_cursor_color); + else + term_cursor_color((char_u *)""); + /* do both blink and shape+blink, in case setting shape does not work */ + term_cursor_blink(term->tl_cursor_blink); + term_cursor_shape(term->tl_cursor_shape, term->tl_cursor_blink); + } + } + + static void + may_restore_cursor_props(void) + { + if (did_change_cursor) + { + did_change_cursor = FALSE; + ui_cursor_shape_forced(TRUE); + term_cursor_color((char_u *)""); + term_cursor_blink(FALSE); + } + } + /* * Returns TRUE if the current window contains a terminal and we are sending * keys to the job. *************** *** 1185,1194 **** --- 1219,1232 ---- { int c; int termkey = 0; + int ret; + + in_terminal_loop = curbuf->b_term; if (*curwin->w_p_tk != NUL) termkey = string_to_key(curwin->w_p_tk, TRUE); position_cursor(curwin, &curbuf->b_term->tl_cursor_pos); + may_set_cursor_props(curbuf->b_term); for (;;) { *************** *** 1235,1241 **** { /* CTRL-\ CTRL-N : go to Terminal-Normal mode. */ term_enter_normal_mode(); ! return FAIL; } /* Send both keys to the terminal. */ send_keys_to_term(curbuf->b_term, prev_c, TRUE); --- 1273,1280 ---- { /* CTRL-\ CTRL-N : go to Terminal-Normal mode. */ term_enter_normal_mode(); ! ret = FAIL; ! goto theend; } /* Send both keys to the terminal. */ send_keys_to_term(curbuf->b_term, prev_c, TRUE); *************** *** 1249,1255 **** { /* CTRL-W N : go to Terminal-Normal mode. */ term_enter_normal_mode(); ! return FAIL; } else if (c == '"') { --- 1288,1295 ---- { /* CTRL-W N : go to Terminal-Normal mode. */ term_enter_normal_mode(); ! ret = FAIL; ! goto theend; } else if (c == '"') { *************** *** 1260,1272 **** { stuffcharReadbuff(Ctrl_W); stuffcharReadbuff(c); ! return OK; } } if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK) ! return OK; } ! return FAIL; } /* --- 1300,1321 ---- { stuffcharReadbuff(Ctrl_W); stuffcharReadbuff(c); ! ret = OK; ! goto theend; } } if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK) ! { ! ret = OK; ! goto theend; ! } } ! ret = FAIL; ! ! theend: ! in_terminal_loop = NULL; ! may_restore_cursor_props(); ! return ret; } /* *************** *** 1303,1309 **** static void may_toggle_cursor(term_T *term) { ! if (curbuf == term->tl_buffer) { if (term->tl_cursor_visible) cursor_on(); --- 1352,1358 ---- static void may_toggle_cursor(term_T *term) { ! if (in_terminal_loop == term) { if (term->tl_cursor_visible) cursor_on(); *************** *** 1385,1390 **** --- 1434,1455 ---- out_flush(); break; + case VTERM_PROP_CURSORBLINK: + term->tl_cursor_blink = value->boolean; + may_set_cursor_props(term); + break; + + case VTERM_PROP_CURSORSHAPE: + term->tl_cursor_shape = value->number; + may_set_cursor_props(term); + break; + + case VTERM_PROP_CURSORCOLOR: + vim_free(term->tl_cursor_color); + term->tl_cursor_color = vim_strsave((char_u *)value->string); + may_set_cursor_props(term); + break; + case VTERM_PROP_ALTSCREEN: /* TODO: do anything else? */ term->tl_using_altscreen = value->boolean; *************** *** 2076,2092 **** f_term_getcursor(typval_T *argvars, typval_T *rettv) { buf_T *buf = term_get_buf(argvars); list_T *l; if (rettv_list_alloc(rettv) == FAIL) return; if (buf == NULL) return; l = rettv->vval.v_list; ! list_append_number(l, buf->b_term->tl_cursor_pos.row + 1); ! list_append_number(l, buf->b_term->tl_cursor_pos.col + 1); ! list_append_number(l, buf->b_term->tl_cursor_visible); } /* --- 2141,2170 ---- f_term_getcursor(typval_T *argvars, typval_T *rettv) { buf_T *buf = term_get_buf(argvars); + term_T *term; list_T *l; + dict_T *d; if (rettv_list_alloc(rettv) == FAIL) return; if (buf == NULL) return; + term = buf->b_term; l = rettv->vval.v_list; ! list_append_number(l, term->tl_cursor_pos.row + 1); ! list_append_number(l, term->tl_cursor_pos.col + 1); ! ! d = dict_alloc(); ! if (d != NULL) ! { ! dict_add_nr_str(d, "visible", term->tl_cursor_visible, NULL); ! dict_add_nr_str(d, "blink", term->tl_cursor_blink, NULL); ! dict_add_nr_str(d, "shape", term->tl_cursor_shape, NULL); ! dict_add_nr_str(d, "color", 0L, term->tl_cursor_color == NULL ! ? (char_u *)"" : term->tl_cursor_color); ! list_append_dict(l, d); ! } } /* *** ../vim-8.0.0917/runtime/doc/eval.txt 2017-08-12 16:01:00.686997023 +0200 --- runtime/doc/eval.txt 2017-08-12 16:46:12.282877906 +0200 *************** *** 7939,7951 **** term_getcursor({buf}) *term_getcursor()* Get the cursor position of terminal {buf}. Returns a list with ! three numbers: [rows, cols, visible]. "rows" and "cols" are ! one based, the first screen cell is row 1, column 1. ! "visible" is one when the cursor is visible, zero when it is ! hidden. ! This is the cursor position of the terminal itself, not of the ! Vim window. {buf} must be the buffer number of a terminal window. If the buffer does not exist or is not a terminal window, an empty --- 7939,7957 ---- term_getcursor({buf}) *term_getcursor()* Get the cursor position of terminal {buf}. Returns a list with ! two numbers and a dictionary: [rows, cols, dict]. ! "rows" and "cols" are one based, the first screen cell is row ! 1, column 1. This is the cursor position of the terminal ! itself, not of the Vim window. ! ! "dict" can have these members: ! "visible" one when the cursor is visible, zero when it ! is hidden. ! "blink" one when the cursor is visible, zero when it ! is hidden. ! "shape" 1 for a block cursor, 2 for underline and 3 ! for a vertical bar. {buf} must be the buffer number of a terminal window. If the buffer does not exist or is not a terminal window, an empty *************** *** 8035,8041 **** "fg" foreground color as #rrggbb "bg" background color as #rrggbb "attr" attributes of the cell, use |term_getattr()| ! to get the individual flags "width" cell width: 1 or 2 {only available when compiled with the |+terminal| feature} --- 8041,8047 ---- "fg" foreground color as #rrggbb "bg" background color as #rrggbb "attr" attributes of the cell, use |term_getattr()| ! to get the individual flags "width" cell width: 1 or 2 {only available when compiled with the |+terminal| feature} *************** *** 8075,8081 **** "term_rows" vertical size to use for the terminal, instead of using 'termsize' "term_cols" horizontal size to use for the terminal, ! instead of using 'termsize' "vertical" split the window vertically "curwin" use the current window, do not split the window; fails if the current buffer --- 8081,8087 ---- "term_rows" vertical size to use for the terminal, instead of using 'termsize' "term_cols" horizontal size to use for the terminal, ! instead of using 'termsize' "vertical" split the window vertically "curwin" use the current window, do not split the window; fails if the current buffer *************** *** 8165,8171 **** in a way that the test doesn't work properly. When using: > call test_override('starting', 1) ! < The value of "starting" is saved. It is restored by: > call test_override('starting', 0) test_settime({expr}) *test_settime()* --- 8171,8177 ---- in a way that the test doesn't work properly. When using: > call test_override('starting', 1) ! < The value of "starting" is saved. It is restored by: > call test_override('starting', 0) test_settime({expr}) *test_settime()* *** ../vim-8.0.0917/src/libvterm/include/vterm.h 2017-08-11 16:24:46.312283924 +0200 --- src/libvterm/include/vterm.h 2017-08-12 17:08:33.370711277 +0200 *************** *** 120,126 **** VTERM_PROP_ICONNAME, /* string */ VTERM_PROP_REVERSE, /* bool */ VTERM_PROP_CURSORSHAPE, /* number */ ! VTERM_PROP_MOUSE /* number */ } VTermProp; enum { --- 120,127 ---- VTERM_PROP_ICONNAME, /* string */ VTERM_PROP_REVERSE, /* bool */ VTERM_PROP_CURSORSHAPE, /* number */ ! VTERM_PROP_MOUSE, /* number */ ! VTERM_PROP_CURSORCOLOR /* string */ } VTermProp; enum { *** ../vim-8.0.0917/src/libvterm/src/state.c 2017-07-26 21:29:29.124507597 +0200 --- src/libvterm/src/state.c 2017-08-12 17:07:01.519274484 +0200 *************** *** 1504,1509 **** --- 1504,1513 ---- settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2); return 1; } + else if(strneq(command, "12;", 3)) { + settermprop_string(state, VTERM_PROP_CURSORCOLOR, command + 3, cmdlen - 3); + return 1; + } else if(state->fallbacks && state->fallbacks->osc) if((*state->fallbacks->osc)(command, cmdlen, state->fbdata)) return 1; *************** *** 1819,1824 **** --- 1823,1829 ---- switch(prop) { case VTERM_PROP_TITLE: case VTERM_PROP_ICONNAME: + case VTERM_PROP_CURSORCOLOR: /* we don't store these, just transparently pass through */ return 1; case VTERM_PROP_CURSORVISIBLE: *** ../vim-8.0.0917/src/libvterm/src/vterm.c 2017-07-11 22:34:47.527932090 +0200 --- src/libvterm/src/vterm.c 2017-08-12 17:07:41.587028757 +0200 *************** *** 294,299 **** --- 294,300 ---- case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL; case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT; case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT; + case VTERM_PROP_CURSORCOLOR: return VTERM_VALUETYPE_STRING; } return 0; /* UNREACHABLE */ } *** ../vim-8.0.0917/src/feature.h 2017-07-07 11:53:29.499876650 +0200 --- src/feature.h 2017-08-12 17:50:31.811177367 +0200 *************** *** 1273,1278 **** --- 1273,1281 ---- #if !defined(FEAT_JOB_CHANNEL) && defined(FEAT_TERMINAL) # undef FEAT_TERMINAL #endif + #if defined(FEAT_TERMINAL) && !defined(CURSOR_SHAPE) + # define CURSOR_SHAPE + #endif /* * +signs Allow signs to be displayed to the left of text lines. *** ../vim-8.0.0917/src/ui.c 2017-07-19 19:55:54.494463778 +0200 --- src/ui.c 2017-08-12 19:43:55.261941198 +0200 *************** *** 1942,1955 **** * May update the shape of the cursor. */ void ! ui_cursor_shape(void) { # ifdef FEAT_GUI if (gui.in_use) gui_update_cursor_later(); else # endif ! term_cursor_shape(); # ifdef MCH_CURSOR_SHAPE mch_update_cursor(); --- 1942,1955 ---- * May update the shape of the cursor. */ void ! ui_cursor_shape_forced(int forced) { # ifdef FEAT_GUI if (gui.in_use) gui_update_cursor_later(); else # endif ! term_cursor_mode(forced); # ifdef MCH_CURSOR_SHAPE mch_update_cursor(); *************** *** 1959,1964 **** --- 1959,1970 ---- conceal_check_cursur_line(); # endif } + + void + ui_cursor_shape(void) + { + ui_cursor_shape_forced(FALSE); + } #endif #if defined(FEAT_CLIPBOARD) || defined(FEAT_GUI) || defined(FEAT_RIGHTLEFT) \ *** ../vim-8.0.0917/src/proto/ui.pro 2017-03-29 19:20:25.385015086 +0200 --- src/proto/ui.pro 2017-08-12 17:55:56.621164927 +0200 *************** *** 47,52 **** --- 47,53 ---- int read_from_input_buf(char_u *buf, long maxlen); void fill_input_buf(int exit_on_error); void read_error_exit(void); + void ui_cursor_shape_forced(int forced); void ui_cursor_shape(void); int check_col(int col); int check_row(int row); *** ../vim-8.0.0917/src/term.c 2017-07-25 22:06:39.319354121 +0200 --- src/term.c 2017-08-12 19:44:50.681613498 +0200 *************** *** 817,822 **** --- 817,830 ---- {(int)KS_MS, "y"}, {(int)KS_UT, "y"}, {(int)KS_LE, "\b"}, + {(int)KS_VI, IF_EB("\033[?25l", ESC_STR "[?25l")}, + {(int)KS_VE, IF_EB("\033[?25h", ESC_STR "[?25h")}, + {(int)KS_VS, IF_EB("\033[?12h", ESC_STR "[?12h")}, + # ifdef TERMINFO + {(int)KS_CSH, IF_EB("\033[%p1%d q", ESC_STR "[%p1%d q")}, + # else + {(int)KS_CSH, IF_EB("\033[%d q", ESC_STR "[%d q")}, + # endif # ifdef TERMINFO {(int)KS_CM, IF_EB("\033[%i%p1%d;%p2%dH", ESC_STR "[%i%p1%d;%p2%dH")}, *************** *** 840,845 **** --- 848,855 ---- {(int)KS_CIE, "\007"}, {(int)KS_TS, IF_EB("\033]2;", ESC_STR "]2;")}, {(int)KS_FS, "\007"}, + {(int)KS_CSC, IF_EB("\033]12;", ESC_STR "]12;")}, + {(int)KS_CEC, "\007"}, # ifdef TERMINFO {(int)KS_CWS, IF_EB("\033[8;%p1%d;%p2%dt", ESC_STR "[8;%p1%d;%p2%dt")}, *************** *** 1142,1147 **** --- 1152,1159 ---- {(int)KS_TE, "[TE]"}, {(int)KS_CIS, "[CIS]"}, {(int)KS_CIE, "[CIE]"}, + {(int)KS_CSC, "[CSC]"}, + {(int)KS_CEC, "[CEC]"}, {(int)KS_TS, "[TS]"}, {(int)KS_FS, "[FS]"}, # ifdef TERMINFO *************** *** 1569,1574 **** --- 1581,1587 ---- {KS_CAB,"AB"}, {KS_CAF,"AF"}, {KS_LE, "le"}, {KS_ND, "nd"}, {KS_OP, "op"}, {KS_CRV, "RV"}, {KS_CIS, "IS"}, {KS_CIE, "IE"}, + {KS_CSC, "SC"}, {KS_CEC, "EC"}, {KS_TS, "ts"}, {KS_FS, "fs"}, {KS_CWP, "WP"}, {KS_CWS, "WS"}, {KS_CSI, "SI"}, {KS_CEI, "EI"}, *************** *** 2283,2290 **** /* * Translate terminal control chars from 7-bit to 8-bit: ! * [ -> CSI ! * ] -> * O -> */ static int --- 2296,2303 ---- /* * Translate terminal control chars from 7-bit to 8-bit: ! * [ -> CSI ! * ] -> OSC * O -> */ static int *************** *** 3655,3661 **** * Set cursor shape to match Insert or Replace mode. */ void ! term_cursor_shape(void) { static int showing_mode = NORMAL; char_u *p; --- 3668,3674 ---- * Set cursor shape to match Insert or Replace mode. */ void ! term_cursor_mode(int forced) { static int showing_mode = NORMAL; char_u *p; *************** *** 3667,3673 **** if ((State & REPLACE) == REPLACE) { ! if (showing_mode != REPLACE) { if (*T_CSR != NUL) p = T_CSR; /* Replace mode cursor */ --- 3680,3686 ---- if ((State & REPLACE) == REPLACE) { ! if (forced || showing_mode != REPLACE) { if (*T_CSR != NUL) p = T_CSR; /* Replace mode cursor */ *************** *** 3682,3699 **** } else if (State & INSERT) { ! if (showing_mode != INSERT && *T_CSI != NUL) { out_str(T_CSI); /* Insert mode cursor */ showing_mode = INSERT; } } ! else if (showing_mode != NORMAL) { out_str(T_CEI); /* non-Insert mode cursor */ showing_mode = NORMAL; } } #endif /* --- 3695,3749 ---- } else if (State & INSERT) { ! if ((forced || showing_mode != INSERT) && *T_CSI != NUL) { out_str(T_CSI); /* Insert mode cursor */ showing_mode = INSERT; } } ! else if (forced || showing_mode != NORMAL) { out_str(T_CEI); /* non-Insert mode cursor */ showing_mode = NORMAL; } } + + # if defined(FEAT_TERMINAL) || defined(PROTO) + void + term_cursor_color(char_u *color) + { + if (*T_CSC != NUL) + { + out_str(T_CSC); /* set cursor color start */ + out_str_nf(color); + out_str(T_CEC); /* set cursor color end */ + out_flush(); + } + } + + void + term_cursor_blink(int blink) + { + if (blink) + out_str(T_VS); + else + out_str(T_VE); + out_flush(); + } + + /* + * "shape" == 1: block, "shape" == 2: underline, "shape" == 3: vertical bar + */ + void + term_cursor_shape(int shape, int blink) + { + if (*T_CSH != NUL) + { + OUT_STR(tgoto((char *)T_CSH, 0, shape * 2 - blink)); + out_flush(); + } + } + # endif #endif /* *** ../vim-8.0.0917/src/proto/term.pro 2017-07-23 16:45:05.669761183 +0200 --- src/proto/term.pro 2017-08-12 19:44:04.153888279 +0200 *************** *** 51,57 **** void scroll_start(void); void cursor_on(void); void cursor_off(void); ! void term_cursor_shape(void); void scroll_region_set(win_T *wp, int off); void scroll_region_reset(void); void clear_termcodes(void); --- 51,60 ---- void scroll_start(void); void cursor_on(void); void cursor_off(void); ! void term_cursor_mode(int forced); ! void term_cursor_color(char_u *color); ! void term_cursor_blink(int blink); ! void term_cursor_shape(int shape, int blink); void scroll_region_set(win_T *wp, int off); void scroll_region_reset(void); void clear_termcodes(void); *** ../vim-8.0.0917/src/option.c 2017-08-06 14:57:44.947764490 +0200 --- src/option.c 2017-08-12 19:42:23.990484277 +0200 *************** *** 3162,3167 **** --- 3162,3170 ---- p_term("t_EI", T_CEI) p_term("t_fs", T_FS) p_term("t_IE", T_CIE) + p_term("t_SC", T_CSC) + p_term("t_EC", T_CEC) + p_term("t_SH", T_CSH) p_term("t_IS", T_CIS) p_term("t_ke", T_KE) p_term("t_ks", T_KS) *** ../vim-8.0.0917/src/term.h 2017-04-04 22:41:04.732342875 +0200 --- src/term.h 2017-08-12 19:42:46.322351419 +0200 *************** *** 40,45 **** --- 40,46 ---- KS_VI, /* cursor invisible */ KS_VE, /* cursor visible */ KS_VS, /* cursor very visible */ + KS_CSH, /* cursor shape */ KS_ME, /* normal mode */ KS_MR, /* reverse mode */ KS_MD, /* bold mode */ *************** *** 74,79 **** --- 75,82 ---- KS_ND, /* cursor right */ KS_CIS, /* set icon text start */ KS_CIE, /* set icon text end */ + KS_CSC, /* set cursor color start */ + KS_CEC, /* set cursor color end */ KS_TS, /* set window title start (to status line)*/ KS_FS, /* set window title end (from status line) */ KS_CWP, /* set window position in pixels */ *************** *** 128,133 **** --- 131,137 ---- #define T_VI (TERM_STR(KS_VI)) /* cursor invisible */ #define T_VE (TERM_STR(KS_VE)) /* cursor visible */ #define T_VS (TERM_STR(KS_VS)) /* cursor very visible */ + #define T_CSH (TERM_STR(KS_CSH)) /* cursor shape */ #define T_ME (TERM_STR(KS_ME)) /* normal mode */ #define T_MR (TERM_STR(KS_MR)) /* reverse mode */ #define T_MD (TERM_STR(KS_MD)) /* bold mode */ *************** *** 164,169 **** --- 168,175 ---- #define T_CIE (TERM_STR(KS_CIE)) /* set icon text end */ #define T_TS (TERM_STR(KS_TS)) /* set window title start */ #define T_FS (TERM_STR(KS_FS)) /* set window title end */ + #define T_CSC (TERM_STR(KS_CSC)) /* set cursor color start */ + #define T_CEC (TERM_STR(KS_CEC)) /* set cursor color end */ #define T_CWP (TERM_STR(KS_CWP)) /* set window position */ #define T_CGP (TERM_STR(KS_CGP)) /* get window position */ #define T_CWS (TERM_STR(KS_CWS)) /* window size */ *** ../vim-8.0.0917/src/version.c 2017-08-12 16:39:01.377461483 +0200 --- src/version.c 2017-08-12 19:47:56.552518945 +0200 *************** *** 771,772 **** --- 771,774 ---- { /* Add new patch number below this line */ + /**/ + 918, /**/ -- Violators can be fined, arrested or jailed for making ugly faces at a dog. [real standing law in Oklahoma, United States of America] /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///