diff options
author | kraxel <kraxel> | 2004-03-28 11:31:57 +0000 |
---|---|---|
committer | kraxel <kraxel> | 2004-03-28 11:31:57 +0000 |
commit | e9e9684117719204929821028ba9dbb7915ea119 (patch) | |
tree | 6dd2d71940b33a9f960945335e9124ef4fea9fe7 /browser.c | |
download | fbida-e9e9684117719204929821028ba9dbb7915ea119.tar.gz |
Initial revision
Diffstat (limited to 'browser.c')
-rw-r--r-- | browser.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/browser.c b/browser.c new file mode 100644 index 0000000..3313d0c --- /dev/null +++ b/browser.c @@ -0,0 +1,571 @@ +/* + * simple file browser + * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <fnmatch.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <X11/Xlib.h> +#include <X11/Intrinsic.h> +#include <X11/extensions/XShm.h> +#include <Xm/Xm.h> +#include <Xm/Form.h> +#include <Xm/Label.h> +#include <Xm/RowColumn.h> +#include <Xm/PushB.h> +#include <Xm/CascadeB.h> +#include <Xm/ScrolledW.h> +#include <Xm/SelectioB.h> +#include <Xm/Transfer.h> +#include <Xm/TransferP.h> +#include <Xm/Container.h> +#include <Xm/Separator.h> + +#include "RegEdit.h" +#include "ida.h" +#include "readers.h" +#include "viewer.h" +#include "browser.h" +#include "filter.h" +#include "x11.h" +#include "dither.h" +#include "selections.h" +#include "filebutton.h" +#include "misc.h" +#include "idaconfig.h" +#include "desktop.h" + +/*----------------------------------------------------------------------*/ + +struct browser_handle; + +struct browser_handle { + char *dirname; + char *lastdir; + char *filter; + Widget shell; + Widget scroll; + Widget container; + Widget status; + XmString details[DETAIL_COUNT+1]; + + struct list_head files; + struct list_head *item; + unsigned int dirs,sfiles,afiles; + + XtWorkProcId wproc; +}; + +/*----------------------------------------------------------------------*/ + +static void dir_info(struct file_button *file) +{ + char path[256]; + char comment[256]; + int len; + + snprintf(path,sizeof(path),"%s/.directory",file->filename); + len = desktop_read_entry(path, "Comment=", comment, sizeof(comment)); + + if (len) { + XmStringFree(file->details[DETAIL_COMMENT]); + file->details[DETAIL_COMMENT] = + XmStringGenerate(comment, NULL, XmMULTIBYTE_TEXT,NULL); + XtVaSetValues(file->widget, + XmNdetail, file->details, + XmNdetailCount, DETAIL_COUNT, + NULL); + } +} + +static Boolean +browser_statfiles(XtPointer clientdata) +{ + struct browser_handle *h = clientdata; + struct file_button *file; + struct list_head *item; + char line[80]; + XmString str; + Pixmap pix; + char *type; + + if (h->item == &h->files) { + /* done => read thumbnails now */ + h->wproc = 0; + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + if ((file->st.st_mode & S_IFMT) != S_IFREG) + continue; + fileinfo_queue(file); + } + + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + if ((file->st.st_mode & S_IFMT) != S_IFDIR) + continue; + dir_info(file); + } + + /* update status line */ + if (h->filter) { + snprintf(line, sizeof(line), "%d dirs, %d/%d files [%s]", + h->dirs,h->sfiles,h->afiles,h->filter); + } else { + snprintf(line, sizeof(line), "%d dirs, %d files", + h->dirs,h->afiles); + } + str = XmStringGenerate(line, NULL, XmMULTIBYTE_TEXT, NULL); + XtVaSetValues(h->status,XmNlabelString,str,NULL); + XmStringFree(str); + h->item = NULL; + return TRUE; + } + + /* handle file */ + file = list_entry(h->item, struct file_button, window); + switch (file->st.st_mode & S_IFMT) { + case S_IFDIR: + type = "dir"; + break; + case S_IFREG: + type = "file"; + break; + default: + type = NULL; + } + if (type) { + pix = XmGetPixmap(XtScreen(h->container),type,0,0); + file_set_icon(file,pix,pix); + } + + h->item = h->item->next; + return FALSE; +} + +static void list_add_sorted(struct browser_handle *h, struct file_button *add) +{ + struct file_button *file; + struct list_head *item; + + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + if (file_cmp_alpha(add,file) <= 0) { + list_add_tail(&add->window,&file->window); + return; + } + } + list_add_tail(&add->window,&h->files); +} + +static Boolean browser_readdir(XtPointer clientdata) +{ + struct browser_handle *h = clientdata; + struct file_button *file; + struct list_head *item; + struct dirent *dirent; + WidgetList children; + struct file_button *lastdir = NULL; + Cardinal nchildren; + XmString str,elem; + DIR *dir; + unsigned int len; + + /* status line */ + str = XmStringGenerate("scanning ", NULL, XmMULTIBYTE_TEXT, NULL); + elem = XmStringGenerate(h->dirname, NULL, XmMULTIBYTE_TEXT, NULL); + str = XmStringConcatAndFree(str,elem); + elem = XmStringGenerate(" ...", NULL, XmMULTIBYTE_TEXT, NULL); + str = XmStringConcatAndFree(str,elem); + XtVaSetValues(h->status,XmNlabelString,str,NULL); + XmStringFree(str); + ptr_busy(); + + /* read + sort dir */ + dir = opendir(h->dirname); + if (NULL == dir) { + fprintf(stderr,"opendir %s: %s\n",h->dirname,strerror(errno)); + return -1; + } + h->dirs = 0; + h->sfiles = 0; + h->afiles = 0; + while (NULL != (dirent = readdir(dir))) { + /* skip dotfiles */ + if (dirent->d_name[0] == '.' && 0 != strcmp(dirent->d_name,"..")) + continue; + + /* get memory */ + file = malloc(sizeof(*file)); + memset(file,0,sizeof(*file)); + + /* get file type */ + file->basename = strdup(dirent->d_name); + file->d_type = dirent->d_type; + if (0 == strcmp(dirent->d_name, "..")) { + char *slash; + file->filename = strdup(h->dirname); + slash = strrchr(file->filename,'/'); + if (slash == file->filename) + slash++; + *slash = 0; + } else { + len = strlen(h->dirname)+strlen(file->basename)+4; + file->filename = malloc(len); + if (0 == strcmp(h->dirname,"/")) { + sprintf(file->filename,"/%s",file->basename); + } else { + sprintf(file->filename,"%s/%s",h->dirname, + file->basename); + } + } + if (file->d_type != DT_UNKNOWN) { + file->st.st_mode = DTTOIF(file->d_type); + } else { + if (-1 == stat(file->filename, &file->st)) { + fprintf(stderr,"stat %s: %s\n", + file->filename,strerror(errno)); + } + } + + /* user-specified filter */ + if (S_ISDIR(file->st.st_mode)) { + h->dirs++; + if (h->lastdir && 0 == strcmp(h->lastdir,file->filename)) { + lastdir = file; + } + } else { + h->afiles++; + if (h->filter && 0 != fnmatch(h->filter,dirent->d_name,0)) { + free(file); + continue; + } else + h->sfiles++; + } + + list_add_sorted(h,file); + } + closedir(dir); + + /* create & manage widgets */ + list_for_each(item,&h->files) { + file = list_entry(item, struct file_button, window); + file_createwidgets(h->container, file); + } + nchildren = XmContainerGetItemChildren(h->container,NULL,&children); + XtManageChildren(children,nchildren); + if (nchildren) + XtFree((XtPointer)children); + container_relayout(h->container); + if (h->lastdir) { + if (lastdir) { + if (debug) + fprintf(stderr,"lastdir: %s\n",h->lastdir); + XtVaSetValues(h->container, + XmNinitialFocus, lastdir->widget, + NULL); +// XmScrollVisible(h->scroll, lastdir->widget, 25, 25); +// XtSetKeyboardFocus(h->shell,h->container); + } + free(h->lastdir); + h->lastdir = NULL; + } + XtVaSetValues(h->shell,XtNtitle,h->dirname,NULL); + ptr_idle(); + + /* start bg processing */ + h->item = h->files.next; + h->wproc = XtAppAddWorkProc(app_context,browser_statfiles,h); + return TRUE; +} + +static void browser_bgcancel(struct browser_handle *h) +{ + if (h->wproc) + XtRemoveWorkProc(h->wproc); + h->wproc = 0; +} + +/*----------------------------------------------------------------------*/ + +static void +browser_cd(struct browser_handle *h, char *dir) +{ + /* build new dir path */ + if (h->lastdir) + free(h->lastdir); + h->lastdir = h->dirname; + h->dirname = strdup(dir); + + /* cleanup old stuff + read dir */ + browser_bgcancel(h); + container_delwidgets(h->container); + h->wproc = XtAppAddWorkProc(app_context,browser_readdir,h); +} + +static void +browser_filter_done(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + XmSelectionBoxCallbackStruct *cb = call_data; + char *filter; + + if (cb->reason == XmCR_OK) { + filter = XmStringUnparse(cb->value,NULL, + XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, + NULL,0,0); + if (h->filter) + free(h->filter); + h->filter = NULL; + if (strlen(filter) > 0) + h->filter = strdup(filter); + XtFree(filter); + + if (debug) + fprintf(stderr,"filter: %s\n", h->filter ? h->filter : "[none]"); + browser_bgcancel(h); + container_delwidgets(h->container); + h->wproc = XtAppAddWorkProc(app_context,browser_readdir,h); + } + XtDestroyWidget(widget); +} + +static void +browser_filter(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + Widget shell; + + shell = XmCreatePromptDialog(h->shell,"filter",NULL,0); + XtUnmanageChild(XmSelectionBoxGetChild(shell,XmDIALOG_HELP_BUTTON)); + XtAddCallback(shell,XmNokCallback,browser_filter_done,h); + XtAddCallback(shell,XmNcancelCallback,browser_filter_done,h); + XtManageChild(shell); +} + +static void +browser_nofilter(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + + if (!h->filter) + return; + if (debug) + fprintf(stderr,"filter: reset\n"); + free(h->filter); + h->filter = NULL; + + browser_bgcancel(h); + container_delwidgets(h->container); + h->wproc = XtAppAddWorkProc(app_context,browser_readdir,h); +} + +/*----------------------------------------------------------------------*/ + +static void +browser_action_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + XmContainerSelectCallbackStruct *cd = call_data; + struct browser_handle *h = clientdata; + struct stat st; + char *file; + + if (XmCR_DEFAULT_ACTION == cd->reason && 1 == cd->selected_item_count) { + file = XtName(cd->selected_items[0]); + if (debug) + fprintf(stderr,"browser: action %s\n", file); + if (-1 == stat(file,&st)) { + fprintf(stderr,"stat %s: %s\n",file,strerror(errno)); + return; + } + if (S_ISDIR(st.st_mode)) + browser_cd(h,file); + if (S_ISREG(st.st_mode)) + new_file(file,1); + } +} + +static void +browser_bookmark_cb(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + browser_cd(h, cfg_get_str(O_BOOKMARKS, XtName(widget))); +} + +/*----------------------------------------------------------------------*/ + +static void +browser_destroy(Widget widget, XtPointer clientdata, XtPointer call_data) +{ + struct browser_handle *h = clientdata; + + if (debug) + fprintf(stderr,"browser: destroy\n"); + browser_bgcancel(h); + ptr_unregister(h->shell); + free(h); +} + +static char* +browser_fixpath(char *dir) +{ + char path[1024]; + char *s,*d; + + memset(path,0,sizeof(path)); + if (dir[0] == '/') { + /* absolute */ + strncpy(path,dir,sizeof(path)-1); + } else { + /* relative */ + getcwd(path,sizeof(path)-1); + if (strlen(path)+strlen(dir)+4 < sizeof(path)) { + strcat(path,"/"); + strcat(path,dir); + } + } + + for (s = d = path; *s != 0;) { + if (0 == strncmp(s,"//",2)) { + s++; + continue; + } + if (0 == strncmp(s,"/./",3)) { + s+=2; + continue; + } + if (0 == strcmp(s,"/")) + s++; + if (0 == strcmp(s,"/.")) + s+=2; + *d = *s; + s++, d++; + } + return strdup(path); +} + +void +browser_window(char *dirname) +{ + Widget form,menubar,menu,push,clip; + struct browser_handle *h; + Arg args[8]; + char *list; + int n = 0; + + h = malloc(sizeof(*h)); + if (NULL == h) { + fprintf(stderr,"out of memory"); + return; + } + memset(h,0,sizeof(*h)); + INIT_LIST_HEAD(&h->files); + h->dirname = browser_fixpath(dirname); + + h->shell = XtVaAppCreateShell("browser","Ida", + topLevelShellWidgetClass, + dpy, + XtNclientLeader,app_shell, + XmNdeleteResponse,XmDESTROY, + NULL); + XmdRegisterEditres(h->shell); + XtAddCallback(h->shell,XtNdestroyCallback,browser_destroy,h); + + /* widgets */ + form = XtVaCreateManagedWidget("form", xmFormWidgetClass, h->shell, + NULL); + menubar = XmCreateMenuBar(form,"cbar",NULL,0); + XtManageChild(menubar); + h->status = XtVaCreateManagedWidget("status",xmLabelWidgetClass, form, + NULL); + + /* scrolled container */ + h->details[0] = XmStringGenerate("Image", NULL, XmMULTIBYTE_TEXT,NULL); + h->details[DETAIL_SIZE+1] = + XmStringGenerate("Size", NULL, XmMULTIBYTE_TEXT,NULL); + h->details[DETAIL_COMMENT+1] = + XmStringGenerate("Comment", NULL, XmMULTIBYTE_TEXT,NULL); + XtSetArg(args[n], XmNdetailColumnHeading, h->details); n++; + XtSetArg(args[n], XmNdetailColumnHeadingCount, DETAIL_COUNT+1); n++; + + h->scroll = XmCreateScrolledWindow(form, "scroll", NULL, 0); + XtManageChild(h->scroll); + h->container = XmCreateContainer(h->scroll,"container", + args,n); + XtManageChild(h->container); + XtAddCallback(h->container,XmNdefaultActionCallback, + browser_action_cb,h); + + XtAddCallback(h->scroll, XmNtraverseObscuredCallback, + container_traverse_cb, NULL); + XtAddCallback(h->container,XmNconvertCallback, + container_convert_cb,h); + + XtVaGetValues(h->scroll,XmNclipWindow,&clip,NULL); + XtAddEventHandler(clip,StructureNotifyMask,True,container_resize_eh,NULL); + + /* menu - file */ + menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0); + XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + push = XtVaCreateManagedWidget("close",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,destroy_cb,h->shell); + + /* menu - edit */ + menu = XmCreatePulldownMenu(menubar,"editM",NULL,0); + XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_edit(menu,h->container, 0,1,0,0); + + /* menu - view */ + menu = XmCreatePulldownMenu(menubar,"viewM",NULL,0); + XtVaCreateManagedWidget("view",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_view(menu,h->container); + XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); + push = XtVaCreateManagedWidget("filter",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,browser_filter,h); + push = XtVaCreateManagedWidget("freset",xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,browser_nofilter,h); + + /* menu - ops */ + menu = XmCreatePulldownMenu(menubar,"opsM",NULL,0); + XtVaCreateManagedWidget("ops",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + container_menu_ops(menu,h->container); + + /* menu - dirs (bookmarks) */ + menu = XmCreatePulldownMenu(menubar,"dirsM",NULL,0); + XtVaCreateManagedWidget("dirs",xmCascadeButtonWidgetClass,menubar, + XmNsubMenuId,menu,NULL); + for (list = cfg_entries_first(O_BOOKMARKS); + list != NULL; + list = cfg_entries_next(O_BOOKMARKS,list)) { + push = XtVaCreateManagedWidget(list,xmPushButtonWidgetClass,menu,NULL); + XtAddCallback(push,XmNactivateCallback,browser_bookmark_cb,h); + } + + /* read dir and show window */ + container_spatial_cb(NULL,h->container,NULL); + browser_readdir(h); + XtPopup(h->shell,XtGrabNone); + ptr_register(h->shell); +} + +void +browser_ac(Widget widget, XEvent *event, + String *params, Cardinal *num_params) +{ + if (*num_params > 0) + browser_window(params[0]); + else + browser_window("."); +} |