uifcfltk.cpp 34.11 KiB
/* uifcfltk.c */
/* X/Windows Implementation of UIFC (user interface) library */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2002 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* 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. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
/*********************************************/
/* UIFC Defines specific to the FLTK version */
/*********************************************/
#define MAX_POPUPS 10 // Maximum number of popup()s
#define MAX_WINDOWS 100 // Maximum number of list()s
#define UIFC_CANCEL -1 // Return value for Cancel
#define UIFC_MENU -2 // Keep looping value
#define UIFC_INPUT -3 // Input Box
#define UIFC_HELP -4 // Return value for Help
#define UIFC_RCLICK -5 // Right-click
/***********************
* Fast Light Includes *
***********************/
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input_.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Box.H>
#include <FL/fl_ask.H> // fl_beep(), fl_message()
/******************/
/* Layout Defines */
/******************/
#define UIFC_CHAR_WIDTH 8 // Width of one "Character" (Assumes 80x25)
#define UIFC_CHAR_HEIGHT 12 // Height of one Character
#define UIFC_LINE_HEIGHT 16 // Height of one "Line"
#define UIFC_BORDER_WIDTH 2 // Width of a single border
#define UIFC_SCROLL_WIDTH 18 // Width of the scroll bar
#define UIFC_MAIN_BG_COLOR FL_DARK_CYAN
#define UIFC_BORDER_COLOR FL_YELLOW
#define UIFC_BUTTON_TEXT_COLOR FL_WHITE
#define UIFC_BUTTON_COLOR FL_DARK_BLUE
#define UIFC_BUTTON_SELECTED_COLOR FL_BLUE
#define UIFC_WINDOW_TITLE_COLOR FL_YELLOW
#define UIFC_INPUT_WIN_BG_COLOR FL_DARK_BLUE
#define UIFC_INPUT_TEXT_COLOR FL_WHITE
#define UIFC_INPUT_BOX_BG_COLOR FL_DARK_BLUE
#define UIFC_INPUT_PROMPT_COLOR FL_YELLOW
#define UIFC_HELP_PLAIN_COLOR FL_WHITE
#define UIFC_HELP_HIGH_COLOR FL_YELLOW
#define UIFC_HELP_INVERSE_COLOR FL_CYAN
#define UIFC_HELP_BOTH_COLOR FL_MAGENTA
#define UIFC_HELP_WIN_BG_COLOR FL_DARK_BLUE
#define UIFC_LIST_WIN_BG_COLOR FL_DARK_BLUE
#define UIFC_FOREGROUND 255,255,0
#define UIFC_BACKGROUND 0,64,64
#define UIFC_BACKGROUND2 0,0,255
#include <sys/types.h>
#include "uifc.h"
#ifdef __unix__
#include <unistd.h>
#else
#include <stdio.h>
#endif
static char *helpfile=0;
static uint helpline=0;
static uifcapi_t* api;
/* Typedefs */
typedef struct {
int mode;
int max;
Fl_Input_ *InputBox;
} modes_t;
/* Prototypes */
static void help(void);
static void GenCallback(Fl_Widget *, void *);
static void LBCallback(Fl_Widget *, void *);
/* API routines */
static void uifcbail(void);
static int uscrn(char *str);
static int ulist(int mode, int left, int top, int width, int *dflt, int *bar
,char *title, char **option);
static int uinput(int imode, int left, int top, char *prompt, char *str
,int len ,int kmode);
static void umsg(char *str);
static void upop(char *str);
static void sethelp(int line, char* file);
/* Interal routines */
void delwin(int WinNum);
/* Classes */
class UIFC_PopUp : public Fl_Double_Window {
int handle(int);
public:
UIFC_PopUp(int w,int h,const char *title) : Fl_Double_Window(w,h,title) {
border(FALSE);
box(FL_UP_BOX);
set_modal();
}
};
class UIFC_Button : public Fl_Button {
char uifc_label[MAX_OPLN+1];
public:
int retval;
int mode;
int handle(int);
UIFC_Button(int x,int y,int w,int h,const char *label) : Fl_Button(x,y,w,h,label) {
if(label==NULL)
strcpy(uifc_label,"");
else
strcpy(uifc_label,label);
this->label(uifc_label);
labelfont(FL_COURIER);
labelsize(UIFC_CHAR_HEIGHT);
when(FL_WHEN_RELEASE|FL_WHEN_CHANGED);
box(FL_FLAT_BOX);
if(!(api->mode&UIFC_MONO)) {
color(UIFC_BUTTON_COLOR,UIFC_BUTTON_SELECTED_COLOR);
labelcolor(UIFC_BUTTON_TEXT_COLOR);
}
}
};
class UIFC_Window : public Fl_Double_Window {
public:
UIFC_Window(int x,int y,int w,int h,const char *title) : Fl_Double_Window(x,y,w,h,title) {
box(FL_UP_BOX);
if(!(api->mode&UIFC_MONO))
color(UIFC_LIST_WIN_BG_COLOR);
UIFC_Button *HButton = new UIFC_Button(
w-(UIFC_LINE_HEIGHT*2)+UIFC_BORDER_WIDTH*3,
UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT-(UIFC_BORDER_WIDTH*2),
UIFC_LINE_HEIGHT-(UIFC_BORDER_WIDTH*2),
"?");
HButton->box(FL_NO_BOX);
HButton->labelfont(FL_SCREEN);
HButton->labelsize(UIFC_LINE_HEIGHT-(UIFC_BORDER_WIDTH*4));
HButton->callback(GenCallback);
HButton->retval=UIFC_HELP;
HButton->when(FL_WHEN_CHANGED);
if(!(api->mode&UIFC_MONO)) {
HButton->color(FL_BLUE,FL_BLUE);
HButton->labelcolor(UIFC_WINDOW_TITLE_COLOR);
}
UIFC_Button *CButton = new UIFC_Button(
w-(UIFC_LINE_HEIGHT)+UIFC_BORDER_WIDTH,
UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT-(UIFC_BORDER_WIDTH*2),
UIFC_LINE_HEIGHT-(UIFC_BORDER_WIDTH*2),
"X");
CButton->box(FL_NO_BOX);
CButton->labelfont(FL_SCREEN);
CButton->labelsize(UIFC_LINE_HEIGHT-(UIFC_BORDER_WIDTH*4));
CButton->callback(GenCallback);
CButton->retval=UIFC_CANCEL;
CButton->when(FL_WHEN_CHANGED);
if(!(api->mode&UIFC_MONO)) {
CButton->color(FL_BLUE,FL_BLUE);
CButton->labelcolor(UIFC_WINDOW_TITLE_COLOR);
}
}
};
class UIFC_Menu : public UIFC_Window {
public:
int *uifc_id;
UIFC_Menu(int x,int y,int w,int h,const char *title,int mode,int opts,int *cur,char **option) : UIFC_Window(x,y,w,h,title) {
UIFC_Button *Button;
Fl_Scroll *SGroup;
int opt=0;
int scrolloffset=0;
uifc_id=cur;
if(opts>20)
scrolloffset=UIFC_SCROLL_WIDTH;
SGroup = new Fl_Scroll(
UIFC_CHAR_WIDTH-UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT-UIFC_BORDER_WIDTH,
w-(2*UIFC_CHAR_WIDTH)+(UIFC_BORDER_WIDTH*2),
UIFC_LINE_HEIGHT*(((opts<20)?opts:20))+(UIFC_BORDER_WIDTH*4),
title);
SGroup->box(FL_DOWN_BOX);
SGroup->labelfont(FL_COURIER_BOLD);
SGroup->labelsize(UIFC_CHAR_HEIGHT);
if(!(api->mode&UIFC_MONO)) {
SGroup->labelcolor(UIFC_WINDOW_TITLE_COLOR);
SGroup->color(UIFC_BORDER_COLOR);
}
for(opt=0;option[opt][0];opt++) {
Button=new UIFC_Button(
UIFC_CHAR_WIDTH+UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT*(opt+1)+UIFC_BORDER_WIDTH,
w-(2*UIFC_CHAR_WIDTH)-(2*UIFC_BORDER_WIDTH)-scrolloffset,
UIFC_LINE_HEIGHT,
option[opt]);
Button->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CENTER);
Button->callback(LBCallback);
Button->retval=opt;
Button->mode=mode;
}
if((mode&WIN_XTR)&&(mode&WIN_INS)) {
Button=new UIFC_Button(
UIFC_CHAR_WIDTH+UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT*(opt+1)+UIFC_BORDER_WIDTH,
w-(2*UIFC_CHAR_WIDTH)-(2*UIFC_BORDER_WIDTH)-scrolloffset,
UIFC_LINE_HEIGHT,
NULL);
Button->callback(LBCallback);
Button->retval=opt|MSK_INS;
Button->mode=mode;
opt++;
}
}
};
class UIFC_Input_Box : public Fl_Input_ {
int handle(int);
void draw();
int handle_key();
int shift_position(int);
public:
int mode;
int max;
UIFC_Input_Box(int x,int y,int w,int h,const char *title,int inmode,int inmax,char *outval) : Fl_Input_(x,y,w,h,title) {
mode=inmode;
max=inmax;
if(mode&K_EDIT)
value(outval);
else
value("");
position(strlen(value()));
maximum_size(max);
if(!(api->mode&UIFC_MONO)) {
labelcolor(UIFC_INPUT_PROMPT_COLOR);
color(UIFC_INPUT_BOX_BG_COLOR);
textcolor(UIFC_INPUT_TEXT_COLOR);
}
}
};
class UIFC_Input : public UIFC_Window {
public:
char invalue[256];
UIFC_Input(int x,int y,int w,int h,const char *title,int inmode,int inmax,int len,int width,char *outval) : UIFC_Window(x,y,w,h,title) {
box(FL_UP_BOX);
new UIFC_Input_Box(
UIFC_CHAR_WIDTH*(len+2),
UIFC_LINE_HEIGHT,
(width)*UIFC_CHAR_WIDTH,
UIFC_LINE_HEIGHT,
title,
inmode,
inmax,
outval);
int button_left=w/2-(UIFC_CHAR_WIDTH*10);
UIFC_Button *OkButton = new UIFC_Button(
button_left,
UIFC_LINE_HEIGHT*3,
(8)*UIFC_CHAR_WIDTH,
UIFC_LINE_HEIGHT,
"Ok");
OkButton->callback(GenCallback);
OkButton->retval=0;
UIFC_Button *CancelButton=new UIFC_Button(
button_left+UIFC_CHAR_WIDTH*10,
UIFC_LINE_HEIGHT*3,
(10)*UIFC_CHAR_WIDTH
,UIFC_LINE_HEIGHT
,"Cancel");
CancelButton->retval=UIFC_CANCEL;
CancelButton->shortcut(FL_Escape);
CancelButton->callback(GenCallback);
if(inmode&K_EDIT)
snprintf(invalue,inmax,"%s",outval);
else
invalue[0]=0;
}
};
/* Globals */
Fl_Double_Window *MainWin;
int GUI_RetVal;
UIFC_Menu *Windows[MAX_WINDOWS]; // Array of windows for ulist
int CurrWin; // Current window in array.
int *uifc_cur=NULL; // Current selection.
/*****************************/
/* UIFC_Button ENTER catcher */
/*****************************/
int UIFC_Button::handle(int event) {
if (event == FL_FOCUS) {
if(uifc_cur != NULL)
*uifc_cur=retval&MSK_OFF;
if(!(api->mode&UIFC_MONO))
color(UIFC_BUTTON_SELECTED_COLOR);
Fl_Scroll *s=(Fl_Scroll *)parent();
/******************************************************************************
* This works, it will always work if there is vertical room for at least one *
* button in the scroll box. For gods sake, don't touch this unless you KNOW *
* how it works, and can do it better. *
******************************************************************************/
if(s->type()==Fl_Scroll::BOTH) {
if(y() + h() + Fl::box_dy(box()) + Fl::box_dh(box()) > s->y()+Fl::box_dy(s->box())+s->h()-Fl::box_dh(s->box())) {
// Scroll Down
s->position(0,s->yposition()-(s->y() + Fl::box_dy(s->box()) + s->h() - Fl::box_dh(s->box()) - y() - h() - Fl::box_dy(box()) - Fl::box_dh(box())));
}
if(y() < s->y()+Fl::box_dy(s->box())) {
// Scroll Up
s->position(0,s->yposition()-(s->y() + Fl::box_dy(s->box()) - y()));
}
}
}
else if (event == FL_UNFOCUS) {
if(!(api->mode&UIFC_MONO))
color(UIFC_BUTTON_COLOR);
}
else if (event == FL_KEYBOARD) {
int key=Fl::event_key();
int i;
if (key == FL_Enter || key == FL_KP_Enter) {
do_callback();
return 1;
}
if(key==FL_Page_Up) {
for(i=0;i<parent()->children() && this!=parent()->child(i);i++) {}
i=i-((parent()->h() - Fl::box_dh(parent()->box()))/UIFC_LINE_HEIGHT)+1;
if(i<0)
i=0;
Fl::focus(parent()->child(i));
parent()->child(i)->handle(FL_FOCUS);
return(1);
}
if(key==FL_Page_Down) {
for(i=0;i<parent()->children() && this!=parent()->child(i);i++) {}
i=i+((parent()->h() - Fl::box_dh(parent()->box()))/UIFC_LINE_HEIGHT)-1;
if(i>parent()->children()-3)
i=parent()->children()-3;
Fl::focus(parent()->child(i));
parent()->child(i)->handle(FL_FOCUS);
return(1);
}
if(key==FL_Up) {
for(i=0;i<parent()->children() && this!=parent()->child(i);i++) {}
i=i-1;
if(i<0)
i=parent()->children()-3;
Fl::focus(parent()->child(i));
parent()->child(i)->handle(FL_FOCUS);
return(1);
}
if(key==FL_Down) {
for(i=0;i<parent()->children() && this!=parent()->child(i);i++) {}
i=i+1;
if(i>parent()->children()-3)
i=0;
Fl::focus(parent()->child(i));
parent()->child(i)->handle(FL_FOCUS);
return(1);
}
if(key==FL_Home) {
i=0;
Fl::focus(parent()->child(i));
parent()->child(i)->handle(FL_FOCUS);
return(1);
}
if(key==FL_End) {
i=parent()->children()-3;
Fl::focus(parent()->child(i));
parent()->child(i)->handle(FL_FOCUS);
return(1);
}
}
return Fl_Button::handle(event);
}
/**********************************/
/* right-click menu event handler */
/**********************************/
int UIFC_PopUp::handle(int event) {
int i;
if(event == FL_SHORTCUT && Fl::test_shortcut(FL_Escape)) {
GUI_RetVal=UIFC_CANCEL;
return(1);
}
if(event==FL_PUSH) {
for(i=0;i<children();i++) {
if(Fl::event_inside(child(i))) {
GUI_RetVal=((UIFC_Button *)child(i))->retval;
return(1);
}
}
GUI_RetVal=UIFC_CANCEL;
return(1);
}
return(0);
}
/*******************/
/* Input Box stuff */
/*******************/
void UIFC_Input_Box::draw() {
if (input_type() == FL_HIDDEN_INPUT) return;
Fl_Boxtype b = box();
if (damage() & FL_DAMAGE_ALL) draw_box(b, color());
Fl_Input_::drawtext(x()+Fl::box_dx(b), y()+Fl::box_dy(b),
w()-Fl::box_dw(b), h()-Fl::box_dh(b));
}
// kludge so shift causes selection to extend:
int UIFC_Input_Box::shift_position(int p) {
return position(p, Fl::event_state(FL_SHIFT) ? mark() : p);
}
#define ctrl(x) (x^0x40)
int UIFC_Input_Box::handle_key() {
char ascii = Fl::event_text()[0];
int del;
if (Fl::compose(del)) {
// Insert characters into numeric fields after checking for legality:
if (mode&K_NUMBER) {
Fl::compose_reset(); // ignore any foreign letters...
// This is complex to allow "0xff12" hex to be typed:
if (ascii >= '0' && ascii <= '9') {
if (readonly())
fl_beep();
else
replace(position(), mark(), &ascii, 1);
}
strcpy(((UIFC_Input *)parent())->invalue,value());
return 1;
}
if (del || Fl::event_length()) {
if (readonly())
fl_beep();
else
replace(position(), del ? position()-del : mark(),
Fl::event_text(), Fl::event_length());
}
strcpy(((UIFC_Input *)parent())->invalue,value());
return 1;
}
switch (Fl::event_key()) {
case FL_Insert:
if (Fl::event_state() & FL_CTRL)
ascii = ctrl('C');
else
if (Fl::event_state() & FL_SHIFT)
ascii = ctrl('V');
break;
case FL_Delete:
if (Fl::event_state() & FL_SHIFT)
ascii = ctrl('X');
else
ascii = ctrl('D');
break;
case FL_Left:
return shift_position(position()-1) + 1;
break;
case FL_Right:
ascii = ctrl('F');
break;
case FL_Home:
if (Fl::event_state() & FL_CTRL) {
shift_position(0);
return 1;
}
return shift_position(line_start(position())) + 1;
break;
case FL_End:
if (Fl::event_state() & FL_CTRL) {
shift_position(size());
return 1;
}
ascii = ctrl('E'); break;
case FL_BackSpace:
ascii = ctrl('H');
break;
case FL_Enter:
case FL_KP_Enter:
GUI_RetVal=0;
return 1;
}
int i;
switch (ascii) {
case ctrl('C'): // copy
return copy(1);
case ctrl('D'):
case ctrl('?'):
if (readonly()) {
fl_beep();
return 1;
}
if (mark() != position()) {
i=cut();
strcpy(((UIFC_Input *)parent())->invalue,value());
return i;
}
else {
i=cut(1);
strcpy(((UIFC_Input *)parent())->invalue,value());
return i;
}
case ctrl('E'):
return shift_position(line_end(position())) + 1;
case ctrl('F'):
return shift_position(position()+1) + 1;
case ctrl('H'):
if (readonly()) {
fl_beep();
return 1;
}
if (mark() != position()) {
i=cut();
strcpy(((UIFC_Input *)parent())->invalue,value());
}
else {
i=cut(-1);
strcpy(((UIFC_Input *)parent())->invalue,value());
}
return i;
case ctrl('K'):
if (readonly()) {
fl_beep();
return 1;
}
if (position()>=size()) return 0;
i = line_end(position());
if (i == position() && i < size())
i++;
cut(position(), i);
i=copy_cuts();
strcpy(((UIFC_Input *)parent())->invalue,value());
return i;
case ctrl('U'):
if (readonly()) {
fl_beep();
return 1;
}
i=cut(0, size());
strcpy(((UIFC_Input *)parent())->invalue,value());
return i;
case ctrl('V'):
case ctrl('Y'):
if (readonly()) {
fl_beep();
return 1;
}
Fl::paste(*this, 1);
strcpy(((UIFC_Input *)parent())->invalue,value());
return 1;
case ctrl('X'):
case ctrl('W'):
if (readonly()) {
fl_beep();
return 1;
}
copy(1);
i=cut();
strcpy(((UIFC_Input *)parent())->invalue,value());
return i;
case ctrl('Z'):
case ctrl('_'):
if (readonly()) {
fl_beep();
return 1;
}
i=undo();
strcpy(((UIFC_Input *)parent())->invalue,value());
return i;
case ctrl('A'):
case ctrl('G'):
if (readonly()) {
fl_beep();
return 1;
}
// insert a few selected control characters literally:
if (!(mode&K_NUMBER) && (mode&K_MSG)) {
i=replace(position(), mark(), &ascii, 1);
strcpy(((UIFC_Input *)parent())->invalue,value());
return i;
}
}
return 0;
}
int UIFC_Input_Box::handle(int event) {
static int drag_start = -1;
switch (event) {
case FL_FOCUS:
switch (Fl::event_key()) {
case FL_Right:
position(0);
break;
case FL_Left:
position(size());
break;
case FL_Down:
up_down_position(0);
break;
case FL_Up:
up_down_position(line_start(size()));
break;
case FL_Tab:
case 0xfe20: // XK_ISO_Left_Tab
position(size(),0);
break;
default:
position(position(),mark());// turns off the saved up/down arrow position
break;
}
break;
case FL_KEYBOARD:
if (Fl::event_key() == FL_Tab && mark() != position()) {
// Set the current cursor position to the end of the selection...
if (mark() > position())
position(mark());
else
position(position());
return (1);
}
else
return handle_key();
case FL_PUSH:
if (Fl::focus() != this) {
Fl::focus(this);
handle(FL_FOCUS);
}
break;
case FL_RELEASE:
if (Fl::event_button() == 2) {
Fl::event_is_click(0); // stop double click from picking a word
Fl::paste(*this, 0);
}
else if (!Fl::event_is_click()) {
// copy drag-selected text to the clipboard.
copy(0);
}
else if (Fl::event_is_click() && drag_start >= 0) {
// user clicked in the field and wants to reset the cursor position...
position(drag_start, drag_start);
drag_start = -1;
}
// For output widgets, do the callback so the app knows the user
// did something with the mouse...
if (readonly())
do_callback();
return 1;
}
Fl_Boxtype b = box();
return Fl_Input_::handletext(event,
x()+Fl::box_dx(b), y()+Fl::box_dy(b),
w()-Fl::box_dw(b), h()-Fl::box_dh(b));
}
/*********************/
/* top level handler */
/*********************/
static int handle_escape(int event) {
UIFC_Button *w=(UIFC_Button *)Fl::focus();
int i,j,key;
if(event == FL_SHORTCUT && Fl::test_shortcut(FL_Escape)) {
GUI_RetVal=UIFC_CANCEL;
return(1);
}
if(event == FL_SHORTCUT && Fl::focus()->type()==FL_NORMAL_BUTTON) {
int mode=((UIFC_Button *)Fl::focus())->mode;
key=Fl::event_key();
if(key==FL_Insert && (mode&WIN_INS)) {
GUI_RetVal=w->retval|MSK_INS;
return(1);
}
if(key==FL_Delete && (mode&WIN_DEL)) {
GUI_RetVal=w->retval|MSK_DEL;
return(1);
}
if(key==FL_F+5 && (mode&WIN_GET)) {
GUI_RetVal=w->retval|MSK_GET;
return(1);
}
if(key==FL_F+6 && (mode&WIN_PUT)) {
GUI_RetVal=w->retval|MSK_PUT;
return(1);
}
if(key==FL_F+1) {
GUI_RetVal=UIFC_HELP;
return(1);
}
if((key>='a' && key<='z')||(key>='0' && key<='9')) {
for(i=0;i<w->parent()->children() && w!=w->parent()->child(i);i++) {}
if(i!=w->parent()->children()) {
j=i;
for(i++;i!=j;i++) {
if(i>=w->parent()->children())
i=0;
if(w->parent()->child(i)->type()==FL_NORMAL_BUTTON
&& w->parent()->child(i)!=NULL
&& w->parent()->child(i)->label()!=NULL) {
if(toupper(key)==toupper(*(char *)w->parent()->child(i)->label())) {
Fl::focus(w->parent()->child(i));
w->parent()->child(i)->handle(FL_FOCUS);
return(1);
}
}
}
}
}
}
return(0);
}
/****************************************************************************/
/* Initialization function, see uifc.h for details. */
/* Returns 0 on success. */
/****************************************************************************/
int uifcinifltk(uifcapi_t* uifcapi)
{
if(uifcapi==NULL || uifcapi->size!=sizeof(uifcapi_t))
return(-1);
api=uifcapi;
/* install function handlers */
api->bail=uifcbail;
api->scrn=uscrn;
api->msg=umsg;
api->pop=upop;
api->list=ulist;
api->input=uinput;
api->sethelp=sethelp;
api->scrn_len=24;
api->mode |= UIFC_MOUSE;
Fl::scheme("plastic");
Fl::add_handler(handle_escape);
Fl::visible_focus(TRUE);
Fl::visual(FL_DOUBLE|FL_INDEX);
if(!(api->mode&UIFC_MONO)) {
Fl::foreground(UIFC_FOREGROUND);
Fl::background(UIFC_BACKGROUND);
Fl::background2(UIFC_BACKGROUND2);
}
Windows[0]=NULL;
CurrWin=0;
MainWin=NULL;
return(0);
}
/****************************************************************************/
/* Exit/uninitialize UIFC implementation. */
/****************************************************************************/
void uifcbail(void)
{
int i;
for(i=CurrWin;i>=0;i--) {
delwin(i);
}
if(MainWin != NULL) {
MainWin->hide();
delete MainWin;
}
Fl::wait(5);
}
/****************************************************************************/
/* Clear screen, fill with background attribute, display application title. */
/* Returns 0 on success. */
/****************************************************************************/
int uscrn(char *str)
{
if(MainWin==NULL) {
MainWin=new Fl_Double_Window(UIFC_CHAR_WIDTH*80,UIFC_LINE_HEIGHT*25,str);
if(!(api->mode&UIFC_MONO))
MainWin->color(UIFC_MAIN_BG_COLOR,UIFC_MAIN_BG_COLOR);
}
MainWin->show();
return(0);
}
/**********************************/
/* Delete window and all children */
/**********************************/
void delwin(int WinNum) {
if(WinNum<0)
return;
if(Windows[WinNum]!=NULL) {
MainWin->remove(Windows[WinNum]);
delete Windows[WinNum];
Windows[WinNum]=NULL;
}
}
/********************/
/* Generic callback */
/********************/
static void GenCallback(Fl_Widget *w, void *data) {
GUI_RetVal=((UIFC_Button *)w)->retval;
}
/*****************/
/* NULL callback */
/*****************/
static void NULLCallback(int i, void *data) {
}
/***************/
/* Pop up menu */
/***************/
static void doPopUp(int mode) {
int height=0;
int width=3;
int curry=0;
Fl_Widget *w=Fl::pushed();
if(w->type()==FL_NORMAL_BUTTON) {
((Fl_Button *)w)->value(0);
if(!(api->mode&UIFC_MONO))
w->color(UIFC_BUTTON_SELECTED_COLOR);
}
// Right Click
if(mode&WIN_INS) {
height++;
width=7;
}
if(mode&WIN_DEL) {
height++;
width=7;
}
if(mode&WIN_GET) {
height++;
}
if(mode&WIN_PUT) {
height++;
}
if(height) {
UIFC_PopUp *PopUp=new UIFC_PopUp(
UIFC_CHAR_WIDTH*width+UIFC_BORDER_WIDTH*2,
UIFC_LINE_HEIGHT*height+UIFC_BORDER_WIDTH*2,
"PopUp");
PopUp->position(Fl::event_x_root()+3,Fl::event_y_root()+3);
if(mode&WIN_INS) {
UIFC_Button *InsertButton=new UIFC_Button(
UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT*curry+UIFC_BORDER_WIDTH,
UIFC_CHAR_WIDTH*width,
UIFC_LINE_HEIGHT,
"Insert");
InsertButton->retval=((UIFC_Button *)w)->retval|MSK_INS;
InsertButton->mode=0;
InsertButton->box(FL_NO_BOX);
curry++;
}
if(mode&WIN_DEL) {
UIFC_Button *DeleteButton=new UIFC_Button(
UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT*curry+UIFC_BORDER_WIDTH,
UIFC_CHAR_WIDTH*width,
UIFC_LINE_HEIGHT,
"Delete");
DeleteButton->retval=((UIFC_Button *)w)->retval|MSK_DEL;
DeleteButton->mode=0;
DeleteButton->box(FL_NO_BOX);
curry++;
}
if(mode&WIN_GET) {
UIFC_Button *GetButton=new UIFC_Button(
UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT*curry+UIFC_BORDER_WIDTH,
UIFC_CHAR_WIDTH*width,
UIFC_LINE_HEIGHT,
"Get");
GetButton->retval=((UIFC_Button *)w)->retval|MSK_GET;
GetButton->mode=0;
GetButton->box(FL_NO_BOX);
curry++;
}
if(mode&WIN_PUT) {
UIFC_Button *PutButton=new UIFC_Button(
UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT*curry+UIFC_BORDER_WIDTH,
UIFC_CHAR_WIDTH*width,
UIFC_LINE_HEIGHT,
"Put");
PutButton->retval=((UIFC_Button *)w)->retval|MSK_PUT;
PutButton->mode=0;
PutButton->box(FL_NO_BOX);
curry++;
}
PopUp->end();
PopUp->show();
PopUp->show();
if(w->type()==FL_NORMAL_BUTTON) {
if(!(api->mode&UIFC_MONO))
w->color(UIFC_BUTTON_SELECTED_COLOR);
w->redraw();
}
Fl::grab(PopUp);
while(GUI_RetVal==UIFC_RCLICK) {
Fl::wait();
}
Fl::grab(0);
delete PopUp;
}
return;
}
/************************/
/* List button Callback */
/************************/
static void LBCallback(Fl_Widget *w, void *data) {
((Fl_Button *)w)->value(1);
if(uifc_cur != NULL)
*uifc_cur=((UIFC_Button *)w)->retval&MSK_OFF;
if(Fl::event_key()==FL_Button+3) {
Fl::focus(w);
w->handle(FL_FOCUS);
GUI_RetVal=UIFC_RCLICK;
return;
}
GUI_RetVal=((UIFC_Button *)w)->retval;
}
/****************************************************************************/
/* General menu function, see uifc.h for details. */
/****************************************************************************/
int ulist(int mode, int left, int top, int width, int *cur, int *bar
, char *title, char **option)
{
int opts,i;
int len;
int scrolloffset=0;
// Find WinID
for(i=CurrWin;(i>=0)&&(Windows[i]==NULL || Windows[i]->uifc_id!=cur);i--) {}
if(i>=0) {
for(;CurrWin>=i;CurrWin--)
delwin(CurrWin);
}
CurrWin++;
delwin(CurrWin);
len=strlen(title)+6;
if(width<len)
width=len;
for(opts=0;option[opts][0];opts++) {
len=strlen(option[opts])+4;
if(width<len)
width=len;
}
if(mode&WIN_XTR)
opts+=1;
if(*cur>=opts)
*cur=opts-1;
if(*cur<0)
*cur=0;
if(opts>20)
scrolloffset=UIFC_SCROLL_WIDTH;
GUI_RetVal=UIFC_MENU;
if(top==0)
top=SCRN_TOP;
if(left==0)
left=SCRN_LEFT;
if(mode&WIN_RHT)
left=SCRN_RIGHT-width;
while(left+width>79)
left--;
if(left<=0 || (mode&WIN_L2R))
left=(80-width)/2;
if(mode&WIN_BOT)
top=23;
while (top+((opts<20)?opts:20)>22)
top--;
if (mode&WIN_T2B)
top=0;
if(top<=0)
top=(23-((opts<20)?opts:20))/2;
Windows[CurrWin] = new UIFC_Menu(
UIFC_CHAR_WIDTH*left-UIFC_BORDER_WIDTH-(scrolloffset/2),
UIFC_LINE_HEIGHT*top-UIFC_BORDER_WIDTH,
UIFC_CHAR_WIDTH*width+(UIFC_BORDER_WIDTH*2)+scrolloffset,
UIFC_LINE_HEIGHT*(((opts<20)?opts:20)+1)+(UIFC_BORDER_WIDTH*6),
title,
mode,
opts,
cur,
option);
MainWin->add(Windows[CurrWin]);
Windows[CurrWin]->end();
Windows[CurrWin]->show();
Windows[CurrWin]->show();
/* This is kind of kludgy... it doesn't allow for adding of buttons to
the UIFC_Window before the Scroll Group */
Fl::focus(((UIFC_Menu *)Windows[CurrWin]->child(2))->child(*cur));
if(!(api->mode&UIFC_MONO))
((UIFC_Menu *)Windows[CurrWin]->child(2))->child(*cur)->color(UIFC_BUTTON_SELECTED_COLOR);
uifc_cur=cur;
while(GUI_RetVal==UIFC_MENU) {
Fl::wait();
if(GUI_RetVal==UIFC_HELP) {
help();
GUI_RetVal=UIFC_MENU;
}
else if(GUI_RetVal==UIFC_RCLICK) {
doPopUp(mode);
if(GUI_RetVal==UIFC_CANCEL)
GUI_RetVal=UIFC_MENU;
}
}
uifc_cur=NULL;
Windows[CurrWin]->deactivate();
return GUI_RetVal;
}
/*************************************************************************/
/* This function is a windowed input string input routine. */
/*************************************************************************/
int uinput(int mode, int left, int top, char *prompt, char *outstr,
int max, int kmode)
{
int width;
int wwidth;
int len;
width=max;
len=strlen(prompt);
if(width>(76-len))
width=76-len;
wwidth=width+len+1;
if(wwidth<18) {
len+=(18-wwidth)/2;
wwidth=18;
}
GUI_RetVal=UIFC_INPUT;
if(top==0)
top=SCRN_TOP;
if(left==0)
left=SCRN_LEFT;
if(mode&WIN_RHT)
left=SCRN_RIGHT-width;
while(left+wwidth>79)
left--;
if(left<=0 || (mode&WIN_L2R))
left=(80-wwidth)/2;
if(mode&WIN_BOT)
top=23;
while (top+5>22)
top--;
if (mode&WIN_T2B)
top=0;
if(top<=0)
top=(23-5)/2;
UIFC_Input *InputWin = new UIFC_Input(
UIFC_CHAR_WIDTH*left,
UIFC_LINE_HEIGHT*top,
UIFC_CHAR_WIDTH*(wwidth+4),
UIFC_LINE_HEIGHT*5,
prompt,
kmode,
max,
len,
width,
outstr);
MainWin->add(InputWin);
InputWin->end();
InputWin->show();
InputWin->show();
/* This is kind of kludgy... it doesn't allow for adding of buttons to
the UIFC_Window before the UIFC_Input_Box */
Fl::focus((UIFC_Menu *)InputWin->child(2));
while(GUI_RetVal==UIFC_INPUT) {
Fl::wait();
if(GUI_RetVal==UIFC_HELP) {
help();
GUI_RetVal=UIFC_INPUT;
}
else if(GUI_RetVal==UIFC_RCLICK)
GUI_RetVal=UIFC_INPUT;
}
if(GUI_RetVal==0) {
if(kmode&K_EDIT) {
if(strcmp(outstr,InputWin->invalue)) {
api->changes=TRUE;
}
}
else {
if(strlen(InputWin->invalue)) {
api->changes=TRUE;
}
}
snprintf(outstr,max,"%s",InputWin->invalue);
}
MainWin->remove(InputWin);
delete InputWin;
if(GUI_RetVal==UIFC_CANCEL)
return(-1);
return(strlen(outstr));
}
/****************************************************************************/
/* Displays the message 'str' and waits for the user to select "OK" */
/****************************************************************************/
void umsg(char *str)
{
fl_message(str);
}
/****************************************************************************/
/* Status popup/down function, see uifc.h for details. */
/****************************************************************************/
void upop(char *str)
{
static Fl_Double_Window *PopUp[MAX_POPUPS];
static int CurrPop=0;
int width=0;
int height=1;
if(str == NULL) {
for(;CurrPop>0;CurrPop--) {
delete PopUp[CurrPop-1];
}
return;
}
width=strlen(str);
while(width>78) {
height+=1;
width-=78;
}
PopUp[CurrPop]=new Fl_Double_Window((width+4)*UIFC_CHAR_WIDTH,(height+2)*UIFC_LINE_HEIGHT,NULL);
PopUp[CurrPop]->border(FALSE);
Fl_Box *box = new Fl_Box(
UIFC_CHAR_WIDTH,UIFC_LINE_HEIGHT,
UIFC_CHAR_WIDTH*width,UIFC_LINE_HEIGHT*height,"str");
box->align(FL_ALIGN_CENTER|FL_ALIGN_WRAP|FL_ALIGN_INSIDE);
PopUp[CurrPop]->set_modal();
PopUp[CurrPop]->end();
PopUp[CurrPop]->show();
PopUp[CurrPop]->show();
CurrPop++;
}
/****************************************************************************/
/* Sets the current help index by source code file and line number. */
/****************************************************************************/
void sethelp(int line, char* file)
{
helpline=line;
helpfile=file;
}
/****************************************************************************/
/* Help function. */
/****************************************************************************/
void help()
{
char hbuf[HELPBUF_SIZE],str[256];
char ch[2]={0,0};
char *p;
unsigned short line;
size_t len,j;
int i=0;
long l;
FILE *fp;
int inverse=0;
int high=0;
// Read help buffer
if(!api->helpbuf) {
if((fp=fopen(api->helpixbfile,"rb"))==NULL)
sprintf(hbuf," ERROR Cannot open help index:\n %s"
,api->helpixbfile);
else {
p=strrchr(helpfile,'/');
if(p==NULL)
p=strrchr(helpfile,'\\');
if(p==NULL)
p=helpfile;
else
p++;
l=-1L;
while(!feof(fp)) {
if(!fread(str,12,1,fp))
break;
str[12]=0;
fread(&line,2,1,fp);
if(stricmp(str,p) || line != helpline) {
fseek(fp,4,SEEK_CUR);
continue;
}
fread(&l,4,1,fp);
break;
}
fclose(fp);
if(l==-1L)
sprintf(hbuf," ERROR Cannot locate help key (%s:%u) in:\n"
" %s",p,helpline,api->helpixbfile);
else {
if((fp=fopen(api->helpdatfile,"rb"))==NULL)
sprintf(hbuf," ERROR Cannot open help file:\r\n %s"
,api->helpdatfile);
else {
fseek(fp,l,SEEK_SET);
fread(hbuf,HELPBUF_SIZE,1,fp);
fclose(fp);
}
}
}
}
else
strcpy(hbuf,api->helpbuf);
len=strlen(hbuf);
UIFC_Window *HelpWin = new UIFC_Window(
UIFC_CHAR_WIDTH*2-UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT*1-UIFC_BORDER_WIDTH,
UIFC_CHAR_WIDTH*76+(UIFC_BORDER_WIDTH*2),
UIFC_LINE_HEIGHT*22+(UIFC_BORDER_WIDTH*2),
"Help Window");
Fl_Text_Display *TextBox = new Fl_Text_Display(
UIFC_BORDER_WIDTH,
UIFC_LINE_HEIGHT+UIFC_BORDER_WIDTH,
UIFC_CHAR_WIDTH*76,
UIFC_LINE_HEIGHT*20+(UIFC_BORDER_WIDTH*6),
"Help Window");
TextBox->labelfont(FL_COURIER_BOLD);
TextBox->labelsize(UIFC_CHAR_HEIGHT);
if(!(api->mode&UIFC_MONO)) {
TextBox->labelcolor(UIFC_WINDOW_TITLE_COLOR);
TextBox->color(UIFC_HELP_WIN_BG_COLOR);
}
TextBox->textfont(FL_COURIER);
TextBox->textsize(UIFC_CHAR_HEIGHT);
TextBox->buffer(new Fl_Text_Buffer(HELPBUF_SIZE));
Fl_Text_Display::Style_Table_Entry styletable[] = { // Style table
{ UIFC_HELP_PLAIN_COLOR, FL_COURIER, UIFC_CHAR_HEIGHT }, // A - Plain
{ UIFC_HELP_HIGH_COLOR, FL_COURIER_ITALIC, UIFC_CHAR_HEIGHT }, // B - Bold
{ UIFC_HELP_INVERSE_COLOR, FL_COURIER_ITALIC, UIFC_CHAR_HEIGHT }, // C - Inverse
{ UIFC_HELP_BOTH_COLOR, FL_COURIER_ITALIC, UIFC_CHAR_HEIGHT }, // D - Both
};
Fl_Text_Buffer *StyleBuf=new Fl_Text_Buffer(HELPBUF_SIZE);
TextBox->highlight_data(StyleBuf, styletable,
(int)(sizeof(styletable) / sizeof(styletable[0])),
'A',NULLCallback,(void *)NULL);
MainWin->add(HelpWin);
HelpWin->end();
HelpWin->show();
HelpWin->show();
for(j=0;j<len;j++) {
if(hbuf[j]==2 || hbuf[j]=='~') { /* Ctrl-b toggles inverse */
if(inverse)
inverse=0;
else
inverse=1;
}
else if(hbuf[j]==1 || hbuf[j]=='`') { /* Ctrl-a toggles high intensity */
if(high)
high=0;
else
high=1;
}
else {
ch[0]=hbuf[j];
TextBox->insert(ch);
ch[0]='A'+(high|(inverse<<1));
StyleBuf->insert(i,ch);
i++;
}
}
while(GUI_RetVal==UIFC_HELP) {
Fl::wait();
if(GUI_RetVal==UIFC_RCLICK)
GUI_RetVal=UIFC_HELP;
}
delete StyleBuf;
MainWin->remove(HelpWin);
delete HelpWin;
}