Test checkin of some modules
67
file/BorderPanel.java
Executable file
@ -0,0 +1,67 @@
|
||||
import java.awt.*;
|
||||
|
||||
class BorderPanel extends Panel
|
||||
{
|
||||
int border = 5; // size of border
|
||||
Color col1 = Util.light_edge;
|
||||
Color col2 = Util.dark_edge;
|
||||
Color body;
|
||||
|
||||
BorderPanel()
|
||||
{
|
||||
}
|
||||
|
||||
BorderPanel(int w)
|
||||
{
|
||||
border = w;
|
||||
}
|
||||
|
||||
BorderPanel(int w, Color cb)
|
||||
{
|
||||
border = w;
|
||||
body = cb;
|
||||
}
|
||||
|
||||
BorderPanel(int w, Color c1, Color c2)
|
||||
{
|
||||
border = w;
|
||||
col1 = c1; col2 = c2;
|
||||
}
|
||||
|
||||
BorderPanel(int w, Color c1, Color c2, Color cb)
|
||||
{
|
||||
border = w;
|
||||
col1 = c1; col2 = c2; body = cb;
|
||||
}
|
||||
|
||||
BorderPanel(Color c1, Color c2)
|
||||
{
|
||||
col1 = c1; col2 = c2;
|
||||
}
|
||||
|
||||
public Insets insets()
|
||||
{
|
||||
return new Insets(border+2, border+2, border+2, border+2);
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
if (body != null) {
|
||||
g.setColor(body);
|
||||
g.fillRect(0, 0, size().width, size().height);
|
||||
}
|
||||
super.paint(g);
|
||||
int w = size().width-1, h = size().height-1;
|
||||
g.setColor(col1);
|
||||
for(int i=0; i<border; i++) {
|
||||
g.drawLine(i,i,w-i,i);
|
||||
g.drawLine(i,i,i,h-i);
|
||||
}
|
||||
g.setColor(col2);
|
||||
for(int i=0; i<border; i++) {
|
||||
g.drawLine(w-i,h-i, w-i,i);
|
||||
g.drawLine(w-i,h-i, i,h-i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
file/CHANGELOG
Normal file
@ -0,0 +1,33 @@
|
||||
---- Changes since 1.130 ----
|
||||
An uploaded zip, tar or tar.gz file can be extracted in the directory it was uploaded to.
|
||||
Directories can now be downloaded as zip, tar or tar.gz files
|
||||
Added improved access control options to hide buttons.
|
||||
Added a chroot access control option to hide all directories above it.
|
||||
---- Changes since 1.190 ----
|
||||
Added checkbox for saving text files in DOS mode.
|
||||
Users can now be prevented from accessing certain directories by a new option on the access control page.
|
||||
---- Changes since 1.240 ----
|
||||
When searching for files, you can now search by their contents too.
|
||||
Added a Preview button, for viewing a scaled-down version of a GIF, JPEG or PNG image.
|
||||
---- Changes since 1.250 ----
|
||||
Absolute and relative paths like /tmp/foo and bar/foo can be used when renaming a file.
|
||||
The GD Perl module will be used for scaling preview images, if installed.
|
||||
---- Changes since 1.260 ----
|
||||
Added a button for editing HTML files, using a rich-text editor.
|
||||
---- Changes since 1.270 ----
|
||||
Added Module Config options for changing the font size for buttons and regular text.
|
||||
---- Changes since 1.290 ----
|
||||
Added the ability to extract tar.bz2 files.
|
||||
Added a History button next to the field for entering a directory to show, for quickly navigating to recently entered paths.
|
||||
Added text fields to the file info window showing the total size, number of sub-files and number of sub-directories in a directory. These are only populated when a new 'Get Size' button is clicked.
|
||||
---- Changes since 1.300 ----
|
||||
Added ACL options to prevent users from editing file permissions or ownership in the Info window, to stop filesystem mount points from being shown, and to restrict file contents searches.
|
||||
Added Extract button for un-compressing tar, tgz, zip and gz archives on the server.
|
||||
Added a Download button to the search results window, for downloading a selected matching file.
|
||||
---- Changes since 1.310 ----
|
||||
Added a popup progress window to track large uploads.
|
||||
---- Changes since 1.320 ----
|
||||
Removed the HTML editing button, and changed the Edit button to detect HTML files and launch the HTML editor instead (optional on the Module Config page).
|
||||
Added a button for creating a new HTML file.
|
||||
---- Changes since 1.330 ----
|
||||
Replaced the HTMLarea widget for editing .html files with Xinha.
|
264
file/CbButton.java
Normal file
@ -0,0 +1,264 @@
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
public class CbButton extends Canvas
|
||||
{
|
||||
public static final int LEFT = 0;
|
||||
public static final int RIGHT = 1;
|
||||
public static final int ABOVE = 2;
|
||||
public static final int BELOW = 3;
|
||||
|
||||
Image image;
|
||||
String string;
|
||||
CbButtonCallback callback;
|
||||
int imode;
|
||||
int iwidth, iheight, pwidth, pheight, twidth, theight;
|
||||
boolean inside, indent;
|
||||
|
||||
CbButtonGroup group;
|
||||
boolean selected;
|
||||
|
||||
Color lc1 = Util.light_edge, lc2 = Util.body, lc3 = Util.dark_edge;
|
||||
Color hc1 = Util.light_edge_hi, hc2 = Util.body_hi, hc3 = Util.dark_edge_hi;
|
||||
|
||||
public CbButton(Image i, CbButtonCallback cb)
|
||||
{
|
||||
this(i, null, LEFT, cb);
|
||||
}
|
||||
|
||||
public CbButton(String s, CbButtonCallback cb)
|
||||
{
|
||||
this(null, s, LEFT, cb);
|
||||
}
|
||||
|
||||
public CbButton(Image i, String s, int im, CbButtonCallback cb)
|
||||
{
|
||||
image = i;
|
||||
string = s;
|
||||
imode = im;
|
||||
callback = cb;
|
||||
if (image != null) {
|
||||
iwidth = Util.getWidth(image);
|
||||
iheight = Util.getHeight(image);
|
||||
}
|
||||
if (string != null) {
|
||||
twidth = Util.fnm.stringWidth(string);
|
||||
theight = Util.fnm.getHeight();
|
||||
}
|
||||
if (image != null && string != null) {
|
||||
switch(imode) {
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
pwidth = iwidth + twidth + 6;
|
||||
pheight = Math.max(iheight , theight) + 4;
|
||||
break;
|
||||
case ABOVE:
|
||||
case BELOW:
|
||||
pwidth = Math.max(iwidth, twidth) + 4;
|
||||
pheight = iheight + theight + 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (image != null) {
|
||||
pwidth = iwidth + 4;
|
||||
pheight = iheight + 4;
|
||||
}
|
||||
else if (string != null) {
|
||||
pwidth = twidth + 8;
|
||||
pheight = theight + 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**Make this button part of a mutual-exclusion group. Only one such
|
||||
* button can be indented at a time
|
||||
*/
|
||||
public void setGroup(CbButtonGroup g)
|
||||
{
|
||||
group = g;
|
||||
group.add(this);
|
||||
}
|
||||
|
||||
/**Make this button the selected one in it's group
|
||||
*/
|
||||
public void select()
|
||||
{
|
||||
if (group != null)
|
||||
group.select(this);
|
||||
}
|
||||
|
||||
/**Display the given string
|
||||
*/
|
||||
public void setText(String s)
|
||||
{
|
||||
string = s;
|
||||
image = null;
|
||||
twidth = Util.fnm.stringWidth(string);
|
||||
theight = Util.fnm.getHeight();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**Display the given image
|
||||
*/
|
||||
public void setImage(Image i)
|
||||
{
|
||||
string = null;
|
||||
image = i;
|
||||
iwidth = Util.getWidth(image);
|
||||
iheight = Util.getHeight(image);
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**Display the given image and text, with the given alignment mode
|
||||
*/
|
||||
public void setImageText(Image i, String s, int m)
|
||||
{
|
||||
image = i;
|
||||
string = s;
|
||||
imode = m;
|
||||
twidth = Util.fnm.stringWidth(string);
|
||||
theight = Util.fnm.getHeight();
|
||||
iwidth = Util.getWidth(image);
|
||||
iheight = Util.getHeight(image);
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
Color c1 = inside ? hc1 : lc1,
|
||||
c2 = inside ? hc2 : lc2,
|
||||
c3 = inside ? hc3 : lc3;
|
||||
int w = size().width, h = size().height;
|
||||
Color hi = indent||selected ? c3 : c1,
|
||||
lo = indent||selected ? c1 : c3;
|
||||
g.setColor(c2);
|
||||
g.fillRect(0, 0, w-1, h-1);
|
||||
g.setColor(hi);
|
||||
g.drawLine(0, 0, w-2, 0);
|
||||
g.drawLine(0, 0, 0, h-2);
|
||||
g.setColor(lo);
|
||||
g.drawLine(w-1, h-1, w-1, 1);
|
||||
g.drawLine(w-1, h-1, 1, h-1);
|
||||
if (inside) {
|
||||
/* g.setColor(hi);
|
||||
g.drawLine(1, 1, w-3, 1);
|
||||
g.drawLine(1, 1, 1, h-3); */
|
||||
g.setColor(lo);
|
||||
g.drawLine(w-2, h-2, w-2, 2);
|
||||
g.drawLine(w-2, h-2, 2, h-2);
|
||||
}
|
||||
|
||||
g.setColor(c3);
|
||||
g.setFont(Util.f);
|
||||
if (image != null && string != null) {
|
||||
if (imode == LEFT) {
|
||||
Dimension is = imgSize(w-twidth-6, h-4);
|
||||
g.drawImage(image, (w - is.width - twidth - 2)/2,
|
||||
(h-is.height)/2, is.width, is.height, this);
|
||||
g.drawString(string,
|
||||
(w - is.width - twidth - 2)/2 +is.width +2,
|
||||
(h + theight - Util.fnm.getDescent())/2);
|
||||
}
|
||||
else if (imode == RIGHT) {
|
||||
}
|
||||
else if (imode == ABOVE) {
|
||||
//Dimension is = imgSize(w-4, h-theight-6);
|
||||
g.drawImage(image, (w - iwidth)/2,
|
||||
(h - iheight - theight - 2)/2,
|
||||
iwidth, iheight, this);
|
||||
g.drawString(string, (w - twidth)/2, iheight+Util.fnm.getHeight()+2);
|
||||
}
|
||||
else if (imode == BELOW) {
|
||||
}
|
||||
}
|
||||
else if (image != null) {
|
||||
Dimension is = imgSize(w-4, h-4);
|
||||
g.drawImage(image, (w - is.width)/2, (h-is.height)/2,
|
||||
is.width, is.height, this);
|
||||
}
|
||||
else if (string != null) {
|
||||
g.drawString(string, (w - twidth)/2,
|
||||
(h+theight-Util.fnm.getDescent())/2);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(Graphics g) { paint(g); }
|
||||
|
||||
public boolean mouseEnter(Event e, int x, int y)
|
||||
{
|
||||
inside = true;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseExit(Event e, int x, int y)
|
||||
{
|
||||
inside = false;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseDown(Event e, int x, int y)
|
||||
{
|
||||
indent = true;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseUp(Event e, int x, int y)
|
||||
{
|
||||
if (x >= 0 && y >= 0 && x < size().width && y < size().height) {
|
||||
if (callback != null)
|
||||
callback.click(this);
|
||||
select();
|
||||
}
|
||||
indent = false;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return new Dimension(pwidth, pheight);
|
||||
}
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
return preferredSize();
|
||||
}
|
||||
|
||||
private Dimension imgSize(int mw, int mh)
|
||||
{
|
||||
float ws = (float)mw/(float)iwidth,
|
||||
hs = (float)mh/(float)iheight;
|
||||
float s = ws < hs ? ws : hs;
|
||||
if (s > 1) s = 1;
|
||||
return new Dimension((int)(iwidth*s), (int)(iheight*s));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface CbButtonCallback
|
||||
{
|
||||
void click(CbButton b);
|
||||
}
|
||||
|
||||
|
||||
class CbButtonGroup
|
||||
{
|
||||
Vector buttons = new Vector();
|
||||
|
||||
void add(CbButton b)
|
||||
{
|
||||
buttons.addElement(b);
|
||||
}
|
||||
|
||||
void select(CbButton b)
|
||||
{
|
||||
for(int i=0; i<buttons.size(); i++) {
|
||||
CbButton but = (CbButton)buttons.elementAt(i);
|
||||
but.selected = (b == but);
|
||||
but.repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
51
file/CbColorButton.java
Normal file
@ -0,0 +1,51 @@
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
/**A component for choosing a color
|
||||
*/
|
||||
public class CbColorButton extends Panel implements CbButtonCallback,
|
||||
CbColorWindowCallback
|
||||
{
|
||||
Color col;
|
||||
CbButton but;
|
||||
Vector pal;
|
||||
Image swatch = Util.createImage(32, 16);
|
||||
Graphics g = swatch.getGraphics();
|
||||
CbColorWindow win;
|
||||
|
||||
CbColorButton(Color c)
|
||||
{
|
||||
this(c, new Vector());
|
||||
}
|
||||
|
||||
CbColorButton(Color c, Vector p)
|
||||
{
|
||||
if (c == null) c = Color.black;
|
||||
col = c;
|
||||
g.setColor(col); g.fillRect(0, 0, 32, 16);
|
||||
setLayout(new BorderLayout());
|
||||
add("Center", but = new CbButton(swatch, this));
|
||||
}
|
||||
|
||||
public void click(CbButton b)
|
||||
{
|
||||
if (win == null)
|
||||
win = new CbColorWindow(col, this);
|
||||
}
|
||||
|
||||
public void chosen(CbColorWindow w, Color c)
|
||||
{
|
||||
if (c != null) {
|
||||
col = c;
|
||||
g.setColor(col); g.fillRect(0, 0, 32, 16);
|
||||
but.repaint();
|
||||
}
|
||||
win = null;
|
||||
}
|
||||
|
||||
public Vector palette(CbColorWindow w)
|
||||
{
|
||||
return pal;
|
||||
}
|
||||
}
|
||||
|
226
file/CbColorWindow.java
Normal file
@ -0,0 +1,226 @@
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
/**A window for choosing a colour, either from a pre-set palette
|
||||
* or from a color cube
|
||||
*/
|
||||
class CbColorWindow extends FixedFrame implements CbButtonCallback
|
||||
{
|
||||
CbColorWindowCallback callback;
|
||||
Color col;
|
||||
Vector pal;
|
||||
static Vector defpal = new Vector();
|
||||
Image palimg[] = new Image[12];
|
||||
CbButton palbut[] = new CbButton[12];
|
||||
int curpal = -1;
|
||||
CbButton ok, cancel;
|
||||
CbColorWindowCube ccube;
|
||||
|
||||
static
|
||||
{
|
||||
defpal.addElement(Color.black);
|
||||
defpal.addElement(Color.blue);
|
||||
defpal.addElement(Color.cyan);
|
||||
defpal.addElement(Color.gray);
|
||||
defpal.addElement(Color.green);
|
||||
defpal.addElement(Color.darkGray);
|
||||
defpal.addElement(Color.magenta);
|
||||
defpal.addElement(Color.orange);
|
||||
defpal.addElement(Color.pink);
|
||||
defpal.addElement(Color.red);
|
||||
defpal.addElement(Color.white);
|
||||
defpal.addElement(Color.yellow);
|
||||
}
|
||||
|
||||
CbColorWindow(Color c, CbColorWindowCallback cb)
|
||||
{
|
||||
col = c;
|
||||
callback = cb;
|
||||
|
||||
// Setup color vector
|
||||
pal = callback.palette(this);
|
||||
if (pal == null)
|
||||
pal = defpal;
|
||||
else if (pal.size() == 0)
|
||||
for(int i=0; i<12; i++)
|
||||
pal.addElement(defpal.elementAt(i));
|
||||
|
||||
// Create palette images
|
||||
for(int i=0; i<12; i++) {
|
||||
palimg[i] = Util.createImage(16, 16);
|
||||
updatePal(i);
|
||||
}
|
||||
|
||||
// create UI
|
||||
setLayout(new BorderLayout());
|
||||
Panel bot = new GrayPanel();
|
||||
bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
|
||||
bot.add(ok = new CbButton("Ok", this));
|
||||
bot.add(cancel = new CbButton("Cancel", this));
|
||||
add("South", bot);
|
||||
Panel mid = new BorderPanel(1);
|
||||
mid.setLayout(new BorderLayout());
|
||||
Panel midbot = new GrayPanel();
|
||||
midbot.setLayout(new GridLayout(2, 6, 4, 4));
|
||||
CbButtonGroup g = new CbButtonGroup();
|
||||
for(int i=0; i<12; i++) {
|
||||
midbot.add(palbut[i] = new CbButton(palimg[i], this));
|
||||
palbut[i].setGroup(g);
|
||||
}
|
||||
for(int i=0; i<12; i++)
|
||||
if (c.equals(pal.elementAt(i))) {
|
||||
curpal = i;
|
||||
palbut[i].select();
|
||||
break;
|
||||
}
|
||||
mid.add("South", midbot);
|
||||
mid.add("North", ccube = new CbColorWindowCube(this));
|
||||
add("Center", mid);
|
||||
|
||||
pack();
|
||||
show();
|
||||
setTitle("Choose Color...");
|
||||
}
|
||||
|
||||
void updatePal(int i)
|
||||
{
|
||||
Graphics g = palimg[i].getGraphics();
|
||||
g.setColor((Color)pal.elementAt(i));
|
||||
g.fillRect(0, 0, 16, 16);
|
||||
if (palbut[i] != null) palbut[i].repaint();
|
||||
}
|
||||
|
||||
public void click(CbButton b)
|
||||
{
|
||||
if (b == ok) {
|
||||
callback.chosen(this, col);
|
||||
super.dispose();
|
||||
}
|
||||
else if (b == cancel)
|
||||
dispose();
|
||||
else {
|
||||
for(int i=0; i<12; i++)
|
||||
if (b == palbut[i]) {
|
||||
curpal = i;
|
||||
col = (Color)pal.elementAt(i);
|
||||
ccube.red.setPosition(col.getRed());
|
||||
ccube.blue.setPosition(col.getBlue());
|
||||
ccube.green.setPosition(col.getGreen());
|
||||
ccube.swatch.setColor(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
callback.chosen(this, null);
|
||||
}
|
||||
|
||||
public boolean isResizable() { return false; }
|
||||
}
|
||||
|
||||
/**Displays 3 sliders, for red green and blue plus a block to show the
|
||||
* current color
|
||||
*/
|
||||
class CbColorWindowCube extends BorderPanel implements CbSliderCallback
|
||||
{
|
||||
CbColorWindow parent;
|
||||
CbSlider red, green, blue;
|
||||
CbColorWindowSwatch swatch;
|
||||
|
||||
CbColorWindowCube(CbColorWindow p)
|
||||
{
|
||||
super(1, Util.body, Util.body);
|
||||
parent = p;
|
||||
setLayout(new BorderLayout());
|
||||
Panel sl = new GrayPanel();
|
||||
sl.setLayout(new GridLayout(3, 1));
|
||||
sl.add(red = new CbSlider(0, 0, 255, p.col.getRed(), this));
|
||||
sl.add(green = new CbSlider(0, 0, 255, p.col.getBlue(), this));
|
||||
sl.add(blue = new CbSlider(0, 0, 255, p.col.getGreen(), this));
|
||||
add("Center", sl);
|
||||
add("East", swatch = new CbColorWindowSwatch(p.col));
|
||||
}
|
||||
|
||||
public void moved(CbSlider s, int p)
|
||||
{
|
||||
moving(s, p);
|
||||
}
|
||||
|
||||
public void moving(CbSlider s, int p)
|
||||
{
|
||||
parent.col = new Color(red.getPosition(), green.getPosition(),
|
||||
blue.getPosition());
|
||||
swatch.setColor(parent.col);
|
||||
if (parent.curpal != -1) {
|
||||
parent.pal.setElementAt(parent.col, parent.curpal);
|
||||
parent.updatePal(parent.curpal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface CbColorWindowCallback
|
||||
{
|
||||
/**This method will be called when the user chooses a colour. If
|
||||
* the user cancels the dialog, then this method will also be chosen
|
||||
* but with null for the color.
|
||||
*/
|
||||
public void chosen(CbColorWindow w, Color c);
|
||||
|
||||
/**The chooser keeps a palette of colors that the user can modify,
|
||||
* stored in a vector. The callback class should provide this vector
|
||||
* so as to maintain the palette between color window calls.
|
||||
* If an empty vector is returned, it will be filled with the default
|
||||
* color table (which can be then modified).
|
||||
* If null is returned, the chooser will use it's own internal
|
||||
* vector.
|
||||
*/
|
||||
public Vector palette(CbColorWindow w);
|
||||
}
|
||||
|
||||
|
||||
class CbColorWindowSwatch extends BorderPanel
|
||||
{
|
||||
Color col = Color.black;
|
||||
String txt;
|
||||
|
||||
CbColorWindowSwatch(Color c)
|
||||
{
|
||||
super(1);
|
||||
setColor(c);
|
||||
}
|
||||
|
||||
void setColor(Color c)
|
||||
{
|
||||
col = c;
|
||||
txt = col.getRed()+","+col.getGreen()+","+col.getBlue();
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
g.setColor(col);
|
||||
g.fillRect(1, 1, size().width-2, size().height-2);
|
||||
g.setColor(Color.white);
|
||||
g.setXORMode(Color.black);
|
||||
g.setFont(Util.f);
|
||||
g.drawString(txt, 3, Util.fnm.getHeight()+1);
|
||||
g.setPaintMode();
|
||||
}
|
||||
|
||||
public void upate(Graphics g) { paint(g); }
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return new Dimension(60, 60);
|
||||
}
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
return preferredSize();
|
||||
}
|
||||
}
|
||||
|
233
file/CbImageChooser.java
Normal file
@ -0,0 +1,233 @@
|
||||
import java.awt.*;
|
||||
import java.net.*;
|
||||
|
||||
class CbImageChooser extends Panel implements CbButtonCallback
|
||||
{
|
||||
Image img;
|
||||
String imgsrc;
|
||||
int imgw, imgh;
|
||||
CbButton but;
|
||||
CbImageFileWindow filewin;
|
||||
//CbImageChooserCallback callback;
|
||||
|
||||
CbImageChooser(Image i)
|
||||
{
|
||||
this(i, null);
|
||||
}
|
||||
|
||||
CbImageChooser(Image i, String s)
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
add("Center", but = new CbButton("Choose..", this));
|
||||
setImage(i, s==null ? "" : s);
|
||||
}
|
||||
|
||||
void setImage(Image i, String s)
|
||||
{
|
||||
img = i;
|
||||
imgsrc = s;
|
||||
if (img != null) but.setImage(img);
|
||||
else but.setText("Choose..");
|
||||
}
|
||||
|
||||
public void click(CbButton b)
|
||||
{
|
||||
if (b == but && filewin == null)
|
||||
new CbImageFileWindow(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CbImageFileWindow extends FixedFrame implements CbButtonCallback
|
||||
{
|
||||
CbImageChooser parent;
|
||||
ScrollImage imgp;
|
||||
TextField url;
|
||||
CbButton browse, ok, cancel;
|
||||
FileDialog filedlog;
|
||||
String lastfile = "";
|
||||
|
||||
CbImageFileWindow(CbImageChooser p)
|
||||
{
|
||||
parent = p;
|
||||
parent.filewin = this;
|
||||
setLayout(new BorderLayout());
|
||||
add("Center", imgp = new ScrollImage(parent.img, 200, 200));
|
||||
Panel bot = new GrayPanel();
|
||||
bot.setLayout(new FlowLayout(FlowLayout.LEFT));
|
||||
bot.add(new Label("URL:"));
|
||||
bot.add(url = new TextField(parent.imgsrc, 20));
|
||||
bot.add(browse = new CbButton("Browse..", this));
|
||||
bot.add(new Label(" "));
|
||||
bot.add(ok = new CbButton("Ok", this));
|
||||
bot.add(cancel = new CbButton("Cancel", this));
|
||||
add("South", bot);
|
||||
|
||||
pack();
|
||||
show();
|
||||
setTitle("Choose Image..");
|
||||
Util.recursiveBackground(this, Util.body);
|
||||
}
|
||||
|
||||
public void click(CbButton b)
|
||||
{
|
||||
if (b == ok)
|
||||
parent.setImage(imgp.img, lastfile);
|
||||
if (b == ok || b == cancel)
|
||||
dispose();
|
||||
else if (b == browse) {
|
||||
// Open file chooser here!
|
||||
FileDialog filedlog =
|
||||
new FileDialog(this, "Choose Image",FileDialog.LOAD);
|
||||
filedlog.show();
|
||||
if (filedlog.getFile() != null) {
|
||||
// file chosen.. load it in
|
||||
String fn = filedlog.getDirectory()+filedlog.getFile();
|
||||
url.setText(fn);
|
||||
loadFile(fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
parent.filewin = null;
|
||||
}
|
||||
|
||||
public boolean action(Event evt, Object obj)
|
||||
{
|
||||
if (evt.target == url) {
|
||||
String ut = url.getText();
|
||||
if (ut.startsWith("http:") || ut.startsWith("ftp:"))
|
||||
loadURL(ut);
|
||||
else
|
||||
loadFile(ut);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void loadFile(String f)
|
||||
{
|
||||
Image i = Util.tk.getImage(f);
|
||||
if (i == null || !Util.waitForImage(i))
|
||||
new ErrorWindow("Failed to load image "+f);
|
||||
else {
|
||||
imgp.setImage(i);
|
||||
lastfile = f;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadURL(String u)
|
||||
{
|
||||
try {
|
||||
Image i = Util.tk.getImage(new URL(u));
|
||||
if (i == null || !Util.waitForImage(i))
|
||||
new ErrorWindow("Failed to load image from "+u);
|
||||
else {
|
||||
imgp.setImage(i);
|
||||
lastfile = u;
|
||||
}
|
||||
}
|
||||
catch(MalformedURLException e) {
|
||||
new ErrorWindow(u+" is not a valid URL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ScrollImage extends Panel implements CbScrollbarCallback
|
||||
{
|
||||
Image img;
|
||||
int imgw, imgh;
|
||||
int pw, ph;
|
||||
CbScrollbar vsc, hsc;
|
||||
boolean compute_scrollbars = true;
|
||||
|
||||
ScrollImage(Image i)
|
||||
{
|
||||
this(i, Util.getWidth(i), Util.getHeight(i));
|
||||
}
|
||||
|
||||
ScrollImage(Image i, int w, int h)
|
||||
{
|
||||
pw = w; ph = h;
|
||||
setLayout(new BorderLayout());
|
||||
add("East", vsc = new CbScrollbar(CbScrollbar.VERTICAL, this));
|
||||
add("South", hsc = new CbScrollbar(CbScrollbar.HORIZONTAL, this));
|
||||
setImage(i);
|
||||
}
|
||||
|
||||
void setImage(Image i)
|
||||
{
|
||||
img = i;
|
||||
if (img != null) {
|
||||
imgw = Util.getWidth(img);
|
||||
imgh = Util.getHeight(img);
|
||||
}
|
||||
compute_scrollbars = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
int w = size().width-vsc.size().width,
|
||||
h = size().height-hsc.size().height;
|
||||
if (compute_scrollbars) {
|
||||
if (img == null) {
|
||||
hsc.setValues(0, 1, 1);
|
||||
vsc.setValues(0, 1, 1);
|
||||
}
|
||||
else {
|
||||
if (imgw < w) hsc.setValues(0, 1, 1);
|
||||
else hsc.setValues(0, w, imgw);
|
||||
if (imgh < h) vsc.setValues(0, 1, 1);
|
||||
else vsc.setValues(0, h, imgh);
|
||||
}
|
||||
compute_scrollbars = false;
|
||||
}
|
||||
|
||||
g.setColor(Util.body);
|
||||
g.fillRect(0, 0, w, h);
|
||||
if (img != null) {
|
||||
if (imgw < w && imgh < h)
|
||||
g.drawImage(img, (w-imgw)/2, (h-imgh)/2, this);
|
||||
else
|
||||
g.drawImage(img, -hsc.getValue(), -vsc.getValue(),this);
|
||||
}
|
||||
else {
|
||||
g.setFont(Util.f);
|
||||
g.setColor(Util.text);
|
||||
String s = "<None>";
|
||||
g.drawString(s, (w-Util.fnm.stringWidth(s))/2,
|
||||
(h-Util.fnm.getHeight())/2);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(Graphics g) { paint(g); }
|
||||
|
||||
public void reshape(int nx, int ny, int nw, int nh)
|
||||
{
|
||||
super.reshape(nx, ny, nw, nh);
|
||||
compute_scrollbars = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void moved(CbScrollbar s, int p)
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void moving(CbScrollbar s, int p) { }
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
return new Dimension(pw, ph);
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return minimumSize();
|
||||
}
|
||||
}
|
345
file/CbScrollbar.java
Normal file
@ -0,0 +1,345 @@
|
||||
// CbScrollbar.java
|
||||
// A drop-in replacement for the AWT scrollbar class, with callbacks
|
||||
// and a nicer look. This scrollbar is typically used to display some
|
||||
// fraction of a list of items, with values ranging from min to max.
|
||||
// The lvisible parameter determines how many of the list are lvisible
|
||||
// at any one time. The value of the scrollbar ranges from min to
|
||||
// max-lvisible+1 (the highest position in the list to start displaying)
|
||||
import java.awt.*;
|
||||
|
||||
public class CbScrollbar extends Panel
|
||||
{
|
||||
static final int VERTICAL = 0;
|
||||
static final int HORIZONTAL = 1;
|
||||
CbScrollbarCallback callback; // who to call back to
|
||||
boolean inside, indent;
|
||||
int orient; // horizontal or vertical?
|
||||
int value; // position
|
||||
int lvisible; // the number of lines lvisible
|
||||
int num; // total number of lines
|
||||
int lineinc = 1; // how much the arrow buttons move by
|
||||
Color lc1 = Util.light_edge, lc2 = Util.body, lc3 = Util.dark_edge;
|
||||
Color hc1 = Util.light_edge_hi, hc2 = Util.body_hi, hc3 = Util.dark_edge_hi;
|
||||
Color bc = Util.dark_bg;
|
||||
int y1, y2, x1, x2, drag;
|
||||
|
||||
CbScrollbarArrow arrow1, arrow2;
|
||||
|
||||
CbScrollbar(int o, CbScrollbarCallback cb)
|
||||
{
|
||||
this(o, 0, 1, 1, cb);
|
||||
}
|
||||
|
||||
/**Create a new scrollbar
|
||||
*/
|
||||
CbScrollbar(int o, int v, int vis, int n, CbScrollbarCallback cb)
|
||||
{
|
||||
setValues(v, vis, n);
|
||||
orient = o;
|
||||
callback = cb;
|
||||
setLayout(null);
|
||||
if (orient == VERTICAL) {
|
||||
add(arrow1 = new CbScrollbarArrow(this, 0));
|
||||
add(arrow2 = new CbScrollbarArrow(this, 1));
|
||||
}
|
||||
else {
|
||||
add(arrow1 = new CbScrollbarArrow(this, 2));
|
||||
add(arrow2 = new CbScrollbarArrow(this, 3));
|
||||
}
|
||||
}
|
||||
|
||||
/**Set the current scrollbar parameters
|
||||
* @param v Current position
|
||||
* @param vis Number of lines lvisible
|
||||
* @param n Total number of lines
|
||||
*/
|
||||
public void setValues(int v, int vis, int n)
|
||||
{
|
||||
value = v;
|
||||
lvisible = vis;
|
||||
num = n;
|
||||
if (lvisible > num) lvisible = num;
|
||||
checkValue();
|
||||
repaint();
|
||||
}
|
||||
|
||||
public int getValue() { return value; }
|
||||
|
||||
public void setValue(int v)
|
||||
{
|
||||
value = v;
|
||||
checkValue();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void checkValue()
|
||||
{
|
||||
if (value < 0) value = 0;
|
||||
else if (value > num-lvisible) value = num-lvisible;
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
if (num == 0) return;
|
||||
int w = size().width, h = size().height;
|
||||
boolean ins = inside && !(arrow1.inside || arrow2.inside);
|
||||
Color c1 = ins ? hc1 : lc1, c2 = ins ? hc2 : lc2,
|
||||
c3 = ins ? hc3 : lc3;
|
||||
g.setColor(bc);
|
||||
g.fillRect(0, 0, w, h);
|
||||
g.setColor(c3);
|
||||
g.drawLine(0, 0, w-1, 0); g.drawLine(0, 0, 0, h-1);
|
||||
g.setColor(c1);
|
||||
g.drawLine(w-1, h-1, w-1, 0); g.drawLine(w-1, h-1, 0, h-1);
|
||||
|
||||
if (orient == VERTICAL) {
|
||||
int va = h-w*2;
|
||||
y1 = w+va*value/num;
|
||||
y2 = w+va*(value+lvisible)/num-1;
|
||||
g.setColor(c2);
|
||||
g.fillRect(1, y1, w-2, y2-y1);
|
||||
g.setColor(indent ? c3 : c1);
|
||||
g.drawLine(1, y1, w-2, y1);
|
||||
g.drawLine(1, y1, 1, y2-1);
|
||||
g.setColor(indent ? c1 : c3);
|
||||
g.drawLine(w-2, y2-1, w-2, y1);
|
||||
g.drawLine(w-2, y2-1, 1, y2-1);
|
||||
if (ins) {
|
||||
g.drawLine(w-3, y2-2, w-3, y1+1);
|
||||
g.drawLine(w-3, y2-2, 2, y2-2);
|
||||
}
|
||||
}
|
||||
else if (orient == HORIZONTAL) {
|
||||
int va = w-h*2;
|
||||
x1 = h+va*value/num;
|
||||
x2 = h+va*(value+lvisible)/num-1;
|
||||
g.setColor(c2);
|
||||
g.fillRect(x1, 1, x2-x1, h-2);
|
||||
g.setColor(indent ? c3 : c1);
|
||||
g.drawLine(x1, 1, x1, h-2);
|
||||
g.drawLine(x1, 1, x2-1, 1);
|
||||
g.setColor(indent ? c1 : c3);
|
||||
g.drawLine(x2-1, h-2, x1, h-2);
|
||||
g.drawLine(x2-1, h-2, x2-1, 1);
|
||||
if (ins) {
|
||||
g.drawLine(x2-2, h-3, x1+1, h-3);
|
||||
g.drawLine(x2-2, h-3, x2-2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**Called by arrows to move the slider
|
||||
*/
|
||||
void arrowClick(int d)
|
||||
{
|
||||
int oldvalue = value;
|
||||
value += d;
|
||||
checkValue();
|
||||
if (value != oldvalue) {
|
||||
callback.moved(this, value);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void reshape(int nx, int ny, int nw, int nh)
|
||||
{
|
||||
super.reshape(nx, ny, nw, nh);
|
||||
if (orient == VERTICAL) {
|
||||
arrow1.reshape(1, 1, nw-2, nw-1);
|
||||
arrow2.reshape(1, nh-nw-1, nw-2, nw-1);
|
||||
}
|
||||
else {
|
||||
arrow1.reshape(1, 1, nh-1, nh-2);
|
||||
arrow2.reshape(nw-nh-1, 1, nh-1, nh-2);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return orient==VERTICAL ? new Dimension(16, 100)
|
||||
: new Dimension(100, 16);
|
||||
}
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
return preferredSize();
|
||||
}
|
||||
|
||||
public boolean mouseDown(Event e, int mx, int my)
|
||||
{
|
||||
if (orient == VERTICAL) {
|
||||
// move up/down one page, or start dragging
|
||||
if (my < y1) arrowClick(-lvisible);
|
||||
else if (my > y2) arrowClick(lvisible);
|
||||
else {
|
||||
indent = true;
|
||||
drag = my-y1;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// move left/right one page, or start dragging
|
||||
if (mx < x1) arrowClick(-lvisible);
|
||||
else if (mx > x2) arrowClick(lvisible);
|
||||
else {
|
||||
indent = true;
|
||||
drag = mx-x1;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseDrag(Event e, int mx, int my)
|
||||
{
|
||||
if (indent) {
|
||||
int w = size().width, h = size().height;
|
||||
int oldvalue = value;
|
||||
if (orient == VERTICAL) {
|
||||
int va = h-w*2, ny = my-drag-w;
|
||||
value = ny*num/va;
|
||||
}
|
||||
else {
|
||||
int va = w-h*2, nx = mx-drag-h;
|
||||
value = nx*num/va;
|
||||
}
|
||||
checkValue();
|
||||
if (value != oldvalue) {
|
||||
callback.moving(this, value);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
return indent;
|
||||
}
|
||||
|
||||
public boolean mouseUp(Event e, int mx, int my)
|
||||
{
|
||||
if (indent) {
|
||||
indent = false;
|
||||
repaint();
|
||||
callback.moved(this, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
public boolean mouseEnter(Event e, int mx, int my)
|
||||
{
|
||||
inside = true;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseExit(Event e, int mx, int my)
|
||||
{
|
||||
inside = false;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
class CbScrollbarArrow extends Canvas implements Runnable
|
||||
{
|
||||
int mode;
|
||||
CbScrollbar scrollbar;
|
||||
boolean inside, indent;
|
||||
Thread th;
|
||||
|
||||
CbScrollbarArrow(CbScrollbar p, int m)
|
||||
{
|
||||
scrollbar = p;
|
||||
mode = m;
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
int w = size().width, h = size().height;
|
||||
Color c1 = inside ? scrollbar.hc1 : scrollbar.lc1,
|
||||
c2 = inside ? scrollbar.hc2 : scrollbar.lc2,
|
||||
c3 = inside ? scrollbar.hc3 : scrollbar.lc3;
|
||||
g.setColor(scrollbar.bc);
|
||||
g.fillRect(0, 0, w, h);
|
||||
int xp[] = new int[3], yp[] = new int[3];
|
||||
// blank, dark, light
|
||||
if (mode == 0) {
|
||||
// up arrow
|
||||
xp[0] = w/2; xp[1] = w-1; xp[2] = 0;
|
||||
yp[0] = 0; yp[1] = h-1; yp[2] = h-1;
|
||||
}
|
||||
else if (mode == 1) {
|
||||
// down arrow
|
||||
xp[0] = 0; xp[1] = w/2; xp[2] = w-1;
|
||||
yp[0] = 0; yp[1] = h-1; yp[2] = 0;
|
||||
}
|
||||
else if (mode == 2) {
|
||||
// left arrow
|
||||
xp[0] = 0; xp[1] = w-1; xp[2] = w-1;
|
||||
yp[0] = h/2; yp[1] = h-1; yp[2] = 0;
|
||||
}
|
||||
else if (mode == 3) {
|
||||
// right arrow
|
||||
xp[0] = 0; xp[1] = w-1; xp[2] = 0;
|
||||
yp[0] = 0; yp[1] = h/2; yp[2] = h-1;
|
||||
}
|
||||
g.setColor(c2);
|
||||
g.fillPolygon(xp, yp, 3);
|
||||
g.setColor(indent ? c1 : c3);
|
||||
g.drawLine(xp[1], yp[1], xp[2], yp[2]);
|
||||
g.setColor(indent ? c3 : c1);
|
||||
g.drawLine(xp[0], yp[0], xp[2], yp[2]);
|
||||
}
|
||||
|
||||
public boolean mouseDown(Event e, int mx, int my)
|
||||
{
|
||||
indent = true;
|
||||
repaint();
|
||||
(th = new Thread(this)).start();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseUp(Event e, int mx, int my)
|
||||
{
|
||||
indent = false;
|
||||
repaint();
|
||||
if (th != null) th.stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**Thread for doing repeated scrolling
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
int stime = 500;
|
||||
while(true) {
|
||||
scrollbar.arrowClick(mode%2 == 0 ? -1 : 1);
|
||||
try { Thread.sleep(stime); } catch(Exception e) { }
|
||||
stime = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// CbScrollbarCallback
|
||||
// Methods for reporting the movement of the scrollbar to another object
|
||||
interface CbScrollbarCallback
|
||||
{
|
||||
/**Called when the scrollbar stops moving. This happens when an
|
||||
* arrow is clicked, the scrollbar is moved by a page, or the user
|
||||
* lets go of the scrollbar after dragging it.
|
||||
* @param sb The scrollar that has been moved
|
||||
* @param v The new value
|
||||
*/
|
||||
void moved(CbScrollbar sb, int v);
|
||||
|
||||
/**Called upon every pixel movement of the scrollbar when it is
|
||||
* being dragged, but NOT when moved() is called.
|
||||
* @param sb The scrollar that has been moved
|
||||
* @param v The new value
|
||||
*/
|
||||
void moving(CbScrollbar sb, int v);
|
||||
}
|
||||
|
||||
|
233
file/CbSlider.java
Normal file
@ -0,0 +1,233 @@
|
||||
import java.awt.*;
|
||||
|
||||
class CbSlider extends Canvas
|
||||
{
|
||||
int dir, min, max, pos;
|
||||
CbSliderCallback callback;
|
||||
int px, py;
|
||||
Color lc1 = Util.light_edge, lc2 = Util.body, lc3 = Util.dark_edge;
|
||||
Color hc1 = Util.light_edge_hi, hc2 = Util.body_hi, hc3 = Util.dark_edge_hi;
|
||||
int ticks = 0;
|
||||
boolean inside = false, dragging = false;
|
||||
int dragx;
|
||||
|
||||
/**Create a new slider
|
||||
* @param d 0=horizontal, 1=vertical
|
||||
* @param mi Minimum value
|
||||
* @param ma Maximum value
|
||||
* @param p Current value
|
||||
*/
|
||||
public CbSlider(int d, int mi, int ma, int p)
|
||||
{
|
||||
this(d, mi, ma, p, null);
|
||||
}
|
||||
|
||||
/**Create a new slider
|
||||
* @param d 0=horizontal, 1=vertical
|
||||
* @param mi Minimum value
|
||||
* @param ma Maximum value
|
||||
* @param p Current value
|
||||
* @param cb Object to call back to
|
||||
*/
|
||||
public CbSlider(int d, int mi, int ma, int p, CbSliderCallback cb)
|
||||
{
|
||||
dir = d; min = mi; max = ma;
|
||||
pos = p;
|
||||
callback = cb;
|
||||
}
|
||||
|
||||
/**Toggle drawing of tick-marks on the slider track
|
||||
* @param t The number of units/tick, or 0 to disable
|
||||
*/
|
||||
public void setTicks(int t)
|
||||
{
|
||||
ticks = t;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**Returns the current slider position
|
||||
*/
|
||||
public int getPosition() { return pos; }
|
||||
|
||||
/**Sets the current slider position
|
||||
*/
|
||||
public void setPosition(int p)
|
||||
{
|
||||
if (pos != p) {
|
||||
pos = p;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/**Returns the current minimum slider value
|
||||
*/
|
||||
public int getMinimum() { return min; }
|
||||
|
||||
/**Sets the minimum slider value
|
||||
* @param mi The new minimum
|
||||
*/
|
||||
public void setMinimum(int mi)
|
||||
{
|
||||
min = mi;
|
||||
checkPos();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**Returns the current maximum slider value
|
||||
*/
|
||||
public int getMaximum() { return max; }
|
||||
|
||||
/**Sets the maximum slider value
|
||||
* @param mx The new maximum
|
||||
*/
|
||||
public void setMaximum(int mx)
|
||||
{
|
||||
max = mx;
|
||||
checkPos();
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
Color c1 = inside ? hc1 : lc1,
|
||||
c2 = inside ? hc2 : lc2,
|
||||
c3 = inside ? hc3 : lc3;
|
||||
|
||||
// draw slider track
|
||||
int w = size().width, h = size().height;
|
||||
g.setColor(c2);
|
||||
g.fillRect(0, 0, w, h);
|
||||
g.setColor(c3);
|
||||
g.drawLine(8, h/2, w-8, h/2);
|
||||
g.setColor(c1);
|
||||
g.drawLine(8, h/2+1, w-8, h/2+1);
|
||||
|
||||
// draw border
|
||||
g.setColor(c1);
|
||||
g.drawLine(0, 0, w-1, 0);
|
||||
g.drawLine(0, 0, 0, h-1);
|
||||
g.setColor(c3);
|
||||
g.drawLine(w-1, h-1, w-1, 0);
|
||||
g.drawLine(w-1, h-1, 0, h-1);
|
||||
if (inside) {
|
||||
g.drawLine(w-2, h-2, w-2, 0);
|
||||
g.drawLine(w-2, h-2, 0, h-2);
|
||||
}
|
||||
|
||||
// draw tick marks
|
||||
if (ticks != 0) {
|
||||
int mm = max-min;
|
||||
for(int i=0; i<=mm; i+=ticks) {
|
||||
int tx = ((w-16)*i / mm) + 8;
|
||||
g.setColor(c3);
|
||||
g.drawLine(tx, h/2, tx, h/2-6);
|
||||
}
|
||||
}
|
||||
|
||||
// draw slider
|
||||
px = ((w-16)*pos / (max - min)) + 8;
|
||||
py = h/2;
|
||||
g.setColor(c2);
|
||||
int xpt[] = { px-3, px-3, px, px+3, px+3 };
|
||||
int ypt[] = { py+5, py-4, py-6, py-4, py+5 };
|
||||
g.fillPolygon(xpt, ypt, 5);
|
||||
g.setColor(dragging ? c3 : c1);
|
||||
g.drawLine(px-3, py+5, px-3, py-4);
|
||||
g.drawLine(px-3, py-4, px, py-6);
|
||||
g.setColor(dragging ? c1 : c3);
|
||||
g.drawLine(px-3, py+5, px+3, py+5);
|
||||
g.drawLine(px+3, py+5, px+3, py-4);
|
||||
}
|
||||
|
||||
public void update(Graphics g) { paint(g); }
|
||||
|
||||
public boolean mouseEnter(Event e, int x, int y)
|
||||
{
|
||||
inside = true;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseDown(Event e, int x, int y)
|
||||
{
|
||||
int step = ticks==0 ? (max-min)/10 : ticks;
|
||||
if (x < px-3) {
|
||||
// move one tick to the left
|
||||
pos -= step;
|
||||
}
|
||||
else if (x > px+3) {
|
||||
// move one tick to the right
|
||||
pos += step;
|
||||
}
|
||||
else {
|
||||
// start dragging
|
||||
dragging = true;
|
||||
dragx = x-px;
|
||||
}
|
||||
checkPos();
|
||||
if (callback != null)
|
||||
callback.moved(this, pos);
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mouseDrag(Event e, int x, int y)
|
||||
{
|
||||
if (dragging) {
|
||||
px = x-dragx;
|
||||
pos = (px-8)*(max - min) / (size().width-16);
|
||||
checkPos();
|
||||
if (callback != null)
|
||||
callback.moving(this, pos);
|
||||
repaint();
|
||||
}
|
||||
return dragging;
|
||||
}
|
||||
|
||||
public boolean mouseUp(Event e, int x, int y)
|
||||
{
|
||||
if (dragging) {
|
||||
dragging = false;
|
||||
if (callback != null)
|
||||
callback.moved(this, pos);
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean mouseExit(Event e, int x, int y)
|
||||
{
|
||||
inside = false;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void checkPos()
|
||||
{
|
||||
if (pos < min) pos = min;
|
||||
else if (pos > max) pos = max;
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return new Dimension(100, 20);
|
||||
}
|
||||
|
||||
public Dimension minimumSize() { return preferredSize(); }
|
||||
}
|
||||
|
||||
interface CbSliderCallback
|
||||
{
|
||||
/**Callled back when the slider stops at a new position
|
||||
* @param s The slider being moved
|
||||
* @param p New position
|
||||
*/
|
||||
public void moved(CbSlider s, int p);
|
||||
|
||||
/**Callled back whenever the slider is being dragged
|
||||
* @param s The slider being moved
|
||||
* @param p New position
|
||||
*/
|
||||
public void moving(CbSlider s, int p);
|
||||
}
|
34
file/ErrorWindow.java
Normal file
@ -0,0 +1,34 @@
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
class ErrorWindow extends FixedFrame implements CbButtonCallback
|
||||
{
|
||||
CbButton ok;
|
||||
|
||||
ErrorWindow(String m)
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
Panel cen = new BorderPanel(1);
|
||||
StringTokenizer tok = new StringTokenizer(m, "\r\n");
|
||||
cen.setLayout(new GridLayout(tok.countTokens(), 1));
|
||||
while(tok.hasMoreTokens()) {
|
||||
cen.add(new Label(tok.nextToken()));
|
||||
}
|
||||
add("Center", cen);
|
||||
Panel bot = new GrayPanel();
|
||||
bot.setLayout(new FlowLayout(FlowLayout.CENTER));
|
||||
bot.add(new CbButton("Ok", this));
|
||||
add("South", bot);
|
||||
pack();
|
||||
show();
|
||||
setTitle("Error");
|
||||
Util.recursiveBackground(this, Util.body);
|
||||
}
|
||||
|
||||
public void click(CbButton b)
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
4504
file/FileManager.java
Normal file
3497
file/FileManager.java.bak
Normal file
46
file/FixedFrame.java
Normal file
@ -0,0 +1,46 @@
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
|
||||
public class FixedFrame extends Frame
|
||||
{
|
||||
int mw = 0, mh = 0;
|
||||
|
||||
public FixedFrame()
|
||||
{
|
||||
Dimension d = Util.tk.getScreenSize();
|
||||
double rx = Math.random(), ry = Math.random();
|
||||
move((int)((d.width/2)*rx), (int)((d.height/2)*ry));
|
||||
}
|
||||
|
||||
public FixedFrame(int w, int h)
|
||||
{
|
||||
this();
|
||||
mw = w; mh = h;
|
||||
}
|
||||
|
||||
public boolean handleEvent(Event evt)
|
||||
{
|
||||
if (evt.target == this && evt.id == Event.WINDOW_DESTROY) {
|
||||
dispose();
|
||||
return true;
|
||||
}
|
||||
return super.handleEvent(evt);
|
||||
}
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
if (mw != 0 && mh != 0) return new Dimension(mw, mh);
|
||||
else return super.minimumSize();
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return minimumSize();
|
||||
}
|
||||
|
||||
public void setFixedSize(int w, int h)
|
||||
{
|
||||
mw = w; mh = h;
|
||||
}
|
||||
}
|
||||
|
10
file/GrayPanel.java
Normal file
@ -0,0 +1,10 @@
|
||||
import java.awt.*;
|
||||
|
||||
public class GrayPanel extends Panel
|
||||
{
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
g.setColor(Util.body);
|
||||
g.fillRect(0, 0, size().width, size().height);
|
||||
}
|
||||
}
|
345
file/Hierarchy.java
Normal file
@ -0,0 +1,345 @@
|
||||
// Hierarchy
|
||||
// An AWT component for displaying a tree-like heirachy, with each node
|
||||
// having an icon and a name. This heirachy can be expanded or contracted
|
||||
// by the user.
|
||||
import java.awt.*;
|
||||
import java.util.Vector;
|
||||
|
||||
public class Hierarchy extends BorderPanel implements CbScrollbarCallback
|
||||
{
|
||||
HierarchyNode root; // the root of the tree
|
||||
CbScrollbar sb; // scrollbar at right
|
||||
int width, height; // usable drawing area
|
||||
int sbwidth; // size of scrollbar
|
||||
HierarchyCallback callback; // who to call on open / close
|
||||
Image bim; // double-buffer image
|
||||
Font font = new Font("courier", Font.PLAIN, 12);
|
||||
FontMetrics fnm; // size of font used
|
||||
Graphics bg; // back-images graphics
|
||||
int top = 0; // top-most row displayed
|
||||
int count = 0; // total rows in the tree
|
||||
Insets in; // insets from border
|
||||
HierarchyNode sel; // selected node
|
||||
long last; // time of last mouse click
|
||||
static boolean broken_awt = System.getProperty("os.name").
|
||||
startsWith("Windows");
|
||||
|
||||
// Create a new Hierarchy object with the given root
|
||||
Hierarchy(HierarchyNode r)
|
||||
{
|
||||
this();
|
||||
root = r;
|
||||
}
|
||||
|
||||
// Create a new Hierarchy object that calls back to the given object
|
||||
// when nodes are clicked on.
|
||||
Hierarchy(HierarchyNode r, HierarchyCallback c)
|
||||
{
|
||||
this(r);
|
||||
callback = c;
|
||||
}
|
||||
|
||||
// Create an empty hierarchy object, with no callback
|
||||
Hierarchy()
|
||||
{
|
||||
super(3, Util.dark_edge_hi, Util.body_hi);
|
||||
|
||||
// Create UI
|
||||
setLayout(null);
|
||||
sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
|
||||
add(sb);
|
||||
}
|
||||
|
||||
// Create an empty hierarchy object, set to report user actions to
|
||||
// the given object.
|
||||
Hierarchy(HierarchyCallback c)
|
||||
{
|
||||
this();
|
||||
callback = c;
|
||||
}
|
||||
|
||||
// redraw
|
||||
// Called by the using class when the tree passed to this object
|
||||
// changes, to force a redraw and resizing of the scrollbar
|
||||
void redraw()
|
||||
{
|
||||
if (fnm != null) {
|
||||
render();
|
||||
paint(getGraphics());
|
||||
compscroll();
|
||||
}
|
||||
}
|
||||
|
||||
// setRoot
|
||||
// Set the root node for this hierarchy
|
||||
void setRoot(HierarchyNode r)
|
||||
{
|
||||
root = r;
|
||||
redraw();
|
||||
}
|
||||
|
||||
// selected
|
||||
// Return the currently selected node, or null
|
||||
HierarchyNode selected()
|
||||
{
|
||||
return sel;
|
||||
}
|
||||
|
||||
// select
|
||||
// Selected the given node
|
||||
void select(HierarchyNode s)
|
||||
{
|
||||
sel = s;
|
||||
}
|
||||
|
||||
// force the use of some font
|
||||
public void setFont(Font f)
|
||||
{
|
||||
font = f;
|
||||
bim = null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// reshape
|
||||
// Called when this component gets resized
|
||||
public void reshape(int nx, int ny, int nw, int nh)
|
||||
{
|
||||
in = insets();
|
||||
sbwidth = sb.minimumSize().width;
|
||||
width = nw-sbwidth - (in.left + in.right);
|
||||
height = nh - (in.top + in.bottom);
|
||||
sb.reshape(width+in.left, in.top, sbwidth, height);
|
||||
|
||||
// force creation of a new backing images
|
||||
bim = null;
|
||||
repaint();
|
||||
compscroll();
|
||||
|
||||
super.reshape(nx, ny, nw, nh);
|
||||
}
|
||||
|
||||
// update
|
||||
// Called sometime after repaint()
|
||||
public void update(Graphics g)
|
||||
{
|
||||
render();
|
||||
paint(g);
|
||||
}
|
||||
|
||||
// paint
|
||||
// Blit the backing image to the front
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
if (bim == null) {
|
||||
// This is the first rendering
|
||||
bim = createImage(width, height);
|
||||
bg = bim.getGraphics();
|
||||
bg.setFont(font);
|
||||
fnm = bg.getFontMetrics();
|
||||
render();
|
||||
compscroll();
|
||||
}
|
||||
g.drawImage(bim, in.left, in.top, this);
|
||||
}
|
||||
|
||||
// mouseDown
|
||||
// Called upon a mouseclick
|
||||
public boolean mouseDown(Event evt, int x, int y)
|
||||
{
|
||||
if (root == null)
|
||||
return false; // nothing to do
|
||||
HierarchyNode s = nodeat(root, x/16, (y/16)+top);
|
||||
if (s == null) {
|
||||
// Just deselect
|
||||
sel = null;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for double-click
|
||||
boolean dc = false;
|
||||
if (evt.when-last < 500 && sel == s)
|
||||
dc = true;
|
||||
else
|
||||
last = evt.when;
|
||||
sel = s;
|
||||
|
||||
if (dc && sel.ch != null) {
|
||||
// Open or close this node
|
||||
sel.open = !sel.open;
|
||||
if (callback != null) {
|
||||
// Notify callback, which MAY do something to change
|
||||
// the structure of the tree
|
||||
if (sel.open) callback.openNode(this, sel);
|
||||
else callback.closeNode(this, sel);
|
||||
}
|
||||
}
|
||||
else if (callback != null) {
|
||||
// Single click on a node or double-click on leaf node
|
||||
if (dc) callback.doubleNode(this, sel);
|
||||
else callback.clickNode(this, sel);
|
||||
}
|
||||
compscroll();
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void moved(CbScrollbar s, int v)
|
||||
{
|
||||
moving(s, v);
|
||||
}
|
||||
|
||||
public void moving(CbScrollbar s, int v)
|
||||
{
|
||||
top = sb.getValue();
|
||||
compscroll();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// render
|
||||
// Draw the current tree view into the backing image
|
||||
private void render()
|
||||
{
|
||||
if (fnm != null) {
|
||||
int fh = fnm.getHeight(), // useful font metrics
|
||||
fa = fnm.getAscent();
|
||||
bg.setColor(Util.light_bg);
|
||||
bg.fillRect(0, 0, width, height);
|
||||
if (root == null)
|
||||
return; // nothing to do
|
||||
bg.setColor(Util.text);
|
||||
recurse(root, 0, 0, fh, fa);
|
||||
}
|
||||
}
|
||||
|
||||
// recurse
|
||||
// Render a node in the tree at the given location, maybe followed
|
||||
// by all it's children. Return the number of rows this node took
|
||||
// to display.
|
||||
private int recurse(HierarchyNode n, int x, int y, int fh, int fa)
|
||||
{
|
||||
int xx = x*16, yy = (y-top)*16;
|
||||
int len = 1;
|
||||
|
||||
n.x = x;
|
||||
n.y = y;
|
||||
int tw = fnm.stringWidth(n.text);
|
||||
if (yy >= 0 && yy <= height) {
|
||||
// Draw this node
|
||||
if (n.im != null)
|
||||
bg.drawImage(n.im, xx, yy, this);
|
||||
if (sel == n) {
|
||||
// Select this node
|
||||
bg.setColor(Util.body);
|
||||
bg.fillRect(xx+17, yy+2, tw+2, 13);
|
||||
bg.setColor(Util.text);
|
||||
}
|
||||
bg.drawString(n.text, xx+18, yy+12);
|
||||
}
|
||||
if (n.ch != null && n.open && yy <= height) {
|
||||
// Mark this node
|
||||
bg.drawLine(xx+18, yy+14, xx+17+tw, yy+14);
|
||||
|
||||
// Draw subnodes
|
||||
yy += 16;
|
||||
for(int i=0; i<n.ch.size() && yy<=height; i++) {
|
||||
int l=recurse((HierarchyNode)n.ch.elementAt(i),
|
||||
x+1, y+len, fh, fa);
|
||||
bg.drawLine(xx+7, yy+7, xx+15, yy+7);
|
||||
if (i == n.ch.size()-1)
|
||||
bg.drawLine(xx+7, yy, xx+7, yy+7);
|
||||
else
|
||||
bg.drawLine(xx+7, yy, xx+7,yy+(l*16)-1);
|
||||
len += l;
|
||||
yy += l*16;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// compscroll
|
||||
// Re-compute scrollbar size
|
||||
private void compscroll()
|
||||
{
|
||||
if (fnm == null)
|
||||
return;
|
||||
int ct = root!=null ? count(root) : 1;
|
||||
int r = Math.min(ct, height/16 - 1);
|
||||
int c = ct - r;
|
||||
//sb.setValues(top, r==0?1:r, c<0?0:c);
|
||||
sb.setValues(top, r==0?1:r, ct);
|
||||
}
|
||||
|
||||
// count
|
||||
// Returns the number of visible rows from a node
|
||||
private int count(HierarchyNode n)
|
||||
{
|
||||
int l = 1;
|
||||
if (n.open && n.ch != null)
|
||||
for(int i=0; i<n.ch.size(); i++)
|
||||
l += count((HierarchyNode)n.ch.elementAt(i));
|
||||
return l;
|
||||
}
|
||||
|
||||
// nodeat
|
||||
// Is the given node at the given position? If not, check its
|
||||
// children too.
|
||||
private HierarchyNode nodeat(HierarchyNode n, int x, int y)
|
||||
{
|
||||
if (y == n.y && x >= n.x)
|
||||
return n;
|
||||
if (n.ch == null || !n.open)
|
||||
return null;
|
||||
for(int i=0; i<n.ch.size(); i++) {
|
||||
HierarchyNode c = nodeat((HierarchyNode)n.ch.elementAt(i),x,y);
|
||||
if (c != null) return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// HierarchyNode
|
||||
// One node in the tree displayed by the Hierarchy object.
|
||||
class HierarchyNode
|
||||
{
|
||||
boolean open; // is this node open?
|
||||
Image im; // icon for this node (assumed to be 16x16!)
|
||||
Vector ch; // sub-nodes of this one, or null
|
||||
String text; // name of this node
|
||||
int x, y; // row/column in list
|
||||
|
||||
HierarchyNode() { }
|
||||
|
||||
HierarchyNode(boolean o, Image i, Vector c, String t)
|
||||
{
|
||||
open = o;
|
||||
im = i;
|
||||
ch = c;
|
||||
text = t;
|
||||
}
|
||||
}
|
||||
|
||||
// HierarchyCallback
|
||||
// Programmers using the Hierarchy class pass an object that implements the
|
||||
// HierarchyCallback interface to its constructor, to receive information
|
||||
// about user actions.
|
||||
interface HierarchyCallback
|
||||
{
|
||||
// openNode
|
||||
// Called when a node with children is opened
|
||||
void openNode(Hierarchy h, HierarchyNode n);
|
||||
|
||||
// closeNode
|
||||
// Called when a node is closed
|
||||
void closeNode(Hierarchy h, HierarchyNode n);
|
||||
|
||||
// clickNode
|
||||
// Called when the user clicks on a node
|
||||
void clickNode(Hierarchy h, HierarchyNode n);
|
||||
|
||||
// doubleNode
|
||||
// Called when a user double-clicks on a node
|
||||
void doubleNode(Hierarchy h, HierarchyNode n);
|
||||
}
|
||||
|
345
file/Hierarchy.java.bak
Normal file
@ -0,0 +1,345 @@
|
||||
// Hierarchy
|
||||
// An AWT component for displaying a tree-like heirachy, with each node
|
||||
// having an icon and a name. This heirachy can be expanded or contracted
|
||||
// by the user.
|
||||
import java.awt.*;
|
||||
import java.util.Vector;
|
||||
|
||||
public class Hierarchy extends BorderPanel implements CbScrollbarCallback
|
||||
{
|
||||
HierarchyNode root; // the root of the tree
|
||||
CbScrollbar sb; // scrollbar at right
|
||||
int width, height; // usable drawing area
|
||||
int sbwidth; // size of scrollbar
|
||||
HierarchyCallback callback; // who to call on open / close
|
||||
Image bim; // double-buffer image
|
||||
Font font = new Font("courier", Font.PLAIN, 12);
|
||||
FontMetrics fnm; // size of font used
|
||||
Graphics bg; // back-images graphics
|
||||
int top = 0; // top-most row displayed
|
||||
int count = 0; // total rows in the tree
|
||||
Insets in; // insets from border
|
||||
HierarchyNode sel; // selected node
|
||||
long last; // time of last mouse click
|
||||
static boolean broken_awt = System.getProperty("os.name").
|
||||
startsWith("Windows");
|
||||
|
||||
// Create a new Hierarchy object with the given root
|
||||
Hierarchy(HierarchyNode r)
|
||||
{
|
||||
this();
|
||||
root = r;
|
||||
}
|
||||
|
||||
// Create a new Hierarchy object that calls back to the given object
|
||||
// when nodes are clicked on.
|
||||
Hierarchy(HierarchyNode r, HierarchyCallback c)
|
||||
{
|
||||
this(r);
|
||||
callback = c;
|
||||
}
|
||||
|
||||
// Create an empty hierarchy object, with no callback
|
||||
Hierarchy()
|
||||
{
|
||||
super(3, new Color(50,50,50), new Color(220,220,220));
|
||||
|
||||
// Create UI
|
||||
setLayout(null);
|
||||
sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
|
||||
add(sb);
|
||||
}
|
||||
|
||||
// Create an empty hierarchy object, set to report user actions to
|
||||
// the given object.
|
||||
Hierarchy(HierarchyCallback c)
|
||||
{
|
||||
this();
|
||||
callback = c;
|
||||
}
|
||||
|
||||
// redraw
|
||||
// Called by the using class when the tree passed to this object
|
||||
// changes, to force a redraw and resizing of the scrollbar
|
||||
void redraw()
|
||||
{
|
||||
if (fnm != null) {
|
||||
render();
|
||||
paint(getGraphics());
|
||||
compscroll();
|
||||
}
|
||||
}
|
||||
|
||||
// setRoot
|
||||
// Set the root node for this hierarchy
|
||||
void setRoot(HierarchyNode r)
|
||||
{
|
||||
root = r;
|
||||
redraw();
|
||||
}
|
||||
|
||||
// selected
|
||||
// Return the currently selected node, or null
|
||||
HierarchyNode selected()
|
||||
{
|
||||
return sel;
|
||||
}
|
||||
|
||||
// select
|
||||
// Selected the given node
|
||||
void select(HierarchyNode s)
|
||||
{
|
||||
sel = s;
|
||||
}
|
||||
|
||||
// force the use of some font
|
||||
void setFont(Font f)
|
||||
{
|
||||
font = f;
|
||||
bim = null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// reshape
|
||||
// Called when this component gets resized
|
||||
public void reshape(int nx, int ny, int nw, int nh)
|
||||
{
|
||||
in = insets();
|
||||
sbwidth = sb.minimumSize().width;
|
||||
width = nw-sbwidth - (in.left + in.right);
|
||||
height = nh - (in.top + in.bottom);
|
||||
sb.reshape(width+in.left, in.top, sbwidth, height);
|
||||
|
||||
// force creation of a new backing images
|
||||
bim = null;
|
||||
repaint();
|
||||
compscroll();
|
||||
|
||||
super.reshape(nx, ny, nw, nh);
|
||||
}
|
||||
|
||||
// update
|
||||
// Called sometime after repaint()
|
||||
public void update(Graphics g)
|
||||
{
|
||||
render();
|
||||
paint(g);
|
||||
}
|
||||
|
||||
// paint
|
||||
// Blit the backing image to the front
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
if (bim == null) {
|
||||
// This is the first rendering
|
||||
bim = createImage(width, height);
|
||||
bg = bim.getGraphics();
|
||||
bg.setFont(font);
|
||||
fnm = bg.getFontMetrics();
|
||||
render();
|
||||
compscroll();
|
||||
}
|
||||
g.drawImage(bim, in.left, in.top, this);
|
||||
}
|
||||
|
||||
// mouseDown
|
||||
// Called upon a mouseclick
|
||||
public boolean mouseDown(Event evt, int x, int y)
|
||||
{
|
||||
if (root == null)
|
||||
return false; // nothing to do
|
||||
HierarchyNode s = nodeat(root, x/16, (y/16)+top);
|
||||
if (s == null) {
|
||||
// Just deselect
|
||||
sel = null;
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for double-click
|
||||
boolean dc = false;
|
||||
if (evt.when-last < 500 && sel == s)
|
||||
dc = true;
|
||||
else
|
||||
last = evt.when;
|
||||
sel = s;
|
||||
|
||||
if (dc && sel.ch != null) {
|
||||
// Open or close this node
|
||||
sel.open = !sel.open;
|
||||
if (callback != null) {
|
||||
// Notify callback, which MAY do something to change
|
||||
// the structure of the tree
|
||||
if (sel.open) callback.openNode(this, sel);
|
||||
else callback.closeNode(this, sel);
|
||||
}
|
||||
}
|
||||
else if (callback != null) {
|
||||
// Single click on a node or double-click on leaf node
|
||||
if (dc) callback.doubleNode(this, sel);
|
||||
else callback.clickNode(this, sel);
|
||||
}
|
||||
compscroll();
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void moved(CbScrollbar s, int v)
|
||||
{
|
||||
moving(s, v);
|
||||
}
|
||||
|
||||
public void moving(CbScrollbar s, int v)
|
||||
{
|
||||
top = sb.getValue();
|
||||
compscroll();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// render
|
||||
// Draw the current tree view into the backing image
|
||||
private void render()
|
||||
{
|
||||
if (fnm != null) {
|
||||
int fh = fnm.getHeight(), // useful font metrics
|
||||
fa = fnm.getAscent();
|
||||
bg.setColor(Color.white);
|
||||
bg.fillRect(0, 0, width, height);
|
||||
if (root == null)
|
||||
return; // nothing to do
|
||||
bg.setColor(Color.black);
|
||||
recurse(root, 0, 0, fh, fa);
|
||||
}
|
||||
}
|
||||
|
||||
// recurse
|
||||
// Render a node in the tree at the given location, maybe followed
|
||||
// by all it's children. Return the number of rows this node took
|
||||
// to display.
|
||||
private int recurse(HierarchyNode n, int x, int y, int fh, int fa)
|
||||
{
|
||||
int xx = x*16, yy = (y-top)*16;
|
||||
int len = 1;
|
||||
|
||||
n.x = x;
|
||||
n.y = y;
|
||||
int tw = fnm.stringWidth(n.text);
|
||||
if (yy >= 0 && yy <= height) {
|
||||
// Draw this node
|
||||
if (n.im != null)
|
||||
bg.drawImage(n.im, xx, yy, this);
|
||||
if (sel == n) {
|
||||
// Select this node
|
||||
bg.setColor(Color.lightGray);
|
||||
bg.fillRect(xx+17, yy+2, tw+2, 13);
|
||||
bg.setColor(Color.black);
|
||||
}
|
||||
bg.drawString(n.text, xx+18, yy+12);
|
||||
}
|
||||
if (n.ch != null && n.open && yy <= height) {
|
||||
// Mark this node
|
||||
bg.drawLine(xx+18, yy+14, xx+17+tw, yy+14);
|
||||
|
||||
// Draw subnodes
|
||||
yy += 16;
|
||||
for(int i=0; i<n.ch.size() && yy<=height; i++) {
|
||||
int l=recurse((HierarchyNode)n.ch.elementAt(i),
|
||||
x+1, y+len, fh, fa);
|
||||
bg.drawLine(xx+7, yy+7, xx+15, yy+7);
|
||||
if (i == n.ch.size()-1)
|
||||
bg.drawLine(xx+7, yy, xx+7, yy+7);
|
||||
else
|
||||
bg.drawLine(xx+7, yy, xx+7,yy+(l*16)-1);
|
||||
len += l;
|
||||
yy += l*16;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// compscroll
|
||||
// Re-compute scrollbar size
|
||||
private void compscroll()
|
||||
{
|
||||
if (fnm == null)
|
||||
return;
|
||||
int ct = root!=null ? count(root) : 1;
|
||||
int r = Math.min(ct, height/16 - 1);
|
||||
int c = ct - r;
|
||||
//sb.setValues(top, r==0?1:r, c<0?0:c);
|
||||
sb.setValues(top, r==0?1:r, ct);
|
||||
}
|
||||
|
||||
// count
|
||||
// Returns the number of visible rows from a node
|
||||
private int count(HierarchyNode n)
|
||||
{
|
||||
int l = 1;
|
||||
if (n.open && n.ch != null)
|
||||
for(int i=0; i<n.ch.size(); i++)
|
||||
l += count((HierarchyNode)n.ch.elementAt(i));
|
||||
return l;
|
||||
}
|
||||
|
||||
// nodeat
|
||||
// Is the given node at the given position? If not, check its
|
||||
// children too.
|
||||
private HierarchyNode nodeat(HierarchyNode n, int x, int y)
|
||||
{
|
||||
if (y == n.y && x >= n.x)
|
||||
return n;
|
||||
if (n.ch == null || !n.open)
|
||||
return null;
|
||||
for(int i=0; i<n.ch.size(); i++) {
|
||||
HierarchyNode c = nodeat((HierarchyNode)n.ch.elementAt(i),x,y);
|
||||
if (c != null) return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// HierarchyNode
|
||||
// One node in the tree displayed by the Hierarchy object.
|
||||
class HierarchyNode
|
||||
{
|
||||
boolean open; // is this node open?
|
||||
Image im; // icon for this node (assumed to be 16x16!)
|
||||
Vector ch; // sub-nodes of this one, or null
|
||||
String text; // name of this node
|
||||
int x, y; // row/column in list
|
||||
|
||||
HierarchyNode() { }
|
||||
|
||||
HierarchyNode(boolean o, Image i, Vector c, String t)
|
||||
{
|
||||
open = o;
|
||||
im = i;
|
||||
ch = c;
|
||||
text = t;
|
||||
}
|
||||
}
|
||||
|
||||
// HierarchyCallback
|
||||
// Programmers using the Hierarchy class pass an object that implements the
|
||||
// HierarchyCallback interface to its constructor, to receive information
|
||||
// about user actions.
|
||||
interface HierarchyCallback
|
||||
{
|
||||
// openNode
|
||||
// Called when a node with children is opened
|
||||
void openNode(Hierarchy h, HierarchyNode n);
|
||||
|
||||
// closeNode
|
||||
// Called when a node is closed
|
||||
void closeNode(Hierarchy h, HierarchyNode n);
|
||||
|
||||
// clickNode
|
||||
// Called when the user clicks on a node
|
||||
void clickNode(Hierarchy h, HierarchyNode n);
|
||||
|
||||
// doubleNode
|
||||
// Called when a user double-clicks on a node
|
||||
void doubleNode(Hierarchy h, HierarchyNode n);
|
||||
}
|
||||
|
39
file/LinedPanel.java
Normal file
@ -0,0 +1,39 @@
|
||||
import java.awt.*;
|
||||
|
||||
class LinedPanel extends GrayPanel
|
||||
{
|
||||
String title;
|
||||
|
||||
LinedPanel(String t)
|
||||
{
|
||||
title = t;
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
Font f = g.getFont();
|
||||
FontMetrics fnm = g.getFontMetrics();
|
||||
int w = size().width-1, h = size().height - 1;
|
||||
int tl = fnm.stringWidth(title);
|
||||
|
||||
g.setColor(Util.light_edge);
|
||||
g.drawLine(5, 5, 5, h-5);
|
||||
g.drawLine(5, h-5, w-5, h-5);
|
||||
g.drawLine(w-5, h-5, w-5, 5);
|
||||
g.drawLine(tl+9, 5, w-5, 5);
|
||||
|
||||
g.setColor(Util.dark_edge);
|
||||
g.drawLine(4, 4, 4, h-6);
|
||||
g.drawLine(6, h-6, w-6, h-6);
|
||||
g.drawLine(w-6, h-6, w-6, 6);
|
||||
g.drawLine(w-6, 4, tl+9, 4);
|
||||
g.drawString(title, 7, fnm.getAscent());
|
||||
}
|
||||
|
||||
public Insets insets()
|
||||
{
|
||||
return new Insets(15, 10, 10, 10);
|
||||
}
|
||||
}
|
||||
|
4
file/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
file.jar: FileManager.java Util.java
|
||||
CLASSPATH=/usr/local/netscape7/plugins/java2/lib/javaplugin.jar:. javac -target 1.1 *.java
|
||||
jar cf file.jar *.class
|
||||
[ -d "../zomos-virtual" ] && jar cf ../zomos-virtual/file.jar *.class
|
583
file/MultiColumn.java
Normal file
@ -0,0 +1,583 @@
|
||||
// MultiColumn
|
||||
// A List box that supports multiple columns.
|
||||
import java.awt.*;
|
||||
import java.util.Vector;
|
||||
|
||||
public class MultiColumn extends BorderPanel implements CbScrollbarCallback
|
||||
{
|
||||
MultiColumnCallback callback; // what to call back to
|
||||
String title[]; // column titles
|
||||
boolean adjustable = true;
|
||||
boolean drawlines = true;
|
||||
Color colors[][] = null;
|
||||
boolean enabled = true;
|
||||
boolean multiselect = false;
|
||||
int cpos[]; // column x positions
|
||||
float cwidth[]; // proportional column widths
|
||||
Vector list[]; // columns of the list
|
||||
CbScrollbar sb; // scrollbar at the right side
|
||||
int width, height; // size, minus the scrollbar
|
||||
Insets in; // used space around the border
|
||||
int sbwidth; // width of the scrollbar
|
||||
int th; // height of title bar
|
||||
Image bim; // backing image
|
||||
Graphics bg; // backing graphics
|
||||
Font font = new Font("timesRoman", Font.PLAIN, 12);
|
||||
FontMetrics fnm; // drawing font size
|
||||
int coldrag = -1; // column being resized
|
||||
int sel = -1; // selected row
|
||||
int sels[] = new int[0]; // all selected rows
|
||||
int top = 0; // first row displayed
|
||||
long last; // last mouse click time
|
||||
int rowh = 16; // row height
|
||||
Event last_event; // last event that triggered callback
|
||||
int sortcol; // Column currently being sorted
|
||||
int sortdir; // Sort direction (0=none, 1=up, 2=down)
|
||||
|
||||
// Create a new list with the given column titles
|
||||
MultiColumn(String t[])
|
||||
{
|
||||
super(3, Util.dark_edge_hi, Util.body_hi);
|
||||
title = new String[t.length];
|
||||
for(int i=0; i<t.length; i++)
|
||||
title[i] = t[i];
|
||||
list = new Vector[t.length];
|
||||
for(int i=0; i<t.length; i++)
|
||||
list[i] = new Vector();
|
||||
cwidth = new float[t.length];
|
||||
for(int i=0; i<t.length; i++)
|
||||
cwidth[i] = 1.0f/t.length;
|
||||
cpos = new int[t.length+1];
|
||||
setLayout(null);
|
||||
sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
|
||||
add(sb);
|
||||
}
|
||||
|
||||
// Create a new list that calls back to the given object on
|
||||
// single or double clicks.
|
||||
MultiColumn(String t[], MultiColumnCallback c)
|
||||
{
|
||||
this(t);
|
||||
callback = c;
|
||||
}
|
||||
|
||||
// addItem
|
||||
// Add a row to the list
|
||||
void addItem(Object item[])
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].addElement(item[i]);
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// addItems
|
||||
// Add several rows to the list
|
||||
void addItems(Object item[][])
|
||||
{
|
||||
for(int i=0; i<item.length; i++)
|
||||
for(int j=0; j<title.length; j++)
|
||||
list[j].addElement(item[i][j]);
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// modifyItem
|
||||
// Changes one row of the table
|
||||
void modifyItem(Object item[], int row)
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].setElementAt(item[i], row);
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// getItem
|
||||
// Returns the contents of a given row
|
||||
Object []getItem(int n)
|
||||
{
|
||||
Object r[] = new Object[title.length];
|
||||
for(int i=0; i<title.length; i++)
|
||||
r[i] = list[i].elementAt(n);
|
||||
return r;
|
||||
}
|
||||
|
||||
// selected
|
||||
// Return the most recently selected row
|
||||
int selected()
|
||||
{
|
||||
return sel;
|
||||
}
|
||||
|
||||
// select
|
||||
// Select some row
|
||||
void select(int s)
|
||||
{
|
||||
sel = s;
|
||||
sels = new int[1];
|
||||
sels[0] = s;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// select
|
||||
// Select multiple rows
|
||||
void select(int s[])
|
||||
{
|
||||
if (s.length == 0) {
|
||||
sel = -1;
|
||||
sels = new int[0];
|
||||
}
|
||||
else {
|
||||
sel = s[0];
|
||||
sels = s;
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
// allSelected
|
||||
// Returns all the selected rows
|
||||
int[] allSelected()
|
||||
{
|
||||
return sels;
|
||||
}
|
||||
|
||||
// scrollto
|
||||
// Scroll to make some row visible
|
||||
void scrollto(int s)
|
||||
{
|
||||
int r = rows();
|
||||
if (s < top || s >= top+r) {
|
||||
top = s-1;
|
||||
if (top > list[0].size() - r)
|
||||
top = list[0].size() - r;
|
||||
sb.setValue(top);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
// deleteItem
|
||||
// Remove one row from the list
|
||||
void deleteItem(int n)
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].removeElementAt(n);
|
||||
if (n == sel) {
|
||||
// De-select deleted file
|
||||
sel = -1;
|
||||
}
|
||||
for(int i=0; i<sels.length; i++) {
|
||||
if (sels[i] == n) {
|
||||
// Remove from selection list
|
||||
int nsels[] = new int[sels.length-1];
|
||||
if (nsels.length > 0) {
|
||||
System.arraycopy(sels, 0, nsels, 0, i);
|
||||
System.arraycopy(sels, i+1, nsels, i,
|
||||
nsels.length-i);
|
||||
sel = nsels[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// clear
|
||||
// Remove everything from the list
|
||||
void clear()
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].removeAllElements();
|
||||
sel = -1;
|
||||
sels = new int[0];
|
||||
top = 0;
|
||||
repaint();
|
||||
sb.setValues(0, 1, 0);
|
||||
}
|
||||
|
||||
// setWidths
|
||||
// Set the proportional widths of each column
|
||||
void setWidths(float w[])
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
cwidth[i] = w[i];
|
||||
respace();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**Turns on or off the user's ability to adjust column widths
|
||||
* @param a Can adjust or not?
|
||||
*/
|
||||
void setAdjustable(boolean a)
|
||||
{
|
||||
adjustable = a;
|
||||
}
|
||||
|
||||
/**Turns on or off the drawing of column lines
|
||||
* @param d Draw lines or not?
|
||||
*/
|
||||
void setDrawLines(boolean d)
|
||||
{
|
||||
drawlines = d;
|
||||
}
|
||||
|
||||
/**Sets the array of colors used to draw text items.
|
||||
* @param c The color array (in row/column order), or null to
|
||||
* use the default
|
||||
*/
|
||||
void setColors(Color c[][])
|
||||
{
|
||||
colors = c;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Turns on or off multi-row selection with ctrl and shift
|
||||
void setMultiSelect(boolean m)
|
||||
{
|
||||
multiselect = m;
|
||||
}
|
||||
|
||||
// Enables the entire list
|
||||
public void enable()
|
||||
{
|
||||
enabled = true;
|
||||
sb.enable();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Disables the entire list
|
||||
public void disable()
|
||||
{
|
||||
enabled = false;
|
||||
sb.disable();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Sets or turns off the sort indication arrow for a column
|
||||
// Direction 0 = None, 1 = Up arrow, 2 = Down arrow
|
||||
public void sortingArrow(int col, int dir)
|
||||
{
|
||||
sortcol = col;
|
||||
sortdir = dir;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void setFont(Font f)
|
||||
{
|
||||
font = f;
|
||||
bim = null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// reshape
|
||||
// Called when this component gets resized
|
||||
public void reshape(int nx, int ny, int nw, int nh)
|
||||
{
|
||||
if (nw != width+sbwidth || nh != height) {
|
||||
in = insets();
|
||||
sbwidth = sb.minimumSize().width;
|
||||
width = nw-sbwidth - (in.left + in.right);
|
||||
height = nh - (in.top + in.bottom);
|
||||
sb.reshape(width+in.left, in.top, sbwidth, height);
|
||||
respace();
|
||||
|
||||
// Force creation of a new backing image and re-painting
|
||||
bim = null;
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
super.reshape(nx, ny, nw, nh);
|
||||
}
|
||||
|
||||
// respace
|
||||
// Compute pixel column widths from proportional widths
|
||||
void respace()
|
||||
{
|
||||
cpos[0] = 0;
|
||||
for(int i=0; i<title.length; i++)
|
||||
cpos[i+1] = cpos[i] + (int)(width*cwidth[i]);
|
||||
}
|
||||
|
||||
// paint
|
||||
// Blit the backing image to the front
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
if (bim == null) {
|
||||
// This is the first rendering
|
||||
bim = createImage(width, height);
|
||||
bg = bim.getGraphics();
|
||||
bg.setFont(font);
|
||||
fnm = bg.getFontMetrics();
|
||||
th = fnm.getHeight() + 4;
|
||||
render();
|
||||
compscroll();
|
||||
}
|
||||
g.drawImage(bim, in.left, in.top, this);
|
||||
}
|
||||
|
||||
// update
|
||||
// Called sometime after repaint()
|
||||
public void update(Graphics g)
|
||||
{
|
||||
if (fnm != null) {
|
||||
render();
|
||||
paint(g);
|
||||
}
|
||||
}
|
||||
|
||||
// render
|
||||
// Re-draw the list into the backing image
|
||||
void render()
|
||||
{
|
||||
int fh = fnm.getHeight(), // useful font metrics
|
||||
fd = fnm.getDescent(),
|
||||
fa = fnm.getAscent();
|
||||
int bot = Math.min(top+rows()-1, list[0].size()-1);
|
||||
|
||||
// Clear title section and list
|
||||
bg.setColor(Util.body);
|
||||
bg.fillRect(0, 0, width, th);
|
||||
bg.setColor(Util.light_bg);
|
||||
bg.fillRect(0, th, width, height-th);
|
||||
Color lighterGray = Util.body_hi;
|
||||
|
||||
if (enabled) {
|
||||
// Mark the selected rows
|
||||
for(int i=0; i<sels.length; i++) {
|
||||
if (sels[i] >= top && sels[i] <= bot) {
|
||||
bg.setColor(sels[i] == sel ? Util.body
|
||||
: lighterGray);
|
||||
bg.fillRect(0, th+(sels[i]-top)*rowh,
|
||||
width, rowh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw each column
|
||||
for(int i=0; i<title.length; i++) {
|
||||
int x = cpos[i], w = cpos[i+1]-x-1;
|
||||
|
||||
// Column title
|
||||
bg.setColor(Util.light_edge);
|
||||
bg.drawLine(x, 0, x+w, 0);
|
||||
bg.drawLine(x, 1, x+w-1, 1);
|
||||
bg.drawLine(x, 0, x, th-1);
|
||||
bg.drawLine(x+1, 0, x+1, th-2);
|
||||
bg.setColor(Util.dark_edge);
|
||||
bg.drawLine(x, th-1, x+w, th-1);
|
||||
bg.drawLine(x, th-2, x+w-1, th-2);
|
||||
bg.drawLine(x+w, th-1, x+w, 0);
|
||||
bg.drawLine(x+w-1, th-1, x+w-1, 1);
|
||||
int tw = fnm.stringWidth(title[i]);
|
||||
if (tw < w-6)
|
||||
bg.drawString(title[i], x+(w-tw)/2, th-fd-2);
|
||||
|
||||
// Sorting arrow
|
||||
int as = th-8;
|
||||
if (sortcol == i && sortdir == 1) {
|
||||
bg.setColor(Util.light_edge);
|
||||
bg.drawLine(x+4, th-5, x+4+as, th-5);
|
||||
bg.drawLine(x+4+as, th-5, x+4+as/2, th-5-as);
|
||||
bg.setColor(Util.dark_edge);
|
||||
bg.drawLine(x+4+as/2, th-5-as, x+4, th-5);
|
||||
}
|
||||
else if (sortcol == i && sortdir == 2) {
|
||||
bg.setColor(Util.light_edge);
|
||||
bg.drawLine(x+4+as/2, th-5, x+4+as, th-5-as);
|
||||
bg.setColor(Util.dark_edge);
|
||||
bg.drawLine(x+4, th-5-as, x+4+as, th-5-as);
|
||||
bg.drawLine(x+4, th-5-as, x+4+as/2, th-5);
|
||||
}
|
||||
|
||||
// Column items
|
||||
if (drawlines) {
|
||||
bg.setColor(Util.body);
|
||||
bg.drawLine(x+w-1, th, x+w-1, height);
|
||||
bg.setColor(Util.dark_edge);
|
||||
bg.drawLine(x+w, th, x+w, height);
|
||||
}
|
||||
for(int j=top; j<=bot; j++) {
|
||||
Object o = list[i].elementAt(j);
|
||||
if (o instanceof String) {
|
||||
// Render string in column
|
||||
String s = (String)o;
|
||||
while(fnm.stringWidth(s) > w-3)
|
||||
s = s.substring(0, s.length()-1);
|
||||
if (!enabled)
|
||||
bg.setColor(Util.body);
|
||||
else if (colors != null)
|
||||
bg.setColor(colors[j][i]);
|
||||
bg.drawString(s, x+1, th+(j+1-top)*rowh-fd);
|
||||
}
|
||||
else if (o instanceof Image) {
|
||||
// Render image in column
|
||||
Image im = (Image)o;
|
||||
bg.drawImage(im, x+1, th+(j-top)*rowh, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mouseDown
|
||||
// Select a list item or a column to drag
|
||||
public boolean mouseDown(Event e, int x, int y)
|
||||
{
|
||||
if (!enabled) {
|
||||
return true;
|
||||
}
|
||||
x -= in.left;
|
||||
y -= in.top;
|
||||
coldrag = -1;
|
||||
if (y < th) {
|
||||
// Click in title bar
|
||||
for(int i=0; i<title.length; i++) {
|
||||
if (adjustable && i > 0 && Math.abs(cpos[i] - x) < 3) {
|
||||
// clicked on a column separator
|
||||
coldrag = i;
|
||||
}
|
||||
else if (x >= cpos[i] && x < cpos[i+1]) {
|
||||
// clicked in a title
|
||||
callback.headingClicked(this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Item chosen from list
|
||||
int row = (y-th)/rowh + top;
|
||||
if (row < list[0].size()) {
|
||||
// Double-click?
|
||||
boolean dclick = false;
|
||||
if (e.when-last < 1000 && sel == row)
|
||||
dclick = true;
|
||||
else
|
||||
last = e.when;
|
||||
|
||||
if (e.shiftDown() && multiselect && sel != -1) {
|
||||
// Select all from last selection to this one
|
||||
int zero = sels[0];
|
||||
if (zero < row) {
|
||||
sels = new int[row-zero+1];
|
||||
for(int i=zero; i<=row; i++)
|
||||
sels[i-zero] = i;
|
||||
}
|
||||
else {
|
||||
sels = new int[zero-row+1];
|
||||
for(int i=zero; i>=row; i--)
|
||||
sels[zero-i] = i;
|
||||
}
|
||||
}
|
||||
else if (e.controlDown() && multiselect) {
|
||||
// Add this one to selection
|
||||
int nsels[] = new int[sels.length + 1];
|
||||
System.arraycopy(sels, 0, nsels, 0,sels.length);
|
||||
nsels[sels.length] = row;
|
||||
sels = nsels;
|
||||
}
|
||||
else {
|
||||
// Select one row only, and de-select others
|
||||
sels = new int[1];
|
||||
sels[0] = row;
|
||||
}
|
||||
sel = row;
|
||||
repaint();
|
||||
last_event = e;
|
||||
if (callback != null) {
|
||||
// Callback the right function
|
||||
if (dclick) callback.doubleClick(this, row);
|
||||
else callback.singleClick(this, row);
|
||||
}
|
||||
else {
|
||||
// Send an event
|
||||
getParent().postEvent(
|
||||
new Event(this,
|
||||
Event.ACTION_EVENT,
|
||||
dclick?"Double":"Single"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// mouseDrag
|
||||
// If a column is selected, change it's width
|
||||
public boolean mouseDrag(Event e, int x, int y)
|
||||
{
|
||||
if (!enabled) {
|
||||
return true;
|
||||
}
|
||||
x -= in.left;
|
||||
y -= in.top;
|
||||
if (coldrag != -1) {
|
||||
if (x > cpos[coldrag-1]+3 && x < cpos[coldrag+1]-3) {
|
||||
cpos[coldrag] = x;
|
||||
cwidth[coldrag-1] = (cpos[coldrag]-cpos[coldrag-1]) /
|
||||
(float)width;
|
||||
cwidth[coldrag] = (cpos[coldrag+1]-cpos[coldrag]) /
|
||||
(float)width;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void moved(CbScrollbar s, int v)
|
||||
{
|
||||
moving(s, v);
|
||||
}
|
||||
|
||||
public void moving(CbScrollbar s, int v)
|
||||
{
|
||||
top = sb.getValue();
|
||||
compscroll();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// compscroll
|
||||
// Re-compute the size of the scrollbar
|
||||
private void compscroll()
|
||||
{
|
||||
if (fnm == null)
|
||||
return; // not visible
|
||||
int r = rows();
|
||||
int c = list[0].size() - r;
|
||||
sb.setValues(top, r==0?1:r, list[0].size());
|
||||
}
|
||||
|
||||
// rows
|
||||
// Returns the number of rows visible in the list
|
||||
private int rows()
|
||||
{
|
||||
return Math.min(height/rowh - 1, list[0].size());
|
||||
}
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
return new Dimension(400, 100);
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return minimumSize();
|
||||
}
|
||||
}
|
||||
|
||||
// MultiColumnCallback
|
||||
// Objects implementing this interface can be passed to the MultiColumn
|
||||
// class, to have their singleClick() and doubleClick() functions called in
|
||||
// response to single or double click in the list.
|
||||
interface MultiColumnCallback
|
||||
{
|
||||
// singleClick
|
||||
// Called on a single click on a list item
|
||||
void singleClick(MultiColumn list, int num);
|
||||
|
||||
// doubleClick
|
||||
// Called upon double-clicking on a list item
|
||||
void doubleClick(MultiColumn list, int num);
|
||||
|
||||
// headingClicked
|
||||
// Called when a column heading is clicked on
|
||||
void headingClicked(MultiColumn list, int col);
|
||||
}
|
||||
|
578
file/MultiColumn.java.bak
Normal file
@ -0,0 +1,578 @@
|
||||
// MultiColumn
|
||||
// A List box that supports multiple columns.
|
||||
import java.awt.*;
|
||||
import java.util.Vector;
|
||||
|
||||
public class MultiColumn extends BorderPanel implements CbScrollbarCallback
|
||||
{
|
||||
MultiColumnCallback callback; // what to call back to
|
||||
String title[]; // column titles
|
||||
boolean adjustable = true;
|
||||
boolean drawlines = true;
|
||||
Color colors[][] = null;
|
||||
boolean enabled = true;
|
||||
boolean multiselect = false;
|
||||
int cpos[]; // column x positions
|
||||
float cwidth[]; // proportional column widths
|
||||
Vector list[]; // columns of the list
|
||||
CbScrollbar sb; // scrollbar at the right side
|
||||
int width, height; // size, minus the scrollbar
|
||||
Insets in; // used space around the border
|
||||
int sbwidth; // width of the scrollbar
|
||||
int th; // height of title bar
|
||||
Image bim; // backing image
|
||||
Graphics bg; // backing graphics
|
||||
Font font = new Font("timesRoman", Font.PLAIN, 12);
|
||||
FontMetrics fnm; // drawing font size
|
||||
int coldrag = -1; // column being resized
|
||||
int sel = -1; // selected row
|
||||
int sels[] = new int[0]; // all selected rows
|
||||
int top = 0; // first row displayed
|
||||
long last; // last mouse click time
|
||||
int rowh = 16; // row height
|
||||
Event last_event; // last event that triggered callback
|
||||
int sortcol; // Column currently being sorted
|
||||
int sortdir; // Sort direction (0=none, 1=up, 2=down)
|
||||
|
||||
// Create a new list with the given column titles
|
||||
MultiColumn(String t[])
|
||||
{
|
||||
super(3, new Color(50,50,50), new Color(220,220,220));
|
||||
title = new String[t.length];
|
||||
for(int i=0; i<t.length; i++)
|
||||
title[i] = t[i];
|
||||
list = new Vector[t.length];
|
||||
for(int i=0; i<t.length; i++)
|
||||
list[i] = new Vector();
|
||||
cwidth = new float[t.length];
|
||||
for(int i=0; i<t.length; i++)
|
||||
cwidth[i] = 1.0f/t.length;
|
||||
cpos = new int[t.length+1];
|
||||
setLayout(null);
|
||||
sb = new CbScrollbar(CbScrollbar.VERTICAL, this);
|
||||
add(sb);
|
||||
}
|
||||
|
||||
// Create a new list that calls back to the given object on
|
||||
// single or double clicks.
|
||||
MultiColumn(String t[], MultiColumnCallback c)
|
||||
{
|
||||
this(t);
|
||||
callback = c;
|
||||
}
|
||||
|
||||
// addItem
|
||||
// Add a row to the list
|
||||
void addItem(Object item[])
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].addElement(item[i]);
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// addItems
|
||||
// Add several rows to the list
|
||||
void addItems(Object item[][])
|
||||
{
|
||||
for(int i=0; i<item.length; i++)
|
||||
for(int j=0; j<title.length; j++)
|
||||
list[j].addElement(item[i][j]);
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// modifyItem
|
||||
// Changes one row of the table
|
||||
void modifyItem(Object item[], int row)
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].setElementAt(item[i], row);
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// getItem
|
||||
// Returns the contents of a given row
|
||||
Object []getItem(int n)
|
||||
{
|
||||
Object r[] = new Object[title.length];
|
||||
for(int i=0; i<title.length; i++)
|
||||
r[i] = list[i].elementAt(n);
|
||||
return r;
|
||||
}
|
||||
|
||||
// selected
|
||||
// Return the most recently selected row
|
||||
int selected()
|
||||
{
|
||||
return sel;
|
||||
}
|
||||
|
||||
// select
|
||||
// Select some row
|
||||
void select(int s)
|
||||
{
|
||||
sel = s;
|
||||
sels = new int[1];
|
||||
sels[0] = s;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// select
|
||||
// Select multiple rows
|
||||
void select(int s[])
|
||||
{
|
||||
if (s.length == 0) {
|
||||
sel = -1;
|
||||
sels = new int[0];
|
||||
}
|
||||
else {
|
||||
sel = s[0];
|
||||
sels = s;
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
// allSelected
|
||||
// Returns all the selected rows
|
||||
int[] allSelected()
|
||||
{
|
||||
return sels;
|
||||
}
|
||||
|
||||
// scrollto
|
||||
// Scroll to make some row visible
|
||||
void scrollto(int s)
|
||||
{
|
||||
int r = rows();
|
||||
if (s < top || s >= top+r) {
|
||||
top = s-1;
|
||||
if (top > list[0].size() - r)
|
||||
top = list[0].size() - r;
|
||||
sb.setValue(top);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
// deleteItem
|
||||
// Remove one row from the list
|
||||
void deleteItem(int n)
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].removeElementAt(n);
|
||||
if (n == sel) {
|
||||
// De-select deleted file
|
||||
sel = -1;
|
||||
}
|
||||
for(int i=0; i<sels.length; i++) {
|
||||
if (sels[i] == n) {
|
||||
// Remove from selection list
|
||||
int nsels[] = new int[sels.length-1];
|
||||
if (nsels.length > 0) {
|
||||
System.arraycopy(sels, 0, nsels, 0, i);
|
||||
System.arraycopy(sels, i+1, nsels, i,
|
||||
nsels.length-i);
|
||||
sel = nsels[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
|
||||
// clear
|
||||
// Remove everything from the list
|
||||
void clear()
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
list[i].removeAllElements();
|
||||
sel = -1;
|
||||
sels = new int[0];
|
||||
top = 0;
|
||||
repaint();
|
||||
sb.setValues(0, 1, 0);
|
||||
}
|
||||
|
||||
// setWidths
|
||||
// Set the proportional widths of each column
|
||||
void setWidths(float w[])
|
||||
{
|
||||
for(int i=0; i<title.length; i++)
|
||||
cwidth[i] = w[i];
|
||||
respace();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**Turns on or off the user's ability to adjust column widths
|
||||
* @param a Can adjust or not?
|
||||
*/
|
||||
void setAdjustable(boolean a)
|
||||
{
|
||||
adjustable = a;
|
||||
}
|
||||
|
||||
/**Turns on or off the drawing of column lines
|
||||
* @param d Draw lines or not?
|
||||
*/
|
||||
void setDrawLines(boolean d)
|
||||
{
|
||||
drawlines = d;
|
||||
}
|
||||
|
||||
/**Sets the array of colors used to draw text items.
|
||||
* @param c The color array (in row/column order), or null to
|
||||
* use the default
|
||||
*/
|
||||
void setColors(Color c[][])
|
||||
{
|
||||
colors = c;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Turns on or off multi-row selection with ctrl and shift
|
||||
void setMultiSelect(boolean m)
|
||||
{
|
||||
multiselect = m;
|
||||
}
|
||||
|
||||
// Enables the entire list
|
||||
public void enable()
|
||||
{
|
||||
enabled = true;
|
||||
sb.enable();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Disables the entire list
|
||||
public void disable()
|
||||
{
|
||||
enabled = false;
|
||||
sb.disable();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Sets or turns off the sort indication arrow for a column
|
||||
// Direction 0 = None, 1 = Up arrow, 2 = Down arrow
|
||||
public void sortingArrow(int col, int dir)
|
||||
{
|
||||
sortcol = col;
|
||||
sortdir = dir;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// reshape
|
||||
// Called when this component gets resized
|
||||
public void reshape(int nx, int ny, int nw, int nh)
|
||||
{
|
||||
if (nw != width+sbwidth || nh != height) {
|
||||
in = insets();
|
||||
sbwidth = sb.minimumSize().width;
|
||||
width = nw-sbwidth - (in.left + in.right);
|
||||
height = nh - (in.top + in.bottom);
|
||||
sb.reshape(width+in.left, in.top, sbwidth, height);
|
||||
respace();
|
||||
|
||||
// Force creation of a new backing image and re-painting
|
||||
bim = null;
|
||||
repaint();
|
||||
compscroll();
|
||||
}
|
||||
super.reshape(nx, ny, nw, nh);
|
||||
}
|
||||
|
||||
// respace
|
||||
// Compute pixel column widths from proportional widths
|
||||
void respace()
|
||||
{
|
||||
cpos[0] = 0;
|
||||
for(int i=0; i<title.length; i++)
|
||||
cpos[i+1] = cpos[i] + (int)(width*cwidth[i]);
|
||||
}
|
||||
|
||||
// paint
|
||||
// Blit the backing image to the front
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
super.paint(g);
|
||||
if (bim == null) {
|
||||
// This is the first rendering
|
||||
bim = createImage(width, height);
|
||||
bg = bim.getGraphics();
|
||||
bg.setFont(font);
|
||||
fnm = bg.getFontMetrics();
|
||||
th = fnm.getHeight() + 4;
|
||||
render();
|
||||
compscroll();
|
||||
}
|
||||
g.drawImage(bim, in.left, in.top, this);
|
||||
}
|
||||
|
||||
// update
|
||||
// Called sometime after repaint()
|
||||
public void update(Graphics g)
|
||||
{
|
||||
if (fnm != null) {
|
||||
render();
|
||||
paint(g);
|
||||
}
|
||||
}
|
||||
|
||||
// render
|
||||
// Re-draw the list into the backing image
|
||||
void render()
|
||||
{
|
||||
int fh = fnm.getHeight(), // useful font metrics
|
||||
fd = fnm.getDescent(),
|
||||
fa = fnm.getAscent();
|
||||
int bot = Math.min(top+rows()-1, list[0].size()-1);
|
||||
|
||||
// Clear title section and list
|
||||
bg.setColor(Color.lightGray);
|
||||
bg.fillRect(0, 0, width, th);
|
||||
bg.setColor(Color.white);
|
||||
bg.fillRect(0, th, width, height-th);
|
||||
Color lighterGray = new Color(Color.lightGray.getRed() + 20,
|
||||
Color.lightGray.getGreen() + 20,
|
||||
Color.lightGray.getBlue() + 20);
|
||||
|
||||
if (enabled) {
|
||||
// Mark the selected rows
|
||||
for(int i=0; i<sels.length; i++) {
|
||||
if (sels[i] >= top && sels[i] <= bot) {
|
||||
bg.setColor(sels[i] == sel ? Color.lightGray
|
||||
: lighterGray);
|
||||
bg.fillRect(0, th+(sels[i]-top)*rowh,
|
||||
width, rowh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw each column
|
||||
for(int i=0; i<title.length; i++) {
|
||||
int x = cpos[i], w = cpos[i+1]-x-1;
|
||||
|
||||
// Column title
|
||||
bg.setColor(Color.white);
|
||||
bg.drawLine(x, 0, x+w, 0);
|
||||
bg.drawLine(x, 1, x+w-1, 1);
|
||||
bg.drawLine(x, 0, x, th-1);
|
||||
bg.drawLine(x+1, 0, x+1, th-2);
|
||||
bg.setColor(Color.black);
|
||||
bg.drawLine(x, th-1, x+w, th-1);
|
||||
bg.drawLine(x, th-2, x+w-1, th-2);
|
||||
bg.drawLine(x+w, th-1, x+w, 0);
|
||||
bg.drawLine(x+w-1, th-1, x+w-1, 1);
|
||||
int tw = fnm.stringWidth(title[i]);
|
||||
if (tw < w-6)
|
||||
bg.drawString(title[i], x+(w-tw)/2, th-fd-2);
|
||||
|
||||
// Sorting arrow
|
||||
int as = th-8;
|
||||
if (sortcol == i && sortdir == 1) {
|
||||
bg.setColor(Color.white);
|
||||
bg.drawLine(x+4, th-5, x+4+as, th-5);
|
||||
bg.drawLine(x+4+as, th-5, x+4+as/2, th-5-as);
|
||||
bg.setColor(Color.black);
|
||||
bg.drawLine(x+4+as/2, th-5-as, x+4, th-5);
|
||||
}
|
||||
else if (sortcol == i && sortdir == 2) {
|
||||
bg.setColor(Color.white);
|
||||
bg.drawLine(x+4+as/2, th-5, x+4+as, th-5-as);
|
||||
bg.setColor(Color.black);
|
||||
bg.drawLine(x+4, th-5-as, x+4+as, th-5-as);
|
||||
bg.drawLine(x+4, th-5-as, x+4+as/2, th-5);
|
||||
}
|
||||
|
||||
// Column items
|
||||
if (drawlines) {
|
||||
bg.setColor(Color.lightGray);
|
||||
bg.drawLine(x+w-1, th, x+w-1, height);
|
||||
bg.setColor(Color.black);
|
||||
bg.drawLine(x+w, th, x+w, height);
|
||||
}
|
||||
for(int j=top; j<=bot; j++) {
|
||||
Object o = list[i].elementAt(j);
|
||||
if (o instanceof String) {
|
||||
// Render string in column
|
||||
String s = (String)o;
|
||||
while(fnm.stringWidth(s) > w-3)
|
||||
s = s.substring(0, s.length()-1);
|
||||
if (!enabled)
|
||||
bg.setColor(Color.lightGray);
|
||||
else if (colors != null)
|
||||
bg.setColor(colors[j][i]);
|
||||
bg.drawString(s, x+1, th+(j+1-top)*rowh-fd);
|
||||
}
|
||||
else if (o instanceof Image) {
|
||||
// Render image in column
|
||||
Image im = (Image)o;
|
||||
bg.drawImage(im, x+1, th+(j-top)*rowh, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mouseDown
|
||||
// Select a list item or a column to drag
|
||||
public boolean mouseDown(Event e, int x, int y)
|
||||
{
|
||||
if (!enabled) {
|
||||
return true;
|
||||
}
|
||||
x -= in.left;
|
||||
y -= in.top;
|
||||
coldrag = -1;
|
||||
if (y < th) {
|
||||
// Click in title bar
|
||||
for(int i=0; i<title.length; i++) {
|
||||
if (adjustable && i > 0 && Math.abs(cpos[i] - x) < 3) {
|
||||
// clicked on a column separator
|
||||
coldrag = i;
|
||||
}
|
||||
else if (x >= cpos[i] && x < cpos[i+1]) {
|
||||
// clicked in a title
|
||||
callback.headingClicked(this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Item chosen from list
|
||||
int row = (y-th)/rowh + top;
|
||||
if (row < list[0].size()) {
|
||||
// Double-click?
|
||||
boolean dclick = false;
|
||||
if (e.when-last < 1000 && sel == row)
|
||||
dclick = true;
|
||||
else
|
||||
last = e.when;
|
||||
|
||||
if (e.shiftDown() && multiselect && sel != -1) {
|
||||
// Select all from last selection to this one
|
||||
int zero = sels[0];
|
||||
if (zero < row) {
|
||||
sels = new int[row-zero+1];
|
||||
for(int i=zero; i<=row; i++)
|
||||
sels[i-zero] = i;
|
||||
}
|
||||
else {
|
||||
sels = new int[zero-row+1];
|
||||
for(int i=zero; i>=row; i--)
|
||||
sels[zero-i] = i;
|
||||
}
|
||||
}
|
||||
else if (e.controlDown() && multiselect) {
|
||||
// Add this one to selection
|
||||
int nsels[] = new int[sels.length + 1];
|
||||
System.arraycopy(sels, 0, nsels, 0,sels.length);
|
||||
nsels[sels.length] = row;
|
||||
sels = nsels;
|
||||
}
|
||||
else {
|
||||
// Select one row only, and de-select others
|
||||
sels = new int[1];
|
||||
sels[0] = row;
|
||||
}
|
||||
sel = row;
|
||||
repaint();
|
||||
last_event = e;
|
||||
if (callback != null) {
|
||||
// Callback the right function
|
||||
if (dclick) callback.doubleClick(this, row);
|
||||
else callback.singleClick(this, row);
|
||||
}
|
||||
else {
|
||||
// Send an event
|
||||
getParent().postEvent(
|
||||
new Event(this,
|
||||
Event.ACTION_EVENT,
|
||||
dclick?"Double":"Single"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// mouseDrag
|
||||
// If a column is selected, change it's width
|
||||
public boolean mouseDrag(Event e, int x, int y)
|
||||
{
|
||||
if (!enabled) {
|
||||
return true;
|
||||
}
|
||||
x -= in.left;
|
||||
y -= in.top;
|
||||
if (coldrag != -1) {
|
||||
if (x > cpos[coldrag-1]+3 && x < cpos[coldrag+1]-3) {
|
||||
cpos[coldrag] = x;
|
||||
cwidth[coldrag-1] = (cpos[coldrag]-cpos[coldrag-1]) /
|
||||
(float)width;
|
||||
cwidth[coldrag] = (cpos[coldrag+1]-cpos[coldrag]) /
|
||||
(float)width;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void moved(CbScrollbar s, int v)
|
||||
{
|
||||
moving(s, v);
|
||||
}
|
||||
|
||||
public void moving(CbScrollbar s, int v)
|
||||
{
|
||||
top = sb.getValue();
|
||||
compscroll();
|
||||
repaint();
|
||||
}
|
||||
|
||||
// compscroll
|
||||
// Re-compute the size of the scrollbar
|
||||
private void compscroll()
|
||||
{
|
||||
if (fnm == null)
|
||||
return; // not visible
|
||||
int r = rows();
|
||||
int c = list[0].size() - r;
|
||||
sb.setValues(top, r==0?1:r, list[0].size());
|
||||
}
|
||||
|
||||
// rows
|
||||
// Returns the number of rows visible in the list
|
||||
private int rows()
|
||||
{
|
||||
return Math.min(height/rowh - 1, list[0].size());
|
||||
}
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
return new Dimension(400, 100);
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return minimumSize();
|
||||
}
|
||||
}
|
||||
|
||||
// MultiColumnCallback
|
||||
// Objects implementing this interface can be passed to the MultiColumn
|
||||
// class, to have their singleClick() and doubleClick() functions called in
|
||||
// response to single or double click in the list.
|
||||
interface MultiColumnCallback
|
||||
{
|
||||
// singleClick
|
||||
// Called on a single click on a list item
|
||||
void singleClick(MultiColumn list, int num);
|
||||
|
||||
// doubleClick
|
||||
// Called upon double-clicking on a list item
|
||||
void doubleClick(MultiColumn list, int num);
|
||||
|
||||
// headingClicked
|
||||
// Called when a column heading is clicked on
|
||||
void headingClicked(MultiColumn list, int col);
|
||||
}
|
||||
|
77
file/QuickSort.java
Normal file
@ -0,0 +1,77 @@
|
||||
public class QuickSort
|
||||
{
|
||||
static int col, dir;
|
||||
|
||||
// Sorts entire array
|
||||
public static void sort(RemoteFile array[], int c, int d)
|
||||
{
|
||||
col = c;
|
||||
dir = d;
|
||||
psort(array, 0, array.length - 1);
|
||||
}
|
||||
|
||||
// Sorts partial array
|
||||
public static void psort(RemoteFile array[], int start, int end)
|
||||
{
|
||||
int p;
|
||||
if (end > start)
|
||||
{
|
||||
p = partition(array, start, end);
|
||||
psort(array, start, p-1);
|
||||
psort(array, p+1, end);
|
||||
}
|
||||
}
|
||||
|
||||
protected static int compare(RemoteFile a, RemoteFile b) {
|
||||
long rv = 0;
|
||||
if (col == 1)
|
||||
rv = a.name.compareTo(b.name);
|
||||
else if (col == 2)
|
||||
rv = a.size - b.size;
|
||||
else if (col == 3)
|
||||
rv = a.user.compareTo(b.user);
|
||||
else if (col == 4)
|
||||
rv = a.group.compareTo(b.group);
|
||||
else
|
||||
rv = a.modified - b.modified;
|
||||
rv = rv < 0 ? -1 : rv > 0 ? 1 : 0;
|
||||
return (int)(dir == 2 ? -rv : rv);
|
||||
}
|
||||
|
||||
protected static int partition(RemoteFile array[], int start, int end)
|
||||
{
|
||||
int left, right;
|
||||
RemoteFile partitionElement;
|
||||
|
||||
// Arbitrary partition start...there are better ways...
|
||||
partitionElement = array[end];
|
||||
|
||||
left = start - 1;
|
||||
right = end;
|
||||
for (;;)
|
||||
{
|
||||
while (compare(partitionElement, array[++left]) == 1)
|
||||
{
|
||||
if (left == end) break;
|
||||
}
|
||||
while (compare(partitionElement, array[--right]) == -1)
|
||||
{
|
||||
if (right == start) break;
|
||||
}
|
||||
if (left >= right) break;
|
||||
swap(array, left, right);
|
||||
}
|
||||
swap(array, left, end);
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
protected static void swap(RemoteFile array[], int i, int j)
|
||||
{
|
||||
RemoteFile temp;
|
||||
temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
}
|
||||
|
169
file/ResizePanel.java
Normal file
@ -0,0 +1,169 @@
|
||||
// ResizePanel
|
||||
// A panel with two parts, arranged either vertically or horizontally,
|
||||
// whose midpoint is adjustable
|
||||
import java.awt.*;
|
||||
import java.util.Vector;
|
||||
|
||||
public class ResizePanel extends Panel implements LayoutManager
|
||||
{
|
||||
Component one, two;
|
||||
int pos = -1;
|
||||
double ratio;
|
||||
boolean vertical;
|
||||
boolean dragging;
|
||||
int border = 100;
|
||||
|
||||
// Provide two components where component one initially occupies rt fraction of
|
||||
// parent area. When vertical=true components are layed out one above the other
|
||||
public ResizePanel(Component one, Component two, double rt, boolean vertical)
|
||||
{
|
||||
this.one = one;
|
||||
this.two = two;
|
||||
this.vertical = vertical;
|
||||
ratio = rt;
|
||||
setLayout(this);
|
||||
add(one);
|
||||
add(two);
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
Dimension s = size();
|
||||
if (vertical)
|
||||
{
|
||||
// Draw horizontal bar between vertically aligned components
|
||||
pos = (int)(s.height * ratio);
|
||||
g.setColor(Color.white);
|
||||
g.drawLine(0, pos-2, 0, pos+1);
|
||||
g.drawLine(0, pos-2, s.width-2, pos-2);
|
||||
g.setColor(Color.black);
|
||||
g.drawLine(s.width-1, pos+2, s.width-1, pos-1);
|
||||
g.drawLine(s.width-1, pos+2, 1, pos+2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw vertical divider bar
|
||||
pos = (int)(s.width * ratio);
|
||||
g.setColor(Color.white);
|
||||
g.drawLine(pos-2, 0, pos+1, 0);
|
||||
g.drawLine(pos-2, 0, pos-2, s.height-2);
|
||||
g.setColor(Color.black);
|
||||
g.drawLine(pos+2, s.height-1, pos-1, s.height-1);
|
||||
g.drawLine(pos+2, s.height-1, pos+2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect mouse click on divider bar
|
||||
public boolean mouseDown(Event evt, int x, int y)
|
||||
{
|
||||
int sh;
|
||||
Dimension s = size();
|
||||
if (vertical && y >= pos-2 && y <= pos+2)
|
||||
{
|
||||
// Started dragging
|
||||
dragging = true;
|
||||
}
|
||||
if (!vertical && x >= pos-2 && x <= pos+2)
|
||||
{
|
||||
// Started dragging
|
||||
dragging = true;
|
||||
}
|
||||
return dragging;
|
||||
}
|
||||
|
||||
// Move division point on mouse drag
|
||||
public boolean mouseDrag(Event evt, int x, int y)
|
||||
{
|
||||
if (dragging)
|
||||
{
|
||||
Dimension s = size();
|
||||
if (vertical)
|
||||
{
|
||||
if (y < border)
|
||||
pos = border;
|
||||
else if (y > s.height - border)
|
||||
pos = s.height - border;
|
||||
else
|
||||
pos = y;
|
||||
ratio = (double)pos / (double)s.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x < border)
|
||||
pos = border;
|
||||
else if (x > s.width - border)
|
||||
pos = s.width - border;
|
||||
else
|
||||
pos = x;
|
||||
ratio = (double)pos / (double)s.width;
|
||||
}
|
||||
layoutContainer(this);
|
||||
repaint();
|
||||
}
|
||||
return dragging;
|
||||
}
|
||||
|
||||
// No longer dragging on mouse button release
|
||||
public boolean mouseUp(Event evt, int x, int y)
|
||||
{
|
||||
boolean o = dragging;
|
||||
dragging = false;
|
||||
return o;
|
||||
}
|
||||
|
||||
public void addLayoutComponent(String name, Component comp)
|
||||
{
|
||||
}
|
||||
|
||||
// Arrange components within container
|
||||
public void layoutContainer(Container parent)
|
||||
{
|
||||
Dimension s = parent.size();
|
||||
if (vertical)
|
||||
{
|
||||
pos = (int)(s.height * ratio);
|
||||
one.reshape(0, 0, s.width, pos-3);
|
||||
one.layout();
|
||||
two.reshape(0, pos+3, s.width, s.height - pos - 5);
|
||||
two.layout();
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = (int)(s.width * ratio);
|
||||
one.reshape(0, 0, pos-3, s.height);
|
||||
one.layout();
|
||||
two.reshape(pos+3, 0, s.width - pos - 5, s.height);
|
||||
two.layout();
|
||||
}
|
||||
}
|
||||
|
||||
// Determine minimum size for ResizePanel
|
||||
public Dimension minimumLayoutSize(Container parent)
|
||||
{
|
||||
Dimension d1 = one.minimumSize(),
|
||||
d2 = two.minimumSize();
|
||||
|
||||
if (vertical)
|
||||
{
|
||||
// Largest of the widths, sum of the heights
|
||||
return new Dimension(d1.width > d2.width ? d1.width : d2.width,
|
||||
d1.height + d2.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Largest of the heights, sum of the widths
|
||||
return new Dimension(d1.width + d2.width,
|
||||
d1.height > d2.height ? d1.height : d2.height);
|
||||
}
|
||||
}
|
||||
|
||||
public Dimension preferredLayoutSize(Container parent)
|
||||
{
|
||||
return minimumLayoutSize(parent);
|
||||
}
|
||||
|
||||
public void removeLayoutComponent(Component comp)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
24
file/StaticTextField.java
Normal file
@ -0,0 +1,24 @@
|
||||
import java.awt.*;
|
||||
|
||||
// StaticTextField
|
||||
// A text field that is set to be non-editable by default
|
||||
class StaticTextField extends TextField
|
||||
{
|
||||
StaticTextField()
|
||||
{
|
||||
super();
|
||||
setEditable(false);
|
||||
}
|
||||
|
||||
StaticTextField(String s)
|
||||
{
|
||||
super(s);
|
||||
setEditable(false);
|
||||
}
|
||||
|
||||
StaticTextField(String s, int i)
|
||||
{
|
||||
super(s,i);
|
||||
setEditable(false);
|
||||
}
|
||||
}
|
103
file/StringSplitter.java
Normal file
@ -0,0 +1,103 @@
|
||||
import java.util.Vector;
|
||||
|
||||
// StringSplitter
|
||||
// A stringsplitter object splits a string into a number of substrings,
|
||||
// each separated by one separator character. Separator characters can be
|
||||
// included in the string by escaping them with a \
|
||||
public class StringSplitter
|
||||
{
|
||||
Vector parts = new Vector();
|
||||
int pos = 0;
|
||||
|
||||
StringSplitter(String str, char sep)
|
||||
{
|
||||
this(str, sep, true);
|
||||
}
|
||||
|
||||
StringSplitter(String str, char sep, boolean escape)
|
||||
{
|
||||
StringBuffer current;
|
||||
|
||||
parts.addElement(current = new StringBuffer());
|
||||
for(int i=0; i<str.length(); i++) {
|
||||
char c = str.charAt(i);
|
||||
if (c == '\\' && i != str.length()-1 && escape)
|
||||
current.append(str.charAt(++i));
|
||||
else if (c == sep)
|
||||
parts.addElement(current = new StringBuffer());
|
||||
else
|
||||
current.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
// countTokens
|
||||
// The number of tokens left in the string
|
||||
int countTokens()
|
||||
{
|
||||
return parts.size() - pos;
|
||||
}
|
||||
|
||||
// hasMoreTokens
|
||||
// Can we call nextToken?
|
||||
boolean hasMoreTokens()
|
||||
{
|
||||
return pos < parts.size();
|
||||
}
|
||||
|
||||
// nextToken
|
||||
// Returns the string value of the next token
|
||||
String nextToken()
|
||||
{
|
||||
if (pos < parts.size())
|
||||
return ((StringBuffer)parts.elementAt(pos++)).toString();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
// gettokens
|
||||
// Returns a vector of strings split from the given input string
|
||||
Vector gettokens()
|
||||
{
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// StringJoiner
|
||||
// The complement of StringSplitter. Takes a number of substrings and adds
|
||||
// them to a string, separated by some character. If the separator character
|
||||
// appears in one of the substrings, escape it with a \
|
||||
class StringJoiner
|
||||
{
|
||||
char sep;
|
||||
StringBuffer str = new StringBuffer();
|
||||
int count = 0;
|
||||
|
||||
// Create a new StringJoiner using the given separator
|
||||
StringJoiner(char s)
|
||||
{
|
||||
sep = s;
|
||||
}
|
||||
|
||||
// add
|
||||
// Add one string, and a separator
|
||||
void add(String s)
|
||||
{
|
||||
if (count != 0)
|
||||
str.append(sep);
|
||||
for(int i=0; i<s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == sep || c == '\\') str.append('\\');
|
||||
str.append(c);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
// toString
|
||||
// Get the resulting string
|
||||
public String toString()
|
||||
{
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
|
185
file/TabbedPanel.java
Normal file
@ -0,0 +1,185 @@
|
||||
// TabbedPanel
|
||||
// A panel capable of displaying one of many components at a time. The
|
||||
// component to display is chosen by a row of tab buttons.
|
||||
import java.awt.*;
|
||||
import java.util.Vector;
|
||||
|
||||
public class TabbedPanel extends Panel
|
||||
{
|
||||
TabSelector tab; // component for choosing panel
|
||||
TabbedDisplayPanel disp; // where other panels are displayed
|
||||
CardLayout card;
|
||||
|
||||
TabbedPanel()
|
||||
{
|
||||
this(Util.body_hi, Util.dark_edge_hi, Util.body);
|
||||
}
|
||||
|
||||
TabbedPanel(Color hi, Color lo, Color bk)
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
add("North",tab = new TabSelector(hi, lo, bk));
|
||||
add("Center",disp = new TabbedDisplayPanel(hi, lo));
|
||||
disp.setLayout(card = new CardLayout());
|
||||
}
|
||||
|
||||
// addItem
|
||||
// Add a component to be chosen by a tab with the given name
|
||||
void addItem(String n, Component c)
|
||||
{
|
||||
tab.addItem(n);
|
||||
disp.addItem(n, c);
|
||||
}
|
||||
|
||||
// select
|
||||
// Display a component in the panel
|
||||
void select(String n)
|
||||
{
|
||||
tab.choose(n);
|
||||
disp.choose(n);
|
||||
}
|
||||
|
||||
// chose
|
||||
// Called back by a TabSelector object when the user clicks on a tab
|
||||
void chose(String n)
|
||||
{
|
||||
disp.choose(n);
|
||||
}
|
||||
}
|
||||
|
||||
class TabSelector extends Canvas
|
||||
{
|
||||
Color hi, lo, bk;
|
||||
Vector name = new Vector();
|
||||
int chosen = 0;
|
||||
Font font = new Font("timesRoman", Font.PLAIN, 12),
|
||||
chfont = new Font(font.getName(), Font.BOLD, 13);
|
||||
|
||||
TabSelector(Color h, Color l, Color b)
|
||||
{
|
||||
hi = h; lo = l; bk = b;
|
||||
}
|
||||
|
||||
void addItem(String n)
|
||||
{
|
||||
name.addElement(n);
|
||||
paint(getGraphics());
|
||||
}
|
||||
|
||||
void choose(String n)
|
||||
{
|
||||
for(int i=0; i<name.size(); i++)
|
||||
if (((String)name.elementAt(i)).equals(n)) {
|
||||
chosen = i;
|
||||
paint(getGraphics());
|
||||
}
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
if (g == null || name.size() == 0)
|
||||
return;
|
||||
g.setColor(bk);
|
||||
g.fillRect(0, 0, size().width, size().height);
|
||||
int tw = size().width / name.size(),
|
||||
th = size().height;
|
||||
for(int i=0; i<name.size(); i++) {
|
||||
int x = tw*i;
|
||||
if (i == chosen) {
|
||||
g.setColor(lo);
|
||||
g.drawLine(x+tw-3, 1, x+tw-3, th-1);
|
||||
g.drawLine(x+tw-4, 2, x+tw-4, th-1);
|
||||
g.setColor(hi);
|
||||
g.drawLine(x, 0, x, th-1);
|
||||
g.drawLine(x+1, 0, x+1, th-1);
|
||||
g.drawLine(x, 0, x+tw-4, 0);
|
||||
g.drawLine(x, 1, x+tw-5, 1);
|
||||
g.drawLine(x+tw-3, th-1, x+tw-1, th-1);
|
||||
g.drawLine(x+tw-3, th-2, x+tw-1, th-2);
|
||||
}
|
||||
else {
|
||||
g.setColor(lo);
|
||||
g.drawLine(x+tw-3, 6, x+tw-3, th-1);
|
||||
g.drawLine(x+tw-4, 7, x+tw-4, th-1);
|
||||
g.setColor(hi);
|
||||
g.drawLine(x, 5, x, th-1);
|
||||
g.drawLine(x+1, 5, x+1, th-1);
|
||||
g.drawLine(x, 5, x+tw-4, 5);
|
||||
g.drawLine(x, 6, x+tw-5, 6);
|
||||
g.drawLine(x, th-1, x+tw-1, th-1);
|
||||
g.drawLine(x, th-2, x+tw-1, th-2);
|
||||
}
|
||||
g.setColor(lo);
|
||||
if (i == chosen) g.setFont(chfont);
|
||||
else g.setFont(font);
|
||||
String str = (String)name.elementAt(i);
|
||||
int textw = g.getFontMetrics().stringWidth(str);
|
||||
int texth = g.getFontMetrics().getHeight();
|
||||
if (textw < tw-5)
|
||||
g.drawString(str, x+(tw-textw)/2, (th-texth)/2+texth);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mouseDown(Event evt, int x, int y)
|
||||
{
|
||||
if (name.size() == 0) return false;
|
||||
chosen = x / (size().width / name.size());
|
||||
paint(getGraphics());
|
||||
((TabbedPanel)getParent()).chose((String)name.elementAt(chosen));
|
||||
return true;
|
||||
}
|
||||
|
||||
public Dimension minimumSize()
|
||||
{
|
||||
return new Dimension(50,25);
|
||||
}
|
||||
|
||||
public Dimension preferredSize()
|
||||
{
|
||||
return minimumSize();
|
||||
}
|
||||
}
|
||||
|
||||
class TabbedDisplayPanel extends Panel
|
||||
{
|
||||
Color hi, lo;
|
||||
CardLayout card;
|
||||
|
||||
TabbedDisplayPanel(Color h, Color l)
|
||||
{
|
||||
hi = h; lo = l;
|
||||
setLayout(card = new CardLayout());
|
||||
}
|
||||
|
||||
// addItem
|
||||
// Add one component to the set of possibles to be shown
|
||||
void addItem(String n, Component c)
|
||||
{
|
||||
add(n, c);
|
||||
}
|
||||
|
||||
// choose
|
||||
// Display the named panel
|
||||
void choose(String n)
|
||||
{
|
||||
((CardLayout)getLayout()).show(this, n);
|
||||
}
|
||||
|
||||
public Insets insets()
|
||||
{
|
||||
return new Insets(5,5,5,5);
|
||||
}
|
||||
|
||||
public void paint(Graphics g)
|
||||
{
|
||||
g.setColor(hi);
|
||||
g.drawLine(0, 0, 0, size().height-1);
|
||||
g.drawLine(1, 0, 1, size().height-1);
|
||||
g.setColor(lo);
|
||||
g.drawLine(0, size().height-1, size().width-1, size().height-1);
|
||||
g.drawLine(0, size().height-2, size().width-1, size().height-2);
|
||||
g.drawLine(size().width-1, size().height-1, size().width-1, 0);
|
||||
g.drawLine(size().width-2, size().height-1, size().width-2, 0);
|
||||
}
|
||||
}
|
||||
|
333
file/ToolbarLayout.java
Normal file
@ -0,0 +1,333 @@
|
||||
import java.awt.*;
|
||||
import java.lang.Math;
|
||||
|
||||
/**
|
||||
* A ToolbarLayout arranges components in a left-to-right flow, much
|
||||
* like the FlowLayout which is supplied with the JDK. However, it
|
||||
* fixes the problem with the FlowLayout that occurs when a FlowLayout
|
||||
* is for a North aligned component of a BorderLayout--namely, that
|
||||
* if the window is shrunk so that some of the components within the
|
||||
* FlowLayout wrap to the next line the component does not grow in
|
||||
* height to support this wrapping. This bug was caused by the library
|
||||
* designers using the preferred size in recalculating, not the size
|
||||
* which is determined by the window width. As such, the flow layout
|
||||
* would always want to be the height of one row.
|
||||
*
|
||||
* A ToolbarLayout lets each component assume its natural (preferred) size.
|
||||
*
|
||||
* NOTE: This class was initially a subclass of FlowLayout, but we
|
||||
* encountered problems using that approach.
|
||||
*
|
||||
* @version 0.10, 1999-04-27
|
||||
* @author Peter Armstrong
|
||||
* @author Tony Johnson
|
||||
*/
|
||||
public class ToolbarLayout implements LayoutManager, java.io.Serializable {
|
||||
|
||||
/**
|
||||
* This value indicates that each row of components
|
||||
* should be left-justified.
|
||||
*/
|
||||
public static final int LEFT = 0;
|
||||
|
||||
/**
|
||||
* This value indicates that each row of components
|
||||
* should be centered.
|
||||
*/
|
||||
public static final int CENTER = 1;
|
||||
|
||||
/**
|
||||
* This value indicates that each row of components
|
||||
* should be right-justified.
|
||||
*/
|
||||
public static final int RIGHT = 2;
|
||||
|
||||
int align;
|
||||
int hgap;
|
||||
int vgap;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new ToolbarLayout with a left alignment and a
|
||||
* default 5-unit horizontal and vertical gap.
|
||||
*/
|
||||
public ToolbarLayout() {
|
||||
this(LEFT, 5, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ToolbarLayout with the specified alignment and a
|
||||
* default 5-unit horizontal and vertical gap.
|
||||
* The value of the alignment argument must be one of
|
||||
* <code>ToolbarLayout.LEFT</code>, <code>ToolbarLayout.RIGHT</code>,
|
||||
* or <code>ToolbarLayout.CENTER</code>.
|
||||
* @param align the alignment value
|
||||
*/
|
||||
public ToolbarLayout(int align) {
|
||||
this(align, 5, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ToolbarLayout with the indicated alignment
|
||||
* and the indicated horizontal and vertical gaps.
|
||||
* <p>
|
||||
* The value of the alignment argument must be one of
|
||||
* <code>ToolbarLayout.LEFT</code>, <code>ToolbarLayout.RIGHT</code>,
|
||||
* or <code>ToolbarLayout.CENTER</code>.
|
||||
* @param align the alignment value.
|
||||
* @param hgap the horizontal gap between components.
|
||||
* @param vgap the vertical gap between components.
|
||||
*/
|
||||
public ToolbarLayout(int align, int hgap, int vgap) {
|
||||
this.align = align;
|
||||
this.hgap = hgap;
|
||||
this.vgap = vgap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the alignment for this layout.
|
||||
* Possible values are <code>ToolbarLayout.LEFT</code>,
|
||||
* <code>ToolbarLayout.RIGHT</code>, or <code>ToolbarLayout.CENTER</code>.
|
||||
* @return the alignment value for this layout.
|
||||
* @see ToolbarLayout#setAlignment
|
||||
*/
|
||||
public int getAlignment() {
|
||||
return align;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alignment for this layout.
|
||||
* Possible values are <code>ToolbarLayout.LEFT</code>,
|
||||
* <code>ToolbarLayout.RIGHT</code>, and <code>ToolbarLayout.CENTER</code>.
|
||||
* @param align the alignment value.
|
||||
* @see ToolbarLayout#getAlignment
|
||||
*/
|
||||
public void setAlignment(int align) {
|
||||
this.align = align;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the horizontal gap between components.
|
||||
* @return the horizontal gap between components.
|
||||
* @see ToolbarLayout#setHgap
|
||||
*/
|
||||
public int getHgap() {
|
||||
return hgap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the horizontal gap between components.
|
||||
* @param hgap the horizontal gap between components
|
||||
* @see ToolbarLayout#getHgap
|
||||
*/
|
||||
public void setHgap(int hgap) {
|
||||
this.hgap = hgap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the vertical gap between components.
|
||||
* @return the vertical gap between components.
|
||||
* @see ToolbarLayout#setVgap
|
||||
*/
|
||||
public int getVgap() {
|
||||
return vgap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the vertical gap between components.
|
||||
* @param vgap the vertical gap between components
|
||||
* @see ToolbarLayout#getVgap
|
||||
*/
|
||||
public void setVgap(int vgap) {
|
||||
this.vgap = vgap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified component to the layout. Sets the orientation to be horizontal.
|
||||
* @param name the name of the component
|
||||
* @param comp the component to be added
|
||||
*/
|
||||
public void addLayoutComponent(String name, Component comp) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified component from the layout. Not used by
|
||||
* this class.
|
||||
* @param comp the component to remove
|
||||
* @see java.awt.Container#removeAll
|
||||
*/
|
||||
public void removeLayoutComponent(Component comp) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred dimensions for this layout given the components
|
||||
* in the specified target container. This method is the difference
|
||||
* between ToolbarLayout and FlowLayout.
|
||||
* @param target the component which needs to be laid out
|
||||
* @return the preferred dimensions to lay out the
|
||||
* subcomponents of the specified container.
|
||||
* @see Container
|
||||
* @see #minimumLayoutSize
|
||||
* @see java.awt.Container#getPreferredSize
|
||||
*/
|
||||
public Dimension preferredLayoutSize(Container target) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
Dimension dim = new Dimension(0, 0);
|
||||
int nmembers = target.getComponentCount();
|
||||
|
||||
Insets insets = target.getInsets();
|
||||
|
||||
int numRows = 1; //the number of rows
|
||||
int rowSumWidth = insets.left + insets.right; //the width of the row so far
|
||||
int rowMaxWidth = target.getSize().width; //the width that the ToolbarLayout is in
|
||||
int rowHeight = 0; //the height of each row
|
||||
int numOnRow = 0; //the number of components on the row
|
||||
|
||||
for (int i = 0 ; i < nmembers ; i++) {
|
||||
Component m = target.getComponent(i);
|
||||
if (m.isVisible()) {
|
||||
Dimension d = m.getPreferredSize();
|
||||
rowHeight = Math.max(rowHeight, d.height); //make each row the height of the biggest component of all
|
||||
if (i > 0) {
|
||||
rowSumWidth += hgap;//add on the pre-spacing if this is not the first component
|
||||
}
|
||||
rowSumWidth += d.width; //add the width of the component
|
||||
|
||||
//if it overflowed and if there are components already on this row then bump this component to next row
|
||||
if ((rowSumWidth + hgap) > rowMaxWidth) {
|
||||
if (numOnRow > 0) {
|
||||
numRows++;
|
||||
rowSumWidth = insets.left + insets.right + d.width;
|
||||
numOnRow = 0;//reset the number of components on the next row (we ++ no matter what later)
|
||||
}
|
||||
}
|
||||
numOnRow++;//add this component to the count of the number on the row
|
||||
}
|
||||
}
|
||||
dim.width = rowMaxWidth;
|
||||
dim.height = insets.top + insets.bottom + numRows*rowHeight + vgap*(numRows + 1);
|
||||
return dim;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum dimensions needed to layout the components
|
||||
* contained in the specified target container.
|
||||
* @param target the component which needs to be laid out
|
||||
* @return the minimum dimensions to lay out the
|
||||
* subcomponents of the specified container.
|
||||
* @see #preferredLayoutSize
|
||||
* @see java.awt.Container
|
||||
* @see java.awt.Container#doLayout
|
||||
*/
|
||||
public Dimension minimumLayoutSize(Container target) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
Dimension dim = new Dimension(0, 0);
|
||||
int nmembers = target.getComponentCount();
|
||||
|
||||
for (int i = 0 ; i < nmembers ; i++) {
|
||||
Component m = target.getComponent(i);
|
||||
if (m.isVisible()) {
|
||||
Dimension d = m.getMinimumSize();
|
||||
dim.height = Math.max(dim.height, d.height);
|
||||
if (i > 0) {
|
||||
dim.width += hgap;
|
||||
}
|
||||
dim.width += d.width;
|
||||
}
|
||||
}
|
||||
Insets insets = target.getInsets();
|
||||
dim.width += insets.left + insets.right + hgap*2;
|
||||
dim.height += insets.top + insets.bottom + vgap*2;
|
||||
return dim;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Centers the elements in the specified row, if there is any slack.
|
||||
* @param target the component which needs to be moved
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @param width the width dimensions
|
||||
* @param height the height dimensions
|
||||
* @param rowStart the beginning of the row
|
||||
* @param rowEnd the the ending of the row
|
||||
*/
|
||||
private void moveComponents(Container target, int x, int y, int width, int height, int rowStart, int rowEnd) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
switch (align) {
|
||||
case LEFT:
|
||||
break;
|
||||
case CENTER:
|
||||
x += width / 2;
|
||||
break;
|
||||
case RIGHT:
|
||||
x += width;
|
||||
break;
|
||||
}
|
||||
for (int i = rowStart ; i < rowEnd ; i++) {
|
||||
Component m = target.getComponent(i);
|
||||
if (m.isVisible()) {
|
||||
m.setLocation(x, y + (height - m.size().height) / 2);
|
||||
x += hgap + m.size().width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lays out the container. This method lets each component take
|
||||
* its preferred size by reshaping the components in the
|
||||
* target container in order to satisfy the constraints of
|
||||
* this <code>ToolbarLayout</code> object.
|
||||
* @param target the specified component being laid out.
|
||||
* @see Container
|
||||
* @see java.awt.Container#doLayout
|
||||
*/
|
||||
public void layoutContainer(Container target) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
Insets insets = target.getInsets();
|
||||
int maxwidth = target.size().width - (insets.left + insets.right + hgap*2);
|
||||
int nmembers = target.getComponentCount();
|
||||
int x = 0, y = insets.top + vgap;
|
||||
int rowh = 0, start = 0;
|
||||
|
||||
for (int i = 0 ; i < nmembers ; i++) {
|
||||
Component m = target.getComponent(i);
|
||||
if (m.isVisible()) {
|
||||
Dimension d = m.getPreferredSize();
|
||||
m.setSize(d.width, d.height);
|
||||
if ((x == 0) || ((x + d.width) <= maxwidth)) {
|
||||
if (x > 0) {
|
||||
x += hgap;
|
||||
}
|
||||
x += d.width;
|
||||
rowh = Math.max(rowh, d.height);
|
||||
} else {
|
||||
moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, i);
|
||||
x = d.width;
|
||||
y += vgap + rowh;
|
||||
rowh = d.height;
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, nmembers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this <code>ToolbarLayout</code>
|
||||
* object and its values.
|
||||
* @return a string representation of this layout.
|
||||
*/
|
||||
public String toString() {
|
||||
String str = "";
|
||||
switch (align) {
|
||||
case LEFT: str = ",align=left"; break;
|
||||
case CENTER: str = ",align=center"; break;
|
||||
case RIGHT: str = ",align=right"; break;
|
||||
}
|
||||
return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";
|
||||
}
|
||||
}
|
148
file/Util.java
Normal file
@ -0,0 +1,148 @@
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
class Util
|
||||
{
|
||||
static Frame fr;
|
||||
static Graphics g;
|
||||
static Font f;
|
||||
static FontMetrics fnm;
|
||||
static Toolkit tk;
|
||||
|
||||
static Color light_edge = Color.white;
|
||||
static Color dark_edge = Color.black;
|
||||
static Color body = Color.lightGray;
|
||||
static Color body_hi = new Color(210, 210, 210);
|
||||
static Color light_edge_hi = Color.white;
|
||||
static Color dark_edge_hi = Color.darkGray;
|
||||
static Color dark_bg = new Color(150, 150, 150);
|
||||
static Color text = Color.black;
|
||||
static Color light_bg = Color.white;
|
||||
|
||||
static
|
||||
{
|
||||
fr = new Frame();
|
||||
fr.addNotify();
|
||||
g = fr.getGraphics();
|
||||
setFont(new Font("TimesRoman", Font.PLAIN, 8));
|
||||
tk = Toolkit.getDefaultToolkit();
|
||||
}
|
||||
|
||||
static boolean waitForImage(Image i)
|
||||
{
|
||||
MediaTracker mt = new MediaTracker(fr);
|
||||
mt.addImage(i, 0);
|
||||
try { mt.waitForAll(); } catch(Exception e) { return false; }
|
||||
return !mt.isErrorAny();
|
||||
}
|
||||
|
||||
static boolean waitForImage(Image i, int w, int h)
|
||||
{
|
||||
MediaTracker mt = new MediaTracker(fr);
|
||||
mt.addImage(i, w, h, 0);
|
||||
try { mt.waitForAll(); } catch(Exception e) { return false; }
|
||||
return !mt.isErrorAny();
|
||||
}
|
||||
|
||||
static int getWidth(Image i)
|
||||
{
|
||||
waitForImage(i);
|
||||
return i.getWidth(fr);
|
||||
}
|
||||
|
||||
static int getHeight(Image i)
|
||||
{
|
||||
waitForImage(i);
|
||||
return i.getHeight(fr);
|
||||
}
|
||||
|
||||
static Image createImage(int w, int h)
|
||||
{
|
||||
return fr.createImage(w, h);
|
||||
}
|
||||
|
||||
static Image createImage(ImageProducer p)
|
||||
{
|
||||
return fr.createImage(p);
|
||||
}
|
||||
|
||||
static Object createObject(String name)
|
||||
{
|
||||
try {
|
||||
Class c = Class.forName(name);
|
||||
return c.newInstance();
|
||||
}
|
||||
catch(Exception e) {
|
||||
System.err.println("Failed to create object "+name+" : "+
|
||||
e.getClass().getName());
|
||||
System.exit(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**Create a new instance of some object
|
||||
*/
|
||||
static Object createObject(Object o)
|
||||
{
|
||||
try { return o.getClass().newInstance(); }
|
||||
catch(Exception e) {
|
||||
System.err.println("Failed to reproduce object "+o+" : "+
|
||||
e.getClass().getName());
|
||||
System.exit(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
static void dottedRect(Graphics g, int x1, int y1,
|
||||
int x2, int y2, int s)
|
||||
{
|
||||
int i, s2 = s*2, t;
|
||||
if (x2 < x1) { t = x1; x1 = x2; x2 = t; }
|
||||
if (y2 < y1) { t = y1; y1 = y2; y2 = t; }
|
||||
for(i=x1; i<=x2; i+=s2)
|
||||
g.drawLine(i, y1, i+s > x2 ? x2 : i+s, y1);
|
||||
for(i=y1; i<=y2; i+=s2)
|
||||
g.drawLine(x2, i, x2, i+s > y2 ? y2 : i+s);
|
||||
for(i=x2; i>=x1; i-=s2)
|
||||
g.drawLine(i, y2, i-s < x1 ? x1 : i-s, y2);
|
||||
for(i=y2; i>=y1; i-=s2)
|
||||
g.drawLine(x1, i, x1, i-s < y1 ? y1 : i-s);
|
||||
}
|
||||
|
||||
static void recursiveLayout(Container c)
|
||||
{
|
||||
c.layout();
|
||||
for(int i=0; i<c.countComponents(); i++) {
|
||||
Component cc = c.getComponent(i);
|
||||
if (cc instanceof Container)
|
||||
recursiveLayout((Container)cc);
|
||||
}
|
||||
}
|
||||
|
||||
static void recursiveBackground(Component c, Color b)
|
||||
{
|
||||
if (c instanceof TextField || c instanceof Choice ||
|
||||
c instanceof TextArea)
|
||||
return; // leave these alone
|
||||
c.setBackground(b);
|
||||
if (c instanceof Container) {
|
||||
Container cn = (Container)c;
|
||||
for(int i=0; i<cn.countComponents(); i++)
|
||||
recursiveBackground(cn.getComponent(i), b);
|
||||
}
|
||||
}
|
||||
|
||||
static void recursiveBody(Component c)
|
||||
{
|
||||
recursiveBackground(c, Util.body);
|
||||
}
|
||||
|
||||
static void setFont(Font nf)
|
||||
{
|
||||
f = nf;
|
||||
g.setFont(f);
|
||||
fnm = g.getFontMetrics();
|
||||
}
|
||||
}
|
||||
|
143
file/acl_security.pl
Normal file
@ -0,0 +1,143 @@
|
||||
|
||||
require 'file-lib.pl';
|
||||
do '../ui-lib.pl';
|
||||
|
||||
# acl_security_form(&options)
|
||||
# Output HTML for editing security options for the file module
|
||||
sub acl_security_form
|
||||
{
|
||||
print "<tr> <td><b>$text{'acl_user'}</b></td>\n";
|
||||
local $u = $_[0]->{'uid'} < 0 ? '' : getpwuid($_[0]->{'uid'});
|
||||
printf "<td colspan=3><input type=radio name=uid_def value=1 %s> %s\n",
|
||||
$_[0]->{'uid'} < 0 ? 'checked' : '', $text{'acl_user_def'};
|
||||
printf "<input type=radio name=uid_def value=0 %s>\n",
|
||||
$_[0]->{'uid'} < 0 ? '' : 'checked';
|
||||
print "<input name=uid size=8 value='$u'> ",
|
||||
&user_chooser_button("uid", 0),"</td> </tr>\n";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_umask'}</b></td>\n";
|
||||
print "<td colspan=3><input name=umask size=3 value='$_[0]->{'umask'}'></td> </tr>\n";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_follow'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=follow value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'follow'} == 1 ? "checked" : "";
|
||||
printf "<input type=radio name=follow value=2 %s> $text{'acl_fyes'}\n",
|
||||
$_[0]->{'follow'} == 2 ? "checked" : "";
|
||||
printf "<input type=radio name=follow value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'follow'} == 0 ? "checked" : "";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_ro'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=ro value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'ro'} ? "checked" : "";
|
||||
printf "<input type=radio name=ro value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'ro'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_max'}</b></td>\n";
|
||||
printf "<td colspan=3><input type=radio name=max_def value=1 %s> %s\n",
|
||||
$_[0]->{'max'} ? "" : "checked", $text{'acl_unlim'};
|
||||
printf "<input type=radio name=max_def value=0 %s>\n",
|
||||
$_[0]->{'max'} ? "checked" : "";
|
||||
printf "<input name=max size=8 value='%s'> %s</td> </tr>\n",
|
||||
$_[0]->{'max'}, $text{'acl_b'};
|
||||
|
||||
print "<tr> <td><b>$text{'acl_archive'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=archive value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'archive'} == 1 ? "checked" : "";
|
||||
printf "<input type=radio name=archive value=2 %s> $text{'acl_archmax'}\n",
|
||||
$_[0]->{'archive'} == 2 ? "checked" : "";
|
||||
printf "<input name=archmax size=10 value='%s'> %s\n",
|
||||
$_[0]->{'archmax'}, $text{'acl_b'};
|
||||
printf "<input type=radio name=archive value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'archive'} == 0 ? "checked" : "";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_unarchive'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=unarchive value=2 %s> %s\n",
|
||||
$_[0]->{'unarchive'} == 2 ? "checked" : "", $text{'acl_unarchive2'};
|
||||
printf "<input type=radio name=unarchive value=1 %s> %s\n",
|
||||
$_[0]->{'unarchive'} == 1 ? "checked" : "", $text{'acl_unarchive1'};
|
||||
printf "<input type=radio name=unarchive value=0 %s> %s</td> </tr>\n",
|
||||
$_[0]->{'unarchive'} == 0 ? "checked" : "", $text{'acl_unarchive0'};
|
||||
|
||||
print "<tr> <td><b>$text{'acl_dostounix'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=dostounix value=1 %s> %s\n",
|
||||
$_[0]->{'dostounix'} == 1 ? "checked" : "", $text{'yes'};
|
||||
printf "<input type=radio name=dostounix value=0 %s> %s</td> </tr>\n",
|
||||
$_[0]->{'dostounix'} == 0 ? "checked" : "", $text{'no'};
|
||||
|
||||
print "<tr> <td valign=top><b>$text{'acl_buttons'}</b></td> <td colspan=3>\n";
|
||||
foreach $b (@file_buttons) {
|
||||
printf "<input type=checkbox name=button_%s %s> %s<br>\n",
|
||||
$b, $_[0]->{'button_'.$b} ? "checked" : "",
|
||||
$text{'acl_button_'.$b};
|
||||
}
|
||||
print "</td> </tr>\n";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_noperms'}</b></td>\n";
|
||||
print "<td>",&ui_radio("noperms", int($_[0]->{'noperms'}),
|
||||
[ [ 0, $text{'yes'} ], [ 1, $text{'no'} ] ]),"</td>\n";
|
||||
|
||||
print "<td><b>$text{'acl_nousers'}</b></td>\n";
|
||||
print "<td>",&ui_radio("nousers", int($_[0]->{'nousers'}),
|
||||
[ [ 0, $text{'yes'} ], [ 1, $text{'no'} ] ]),"</td> </tr>\n";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_filesystems'}</b></td>\n";
|
||||
print "<td>",&ui_yesno_radio("filesystems",
|
||||
int($_[0]->{'filesystems'})),"</td>\n";
|
||||
|
||||
print "<td><b>$text{'acl_contents'}</b></td>\n";
|
||||
print "<td>",&ui_yesno_radio("contents",
|
||||
int($_[0]->{'contents'})),"</td> </tr>\n";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_chroot'}</b></td>\n";
|
||||
printf "<td colspan=3><input name=chroot size=40 value='%s'></td>\n",
|
||||
$_[0]->{'chroot'};
|
||||
|
||||
print "<tr> <td valign=top><b>$text{'acl_dirs'}</b><br>$text{'acl_relto'}</td>\n";
|
||||
print "<td colspan=3><textarea name=root rows=3 cols=40>",
|
||||
join("\n", split(/\s+/, $_[0]->{'root'})),"</textarea><br>\n";
|
||||
printf "<input type=checkbox name=home value=1 %s> %s<br>\n",
|
||||
$_[0]->{'home'} ? 'checked' : '', $text{'acl_home'};
|
||||
printf "<input type=checkbox name=goto value=1 %s> %s</td>\n",
|
||||
$_[0]->{'goto'} ? 'checked' : '', $text{'acl_goto'};
|
||||
|
||||
print "<tr> <td valign=top><b>$text{'acl_nodirs'}</b><br>$text{'acl_relto'}</td>\n";
|
||||
print "<td colspan=3><textarea name=noroot rows=3 cols=40>",
|
||||
join("\n", split(/\s+/, $_[0]->{'noroot'})),"</textarea><br>\n";
|
||||
}
|
||||
|
||||
# acl_security_save(&options)
|
||||
# Parse the form for security options for the file module
|
||||
sub acl_security_save
|
||||
{
|
||||
$_[0]->{'uid'} = $in{'uid_def'} ? -1 : getpwnam($in{'uid'});
|
||||
$in{'root'} =~ s/\r//g;
|
||||
local @root = split(/\s+/, $in{'root'});
|
||||
map { s/\/+/\//g } @root;
|
||||
map { s/([^\/])\/+$/$1/ } @root;
|
||||
$_[0]->{'root'} = join(" ", @root);
|
||||
$in{'noroot'} =~ s/\r//g;
|
||||
local @noroot = split(/\s+/, $in{'noroot'});
|
||||
map { s/\/+/\//g } @noroot;
|
||||
map { s/([^\/])\/+$/$1/ } @noroot;
|
||||
$_[0]->{'noroot'} = join(" ", @noroot);
|
||||
$_[0]->{'follow'} = $in{'follow'};
|
||||
$_[0]->{'ro'} = $in{'ro'};
|
||||
$in{'umask'} =~ /^[0-7]{3}$/ || &error("Invalid umask");
|
||||
$_[0]->{'umask'} = $in{'umask'};
|
||||
$_[0]->{'home'} = $in{'home'};
|
||||
$_[0]->{'goto'} = $in{'goto'};
|
||||
$_[0]->{'max'} = $in{'max_def'} ? undef : $in{'max'};
|
||||
$_[0]->{'archive'} = $in{'archive'};
|
||||
$_[0]->{'archmax'} = $in{'archmax'};
|
||||
foreach $b (@file_buttons) {
|
||||
$_[0]->{"button_$b"} = $in{"button_$b"};
|
||||
}
|
||||
$_[0]->{'unarchive'} = $in{'unarchive'};
|
||||
$_[0]->{'dostounix'} = $in{'dostounix'};
|
||||
$_[0]->{'chroot'} = $in{'chroot'};
|
||||
$_[0]->{'noperms'} = $in{'noperms'};
|
||||
$_[0]->{'nousers'} = $in{'nousers'};
|
||||
$_[0]->{'filesystems'} = $in{'filesystems'};
|
||||
$_[0]->{'contents'} = $in{'contents'};
|
||||
}
|
||||
|
92
file/chmod.cgi
Executable file
@ -0,0 +1,92 @@
|
||||
#!/usr/local/bin/perl
|
||||
# chmod.cgi
|
||||
# Change the ownership and permissions on a file
|
||||
|
||||
require './file-lib.pl';
|
||||
$disallowed_buttons{'info'} && &error($text{'ebutton'});
|
||||
&ReadParse();
|
||||
&webmin_log($in{'linkto'} ? "relink" : "chmod", undef, $in{'path'}, \%in);
|
||||
&switch_acl_uid_and_chroot();
|
||||
print "Content-type: text/plain\n\n";
|
||||
!$access{'ro'} && &can_access($in{'path'}) ||
|
||||
&failure(&text('chmod_eaccess', $in{'path'}));
|
||||
|
||||
if (defined($in{'user'})) {
|
||||
$uid = $in{'user'} =~ /^\d+$/ ? $in{'user'} :
|
||||
defined(%user_to_uid) ? $user_to_uid{$in{'user'}} :
|
||||
getpwnam($in{'user'});
|
||||
&failure(&text('chmod_euser', $in{'user'})) if (!defined($uid));
|
||||
$gid = $in{'group'} =~ /^\d+$/ ? $in{'group'} :
|
||||
defined(%group_to_gid) ? $group_to_gid{$in{'group'}} :
|
||||
getgrnam($in{'group'});
|
||||
&failure(&text('chmod_egroup', $in{'group'})) if (!defined($gid));
|
||||
}
|
||||
|
||||
if ($in{'linkto'}) {
|
||||
# Just changing the link target
|
||||
$follow && &failure($text{'chmod_efollow'});
|
||||
&lock_file($in{'path'});
|
||||
unlink($in{'path'});
|
||||
symlink($in{'linkto'}, $in{'path'}) ||
|
||||
&failure(&text('chmod_elink', $1));
|
||||
&unlock_file($in{'path'});
|
||||
}
|
||||
elsif ($in{'rec'} == 0) {
|
||||
# Just this file
|
||||
&update($in{'path'}, 0);
|
||||
}
|
||||
elsif ($in{'rec'} == 1) {
|
||||
# This directory and all its files
|
||||
&update($in{'path'}, 0);
|
||||
opendir(DIR, $in{'path'});
|
||||
foreach $f (readdir(DIR)) {
|
||||
next if ($f eq "." || $f eq "..");
|
||||
next if (-l $full);
|
||||
&update("$in{'path'}/$f", 1) if (!-d $full);
|
||||
}
|
||||
closedir(DIR);
|
||||
}
|
||||
elsif ($in{'rec'} == 2) {
|
||||
# Directory and all subdirectories
|
||||
&update($in{'path'}, 0);
|
||||
&recurse($in{'path'});
|
||||
}
|
||||
print "\n";
|
||||
|
||||
sub recurse
|
||||
{
|
||||
local(@files, $f, $full);
|
||||
opendir(DIR, $_[0]);
|
||||
@files = readdir(DIR);
|
||||
closedir(DIR);
|
||||
foreach $f (@files) {
|
||||
$full = "$_[0]/$f";
|
||||
next if ($f eq "." || $f eq "..");
|
||||
next if (-l $full);
|
||||
&update($full, !-d $full);
|
||||
&recurse($full) if (-d $full);
|
||||
}
|
||||
}
|
||||
|
||||
sub failure
|
||||
{
|
||||
print @_,"\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
# update(file, perms_only)
|
||||
sub update
|
||||
{
|
||||
local $perms = $in{'perms'};
|
||||
if (defined($perms)) {
|
||||
if ($_[1]) {
|
||||
@st = stat($_[0]);
|
||||
$perms = ($perms & 0777) | ($st[2] & 037777777000);
|
||||
}
|
||||
chmod($perms, $_[0]) || &failure(&text('chmod_echmod', $!));
|
||||
}
|
||||
if (defined($uid)) {
|
||||
chown($uid, $gid, $_[0]) || &failure(&text('chmod_echown', $!));
|
||||
}
|
||||
}
|
||||
|
5
file/config
Normal file
@ -0,0 +1,5 @@
|
||||
hide_dot_files=0
|
||||
iconsize=0
|
||||
nocharset=0
|
||||
extract=1
|
||||
force_text=0
|
16
file/config-*-linux
Normal file
@ -0,0 +1,16 @@
|
||||
xfs_acl=&has_command("getfacl") && &has_command("setfacl")
|
||||
ext2_acl=&has_command("getfacl") && &has_command("setfacl")
|
||||
ext3_acl=&has_command("getfacl") && &has_command("setfacl")
|
||||
reiserfs_acl=&has_command("getfacl") && &has_command("setfacl")
|
||||
xfs_attr=&has_command("attr")
|
||||
ext2_attr=&has_command("attr")
|
||||
ext3_attr=&has_command("attr")
|
||||
ext2_ext=&has_command("lsattr") && &has_command("chattr")
|
||||
ext3_ext=&has_command("lsattr") && &has_command("chattr")
|
||||
getfacl=getfacl
|
||||
setfacl=setfacl --set-file=-
|
||||
hide_dot_files=0
|
||||
iconsize=0
|
||||
nocharset=0
|
||||
extract=1
|
||||
force_text=0
|
9
file/config-irix
Normal file
@ -0,0 +1,9 @@
|
||||
xfs_acl=&has_command("chacl")
|
||||
xfs_attr=&has_command("attr")
|
||||
getfacl=./irix-getfacl.pl
|
||||
setfacl=./irix-setfacl.pl
|
||||
hide_dot_files=0
|
||||
iconsize=0
|
||||
nocharset=0
|
||||
extract=1
|
||||
force_text=0
|
10
file/config-solaris
Normal file
@ -0,0 +1,10 @@
|
||||
ufs_acl=&has_command("getfacl") && &has_command("setfacl")
|
||||
nfs_acl=&has_command("getfacl") && &has_command("setfacl")
|
||||
lofs_acl=&has_command("getfacl") && &has_command("setfacl")
|
||||
getfacl=getfacl
|
||||
setfacl=setfacl -f -
|
||||
hide_dot_files=0
|
||||
iconsize=0
|
||||
nocharset=0
|
||||
extract=1
|
||||
force_text=0
|
8
file/config.info
Normal file
@ -0,0 +1,8 @@
|
||||
hide_dot_files=Show files starting with a dot?,1,0-Yes,1-No
|
||||
iconsize=Size of buttons in toolbar,1,1-Small,0-Large with labels
|
||||
nocharset=Attempt to use proper character set?,1,0-Yes,1-No
|
||||
extract=Extract .class files from JAR?,1,1-Yes,0-No
|
||||
width=Width for scaled images,3,Default (300 pixels)
|
||||
fixed=Font size for text,3,Default (12 points)
|
||||
small_fixed=Font size for buttons,3,Default (10 points)
|
||||
force_text=Editor for HTML files,1,1-Text editor,0-HTML editor
|
8
file/config.info.ca
Executable file
@ -0,0 +1,8 @@
|
||||
hide_dot_files=Mostra els fitxers que comencen amb un punt,1,0-S<>,1-No
|
||||
iconsize=Mida dels botons a la barra d'eines,1,1-Petita,0-Gran amb etiquetes
|
||||
nocharset=Intenta fer servir el joc de car<61>cters apropiat,1,0-S<>,1-No
|
||||
extract=Extreu els fitxers .class del JAR?,1,1-S<>,0-No
|
||||
width=Amplada de les imatges escalades,3,Per defecte (300 p<>xels)
|
||||
fixed=Mida de la tipografia del text,3,Per defecte (12 punts)
|
||||
small_fixed=Mida de la tipografia dels botons,3,Per defecte (10 punts)
|
||||
force_text=Editor de fitxers HTML,1,1-Editor de text,0-Editor HTML
|
4
file/config.info.de
Normal file
@ -0,0 +1,4 @@
|
||||
hide_dot_files=Zeige Dateien mit einem Punkt am Anfang?,1,0-Ja,1-Nein
|
||||
iconsize=Größe der Buttons in der Toolbar,1,1-Klein,0-Groß mit Bildunterschrift
|
||||
nocharset=Versuche passende Zeichensätze zu benutzen?,1,0-Ja,1-Nein
|
||||
extract=.class-Dateien aus dem JAR extrahieren?,1,1-Ja,0-Nein
|
4
file/config.info.es
Normal file
@ -0,0 +1,4 @@
|
||||
hide_dot_files=¿Mostrar archivos que comienzan con punto?,1,0-Sí,1-No
|
||||
iconsize=Tamaño de botones en la barra de herramientas,1,1-Pequeño,0-Grande con etiquetas
|
||||
nocharset=¿Intentar usar el juego de caracteres apropiado?,1,0-Sí,1-No
|
||||
extract=¿Extraer archivos .class del JAR?,1,1-Sí,0-No
|
6
file/config.info.fa
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
hide_dot_files=آيا پروندههايي که با يک نقطه آغاز ميشوند نمايش داده شوند؟,1,0-بله,1-خير
|
||||
iconsize=اندازه دگمهها در نوار ابزار,1,1-کوچک,0-بزرگ به همراه برچسبها
|
||||
nocharset=آيا ميخواهيد از مجموعه کاراکترهاي خاص استفاده کنيد؟,1,0-بله,1-خير
|
||||
extract=آيا پروندههاي .class از پروندههاي JAR استخراج شوند؟,1,1-بله,0-خير
|
||||
|
4
file/config.info.it
Executable file
@ -0,0 +1,4 @@
|
||||
hide_dot_files=Visualizza i file che iniziano con un punto?,1,0-Si,1-No
|
||||
iconsize=Dimensioni dei pulsanti nella barra degli strumenti,1,1-Piccoli,0-Grandi con etichette
|
||||
nocharset=Tenta di usare il set di caratteri appropriato?,1,0-Si,1-No
|
||||
extract=Estrarre i file .class dall'archivio JAR?,1,1-Si,0-No
|
4
file/config.info.tr
Normal file
@ -0,0 +1,4 @@
|
||||
hide_dot_files=Nokta ile ba<62>layan dosyalar g<>sterilsin mi?,1,0-Evet,1-Hay<61>r
|
||||
iconsize=Ara<72> <20>ubu<62>undaki butonlar<61>n boyutu,1,1-K<><4B><EFBFBD>k,0-Geni<6E> ve etiketli
|
||||
nocharset=Uygun karakter seti kullan<61>lmaya <20>al<61><6C><EFBFBD>ls<6C>n m<>?,1,0-Evet,1-Hay<61>r
|
||||
extract=JAR'daki .class dosyalar<61> a<><61>ls<6C>n m<>?,1,1-Evet,0-Hay<61>r
|
49
file/copy.cgi
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/local/bin/perl
|
||||
# copy.cgi
|
||||
# Copy some file or directory
|
||||
|
||||
require './file-lib.pl';
|
||||
$disallowed_buttons{'copy'} && &error($text{'ebutton'});
|
||||
&ReadParse();
|
||||
&webmin_log("copy", undef, $in{'from'}, \%in);
|
||||
print "Content-type: text/plain\n\n";
|
||||
if ($access{'ro'} || !&can_access($in{'from'})) {
|
||||
print &text('copy_efrom', $in{'from'}),"\n";
|
||||
exit;
|
||||
}
|
||||
if (!&can_access($in{'to'})) {
|
||||
print &text('copy_eto', $in{'to'}),"\n";
|
||||
exit;
|
||||
}
|
||||
if (-l &unmake_chroot($in{'from'})) {
|
||||
# Remake the link
|
||||
&switch_acl_uid_and_chroot();
|
||||
&lock_file($in{'to'});
|
||||
if (!symlink(readlink($in{'from'}), $in{'to'})) {
|
||||
print &text('copy_elink', $!),"\n";
|
||||
exit;
|
||||
}
|
||||
&unlock_file($in{'to'});
|
||||
$err = undef;
|
||||
$info = $in{'to'};
|
||||
}
|
||||
else {
|
||||
&switch_acl_uid();
|
||||
($ok, $err) = ©_source_dest(&unmake_chroot($in{'from'}), &unmake_chroot($in{'to'}));
|
||||
$err = undef if ($ok);
|
||||
$info = &unmake_chroot($in{'to'});
|
||||
}
|
||||
if ($err) {
|
||||
print $err,"\n";
|
||||
}
|
||||
else {
|
||||
print "\n";
|
||||
print &file_info_line($info),"\n";
|
||||
}
|
||||
|
||||
sub split_dir
|
||||
{
|
||||
$_[0] =~ /^(.*\/)([^\/]+)$/;
|
||||
return ($1, $2);
|
||||
}
|
||||
|
33
file/defaultacl
Normal file
@ -0,0 +1,33 @@
|
||||
noconfig=0
|
||||
user=0
|
||||
root=/
|
||||
follow=0
|
||||
umask=022
|
||||
log=0
|
||||
ro=0
|
||||
goto=1
|
||||
archive=1
|
||||
button_save=1
|
||||
button_edit=1
|
||||
button_info=1
|
||||
button_acl=1
|
||||
button_attr=1
|
||||
button_ext=1
|
||||
button_search=1
|
||||
button_delete=1
|
||||
button_new=1
|
||||
button_upload=1
|
||||
button_mkdir=1
|
||||
button_makelink=1
|
||||
button_rename=1
|
||||
button_sharing=1
|
||||
button_mount=1
|
||||
button_copy=1
|
||||
button_preview=1
|
||||
unarchive=1
|
||||
dostounix=1
|
||||
chroot=/
|
||||
noperms=0
|
||||
nousers=0
|
||||
filesystems=1
|
||||
contents=1
|
26
file/delete.cgi
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/local/bin/perl
|
||||
# delete.cgi
|
||||
# Delete some file or directory
|
||||
|
||||
require './file-lib.pl';
|
||||
$disallowed_buttons{'delete'} && &error($text{'ebutton'});
|
||||
&ReadParse();
|
||||
&webmin_log("delete", undef, $in{'file'}, \%in);
|
||||
print "Content-type: text/plain\n\n";
|
||||
if ($access{'ro'} || !&can_access($in{'file'})) {
|
||||
print &text('delete_eaccess', $in{'file'}),"\n";
|
||||
exit;
|
||||
}
|
||||
if (-r &unmake_chroot($in{'file'}) && !-d &unmake_chroot($in{'file'})) {
|
||||
&switch_acl_uid_and_chroot();
|
||||
$rv = unlink($in{'file'});
|
||||
if (!$rv) { print "$!\n"; }
|
||||
else { print "\n"; }
|
||||
}
|
||||
else {
|
||||
&switch_acl_uid();
|
||||
($ok, $err) = &unlink_file(&unmake_chroot($in{'file'}));
|
||||
if (!$ok) { print "$err\n"; }
|
||||
else { print "\n"; }
|
||||
}
|
||||
|
54
file/edit_html.cgi
Executable file
@ -0,0 +1,54 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show an HTML editor window
|
||||
|
||||
require './file-lib.pl';
|
||||
do '../ui-lib.pl';
|
||||
$disallowed_buttons{'edit'} && &error($text{'ebutton'});
|
||||
&ReadParse();
|
||||
&popup_header($in{'file'} ? $text{'html_title'} : $text{'html_title2'},
|
||||
undef, "onload='initEditor()'");
|
||||
|
||||
# Output HTMLarea init code
|
||||
print <<EOF;
|
||||
<script type="text/javascript">
|
||||
_editor_url = "$gconfig{'webprefix'}/$module_name/xinha/";
|
||||
_editor_lang = "en";
|
||||
</script>
|
||||
<script type="text/javascript" src="xinha/htmlarea.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var editor = null;
|
||||
function initEditor() {
|
||||
editor = new HTMLArea("body");
|
||||
editor.generate();
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
EOF
|
||||
|
||||
# Read the file
|
||||
&switch_acl_uid_and_chroot();
|
||||
$data = &read_file_contents($in{'file'});
|
||||
|
||||
# Output text area
|
||||
print &ui_form_start("save_html.cgi", "form-data");
|
||||
if ($in{'file'}) {
|
||||
# Editing existing file
|
||||
print &ui_hidden("file", $in{'file'}),"\n";
|
||||
$pc = 95;
|
||||
}
|
||||
else {
|
||||
# Creating new, so prompt for path
|
||||
print $text{'edit_filename'}," ",
|
||||
&ui_textbox("file", $in{'dir'}, 70),"<br>\n";
|
||||
$pc = 90;
|
||||
}
|
||||
print "<textarea rows=20 cols=80 style='width:100%;height:$pc%' name=body id=body>";
|
||||
print &html_escape($data);
|
||||
print "</textarea>\n";
|
||||
print &ui_submit($text{'html_save'});
|
||||
print &ui_form_end();
|
||||
|
||||
&popup_footer();
|
||||
|
||||
|
21
file/extract.cgi
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Extract a zip, tar, tar.gz or tar.bz file on the server
|
||||
|
||||
require './file-lib.pl';
|
||||
&ReadParse();
|
||||
print "Content-type: text/plain\n\n";
|
||||
|
||||
# Check permissions
|
||||
$disallowed_buttons{'upload'} && &error($text{'ebutton'});
|
||||
if (!&can_access($in{'file'})) {
|
||||
print &text('extract_eperm', $in{'file'}),"\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
# Go for it
|
||||
&webmin_log("extract", undef, $in{'file'});
|
||||
$realfile = &unmake_chroot($in{'file'});
|
||||
&switch_acl_uid();
|
||||
$err = &extract_archive($in{'file'}, $in{'delete'});
|
||||
print $err,"\n";
|
||||
|
452
file/file-lib.pl
Normal file
@ -0,0 +1,452 @@
|
||||
# file-lib.pl
|
||||
# Common functions for file manager CGIs
|
||||
|
||||
do '../web-lib.pl';
|
||||
&ReadParse(\%prein, 'GET');
|
||||
if ($prein{'trust'}) {
|
||||
&open_trust_db();
|
||||
if ($trustdb{$prein{'trust'}}) {
|
||||
$trust_unknown_referers = 1;
|
||||
$trustdb{$prein{'trust'}} = time();
|
||||
}
|
||||
dbmclose(%trustdb);
|
||||
}
|
||||
&init_config();
|
||||
do '../ui-lib.pl';
|
||||
|
||||
@file_buttons = ( "save", "preview", "edit", "info", "acl", "attr", "ext", "search",
|
||||
"delete", "new", "upload", "mkdir", "makelink",
|
||||
"rename", "sharing", "mount", "copy" );
|
||||
|
||||
if ($module_info{'usermin'}) {
|
||||
# Usermin gets the allowed list from the module config
|
||||
&switch_to_remote_user();
|
||||
&create_user_config_dirs();
|
||||
$hide_dot_files = $userconfig{'hide_dot_files'};
|
||||
$follow = int($config{'follow'});
|
||||
$real_home_dir = &simplify_path(&resolve_links($remote_user_info[7]));
|
||||
$upload_max = $config{'max'};
|
||||
|
||||
if ($config{'home_only'} == 1) {
|
||||
@allowed_roots = ( $real_home_dir,
|
||||
split(/\s+/, $config{'root'}) );
|
||||
}
|
||||
elsif ($config{'home_only'} == 2) {
|
||||
@allowed_roots = split(/\s+/, $config{'root'});
|
||||
}
|
||||
else {
|
||||
@allowed_roots = ( "/" );
|
||||
}
|
||||
@denied_roots = split(/\s+/, $config{'noroot'});
|
||||
|
||||
if ($config{'archive'} eq 'y') {
|
||||
$archive = 1;
|
||||
}
|
||||
elsif ($config{'archive'} eq 'n') {
|
||||
$archive = 0;
|
||||
}
|
||||
else {
|
||||
$archive = 2;
|
||||
$archmax = $config{'archive'};
|
||||
}
|
||||
$unarchive = 1;
|
||||
$dostounix = 1;
|
||||
$chroot = "/";
|
||||
|
||||
@disallowed_buttons = ( );
|
||||
foreach $k (keys %config) {
|
||||
if ($k =~ /^button_(.*)/ && $config{$k} == 0) {
|
||||
push(@disallowed_buttons, $1);
|
||||
}
|
||||
}
|
||||
$canperms = 1;
|
||||
$canusers = 1;
|
||||
$contents = 1;
|
||||
}
|
||||
else {
|
||||
# Webmin gets the list of allowed directories from the ACL
|
||||
%access = &get_module_acl();
|
||||
$hide_dot_files = $config{'hide_dot_files'};
|
||||
$follow = int($access{'follow'});
|
||||
$upload_max = $access{'max'};
|
||||
|
||||
@allowed_roots = split(/\s+/, $access{'root'});
|
||||
if ($access{'home'}) {
|
||||
local @u = getpwnam($remote_user);
|
||||
if (@u) {
|
||||
push(@allowed_roots,
|
||||
&simplify_path(&resolve_links($u[7])));
|
||||
}
|
||||
}
|
||||
@denied_roots = split(/\s+/, $access{'noroot'});
|
||||
|
||||
$archive = $access{'archive'};
|
||||
$archmax = $access{'archmax'};
|
||||
$unarchive = $access{'unarchive'};
|
||||
$dostounix = $access{'dostounix'};
|
||||
$chroot = $access{'chroot'};
|
||||
$access{'button_search'} = 0 if (!&has_command("find"));
|
||||
$access{'button_makelink'} = 0 if (!&supports_symlinks());
|
||||
$access{'button_info'} = 0 if (!&supports_users());
|
||||
|
||||
@disallowed_buttons = grep { !$access{'button_'.$_} } @file_buttons;
|
||||
if (&is_readonly_mode()) {
|
||||
# Force read-only mode for file manager if global readonly
|
||||
# is in effect.
|
||||
$access{'ro'} = 1;
|
||||
}
|
||||
$canperms = $access{'noperms'} ? 0 : 1;
|
||||
$canusers = $access{'nousers'} ? 0 : 1;
|
||||
$contents = $access{'contents'};
|
||||
}
|
||||
%disallowed_buttons = map { $_, 1 } @disallowed_buttons;
|
||||
|
||||
$icon_map = ( "c", 1, "txt", 1,
|
||||
"pl", 1, "cgi", 1,
|
||||
"html", 1, "htm", 1,
|
||||
"gif", 2, "jpg", 2,
|
||||
"tar", 3
|
||||
);
|
||||
|
||||
# file_info_line(path, [displaypath])
|
||||
# Returns a line of text containing encoded details of some file
|
||||
sub file_info_line
|
||||
{
|
||||
local @st;
|
||||
local $islink = (-l $_[0]);
|
||||
local $f = $islink && &must_follow($_[0]);
|
||||
local @st = $f ? stat($_[0]) : lstat($_[0]);
|
||||
local $ext = $_[0] =~ /\S+\.([^\.\/]+)$/ ? $1 : undef;
|
||||
local $dp = $_[1] || $_[0];
|
||||
$dp =~ s/\\/\\\\/g;
|
||||
$dp =~ s/\t/\\t/g;
|
||||
return undef if ($dp =~ /\r|\n/);
|
||||
if (!@st) {
|
||||
# Work around a broken stat function on large files on redhat 7.x
|
||||
&has_command("stat") || return undef;
|
||||
local $out = `stat -t '$_[0]'`;
|
||||
return undef if ($?);
|
||||
$out =~ /^(.*)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/;
|
||||
local $type = defined($icon_map{$ext}) ? $icon_map{$ext} : 4;
|
||||
local $user = defined(%uid_to_user) ? $uid_to_user{$5} : getpwuid($5);
|
||||
$user = $5 if (!$user);
|
||||
local $group = defined(%gid_to_group) ? $gid_to_group{$6} :getgrgid($6);
|
||||
$group = $6 if (!$group);
|
||||
local $size = $2;
|
||||
local $mtime = $13;
|
||||
local $mode = hex($4);
|
||||
return sprintf ("%s\t%u\t%s\t%s\t%u\t%u\t%u\t%s",
|
||||
$dp, $type, $user, $group, $size, $mode, $mtime, undef);
|
||||
}
|
||||
local $type = $islink && !$f ? 5 :
|
||||
-d _ ? 0 :
|
||||
-b _ ? 6 :
|
||||
-c _ ? 6 :
|
||||
-p _ ? 7 :
|
||||
-S _ ? 7 : defined($icon_map{$ext}) ? $icon_map{$ext} : 4;
|
||||
local $user = !&supports_users() ? "root" :
|
||||
defined(%uid_to_user) ? $uid_to_user{$st[4]} : getpwuid($st[4]);
|
||||
$user = $st[4] if (!$user);
|
||||
local $group = !&supports_users() ? "root" :
|
||||
defined(%gid_to_group) ? $gid_to_group{$st[5]} :getgrgid($st[5]);
|
||||
$group = $st[5] if (!$group);
|
||||
local $rl = readlink($_[0]);
|
||||
return join("\t", $dp, $type,
|
||||
$user, $group,
|
||||
$st[7] < 0 ? 2**32+$st[7] : $st[7], $st[2],
|
||||
$st[9], $f ? "" : $islink && !$rl ? "???" : $rl);
|
||||
}
|
||||
|
||||
# switch_acl_uid()
|
||||
sub switch_acl_uid
|
||||
{
|
||||
if (!$module_info{'usermin'} && $access{'uid'}) {
|
||||
local @u = $access{'uid'} < 0 ? getpwnam($remote_user)
|
||||
: getpwuid($access{'uid'});
|
||||
@u || &error($text{'switch_euser'});
|
||||
$( = $u[3]; $) = "$u[3] ".join(" ", $u[3], &other_groups($u[0]));
|
||||
($>, $<) = ($u[2], $u[2]);
|
||||
umask(oct($access{'umask'}));
|
||||
}
|
||||
}
|
||||
|
||||
# switch_acl_uid_and_chroot()
|
||||
# Combines the switch_acl_uid and go_chroot functions
|
||||
sub switch_acl_uid_and_chroot
|
||||
{
|
||||
if (!$module_info{'usermin'} && $access{'uid'}) {
|
||||
local @u = $access{'uid'} < 0 ? getpwnam($remote_user)
|
||||
: getpwuid($access{'uid'});
|
||||
@u || &error($text{'switch_euser'});
|
||||
local @other = &other_groups($u[0]);
|
||||
&go_chroot();
|
||||
$( = $u[3]; $) = "$u[3] ".join(" ", $u[3], @other);
|
||||
($>, $<) = ($u[2], $u[2]);
|
||||
umask(oct($access{'umask'}));
|
||||
}
|
||||
else {
|
||||
&go_chroot();
|
||||
}
|
||||
}
|
||||
|
||||
# can_access(file)
|
||||
# Returns 1 if some file can be edited/deleted
|
||||
sub can_access
|
||||
{
|
||||
return &under_root_dir($_[0], \@allowed_roots) &&
|
||||
($_[0] eq "/" || !&under_root_dir($_[0], \@denied_roots));
|
||||
}
|
||||
|
||||
# under_root_dir(file, &roots)
|
||||
# Returns 1 if some file is under one of the given roots
|
||||
sub under_root_dir
|
||||
{
|
||||
local @f = grep { $_ ne '' } split(/\//, $_[0]);
|
||||
local $r;
|
||||
DIR: foreach $r (@{$_[1]}) {
|
||||
return 1 if ($r eq '/' || $_[0] eq '/' || $_[0] eq $r);
|
||||
local @a = grep { $_ ne '' } split(/\//, $r);
|
||||
local $i;
|
||||
for($i=0; $i<@a; $i++) {
|
||||
next DIR if ($a[$i] ne $f[$i]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# can_list(dir)
|
||||
# Returns 1 if some directory can be listed. Parent directories of allowed
|
||||
# directories are included as well.
|
||||
sub can_list
|
||||
{
|
||||
return &under_root_dir_or_parent($_[0], \@allowed_roots) &&
|
||||
($_[0] eq "/" || !&under_root_dir($_[0], \@denied_roots));
|
||||
}
|
||||
|
||||
# under_root_dir_or_parent(file, &roots)
|
||||
# Returns 1 if some file is under one of the given roots, or their parents
|
||||
sub under_root_dir_or_parent
|
||||
{
|
||||
local @f = grep { $_ ne '' } split(/\//, $_[0]);
|
||||
DIR: foreach $r (@allowed_roots) {
|
||||
return 1 if ($r eq '/' || $_[0] eq '/' || $_[0] eq $r);
|
||||
local @a = grep { $_ ne '' } split(/\//, $r);
|
||||
local $i;
|
||||
for($i=0; $i<@a && $i<@f; $i++) {
|
||||
next DIR if ($a[$i] ne $f[$i]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# accessible_subdir(dir)
|
||||
# Returns the path to a dir under the given one that we can access
|
||||
sub accessible_subdir
|
||||
{
|
||||
local ($r, @rv);
|
||||
foreach $r (@allowed_roots) {
|
||||
if ($r =~ /^(\Q$_[0]\E\/[^\/]+)/) {
|
||||
push(@rv, $1);
|
||||
}
|
||||
}
|
||||
return @rv;
|
||||
}
|
||||
|
||||
sub open_trust_db
|
||||
{
|
||||
local $trust = "$ENV{'WEBMIN_CONFIG'}/file/trust";
|
||||
eval "use SDBM_File";
|
||||
dbmopen(%trustdb, $trust, 0700);
|
||||
eval { $trustdb{'1111111111'} = 'foo bar' };
|
||||
if ($@) {
|
||||
dbmclose(%trustdb);
|
||||
eval "use NDBM_File";
|
||||
dbmopen(%trustdb, $trust, 0700);
|
||||
}
|
||||
}
|
||||
|
||||
# must_follow(path)
|
||||
# For symlinks, returns 1 if a link should be follow, 0 if not
|
||||
sub must_follow
|
||||
{
|
||||
if ($follow == 1) {
|
||||
return 1;
|
||||
}
|
||||
elsif ($follow == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
local @s = stat($_[0]);
|
||||
local @l = lstat($_[0]);
|
||||
@st = ($s[4] == $l[4] ? @s : @l);
|
||||
return $s[4] == $l[4];
|
||||
}
|
||||
}
|
||||
|
||||
# extract_archive(path, delete)
|
||||
# Called by upload to extract some zip or tar.gz file. Returns undef if something
|
||||
# was actually done, an error message otherwise.
|
||||
sub extract_archive
|
||||
{
|
||||
local $out;
|
||||
$_[0] =~ /^(\S*\/)/ || return 0;
|
||||
local $dir = $1;
|
||||
local $qdir = quotemeta($dir);
|
||||
local $qpath = quotemeta($_[0]);
|
||||
if ($_[0] =~ /\.zip$/i) {
|
||||
# Extract zip file
|
||||
return &text('zip_ecmd', "unzip") if (!&has_command("unzip"));
|
||||
$out = `(cd $qdir; unzip -o $qpath) 2>&1 </dev/null`;
|
||||
if ($?) {
|
||||
return &text('zip_eunzip', $out);
|
||||
}
|
||||
}
|
||||
elsif ($_[0] =~ /\.tar$/i) {
|
||||
# Extract un-compressed tar file
|
||||
return &text('zip_ecmd', "tar") if (!&has_command("tar"));
|
||||
$out = `(cd $qdir; tar xf $qpath) 2>&1 </dev/null`;
|
||||
if ($?) {
|
||||
return &text('zip_euntar', $out);
|
||||
}
|
||||
}
|
||||
elsif ($_[0] =~ /\.(tar\.gz|tgz|tar\.bz|tbz|tar\.bz2|tbz2)$/i) {
|
||||
# Extract gzip or bzip2-compressed tar file
|
||||
local $zipper = $_[0] =~ /bz(2?)$/i ? "bunzip2"
|
||||
: "gunzip";
|
||||
return &text('zip_ecmd', "tar") if (!&has_command("tar"));
|
||||
return &text('zip_ecmd', $zipper) if (!&has_command($zipper));
|
||||
$out = `(cd $qdir; $zipper -c $qpath | tar xf -) 2>&1`;
|
||||
if ($?) {
|
||||
return &text('zip_euntar2', $out);
|
||||
}
|
||||
}
|
||||
elsif ($_[0] =~ /\.gz$/i) {
|
||||
# Uncompress gzipped file
|
||||
return &text('zip_ecmd', "gunzip") if (!&has_command("gunzip"));
|
||||
local $final = $_[0];
|
||||
$final =~ s/\.gz$//;
|
||||
local $qfinal = quotemeta($final);
|
||||
$out = `(cd $qdir; gunzip -c $qpath >$qfinal) 2>&1`;
|
||||
if ($?) {
|
||||
return &text('zip_euntar2', $out);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $text{'zip_ename'};
|
||||
}
|
||||
if ($_[1]) {
|
||||
unlink($_[0]);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# post_upload(path, dir, unzip)
|
||||
sub post_upload
|
||||
{
|
||||
local ($path, $dir, $zip) = @_;
|
||||
if ($unarchive == 2) {
|
||||
$zip = $path =~ /\.(zip|tgz|tar|tar\.gz)$/i ? 1 : 0;
|
||||
}
|
||||
elsif ($unarchive == 0) {
|
||||
$zip = 0;
|
||||
}
|
||||
local $refresh = $path;
|
||||
local $err;
|
||||
if ($zip) {
|
||||
$err = &extract_archive($path, $zip-1);
|
||||
if (!$err) {
|
||||
# Refresh whole dir
|
||||
$refresh = $in{'dir'};
|
||||
}
|
||||
}
|
||||
$info = &file_info_line(&unmake_chroot($refresh), $refresh);
|
||||
print "<script>\n";
|
||||
print "opener.document.FileManager.",
|
||||
"upload_notify(\"$refresh\", \"$info\");\n";
|
||||
if ($err) {
|
||||
$err =~ s/\r//g;
|
||||
$err =~ s/\n/\\n/g;
|
||||
print "opener.document.FileManager.","upload_error(\"",&text('zip_err', $err),"\");\n";
|
||||
}
|
||||
print "close();\n";
|
||||
print "</script>\n";
|
||||
}
|
||||
|
||||
sub go_chroot
|
||||
{
|
||||
if ($chroot ne "/" && $chroot ne "") {
|
||||
# First build hash of users and groups, which will not be accessible
|
||||
# after a chroot
|
||||
local (@u, @g);
|
||||
setpwent();
|
||||
while(@u = getpwent()) {
|
||||
$uid_to_user{$u[2]} = $u[0] if (!defined($uid_to_user{$u[2]}));
|
||||
$user_to_uid{$u[0]} = $u[2] if (!defined($user_to_uid{$u[0]}));
|
||||
}
|
||||
endpwent();
|
||||
setgrent();
|
||||
while(@g = getgrent()) {
|
||||
$gid_to_group{$g[2]} = $g[0] if(!defined($gid_to_group{$g[2]}));
|
||||
$group_to_gid{$g[0]} = $g[2] if(!defined($group_to_gid{$g[0]}));
|
||||
}
|
||||
endgrent();
|
||||
chroot($chroot) || die("chroot to $chroot failed");
|
||||
}
|
||||
}
|
||||
|
||||
# make_chroot(dir)
|
||||
# Converts some real directory to the chroot form
|
||||
sub make_chroot
|
||||
{
|
||||
if ($chroot eq "/") {
|
||||
return $_[0];
|
||||
}
|
||||
elsif ($_[0] eq $chroot) {
|
||||
return "/";
|
||||
}
|
||||
else {
|
||||
local $rv = $_[0];
|
||||
if ($rv =~ /^$chroot\//) {
|
||||
$rv =~ s/^$chroot//;
|
||||
return $rv;
|
||||
}
|
||||
else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# unmake_chroot(dir)
|
||||
# Converts some chroot'd directory to the real form
|
||||
sub unmake_chroot
|
||||
{
|
||||
if ($chroot eq "/") {
|
||||
return $_[0];
|
||||
}
|
||||
elsif ($_[0] eq "/") {
|
||||
return $chroot;
|
||||
}
|
||||
else {
|
||||
return $chroot.$_[0];
|
||||
}
|
||||
}
|
||||
|
||||
# print_content_type()
|
||||
# Prints the content-type header, with a charset
|
||||
sub print_content_type
|
||||
{
|
||||
if ($userconfig{'nocharset'} || $config{'nocharset'}) {
|
||||
# Never try to use charset
|
||||
print "Content-type: text/plain\n\n";
|
||||
}
|
||||
else {
|
||||
$charset = &get_charset();
|
||||
print "Content-type: text/plain; charset=$charset\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
64
file/filesystems.cgi
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/local/bin/perl
|
||||
# filesystems.cgi
|
||||
# List all filesystems and their types
|
||||
|
||||
require './file-lib.pl';
|
||||
print "Content-type: text/plain\n\n";
|
||||
if (!&foreign_check("mount") || !$access{'filesystems'}) {
|
||||
print "0\n";
|
||||
exit;
|
||||
}
|
||||
&foreign_require("mount", "mount-lib.pl");
|
||||
@mtab = &mount::list_mounted();
|
||||
%mtab = map { $_->[0], $_ } @mtab;
|
||||
@fstab = &mount::list_mounts();
|
||||
%fstab = map { $_->[0], $_ } @fstab;
|
||||
@mounts = ( @fstab, grep { !$fstab{$_->[0]} } @mtab );
|
||||
|
||||
print "1\n";
|
||||
foreach $m (sort { length($a->[0]) <=> length($b->[0]) } @mounts) {
|
||||
next if ($m->[0] !~ /^\//);
|
||||
local @supp = @{$support{$m->[2]}};
|
||||
if (!@supp) {
|
||||
# Work out what this filesystem supports
|
||||
@supp = ( eval $config{$m->[2]."_acl"} ? 1 : 0,
|
||||
eval $config{$m->[2]."_attr"} ? 1 : 0,
|
||||
eval $config{$m->[2]."_ext"} ? 1 : 0 );
|
||||
$support{$m->[2]} = \@supp;
|
||||
}
|
||||
|
||||
# Check if the filesystem really does support attrs and ACLs
|
||||
local @supp2 = @supp;
|
||||
if ($mtab{$m->[0]}) {
|
||||
if ($supp2[0]) {
|
||||
local $out = `$config{'getfacl'} '$m->[0]' 2>/dev/null`;
|
||||
if ($?) {
|
||||
$supp2[0] = 0;
|
||||
}
|
||||
else {
|
||||
local $aclcount;
|
||||
foreach $l (split(/\n/, $out)) {
|
||||
$l =~ s/#.*$//;
|
||||
$l =~ s/\s+$//;
|
||||
$aclcount++ if ($l =~ /\S/);
|
||||
}
|
||||
$supp2[0] = 0 if (!$aclcount);
|
||||
}
|
||||
}
|
||||
if ($supp2[1]) {
|
||||
local $out = `attr -l '$m->[0]' 2>/dev/null`;
|
||||
if ($?) {
|
||||
$supp2[1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$m->[1] =~ s/\\/\//g;
|
||||
$chrooted = &make_chroot($m->[0]);
|
||||
if ($chrooted) {
|
||||
print join(" ", $chrooted, @$m[1..3], @supp2,
|
||||
$mtab{$m->[0]} ? 1 : 0,
|
||||
$fstab{$m->[0]} ? 1 : 0),"\n";
|
||||
}
|
||||
}
|
||||
|
36
file/getattrs.cgi
Executable file
@ -0,0 +1,36 @@
|
||||
#!/usr/local/bin/perl
|
||||
# getattrs.cgi
|
||||
# Returns a list in URL-encode name=value format of attributes on some file
|
||||
|
||||
require './file-lib.pl';
|
||||
&ReadParse();
|
||||
&switch_acl_uid_and_chroot();
|
||||
print "Content-type: text/plain\n\n";
|
||||
if (!&can_access($in{'file'})) {
|
||||
print $text{'facl_eaccess'},"\n";
|
||||
}
|
||||
else {
|
||||
$out = `attr -l '$in{'file'}' 2>&1`;
|
||||
if ($?) {
|
||||
print $out,"\n";
|
||||
}
|
||||
else {
|
||||
foreach $l (split(/[\r\n]+/, $out)) {
|
||||
if ($l =~ /Attribute\s+"(.*)"/i) {
|
||||
# Get the valid for this attribute
|
||||
local $name = $1;
|
||||
$got = `attr -g '$name' '$in{'file'}' 2>&1`;
|
||||
if ($? || $got !~ /^(.*)\n([\0-\377]*)\n$/) {
|
||||
print $got,"\n";
|
||||
exit;
|
||||
}
|
||||
push(@rv, [ $name, $2 ] );
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
foreach $r (@rv) {
|
||||
print &urlize($r->[0]),"=",&urlize($r->[1]),"\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
file/getext.cgi
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/local/bin/perl
|
||||
# getext.cgi
|
||||
# Returns a string of EXT attributes for some file
|
||||
|
||||
require './file-lib.pl';
|
||||
&ReadParse();
|
||||
&switch_acl_uid_and_chroot();
|
||||
print "Content-type: text/plain\n\n";
|
||||
if (!&can_access($in{'file'})) {
|
||||
print $text{'facl_eaccess'},"\n";
|
||||
}
|
||||
else {
|
||||
$out = `lsattr -d '$in{'file'}' 2>&1`;
|
||||
$out =~ s/^lsattr.*\n//;
|
||||
if ($? || $out !~ /^(\S+)\s/) {
|
||||
print $out,"\n";
|
||||
}
|
||||
else {
|
||||
print "\n";
|
||||
@a = split(//, $1);
|
||||
print join("", grep { $_ ne '-' } @a),"\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
24
file/getext.cgi.bak
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/local/bin/perl
|
||||
# getext.cgi
|
||||
# Returns a string of EXT attributes for some file
|
||||
|
||||
require './file-lib.pl';
|
||||
&ReadParse();
|
||||
&switch_acl_uid();
|
||||
print "Content-type: text/plain\n\n";
|
||||
if (!&can_access($in{'file'})) {
|
||||
print $text{'facl_eaccess'},"\n";
|
||||
}
|
||||
else {
|
||||
$out = `lsattr -d '$in{'file'}' 2>&1`;
|
||||
if ($? || $out !~ /^(\S+)\s/) {
|
||||
print $out,"\n";
|
||||
}
|
||||
else {
|
||||
print "\n";
|
||||
@a = split(//, $1);
|
||||
print join("", grep { $_ ne '-' } @a),"\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
45
file/getfacl.cgi
Executable file
@ -0,0 +1,45 @@
|
||||
#!/usr/local/bin/perl
|
||||
# getfacl.cgi
|
||||
# Gets the ACLs for some file
|
||||
|
||||
require './file-lib.pl';
|
||||
&ReadParse();
|
||||
&switch_acl_uid_and_chroot();
|
||||
print "Content-type: text/plain\n\n";
|
||||
if (!&can_access($in{'file'})) {
|
||||
print $text{'facl_eaccess'},"\n";
|
||||
}
|
||||
else {
|
||||
$getfacl = $config{'getfacl'};
|
||||
if ($getfacl =~ /^\.\//) {
|
||||
$getfacl =~ s/^\./$module_root_directory/;
|
||||
}
|
||||
chdir("/");
|
||||
if ($in{'file'} eq '/') {
|
||||
$in{'file'} = '.';
|
||||
}
|
||||
else {
|
||||
$in{'file'} =~ s/^\///;
|
||||
}
|
||||
$out = `$getfacl '$in{'file'}' 2>&1`;
|
||||
if ($?) {
|
||||
print $out,"\n";
|
||||
}
|
||||
else {
|
||||
foreach $l (split(/\n/, $out)) {
|
||||
$l =~ s/#.*$//;
|
||||
$l =~ s/\s+$//;
|
||||
push(@rv, $l) if ($l =~ /\S/);
|
||||
}
|
||||
if (!@rv) {
|
||||
print "Filesystem does not support ACLs\n";
|
||||
}
|
||||
else {
|
||||
print "\n";
|
||||
foreach $l (@rv) {
|
||||
print $l,"\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
file/images/.xvpics/preview.gif
Normal file
BIN
file/images/Thumbs.db
Executable file
BIN
file/images/acl.gif
Normal file
After Width: | Height: | Size: 193 B |
BIN
file/images/add.gif
Normal file
After Width: | Height: | Size: 118 B |
BIN
file/images/all.gif
Normal file
After Width: | Height: | Size: 155 B |
BIN
file/images/attr.gif
Normal file
After Width: | Height: | Size: 180 B |
BIN
file/images/binary.gif
Normal file
After Width: | Height: | Size: 97 B |
BIN
file/images/cancel.gif
Normal file
After Width: | Height: | Size: 107 B |
BIN
file/images/config.gif
Normal file
After Width: | Height: | Size: 167 B |
BIN
file/images/copy.gif
Normal file
After Width: | Height: | Size: 113 B |
BIN
file/images/cut.gif
Normal file
After Width: | Height: | Size: 119 B |
BIN
file/images/delete.gif
Normal file
After Width: | Height: | Size: 99 B |
BIN
file/images/device.gif
Normal file
After Width: | Height: | Size: 90 B |
BIN
file/images/dir.gif
Normal file
After Width: | Height: | Size: 88 B |
BIN
file/images/down.gif
Normal file
After Width: | Height: | Size: 128 B |
BIN
file/images/edit.gif
Normal file
After Width: | Height: | Size: 138 B |
BIN
file/images/ext.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
file/images/extract.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
file/images/file.gif
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
file/images/find.gif
Normal file
After Width: | Height: | Size: 94 B |
BIN
file/images/goto.gif
Normal file
After Width: | Height: | Size: 93 B |
BIN
file/images/html.gif
Executable file
After Width: | Height: | Size: 1.3 KiB |
BIN
file/images/icon.gif
Normal file
After Width: | Height: | Size: 266 B |
BIN
file/images/image.gif
Normal file
After Width: | Height: | Size: 124 B |
BIN
file/images/makelink.gif
Normal file
After Width: | Height: | Size: 127 B |
BIN
file/images/mdir.gif
Normal file
After Width: | Height: | Size: 169 B |
BIN
file/images/mkdir.gif
Normal file
After Width: | Height: | Size: 106 B |
BIN
file/images/mount.gif
Normal file
After Width: | Height: | Size: 221 B |
BIN
file/images/new.gif
Normal file
After Width: | Height: | Size: 85 B |
BIN
file/images/open.gif
Normal file
After Width: | Height: | Size: 114 B |
BIN
file/images/paste.gif
Normal file
After Width: | Height: | Size: 152 B |
BIN
file/images/pipe.gif
Normal file
After Width: | Height: | Size: 107 B |
BIN
file/images/preview.gif
Normal file
After Width: | Height: | Size: 412 B |
BIN
file/images/props.gif
Normal file
After Width: | Height: | Size: 117 B |
BIN
file/images/refresh.gif
Normal file
After Width: | Height: | Size: 92 B |
BIN
file/images/rename.gif
Normal file
After Width: | Height: | Size: 144 B |
BIN
file/images/replace.gif
Normal file
After Width: | Height: | Size: 155 B |
BIN
file/images/ret.gif
Normal file
After Width: | Height: | Size: 229 B |
BIN
file/images/run.gif
Normal file
After Width: | Height: | Size: 130 B |
BIN
file/images/save.gif
Normal file
After Width: | Height: | Size: 92 B |
BIN
file/images/sdir.gif
Normal file
After Width: | Height: | Size: 180 B |
BIN
file/images/search.gif
Normal file
After Width: | Height: | Size: 131 B |
BIN
file/images/share.gif
Normal file
After Width: | Height: | Size: 132 B |
BIN
file/images/smallicon.gif
Normal file
After Width: | Height: | Size: 194 B |
BIN
file/images/smdir.gif
Normal file
After Width: | Height: | Size: 181 B |
BIN
file/images/sub.gif
Normal file
After Width: | Height: | Size: 96 B |
BIN
file/images/sudir.gif
Normal file
After Width: | Height: | Size: 186 B |
BIN
file/images/symlink.gif
Normal file
After Width: | Height: | Size: 87 B |
BIN
file/images/text.gif
Normal file
After Width: | Height: | Size: 91 B |
BIN
file/images/udir.gif
Normal file
After Width: | Height: | Size: 171 B |