⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 libpg_dirview.c

📁 The major functionality added in this release includes: - Rootless mode in X11 - Widget Templt
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $Id: libpg_dirview.c,v 1.2 2002/07/28 17:06:48 micahjd Exp $ * * libpg_dirview - A directory browser based on the source code of cli_c's *                 dlg_filepicker.c * * This code is oozing with Unixisms, so it will need to be ported to run * on other OSes...  * * - The directory separator is assumed to be '/' * - The user's home directory is retrieved through $HOME * - Multiple drives (as in DOS) are not supported * - Unix functions are used to implement the file tools and validate *   files for the PG_FILE_MUST* flags * * PicoGUI small and efficient client/server GUI * Copyright (C) 2000-2002 Micah Dowty <micahjd@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. *  * This library 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 * Lesser General Public License for more details. *  * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  *  * Contributors: *  o Dirview component architecture and design: *    Copyright (C) 2002 Pascal Bauermeister <pascal.bauermeister@urbanet.ch> *  *  *  */#include <string.h>     /* strcpy(), strcat() */#include <stdlib.h>     /* For getenv() */#include <stdarg.h>#include <dirent.h>#include <unistd.h>#include <errno.h>      /* Check errors when validating a filename */#include <ctype.h>#include <fcntl.h>#include <sys/stat.h>   /* stat() */#include <sys/types.h>  /* opendir(), readdir(), etc */#include "libpg_dirview.h"#include "properties.h"#include "protocol.h"#define DECLARE_DEBUG_VARS#include "debug.h"/*****************************************************************************/typedef struct {  const char*            name;  LpgdvMenuItemEnableF   menu_item_enable;  LpgdvMenuItemsEnableF  menu_items_enable;} MenuItemParams;typedef struct {  const char*            name;  LpgdvVal               place;  int                    place_at;  LpgdvColumnRenderF     column_render;  void*                  payload;} ColumnParams;typedef struct {  /* Params passed in function call */  const char*            title;  LpgdvVal               title_is_visible;  LpgdvVal               location_visible;  LpgdvVal               list_is_header_visible;  int                    list_sort_by_col_nr;  LpgdvVal               list_do_include_parent;  LpgdvVal               list_sort;  LpgdvVal               list_group;  LpgdvSelectionChangedF selection_changed;  LpgdvItemFocusedF      item_focused;  LpgdvItemClickedF      item_clicked;  const char*            menu_title;  LpgdvVal               menu_rendering;  const char*            protocol_default;  LpgdvBrowseSiteEnterF  browse_site_enter;  LpgdvBrowseSiteLeaveF  browse_site_leave;  LpgdvBrowseDirEnterF   browse_dir_enter;  LpgdvBrowseDirLeaveF   browse_dir_leave;  LpgdvBrowseItemNextF   browse_item_next;  /* Calculated params */  int                    menu_items_nb;  int                    columns_nb;} Parameters;/*****************************************************************************//* This is the size of the buffer files are returned in */#define FILEMAX 512/* This is the maximum size of an individual file name */#define NAMEMAX 80/* All items in the file list box have this as their payload * value, so we can easily see if a widget is a file item. * * Files will be the only widgets with payloads in this dialog, * but the danger here is of outside widgets. In the event that * the file dialog is called from a toolbar app, the toolbar could * send events while the file picker is open. Normally toolbar widgets * will not have payloads, but if one has this payload it could really * confuse the file picker. */#define FILETAG   0x19840713/* Save the current directory across instances. */char dirview_dir[FILEMAX];/* Static buffer for returning the file name */char dirview_buf[FILEMAX];/* A structure for passing data to dirview_setdir */struct filepickdata {  /* Important containers */  pghandle wDirectory;  pghandle wFileList;  pghandle wScroll;  /* Filter parameters */  int flags;  pgfilter filefilter;  const char *pattern;    /* Fonts */  pghandle fDirectory, fLink, fHeading;  /* Selected file */  pghandle sFileName, wFile;};/* This is the type of node used to hold a directory entry */struct filenode {  char name[NAMEMAX];  struct stat st;};static int dirview_filter(struct filepickdata *dat, const char *name,			  struct stat *st);static void dirview_fullpath(const char *file);static void dirview_setdir(struct filepickdata *dat);static int  dirview_compare(const void *a, const void *b);static void dirview_pathmenu(struct filepickdata *dat);/*****************************************************************************/static int __indent = 0;#define INDENT        { ++__indent; }#define UNINDENT      { --__indent; }#define _PR(x...)     fprintf(stderr, x)#define _I            { int i; for(i=0;i<__indent;++i) _PR("  "); }#define _DOTS         ". . . . . . . . . . . . . . . . . . . ."#define _OUTM(m,f,v)  { _PR(_DOTS " " f "\r", v); _I; _PR(m " \n"); }#define DUMP(f,v)     { _I; _PR(f, v); }#define DUMP_STR(s)   _OUTM(#s, "[%s]", p->s ? p->s : "<null>");#define DUMP_NUM(s)   _OUTM(#s, "%d", p->s);#define DUMP_FUNC(s)  _OUTM(#s, "0x%08x", p->s);#define DUMP_VAL(s)   _OUTM(#s, "%s", ValTxt[p->s]);static const char* ValTxt[] = {  "NO",  "YES",  "MENU",  "BUTTONS",  "EDITABLE",  "SORT",  "RSORT",  "TOGGLESORT",  "DIRS_FIRST",  "DIRS_LAST",  "AT_LEFT",  "AT_RIGHT",  "AT_ABS",  "AT_REL",};static voiddump_menuitemparams(int nb, MenuItemParams* p){  int i;  DUMP("MenuItemParams:\n", 0);  INDENT;  for (i=0; i<nb; ++i) {    DUMP      ("[%d]\n", i);    INDENT;    DUMP_STR  (name);    DUMP_FUNC (menu_item_enable);    DUMP_FUNC (menu_items_enable);    UNINDENT;    ++p;  }  UNINDENT;}dump_columnparams(int nb, ColumnParams* p){  int i;  DUMP("ColumnParams:\n", 0);  INDENT;  for (i=0; i<nb; ++i) {    DUMP      ("[%d]\n", i);    INDENT;    DUMP_STR  (name);    DUMP_VAL  (place_at);    DUMP_FUNC (column_render);    DUMP_NUM  (payload);    UNINDENT;    ++p;  }  UNINDENT;}static voiddump_params(Parameters *p){  DUMP("Params:\n", 0);  INDENT;  DUMP_STR  (title);  DUMP_VAL  (title_is_visible);  DUMP_VAL  (location_visible);  DUMP_VAL  (list_is_header_visible);  DUMP_NUM  (list_sort_by_col_nr);  DUMP_VAL  (list_do_include_parent);  DUMP_VAL  (list_sort);  DUMP_VAL  (list_group);  DUMP_FUNC (selection_changed);  DUMP_FUNC (item_focused);  DUMP_FUNC (item_clicked);  DUMP_STR  (menu_title);  DUMP_VAL  (menu_rendering);  DUMP_FUNC (protocol_default);  DUMP_FUNC (browse_site_enter);  DUMP_FUNC (browse_site_leave);  DUMP_FUNC (browse_dir_enter);  DUMP_FUNC (browse_dir_leave);  DUMP_FUNC (browse_item_next);  DUMP_NUM  (menu_items_nb);  DUMP_NUM  (columns_nb);  UNINDENT;}/*****************************************************************************//* This is just like pgMenuFromString, except that we use * '/' as the separator and the items are listed backwards, * ending with a bare '/'. When the user chooses a path,  * set the dialog's directory */#if 0static voiddirview_pathmenu(struct filepickdata *dat){  char *p;  char *items = dirview_dir;  pghandle str;  int ret;  int i;  struct stat st;    /* Create the menu popup in its own context */  pgEnterContext();  pgNewPopupAt(PG_POPUP_ATCURSOR,PG_POPUP_ATCURSOR,0,0);  /* Add helpful tools! */  pgNewWidget(PG_WIDGET_MENUITEM,0,0);  pgSetWidget(PGDEFAULT,	      PG_WP_SIDE,PG_S_BOTTOM,	      PG_WP_TEXT,pgNewString("Rename"),	      0);  pgSetPayload(PGDEFAULT,-4);  pgNewWidget(PG_WIDGET_MENUITEM,0,0);  pgSetWidget(PGDEFAULT,	      PG_WP_SIDE,PG_S_BOTTOM,	      PG_WP_TEXT,pgNewString("Delete"),	      0);  pgSetPayload(PGDEFAULT,-3);  pgNewWidget(PG_WIDGET_MENUITEM,0,0);  pgSetWidget(PGDEFAULT,	      PG_WP_SIDE,PG_S_BOTTOM,	      PG_WP_TEXT,pgNewString("New Directory"),	      0);  pgSetPayload(PGDEFAULT,-2);  pgNewWidget(PG_WIDGET_MENUITEM,0,0);  pgSetWidget(PGDEFAULT,	      PG_WP_SIDE,PG_S_BOTTOM,	      PG_WP_TEXT,pgNewString("Home"),	      0);  pgSetPayload(PGDEFAULT,-1);  pgNewWidget(PG_WIDGET_LABEL,0,0);  pgSetWidget(PGDEFAULT,	      PG_WP_SIDE,PG_S_BOTTOM,	      PG_WP_TEXT,pgNewString("Tools:"),	      0);  pgSetPayload(PGDEFAULT,-4);  /* Add directories above this one */  i=0;  do {    /* Do a little fancy stuff to make the string handle.     * This is like pgNewString but we get to specify the      * length instead of having strlen() do it for us.     */    if (!(p = strchr(items,'/'))) p = items + strlen(items);    if (p==items)      str = pgNewString("/");   /* Root directory */    else {      _pg_add_request(PGREQ_MKSTRING,(void *) items,p-items);      pgFlushRequests();      str = _pg_return.e.retdata;          }    items = p+1;    /* Ignore the last part, because that's the current directory */    if (*p) {      /* Create each menu item */      pgNewWidget(PG_WIDGET_MENUITEM,0,0);      pgSetWidget(PGDEFAULT,		  PG_WP_TEXT,str,		  PG_WP_SIDE,PG_S_BOTTOM,		  0);            /* Payload is the index in dirview_dir to set to zero       * if it is chosen */      i = items-dirview_dir;      if (i>1)	i--;      pgSetPayload(PGDEFAULT,i);    }      } while (*p);  /* Run the menu */  ret = pgGetPayload(pgGetEvent()->from);  pgLeaveContext();  if (ret>0) {    dirview_dir[ret] = 0;    dirview_setdir(dat);  }  else if (ret<0) {    /* Update selected file string */    if (dat->wFile)      dat->sFileName = pgGetWidget(dat->wFile,PG_WP_TEXT);        switch (-ret) {    case 1 :    /* Home */      dirview_dir[FILEMAX-1] = 0;      strncpy(dirview_dir,getenv("HOME"),FILEMAX-1);      dirview_setdir(dat);      break;          case 2:     /* New Directory */      str = pgInputDialog("New Directory", "Name:",0);      if (str) {	dirview_fullpath(pgGetString(str));	pgDelete(str);		if (mkdir(dirview_buf,0777)) 	  pgMessageDialogFmt("Error",0,"Unable to create directory:\n%s",			     dirview_buf);	else	  dirview_setdir(dat);    }      break;          case 3:     /* Delete */      if (dat->sFileName) {	dirview_fullpath(pgGetString(dat->sFileName));	if (pgMessageDialog("Delete?",dirview_buf,			    PG_MSGBTN_YES | PG_MSGBTN_NO) == PG_MSGBTN_YES) {	  	  if (unlink(dirview_buf))	    pgMessageDialogFmt("Error",0,"Unable to delete file:\n%s",			       dirview_buf);	  else	    dirview_setdir(dat);	}      }      break;          case 4:     /* Rename */      if (dat->sFileName) {	dirview_fullpath(pgGetString(dat->sFileName));	str = pgInputDialog("Rename File",dirview_buf,0);	if (str) {	  char *oldname, *newname;	  oldname = strdup(dirview_buf);	  newname = pgGetString(str);	  if (newname[0] != '/') {	    dirview_fullpath(newname);	    newname = dirview_buf;	  }	  	  if (rename(oldname,newname))	    pgMessageDialogFmt("Error",0,"Unable to rename file:\n%s\nto\n%s",			       oldname,newname);	  else	    dirview_setdir(dat);	  	  free(oldname);	}      }      break;                }  }}#endif/*****************************************************************************//* Sort the files, in a way that should make sense to users. * Directories always come before files. Case is ignored, punctuation * is ignored. If the file has a trailing number, the numbers are sorted * numerically. */static intdirview_compare(const void *a, const void *b){  struct filenode *f1 = (struct filenode *) a;  struct filenode *f2 = (struct filenode *) b;  const char *n1 = f1->name;  const char *n2 = f2->name;  int d1 = S_ISDIR(f1->st.st_mode);  int d2 = S_ISDIR(f2->st.st_mode);  /* If one is a directory and one is not, the directory comes first. */  if (d1!=d2) {    if (d1)      return -1;    else      return 1;  }  /* Now sort them pseudo-alphabetically */  while (*n1 && *n2) {    /* Skip punctuation */    if (!isalnum(*n1)) {      n1++;      continue;    }    if (!isalnum(*n2)) {      n2++;      continue;    }    if (isdigit(*n1) && isdigit(*n2)) {      /* If they are both numbers, sort them numerically */            char *p;      int u1,u2;      u1 = strtoul(n1,&p,10);      n1 = p-1;      u2 = strtoul(n2,&p,10);      n2 = p-1;      if (u1<u2)	return -1;      else if (u1>u2)	return 1;    }    else {      /* Nope, do a case-insensitive asciibetical sort */      char c1,c2;      c1 = tolower(*n1);      c2 = tolower(*n2);      if (c1<c2)	return -1;      else if (c1>c2)	return 1;    }    /* The same so far, so advance both */    n1++;    n2++;  }  /* Compare length */  if (n1)    return -1;  if (n2)    return 1;  return 0;}/*****************************************************************************//* Utility that, given the current filters and flags, determines if a file * should be visible. Returns nonzero if the file should be visible. */static intdirview_filter(struct filepickdata *dat, const char *name,	       struct stat *st){   /* Normally ignore device nodes. Listing the contents of /dev could   * be disasterous, especially on a low-memory machine   */  if ((S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode))       && !(dat->flags & PG_FILE_SHOWDEV))    return 0;  /* Normally we should hide files beginning with a dot */  if (*name=='.') {    /* Is it '.' or '..' ? */    if (name[1]=='.' || !name[1]) {      if (!(dat->flags & PG_FILE_SHOWDOT))	return 0;    }    else {      if (!(dat->flags & PG_FILE_SHOWHIDDEN))	return 0;    }  }  /* Always include directories if they pass the dot-test above */  if (S_ISDIR(st->st_mode))    return 1;  /* Normally skip editor backups */  if (!(dat->flags & PG_FILE_SHOWBAK)) {    char first, last;    first = *name;    last  = name[strlen(name)-1];    if (last == '~')      return 0;    if (first == '#' && last == '#')      return 0;  }  /* If there is no filter, just include the file */  if (!dat->filefilter)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -