Manual pages: mcmcdiffmceditmcview

root/lib/skin/lines.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. unicode_to_mc_acs
  2. skin_get_char
  3. mc_skin_lines_parse_ini_file

   1 /*
   2    Skins engine.
   3    Work with line draving chars.
   4 
   5    Copyright (C) 2009-2025
   6    Free Software Foundation, Inc.
   7 
   8    Written by:
   9    Slava Zanko <slavazanko@gmail.com>, 2009.
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include <config.h>
  28 #include <stdlib.h>
  29 
  30 #include "internal.h"
  31 #include "lib/tty/tty.h"
  32 #include "lib/strutil.h"
  33 
  34 /*** global variables ****************************************************************************/
  35 
  36 /*** file scope macro definitions ****************************************************************/
  37 
  38 /*** file scope type declarations ****************************************************************/
  39 
  40 /*** forward declarations (file scope functions) *************************************************/
  41 
  42 /*** file scope variables ************************************************************************/
  43 
  44 /* --------------------------------------------------------------------------------------------- */
  45 /*** file scope functions ************************************************************************/
  46 /* --------------------------------------------------------------------------------------------- */
  47 
  48 static mc_tty_char_t
  49 unicode_to_mc_acs (gunichar c)
     /* [previous][next][first][last][top][bottom][index][help]  */
  50 {
  51     switch (c)
  52     {
  53     case 0x2500:  // ─
  54         return MC_ACS_HLINE;
  55     case 0x2502:  // │
  56         return MC_ACS_VLINE;
  57     case 0x250C:  // ┌
  58         return MC_ACS_ULCORNER;
  59     case 0x2510:  // ┐
  60         return MC_ACS_URCORNER;
  61     case 0x2514:  // └
  62         return MC_ACS_LLCORNER;
  63     case 0x2518:  // ┘
  64         return MC_ACS_LRCORNER;
  65     case 0x251C:  // ├
  66         return MC_ACS_LTEE;
  67     case 0x2524:  // ┤
  68         return MC_ACS_RTEE;
  69     case 0x252C:  // ┬
  70         return MC_ACS_TTEE;
  71     case 0x2534:  // ┴
  72         return MC_ACS_BTEE;
  73     case 0x253C:  // ┼
  74         return MC_ACS_PLUS;
  75 
  76     default:
  77         return 0;
  78     }
  79 }
  80 
  81 /* --------------------------------------------------------------------------------------------- */
  82 
  83 // "def" is the default in case the skin doesn't define anything. It's not the fallback in case the
  84 // character cannot be converted to the locale, the fallback is handled by the caller.
  85 static gboolean
  86 skin_get_char (mc_skin_t *mc_skin, const char *name, gunichar def, mc_tty_char_t *result)
     /* [previous][next][first][last][top][bottom][index][help]  */
  87 {
  88     gunichar c;
  89     GIConv conv;
  90     GString *buffer;
  91     estr_t conv_res;
  92     char *value_utf8;
  93 
  94     value_utf8 = mc_config_get_string_raw (mc_skin->config, "Lines", name, NULL);
  95     if (value_utf8 != NULL)
  96     {
  97         c = g_utf8_get_char_validated (value_utf8, -1);
  98         if (c == (gunichar) (-1) || c == (gunichar) (-2))
  99         {
 100             g_free (value_utf8);
 101             return FALSE;
 102         }
 103     }
 104     else
 105     {
 106         // the default to be used if not defined in the skin file
 107         value_utf8 = g_malloc (7);
 108         const int len = g_unichar_to_utf8 (def, value_utf8);
 109         value_utf8[len] = '\0';
 110         c = def;
 111     }
 112 
 113     // in UTF-8 nothing left to do, no ACS business, just return the codepoint
 114     if (mc_global.utf8_display)
 115     {
 116         g_free (value_utf8);
 117         *result = c;
 118         return TRUE;
 119     }
 120 
 121     // use MC_ACS_* values for single line drawing chars
 122     const mc_tty_char_t mc_acs = unicode_to_mc_acs (c);
 123     if (mc_acs != 0)
 124     {
 125         g_free (value_utf8);
 126         *result = mc_acs;
 127         return TRUE;
 128     }
 129 
 130     // convert to 8-bit charset, e.g. KOI8-R has all the double line chars
 131 
 132 #ifdef HAVE_NCURSES
 133     // ncurses versions before 6.5-20251115 are buggy in KOI8-R locale, see mc #4799.
 134     // Claim failure so that the caller will fall back to ACS single lines.
 135     // ASCII chars (e.g. with `--stickchars`) need to fall through.
 136     if (ncurses_koi8r_double_line_bug && c >= 128)
 137     {
 138         g_free (value_utf8);
 139         return FALSE;
 140     }
 141 #endif
 142 
 143     conv = str_crt_conv_from ("UTF-8");
 144     if (conv == INVALID_CONV)
 145     {
 146         g_free (value_utf8);
 147         return FALSE;
 148     }
 149 
 150     buffer = g_string_new ("");
 151     conv_res = str_convert (conv, value_utf8, buffer);
 152     if (conv_res == ESTR_SUCCESS)
 153         *result = (unsigned char) buffer->str[0];
 154     str_close_conv (conv);
 155     g_string_free (buffer, TRUE);
 156     g_free (value_utf8);
 157     return conv_res == ESTR_SUCCESS;
 158 }
 159 
 160 /* --------------------------------------------------------------------------------------------- */
 161 /*** public functions ****************************************************************************/
 162 /* --------------------------------------------------------------------------------------------- */
 163 
 164 void
 165 mc_skin_lines_parse_ini_file (mc_skin_t *mc_skin)
     /* [previous][next][first][last][top][bottom][index][help]  */
 166 {
 167     gboolean success;
 168 
 169     if (mc_global.tty.slow_terminal)
 170         mc_skin_hardcoded_space_lines (mc_skin);
 171     else if (mc_global.tty.ugly_line_drawing)
 172         mc_skin_hardcoded_ugly_lines (mc_skin);
 173 
 174     // single lines
 175     success = TRUE /* just for nicer indentation */
 176         && skin_get_char (mc_skin, "horiz", 0x2500, &mc_tty_frm[MC_TTY_FRM_HORIZ])
 177         && skin_get_char (mc_skin, "vert", 0x2502, &mc_tty_frm[MC_TTY_FRM_VERT])
 178         && skin_get_char (mc_skin, "lefttop", 0x250C, &mc_tty_frm[MC_TTY_FRM_LEFTTOP])
 179         && skin_get_char (mc_skin, "righttop", 0x2510, &mc_tty_frm[MC_TTY_FRM_RIGHTTOP])
 180         && skin_get_char (mc_skin, "leftbottom", 0x2514, &mc_tty_frm[MC_TTY_FRM_LEFTBOTTOM])
 181         && skin_get_char (mc_skin, "rightbottom", 0x2518, &mc_tty_frm[MC_TTY_FRM_RIGHTBOTTOM])
 182         && skin_get_char (mc_skin, "topmiddle", 0x252C, &mc_tty_frm[MC_TTY_FRM_TOPMIDDLE])
 183         && skin_get_char (mc_skin, "bottommiddle", 0x2534, &mc_tty_frm[MC_TTY_FRM_BOTTOMMIDDLE])
 184         && skin_get_char (mc_skin, "leftmiddle", 0x251C, &mc_tty_frm[MC_TTY_FRM_LEFTMIDDLE])
 185         && skin_get_char (mc_skin, "rightmiddle", 0x2524, &mc_tty_frm[MC_TTY_FRM_RIGHTMIDDLE])
 186         && skin_get_char (mc_skin, "cross", 0x253C, &mc_tty_frm[MC_TTY_FRM_CROSS]);
 187 
 188     // if any of them are unavailable, revert all of them to regular single lines
 189     if (!success)
 190     {
 191         mc_tty_frm[MC_TTY_FRM_HORIZ] = MC_ACS_HLINE;
 192         mc_tty_frm[MC_TTY_FRM_VERT] = MC_ACS_VLINE;
 193         mc_tty_frm[MC_TTY_FRM_LEFTTOP] = MC_ACS_ULCORNER;
 194         mc_tty_frm[MC_TTY_FRM_RIGHTTOP] = MC_ACS_URCORNER;
 195         mc_tty_frm[MC_TTY_FRM_LEFTBOTTOM] = MC_ACS_LLCORNER;
 196         mc_tty_frm[MC_TTY_FRM_RIGHTBOTTOM] = MC_ACS_LRCORNER;
 197         mc_tty_frm[MC_TTY_FRM_TOPMIDDLE] = MC_ACS_TTEE;
 198         mc_tty_frm[MC_TTY_FRM_BOTTOMMIDDLE] = MC_ACS_BTEE;
 199         mc_tty_frm[MC_TTY_FRM_LEFTMIDDLE] = MC_ACS_LTEE;
 200         mc_tty_frm[MC_TTY_FRM_RIGHTMIDDLE] = MC_ACS_RTEE;
 201         mc_tty_frm[MC_TTY_FRM_CROSS] = MC_ACS_PLUS;
 202     }
 203 
 204     // double lines
 205     success = TRUE /* just for nicer indentation */
 206         && skin_get_char (mc_skin, "dhoriz", 0x2550, &mc_tty_frm[MC_TTY_FRM_DHORIZ])
 207         && skin_get_char (mc_skin, "dvert", 0x2551, &mc_tty_frm[MC_TTY_FRM_DVERT])
 208         && skin_get_char (mc_skin, "dlefttop", 0x2554, &mc_tty_frm[MC_TTY_FRM_DLEFTTOP])
 209         && skin_get_char (mc_skin, "drighttop", 0x2557, &mc_tty_frm[MC_TTY_FRM_DRIGHTTOP])
 210         && skin_get_char (mc_skin, "dleftbottom", 0x255A, &mc_tty_frm[MC_TTY_FRM_DLEFTBOTTOM])
 211         && skin_get_char (mc_skin, "drightbottom", 0x255D, &mc_tty_frm[MC_TTY_FRM_DRIGHTBOTTOM])
 212         && skin_get_char (mc_skin, "dtopmiddle", 0x2564, &mc_tty_frm[MC_TTY_FRM_DTOPMIDDLE])
 213         && skin_get_char (mc_skin, "dbottommiddle", 0x2567, &mc_tty_frm[MC_TTY_FRM_DBOTTOMMIDDLE])
 214         && skin_get_char (mc_skin, "dleftmiddle", 0x255F, &mc_tty_frm[MC_TTY_FRM_DLEFTMIDDLE])
 215         && skin_get_char (mc_skin, "drightmiddle", 0x2562, &mc_tty_frm[MC_TTY_FRM_DRIGHTMIDDLE]);
 216 
 217     // if any of them are unavailable, revert all of them to their single counterpart
 218     if (!success)
 219     {
 220         mc_tty_frm[MC_TTY_FRM_DHORIZ] = mc_tty_frm[MC_TTY_FRM_HORIZ];
 221         mc_tty_frm[MC_TTY_FRM_DVERT] = mc_tty_frm[MC_TTY_FRM_VERT];
 222         mc_tty_frm[MC_TTY_FRM_DLEFTTOP] = mc_tty_frm[MC_TTY_FRM_LEFTTOP];
 223         mc_tty_frm[MC_TTY_FRM_DRIGHTTOP] = mc_tty_frm[MC_TTY_FRM_RIGHTTOP];
 224         mc_tty_frm[MC_TTY_FRM_DLEFTBOTTOM] = mc_tty_frm[MC_TTY_FRM_LEFTBOTTOM];
 225         mc_tty_frm[MC_TTY_FRM_DRIGHTBOTTOM] = mc_tty_frm[MC_TTY_FRM_RIGHTBOTTOM];
 226         mc_tty_frm[MC_TTY_FRM_DTOPMIDDLE] = mc_tty_frm[MC_TTY_FRM_TOPMIDDLE];
 227         mc_tty_frm[MC_TTY_FRM_DBOTTOMMIDDLE] = mc_tty_frm[MC_TTY_FRM_BOTTOMMIDDLE];
 228         mc_tty_frm[MC_TTY_FRM_DLEFTMIDDLE] = mc_tty_frm[MC_TTY_FRM_LEFTMIDDLE];
 229         mc_tty_frm[MC_TTY_FRM_DRIGHTMIDDLE] = mc_tty_frm[MC_TTY_FRM_RIGHTMIDDLE];
 230     }
 231 }
 232 
 233 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */