summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui_gtk_x11.c56
-rw-r--r--src/optiondefs.h16
-rw-r--r--src/optionstr.c123
-rw-r--r--src/os_mswin.c121
-rw-r--r--src/proto/gui_gtk_x11.pro1
-rw-r--r--src/proto/optionstr.pro1
-rw-r--r--src/proto/os_mswin.pro1
-rw-r--r--src/testdir/test_gui.vim50
-rw-r--r--src/version.c2
9 files changed, 362 insertions, 9 deletions
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 3fd6f3a904..4b3f53ef91 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -5316,6 +5316,62 @@ gui_mch_free_font(GuiFont font)
}
/*
+ * Cmdline expansion for setting 'guifont' / 'guifontwide'. Will enumerate
+ * through all fonts for completion. When setting 'guifont' it will only show
+ * monospace fonts as it's unlikely other fonts would be useful.
+ */
+ void
+gui_mch_expand_font(optexpand_T *args, void *param, int (*add_match)(char_u *val))
+{
+ PangoFontFamily **font_families = NULL;
+ int n_families = 0;
+ int wide = *(int *)param;
+
+ if (args->oe_include_orig_val && *args->oe_opt_value == NUL && !wide)
+ {
+ // If guifont is empty, and we want to fill in the orig value, suggest
+ // the default so the user can modify it.
+ if (add_match((char_u *)DEFAULT_FONT) != OK)
+ return;
+ }
+
+ pango_context_list_families(
+ gui.text_context,
+ &font_families,
+ &n_families);
+
+ for (int i = 0; i < n_families; i++)
+ {
+ if (!wide && !pango_font_family_is_monospace(font_families[i]))
+ continue;
+
+ const char* fam_name = pango_font_family_get_name(font_families[i]);
+ if (input_conv.vc_type != CONV_NONE)
+ {
+ char_u *buf = string_convert(&input_conv, (char_u*)fam_name, NULL);
+ if (buf != NULL)
+ {
+ if (add_match(buf) != OK)
+ {
+ vim_free(buf);
+ break;
+ }
+ vim_free(buf);
+ }
+ else
+ break;
+ }
+ else
+ {
+ if (add_match((char_u *)fam_name) != OK)
+ break;
+ }
+ }
+
+ g_free(font_families);
+}
+
+/*
* Return the Pixel value (color) for the given color name.
*
* Return INVALCOLOR for error.
diff --git a/src/optiondefs.h b/src/optiondefs.h
index f850d19b1c..c4810e7d5d 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -1164,9 +1164,13 @@ static struct vimoption options[] =
{(char_u *)NULL, (char_u *)0L}
#endif
SCTX_INIT},
- {"guifont", "gfn", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
+ {"guifont", "gfn", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP
+#if !defined(FEAT_GUI_GTK)
+ |P_COLON
+#endif
+ ,
#ifdef FEAT_GUI
- (char_u *)&p_guifont, PV_NONE, did_set_guifont, NULL,
+ (char_u *)&p_guifont, PV_NONE, did_set_guifont, expand_set_guifont,
{(char_u *)"", (char_u *)0L}
#else
(char_u *)NULL, PV_NONE, NULL, NULL,
@@ -1183,10 +1187,14 @@ static struct vimoption options[] =
{(char_u *)NULL, (char_u *)0L}
#endif
SCTX_INIT},
- {"guifontwide", "gfw", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
+ {"guifontwide", "gfw", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP
+#if !defined(FEAT_GUI_GTK)
+ |P_COLON
+#endif
+ ,
#if defined(FEAT_GUI)
(char_u *)&p_guifontwide, PV_NONE,
- did_set_guifontwide, NULL,
+ did_set_guifontwide, expand_set_guifont,
{(char_u *)"", (char_u *)0L}
#else
(char_u *)NULL, PV_NONE, NULL, NULL,
diff --git a/src/optionstr.c b/src/optionstr.c
index 135cb9af68..6b40dcd706 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -731,7 +731,8 @@ did_set_option_listflag(char_u *val, char_u *flags, char *errbuf)
}
/*
- * Expand an option that accepts a list of string values.
+ * Expand an option that accepts a list of fixed string values with known
+ * number of items.
*/
static int
expand_set_opt_string(
@@ -801,10 +802,11 @@ static char_u *set_opt_callback_orig_option = NULL;
static char_u *((*set_opt_callback_func)(expand_T *, int));
/*
- * Callback used by expand_set_opt_generic to also include the original value.
+ * Callback used by expand_set_opt_generic to also include the original value
+ * as the first item.
*/
static char_u *
-expand_set_opt_callback(expand_T *xp, int idx)
+expand_set_opt_generic_cb(expand_T *xp, int idx)
{
if (idx == 0)
{
@@ -817,7 +819,8 @@ expand_set_opt_callback(expand_T *xp, int idx)
}
/*
- * Expand an option with a callback that iterates through a list of possible names.
+ * Expand an option with a callback that iterates through a list of possible
+ * names using an index.
*/
static int
expand_set_opt_generic(
@@ -838,7 +841,7 @@ expand_set_opt_generic(
args->oe_regmatch,
matches,
numMatches,
- expand_set_opt_callback,
+ expand_set_opt_generic_cb,
FALSE);
set_opt_callback_orig_option = NULL;
@@ -846,6 +849,95 @@ expand_set_opt_generic(
return ret;
}
+static garray_T *expand_cb_ga;
+static optexpand_T *expand_cb_args;
+
+/*
+ * Callback provided to a function in expand_set_opt_callback. Will perform
+ * regex matching against the value and add to the list.
+ *
+ * Returns OK usually. Returns FAIL if it failed to allocate memory, and the
+ * caller should terminate the enumeration.
+ */
+ static int
+expand_set_opt_callback_cb(char_u *val)
+{
+ regmatch_T *regmatch = expand_cb_args->oe_regmatch;
+ expand_T *xp = expand_cb_args->oe_xp;
+ garray_T *ga = expand_cb_ga;
+ char_u *str;
+
+ if (val == NULL || *val == NUL)
+ return OK;
+
+ if (xp->xp_pattern[0] != NUL &&
+ !vim_regexec(regmatch, val, (colnr_T)0))
+ return OK;
+
+ str = vim_strsave_escaped(val, (char_u *)" \t\\");
+
+ if (str == NULL)
+ return FAIL;
+
+ if (ga_grow(ga, 1) == FAIL)
+ {
+ vim_free(str);
+ return FAIL;
+ }
+
+ ((char_u **)ga->ga_data)[ga->ga_len] = str;
+ ++ga->ga_len;
+ return OK;
+}
+
+/*
+ * Expand an option with a provided function that takes a callback. The
+ * function will enumerate through all options and call the callback to add it
+ * to the list.
+ *
+ * "func" is the enumerator function that will generate the list of options.
+ * "func_params" is a single parameter that will be passed to func.
+ */
+ static int
+expand_set_opt_callback(
+ optexpand_T *args,
+ void (*func)(optexpand_T *, void* params, int (*cb)(char_u *val)),
+ void *func_params,
+ int *numMatches,
+ char_u ***matches)
+{
+ garray_T ga;
+ int include_orig_val = args->oe_include_orig_val;
+ char_u *option_val = args->oe_opt_value;
+
+ ga_init2(&ga, sizeof(char *), 30);
+
+ if (include_orig_val && *option_val != NUL)
+ {
+ char_u *p = vim_strsave(option_val);
+ if (p == NULL)
+ return FAIL;
+ if (ga_grow(&ga, 1) == FAIL)
+ {
+ vim_free(p);
+ return FAIL;
+ }
+ ((char_u **)ga.ga_data)[ga.ga_len] = p;
+ ++ga.ga_len;
+ }
+
+ expand_cb_ga = &ga;
+ expand_cb_args = args;
+
+ func(args, func_params, expand_set_opt_callback_cb);
+
+ expand_cb_ga = NULL;
+ expand_cb_args = NULL;
+
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
+ return OK;
+}
/*
* Expand an option which is a list of flags.
@@ -2237,6 +2329,27 @@ did_set_guifont(optset_T *args UNUSED)
return errmsg;
}
+/*
+ * Expand the 'guifont' option. Only when GUI is being used. Each platform has
+ * specific behaviors.
+ */
+ int
+expand_set_guifont(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+ if (!gui.in_use)
+ return FAIL;
+
+# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK)
+ char_u **varp = (char_u **)args->oe_varp;
+ int wide = (varp == &p_guifontwide);
+
+ return expand_set_opt_callback(
+ args, gui_mch_expand_font, &wide, numMatches, matches);
+# else
+ return FAIL;
+# endif
+}
+
# if defined(FEAT_XFONTSET) || defined(PROTO)
/*
* The 'guifontset' option is changed.
diff --git a/src/os_mswin.c b/src/os_mswin.c
index aa5fe5243e..46f73752f9 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -2819,6 +2819,32 @@ points_to_pixels(WCHAR *str, WCHAR **end, int vertical, long_i pprinter_dc)
return pixels;
}
+/*
+ * Convert pixel into point size. This is a reverse of points_to_pixels.
+ */
+ static double
+pixels_to_points(int pixels, int vertical, long_i pprinter_dc)
+{
+ double points = 0;
+ HWND hwnd = (HWND)0;
+ HDC hdc;
+ HDC printer_dc = (HDC)pprinter_dc;
+
+ if (printer_dc == NULL)
+ {
+ hwnd = GetDesktopWindow();
+ hdc = GetWindowDC(hwnd);
+ }
+ else
+ hdc = printer_dc;
+
+ points = pixels * 72.0 / GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX);
+ if (printer_dc == NULL)
+ ReleaseDC(hwnd, hdc);
+
+ return points;
+}
+
static int CALLBACK
font_enumproc(
ENUMLOGFONTW *elf,
@@ -2890,6 +2916,100 @@ init_logfont(LOGFONTW *lf)
}
/*
+ * Call back for EnumFontFamiliesW in expand_font_enumproc.
+ *
+ */
+ static int CALLBACK
+expand_font_enumproc(
+ ENUMLOGFONTW *elf,
+ NEWTEXTMETRICW *ntm UNUSED,
+ DWORD type UNUSED,
+ LPARAM lparam)
+{
+ LOGFONTW *lf = (LOGFONTW*)elf;
+
+# ifndef FEAT_PROPORTIONAL_FONTS
+ // Ignore non-monospace fonts without further ado
+ if ((ntm->tmPitchAndFamily & 1) != 0)
+ return 1;
+# endif
+
+ // Filter only on ANSI. Otherwise will see a lot of random fonts that we
+ // usually don't want.
+ if (lf->lfCharSet != ANSI_CHARSET)
+ return 1;
+
+ int (*add_match)(char_u *) = (int (*)(char_u *))lparam;
+
+ WCHAR *faceNameW = lf->lfFaceName;
+ char_u *faceName = utf16_to_enc(faceNameW, NULL);
+ if (!faceName)
+ return 0;
+
+ add_match(faceName);
+ vim_free(faceName);
+
+ return 1;
+}
+
+/*
+ * Cmdline expansion for setting 'guifont'. Will enumerate through all
+ * monospace fonts for completion. If used after ':', will expand to possible
+ * font configuration options like font sizes.
+ *
+ * This function has "gui" in its name because in some platforms (GTK) font
+ * handling is done by the GUI code, whereas in Windows it's part of the
+ * platform code.
+ */
+ void
+gui_mch_expand_font(optexpand_T *args, void *param UNUSED, int (*add_match)(char_u *val))
+{
+ expand_T *xp = args->oe_xp;
+ if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern-1) == ':')
+ {
+ char buf[30];
+
+ // Always fill in with the current font size as first option for
+ // convenience. We simply round to the closest integer for simplicity.
+ int font_height = (int)round(
+ pixels_to_points(-current_font_height, TRUE, (long_i)NULL));
+ vim_snprintf(buf, ARRAY_LENGTH(buf), "h%d", font_height);
+ add_match((char_u *)buf);
+
+ // Note: Keep this in sync with get_logfont(). Don't include 'c' and
+ // 'q' as we fill in all the values below.
+ static char *(p_gfn_win_opt_values[]) = {
+ "h" , "w" , "W" , "b" , "i" , "u" , "s"};
+ for (size_t i = 0; i < ARRAY_LENGTH(p_gfn_win_opt_values); i++)
+ add_match((char_u *)p_gfn_win_opt_values[i]);
+
+ struct charset_pair *cp;
+ for (cp = charset_pairs; cp->name != NULL; ++cp)
+ {
+ vim_snprintf(buf, ARRAY_LENGTH(buf), "c%s", cp->name);
+ add_match((char_u *)buf);
+ }
+ struct quality_pair *qp;
+ for (qp = quality_pairs; qp->name != NULL; ++qp)
+ {
+ vim_snprintf(buf, ARRAY_LENGTH(buf), "q%s", qp->name);
+ add_match((char_u *)buf);
+ }
+ return;
+ }
+
+ HWND hwnd = GetDesktopWindow();
+ HDC hdc = GetWindowDC(hwnd);
+
+ EnumFontFamiliesW(hdc,
+ NULL,
+ (FONTENUMPROCW)expand_font_enumproc,
+ (LPARAM)add_match);
+
+ ReleaseDC(hwnd, hdc);
+}
+
+/*
* Compare a UTF-16 string and an ASCII string literally.
* Only works all the code points are inside ASCII range.
*/
@@ -2995,6 +3115,7 @@ get_logfont(
{
switch (*p++)
{
+ // Note: Keep this in sync with gui_mch_expand_font().
case L'h':
lf->lfHeight = - points_to_pixels(p, &p, TRUE, (long_i)printer_dc);
break;
diff --git a/src/proto/gui_gtk_x11.pro b/src/proto/gui_gtk_x11.pro
index 3fa2ac96d6..459cdb9532 100644
--- a/src/proto/gui_gtk_x11.pro
+++ b/src/proto/gui_gtk_x11.pro
@@ -37,6 +37,7 @@ int gui_mch_init_font(char_u *font_name, int fontset);
GuiFont gui_mch_get_font(char_u *name, int report_error);
char_u *gui_mch_get_fontname(GuiFont font, char_u *name);
void gui_mch_free_font(GuiFont font);
+void gui_mch_expand_font(optexpand_T *args, void *param, int (*cb)(char_u *val));
guicolor_T gui_mch_get_color(char_u *name);
guicolor_T gui_mch_get_rgb_color(int r, int g, int b);
void gui_mch_set_fg_color(guicolor_T color);
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index 48cccea8cc..88034fcec5 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -152,6 +152,7 @@ int expand_set_foldclose(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_foldmethod(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_foldopen(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_formatoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_guifont(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_guioptions(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_highlight(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char_u ***matches);
diff --git a/src/proto/os_mswin.pro b/src/proto/os_mswin.pro
index d9172ee01b..9f534e09d3 100644
--- a/src/proto/os_mswin.pro
+++ b/src/proto/os_mswin.pro
@@ -51,5 +51,6 @@ void serverProcessPendingMessages(void);
char *charset_id2name(int id);
char *quality_id2name(DWORD id);
int get_logfont(LOGFONTW *lf, char_u *name, HDC printer_dc, int verbose);
+void gui_mch_expand_font(optexpand_T *args, void *param, int (*cb)(char_u *val));
void channel_init_winsock(void);
/* vim: set ft=c : */
diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim
index acc72f8395..e7f7c6d830 100644
--- a/src/testdir/test_gui.vim
+++ b/src/testdir/test_gui.vim
@@ -580,6 +580,56 @@ func Test_set_guifontwide()
endif
endfunc
+func Test_expand_guifont()
+ if has('gui_win32')
+ let guifont_saved = &guifont
+ let guifontwide_saved = &guifontwide
+
+ " Test recalling existing option, and suggesting current font size
+ set guifont=Courier\ New:h11:cANSI
+ call assert_equal('Courier\ New:h11:cANSI', getcompletion('set guifont=', 'cmdline')[0])
+ call assert_equal('h11', getcompletion('set guifont=Lucida\ Console:', 'cmdline')[0])
+
+ " Test auto-completion working for font names
+ call assert_equal(['Courier\ New'], getcompletion('set guifont=Couri*ew$', 'cmdline'))
+ call assert_equal(['Courier\ New'], getcompletion('set guifontwide=Couri*ew$', 'cmdline'))
+
+ " Make sure non-monospace fonts are filtered out
+ call assert_equal([], getcompletion('set guifont=Arial', 'cmdline'))
+ call assert_equal([], getcompletion('set guifontwide=Arial', 'cmdline'))
+
+ " Test auto-completion working for font options
+ call assert_notequal(-1, index(getcompletion('set guifont=Courier\ New:', 'cmdline'), 'b'))
+ call assert_equal(['cDEFAULT'], getcompletion('set guifont=Courier\ New:cD*T', 'cmdline'))
+ call assert_equal(['qCLEARTYPE'], getcompletion('set guifont=Courier\ New:qC*TYPE', 'cmdline'))
+
+ let &guifontwide = guifontwide_saved
+ let &guifont = guifont_saved
+ elseif has('gui_gtk')
+ let guifont_saved = &guifont
+ let guifontwide_saved = &guifontwide
+
+ " Test recalling default and existing option
+ set guifont=
+ call assert_equal('Monospace\ 10', getcompletion('set guifont=', 'cmdline')[0])
+ set guifont=Monospace\ 9
+ call assert_equal('Monospace\ 9', getcompletion('set guifont=', 'cmdline')[0])
+
+ " Test auto-completion working for font names
+ call assert_equal(['Monospace'], getcompletion('set guifont=Mono*pace$', 'cmdline'))
+ call assert_equal(['Monospace'], getcompletion('set guifontwide=Mono*pace$', 'cmdline'))
+
+ " Make sure non-monospace fonts are filtered out only in 'guifont'
+ call assert_equal([], getcompletion('set guifont=Sans$', 'cmdline'))
+ call assert_equal(['Sans'], getcompletion('set guifontwide=Sans$', 'cmdline'))
+
+ let &guifontwide = guifontwide_saved
+ let &guifont = guifont_saved
+ else
+ call assert_equal([], getcompletion('set guifont=', 'cmdline'))
+ endif
+endfunc
+
func Test_set_guiligatures()
CheckX11BasedGui
diff --git a/src/version.c b/src/version.c
index 0f73dc25ae..9b1c0b4e92 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1991,
+/**/
1990,
/**/
1989,