/* viDOOM - level editor for DOOM Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------- Platform specific GUI type routines */ static const char rcs_id[]="$Id$"; #include "config.h" #include "platgui.h" #include "gui.h" #include "gfx.h" #include "mem.h" #include "file.h" #include "ini.h" #include "util.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include /* ---------------------------------------- MACROS */ #define CENTRE(x,y,c,t) GFX_print((x)-strlen(t)*FW/2,y,c,"%s",t) #define CENTREX(x,y,w,c,t) GFX_print(((x)+(w)/2)-strlen(t)*FW/2,y,c,"%s",t) #define CENTREY(x,y,h,c,t) GFX_print((x),((y)+(h)/2)-FH/2,c,"%s",t) #define CENTREXY(x,y,w,h,c,t) GFX_print(((x)+(w)/2)-strlen(t)*FW/2,\ ((y)+(h)/2)-FH/2,c,"%s",t) #define LENGTH(t) (strlen(t)*FW) /* ---------------------------------------- CONFIG */ static char GUI_EDITOR[PATH_MAX]=""; static INI_Table ini_conf[]= { {INI_STR,"linux","edit",GUI_EDITOR,NULL}, }; /* ---------------------------------------- VARS */ static Display *disp=NULL; static Window win=None; static Pixmap pix=None; static GC gc=None; static int SCRW,SCRH; static int FW,FH; static unsigned long hi_col; static unsigned long mid_col; static unsigned long lo_col; static unsigned long txt_col; static unsigned long shadow_col; static unsigned long bold_col; static const int BEVEL=2; static const int SMALL_BEVEL=1; #define LIST_SCROLLSIZE 20 #define LIST_TEXTHEIGHT (FH+(FH>>1)) /* Internal representation used for GFX_IMAGE IMPORTANT: This *MUST* match the definition in linux/gfx.c */ typedef struct { int w,h; XImage *p; } GfxImage; /* Vars for GUI_yesno_all() */ static int all_pressed=FALSE; static int all_result=FALSE; /* Event Vars */ static int mouse_x=0; static int mouse_y=0; static int mouse_b=0; static int last_mouse_b=0; static int is_key=FALSE; static int key=0; static int shift=0; static int ctrl=0; static char ascii=0; /* 'Hidden' interfaces to gfx.c */ extern void VIDOOM_GET_GFX_VARS(Display **ret_disp, Window *ret_win, Pixmap *ret_pix, GC *ret_gc); extern void VIDOOM_GFX_FORCE_REDRAW(void); extern void VIDOOM_GFX_SAVE_DISPLAY(void); extern void VIDOOM_GFX_RESTORE_DISPLAY(void); extern Pixmap VIDOOM_GFX_SAVE_UNDER(int x, int y, int w, int h); extern void VIDOOM_GFX_RESTORE_UNDER(Pixmap p, int x, int y, int w, int h); extern void VIDOOM_GFX_WAIT_FOR(pid_t pid, int *status); /* Types to define GUI objects. Note picklist done as an image list with no images. */ typedef enum {GuiBox, GuiLabel, GuiButton, GuiText, GuiToggle, GuiRadio, GuiImageList, GuiFileList, GuiTextEdit} ObjType; typedef enum {GuiKeyPress, GuiButtonPress, GuiWheelUp, GuiWheelDown, GuiDraw, GuiExit} ObjEvent; typedef enum {GuiOk, GuiDone, GuiRedraw} CallbackReturn; typedef enum {GuiTextString, GuiTextInteger, GuiTextDouble, GuiTextReadOnly} GuiTextMode; typedef struct { int x,y,w,h; } BoundBox; typedef struct { ObjType type; BoundBox box; int focus; int take_focus; } GuiObjCommon; typedef struct { GuiObjCommon common; const char *title; int invert; GfxImage *img; } GuiObjBox; typedef struct { GuiObjCommon common; const char *text; int col; int centre; } GuiObjLabel; typedef struct { GuiObjCommon common; const char *text; } GuiObjButton; typedef struct { GuiObjCommon common; char text[PATH_MAX+1]; GuiTextMode mode; int len; int no_chars; int first; int curs; int allow_exit; } GuiObjText; typedef struct { GuiObjCommon common; const char *text; int state; } GuiObjToggle; typedef struct { GuiObjCommon common; int state; const char *text; int group; } GuiObjRadio; typedef struct { GuiObjCommon common; int curr; int img_box; int no; char **items; GFX_IMAGE *img; int no_lines; int top; double sbar_step; int scrollbar; BoundBox scroll; BoundBox bar; int double_click; } GuiObjImageList; typedef struct { char name[PATH_MAX+1]; int is_dir; off_t size; } FileEnt; typedef struct { GuiObjCommon common; int curr; int no; FileEnt *ent; char dir[PATH_MAX+1]; char *filter; int no_lines; int top; double sbar_step; int scrollbar; BoundBox scroll; BoundBox bar; int double_click; int path_text; int file_text; int name_width; int size_width; } GuiObjFileList; #define TE_CHUNK 256 typedef struct { int len; char *p; } TextEditLine; typedef struct { GuiObjCommon common; TextEditLine *tline; int top; int x,y; int col; int lines; int width; int height; int info; int read_only; } GuiObjTextEdit; typedef union { GuiObjCommon common; GuiObjBox box; GuiObjLabel lbl; GuiObjButton but; GuiObjText txt; GuiObjToggle tog; GuiObjRadio rad; GuiObjImageList pck; GuiObjFileList fle; GuiObjTextEdit edt; } GuiObj; static int focus=-1; static int dialog_no; static GuiObj *dialog=NULL; /* Types to define menus */ typedef enum {MENU_OK, MENU_CANCEL_ESC, MENU_CANCEL_CLICK, MENU_CHILD_RETURN} MenuStatus; typedef struct { MenuStatus mode; int ret; } MenuControl; /* ---------------------------------------- PRIVATE FUNCTIONS */ static void ExecProgram(char *prog_in, char *file) { char prog[PATH_MAX]; char *arg[PATH_MAX]; int no; int f; strcpy(prog,prog_in); no=0; arg[no]=strtok(prog," "); while (arg[no]) arg[++no]=strtok(NULL," "); for(f=0;f>8)|((col&0xff0000)>>16); pix.green=(col&0xff00)|((col&0xff00)>>8); pix.blue=((col&0xff)<<8)|(col&0xff); pix.flags=DoRed|DoBlue|DoGreen; XAllocColor(disp,DefaultColormap(disp,DefaultScreen(disp)),&pix); return pix.pixel; } static void DumpBox(const char *title, BoundBox b) { Debug(("%s.x=%d\n",title,b.x)); Debug(("%s.y=%d\n",title,b.y)); Debug(("%s.w=%d\n",title,b.w)); Debug(("%s.h=%d\n",title,b.h)); } static int MouseInBox(BoundBox *box, int no) { int f; for(f=0;f=box[f].x)&&(mouse_x=box[f].y)&&(mouse_y=box->x)&&(mouse_xx+box->w))&& ((mouse_y>=box->y)&&(mouse_yy+box->h))) return TRUE; return FALSE; } static void Rect3D(int x,int y,int w,int h, int invert,int bevel) { int f; int mid,lo,hi; mid=GUI_MID; if (invert) { lo=GUI_HI; hi=GUI_LO; } else { hi=GUI_HI; lo=GUI_LO; } GFX_frect(x,y,w,h,mid); for(f=0;fx,b->y,b->w,b->h,invert,bevel); } static void Tickbox(int x,int y,int w,int h, int pick) { int f; if (pick) GFX_frect(x,y,w,h,BLACK); else GFX_frect(x,y,w,h,WHITE); for(f=0;fdialog[no].pck.no_lines) dialog[no].pck.top=curr; else dialog[no].pck.top=0; dialog[no].pck.scroll.x=x+w-BEVEL-LIST_SCROLLSIZE; dialog[no].pck.scroll.y=y+BEVEL; dialog[no].pck.scroll.w=LIST_SCROLLSIZE; dialog[no].pck.scroll.h=h-BEVEL*2+1; if (no_items>dialog[no].pck.no_lines) { dialog[no].pck.scrollbar=TRUE; dialog[no].pck.sbar_step=(double)h/(double)no_items; dialog[no].pck.bar.x=dialog[no].pck.scroll.x; dialog[no].pck.bar.y=dialog[no].pck.scroll.y; dialog[no].pck.bar.w=dialog[no].pck.scroll.w; dialog[no].pck.bar.h= (dialog[no].pck.sbar_step*dialog[no].pck.no_lines+0.5); } else { dialog[no].pck.scrollbar=FALSE; dialog[no].pck.bar=dialog[no].pck.scroll; } return no; } static int CreateFileList(int x, int y, int w, int h, char *path, char *filter, int path_text, int file_text, int allow_double_click) { int no=dialog_no++; int charw; DialCommon(no,GuiFileList,x,y,w,h,TRUE); dialog[no].fle.ent=NULL; strcpy(dialog[no].fle.dir,path); dialog[no].fle.filter=filter; dialog[no].fle.path_text=path_text; dialog[no].fle.file_text=file_text; dialog[no].fle.double_click=allow_double_click; dialog[no].fle.no_lines=h/LIST_TEXTHEIGHT; dialog[no].fle.scroll.x=x+w-BEVEL-LIST_SCROLLSIZE; dialog[no].fle.scroll.y=y+BEVEL; dialog[no].fle.scroll.w=LIST_SCROLLSIZE; dialog[no].fle.scroll.h=h-BEVEL*2+1; charw=(w-BEVEL*2-LIST_SCROLLSIZE)/FW; dialog[no].fle.size_width=12; dialog[no].fle.name_width=charw-dialog[no].fle.size_width; ResetText(path_text,path); ResetText(file_text,""); chdir(path); return no; } static void TextEditInfo(GuiObjTextEdit *o); static int CreateTextEdit(int x, int y, int w, int h, TextEditLine *lines, int no_lines, int info, int read_only) { int no=dialog_no++; DialCommon(no,GuiTextEdit,x,y,w,h,TRUE); dialog[no].edt.top=0; dialog[no].edt.x=0; dialog[no].edt.y=0; dialog[no].edt.col=0; dialog[no].edt.tline=lines; dialog[no].edt.lines=no_lines; dialog[no].edt.info=info; dialog[no].edt.read_only=read_only; dialog[no].edt.width=w/FW; dialog[no].edt.height=h/FH; TextEditInfo(&dialog[no].edt); return no; } /* ---------------------------------------- DIALOG CALLBACK FUNCTIONS */ static CallbackReturn BoxCallback(GuiObj *o ,ObjEvent ev); static CallbackReturn LabelCallback(GuiObj *o,ObjEvent ev); static CallbackReturn ButtonCallback(GuiObj *o,ObjEvent ev); static CallbackReturn ToggleCallback(GuiObj *o,ObjEvent ev); static CallbackReturn RadioCallback(GuiObj *o,ObjEvent ev); static CallbackReturn ImageListCallback(GuiObj *o,ObjEvent ev); static CallbackReturn TextCallback(GuiObj *o,ObjEvent ev); static CallbackReturn FileListCallback(GuiObj *o,ObjEvent ev); static CallbackReturn TextEditCallback(GuiObj *o,ObjEvent ev); static CallbackReturn Callback(GuiObj *o, ObjEvent ev) { switch(o->common.type) { case GuiBox: return BoxCallback(o,ev); case GuiLabel: return LabelCallback(o,ev); case GuiButton: return ButtonCallback(o,ev); case GuiToggle: return ToggleCallback(o,ev); case GuiRadio: return RadioCallback(o,ev); case GuiImageList: return ImageListCallback(o,ev); case GuiText: return TextCallback(o,ev); case GuiFileList: return FileListCallback(o,ev); case GuiTextEdit: return TextEditCallback(o,ev); default: return GuiOk; } } static CallbackReturn BoxCallback(GuiObj *o ,ObjEvent ev) { GuiObjBox *b; b=&(o->box); switch (ev) { case GuiDraw: if (!b->img) { Rect3DBox(&b->common.box,b->invert,BEVEL); if (b->title) { int x,y,w; x=b->common.box.x+BEVEL*2; y=b->common.box.y+BEVEL; w=b->common.box.w-BEVEL*4; CENTREX(x,y,w,GUI_TEXTBOLD,b->title); GFX_line(x,y+FH+FH/2,x+w,y+FH+FH/2,GUI_HI); GFX_line(x,y+FH+FH/2+1,x+w,y+FH+FH/2+1,GUI_LO); } } else { Rect3DBox(&b->common.box,b->invert,BEVEL); GFX_draw_image(b->img, b->common.box.x+b->common.box.w/2-b->img->w/2, b->common.box.y+b->common.box.h/2-b->img->h/2); } break; case GuiKeyPress: break; case GuiButtonPress: break; case GuiWheelUp: break; case GuiWheelDown: break; case GuiExit: break; } return GuiOk; } static CallbackReturn LabelCallback(GuiObj *o ,ObjEvent ev) { GuiObjLabel *l; l=&(o->lbl); switch (ev) { case GuiDraw: GFX_frect(l->common.box.x,l->common.box.y, l->common.box.w,l->common.box.h,GUI_MID); if (l->centre) CENTREXY(l->common.box.x,l->common.box.y, l->common.box.w,l->common.box.h,l->col,l->text); else CENTREY(l->common.box.x,l->common.box.y, l->common.box.h,l->col,l->text); break; case GuiKeyPress: break; case GuiButtonPress: break; case GuiWheelUp: break; case GuiWheelDown: break; case GuiExit: break; } return GuiOk; } static CallbackReturn ButtonCallback(GuiObj *o ,ObjEvent ev) { GuiObjButton *b; b=&(o->but); switch (ev) { case GuiDraw: if (b->common.focus) GFX_rect(b->common.box.x,b->common.box.y, b->common.box.w,b->common.box.h,GUI_TEXTBOLD); else GFX_rect(b->common.box.x,b->common.box.y, b->common.box.w,b->common.box.h,GUI_MID); Rect3D(b->common.box.x+1,b->common.box.y+1, b->common.box.w-2,b->common.box.h-2, FALSE,BEVEL); CENTREXY(b->common.box.x,b->common.box.y, b->common.box.w,b->common.box.h,GUI_TEXTBOLD,b->text); break; case GuiKeyPress: if (key!=GFX_ENTER) break; /* CAUTION: Case above runs into this */ case GuiButtonPress: Rect3D(b->common.box.x+1,b->common.box.y+1, b->common.box.w-2,b->common.box.h-2, TRUE,BEVEL); CENTREXY(b->common.box.x,b->common.box.y, b->common.box.w,b->common.box.h,GUI_TEXTBOLD,b->text); return GuiDone; break; case GuiWheelUp: break; case GuiWheelDown: break; case GuiExit: break; } return GuiOk; } static CallbackReturn ToggleCallback(GuiObj *op ,ObjEvent ev) { GuiObjToggle *o; int x,y,w,h; o=&(op->tog); x=o->common.box.x; y=o->common.box.y; w=o->common.box.h; h=o->common.box.h; switch (ev) { case GuiDraw: if (o->common.focus) GFX_rect(x,y,w,h,GUI_TEXTBOLD); else GFX_rect(x,y,w,h,GUI_MID); CENTREY(o->common.box.x+o->common.box.h+FW,o->common.box.y, o->common.box.h,GUI_TEXT,o->text); Tickbox(x+1,y+1,w-2,h-2,o->state); break; case GuiKeyPress: if (key!=GFX_ENTER && ascii!=' ') break; /* CAUTION: Case above runs into this */ case GuiButtonPress: o->state=!o->state; return GuiRedraw; break; case GuiWheelUp: break; case GuiWheelDown: break; case GuiExit: break; } return GuiOk; } static CallbackReturn RadioCallback(GuiObj *op ,ObjEvent ev) { GuiObjRadio *o; int x,y,w,h; o=&(op->rad); x=o->common.box.x; y=o->common.box.y; w=o->common.box.h; h=o->common.box.h; switch (ev) { case GuiDraw: if (o->common.focus) GFX_rect(x,y,w,h,GUI_TEXTBOLD); else GFX_rect(x,y,w,h,GUI_MID); CENTREY(o->common.box.x+o->common.box.h+FW,o->common.box.y, o->common.box.h,GUI_TEXT,o->text); Tickbox(x+1,y+1,w-2,h-2,o->state); break; case GuiKeyPress: if (key!=GFX_ENTER && ascii!=' ') break; /* CAUTION: Case above runs into this */ case GuiButtonPress: if (!o->state) { int f; for(f=0;fgroup) dialog[f].rad.state=FALSE; o->state=TRUE; return GuiRedraw; } break; case GuiWheelUp: break; case GuiWheelDown: break; case GuiExit: break; } return GuiOk; } static CallbackReturn HandleImageListPage(GuiObjImageList *o, int keycode) { switch(keycode) { case GFX_UP: case GFX_PGUP: if (o->curr>0) { o->curr=MAX(0,o->curr-(key==GFX_UP ? 1:o->no_lines)); if (o->currtop) o->top=o->curr; if ((o->top+o->no_lines)<=o->curr) o->top=o->curr-o->no_lines+1; return GuiRedraw; } break; case GFX_DOWN: case GFX_PGDN: if (o->curr<(o->no-1)) { o->curr=MIN(o->no-1,o->curr+ (key==GFX_DOWN ? 1:o->no_lines)); if (o->currtop) o->top=o->curr; if ((o->top+o->no_lines)<=o->curr) o->top=o->curr-o->no_lines+1; return GuiRedraw; } break; default: break; } return GuiOk; } static CallbackReturn ImageListCallback(GuiObj *op ,ObjEvent ev) { static time_t last=0; GuiObjImageList *o; int x,y,w,h; int sx,sy,sw,sh; int f; o=&(op->pck); x=o->common.box.x+BEVEL; y=o->common.box.y+BEVEL; w=o->common.box.w-BEVEL*2; h=o->common.box.h-BEVEL*2; sx=o->scroll.x; sy=o->scroll.y; sw=o->scroll.w; sh=o->scroll.h; switch (ev) { case GuiDraw: if (o->common.focus) GFX_rect(x-BEVEL-1,y-BEVEL-1, w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); else GFX_rect(x-BEVEL-1,y-BEVEL-1, w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); Rect3DBox(&o->common.box,TRUE,BEVEL); GFX_frect(x,y,w,h,WHITE); GFX_frect(sx,sy,sw,sh,GUI_MID); o->bar.y=sy+o->sbar_step*o->top; Rect3DBox(&o->bar,FALSE,SMALL_BEVEL); for(f=0;fno_lines;f++) { int col; int p=f+o->top; int tx=x; int ty=y+(f*LIST_TEXTHEIGHT); int tw=w-sw; int th=LIST_TEXTHEIGHT; if (p==o->curr) { col=WHITE; GFX_frect(tx,ty,tw,th,BLACK); } else col=BLACK; if (pno) CENTREY(tx,ty,th,col,o->items[p]); } if (o->img) { dialog[o->img_box].box.img=o->img[o->curr]; Callback(dialog+o->img_box,GuiDraw); } break; case GuiKeyPress: switch(key) { case GFX_UP: case GFX_PGUP: case GFX_DOWN: case GFX_PGDN: return HandleImageListPage(o,key); break; case GFX_ENTER: return GuiDone; break; default: break; } break; case GuiButtonPress: if (IsInBox(&o->scroll)) { if (!o->scrollbar) return GuiOk; if (IsInBox(&o->bar)) { int orig; int y; double dtop; orig=mouse_y; dtop=o->top; while (GFX_mouse(NULL,&y)==GFX_BUTLEFT) { int diff; diff=y-orig; dtop+=diff/o->sbar_step; o->top=(int)dtop; o->top=MAX(0,o->top); o->top=MIN(o->no-o->no_lines,o->top); Callback(op,GuiDraw); GFX_redraw(); orig=y; } } else { if (mouse_ybar.y) o->top=MAX(0,o->top-o->no_lines); else o->top=MIN(o->no-1-o->no_lines,o->top+o->no_lines); return GuiRedraw; } } else { int my; int pick; my=mouse_y-y; pick=o->top+my/LIST_TEXTHEIGHT; if (pickno) { if (pick==o->curr && (time(NULL)-last)<2 && o->double_click) return GuiDone; last=time(NULL); o->curr=pick; return GuiRedraw; } } break; case GuiWheelUp: return HandleImageListPage(o,GFX_PGUP); break; case GuiWheelDown: return HandleImageListPage(o,GFX_PGDN); break; case GuiExit: break; } return GuiOk; } static CallbackReturn TextCallback(GuiObj *op ,ObjEvent ev) { int x,y,w,h; GuiObjText *o; int f; int ok; o=&(op->txt); x=o->common.box.x+BEVEL; y=o->common.box.y+BEVEL; w=o->common.box.w-BEVEL*2; h=o->common.box.h-BEVEL*2; switch (ev) { case GuiDraw: if (o->common.focus) GFX_rect(x-BEVEL-1,y-BEVEL-1, w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); else GFX_rect(x-BEVEL-1,y-BEVEL-1, w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); Rect3DBox(&o->common.box,TRUE,BEVEL); if (o->mode!=GuiTextReadOnly) GFX_frect(x,y,w,h,WHITE); for(f=0;fno_chars;f++) { int col; int pos; int tx; tx=x+f*FW; pos=o->first+f; if (o->common.focus) { if (pos==o->curs) { GFX_frect(tx,y,FW,FH,BLACK); col=WHITE; } else col=BLACK; } else col=BLACK; if (poslen) GFX_print(tx,y,col,"%c",o->text[pos]); tx+=FW; } break; case GuiKeyPress: switch(key) { case GFX_LEFT: o->curs--; o->curs=MAX(0,o->curs); o->curs=MIN(o->len,o->curs); if (o->cursfirst) o->first--; return GuiRedraw; break; case GFX_RIGHT: o->curs++; o->curs=MIN(o->len,o->curs); if (o->curs>(o->first+o->no_chars-1)) o->first++; return GuiRedraw; break; case GFX_ENTER: if (o->allow_exit) return GuiDone; break; case GFX_BACKSPACE: case GFX_DELETE: if (o->curs) { memmove(o->text+o->curs-1,o->text+o->curs, o->len-o->curs+1); if (--o->cursfirst) o->first--; o->len=strlen(o->text); return GuiRedraw; } break; case GFX_END: o->curs=o->len; o->first=MAX(0,o->len-o->no_chars-1); return GuiRedraw; break; case GFX_HOME: o->curs=0; o->first=0; return GuiRedraw; break; default: switch(o->mode) { case GuiTextString: ok=isprint(ascii); break; case GuiTextInteger: if (strchr("-+0123456789",ascii)) ok=TRUE; else ok=FALSE; break; case GuiTextDouble: if (strchr(".-+0123456789",ascii)) ok=TRUE; else ok=FALSE; break; case GuiTextReadOnly: ok=FALSE; break; default: ok=FALSE; break; } if (ok && o->lencurs==o->len) { o->text[o->curs++]=ascii; o->text[o->curs]=0; } else { memmove(o->text+o->curs+1,o->text+o->curs, o->len-o->curs+1); o->text[o->curs++]=ascii; } o->len=strlen(o->text); if (o->curs>(o->first+o->no_chars-1)) o->first++; return GuiRedraw; } break; } break; case GuiButtonPress: break; case GuiWheelUp: break; case GuiWheelDown: break; case GuiExit: break; } return GuiOk; } static CallbackReturn HandleFileListPage(GuiObjFileList *o, int keycode) { switch(keycode) { case GFX_UP: case GFX_PGUP: if (o->curr>0) { o->curr=MAX(0,o->curr-(key==GFX_UP ? 1:o->no_lines)); if (o->currtop) o->top=o->curr; if ((o->top+o->no_lines)<=o->curr) o->top=o->curr-o->no_lines+1; } break; case GFX_DOWN: case GFX_PGDN: if (o->curr<(o->no-1)) { o->curr=MIN(o->no-1,o->curr+ (key==GFX_DOWN ? 1:o->no_lines)); if (o->currtop) o->top=o->curr; if ((o->top+o->no_lines)<=o->curr) o->top=o->curr-o->no_lines+1; } break; default: break; } if (o->ent[o->curr].is_dir) ResetText(o->file_text,""); else ResetText(o->file_text,o->ent[o->curr].name); return GuiRedraw; } static int SortFiles(const void *a, const void *b) { const FileEnt *fa,*fb; fa=a; fb=b; if (fa->is_dir && !fb->is_dir) return -1; else if (!fa->is_dir && fb->is_dir) return 1; else return strcmp(fa->name,fb->name); } static int FilesMatch(const char *file, const char *filter) { if (!filter) return TRUE; if (strlen(file)ent) Release(o->ent); o->ent=NULL; o->no=0; o->curr=0; o->top=0; getcwd(o->dir,PATH_MAX); if (o->dir[l=(strlen(o->dir)-1)]!='/') { o->dir[++l]='/'; o->dir[++l]=0; } if ((d=opendir(o->dir))) { struct dirent *ent; struct stat sbuf; while ((ent=readdir(d))) { if (!stat(ent->d_name,&sbuf)) if (S_ISDIR(sbuf.st_mode) || FilesMatch(ent->d_name,o->filter)) { o->no++; o->ent=ReGrab(o->ent,sizeof(FileEnt)*o->no); strcpy(o->ent[o->no-1].name,ent->d_name); o->ent[o->no-1].is_dir=S_ISDIR(sbuf.st_mode); o->ent[o->no-1].size=sbuf.st_size; } } closedir(d); } ResetText(o->file_text,""); ResetText(o->path_text,o->dir); qsort(o->ent,o->no,sizeof(FileEnt),SortFiles); if (o->no>o->no_lines) { o->scrollbar=TRUE; o->sbar_step=(double)o->common.box.h/(double)o->no; o->bar.x=o->scroll.x; o->bar.y=o->scroll.y; o->bar.w=o->scroll.w; o->bar.h=(o->sbar_step*o->no_lines+0.5); } else { o->scrollbar=FALSE; o->bar=o->scroll; } } static CallbackReturn FileListCallback(GuiObj *op ,ObjEvent ev) { static time_t last=0; static char buff[PATH_MAX+10]; GuiObjFileList *o; int x,y,w,h; int sx,sy,sw,sh; int f; o=&(op->fle); if (!o->ent) GetFileList(o); x=o->common.box.x+BEVEL; y=o->common.box.y+BEVEL; w=o->common.box.w-BEVEL*2; h=o->common.box.h-BEVEL*2; sx=o->scroll.x; sy=o->scroll.y; sw=o->scroll.w; sh=o->scroll.h; switch (ev) { case GuiDraw: if (o->common.focus) GFX_rect(x-BEVEL-1,y-BEVEL-1, w+BEVEL*2+2,h+BEVEL*2+2,GUI_TEXTBOLD); else GFX_rect(x-BEVEL-1,y-BEVEL-1, w+BEVEL*2+2,h+BEVEL*2+2,GUI_MID); Rect3DBox(&o->common.box,TRUE,BEVEL); GFX_frect(x,y,w,h,WHITE); GFX_frect(sx,sy,sw,sh,GUI_MID); o->bar.y=sy+o->sbar_step*o->top; Rect3DBox(&o->bar,FALSE,SMALL_BEVEL); for(f=0;fno_lines;f++) { int col; int p=f+o->top; int tx=x; int ty=y+(f*LIST_TEXTHEIGHT); int tw=w-sw; int th=LIST_TEXTHEIGHT; if (p==o->curr) { col=WHITE; GFX_frect(tx,ty,tw,th,BLACK); } else col=BLACK; if (pno) { if (o->ent[p].is_dir) sprintf(buff,"%-*.*s%*.*s", o->name_width, o->name_width, o->ent[p].name, o->size_width, o->size_width, ""); else sprintf(buff,"%-*.*s%*ld", o->name_width, o->name_width, o->ent[p].name, o->size_width, o->ent[p].size); CENTREY(tx,ty,th,col,buff); } } break; case GuiKeyPress: switch(key) { case GFX_UP: case GFX_PGUP: case GFX_DOWN: case GFX_PGDN: return HandleFileListPage(o,key); break; case GFX_ENTER: if (!o->ent[o->curr].is_dir) return GuiDone; break; default: break; } break; case GuiButtonPress: if (IsInBox(&o->scroll)) { if (!o->scrollbar) return GuiOk; if (IsInBox(&o->bar)) { int orig; int y; double dtop; orig=mouse_y; dtop=o->top; while (GFX_mouse(NULL,&y)==GFX_BUTLEFT) { int diff; diff=y-orig; dtop+=diff/o->sbar_step; o->top=(int)dtop; o->top=MAX(0,o->top); o->top=MIN(o->no-o->no_lines,o->top); Callback(op,GuiDraw); GFX_redraw(); orig=y; } } else { if (mouse_ybar.y) o->top=MAX(0,o->top-o->no_lines); else o->top=MIN(o->no-1-o->no_lines,o->top+o->no_lines); return GuiRedraw; } } else { int my; int pick; my=mouse_y-y; pick=o->top+my/LIST_TEXTHEIGHT; if (pickno) { if (pick==o->curr && (time(NULL)-last)<2 && o->double_click) { if (o->ent[o->curr].is_dir) { chdir(o->ent[o->curr].name); GetFileList(o); last=0; return GuiRedraw; } else return GuiDone; } last=time(NULL); o->curr=pick; if (!o->ent[o->curr].is_dir) ResetText(o->file_text,o->ent[o->curr].name); return GuiRedraw; } } break; case GuiWheelUp: return HandleFileListPage(o,GFX_PGUP); break; case GuiWheelDown: return HandleFileListPage(o,GFX_PGDN); break; case GuiExit: if (o->ent) Release(o->ent); break; } return GuiOk; } static void TextEditInfo(GuiObjTextEdit *o) { char b1[128]; char b2[128]; char buff[128]; sprintf(b1,"Cursor: %d,%d",o->x+1,o->y+1); sprintf(b2,"Lines: %d",o->lines); sprintf(buff,"%-20.20s %s",b1,b2); ResetText(o->info,buff); } static CallbackReturn TextEditCallback(GuiObj *op ,ObjEvent ev) { char out; char *tmp; GuiObjTextEdit *te; TextEditLine *p; int bx,by,bw,bh; int x,y,sx,sy; int f; int mod; te=&op->edt; p=te->tline; bx=te->common.box.x; by=te->common.box.y; bw=te->common.box.w; bh=te->common.box.h; switch(ev) { case GuiDraw: if (te->common.focus) GFX_rect(bx-2,by-2,bw+4,bh+4,GUI_TEXTBOLD); else GFX_rect(bx-2,by-2,bw+4,bh+4,GUI_MID); GFX_frect(bx,by,bw,bh,WHITE); y=te->top; for(sy=0;syheight;sy++,y++) if (ylines) { x=te->col; for(sx=0;sxwidth;sx++,x++) { if (xx && y==te->y) { GFX_frect(bx+(sx*FW),by+(sy*FH),FW,FH,BLACK); GFX_print(bx+(sx*FW),by+(sy*FH),WHITE,"%c",out); } else GFX_print(bx+(sx*FW),by+(sy*FH),BLACK,"%c",out); } } break; case GuiButtonPress: te->y=(mouse_y-by)/FH; te->x=te->col+(mouse_x-bx)/FW; if (te->y>=te->lines) te->y=te->lines-1; if (te->x>p[te->y].len) te->x=p[te->y].len; TextEditInfo(te); return GuiRedraw; break; case GuiKeyPress: switch(key) { case GFX_HOME: te->x=0; te->col=0; break; case GFX_END: te->x=p[te->y].len; te->col=MAX(0,te->x-te->width); break; case GFX_LEFT: mod=ctrl||shift; if (mod) while((te->x)&&(isspace(p[te->y].p[te->x]))) { if (te->x) te->x--; else mod=FALSE; if (te->xcol) te->col--; } do { if (te->x) te->x--; else mod=FALSE; if (te->xcol) te->col--; if (isspace(p[te->y].p[te->x])) mod=FALSE; } while(mod); break; case GFX_RIGHT: mod=ctrl||shift; if (mod) while((te->xy].len)&& (isspace(p[te->y].p[te->x]))) { te->x++; if ((te->x-te->width)>te->col) te->col++; } do { if (te->xy].len) { te->x++; if ((te->x-te->width)>te->col) te->col++; } else mod=FALSE; if (isspace(p[te->y].p[te->x])) mod=FALSE; } while(mod); break; case GFX_UP: if (te->y) { te->y--; if (te->ytop) te->top--; if (te->x>p[te->y].len) { te->x=p[te->y].len; te->col=MAX(0,te->x-te->width); } } break; case GFX_PGUP: if (te->y) { te->y-=te->height; if (te->y<0) te->y=0; if (te->ytop) te->top=te->y; if (te->x>p[te->y].len) { te->x=p[te->y].len; te->col=MAX(0,te->x-te->width); } } break; case GFX_DOWN: if (p[te->y+1].p) { te->y++; if (te->y>=te->top+te->height) te->top=te->y-te->height+1; if (te->x>p[te->y].len) { te->x=p[te->y].len; te->col=MAX(0,te->x-te->width); } } break; case GFX_PGDN: te->y+=te->height; if (te->y>=te->lines) te->y=te->lines-1; if (te->y>=te->top+te->height) te->top=te->y-te->height+1; if (te->x>p[te->y].len) { te->x=p[te->y].len; te->col=MAX(0,te->x-te->width); } break; case GFX_DELETE: if (te->read_only) break; if (p[te->y].p[te->x]) { for(f=te->x;fy].len;f++) p[te->y].p[f]=p[te->y].p[f+1]; p[te->y].len--; p[te->y].p[p[te->y].len]=0; } else if (p[te->y+1].p) { p[te->y].len+=p[te->y+1].len; p[te->y].p=ReGrab(p[te->y].p, (p[te->y].len/TE_CHUNK+1)*TE_CHUNK+1); strcat(p[te->y].p,p[te->y+1].p); Release(p[te->y+1].p); for(f=te->y+1;flines;f++) p[f]=p[f+1]; te->lines--; p=te->tline=ReGrab(te->tline, sizeof(TextEditLine)*(te->lines+1)); p[te->lines].p=NULL; } break; case GFX_BACKSPACE: if (te->read_only) break; if (te->x) { for(f=te->x;fy].len;f++) p[te->y].p[f-1]=p[te->y].p[f]; p[te->y].len--; te->x--; p[te->y].p[p[te->y].len]=0; if (te->xcol) te->col--; } else if (te->y) { te->x=p[te->y-1].len; te->col=MAX(0,te->x-te->width); p[te->y-1].len+=p[te->y].len; p[te->y-1].p=ReGrab(p[te->y-1].p, (p[te->y-1].len/TE_CHUNK+1)*TE_CHUNK+1); strcat(p[te->y-1].p,p[te->y].p); Release(p[te->y].p); for(f=te->y;flines;f++) p[f]=p[f+1]; if (--te->ytop) te->top--; te->lines--; p=te->tline=ReGrab(te->tline, sizeof(TextEditLine)*(te->lines+1)); p[te->lines].p=NULL; } break; case GFX_ENTER: if (te->read_only) break; te->lines++; p=te->tline=ReGrab(te->tline, sizeof(TextEditLine)*(te->lines+1)); for(f=te->lines;f>te->y;f--) p[f]=p[f-1]; tmp=Strdup(p[te->y].p+te->x); tmp=ReGrab(tmp,(strlen(tmp)/TE_CHUNK+1)*TE_CHUNK+1); p[te->y].p[te->x]=0; p[te->y].len=strlen(p[te->y].p); te->y++; te->x=0; te->col=0; p[te->y].p=tmp; p[te->y].len=strlen(tmp); if (te->y>=te->top+te->height) te->top=te->y-te->height+1; break; default: if (ascii && !te->read_only) { p[te->y].len++; if (!(p[te->y].len%TE_CHUNK)) p[te->y].p=ReGrab(p[te->y].p, (p[te->y].len/TE_CHUNK+1)* TE_CHUNK+1); for(f=p[te->y].len;f>te->x;f--) p[te->y].p[f]=p[te->y].p[f-1]; p[te->y].p[te->x]=ascii; te->x++; if ((te->x-te->width)>te->col) te->col++; } break; } TextEditInfo(te); return GuiRedraw; break; case GuiWheelUp: if (te->y) { te->y-=te->height; if (te->y<0) te->y=0; if (te->ytop) te->top=te->y; if (te->x>p[te->y].len) { te->x=p[te->y].len; te->col=MAX(0,te->x-te->width); } } TextEditInfo(te); return GuiRedraw; break; case GuiWheelDown: te->y+=te->height; if (te->y>=te->lines) te->y=te->lines-1; if (te->y>=te->top+te->height) te->top=te->y-te->height+1; if (te->x>p[te->y].len) { te->x=p[te->y].len; te->col=MAX(0,te->x-te->width); } TextEditInfo(te); return GuiRedraw; break; case GuiExit: break; } return GuiOk; } /* ---------------------------------------- DIALOG MAIN LOOP */ static int DoDial(void) { Pixmap saved; int done=FALSE; int f; saved=VIDOOM_GFX_SAVE_UNDER(dialog[0].common.box.x, dialog[0].common.box.y, dialog[0].common.box.w, dialog[0].common.box.h); for(f=0;(fx,b->y,b->w,b->h,GUI_MID); GFX_print(b->x+SMALL_BEVEL+2,b->y+b->h/2-FH/2,GUI_TEXT,txt); if (child) { h=b->h-(SMALL_BEVEL*2)-2; hy=b->y+SMALL_BEVEL+1; GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy, b->x+b->w-SMALL_BEVEL-2,hy+h/2,GUI_HI); GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy, b->x+b->w-SMALL_BEVEL-2-h/2,hy+h,GUI_HI); GFX_line(b->x+b->w-SMALL_BEVEL-2-h/2,hy+h, b->x+b->w-SMALL_BEVEL-2,hy+h/2,GUI_LO); } } static MenuControl do_GUI_menu(char *title, int x, int y, PLAT_MENU menu[],int defval,int is_child) { Pixmap under; MenuControl ret; MenuControl cr; int no; int f; int cur; int last_cur; int cx; int by; int done; int quit; int child_done; BoundBox *box; BoundBox menu_box; int do_child; /* Calc dimensions */ no=0; menu_box.x=x; menu_box.y=y; menu_box.w=LENGTH(title); while(menu[no].text) { int l; l=LENGTH(menu[no].text)+(menu[no].child ? 12 : 0); menu_box.w=MAX(menu_box.w,l); no++; } menu_box.h=BEVEL*2+FH+4; menu_box.h+=(FH+SMALL_BEVEL*2+2)*no; menu_box.h+=2; menu_box.w+=BEVEL+SMALL_BEVEL+4; box=Grab(sizeof(*box)*no); if ((menu_box.x+menu_box.w)>SCRW) menu_box.x=SCRW-menu_box.w; if ((menu_box.y+menu_box.h)>SCRH) menu_box.y=SCRH-menu_box.h; if ((menu_box.x<0)||(menu_box.y<0)) { fprintf(stderr, "Cannot display menu:%s\nMenu is bigger than display!\n",title); ret.mode=MENU_CHILD_RETURN; ret.ret=defval; return ret; } under=VIDOOM_GFX_SAVE_UNDER(menu_box.x,menu_box.y,menu_box.w,menu_box.h); cx=menu_box.x+menu_box.w/2; /* Draw menu border and title */ Rect3DBox(&menu_box,FALSE,BEVEL); CENTRE(cx,menu_box.y+BEVEL*2,GUI_TEXTBOLD,title); /* Draw menu items */ cur=0; last_cur=0; by=menu_box.y+BEVEL*2+FH+4; for(f=0;fw); max_imgh=MAX(max_imgh,i->h); } no++; } optset=Grab(sizeof(char *)*no); imgset=Grab(sizeof(GFX_IMAGE)*no); for(f=0;f(SCRW-FW*5)) width-=FW; while (height>(SCRH-FH*5)) height-=FH; x=(SCRW-width)/2; y=(SCRH-height)/2; info=CreateText(x,y-FH*2,width,FH*1.5,"",GuiTextReadOnly,FALSE); edit=CreateTextEdit(x,y,width,height,line,no,info,TRUE); ok=CreateButton(x+10,y+height+FH*2,width-20,FH*2,"OK"); ResizeBox(box); DoDial(); for(f=0;f(SCRW-FW*5)) width-=FW; while (height>(SCRH-FH*5)) height-=FH; x=(SCRW-width)/2; y=(SCRH-height)/2; info=CreateText(x,y-FH*2,width,FH*1.5,"",GuiTextReadOnly,FALSE); edit=CreateTextEdit(x,y,width,height,line,no,info,FALSE); ok=CreateButton(x+10,y+height+FH*2,width/2-20,FH*2,"OK"); cancel=CreateButton(SCRW/2+10,y+height+FH*2,width/2-20,FH*2,"Cancel"); ResizeBox(box); press=DoDial(); if (press==ok) { int tot; tot=0; for(f=0;f