help 18.9 KB
Newer Older
Richard Leggett's avatar
Richard Leggett committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*---------------------------------------------------------------------------*/
/* File:    c.help                                                           */
/* Purpose: Routines to deal with help strings.                              */
/* Author:  Richard Leggett                                                  */
/* History: 15-Dec-97: RML: Created.                                         */
20 21 22
/*          17-Apr-98: RML: Fixed bug with double \ tokens.                  */
/*          07-May-98: RML: Code changed to break up long words if won't fit */
/*                          on one line.                                     */
Richard Leggett's avatar
Richard Leggett committed
23 24 25 26 27 28 29 30 31
/*---------------------------------------------------------------------------*/
/* Notes:                                                                    */
/*  - Bug in Font_ScanString means we have to use Font_StringWidth instead.  */
/*---------------------------------------------------------------------------*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "swis.h"
32
#include "tboxlibs/gadgets.h"
Richard Leggett's avatar
Richard Leggett committed
33 34 35
#include "common.h"
#include "help.h"

36 37 38 39
#define DesiredAspect  3.5         /* The aspect ratio we want */
#define MaxBoxWidth    1024        /* Maximum width of a help box, in OS units */
#define BackSlash      92          /* Backslash ASCII code */
#define Flag_SplitLine 1
Richard Leggett's avatar
Richard Leggett committed
40 41 42 43 44 45 46 47 48 49

/* Global variables:
   offset[]     - an array of pointers which point to the start of each line in a help
                  message (signified by |M).
   num_strings  - the number of originally specified lines in the help message (ie. NOT the
                  number of lines the message ends up being displayed as).
   line_spacing - the height of a line of text in the help box font */

static char* offset[256];
static int   num_strings;
50
static int   line_spacing = 40; /* Temporary value - will be changed later */
Richard Leggett's avatar
Richard Leggett committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64

/*------------------------------------------------------------------------------------------*
 * return_string_length                                                                     *
 *                                                                                          *
 * Return the length, in pts, of a string.                                                  *
 *                                                                                          *
 * In: font = handle of font the string will be displayed in.                               *
 *     string -> string to find length of.                                                  *
 *                                                                                          *
 * Returns: length of string if displayed in the specified font.                            *
 *------------------------------------------------------------------------------------------*/

int return_string_length(int font, char* string)
{
65 66
    int width;
    int current_font;
Richard Leggett's avatar
Richard Leggett committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

    error_trap(_swix(Font_CurrentFont, _OUT(0), &current_font), 0);
    error_trap(_swix(Font_SetFont, _IN(0), font), 0);
    error_trap(_swix(Font_StringWidth, _INR(1,5)|_OUT(2), string, 7200000, 7200000, -1, 256, &width), 0);
    error_trap(_swix(Font_SetFont, _IN(0), current_font), 0);

    return width;
}


/*------------------------------------------------------------------------------------------*
 * count_string_lines                                                                       *
 *                                                                                          *
 * Count the number of lines a string will occupy if it is constrained to a set width.      *
 * Optionally, copy the string into another buffer, null terminating where line breaks      *
 * are necessary.                                                                           *
 *                                                                                          *
 * In: font = handle of fount string will be displayed in.                                  *
 *     string -> the string.                                                                *
 *     buffer -> output buffer for null terminated strings, or 0 just to count without      *
 *               outputting.                                                                *
 *     width = maximum width allowed, in millipoints.                                       *
 *     max_width -> the longest width of any line so far (ie. might be less than 'width').  *
 *                                                                                          *
 * Returns: number of lines the string will occupy.                                         *
 *------------------------------------------------------------------------------------------*/

94
static int count_string_lines(int font, char *string, int width, char *buffer, int *max_width, int *bytes_out, int *flags)
Richard Leggett's avatar
Richard Leggett committed
95
{
96 97 98 99 100 101 102
    char *current = string;
    char *split = 0;
    char *end = string+(strlen(string));
    int   lines = 0;
    int   more = 1;
    int   line_width;
    int   current_font;
Richard Leggett's avatar
Richard Leggett committed
103

104 105
    if (bytes_out) *bytes_out = 0;
    *flags = 0;
Richard Leggett's avatar
Richard Leggett committed
106 107 108 109 110 111

    while (more)
    {
        /* Find split and size of split section */
        error_trap(_swix(Font_CurrentFont, _OUT(0), &current_font), 0);
        error_trap(_swix(Font_SetFont, _IN(0), font), 0);
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
        error_trap(_swix(Font_StringWidth, _INR(1, 5)|_OUTR(1, 2),
                                           current,
                                           width,
                                           72000,
                                           32,
                                           end - current,
                                           &split,
                                           &line_width), 0);

        /* If a word won't fit the maximum width (split = current != end), then split word */
        if ( (split == current) && (current != end) )
        {
            error_trap(_swix(Font_StringWidth, _INR(1, 5)|_OUTR(1, 2),
                                               current,
                                               width,
                                               72000,
                                               -1,
                                               end - current,
                                               &split,
                                               &line_width), 0);
            *flags = Flag_SplitLine;
        }
Richard Leggett's avatar
Richard Leggett committed
134

135
        error_trap(_swix(Font_SetFont, _IN(0), current_font), 0);
Richard Leggett's avatar
Richard Leggett committed
136 137 138 139

        /* Copy the next portion into our output buffer (if one was specified) */
        if (buffer)
        {
140
            if (lines == 0)
Richard Leggett's avatar
Richard Leggett committed
141 142
            {
                sprintf(buffer, " ");
143 144
                buffer += 2;
                *bytes_out += 2;
Richard Leggett's avatar
Richard Leggett committed
145 146
            }
            memcpy(buffer, current, split-current);
147 148 149
            buffer[split-current] = 0;
            buffer += (split-current+1);
            *bytes_out += (split-current+1);
Richard Leggett's avatar
Richard Leggett committed
150 151 152 153
        }

        /* Keep track of the largest width string we've had so far */
        lines++;
154
        if (line_width > *max_width) *max_width = line_width;
Richard Leggett's avatar
Richard Leggett committed
155 156

        /* Check for the end of the string */
157
        if ((split >= end) || (split == current))
Richard Leggett's avatar
Richard Leggett committed
158
        {
159
            more = 0;
Richard Leggett's avatar
Richard Leggett committed
160 161 162 163
        }
        else
        {
            if (split[0]==' ') split++;
164
            current = split;
Richard Leggett's avatar
Richard Leggett committed
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
        }
    }

    return lines;
}


/*------------------------------------------------------------------------------------------*
 * break_strings_down                                                                       *
 *                                                                                          *
 * Break the help message down into null terminated strings which represent each line as it *
 * will appear on text.                                                                     *
 *                                                                                          *
 * In: font = handle of font text will be displayed in.                                     *
 *     width = width text is to fit.                                                        *
 *     buffer -> buffer to put strings.                                                     *
 *------------------------------------------------------------------------------------------*/

int break_strings_down(int font, int width, char* buffer)
{
185 186 187 188 189
    int lines = 0;
    int bytes_out = 0;
    int max_width;
    int flags;
    int n;
Richard Leggett's avatar
Richard Leggett committed
190 191 192

    for (n=0; n<num_strings; n++)
    {
193 194
        lines += count_string_lines(font, offset[n], width, buffer, &max_width, &bytes_out, &flags);
        buffer += bytes_out;
Richard Leggett's avatar
Richard Leggett committed
195 196 197 198 199 200 201 202 203 204 205 206
    }

    return lines;
}


/*------------------------------------------------------------------------------------------*
 * a_nearer_to_c_than_b                                                                     *
 *                                                                                          *
 * If a is nearer to c than b, return 1, else 0.                                            *
 *------------------------------------------------------------------------------------------*/

207
static int a_nearer_to_c_than_b(double a, double b, double c)
Richard Leggett's avatar
Richard Leggett committed
208
{
209 210
    double ad;
    double bd;
Richard Leggett's avatar
Richard Leggett committed
211

212 213
    ad = c - a; if (ad<0) ad = -ad;
    bd = c - b; if (bd<0) bd = -bd;
Richard Leggett's avatar
Richard Leggett committed
214

215
    if (ad < bd) return 1;
Richard Leggett's avatar
Richard Leggett committed
216 217 218 219 220 221

    return 0;
}


/*------------------------------------------------------------------------------------------*
222
 * count_occurrences                                                                        *
Richard Leggett's avatar
Richard Leggett committed
223 224 225 226 227 228 229 230
 *                                                                                          *
 * Count how many times a string occurs within another string.                              *
 *                                                                                          *
 * In: string -> string in which other string may occur.                                    *
 *     count_string -> string whose occurrance we wish to count.                            *
 *                                                                                          *
 * Returns: number of times the string occurs.                                              *
 *------------------------------------------------------------------------------------------*/
231 232
#if 0
static int count_occurrences(char *string, char* count_string)
Richard Leggett's avatar
Richard Leggett committed
233
{
234 235
    char *next = string;
    int   lines = 1;
Richard Leggett's avatar
Richard Leggett committed
236

237
    while (next != 0)
Richard Leggett's avatar
Richard Leggett committed
238
    {
239
        next = strstr(next, count_string);
Richard Leggett's avatar
Richard Leggett committed
240 241 242 243 244
        if (next++) lines++;
    }

    return lines;
}
245
#endif
Richard Leggett's avatar
Richard Leggett committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261


/*------------------------------------------------------------------------------------------*
 * translate_help_string                                                                    *
 *                                                                                          *
 * Take a help string, de-tokenise and fill offset[] with pointers to each line of text (as *
 * specified in the help string by |M).                                                     *
 *                                                                                          *
 * In: help_string -> initial string                                                        *
 *     new_string -> translated string                                                      *
 *                                                                                          *
 * Globals: offset[], num_strings                                                           *
 *------------------------------------------------------------------------------------------*/

int translate_help_string(char* help_string, char* new_string, int length)
{
262 263 264
    char token[4];
    int  m = 0;
    int  n;
Richard Leggett's avatar
Richard Leggett committed
265

266 267
    num_strings = 0;
    offset[num_strings++] = new_string;
Richard Leggett's avatar
Richard Leggett committed
268 269 270 271

    /* Loop through help string searching for tokens (\T) and new lines */
    for (n=0; n<length; n++)
    {
272 273
        /* Turn character 13s (NL) into 0s and store the offset */
        if ( (help_string[n] == 13) && (n<length-1) )
Richard Leggett's avatar
Richard Leggett committed
274
        {
275 276
            new_string[m++] = 0;
            if (help_string[n+1] == 13) break;
Richard Leggett's avatar
Richard Leggett committed
277 278 279
            offset[num_strings++] = new_string+m;
        }

280 281
        /* Tokens: Check for '\T' where T is a character and \T is a token */
        else if (help_string[n] == BackSlash)
Richard Leggett's avatar
Richard Leggett committed
282
        {
283 284 285 286 287
            /* Check for '\\', which becomes '\' */
            if (help_string[n+1] == BackSlash)
            {
                new_string[m++] = BackSlash;
            }
Richard Leggett's avatar
Richard Leggett committed
288

289 290 291 292 293 294 295 296 297
            /* Any other token */
            else
            {
                token[0] = 'T';
                token[1] = help_string[n+1];
                token[2] = 0;
                strcpy(new_string+m, messages_lookup(token));
                m+=strlen(new_string+m);
            }
Richard Leggett's avatar
Richard Leggett committed
298 299 300 301 302 303 304 305 306 307 308

            new_string[m] = 0;
            n++;
        }

        /* Otherwise, it's a normal character */
        else
        {
            if (help_string[n] >= ' ') new_string[m++]=help_string[n];
        }
    }
309 310 311

    /* Terminate the string */
    new_string[m] = 0;
Richard Leggett's avatar
Richard Leggett committed
312 313 314 315 316 317 318 319 320 321 322 323 324

    return num_strings;
}


/*------------------------------------------------------------------------------------------*
 * try_fitting_to_whole_line                                                                *
 *                                                                                          *
 * The first of two attempts to choose box size. This takes each line in turn and uses it's *
 * width to try to fit the other lines. The size of the resultant box is calculated and     *
 * from that, it's aspect ratio. The best aspect ratio is kept track of.                    *
 *------------------------------------------------------------------------------------------*/

325
static int try_fitting_to_whole_line(int font_handle, double* return_aspect, int* return_width, int* return_lines)
Richard Leggett's avatar
Richard Leggett committed
326
{
327 328 329 330 331 332 333 334 335 336 337 338 339
    double aspect = -200;
    int    max_width;
    int    largest;
    int    height;
    int    width;
    int    lines;
    int    more;
    int    flags;
    int    split_line;
    int    return_split = 1;
    int    nearer;
    int    n;
    int    m;
Richard Leggett's avatar
Richard Leggett committed
340 341 342 343 344 345 346 347 348 349 350

    _swix(Font_Converttopoints, _INR(1,2)|_OUT(1), MaxBoxWidth, 0, &max_width);

    for (n=0; n<num_strings; n++)
    {
        lines = 0;
        largest = 0;
        width = return_string_length(font_handle, offset[n]);
        if (width>max_width) width=max_width;

        /* How many lines will be used for this width? */
351
        split_line = 0;
Richard Leggett's avatar
Richard Leggett committed
352 353
        for (m=0; m<num_strings; m++)
        {
354 355 356
            more = count_string_lines(font_handle, offset[m], width, 0, &largest, 0, &flags);
            if (flags & Flag_SplitLine) split_line++;
            lines += more;
Richard Leggett's avatar
Richard Leggett committed
357 358 359 360 361 362 363
        }

        /* Check if this aspect ratio is any better than our best yet */
        if (lines != -1)
        {
            _swix(Font_Converttopoints, _INR(1,2)|_OUT(1), lines*line_spacing, 0, &height);
            aspect = (double)largest / (double)height;
364
            nearer = a_nearer_to_c_than_b(aspect, *return_aspect, DesiredAspect);
Richard Leggett's avatar
Richard Leggett committed
365

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
            /* First of all, if this new fit involved splitting the line, then we only say this is
               the best fit if:
                     a) The current best fit was a split.
                 AND b) The new best fit is nearer */
            if (split_line)
            {
                if (return_split && nearer)
                {
                    *return_width = largest;
                    *return_lines = lines;
                    *return_aspect = aspect;
                    return_split = 1;
                }
            }
            /* Otherwise, if the new fit did NOT involve splitting the line, then we say this is the
               best fit if:
                   a) The current best fit was a split.
                OR b) The new best fit is nearer */
            else
Richard Leggett's avatar
Richard Leggett committed
385
            {
386 387 388 389 390 391 392
                if (return_split || nearer)
                {
                    *return_width = largest;
                    *return_lines = lines;
                    *return_aspect = aspect;
                    return_split = 0;
                }
Richard Leggett's avatar
Richard Leggett committed
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
            }
        }
    }

    return 1;
}


/*------------------------------------------------------------------------------------------*
 * choose_box_width                                                                         *
 *                                                                                          *
 * Choose the width of the help text box, based on the string to be displayed. The help     *
 * text will then be word-wrapped to this width. The function below is quite convuluted     *
 * and could probably be replaced with a much simpler algorithm.                            *
 *                                                                                          *
 * In: help_string -> help text.                                                            *
 *     font_handle -> handle of font that text will be displayed in.                        *
 *                                                                                          *
 * Returns: Width to constrain text to.                                                     *
 *------------------------------------------------------------------------------------------*/

int choose_box_width(int font_handle, int* box_width, int* box_height)
{
416 417 418
    double aspect = -100;
    int    width = MaxBoxWidth;
    int    lines = num_strings;
Richard Leggett's avatar
Richard Leggett committed
419 420 421

    try_fitting_to_whole_line(font_handle, &aspect, &width, &lines);

422
    *box_height = lines * line_spacing;
Richard Leggett's avatar
Richard Leggett committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
    error_trap(_swix(Font_ConverttoOS, _INR(1,2)|_OUT(1), width, 0, box_width), 0);
    *box_height += 16;
    *box_width += 44;

    return width;
}


/*------------------------------------------------------------------------------------------*
 * help_font_initialise                                                                     *
 *                                                                                          *
 * Set up a few font bits and pieces.                                                       *
 *------------------------------------------------------------------------------------------*/

int help_font_initialise(char* fontname, int fontsize, int* ls)
{
439 440 441
    int font_handle;
    int ymin;
    int ymax;
Richard Leggett's avatar
Richard Leggett committed
442 443

    error_trap(_swix(Font_FindFont, _INR(1,5)|_OUT(0), fontname,
444 445 446 447
                                                       fontsize * 16,
                                                       fontsize * 16,
                                                       0,
                                                       0,
Richard Leggett's avatar
Richard Leggett committed
448 449 450
                                                       &font_handle), 0);

    error_trap(_swix(Font_ReadInfo, _IN(0)|_OUT(2)|_OUT(4), font_handle, &ymin, &ymax), 0);
451

Richard Leggett's avatar
Richard Leggett committed
452 453 454 455 456
    line_spacing = ymax-ymin;
    *ls = line_spacing;

    return font_handle;
}