ede/efiler/EDE_DirTree.cpp
2007-07-18 18:40:34 +00:00

397 lines
12 KiB
C++

//
// "$Id$"
//
// FileBrowser routines.
//
// Copyright 1999-2006 by Michael Sweet.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library 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.
//
// Please report all bugs and problems on the following page:
//
// http://www.fltk.org/str.php
//
// Contents:
//
// FileBrowser::full_height() - Return the height of the list.
// FileBrowser::item_height() - Return the height of a list item.
// FileBrowser::item_width() - Return the width of a list item.
// FileBrowser::item_draw() - Draw a list item.
// FileBrowser::FileBrowser() - Create a FileBrowser widget.
// FileBrowser::load() - Load a directory into the browser.
// FileBrowser::filter() - Set the filename filter.
//
//
// Include necessary header files...
//
#include "EDE_DirTree.h"
#include <fltk/Browser.h>
#include <fltk/Item.h>
#include <fltk/draw.h>
#include <fltk/Color.h>
#include <fltk/Flags.h>
#include <fltk/Font.h>
#include <fltk/string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// This library is hopelessly single-platform
#include "../edelib2/Icon.h"
#include "../edelib2/Util.h"
#include "../edelib2/NLS.h"
#include <fltk/run.h>
#define MAX_TREE_DEPTH 100
using namespace fltk;
//
// 'FileBrowser::FileBrowser()' - Create a FileBrowser widget.
//
// Fn that makes sure path ends with slash
// (ensure that path allocation is large enough to take one extra char)
void make_end_with_slash(char* path) {
int i=strlen(path);
if (path[i-1] != '/') {
path[i]='/';
path[i+1]='\0';
}
}
DirTree::DirTree(int X, // I - Upper-lefthand X coordinate
int Y, // I - Upper-lefthand Y coordinate
int W, // I - Width in pixels
int H, // I - Height in pixels
const char *l) // I - Label text
: Browser(X, Y, W, H, l) {
// Initialize the filter pattern, current directory, and icon size...
//pattern_ = "*";
//directory_ = "";
//icon_size_ = 12.0f;
//filetype_ = FILES;
show_hidden_ = false;
//Symbol* fileSmall = edelib::Icon::get(DEFAULT_ICON,edelib::Icon::SMALL);
//set_symbol(Browser::LEAF, fileSmall, fileSmall);
}
// Filesystem data
// TODO: find better icons for Amiga, Macintosh etc.
static struct {
char id[15];
char uiname[50];
char icon[20];
} filesystems[] = {
// Filesystems that should be ignored
{"auto", "", ""}, // removables will be added another way
{"autofs", "", ""}, // removables will be added another way
{"binfmt_misc", "", ""},
{"debugfs", "", ""},
{"devfs", "", ""},
{"devpts", "", ""},
{"fd", "", ""},
{"fdesc", "", ""},
{"kernfs", "", ""},
{"proc", "", ""},
{"procfs", "", ""},
{"swap", "", ""},
{"sysfs", "", ""},
{"tmpfs", "", ""},
{"usbfs", "", ""},
// Common filesystems
{"ext2", "Linux disk (%s)", "hdd_unmount"},
{"ext2fs", "Linux disk (%s)", "hdd_unmount"},
{"ext3", "Linux disk (%s)", "hdd_unmount"},
{"reiserfs", "Linux disk (%s)", "hdd_unmount"},
{"ffs", "BSD disk (%s)", "hdd_unmount"},
{"ufs", "Unix disk (%s)", "hdd_unmount"},
{"dos", "Windows disk (%s)", "win_mount"},
{"fat", "Windows disk (%s)", "win_mount"},
{"msdos", "Windows disk (%s)", "win_mount"},
{"ntfs", "Windows disk (%s)", "win_mount"},
{"pcfs", "Windows disk (%s)", "win_mount"},
{"umsdos", "Windows disk (%s)", "win_mount"},
{"vfat", "Windows disk (%s)", "win_mount"},
{"cd9660", "CD-ROM (%s)", "cdrom_unmount"},
{"cdfs", "CD-ROM (%s)", "cdrom_unmount"},
{"cdrfs", "CD-ROM (%s)", "cdrom_unmount"},
{"hsfs", "CD-ROM (%s)", "cdrom_unmount"},
{"iso9660", "CD-ROM (%s)", "cdrom_unmount"},
{"isofs", "CD-ROM (%s)", "cdrom_unmount"},
{"udf", "DVD-ROM (%s)", "dvd_unmount"},
{"cifs", "Shared directory (%s)", "server"},
{"nfs", "Shared directory (%s)", "server"},
{"nfs4", "Shared directory (%s)", "server"},
{"smbfs", "Shared directory (%s)", "server"},
{"cramfs", "Virtual (RAM) disk (%s)", "memory"},
{"mfs", "Virtual (RAM) disk (%s)", "memory"},
{"ramfs", "Virtual (RAM) disk (%s)", "memory"},
{"romfs", "Virtual (RAM) disk (%s)", "memory"},
{"union", "Virtual (RAM) disk (%s)", "memory"}, // not accurate, but good enough
{"unionfs", "Virtual (RAM) disk (%s)", "memory"}, // not accurate, but good enough
{"jfs", "IBM AIX disk (%s)", "hdd_unmount"},
{"xfs", "SGI IRIX disk (%s)", "hdd_unmount"},
// Other filesystems
{"coherent", "Unix disk (%s)", "hdd_unmount"},
{"sysv", "Unix disk (%s)", "hdd_unmount"},
{"xenix", "Unix disk (%s)", "hdd_unmount"},
{"adfs", "RiscOS disk (%s)", "hdd_unmount"},
{"filecore", "RiscOS disk (%s)", "hdd_unmount"},
{"ados", "Amiga disk (%s)", "hdd_unmount"},
{"affs", "Amiga disk (%s)", "hdd_unmount"},
{"afs", "AFS shared directory (%s)", "server"},
{"befs", "BeOS disk (%s)", "hdd_unmount"},
{"bfs", "UnixWare disk (%s)", "hdd_unmount"},
{"efs", "SGI IRIX disk (%s)", "hdd_unmount"},
{"ext", "Old Linux disk (%s)", "hdd_unmount"},
{"hfs", "Macintosh disk (%s)", "hdd_unmount"}, // Also used for HP-UX filesystem!!
{"hpfs", "OS/2 disk (%s)", "hdd_unmount"},
// {"lfs", "BSD LFS disk (%s)", "hdd_unmount"}, // This is said to not work
{"minix", "Minix disk (%s)", "hdd_unmount"},
{"ncpfs", "NetWare shared directory (%s)", "server"},
{"nwfs", "NetWare shared directory (%s)", "server"},
{"qns", "QNX disk (%s)", "hdd_unmount"},
{"supermount", "Removable disk (%s)", "cdrom_umnount"}, // ???
{"xiafs", "Old Linux disk (%s)", "hdd_unmount"},
// Virtual filesystems (various views of existing files)
// How often are these used?
// {"cachefs", "Unix virtual disk ?? (%s)", "memory"},
// {"lofs", "Solaris virtual disk ?? (%s)", "memory"},
// {"null", "BSD virtual disk ?? (%s)", "memory"},
// {"nullfs", "BSD virtual disk ?? (%s)", "memory"},
// {"overlay", "BSD virtual disk ?? (%s)", "memory"},
// {"portal", "BSD virtual disk ?? (%s)", "memory"},
// {"portalfs", "BSD virtual disk ?? (%s)", "memory"},
// {"umap", "BSD virtual disk ?? (%s)", "memory"},
// {"umapfs", "BSD virtual disk ?? (%s)", "memory"},
{"", "", ""}
};
//
// 'FileBrowser::load()' - Load basic directory list into browser
//
int // O - Number of files loaded
DirTree::load()
{
int num_files=0;
char buffer[PATH_MAX]; // buffer for labels
Group* o; // generic pointer
this->begin();
// Top level icon
Group* system = (Group*)add_group(_("System"), 0, 0, edelib::Icon::get("tux",edelib::Icon::TINY));
// Home icon
snprintf(buffer,PATH_MAX,_("%s's Home"), getenv("USER"));
o = (Group*)add_group(strdup(buffer), system, 0, edelib::Icon::get("folder_home",edelib::Icon::TINY));
strncpy(buffer,getenv("HOME"),PATH_MAX);
// Home env. var. often doesn't end with slash
make_end_with_slash(buffer);
o->user_data(strdup(buffer));
// Root icon
o = (Group*)add_group(_("Whole disk"), system, 0, edelib::Icon::get("folder_red",edelib::Icon::TINY));
o->user_data(strdup("/"));
num_files=3;
// Read mtab/fstab/whatever
FILE *mtab; // /etc/mtab or /etc/mnttab file
char line[1024]; // Input line
char device[1024], mountpoint[1024], fs[1024];
// mtab doesn't contain removable entries, but we will find another way
// to add them (some day)
mtab = fopen("/etc/mnttab", "r"); // Fairly standard
if (mtab == NULL)
mtab = fopen("/etc/mtab", "r"); // More standard
if (mtab == NULL)
mtab = fopen("/etc/fstab", "r"); // Otherwise fallback to full list
if (mtab == NULL)
mtab = fopen("/etc/vfstab", "r"); // Alternate full list file
while (mtab!= NULL && fgets(line, sizeof(line), mtab) != NULL) {
if (line[0] == '#' || line[0] == '\n')
continue;
if (sscanf(line, "%s%s%s", device, mountpoint, fs) != 3)
continue;
make_end_with_slash(mountpoint);
// Stuff that should be invisible cause we already show it
if (strcmp(mountpoint,"/") == 0) continue; // root
if (strcmp(mountpoint,getenv("HOME")) == 0) continue; // home dir
// Device name without "/dev/"
char *shortdev = strrchr(device,'/');
if (shortdev==NULL) shortdev=device; else shortdev++;
// Go through list of known filesystems
for (int i=0; filesystems[i].id[0] != '\0'; i++) {
if (strcmp(fs, filesystems[i].id) == 0) {
if (filesystems[i].uiname[0] == '\0') break; // skipping this fs
snprintf(buffer, PATH_MAX, filesystems[i].uiname, shortdev);
o = (Group*)add_group(strdup(buffer), system, 0, edelib::Icon::get(filesystems[i].icon, edelib::Icon::TINY));
o->user_data(strdup(mountpoint));
num_files++;
}
}
}
fclose(mtab);
this->end();
system->set_flag(fltk::OPENED);
this->redraw();
return (num_files);
}
// Browser method for opening (expanding) a subtree
// We override this method so we could scan directory tree on demand
bool DirTree::set_item_opened(bool open) {
// Eliminate bogus call
if (!item()) return false;
// This is what we do different from Browser:
//fprintf (stderr, "Children: %d\n",children(current_index(),current_level()+1));
if (open && children(current_index(),current_level()+1)<=0) {
dirent** files;
char* directory = (char*)item()->user_data();
char filename[PATH_MAX];
// NOTE we default to casenumericsort!
int num_files = filename_list(directory, &files, casenumericsort);
Group* current = (Group*)item();
for (int i=0; i<num_files; i++) {
if (files[i]->d_name[0] == '.' && show_hidden_==false) continue;
if (!strcmp(files[i]->d_name,"./") || !strcmp(files[i]->d_name,"../")) continue;
snprintf(filename, PATH_MAX, "%s%s", directory, files[i]->d_name);
//fprintf(stderr, "Testing: %s\n",filename);
if (filename_isdir(filename)) {
// Strip slash from label
char *n = strdup(files[i]->d_name);
n[strlen(n)-1] = '\0';
Group* o = (Group*)add_group(n, current, 0, edelib::Icon::get("folder", edelib::Icon::TINY));
o->user_data(strdup(filename));
}
}
}
return Browser::set_item_opened(open);
}
// Recursively find tree entry whose user_data() (system path)
// contains the largest portion of given path
// Best match is focused, return value tells if perfect match was found
bool DirTree::set_current(const char* path) {
int tree[MAX_TREE_DEPTH];
// Copy of path where we add slash as needed
char mypath[PATH_MAX];
strncpy(mypath,path,PATH_MAX-2);
make_end_with_slash(mypath);
// There is always exactly one toplevel entry - "System"
tree[0]=0;
set_item(tree,1);
bool t = find_best_match(mypath,tree,1);
set_focus();
// Expand current item
set_item_opened(true);
return t;
}
// Recursive function called by set_current()
bool DirTree::find_best_match(const char* path, int* tree, int level) {
uint bestlen=0;
int bestindex=-1;
int n_children = children(tree,level);
// Given entry has no children
// let's forward this result to first caller
if (n_children == -1) return false;
// Look at all children of given entry and find the best match
for (int i=0; i<n_children; i++) {
tree[level]=i;
Widget* w = child(tree,level);
char* p = (char*)w->user_data();
if (strlen(p)>strlen(path)) continue; // too specific
if ((strncmp(path, p, strlen(p)) == 0) && (strlen(p)>bestlen)) {
bestlen=strlen(p);
bestindex=i;
}
}
// No matches found
if (bestindex==-1) return false;
// Focus on best match
tree[level]=bestindex;
goto_index(tree,level);
//char* p = (char*)child(tree,level)->user_data();
//fprintf (stderr, "Found: %s\n", p);
// Is this a perfect match?
if (bestlen == strlen(path)) return true;
// Can the best item be further expanded?
set_item_opened(true);
// Try its children (if none, it will return false)
return find_best_match(path, tree, level+1);
}
//
// End of "$Id$".
//