/* * imgmover.c * * Simple C application using raylib to move images between directories. * * History: * 20250119 Creation. Menu bar. * 20250123 Load font. * 20250213 Support sticky drop-down menus. * 20250216 Modularize menu handling. * 20250222 Able to list files. * 20250223 Draw pane titles and main dir list. * 20250224 Draw right dir list and store elem positions. * 20250225 Draw image placeholders * 20250226 Fix rightside positions * 20250228 Navigate directories * 20250301 Aesthetic fixes for leftside. * Basic image loading. * Show big image on hover. * Preserve aspect ratio of big image. * 20250305 Scrollable left pane. * 20250308 Show statustooltip on image hover. * Scrollwheel support for leftside. * * Author: Dario Rodriguez dario@darionomono.com * (c) Dario Rodriguez 2025 * This program is licensed under the terms of GNU GPL v2.1+ */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> #include "raylib.h" #include "roboto_regular.c" #define DEFAULTWIDTH 1280 #define DEFAULTHEIGHT 768 #define UTF8DOWNARROW "\xe2\x86\x86" /* U+2186 in UTF-8 */ #define LEFTSIZE 720 #define DEFAULTDIRDATAHEIGHT 150 #define DEFAULTDIRDATATRIANGLEW 35 #define LEFTIMAGESIDELEN 125 #define FONTSIZE 18 #define FONTBIGSIZE 32 #define FONTHUGESIZE 48 #define WHEELSTEP LEFTIMAGESIDELEN #define ROOTDIR "/var/www/default/animeshot/" #define SEP "/" #define BLOCKLISTINGBUF 2048 #define BLOCKLISTINGELEMS 1024 #define BLOCKDIRDATA 16 #ifndef FILLXY #define FILLXY(xywh,valx,valy) (xywh).x=(valx),(xywh).y=(valy) #endif #ifndef FILLWH #define FILLWH(xywh,valw,valh) (xywh).w=(valw),(xywh).h=(valh) #endif #ifndef FILLXYWH #define FILLXYWH(xywh,valx,valy,valw,valh) (xywh).x=(valx),(xywh).y=(valy),(xywh).w=(valw),(xywh).h=(valh) #endif #ifndef UNROLLXYWH #define UNROLLXYWH(xywh) (xywh).x,(xywh).y,(xywh).w,(xywh).h #endif #ifndef UNROLLWHXY #define UNROLLWHXY(xywh) (xywh).w,(xywh).h,(xywh).x,(xywh).y #endif #ifndef __linux__ /* the old raylib used in the windows build lacks this function */ bool IsImageValid(Image image) { bool result = false; if ((image.data != NULL) && // Validate pixel data available (image.width > 0) && // Validate image width (image.height > 0) && // Validate image height (image.format > 0) && // Validate image format (image.mipmaps > 0)) result = true; // Validate image mipmaps (at least 1 for basic mipmap level) return result; } #endif typedef struct xywh_t { int x; int y; int w; int h; } xywh_t; typedef struct font_t { Font font; int height; } font_t; typedef struct menudata_t { char *title; xywh_t xywh; int sizeoptions; char **options; xywh_t optionsxywh; int flag_open; int flag_stickyopen; int currentoption; } menudata_t; typedef struct menubar_t { int height; int sizemenudata; menudata_t **menudata; font_t *ptrfont; } menubar_t; typedef struct thumb_t { xywh_t xywh; xywh_t screenxywh; Texture2D texture; int has_texture; int has_failedload; } thumb_t; typedef struct listingdata_t { thumb_t left; thumb_t right; char *name; /* first byte in the name is the type, next is the name, i.e. a directory is "dhome" and a file is "f.bashrc" (this is done for easier sorting) */ } listingdata_t; typedef struct listing_t { int sizeelems; int usedelems; listingdata_t *elems; int sizebuf; int usedbuf; char *buf; int has_leftxywh; int has_rightxywh; xywh_t lastleftxywh; } listing_t; typedef struct dirdata_t { int height; char *dirname; listing_t listing; } dirdata_t; typedef struct body_t { char *rootdir; xywh_t xywh; xywh_t backxywh; int leftsize; int sizedirdata; dirdata_t **dirdata; int currentdirdata; int leftscrollpos; int rightscrollpos; font_t *ptrfont; font_t *ptrfontbig; font_t *ptrfonthuge; char currenttexture[2048]; Texture2D texture; int texturew; int textureh; int has_texture; int has_failedload; } body_t; typedef struct im_t { int windowinit; int w; int h; menubar_t *menubar; body_t *body; font_t *font; font_t *fontbig; font_t *fonthuge; } im_t; im_t *im_init(char *menus, char *rootdir); void im_free(im_t *im); font_t *im_font_init(int size); void im_font_free(font_t *font); menubar_t *im_menubar_init(char *menus, font_t *font); void im_menubar_free(menubar_t *menubar); int im_menubar_mouse(menubar_t *menubar, Vector2 mousepos, int lmbpressed, int lmbreleased, int lmbdown, int *click_avail, char **sel_menu, char **sel_submenu); int im_menubar_draw(menubar_t *menubar, int windowwidth, int windowheight); body_t *im_body_init(int x, int y, font_t *font, font_t *fontbig, font_t *fonthuge, int leftsize, char *rootdir, int windowwidth, int windowheight); void im_body_free(body_t *body); int im_body_add(body_t *body,char *dir); int im_body_mouse(body_t *body, Vector2 mousepos, Vector2 wheel, int lmbpressed, int lmbreleased, int lmbdown, int *click_avail); int im_body_draw(body_t *body, Vector2 mousepos, int lmbdown, int windowwidth, int windowheight); int listing_get(listing_t *listing, char *pathprefix, char *path, int flag_sort); void listing_freedata(listing_t *listing); int listing_fillxywh(listing_t *listing, font_t *font, int w, int sidelen, int is_left); int imutil_menu_count(char *menus); char *imutil_menu_get(char *menus, int targetn, int *len); int imutil_submenu_count(char *menus); char *imutil_submenu_get(char *menus, int targetn, int *len); char *imutil_strduplen(char *str, int len); int is_imutil_insidexywh(Vector2 pos, xywh_t *xywh); int im_util_aspectmaximize(int w, int h, int maxw, int maxh, int *neww, int *newh); int menudata_pos2option(menudata_t *menudata, Vector2 pos); int *getcodepoints(int *sizecodepoints); int is_imagefilename(char *filename); int main(int argc, char *argv[]) { im_t *im; Vector2 mousepos,wheel; int flag_ignorelmb; int lmbpressed,lmbreleased,lmbdown; int click_avail; char *sel_menu,*sel_submenu; if((im=im_init("Fichero\nAjustes\nSalir\n\nEditar\nNuevo directorio\n\nAyuda\nInformación sobre el programa\n\n",ROOTDIR))==NULL) { return(1); } flag_ignorelmb=0; while(!WindowShouldClose()) { mousepos=GetMousePosition(); wheel=GetMouseWheelMoveV(); lmbpressed=IsMouseButtonPressed(0); lmbreleased=IsMouseButtonReleased(0); lmbdown=IsMouseButtonDown(0); click_avail=1; /* process clicks on menus */ if(click_avail) { sel_menu=sel_submenu=NULL; im_menubar_mouse(im->menubar, mousepos, lmbpressed, lmbreleased, lmbdown, &click_avail, &sel_menu, &sel_submenu); if(sel_menu!=NULL && sel_submenu!=NULL) { #if 1 fprintf(stderr,"SELECTED: \"%s\"->\"%s\"\n",sel_menu,sel_submenu); #endif } } if(click_avail) im_body_mouse(im->body, mousepos, wheel, lmbpressed, lmbreleased, lmbdown, &click_avail); /* draw screen contents */ BeginDrawing(); ClearBackground(RAYWHITE); im_body_draw(im->body,mousepos,lmbdown,im->w,im->h); im_menubar_draw(im->menubar,im->w,im->h); #if 0 { int i,j; Vector2 v2; listing_t listing={0}; if(listing_get(&listing,"..",".",1)==0) { i=2; v2.x=(float) (im->font->height/2); v2.y=(float) ((im->font->height+im->font->height/2)*i+(im->font->height/4)); DrawTextEx(im->font->font ,SEP ,v2 ,im->font->height ,0 ,((Color){ 45, 45, 45, 255 }) ); for(j=0;j<listing.usedelems;j++) { Color c; i++; c=((Color){ 45, 45, 45, 255 }); v2.x=(float) (im->font->height/2); v2.y=(float) ((im->font->height+im->font->height/2)*i+(im->font->height/4)); DrawTextEx(im->font->font ,listing.elems[j].name ,v2 ,im->font->height ,0 ,c ); } } } #endif EndDrawing(); } CloseWindow(); im_free(im),im=NULL; return(0); } im_t * im_init(char *menus, char *rootdir) { im_t *im; if(menus==NULL) return(NULL); /* sanity check failed */ if((im=calloc(1,sizeof(im_t)))==NULL) { im_free(im),im=NULL; return(NULL); /* insuf. mem. */ } /* init window */ SetTraceLogLevel(LOG_ERROR); InitWindow((im->w=DEFAULTWIDTH),(im->h=DEFAULTHEIGHT),"Image Mover"); im->windowinit=1; SetTargetFPS(30); /* init fonts and contents */ if((im->font=im_font_init(FONTSIZE))==NULL || (im->fontbig=im_font_init(FONTBIGSIZE))==NULL || (im->fonthuge=im_font_init(FONTHUGESIZE))==NULL || (im->menubar=im_menubar_init(menus,im->font))==NULL || (im->body=im_body_init(0,im->menubar->height, im->font, im->fontbig, im->fonthuge, LEFTSIZE, rootdir,im->w,im->h))==NULL ) { im_free(im),im=NULL; return(NULL); /* insuf. mem. */ } /* add the starting directory */ im_body_add(im->body,""); return(im); } void im_free(im_t *im) { if(im==NULL) return; if(im->menubar!=NULL) im_menubar_free(im->menubar),im->menubar=NULL; if(im->body!=NULL) im_body_free(im->body),im->body=NULL; if(im->font!=NULL) im_font_free(im->font),im->font=NULL; if(im->fontbig!=NULL) im_font_free(im->fontbig),im->fontbig=NULL; if(im->fonthuge!=NULL) im_font_free(im->fonthuge),im->fonthuge=NULL; #if 0 /* not working as intended */ if(im->windowinit) CloseWindow(),im->windowinit=0; #endif free(im),im=NULL; return; } font_t * im_font_init(int size) { font_t *font; int sizecodepoints; int *codepoints; if((font=calloc(1,sizeof(font_t)))==NULL) return(NULL); /* insuf. mem. */ font->height=size; codepoints=getcodepoints(&sizecodepoints); font->font=LoadFontFromMemory(".ttf",(const unsigned char *)roboto_regular,sizeof(roboto_regular)-1,font->height,codepoints,sizecodepoints); return(font); } void im_font_free(font_t *font) { if(font==NULL) return; /* NOTE: Cannot call UnloadFont(font->font) as the data was not malloc'd; see https://github.com/raysan5/raylib/blob/master/examples/others/embedded_files_loading.c */ free(font),font=NULL; return; } menubar_t * im_menubar_init(char *menus, font_t *font) { int i,j; char *str,*substr; int len,sublen; menubar_t *menubar; menudata_t *menudata; if(menus==NULL || font==NULL) return(NULL); /* sanity check failed */ if((menubar=calloc(1,sizeof(menubar_t)))==NULL || (menubar->sizemenudata=imutil_menu_count(menus))<=0 || (menubar->menudata=calloc(menubar->sizemenudata,sizeof(menudata_t)))==NULL ) { im_menubar_free(menubar),menubar=NULL; return(NULL); /* insuf. mem. */ } menubar->ptrfont=font; menubar->height=font->height+font->height/2; /* init menus */ for(i=0;i<menubar->sizemenudata;i++) { if((menudata=menubar->menudata[i]=calloc(1,sizeof(menudata_t)))==NULL || (str=imutil_menu_get(menus,i,&len))==NULL || (menudata->title=imutil_strduplen(str,len))==NULL || (menudata->sizeoptions=imutil_submenu_count(str))<=0 || (menudata->options=calloc(menudata->sizeoptions,sizeof(char *)))==NULL ) { im_menubar_free(menubar),menubar=NULL; return(NULL); /* insuf. mem. */ } for(j=0;j<menudata->sizeoptions;j++) { if((substr=imutil_submenu_get(str,j,&sublen))==NULL || (menudata->options[j]=imutil_strduplen(substr,sublen))==NULL ) { im_menubar_free(menubar),menubar=NULL; return(NULL); /* insuf. mem. */ } } } #if 0 /* test imutil_menu_xxx */ int n,m,l,ml,len,mlen; char *ptr,*mptr; for(n=0,l=imutil_menu_count(menus);n<l;n++) { ptr=imutil_menu_get(menus,n,&len); fprintf(stderr,"menu[%i]:\"",n); fwrite(ptr,1,len,stderr); fprintf(stderr,"\"->"); for(m=0,ml=imutil_submenu_count(ptr);m<ml;m++) { mptr=imutil_submenu_get(ptr,m,&mlen); fprintf(stderr,"|\""); fwrite(mptr,1,mlen,stderr); fprintf(stderr,"\""); } fprintf(stderr,"\n"); } #endif #if 0 /* test menudata */ for(i=0;i<menubar->sizemenudata;i++) { fprintf(stderr,"menu[%i]:\"%s\"->",i,menubar->menudata[i]->title); for(j=0;j<menubar->menudata[i]->sizeoptions;j++) fprintf(stderr,"|\"%s\"",menubar->menudata[i]->options[j]); fprintf(stderr,"\n"); } #endif return(menubar); } void im_menubar_free(menubar_t *menubar) { int i,j; menudata_t *menudata; if(menubar==NULL) return; if(menubar->menudata!=NULL) { for(i=0;i<menubar->sizemenudata;i++) { if((menudata=menubar->menudata[i])==NULL) continue; if(menudata->title!=NULL) free(menudata->title),menudata->title=NULL; if(menudata->options!=NULL) { for(j=0;j<menudata->sizeoptions;j++) { if(menudata->options[j]!=NULL) free(menudata->options[j]),menudata->options[j]=NULL; } free(menudata->options),menudata->options=NULL,menudata->sizeoptions=0; } free(menubar->menudata[i]),menubar->menudata[i]=NULL,menudata=NULL; } free(menubar->menudata),menubar->menudata=NULL,menubar->sizemenudata=0; } free(menubar),menubar=NULL; return; } int im_menubar_mouse(menubar_t *menubar, Vector2 mousepos, int lmbpressed, int lmbreleased, int lmbdown, int *click_avail, char **sel_menu, char **sel_submenu) { int flag_outsideall; int i,j; if(menubar==NULL || click_avail==NULL || sel_menu==NULL || sel_submenu==NULL) return(-1); *click_avail=1; *sel_menu=NULL; *sel_submenu=NULL; flag_outsideall=1; for(i=0;i<menubar->sizemenudata;i++) { int insidetitle,currentoption; insidetitle=is_imutil_insidexywh(mousepos,&(menubar->menudata[i]->xywh)); currentoption=menudata_pos2option(menubar->menudata[i],mousepos); flag_outsideall=(currentoption!=-1 || insidetitle)?0:flag_outsideall; if(lmbreleased && insidetitle) { for(j=0;j<menubar->sizemenudata;j++) { menubar->menudata[j]->flag_stickyopen=(j==i)?1:0; menubar->menudata[j]->flag_open=0; menubar->menudata[j]->currentoption=-1; } } else if((lmbpressed || lmbdown) && insidetitle) { for(j=0;j<menubar->sizemenudata;j++) { menubar->menudata[j]->flag_open=(j==i)?1:0; menubar->menudata[j]->flag_stickyopen=0; menubar->menudata[j]->currentoption=-1; } } else if((lmbdown || menubar->menudata[i]->flag_stickyopen) && currentoption!=-1) { for(j=0;j<menubar->sizemenudata;j++) { if(lmbreleased==0 || j!=i || menubar->menudata[i]->flag_stickyopen==0) { menubar->menudata[j]->flag_open=(j==i)?menubar->menudata[i]->flag_open:0; menubar->menudata[j]->flag_stickyopen=(j==i)?menubar->menudata[i]->flag_stickyopen:0; menubar->menudata[j]->currentoption=(j==i)?currentoption:-1; } else { menubar->menudata[j]->flag_open=0; menubar->menudata[j]->flag_stickyopen=0; menubar->menudata[j]->currentoption=-1; /* has selected this submenu */ *click_avail=0; *sel_menu=menubar->menudata[j]->title; *sel_submenu=menubar->menudata[j]->options[currentoption]; } } } else if(menubar->menudata[i]->flag_stickyopen && currentoption==-1) { if(lmbreleased) { menubar->menudata[i]->flag_open=0; menubar->menudata[i]->flag_stickyopen=0; } menubar->menudata[i]->currentoption=-1; } else if(lmbreleased && currentoption!=-1) { for(j=0;j<menubar->sizemenudata;j++) { menubar->menudata[j]->flag_open=0; menubar->menudata[j]->flag_stickyopen=0; menubar->menudata[j]->currentoption=-1; } /* has selected this submenu */ *click_avail=0; *sel_menu=menubar->menudata[i]->title; *sel_submenu=menubar->menudata[i]->options[currentoption]; } else if(lmbdown==0) { menubar->menudata[i]->flag_open=0; menubar->menudata[i]->flag_stickyopen=0; menubar->menudata[i]->currentoption=-1; } } if(flag_outsideall) { for(j=0;j<menubar->sizemenudata;j++) { menubar->menudata[j]->currentoption=-1; } } /* update click_avail */ for(j=0;j<menubar->sizemenudata;j++) { if(menubar->menudata[j]->flag_open || menubar->menudata[j]->flag_stickyopen) { *click_avail=0; break; } } return(0); } int im_menubar_draw(menubar_t *menubar, int windowwidth, int windowheight) { int i,j,k,x; menudata_t *menudata; font_t *font; if(menubar==NULL) return(-1); /* sanity check failed */ font=menubar->ptrfont; DrawRectangle(0,0,windowwidth, font->height+font->height/2, (Color){ 235, 235, 235, 235 } ); for(i=0,x=0;i<menubar->sizemenudata;i++) { Vector2 v2; menudata=menubar->menudata[i]; v2=MeasureTextEx(font->font,menudata->title,font->height,0); FILLXYWH(menudata->xywh,x,0,((int)v2.x)+font->height,font->height+font->height/2); v2.x=(float) (menudata->xywh.x+font->height/2); v2.y=(float) (menudata->xywh.y+font->height/4); DrawTextEx(font->font ,menudata->title ,v2 ,font->height ,0 ,(Color){ 45, 45, 45, 255 } ); if(menudata->flag_open || menudata->flag_stickyopen) { int underline_height=3; int maxw; DrawRectangle(menudata->xywh.x,menudata->xywh.y+menudata->xywh.h-underline_height,menudata->xywh.w,underline_height, (Color){ 53,132,228,255 } ); for(j=0,maxw=0;j<menudata->sizeoptions;j++) { v2=MeasureTextEx(font->font,menudata->options[j],font->height,0); maxw=(((int)(v2.x))>maxw)?((int)(v2.x)):maxw; } maxw=(maxw<(menudata->xywh.w+font->height))?(menudata->xywh.w+font->height):maxw; maxw+=font->height; FILLXYWH(menudata->optionsxywh,menudata->xywh.x+1,menudata->xywh.y+menudata->xywh.h+2,maxw,(font->height+font->height/2)*menudata->sizeoptions); DrawLine(menudata->optionsxywh.x-1,menudata->optionsxywh.y-2,menudata->optionsxywh.x+menudata->optionsxywh.w+2,menudata->optionsxywh.y-2,(Color){ 255,255,255,255 } ); DrawLine(menudata->optionsxywh.x-1,menudata->optionsxywh.y,menudata->optionsxywh.x-1,menudata->optionsxywh.y+menudata->optionsxywh.h+1,(Color){ 255,255,255,255 } ); DrawLine(menudata->optionsxywh.x+menudata->optionsxywh.w+2,menudata->optionsxywh.y,menudata->optionsxywh.x+menudata->optionsxywh.w+2,menudata->optionsxywh.y+menudata->optionsxywh.h+1,(Color){ 192,192,192,255 } ); DrawLine(menudata->optionsxywh.x-1,menudata->optionsxywh.y+menudata->optionsxywh.h+1,menudata->optionsxywh.x+menudata->optionsxywh.w+2,menudata->optionsxywh.y+menudata->optionsxywh.h+1,(Color){ 192,192,192,255 } ); DrawRectangle(menudata->optionsxywh.x,menudata->optionsxywh.y,menudata->optionsxywh.w,menudata->optionsxywh.h,(Color){ 235, 235, 235, 235 }); for(k=0;k<menudata->sizeoptions;k++) { Color c; c=(k==menudata->currentoption)?((Color){ 255,255,255,255 }):((Color){ 45, 45, 45, 255 }); if(k==menudata->currentoption) DrawRectangle(menudata->optionsxywh.x+1,menudata->optionsxywh.y+(font->height+(font->height/2))*k,menudata->optionsxywh.w-2,font->height+font->height/2,(Color){ 53,132,228,255 }); v2.x=(float) (menudata->optionsxywh.x+font->height/2); v2.y=(float) (menudata->optionsxywh.y+(font->height/4)+(font->height+(font->height/2))*k); DrawTextEx(font->font ,menudata->options[k] ,v2 ,font->height ,0 ,c ); } } else { FILLXYWH(menudata->optionsxywh,0,0,0,0); } x=menudata->xywh.x+menudata->xywh.w; } return(0); } body_t * im_body_init(int x, int y, font_t *font, font_t *fontbig, font_t *fonthuge, int leftsize, char *rootdir, int windowwidth, int windowheight) { body_t *body; static char sep[]={SEP}; if(font==NULL || fontbig==NULL || fonthuge==NULL || rootdir==NULL) return(NULL); /* sanity check failed */ if((body=calloc(1,sizeof(body_t)))==NULL || (body->rootdir=strdup(rootdir))==NULL ) { im_body_free(body),body=NULL; return(NULL); } FILLXYWH(body->xywh,x,y,windowwidth-x,windowheight-y); body->leftsize=leftsize; body->ptrfont=font; body->ptrfontbig=fontbig; body->ptrfonthuge=fonthuge; if(body->rootdir[0]!='\0' && strcmp(body->rootdir,SEP)!=0 && body->rootdir[strlen(body->rootdir)-1]==sep[0]) body->rootdir[strlen(body->rootdir)-1]='\0'; /* rootdir doesn't need the final '/' */ return(body); } void im_body_free(body_t *body) { int i; dirdata_t *dirdata; if(body==NULL) return; /* nothing to do */ if(body->rootdir!=NULL) free(body->rootdir),body->rootdir=NULL; if(body->dirdata!=NULL) { for(i=0;i<body->sizedirdata;i++) { if((dirdata=body->dirdata[i])==NULL) continue; if(dirdata->dirname!=NULL) free(dirdata->dirname),dirdata->dirname=NULL; listing_freedata(&(dirdata->listing)); free(dirdata),dirdata=body->dirdata[i]=NULL; } free(body->dirdata),body->dirdata=NULL,body->sizedirdata=0; } if(body->has_texture) { UnloadTexture(body->texture); body->currenttexture[0]='\0'; body->has_texture=0; body->has_failedload=0; } free(body),body=NULL; return; } int im_body_add(body_t *body,char *dir) { int i; dirdata_t *dirdata; if(body==NULL || dir==NULL) return(-1); /* sanity check failed */ for(i=0;i<body->sizedirdata;i++) { if(body->dirdata[i]==NULL) break; } if(i==body->sizedirdata) { /* dirdata container full, add more slots */ dirdata_t **newdirdata; if((newdirdata=realloc(body->dirdata,sizeof(dirdata_t *)*(body->sizedirdata+BLOCKDIRDATA)))==NULL) return(-1); /* insuf. mem. */ body->dirdata=newdirdata; memset(body->dirdata+body->sizedirdata,0,sizeof(dirdata_t *)*BLOCKDIRDATA); body->sizedirdata+=BLOCKDIRDATA; } if((dirdata=body->dirdata[i]=calloc(1,sizeof(dirdata_t)))==NULL || (dirdata->dirname=strdup(dir))==NULL ) { if(dirdata!=NULL) free(dirdata),dirdata=body->dirdata[i]=NULL; return(-1); /* insuf. mem. */ } dirdata->height=DEFAULTDIRDATAHEIGHT; dirdata->dirname=strdup(dir); listing_get(&(dirdata->listing),body->rootdir,dir,1); return(0); } int im_body_mouse(body_t *body, Vector2 mousepos, Vector2 wheel, int lmbpressed, int lmbreleased, int lmbdown, int *click_avail) { int i; char *ptr; dirdata_t *dirdata; listingdata_t *ld; if(body==NULL || click_avail==NULL || body->currentdirdata<0 || body->currentdirdata>=body->sizedirdata || body->dirdata[body->currentdirdata]==NULL) return(-1); /* sanity check error */ dirdata=body->dirdata[body->currentdirdata]; /* wheel */ body->leftscrollpos-=(int)wheel.y*WHEELSTEP; body->leftscrollpos=(body->leftscrollpos<0)?0:body->leftscrollpos; if(body->leftscrollpos>dirdata->listing.lastleftxywh.y) body->leftscrollpos=dirdata->listing.lastleftxywh.y; /* check if we have to process a click */ if(*click_avail==0 || lmbreleased==0) return(0); /* nothing else to do */ /* leftside backbutton */ if(is_imutil_insidexywh(mousepos,&(body->backxywh))) { static char sep[]={SEP}; if((ptr=strrchr(dirdata->dirname,sep[0]))!=NULL) { /* previous dir */ *ptr='\0'; } else { /* root dir */ dirdata->dirname[0]='\0'; } listing_get(&(dirdata->listing),body->rootdir,dirdata->dirname,1); body->leftscrollpos=0; *click_avail=0; return(0); } /* leftside directories */ for(i=0;i<dirdata->listing.usedelems;i++) { ld=dirdata->listing.elems+i; if(ld->name[0]!='d') continue; if(is_imutil_insidexywh(mousepos,&(ld->left.screenxywh))) { char *newname,*oldprefix; int l,l0,l1; static char sep[]={SEP}; oldprefix=(strcmp(dirdata->dirname,SEP)==0)?"":dirdata->dirname; l0=strlen(oldprefix); l1=strlen(ld->name+1); l=l0+((l0>0)?1:0)+l1+1; if((newname=malloc(l))==NULL) return(-1); /* insuf. mem. */ if(l0>0) { memcpy(newname,oldprefix,l0); newname[l0]=sep[0]; } memcpy(newname+l0+((l0>0)?1:0),ld->name+1,l1); newname[l0+((l0>0)?1:0)+l1]='\0'; free(dirdata->dirname),dirdata->dirname=NULL; dirdata->dirname=newname; listing_get(&(dirdata->listing),body->rootdir,dirdata->dirname,1); body->leftscrollpos=0; *click_avail=0; return(0); } } #warning TODO return(0); } int im_body_draw(body_t *body, Vector2 mousepos, int lmbdown, int windowwidth, int windowheight) { int i,k,margin,righty; int lastx,lasty; dirdata_t *dirdata; listingdata_t *elem; Vector2 v2,m2; font_t *font,*fontbig,*fonthuge; int is_leftside; int flag_skiprightside; int xoff,yoff; thumb_t *thumb; char statustooltip[1024]; if(body==NULL) return(-1); font=body->ptrfont; fontbig=body->ptrfontbig; fonthuge=body->ptrfonthuge; FILLXYWH(body->backxywh,0,0,0,0); margin=font->height/4; /* calculate positions */ for(i=0;i<body->sizedirdata;i++) { if(body->dirdata[i]==NULL) continue; if(body->dirdata[i]->listing.has_rightxywh==0) { int sidelen; sidelen=body->dirdata[i]->height-(fontbig->height+fontbig->height/4+font->height+margin*4+fontbig->height/4); listing_fillxywh(&(body->dirdata[i]->listing),body->ptrfont,body->xywh.w-(body->xywh.x+body->leftsize),sidelen,0); body->dirdata[i]->listing.has_rightxywh=1; } if(i==body->currentdirdata && body->dirdata[i]->listing.has_leftxywh==0) { listing_fillxywh(&(body->dirdata[i]->listing),body->ptrfont,body->leftsize,LEFTIMAGESIDELEN,1); body->dirdata[i]->listing.has_leftxywh=1; } } /* draw left side background */ DrawRectangle(body->xywh.x,body->xywh.y,body->leftsize, body->xywh.h, (Color){ 215, 215, 215, 255 } ); /* draw right side background */ DrawRectangle(body->xywh.x+body->leftsize,body->xywh.y,body->xywh.w-body->leftsize, body->xywh.h, (Color){ 227, 227, 227, 255 } ); /* first pass, draw leftside, second pass, draw all of rightside */ statustooltip[0]='\0'; for(is_leftside=1,flag_skiprightside=0;is_leftside>=0 && flag_skiprightside==0;is_leftside--) { for(i=(is_leftside)?body->currentdirdata:0,righty=body->xywh.y;(is_leftside && i==body->currentdirdata) || (!is_leftside && i<body->sizedirdata);i++) { int sidelen; if((dirdata=body->dirdata[i])==NULL) continue; if(is_leftside && !(i==body->currentdirdata)) continue; /* this element is not in leftside */ sidelen=(is_leftside)?LEFTIMAGESIDELEN:dirdata->height-(fontbig->height+fontbig->height/4+font->height+margin*4+fontbig->height/4); /* draw left side back arrow if is_leftside and not in root dir */ if(is_leftside && !(dirdata->dirname[0]=='\0' || strcmp(dirdata->dirname,SEP)==0)) { m2=MeasureTextEx(fontbig->font,UTF8DOWNARROW,fontbig->height,0); v2.x=(float) (body->xywh.x+fontbig->height/2); v2.y=(float) (body->xywh.y+fontbig->height/4+(fontbig->height-v2.x)/2)-body->leftscrollpos; FILLXYWH(body->backxywh,v2.x-fontbig->height/4,v2.y-fontbig->height/8,m2.x+fontbig->height/4,m2.x+fontbig->height/4); #if 0 DrawTexture(fontbig->font.texture, 0, 0, WHITE); /* font glyphs -- see https://github.com/raysan5/raylib/issues/2022 */ DrawRectangle(UNROLLXYWH(body->backxywh),((Color){ 0,255,0,255 })); /* hit zone */ #endif if((v2.y+fontbig->height)>=0) DrawTextPro(fontbig->font,UTF8DOWNARROW,(Vector2){v2.x+m2.x,v2.y},(Vector2){0,0},90.0,fontbig->height,0,(Color){65,65,65,255}); } if(is_leftside) { /* ...dirname */ v2.y=(float) (body->xywh.y+fontbig->height/4-body->leftscrollpos); if((v2.y+fontbig->height)>=0) { m2=MeasureTextEx(fontbig->font,dirdata->dirname,fontbig->height,0); v2.x=(float) (body->xywh.x+fontbig->height/2)+(body->leftsize-(body->xywh.x+fontbig->height)-m2.x)/2; DrawTextEx(fontbig->font ,dirdata->dirname ,v2 ,fontbig->height ,0 ,(Color){ 65, 65, 65, 255 } ); } } else { /* rightside */ /* ...bg */ DrawRectangle(body->xywh.x+body->leftsize,righty,body->xywh.w-body->leftsize, dirdata->height,(i==body->currentdirdata)?((Color){ 168, 168, 168, 255 }):((Color){ 227, 227, 227, 255 })); /* ...bottom separator */ DrawRectangle(body->xywh.x+body->leftsize,righty+dirdata->height-1,body->xywh.w-body->leftsize, 1, (Color){ 221, 221, 221, 255 } ); /* ...dirname */ DrawTextEx(fontbig->font ,dirdata->dirname ,(Vector2) {body->xywh.x+body->leftsize+fontbig->height/2,righty+fontbig->height/4} ,fontbig->height ,0 ,(Color){ 240, 240, 240, 255 } ); } /* directories */ xoff=((is_leftside)?0:body->leftsize); yoff=((is_leftside)?body->xywh.y:righty)+fontbig->height/4+fontbig->height+font->height/4-(is_leftside?body->leftscrollpos:0); if(is_leftside && dirdata->dirname[0]=='\0') yoff-=fontbig->height/4+fontbig->height; for(k=0,lastx=lasty=0;k<dirdata->listing.usedelems;k++) { elem=dirdata->listing.elems+k; thumb=(is_leftside)?&(elem->left):&(elem->right); if(elem->name[0]!='d' || strcmp(elem->name,"d.")==0 || strcmp(elem->name,"d..")==0) { if(elem->name[0]=='d') FILLXYWH(thumb->screenxywh,0,0,0,0); continue; } if((thumb->xywh.y+yoff)>(body->xywh.y+body->xywh.h) || thumb->xywh.w==0 || thumb->xywh.h==0) { #warning TODO: if !is_leftside, draw "..." in huge font using lastx,lasty as reference break; } if((thumb->xywh.y+yoff+thumb->xywh.h)<0) { FILLXYWH(thumb->screenxywh,0,0,0,0); continue; } FILLXYWH(thumb->screenxywh,thumb->xywh.x+xoff,thumb->xywh.y+yoff,thumb->xywh.w,thumb->xywh.h); DrawRectangleLines(UNROLLXYWH(thumb->screenxywh),((Color){ 65, 65, 65, 255 })); DrawTextEx(font->font,elem->name+1,(Vector2){thumb->screenxywh.x+margin,thumb->screenxywh.y+margin},font->height,0,((Color){ 65, 65, 65, 255 })); lastx=thumb->screenxywh.x+thumb->screenxywh.w,lasty=thumb->screenxywh.y+thumb->screenxywh.h; } for(;k<dirdata->listing.usedelems;k++) { elem=dirdata->listing.elems+k; if(elem->name[0]!='d') continue; thumb=(is_leftside)?&(elem->left):&(elem->right); FILLXYWH(thumb->screenxywh,0,0,0,0); } /* files */ for(k=0,lastx=lasty=0;k<dirdata->listing.usedelems;k++) { Texture2D *te; xywh_t *xywh; elem=dirdata->listing.elems+k; thumb=(is_leftside)?&(elem->left):&(elem->right); xywh=&(thumb->xywh); te=&(thumb->texture); int has_imagedrawn; if(elem->name[0]!='f' && elem->name[0]!='l') continue; if((xywh->y+yoff)>(body->xywh.y+body->xywh.h) || xywh->w==0 || xywh->h==0) { #warning TODO: if !is_leftside, draw "..." in huge font using lastx,lasty as reference break; } if(is_leftside && is_imutil_insidexywh(mousepos,&(thumb->screenxywh))) { strncpy(statustooltip,elem->name+1,sizeof(statustooltip)); statustooltip[sizeof(statustooltip)-1]='\0'; } /* show image */ has_imagedrawn=0; #if 1 if(is_imagefilename(elem->name+1)) { if(thumb->has_texture==0) { Image im; char fullpath[2048]; snprintf(fullpath,sizeof(fullpath),"%s/%s/%s",body->rootdir,dirdata->dirname,elem->name+1); fullpath[sizeof(fullpath)-1]='\0'; im=LoadImage(fullpath); if(IsImageValid(im)) { #if 1 fprintf(stderr,"Loaded %s\n",fullpath); #endif ImageResize(&im,sidelen,sidelen); *te=LoadTextureFromImage(im); UnloadImage(im); thumb->has_texture=1; } } if(thumb->has_texture!=0) { FILLXYWH(thumb->screenxywh,xywh->x+xoff,xywh->y+yoff,xywh->w,xywh->h); DrawTexture(*te,thumb->screenxywh.x,thumb->screenxywh.y,WHITE); has_imagedrawn=1; lastx=xywh->x+xywh->w,lasty=xywh->y+xywh->h+yoff; if(is_leftside && lmbdown==0 && is_imutil_insidexywh(mousepos,&(thumb->screenxywh))) { /* draw image in rightside */ char path[2048]; int maxw,maxh; snprintf(path,sizeof(path),"%s/%s",dirdata->dirname,elem->name+1); path[sizeof(path)-1]='\0'; maxw=windowwidth-(body->leftsize-DEFAULTDIRDATATRIANGLEW); maxh=windowheight-body->xywh.y; if((body->has_texture==0 && !(body->has_failedload && strcmp(body->currenttexture,path)==0)) || strcmp(body->currenttexture,path)!=0 ) { Image im; int neww,newh; char fullpath[2048]; if(body->has_texture) { UnloadTexture(body->texture); body->currenttexture[0]='\0'; body->has_texture=0; body->has_failedload=0; } snprintf(fullpath,sizeof(fullpath),"%s/%s/%s",body->rootdir,dirdata->dirname,elem->name+1); fullpath[sizeof(fullpath)-1]='\0'; im=LoadImage(fullpath); if(IsImageValid(im)) { im_util_aspectmaximize(im.width,im.height,maxw,maxh,&neww,&newh); ImageResize(&im,neww,newh); body->texture=LoadTextureFromImage(im); UnloadImage(im); strncpy(body->currenttexture,fullpath,sizeof(body->currenttexture)); body->currenttexture[sizeof(body->currenttexture)-1]='\0'; body->has_texture=1; body->has_failedload=0; body->texturew=neww; body->textureh=newh; } else { strncpy(body->currenttexture,fullpath,sizeof(body->currenttexture)); body->currenttexture[sizeof(body->currenttexture)-1]='\0'; body->has_texture=0; body->has_failedload=1; } } if(body->has_texture) { int x0,y0; x0=body->leftsize-DEFAULTDIRDATATRIANGLEW; y0=body->xywh.y; DrawRectangle(x0,y0,maxw,maxh,(Color){ 215, 215, 215, 255 } ); DrawTexture(body->texture,x0+(maxw-body->texturew)/2,y0+(maxh-body->textureh)/2,WHITE); flag_skiprightside=1; } } } } #endif if(has_imagedrawn==0) { char *ptr; char shortname[1024]; xywh_t *pos; int l; font_t *myfont=(is_leftside)?fonthuge:font; pos=is_leftside?&(elem->left.xywh):&(elem->right.xywh); FILLXYWH(thumb->screenxywh,xywh->x+xoff,xywh->y+yoff,xywh->w,xywh->h); DrawRectangle(UNROLLXYWH(thumb->screenxywh),((Color){0,0,0,64})); if((ptr=strchr(elem->name+1,'.'))!=NULL) { m2=MeasureTextEx(myfont->font,ptr,myfont->height,0); DrawTextEx(myfont->font,ptr,(Vector2){xywh->x+xoff+(sidelen-m2.x)/2,xywh->y+yoff+(font->height)/2+(xywh->w-myfont->height)/2},myfont->height,0,(Color){ 0,0,0,96 }); } ptr=(ptr==NULL)?elem->name+1:ptr; l=(ptr-(elem->name+1)); l=(l>=sizeof(shortname))?sizeof(shortname)-1:l; memcpy(shortname,elem->name+1,l); shortname[l]='\0'; DrawRectangle(pos->x+xoff,pos->y+yoff,pos->w,font->height+font->height/2,((Color){0,0,0,64})); DrawTextEx(font->font,shortname,(Vector2){pos->x+xoff+font->height/4,pos->y+yoff+font->height/4},font->height,0,(Color){ 192,192,192,255 }); lastx=xywh->x+xoff+xywh->w,lasty=xywh->y+yoff+xywh->h; } } for(;k<dirdata->listing.usedelems;k++) { elem=dirdata->listing.elems+k; if(elem->name[0]!='f' && elem->name[0]!='l') continue; thumb=(is_leftside)?&(elem->left):&(elem->right); FILLXYWH(thumb->screenxywh,0,0,0,0); } /* ...finishing touchs */ if(is_leftside) { ; } else { if(i==body->currentdirdata) { /* draw right side "current" marker inside left side area */ DrawTriangle((Vector2){((float)body->xywh.x)+body->leftsize,((float)righty)}, (Vector2){((float)body->xywh.x)+body->leftsize-DEFAULTDIRDATATRIANGLEW,((float)righty)+dirdata->height/2}, (Vector2){((float)body->xywh.x)+body->leftsize,((float)righty)+dirdata->height-1}, (Color){ 168, 168, 168, 255 } ); } /* advance to next element */ righty+=dirdata->height; } } } if(statustooltip[0]!='\0') { m2=MeasureTextEx(font->font,statustooltip,font->height,0); DrawRectangle(0,windowheight-1-margin*2-font->height,m2.x+margin*2,font->height+margin*2,((Color){0,0,0,96})); DrawTextEx(font->font,statustooltip,(Vector2){margin,windowheight-1-margin-font->height},font->height,0,(Color){ 255,255,255,128 }); } return(0); } int imutil_menu_count(char *menus) { int n; char *ptr,*next; if(menus==NULL) return(0); /* sanity check error */ for(ptr=menus,n=0;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1) { if(next[1]=='\n') { next++; n++; } } return(n); } char * imutil_menu_get(char *menus, int targetn, int *len) { int n; char *ptr,*next,*end; int is_title; if(len!=NULL) *len=0; if(menus==NULL || targetn<0) return(NULL); /* sanity check error */ end=menus+strlen(menus); for(ptr=menus,is_title=1,n=0;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1) { if(targetn==n && is_title==1) { if(len!=NULL) *len=next-ptr; return(ptr); /* found, return start of menu string */ } is_title=0; if(next[1]=='\n') { n++; next++; is_title=1; } } return(end); /* not found, return end of menus */ } int imutil_submenu_count(char *menus) { int subn; char *ptr,*next; if(menus==NULL) return(0); /* sanity check error */ next=strchr(menus,'\n'); if(next==NULL) return(0); /* no title */ for(subn=0,ptr=next+1;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1) { subn++; if(next[1]=='\n') break; } return(subn); } char * imutil_submenu_get(char *menus, int targetsubn, int *len) { char *ptr,*next,*end; int subn; if(len!=NULL) *len=0; if(menus==NULL || targetsubn<0) return(NULL); /* sanity check error */ end=menus+strlen(menus); next=strchr(menus,'\n'); if(next==NULL) return(end); /* no title */ for(ptr=next+1,subn=0;(next=strchr(ptr,'\n'))!=NULL;ptr=next+1,subn++) { if(targetsubn==subn) { if(len!=NULL) *len=next-ptr; return(ptr); } if(next[1]=='\n') break; /* "\n\n" marks the end of submenus */ } return(end); } char * imutil_strduplen(char *str, int len) { char *res; if(len<0 || (str==NULL && len!=0)) return(NULL); if((res=malloc(len+1))==NULL) return(NULL); memcpy(res,str,len); res[len]='\0'; return(res); } int is_imutil_insidexywh(Vector2 pos, xywh_t *xywh) { if(xywh==NULL) return(0); /* sanity check error */ if(pos.x>=(float)(xywh->x) && pos.x<=(float)(xywh->x+xywh->w) && pos.y>=(float)(xywh->y) && pos.y<=(float)(xywh->y+xywh->h) ) { return(1); } return(0); } int im_util_aspectmaximize(int w, int h, int maxw, int maxh, int *neww, int *newh) { if(neww==NULL || newh==NULL || w==0 || h==0 || maxw==0 || maxh==0) return(-1); if((w/h)>(maxw/maxh)) { *neww=maxw; *newh=h*maxw/w; } else { /* (w/h)<=(maxw/maxh) */ *newh=maxh; *neww=w*maxh/h; } return(0); } int menudata_pos2option(menudata_t *menudata, Vector2 pos) { int n,h; if(menudata==NULL || (menudata->flag_open==0 && menudata->flag_stickyopen==0) || is_imutil_insidexywh(pos, &(menudata->optionsxywh))==0 ) { return(-1); } h=(menudata->sizeoptions==0)?0:menudata->optionsxywh.h/menudata->sizeoptions; n=(((int)pos.y)-menudata->optionsxywh.y)/h; return(n); } int * getcodepoints(int *sizecodepoints) { static int codepoints[]={ /* Basic Latin */ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,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,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* Latin-1 Supplement */ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* down arrow (U+2186 -- this font doesn't have arrows, this is old roman numeral 50 */ (0x21<<8)|0x86, }; if(sizecodepoints!=NULL) *sizecodepoints=(int) (sizeof(codepoints)/sizeof(codepoints[0])); return(codepoints); } static int strptrcmp(void *a,void *b) { return(strcmp(((listingdata_t *)a)->name,((listingdata_t *)b)->name)); } int listing_get(listing_t *listing, char *pathprefix, char *parampath, int flag_sort) { int i,l; DIR *d; struct dirent *de; unsigned char dtype; char path[1024]; listingdata_t *ld; if(listing==NULL) return(-1); /* sanity check failed */ /* free old textures */ for(i=0;i<listing->usedelems;i++) { ld=listing->elems+i; if(ld->left.has_texture) { UnloadTexture(ld->left.texture); ld->left.has_texture=0; ld->left.has_failedload=0; } if(ld->right.has_texture) { UnloadTexture(ld->right.texture); ld->right.has_texture=0; ld->right.has_failedload=0; } } /* reset struct */ listing->usedelems=listing->usedbuf=0; memset(listing->elems,0,sizeof(listingdata_t)*listing->sizeelems); listing->has_leftxywh=listing->has_rightxywh=0; /* fill listing */ if(pathprefix==NULL && parampath==NULL) return(-1); /* nothing to fill */ snprintf(path,sizeof(path),"%s%s%s",(pathprefix!=NULL)?pathprefix:"",SEP,(parampath!=NULL)?parampath:""); path[sizeof(path)-1]='\0'; if((d=opendir(path))==NULL) return(-1); /* dir not found */ while((de=readdir(d))!=NULL) { l=strlen(de->d_name); dtype='u'; #ifdef _DIRENT_HAVE_D_TYPE dtype=(de->d_type==DT_BLK)?'b' :(de->d_type==DT_CHR)?'c' :(de->d_type==DT_DIR)?'d' :(de->d_type==DT_FIFO)?'p' :(de->d_type==DT_LNK)?'l' :(de->d_type==DT_REG)?'f' :(de->d_type==DT_SOCK)?'s' :'u'; #endif if(dtype=='u') { char stpath[2048]; struct stat st; mode_t m; snprintf(stpath,sizeof(stpath),"%s%s%s",path,SEP,de->d_name); stpath[sizeof(stpath)-1]='\0'; if(stat(stpath,&st)==0 && (m=(st.st_mode&S_IFMT))!=0) { dtype=(m==S_IFBLK)?'b' :(m==S_IFCHR)?'c' :(m==S_IFDIR)?'d' :(m==S_IFIFO)?'p' #ifdef S_IFLNK :(m==S_IFLNK)?'l' #endif :(m==S_IFREG)?'f' #ifdef S_IFSOCK :(m==S_IFSOCK)?'s' #endif :'u'; } } /* check for space for this elem (and get space if necessary) */ if(listing->usedelems==listing->sizeelems) { listingdata_t *newelems; if((newelems=realloc(listing->elems,sizeof(listingdata_t)*(listing->sizeelems+BLOCKLISTINGELEMS)))==NULL) { closedir(d),d=NULL; return(-1); /* insuf. mem. */ } listing->elems=newelems; memset(listing->elems+listing->sizeelems,0,sizeof(listingdata_t)*BLOCKLISTINGELEMS); listing->sizeelems+=BLOCKLISTINGELEMS; } /* check for space for this elem data (and get space if necessary) */ if((listing->sizebuf+l+2)>listing->sizebuf) { int i; char *newbuf; int newsize; int off; newsize=listing->sizebuf+l+2+BLOCKLISTINGBUF-1; newsize/=BLOCKLISTINGBUF; newsize*=BLOCKLISTINGBUF; if((newbuf=realloc(listing->buf,newsize))==NULL) { closedir(d),d=NULL; return(-1); /* insuf. mem. */ } /* the elem data buffer has a new pointer, fix previous entries */ for(i=0;i<listing->usedelems;i++) { off=(listing->elems[i].name)-listing->buf; listing->elems[i].name=newbuf+off; } listing->buf=newbuf; listing->sizebuf+=BLOCKLISTINGELEMS; } /* store the data */ listing->elems[listing->usedelems++].name=listing->buf+listing->usedbuf; listing->buf[listing->usedbuf++]=dtype; memcpy(listing->buf+listing->usedbuf,de->d_name,l+1); listing->usedbuf+=l+1; } closedir(d),d=NULL; if(flag_sort) qsort(listing->elems,listing->usedelems,sizeof(listingdata_t ),(int (*)(const void *, const void *)) strptrcmp); return(0); } void listing_freedata(listing_t *listing) { if(listing==NULL) return; /* nothing to do */ if(listing->elems!=NULL) { int i; listingdata_t *ld; for(i=0;i<listing->usedelems;i++) { ld=listing->elems+i; if(ld->left.has_texture) { UnloadTexture(ld->left.texture); ld->left.has_texture=0; ld->left.has_failedload=0; } if(ld->right.has_texture) { UnloadTexture(ld->right.texture); ld->right.has_texture=0; ld->right.has_failedload=0; } } free(listing->elems),listing->elems=NULL,listing->sizeelems=listing->usedelems=0; } if(listing->buf!=NULL) free(listing->buf),listing->buf=NULL,listing->sizebuf=listing->usedbuf=0; return; } int listing_fillxywh(listing_t *listing, font_t *font, int w, int sidelen, int is_leftside) { int x0,y0,x1; int k,x,y; Vector2 m2; int margin; listingdata_t *elem; thumb_t *thumb; if(listing==NULL || font==NULL) return(-1); /* sanity check failed */ margin=font->height/4; if(is_leftside) memset(&(listing->lastleftxywh),0,sizeof(listing->lastleftxywh)); /* directories */ if(is_leftside) { x0=font->height/2; x1=w-DEFAULTDIRDATATRIANGLEW-font->height/2; y0=0; } else { x0=FONTBIGSIZE/2; x1=w-font->height/2; y0=0; } for(k=0,x=x0,y=y0;k<listing->usedelems;k++) { elem=listing->elems+k; thumb=(is_leftside)?&(elem->left):&(elem->right); if(elem->name[0]!='d' || strcmp(elem->name,"d.")==0 || strcmp(elem->name,"d..")==0) continue; m2=MeasureTextEx(font->font,elem->name+1,font->height,0); if((x+margin*2+m2.x)>x1 && !(is_leftside==0 && y>y0)) x=x0,y+=font->height+margin*2+font->height/4; if(is_leftside==0 && y>y0) { FILLXYWH(thumb->xywh,0,0,0,0); continue; } FILLXYWH(thumb->xywh,x,y,margin*2+m2.x,margin*2+font->height); if(is_leftside && (thumb->xywh.y+thumb->xywh.h)>(listing->lastleftxywh.y+listing->lastleftxywh.h)) memcpy(&(listing->lastleftxywh),&(thumb->xywh),sizeof(xywh_t)); x+=margin*2+m2.x+font->height/4; } y+=((x==x0)?0:font->height+margin*2+font->height/4); /* files */ if(is_leftside) y0=y; else y0+=margin*2+font->height+margin; for(k=0,x=x0,y=y0;k<listing->usedelems;k++) { elem=listing->elems+k; thumb=(is_leftside)?&(elem->left):&(elem->right); if(elem->name[0]!='f' && elem->name[0]!='l') continue; if(!is_leftside && y>y0) { FILLXYWH(thumb->xywh,0,0,0,0); continue; } if((x+margin*2+sidelen)>x1 && !(is_leftside==0 && y>y0)) x=x0,y+=sidelen+margin*2; if(is_leftside==0 && y>y0) { FILLXYWH(thumb->xywh,0,0,0,0); continue; } FILLXYWH(thumb->xywh,x,y,sidelen,sidelen); if(is_leftside && (thumb->xywh.y+thumb->xywh.h)>(listing->lastleftxywh.y+listing->lastleftxywh.h)) memcpy(&(listing->lastleftxywh),&(thumb->xywh),sizeof(xywh_t)); x+=margin*2+sidelen; } return(0); } int is_imagefilename(char *filename) { char *ptr; int i; char *knownext[]={".jpg",".jpeg",".JPG",".JPEG",".Jpg",".Jpeg" ,".png",".PNG",".Png" ,".tga",".TGA",".Tga" ,".bmp",".BMP",".Bmp" ,".psd",".PSD",".Psd" ,".gif",".GIF",".Gif" ,".hdr",".HDR",".Hdr" ,".pic",".PIC",".Pic" ,".pnm",".PNM",".Pnm" }; if(filename==NULL) return(0); /* sanity check failed */ if((ptr=strrchr(filename,'.'))==NULL) return(0); /* no extension found */ for(i=0;i<(sizeof(knownext)/sizeof(knownext[0]));i++) { if(strcmp(ptr,knownext[i])==0) return(1); /* it has a known ext */ } return(0); /* not in the knownext list */ }