mc/xv/xvhelp.c

603 lines
14 KiB
C

/* XView help viewer routines.
Copyright (C) 1995 Jakub Jelinek.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <config.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include <xview/xview.h>
#include <xview/frame.h>
#include <xview/panel.h>
#include <xview/svrimage.h>
#include <xview/canvas.h>
#include <xview/scrollbar.h>
#include <xview/cms.h>
#include <xview/xv_xrect.h>
#include "help.h"
#include "../src/mad.h"
#include "../src/util.h"
#include "xvmain.h"
#include "help.icons"
#define TOPMARGIN 10
#define LEFTMARGIN 10
#define RIGHTMARGIN 10
#define BOTTOMMARGIN 10
extern Frame mcframe;
extern Server_image mcimage; /* xvinfo.c */
Frame helpframe = XV_NULL;
Panel helppanel;
Canvas helpcanvas;
Cms helpcms;
extern Display *dpy;
typedef struct {
Xv_Font font;
GC gc;
int height;
} fontstruct;
extern char *currentpoint, *startpoint;
static void repaint (void);
static void clear_link_areas (void);
static void x_index_cmd (void)
{
help_index_cmd ();
repaint ();
}
static void x_back_cmd (void)
{
}
static void x_previous_cmd (void)
{
}
static void x_next_cmd (void)
{
}
static void x_search_cmd (void)
{
}
static void x_help_on_help (void)
{
help_help_cmd ();
repaint ();
}
static void x_quit_cmd (void)
{
xv_destroy_safe (helpframe);
clear_link_areas ();
helpframe = XV_NULL;
interactive_display_finish ();
xv_dispatch_a_bit ();
}
fontstruct getfont (char *name, int fg)
{
fontstruct f;
XGCValues gcvalues;
f.font = (Xv_Font) xv_find (helpcanvas, FONT, FONT_NAME, name, NULL);
if (f.font == XV_NULL)
f.font = (Xv_Font) xv_get (helpframe, XV_FONT);
gcvalues.font = (Font) xv_get (f.font, XV_XID);
gcvalues.foreground = (unsigned long) xv_get (helpcms, CMS_PIXEL, fg);
gcvalues.background = (unsigned long) xv_get (helpcms, CMS_BACKGROUND_PIXEL);
gcvalues.graphics_exposures = FALSE;
f.gc = XCreateGC (dpy, RootWindow (dpy, DefaultScreen (dpy)),
GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcvalues);
f.height = xv_get (f.font, FONT_DEFAULT_CHAR_HEIGHT);
return f;
}
static char buffer [200];
int x, h, y;
int bufferindex;
fontstruct fonormal, fobold, folink, fotitle;
#define NORMAL &fonormal
#define BOLD &fobold
#define LINK &folink
#define TITLE &fotitle
fontstruct *curfont, *linefont, *wordfont;
int startup;
int paintwidth;
Window xId;
char *linebegin, *wordbegin;
int tabsize = 30;
struct area;
typedef struct area *AREA;
struct area {
int wx; /* West x, Northwest y, Southwest y etc. */
int nwy;
int swy;
int ex;
int ney;
int sey;
char *jump;
AREA next;
};
AREA firstarea = NULL, lastarea = NULL;
int do_link_areas = 1;
static int painting = 1, linepainting = 1;
static void clear_link_areas (void)
{
AREA p;
while (firstarea != NULL) {
p = firstarea;
firstarea = firstarea->next;
free (p);
}
lastarea = NULL;
}
static void start_link_area (char *link, int test)
{
AREA p;
if (test || !do_link_areas)
return;
p = (AREA) xmalloc (sizeof (struct area), "Help link area");
if (firstarea != NULL)
lastarea->next = p;
else
firstarea = p;
lastarea = p;
p->wx = x;
p->nwy = y - h;
p->swy = y;
p->ex = x;
p->ney = y - h;
p->sey = y;
p->jump = link;
p->next = NULL;
}
static void end_link_area (int test)
{
if (test || !do_link_areas || !lastarea)
return;
lastarea->ex = x;
lastarea->ney = y - h;
lastarea->sey = y;
}
static AREA is_in_area (int x, int y)
{
AREA p;
for (p = firstarea; p != (AREA) NULL; p = p->next) {
if (y < p->nwy || y >= p->sey)
continue;
if (y < p->swy) {
if (x < p->wx)
continue;
if (p->nwy == p->ney && x >= p->ex)
continue;
} else if (y >= p->ney) {
if (x >= p->ex)
continue;
}
return p;
}
return NULL;
}
static void hparse (char *paint_start, int test);
static void setfont (fontstruct *f)
{
curfont = f;
}
static void newline (void)
{
x = LEFTMARGIN;
y += h;
curfont = linefont;
hparse (linebegin, 0);
h = 0;
x = LEFTMARGIN;
linepainting = painting;
linebegin = wordbegin;
linefont = curfont;
}
static int flush (char *p, int test)
{
Font_string_dims dims;
buffer [bufferindex] = 0;
xv_get (curfont->font, FONT_STRING_DIMS, buffer, &dims);
if (x + dims.width > paintwidth - RIGHTMARGIN && x) {
if (test)
newline ();
else
return 1;
}
if (!test) {
XDrawString (dpy, xId, curfont->gc, x, y, buffer, strlen (buffer));
}
x += dims.width;
if (dims.height > h)
h = dims.height;
wordbegin = p + 1;
bufferindex = 0;
return 0;
}
static void hparse (char *paint_start, int test)
{
char *p;
int c;
if (test) {
painting = 1;
linepainting = 1;
} else
painting = linepainting;
if (test)
setfont (NORMAL);
h = 0;
x = LEFTMARGIN;
bufferindex = 0;
if (test) {
wordbegin = paint_start;
linebegin = paint_start;
linefont = curfont;
}
for (p = paint_start; *p != CHAR_NODE_END; p++){
c = *p;
switch (c){
case CHAR_LINK_START:
if (flush (p, test))
return;
setfont (LINK);
start_link_area (p, test);
break;
case CHAR_LINK_POINTER:
if (flush (p, test))
return;
setfont (NORMAL);
painting = 0;
end_link_area (test);
break;
case CHAR_LINK_END:
painting = 1;
break;
case CHAR_ALTERNATE:
break;
case CHAR_NORMAL:
break;
case CHAR_VERSION:
strcpy (buffer + bufferindex, VERSION);
bufferindex += strlen (VERSION);
break;
case CHAR_BOLD_ON:
if (flush (p, test))
return;
setfont (BOLD);
break;
case CHAR_BOLD_OFF:
if (flush (p, test))
return;
setfont (NORMAL);
break;
case CHAR_MCLOGO:
{
int mcimage_width = (int) xv_get (mcimage, XV_WIDTH);
int mcimage_height = (int) xv_get (mcimage, XV_HEIGHT);
if (flush (p, test))
return;
if (!test) {
XCopyArea (dpy,
(Drawable) xv_get (mcimage, SERVER_IMAGE_PIXMAP),
xId, curfont->gc, 0,0, mcimage_width, mcimage_height,
x, y - mcimage_height);
}
x += mcimage_width;
if (h < mcimage_height)
h = mcimage_height;
break;
}
case CHAR_TEXTONLY_START:
while (*p && *p != CHAR_NODE_END && *p != CHAR_TEXTONLY_END)
p++;
if (*p != CHAR_TEXTONLY_END)
p--;
break;
case CHAR_XONLY_START:
case CHAR_XONLY_END:
break;
case '\n':
if (p [1] == '\n') {
if (flush (p, test))
return;
if (x > LEFTMARGIN) {
if (!test)
return;
else
newline ();
}
break;
}
case ' ':
buffer [bufferindex++] = ' ';
if (flush (p, test))
return;
break;
case '\t':
if (flush (p, test))
return;
x = (x + tabsize - LEFTMARGIN) / tabsize * tabsize + LEFTMARGIN;
if (x > paintwidth - RIGHTMARGIN) {
if (!test)
return;
else
newline ();
x = tabsize + LEFTMARGIN;
}
break;
default:
if (!painting)
continue;
buffer [bufferindex++] = c;
break;
}
}
if (flush (p, test))
return;
if (x > LEFTMARGIN) {
if (!test)
return;
else
newline ();
}
}
static void canvas_repaint (Canvas canvas, Xv_Window paint)
{
if (startup)
return;
xId = (Window) xv_get (paint, XV_XID);
y = TOPMARGIN;
paintwidth = (int) xv_get (xv_get (paint, CANVAS_PAINT_VIEW_WINDOW), XV_WIDTH);
XClearWindow (dpy, xId);
hparse (startpoint, 1);
XFlush (dpy);
}
static void canvas_resize (Canvas canvas, int width, int height)
{
if (startup)
return;
if (width != paintwidth)
repaint ();
}
static void repaint (void)
{
Xv_Window paint, view;
Scrollbar scroll;
clear_link_areas ();
do_link_areas = 1;
OPENWIN_EACH_VIEW (helpcanvas, view)
paint = (Xv_Window) xv_get (view, CANVAS_VIEW_PAINT_WINDOW);
canvas_repaint (helpcanvas, paint);
if (do_link_areas) {
do_link_areas = 0;
xv_set (helpcanvas,
CANVAS_MIN_PAINT_HEIGHT, y + BOTTOMMARGIN,
NULL);
}
scroll = (Scrollbar) xv_get (helpcanvas,
OPENWIN_VERTICAL_SCROLLBAR, view);
if (scroll != XV_NULL) {
xv_set (scroll,
SCROLLBAR_VIEW_START, 0,
NULL);
xv_set (scroll,
SCROLLBAR_OBJECT_LENGTH, y + BOTTOMMARGIN,
NULL);
}
OPENWIN_END_EACH
}
static struct {
unsigned short *bits;
void (*callback) (void);
} buttons [] = {
index_bits, x_index_cmd,
back_bits, x_back_cmd,
previous_bits, x_previous_cmd,
next_bits, x_next_cmd,
search_bits, x_search_cmd };
Cursor cursors [2];
static void event_handler (Xv_Window paint, Event *event)
{
int x, y;
AREA p;
static int current_cursor = 0;
switch (event_action (event)) {
case ACTION_SELECT:
x = event_x (event);
y = event_y (event);
p = is_in_area (x, y);
if (p != NULL && p->jump != NULL) {
currentpoint = startpoint = help_follow_link (startpoint, p->jump);
repaint ();
}
break;
case LOC_MOVE:
x = event_x (event);
y = event_y (event);
p = is_in_area (x, y);
if (p != NULL) {
if (!current_cursor) {
XDefineCursor (dpy, xv_get (paint, XV_XID), cursors [1]);
current_cursor = 1;
}
} else {
if (current_cursor) {
XDefineCursor (dpy, xv_get (paint, XV_XID), cursors [0]);
current_cursor = 0;
}
}
break;
}
}
static void create_frame (void)
{
Panel_button_item button;
int i;
helpcms = (Cms) xv_create (XV_NULL, CMS,
CMS_CONTROL_CMS, TRUE,
CMS_SIZE, CMS_CONTROL_COLORS + 5,
CMS_NAMED_COLORS, "black", "black", "green4", "red4", "black", NULL,
NULL);
helpframe = xv_create (mcframe, FRAME,
FRAME_LABEL, "Midnight X Commander Help",
WIN_CMS, helpcms,
FRAME_INHERIT_COLORS, TRUE,
NULL);
helppanel = xv_create (helpframe, PANEL,
NULL);
xv_create (helppanel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Help",
PANEL_ITEM_MENU, xv_create (XV_NULL, MENU,
MENU_ITEM,
MENU_STRING, "Help on Help",
MENU_NOTIFY_PROC, x_help_on_help,
NULL,
MENU_ITEM,
MENU_STRING, "Index",
MENU_NOTIFY_PROC, x_index_cmd,
NULL,
MENU_ITEM,
MENU_STRING, "Back",
MENU_NOTIFY_PROC, x_back_cmd,
NULL,
MENU_ITEM,
MENU_STRING, "Previous",
MENU_NOTIFY_PROC, x_previous_cmd,
NULL,
MENU_ITEM,
MENU_STRING, "Next",
MENU_NOTIFY_PROC, x_next_cmd,
NULL,
MENU_ITEM,
MENU_STRING, "Search",
MENU_NOTIFY_PROC, x_search_cmd,
NULL,
NULL),
NULL);
button = (Panel_button_item) xv_create (helppanel, PANEL_BUTTON,
PANEL_LABEL_STRING, "Quit",
PANEL_NOTIFY_PROC, x_quit_cmd,
NULL);
for (i = 0; i < sizeof (buttons) / sizeof (buttons [0]); i++) {
button = (Panel_button_item) xv_create (helppanel, PANEL_BUTTON,
PANEL_LABEL_IMAGE, xv_create (XV_NULL, SERVER_IMAGE,
SERVER_IMAGE_DEPTH, 1,
XV_WIDTH, 48,
XV_HEIGHT, 48,
SERVER_IMAGE_BITS, buttons [i].bits,
NULL),
PANEL_NOTIFY_PROC, buttons [i].callback,
NULL);
if (!i)
xv_set (button,
PANEL_NEXT_ROW, -1,
NULL);
}
window_fit (helppanel);
xv_set (helpframe,
XV_WIDTH, xv_get (helppanel, XV_WIDTH),
NULL);
xv_set (helppanel,
XV_WIDTH, WIN_EXTEND_TO_EDGE,
NULL);
startup = 1;
helpcanvas = xv_create (helpframe, CANVAS,
WIN_BELOW, helppanel,
CANVAS_REPAINT_PROC, canvas_repaint,
CANVAS_RESIZE_PROC, canvas_resize,
CANVAS_X_PAINT_WINDOW, FALSE,
CANVAS_PAINTWINDOW_ATTRS,
WIN_CONSUME_EVENTS,
WIN_MOUSE_BUTTONS, LOC_MOVE, NULL,
WIN_EVENT_PROC, event_handler,
NULL,
NULL);
cursors [0] = (Cursor) xv_get (xv_get (helpcanvas, WIN_CURSOR), XV_XID);
cursors [1] = XCreateFontCursor (dpy, XC_hand2);
xv_create (helpcanvas, SCROLLBAR,
SCROLLBAR_DIRECTION, SCROLLBAR_VERTICAL,
SCROLLBAR_SPLITTABLE, TRUE,
NULL);
fonormal = getfont ("lucidasans-12", CMS_CONTROL_COLORS);
fobold = getfont ("lucidasans-bold-12", CMS_CONTROL_COLORS + 1);
folink = getfont ("lucidasans-12", CMS_CONTROL_COLORS + 2);
fotitle = getfont ("lucidasans-bold-18", CMS_CONTROL_COLORS + 3);
window_fit (helpframe);
xv_set (helpframe,
XV_SHOW, TRUE,
NULL);
startup = 0;
repaint ();
}
void x_interactive_display (void)
{
if (helpframe == XV_NULL)
create_frame ();
}