tkdesk-2.0/0040755000175000007640000000000010037657327010742 5ustar jccjcctkdesk-2.0/blt/0040755000175000007640000000000010037657326011522 5ustar jccjcctkdesk-2.0/blt/man/0040755000175000007640000000000010037657326012275 5ustar jccjcctkdesk-2.0/blt/man/bgexec.man0100644000175000007640000002662310020457430014217 0ustar jccjcc'\" '\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies. '\" '\" Permission to use, copy, modify, and distribute this software and its '\" documentation for any purpose and without fee is hereby granted, provided '\" that the above copyright notice appear in all copies and that both that the '\" copyright notice and warranty disclaimer appear in supporting documentation, '\" and that the names of Lucent Technologies any of their entities not be used '\" in advertising or publicity pertaining to distribution of the software '\" without specific, written prior permission. '\" '\" Lucent Technologies disclaims all warranties with regard to this software, '\" including all implied warranties of merchantability and fitness. In no event '\" shall Lucent Technologies be liable for any special, indirect or '\" consequential damages or any damages whatsoever resulting from loss of use, '\" data or profits, whether in an action of contract, negligence or other '\" tortuous action, arising out of or in connection with the use or performance '\" of this software. '\" '\" Bgexec command created by George Howlett. '\" .so man.macros .HS bgexec BLT .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME bgexec \- Run Unix commands in the background while handling Tk events. .SH SYNOPSIS \fBbgexec \fIvarName\fR ?\fIoption value\fR?... \fIcommand\fR ?\fIarg\fR?... .BE .SH DESCRIPTION .PP The \fBbgexec\fR command executes Unix commands in the background, allowing Tk to handle events. A global Tcl variable \fIvarName\fR is set when the command has completed. .SH INTRODUCTION Tcl's \fBexec\fR command is very useful for gathering information from the Unix system. It runs a Unix command and returns the output of the command as its result. This works well for Tcl-only applications. But in Tk applications, a problem occurs when a Unix command takes time to process. For example, let's say we want the get the disk usage of a directory. We'll use the Unix command \f(CWdu\fR to get the summary. .DS set out [exec du -s $dir] puts "Disk usage for $dir is $out" .DE While \f(CWdu\fR is running, scrollbars won't respond. None of the Tk widgets will be redrawn properly. The \fBsend\fR command won't work. And the worst part is that the application appears hung up or dead. The problem is that while the application is waiting for \fIdu\fR to finish, Tk is not handling X events. .PP The \fBbgexec\fR command performs the same functions as \fBexec\fR, but also allows Tk to handle events. You can execute a long-running Unix command and the Tk widgets will behave normally. When the command finishes, its output and the exit status are written to Tcl variables. This makes it easy to monitor and save the output of a command. .SH EXAMPLE This is the disk usage example again, this time using \fBbgexec\fR. The Unix command syntax is exactly the same as the previous example, when we used \fBexec\fR. .DS global myStatus myOutput bgexec myStatus -output myOutput du -s $dir puts "Disk usage for $dir is $myOutput" .DE Two global variables, \f(CWmyStatus\fR and \f(CWmyOutput\fR, will be set by \fBbgexec\fR when the \f(CWdu\fR command has completed. \f(CWMyStatus\fR will contain the command's exit status. \f(CWMyOutput\fR, specified by the \fB\-output\fR option, will store the output of the command. .PP You can also terminate the command by setting the variable \f(CWmyStatus\fR. If \f(CWmyStatus\fR is set before \f(CWdu\fR has completed, the process is killed by sending a configurable Unix signal (by default it's SIGKILL). It makes no difference what \f(CWmyStatus\fR is set to. .DS set myStatus {} .DE There are other \fBbgexec\fR options to collect different types of information. .DS global myStatus myOutput myErrs bgexec myStatus -output myOutput -error myErrs du -s $dir .DE The \fB\-error\fR option is similar to \fB\-output\fR. It sets a global variable when the command completes. The variable will contain any data written to stderr by the command. .PP The \fB\-output\fR and \fB\-error\fR variables are written to only after the command completes. If the command takes a long time, you may want to receive its partial output. You can gather data as it becomes available using the \fB\-onoutput\fR option. It specifies a Tcl command prefix. Whenever new data is available, this command is executed, with the data appended as an argument to the command. .DS proc GetInfo { data } { puts $data } bgexec myStatus -onoutput GetInfo du -s $dir .DE When output is available, the procedure \f(CWGetInfo\fR is called. The \fB\f-onerror\fR option performs a similar function for the stderr data stream. .PP Like \fBexec\fR, \fBbgexec\fR returns an error if the exit code of the Unix command is not zero. If you think you may get a non-zero exit code, you might want to invoke \fBbgexec\fR from within a \fBcatch\fR. .DS catch { bgexec myStatus -output myOutput du -s $dir } .DE By default, \fBbgexec\fR will wait for the command to finish. But you can detach the command by adding an ampersand (&) to the end of the command line. .DS global myStatus myOutput bgexec myStatus -output myOutput du -s $dir & .DE \fBBgexec\fR will return immediately and its result will be a list of the spawned process ids. If at some point, you need to wait for the command to finish, you can use \fBtkwait\fR. When the command finishes, the variable \f(CWmyStatus\fR will be written to, breaking the \fBtkwait\fR loop. .DS global myStatus myOutput bgexec myStatus -output myOutput du -s $dir & ... tkwait variable myStatus .DE .SH SYNTAX The \fBbgexec\fR command takes the following form: .sp \fB bgexec \fIvarName\fR ?\fIoption value\fR?... \fIcommand\fR ?\fIarg\fR?... .sp \fIVarName\fR is the name of a global variable which is set when the designated Unix command has finished executing. The exit status of the command will be stored in \fIvarName\fR. The exit status is a list of a status token, the process-id of the command, the exit code, and a status message. You can also prematurely terminate the command by setting \fIvarName\fR. The command will be sent a signal to terminate it (by default the signal is a SIGKILL; see the \fB\-killsignal\fR option). .PP \fICommand\fR is the name of the Unix command to be executed and \fIargs\fR are any extra arguments for \fIcommand\fR. \fICommand\fR and \fIargs\fR may be in any form accepted by \fBexec\fR. (See the \fBexec\fR manual for further information.) If the last argument is an ampersand (&), the command will be run detached, and \fBbgexec\fR will return immediately. \fIVarName\fR will still be set with the return status when \fIcommand\fR completes. .SH OPTIONS \fIOption\fR is the switch name, always beginning with a dash (\-). \fIValue\fR is the value of the option. Option-value pairs are terminated either by the Unix command name, or double dashes (\-\-). The following options are available for \fBbgexec\fR: .TP \fB\-error \fIvarName\fR .br Specifies that a global variable \fIvarName\fR is to be set with the contents of stderr after the command has completed. .TP \fB\-keepnewline \fIboolean\fR Specifies that a trailing newline should be retained in the output. If \fIboolean\fR is true, the trailing newline is truncated from the output of the \fB\-onoutput\fR and \fB\-output\fR variables. The default value is \f(CWtrue\fR. .TP \fB\-killsignal \fIsignal\fR Specifies the signal to be sent to the Unix command when terminating. \fISignal\fR can either be a number (typically 1-32) or a mnemonic (e.g. SIGINT). If \fIsignal\fR is the empty string, then no signal is sent. The default signal is \f(CW9\fR (SIGKILL). .TP \fB\-lasterror \fIvarName\fR Specifies a variable \fIvarName\fR that is updated whenever data becomes available from standard error of the Unix command. \fIVarName\fR is a global variable. Unlike the \fB\-error\fR option, data is available as soon as it arrives. .TP \fB\-lastoutput \fIvarName\fR Specifies a variable \fIvarName\fR that is updated whenever data becomes available from standard output of the Unix command. \fIVarName\fR is a global variable. Unlike the \fB\-output\fR option, data is available as soon as it arrives. .TP \fB\-output \fIvarName\fR .br Specifies that a global variable \fIvarName\fR is to be set with the output of the command, after the commmand has completed. If this option is not set, no output will be accumulated. .TP \fB\-onerror \fIcommand\fR Specifies the start of a Tcl command that will be executed whenever new data is available from standard error. The data is appended to the command as an extra argument before it is executed. .TP \fB\-onoutput \fIcommand\fR Specifies the start of a Tcl command that will be executed whenever new data is available from standard output. The data is appended to the command as an extra argument before it is executed. .TP \fB\-update \fIvarName\fR Deprecated. This option is replaced by \fB\-onerror\fR. .TP \fB\-\|\-\fR This marks the end of the options. The following argument will be considered the name of a Unix command even if it starts with a dash (\fB\-\fR). .SH PREEMPTION Because Tk events are handled while a Unix command is running, it's possible for an application to preempt itself with further user-interactions. Let's say your application has a button, that when pressed runs the disk usage example. While the \f(CWdu\fR command is already running, the user may press the button again. The second \fBbgexec\fR command will preempt the first. This means that the first command can not finish until the second command has completed. .PP Care must be taken to prevent an application from preempting itself, by blocking further user-interactions (such as button clicks). The BLT \fBbusy\fR command is very useful in these situations, temporarily preventing user interaction. See the \fBbusy\fR manual for details. .SH DIFFERENCES WITH FILEEVENT Some of the functionality of \fBbgexec\fR is now be provided in Tk 4.0 with the \fBfileevent\fR command. The steps for running a command in the background are: .PP Execute the Unix command with the \fBopen\fR command (using the "|" syntax) and save the file handle. .DS global fileId set fileId [open "|du -s $dir" r] .DE Next register a Tcl code snippet with \fBfileevent\fR to be run whenever output is available on the file handle. The code snippet will read from the file handle and save the output in a variable. .DS fileevent fileId readable { if { [gets $fileId line] < 0 } { close $fileId set output $temp unset fileId temp } else { append temp $line } } .DE While this works with the above example, but there are some important differences. .PP The biggest advantage of \fBbgexec\fR is that it requires no additional Tcl code to run a Unix command. It's simpler, and therefore there's less chance of errors being introduced. .PP \fBBgexec\fR also handles things that \fBfileevent\fR can not. For example, you can't get back the exit status of the command. In the code above, we're assuming that the command has completed once stdout is closed. The problem is that some commands, like \f(CWcompress\fR, reopen stdout, which fool \fBfileevent\fR. We're also assuming that the Unix command will write its output line-by-line. Running another command, your application may block in the \fBgets\fR command, reading a partial line. Conversely, \fBbgexec\fR handles non-blocking I/O tranparently for you. Finally, since data collection is handled in C code, \fBbgexec\fR is faster, getting you back to the Tk event loop more quickly. .SH SEE ALSO busy, exec, tkwait .SH KEYWORDS exec, background, busy tkdesk-2.0/blt/man/busy.man0100644000175000007640000002341110020457430013734 0ustar jccjcc'\" '\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies. '\" '\" Permission to use, copy, modify, and distribute this software and its '\" documentation for any purpose and without fee is hereby granted, provided '\" that the above copyright notice appear in all copies and that both that the '\" copyright notice and warranty disclaimer appear in supporting documentation, '\" and that the names of Lucent Technologies any of their entities not be used '\" in advertising or publicity pertaining to distribution of the software '\" without specific, written prior permission. '\" '\" Lucent Technologies disclaims all warranties with regard to this software, '\" including all implied warranties of merchantability and fitness. In no event '\" shall Lucent Technologies be liable for any special, indirect or '\" consequential damages or any damages whatsoever resulting from loss of use, '\" data or profits, whether in an action of contract, negligence or other '\" tortuous action, arising out of or in connection with the use or performance '\" of this software. '\" '\" Busy command created by George Howlett. '\" .so man.macros .HS busy BLT .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME busy \- Make Tk widgets busy, temporarily blocking user interactions. .SH SYNOPSIS \fBbusy hold \fIwindow\fR ?\fIoption value\fR?... .sp \fBbusy release \fIwindow\fR ?\fIwindow\fR?... .sp \fBbusy configure \fIwindow\fR ?\fIoption value\fR?... .sp \fBbusy forget \fIwindow\fR ?\fIwindow\fR?... .sp \fBbusy isbusy \fR?\fIpattern\fR? .sp \fBbusy status \fIwindow\fR .sp \fBbusy windows \fR?\fIpattern\fR? .BE .SH DESCRIPTION .PP The \fBbusy\fR command provides a simple means to block keyboard, button, and pointer events from Tk widgets, while overriding the widget's cursor with a configurable busy cursor. .SH INTRODUCTION .PP There are many times in applications where you want to temporarily restrict what actions the user can take. For example, an application could have a "run" button that when pressed causes some processing to occur. But while the application is busy processing, you probably don't want the the user to be able to click the "run" button again. You may also want restrict the user from other tasks such as clicking a "print" button. .PP The \fBbusy\fR command lets you make Tk widgets busy. This means that user interactions such as button clicks, moving the mouse, typing at the keyboard, etc. are ignored by the widget. You can also set a special cursor (like a watch) which overrides the widget's normal cursor, providing feedback that the application (widget) is temporarily busy. .PP When a widget is made busy, all of the widgets in its window hierarchy also become busy. It's easy then to make an entire panel of widgets busy by simply making the uppermost widget (such as ".") busy. This is far easier and much more efficient than recursively traversing the widget hierarchy, disabling each widget and re-configuring its cursor. .PP The busy command can also be used in many cases instead of Tk's \fBgrab\fR command. Unlike \fBgrab\fR which directs all user interaction to one widget, the busy command allows more than one widget to be active (for example, a "cancel" dialog and a "help" button) while the other widgets are busy. .SH EXAMPLE You can make a set of widgets busy by simply making the uppermost widget in the hierarchy busy using the \fBhold\fR operation. .DS frame .top button .top.button; canvas .top.canvas pack .top.button .top.canvas pack .top . . . busy hold .top update .DE All the widgets within \f(CW.top\fR (including \f(CW.top\fR) are now busy. The \fBupdate\fR command insures that \fBbusy\fR command has a chance to effect. .PP When the application is no longer busy, you can allow user interaction again by the \fBrelease\fR operation. .nf \f(CW busy release .top \fR .fi You can change the busy cursor using the \fBconfigure\fR operation. .nf \f(CW busy configure .top -cursor "watch"\fR .fi Finally, when you no longer need to make the widget busy anymore, invoke the \fBforget\fR operation to free any resources allocated. .nf \f(CW busy forget .top \fR .fi Destroying the widget also cleans up any resources allocated by the busy command. .PP .SH OPERATIONS The following operations are available for the \fBbusy\fR command: .TP \fBbusy hold \fIwindow\fR ?\fIoption value\fR?... Makes the widget \fIwindow\fR (and its descendants in the Tk window hierarchy) busy. \fIWindow\fR must be a valid path name of a Tk widget. After idle tasks are processed, the widget will be blocked from user interactions. All device events in the widget window and its descendants will be ignored. Typically \fBupdate\fR should be called immediately afterward to insure that the \fBhold\fR operation is in effect \fIbefore\fR the application starts its processing. The following configuration options are valid: .RS .TP \fB\-cursor \fIcursorName\fR Specifies the cursor to be displayed when the widget is made busy. \fICursorName\fR can be in any form accepted by \fBTk_GetCursor\fR. The default cursor is \f(CWwatch\fR. .RE .TP \fBbusy configure \fIwindow\fR ?\fIoption value\fR?... Queries or modifies the \fBbusy\fR command configuration options for \fIwindow\fR. \fIWindow\fR must be the path name of a widget previously made busy by the \fBhold\fR operation. If no options are specified, a list describing all of the available options for \fIwindow\fR (see \fBTk_ConfigureInfo\fR for information on the format of this list) is returned. If \fIoption\fR is specified with no \fIvalue\fR, then the command returns a list describing the one named option (this list will be identical to the corresponding sublist of the value returned if no \fIoption\fR is specified). If one or more \fIoption\-value\fR pairs are specified, then the command modifies the given widget option(s) to have the given value(s); in this case the command returns the empty string. \fIOption\fR may have any of the values accepted by the \fBhold\fR operation. .sp Please note that the option database is referenced through \fIwindow\fR. For example, if the widget \f(CW.frame\fR is to be made busy, the busy cursor can be specified for it by either \fBoption\fR command: .nf \f(CWoption add *frame.busyCursor gumby\fR \f(CWoption add *Frame.BusyCursor gumby\fR .fi .TP \fBbusy forget \fIwindow\fR ?\fIwindow\fR?... Releases resources allocated by the busy command for \fIwindow\fR, including the InputOnly window. User events will again be received again by \fIwindow\fR. Busy resources are also released when \fIwindow\fR is destroyed. \fIWindow\fR must be the name of a widget specified in the \fBhold\fR operation, otherwise an error is reported. .TP \fBbusy isbusy \fR?\fIpattern\fR? Returns the pathnames of all widget windows which are currently busy. If a \fIpattern\fR is given, the path names of busy widgets matching \fIpattern\fR are returned. .TP \fBbusy release \fIwindow\fR ?\fIwindow\fR?... Restores user interactions to the widget \fIwindow\fR again. This differs from the \fBforget\fR operation in that the InputOnly window is not destroyed, but simply unmapped. \fIWindow\fR must be the name of a widget specified in a \fBhold\fR operation, otherwise an error is reported. .TP \fBbusy status \fIwindow\fR Returns the status of a widget \fIwindow\fR previously made busy. An error is reported if \fIwindow\fR was never made busy, or the \fBforget\fR operation was invoked (i.e. does not currently have a InputOnly window associated with it). If \fIwindow\fR is presently can not receive user interaction, \f(CW1\fR is returned, otherwise \f(CW0\fR. .TP \fBbusy windows \fR?\fIpattern\fR? Returns the pathnames of all widget windows which have previously been made busy (i.e. an InputOnly is allocated and associated with the widget). It makes no difference if the window is currently busy ot not. If a \fIpattern\fR is given, the path names of busy widgets matching \fIpattern\fR are returned. .sp 1 .SH BINDINGS The blocking feature is implemented by creating and mapping a transparent InputOnly class window which completely covers the widget. When the InputOnly window is mapped, it intercepts all events which could be sent to the widget and its hierarchy. Like Tk widgets, the InputOnly windows have names in the Tk window hierarchy. This means that you can use the \fBbind\fR command, to handle events in the InputOnly window. .DS busy hold .frame.canvas bind .frame.canvas_Busy { ... } .DE .PP Typically, the InputOnly window is a sibling of the widget's window. The name of the InputOnly window will be "\fIwidget\f(CW_Busy\fR" where \fIwidget\fR is the name of the widget made busy. In the above example, the pathname of the InputOnly window is "\f(CW.frame.canvas_Busy\fR" The exception is when the widget is a toplevel window (such as "."). Then the InputOnly window is a child of the widget's window and the name of the widget will be "\fIwidget\f(CW._Busy\fR" where \fIwidget\fR is the name of the widget made busy. In the following example, the pathname of the InputOnly window is "\f(CW._Busy\fR" .DS busy hold . bind ._Busy { ... } .DE .SH ENTER/LEAVE EVENTS Mapping and unmapping the InputOnly window generates Enter/Leave events for all widget (windows) that it covers. Please note this when you are tracking Enter/Leave events in widgets. .SH KEYBOARD EVENTS When a widget is made busy, the widget is prevented from gaining the keyboard focus by the InputOnly window. But Tk widgets can still get keyboard events if the keyboard focus is already set. Care must be taken to move focus to another window. .DS busy hold .frame label .dummy focus .dummy update .DE The above example moves the focus from .frame immediately after invoking the \fBhold\fR so that no keyboard events will be relayed to windows under the hierarchy of \f(CW.frame\fR. .SH KEYWORDS busy, keyboard events, pointer events, window, cursor tkdesk-2.0/blt/man/dragdrop.man0100644000175000007640000004266610020457430014571 0ustar jccjcc'\" '\" Copyright 1991-1997 by Bell Labs Innovations for Lucent Technologies. '\" '\" Permission to use, copy, modify, and distribute this software and its '\" documentation for any purpose and without fee is hereby granted, provided '\" that the above copyright notice appear in all copies and that both that the '\" copyright notice and warranty disclaimer appear in supporting documentation, '\" and that the names of Lucent Technologies any of their entities not be used '\" in advertising or publicity pertaining to distribution of the software '\" without specific, written prior permission. '\" '\" Lucent Technologies disclaims all warranties with regard to this software, '\" including all implied warranties of merchantability and fitness. In no event '\" shall Lucent Technologies be liable for any special, indirect or '\" consequential damages or any damages whatsoever resulting from loss of use, '\" data or profits, whether in an action of contract, negligence or other '\" tortuous action, arising out of or in connection with the use or performance '\" of this software. '\" '\" .so man.macros .HS drag&drop BLT .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME drag&drop \- facilities for handling drag&drop data transfers .SH SYNOPSIS \fBdrag&drop source .br \fBdrag&drop source \fIwindow \fR?\fIoptions\fR? .br \fBdrag&drop source \fIwindow \fBhandler \fR?\fIdataType\fR? ?\fIcommand arg arg...\fR? .sp \fBdrag&drop target .br \fBdrag&drop target \fIwindow \fBhandler \fR?\fIdataType command arg arg...\fR? .sp \fBdrag&drop target \fIwindow \fBhandle \fIdataType\fR ?\fIvalue\fR? .sp \fBdrag&drop token \fIwindow .sp \fBdrag&drop drag \fIwindow x y .br \fBdrag&drop drop \fIwindow x y .br \fBdrag&drop active .br \fBdrag&drop errors \fR?\fIproc\fR? .br \fBdrag&drop location \fR?\fIx y\fR? .BE .SH DESCRIPTION .PP The \fBdrag&drop\fR command provides access to a set of facilities for managing drag-and-drop data transfers. Any of the usual Tk widgets can be registered to participate in the drag-and-drop process. Widgets registered as a drag&drop \fIsource\fP can export data to other widgets registered as a drag&drop \fItarget\fP. Note that a particular widget can be registered as a source, as a target, or as both. .PP The drag-and-drop process begins when the user clicks and holds a mouse button in a source window; a token window appears with an icon or message to represent the data being transferred. As the user moves the mouse pointer, the token window follows along, acting as a movable packet of data. Whenever the mouse pointer falls on a valid target window, the border of the token window is changed to a raised (active) state. When the mouse button is released over the target window, a Tcl routine is invoked to send the data to the desired application, and the target window is asked to "handle" the data. If this communication process fails, a rejection symbol (a circle with a line through it) is displayed on the token window to indicate failure. .PP The details of the communication process are fully configurable by the application developer. In the simplest case, the value that is sent to the target window is a simple string. The target window is simply asked to "handle" that string value. In general, the source window can have a special "handler" procedure to transfer a particular data type by issuing a series of "send" commands. After this, the target window is again asked to "handle" the result. .PP Both sources and targets can have a list of "handlers" for different data types. As a token window is dragged from its source to various targets, each target is checked to see if it recognizes a handler offered by the source. If it does, it is treated as a valid target. Otherwise, it is ignored. This scheme allows the same source to interact with many different kinds of targets. For example, a source for RGB color samples might have "color" and "string" handlers. This would allow it to communicate with "color" targets (sending RGB data) as well as entry widgets (sending strings of the form "#rrggbb"). .PP This introduction was presented as a brief overview of the communication process; further details are presented below: .TP \fBdrag&drop source\fR Returns a list of path names for widgets registered as drag&drop sources. Returns an empty string if no widgets have been registered. .TP \fBdrag&drop source \fIwindow \fR?\fIoptions\fR? Registers a new drag&drop source window with the given options, or modifies the options for an existing window: .RS .LP .nf Name: \fBbuttonBinding\fR Class: \fBButtonBinding\fR Switch: \fB\-button\fR \fIn\fR .fi .IP Specifies the mouse button (integer 1-5) that will invoke the drag&drop operation on the source window. This causes the following bindings to be added to the widget: .sp .nf .RS \f(CWbind \fIwin\fP {drag&drop drag %W %X %Y} \f(CWbind \fIwin\fP {drag&drop drag %W %X %Y} \f(CWbind \fIwin\fP {drag&drop drop %W %X %Y}\fR .RE .fi .sp The default value is button 3. If the value "0" is specified, then no bindings are added; this enables the user to establish bindings manually. .LP .nf Name: \fBpackageCommand\fR Class: \fBCommand\fR Switch: \fB\-packagecmd \fIcommand\fR .fi .IP Specifies a Tcl command used to establish the appearance of the token window at the start of each drag&drop operation. This command is automatically invoked by the \fBdrag&drop drag\fP command whenever the token window is about to be mapped for a drag operation. It should update the appearance of the token window to represent the data that is being moved. .PP The following substitutions are made in the \fIcommand\fR string before it is executed: .RS .TP \fB%t\fR Replaced with the window path name for the token which represents the data being dragged. .TP \fB%W\fR Replaced with the window path name for the drag&drop source. .RE .LP The return value from the package command represents the data being transferred. If the package command returns an empty string, the drag operation is quietly aborted. This can be used to disallow drag&drop operations from certain parts of a widget, if the drag position is inappropriate. .LP For example, the following package routine will select an item from a listbox and configure the token window to display the selected string. It uses the \fBdrag&drop location\fR command to determine the entry in the listbox that the user has selected and it returns this as the data value: .sp .nf .RS \f(CWproc package_list_item {lbox token} { set xy [drag&drop location] set y [expr [lindex $xy 1]-[winfo rooty $lbox]] set str [$lbox get [$lbox nearest $y]] $token.value configure -text $str return $str }\fR .RE .fi .sp The return value is available later when the source and target communicate. If the source has a command associated with its data handler, then this value is substituted in place of "%v" in the source handler. Otherwise, it is substituted in place of "%v" in the target handler. .LP .nf Name: \fBrejectBackground\fR Class: \fBBackground\fR Switch: \fB\-rejectbg \fIcolor\fR .fi .IP Specifies the color used to draw the background of the rejection symbol on the token window. The rejection symbol (a circle with a line through it--the international "no") appears whenever communication fails. .LP .nf Name: \fBrejectForeground\fR Class: \fBForeground\fR Switch: \fB\-rejectfg \fIcolor\fR .fi .IP Specifies the color used to draw the foreground of the rejection symbol on the token window. .LP .nf Name: \fBrejectStipple\fR Class: \fBStipple\fR Switch: \fB\-rejectstipple \fIpattern\fR .fi .IP Specifies a stipple pattern used to draw the foreground of the rejection symbol on the token window. Any of the forms acceptable to Tk_GetBitmap can be used. .LP .nf Name: \fBselfTarget\fR Class: \fBSelfTarget\fR Switch: \fB\-selftarget \fIboolean\fR .fi .IP If the \fIboolean\fR value is true, and if a source widget is also registered as a compatible target, then the source will be able to transmit to itself during drag&drop operations. This is primarily useful for complex sources such as a canvas widget, where items may be moved from place to place within the same widget. By default, this option is disabled. .LP .nf Name: \fBsend\fR Class: \fBSend\fR Switch: \fB\-send \fIlist\fR .fi .IP Specifies a \fIlist\fR of \fIdataTypes\fR enabled for communication. Only \fIdataTypes\fR defined by commands of the form "\fBdrag&drop source \fIwindow \fBhandler \fR?\fIdataType\fR ?\fIcommand arg arg...\fR?" are allowed. This list also determines the priority of the various \fIdataTypes\fR. When a token window is over a potential drag&drop target, this list is searched from start to finish for a \fIdataType\fR that is also recognized by the target. The first matching \fIdataType\fR found determines the value that will be sent if the token is dropped. If no matching \fIdataType\fR is found, then the target is incompatible, and is ignored. By default, this option has the value "all", indicating that all \fIdataTypes\fR should be considered in the order that they were defined for the source. .LP Note that this option makes it easy to control a drag&drop source. Setting the value to an empty string disables the source; setting the value back to "all" restores communication. .LP .nf Name: \fBsiteCommand\fR Class: \fBCommand\fR Switch: \fB\-sitecmd \fIcommand\fR .fi .IP Specifies a Tcl command used to update the appearance of the token window. If specified, this command is automatically invoked by the \fBdrag&drop drag\fP command whenever the token window is over a compatible drag&drop target. .PP The following substitutions are made in the \fIcommand\fR string before it is executed: .RS .TP \fB%s\fR Replaced with "1" if the token window is over a compatible target, and "0" otherwise. .TP \fB%t\fR Replaced with the window path name for the token which represents the data being dragged. .RE .LP Regardless of this command, border of the token window will become raised whenever the token is over a valid target. This command can be used to display other visual cues. .LP .nf Name: \fBtokenAnchor\fR Class: \fBAnchor\fR Switch: \fB\-tokenanchor \fIanchor\fR .fi .IP Specifies how the token window is positioned relative to the mouse pointer coordinates passed to the \fBdrag&drop drag\fP command. Must be one of the values n, s, e, w, center, nw, ne, sw or se. For example, "nw" means to position the token such that its upper-left corner is at the mouse pointer. The default value is "center". .LP .nf Name: \fBtokenBackground\fR Class: \fBBackground\fR Switch: \fB\-tokenbg \fIcolor\fR .fi .IP Specifies the color used to draw the background of the token window. .LP .nf Name: \fBtokenBorderWidth\fR Class: \fBBorderWidth\fR Switch: \fB\-tokenborderwidth \fIsize\fR .fi .IP Specifies the width in pixels of the border around the token window. This border becomes raised to indicate when the token is over a compatible drag&drop target site. The value may have any of the forms acceptable to Tk_GetPixels. The default value is "3". .LP .nf Name: \fBtokenCursor\fR Class: \fBCursor\fR Switch: \fB\-tokencursor \fIcursor\fR .fi .IP Specifies the cursor used when a token window is active. The value may have any of the forms acceptable to Tk_GetCursor. The default value is "center_ptr". .RE .TP \fBdrag&drop source \fIwindow \fBhandler \fR?\fIdataType\fR? ?\fIcommand arg arg...\fR? With no extra arguments, this command returns a list of all \fIdataType\fR names that have been registered for the source \fIwindow\fR. If only the \fIdataType\fR is specified, then the \fIdataType\fR is created if necessary, and the command associated with the \fIdataType\fR is returned. Otherwise, it concatenates the \fIcommand\fR and any extra \fIarg\fR strings, and registers a new \fIdataType\fR with this command. .PP The following substitutions are made in the \fIcommand\fR string before it is executed: .RS .TP \fB%i\fR Replaced with the name of the interpreter for the target application. .TP \fB%v\fR Replaced with the value returned from the "-packagecmd" command. .TP \fB%w\fR Replaced with the window path name for the target window. .RE .LP A typical source handler contains one or more "send" commands which transfer data to the remote application. The target window is then asked to handle the new data. Whatever value is returned by the source \fIcommand\fR handler is automatically substituted into the "%v" fields of the target handler. .LP This separation between the transfer and the handling of the data is important. It allows the same source handler to transfer data for many different targets, and it allows each of the targets to handle the incoming data differently. If an error is encountered during the communication process, the rejection symbol is posted on the token window to indicate failure. .RE .sp .TP \fBdrag&drop target\fR Returns a list of path names for widgets registered as drag&drop targets. Returns an empty string if no widgets have been registered. .TP \fBdrag&drop target \fIwindow \fBhandler \fR?\fIdataType command arg arg...\fR? Registers a new drag&drop target window with a given handler, or modifies the handlers for an existing window. If no \fIdataType\fR is specified, this command returns the current list of recognized \fIdataType\fR strings. Each \fIdataType\fR is a symbolic name representing a form of data, and the corresponding \fIcommand\fR is a Tcl command that specifies how the target will make use of the data. This command is invoked indirectly after a source has transferred data to a target application. .PP The following substitutions are made in the \fIcommand\fR string before it is executed: .RS .TP \fB%v\fR In the simplest case, the source window does not have a handler command for the selected \fIdataType\fR, and this field is replaced with the result from the "-packagecmd" command. When the source does have a handler command, the result from the "-packagecmd" command is substituted into its "%v" field, and the result from this command is substituted into this field in the target command. .TP \fB%W\fR Replaced with the window path name for the target window. .RE .TP \fBdrag&drop target \fIwindow \fRhandle \fIdataType\fR ?\fIvalue\fR? Searches for the given \fIdataType\fR name among the handlers registered for the target \fIwindow\fR, and invokes the appropriate \fIcommand\fR. If a \fIvalue\fR is specified, it is substituted into any "%v" fields in the handler command associated with the \fIdataType\fR. If the \fIdataType\fR name is not recognized, this command returns an error. This command is invoked automatically by the drag&drop facility when data is being transferred from a source to a target. .TP \fBdrag&drop token \fIwindow\fR Returns the token window associated with a drag&drop source \fIwindow\fR. The token window is used to represent data as it is being dragged from the source to a target. When a source is first established, its token window must be filled with widgets to display the source data. For example, .sp .nf .RS \f(CWdrag&drop source .foo set win [drag&drop token .foo] label $win.label -text "Data" pack $win.label\fR .RE .fi .sp .TP \fBdrag&drop drag \fIwindow x y\fR Marks the start of (or movement during) a drag&drop operation. If the token window is unmapped when this command is invoked, then the \fB\-packagecmd\fR for the source \fIwindow\fR is executed. If this command is successful and returns a non-null string, the token window is mapped. On subsequent calls, the token window is moved to the new \fIx y\fR location. Unless the "\fB\-button 0\fR" option is specified for the source, this command is automatically bound to and events for "\fB\-button \fIn\fR" of the source widget. .TP \fBdrag&drop drop \fIwindow x y\fR Marks the end of a drag&drop operation. If the mouse pointer is over a compatible target window, then the appropriate send handler for the first compatible \fIdataType\fR is invoked to handle the data transfer. If the data transfer is successful, then the token window is unmapped; otherwise, a rejection symbol is drawn on the token window, and the window is unmapped after a small delay. Unless the "\fB\-button 0\fR" option is specified for the source, this command is automatically bound to the event for "\fB\-button \fIn\fR" of the source widget. .TP \fBdrag&drop active\fR Returns "1" if a drag&drop operation is in progress, and "0" otherwise. A drag&drop operation officially starts after the package command has been executed successfully, and ends after the send handler has been executed (successfully or otherwise). .TP \fBdrag&drop errors \fR?\fIproc\fR? Specifies a Tcl \fIproc\fR used to handle errors encountered during drag&drop operations. If a \fIproc\fR is not specified, this command returns the current error handler. By default, all errors are sent to the usual \fBtkerror\fR command, and therefore appear in a dialog box to the user. This behavior is quite useful when debugging communication protocols, but may not be desirable in a finished application. Errors can be suppressed entirely (leaving the rejection symbol as the only error indicator) by specifying a null string in place of the \fIproc\fR name. .TP \fBdrag&drop location \fR?\fIx y\fR? Used to set or query the pointer location during a drag&drop operation. The \fIx y\fR arguments specify the current location; if these arguments are missing, then the last reported (x,y) location is returned as a list with two elements. This command is issued automatically within the \fBdrag&drop drag\fR and \fBdrag&drop drop\fR commands, to keep track of pointer movement. .SH KEYWORDS drag&drop, send, bind, widget tkdesk-2.0/blt/man/man.macros0100644000175000007640000000712310020457430014240 0ustar jccjcc'\" The definitions below are for supplemental macros used in Tcl/Tk '\" manual entries. '\" '\" .HS name section [date [version]] '\" Replacement for .TH in other man pages. See below for valid '\" section names. '\" '\" .AP type name in/out [indent] '\" Start paragraph describing an argument to a library procedure. '\" type is type of argument (int, etc.), in/out is either "in", "out", '\" or "in/out" to describe whether procedure reads or modifies arg, '\" and indent is equivalent to second arg of .IP (shouldn't ever be '\" needed; use .AS below instead) '\" '\" .AS [type [name]] '\" Give maximum sizes of arguments for setting tab stops. Type and '\" name are examples of largest possible arguments that will be passed '\" to .AP later. If args are omitted, default tab stops are used. '\" '\" .BS '\" Start box enclosure. From here until next .BE, everything will be '\" enclosed in one large box. '\" '\" .BE '\" End of box enclosure. '\" '\" .VS '\" Begin vertical sidebar, for use in marking newly-changed parts '\" of man pages. '\" '\" .VE '\" End of vertical sidebar. '\" '\" .DS '\" Begin an indented unfilled display. '\" '\" .DE '\" End of indented unfilled display. '\" '\" # Heading for Tcl/Tk man pages .de HS .if '\\$2'cmds' .TH \\$1 1 \\$3 \\$4 .if '\\$2'lib' .TH \\$1 3 \\$3 \\$4 .if '\\$2'tcl' .TH \\$1 3 \\$3 \\$4 .if '\\$2'tk' .TH \\$1 3 \\$3 \\$4 .if '\\$2'BLT' .TH \\$1 "BLT 2.4" \\$3 \\$4 .if t .wh -1.3i ^B .nr ^l \\n(.l .ad b .. '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ie !"\\$3"" \{\ .ta \\n()Au \\n()Bu \&\\$1 \\fI\\$2\\fP (\\$3) '\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n '\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ '\" Draw four-sided box normally, but don't draw top of '\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ '\" Draw three-sided box if this is the box's first page, '\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .ft CW .nf .sp .. '\" # DE - end display .de DE .ft R .fi .sp .RE .. tkdesk-2.0/blt/Makefile.in0100644000175000007640000000306110020457430013546 0ustar jccjcc# This is the Makefile for the BLT subset needed by TkDesk. # It is called from the main Makefile in .., which passes the CFLAGS. #---------------------------------------------------------------- # Things you can change to personalize the Makefile for your own # site (you can make these changes in either Makefile.in or # Makefile, but changes to Makefile will get lost if you re-run # the configuration script). #---------------------------------------------------------------- # Location of Tcl header files: TCL_INCLUDE_DIR = @TCL_INCLUDE_PATH@ # Location of Tk header files: TK_INCLUDE_DIR = @TK_INCLUDE_PATH@ # Location of X11 header files: X_INCLUDE_FLAG = @TK_XINCLUDES@ # Configuration compiler flags: AC_FLAGS = @DEFS@ # Miscellaneous settings: RANLIB = @RANLIB@ CC = @CC@ AR = ar rc RM = rm -f #---------------------------------------------------------------- # The information below should be usable as is. The configure # script won't modify it and you shouldn't need to modify it # either. #---------------------------------------------------------------- CFLAGS = ${CC_OPTS} ${AC_FLAGS} -I.. -I. -I${TCL_INCLUDE_DIR} -I${TK_INCLUDE_DIR} ${X_INCLUDE_FLAG} BLT_LIBRARY = $(LIB_DIR) libname = libBLT.a OBJS = bltBgexec.o bltBusy.o bltUnixPipe.o \ bltText.o bltDnd.o bltInit.o bltList.o bltUtil.o \ bltConfig.o bltWindow.o bltChain.o bltUnixDnd.o lib: $(libname) $(libname): $(OBJS) $(RM) $@ $(AR) $@ $(OBJS) $(RANLIB) $@ bltInit.o: bltInit.c $(CC) -c $(CFLAGS) -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" $< clean: $(RM) $(OBJS) $(libname) *\~ "#"* tkdesk-2.0/blt/README0100644000175000007640000000102410020457430012356 0ustar jccjccThis directory contains the part of BLT 2.4j which TkDesk currently requires (as of version 1.2). This may vanish over time when I find other methods for performing these tasks: blt_bgexec: replace with Tk's fileevent? blt_busy: pure tcl replacement? blt_drag&drop: pure tcl replacement? Note that you can also use an installed version of BLT 2.4j. If you'd like to do this, run TkDesk's configure script with the --with-blt= flag. The subdirectory diffs contains the diffs I had to make against BLT 2.4j for TkDesk. tkdesk-2.0/blt/README.BLT0100644000175000007640000002635410020457430013013 0ustar jccjcc [ This is the last beta-release version of BLT (I know I said that before, but then came along the 8.1.1 release, and now the 8.2.0 release). What this means is that the documentation and demos still need work. Please let me know about any configuration/compiler/installation goofs so I make sure they're fixed for the final release.] This is version 2.4 of the BLT library. It's an extension to the Tcl/Tk toolkit. You simply compile and link with the Tcl/Tk libraries. It does not require the Tcl or Tk source files. BLT is available from ftp.tcltk.com in the "pub/blt" directory. The URL is ftp://ftp.tcltk.com/pub/blt/BLT2.4.tar.gz This release has been built and tested with the following Tcl/Tk versions: Tcl 7.5 / Tk 4.1 Tcl 7.6 / Tk 4.2 Tcl/Tk 8.0.2 thru 8.0.5 Tcl/Tk 8.1.0 thru 8.1.1 Tcl/Tk 8.2.0 (Alpha and beta versions probably won't work.) What is BLT? BLT is an extension to Tcl/Tk. It adds plotting widgets (X-Y graph, barchart, stripchart), a powerful geometry manager, a new canvas item, and several new commands to Tk. Plotting widgets: graph, barchart, stripchart BLT has X-Y graph, barchart, and stripchart widgets that are both easy to use and customize. All the widgets work with BLT vector data objects, which makes it easy to manage data. Hierarchical list box: hierbox Displays a general ordered tree which may be built on-the-fly or all at once. Tab set: tabset Can be used either as a tab notebook or simple tabset. Multi-tiered and/or scrolled tabsets are available. Notebook pages can be torn-off into separate windows and later put back. Geometry Manager: table A table-based geometry manager. Lets you specify widget layouts by row and column positions in the table. Unlike the packer or grid, you can finely control and constrain window sizes. Vector Data Object: vector Lets you manage a vector of floating point values in a high-level fashion. Vectors inter-operate seamlessly with the plotting widgets. The graphs will automatically redraw themselves when the vector data changes. Vector's components can be managed through a Tcl array variable, a Tcl command, or the using its own C API. Background Program Execution: bgexec Like Tcl's "exec ... &", but collects the output, error, and status of the detached UNIX subprocesses. Sets a Tcl variable upon completion. Busy Command: busy For preventing user-interactions when the application is busy. Manages an invisible "busy" window which prevents further user interactions (keyboard, mouse, button, etc.). Also you can provide a busy cursor that temporarily overrides those of the Tk widgets. New Canvas Item: eps An new item is added to the Tk canvas for handling encapsulated PostScript. It lets you embed an EPS file into the canvas displaying either an EPS preview image found in the file, or a Tk image that you provide. When you print the canvas the EPS item will automatically include the EPS file, translating and scaling the PostScript. For example, you could use "eps" items to tile several PostScript pages into single page. The "eps" item can also be used as a replacement for "image" canvas items. Unlike "image" canvas items, the image of an eps item can be printed and scaled arbitrarily. Drag & Drop Facility: drag&drop Adds drag-n-drop capabilities to Tk. It uses "send"-style communication between drag-drop sources and targets. The result is a much more powerful drag-and-drop mechanism than is available with OpenLook or Motif. Bitmap Command: bitmap Lets you read and write bitmaps from Tcl. You can define bitmaps from ordinary text strings. Bitmaps can also be scaled and rotated. For example, you can create a button with rotated text by defining a bitmap from a text string and rotating it. You can then use the bitmap in the button widget. Miscellaneous Commands: winop Basic window operations. You can raise, lower, map, or, unmap windows. Other operations let you move the pointer or take photo image snapshots of Tk widgets. bltdebug Lets you trace the execution of Tcl commands and procedures. Prints out each Tcl command before it's executed. watch Lets you specify Tcl procedures to be run before and/or after every Tcl command. May be used for logging, tracing, profiling, or debugging or Tcl code. spline Computes a spline fitting a set of data points (x and y vectors) and produces a vector of the interpolated images (y-coordinates) at a given set of x-coordinates. htext A simple hypertext widget. Allows text and Tk widgets to be combined in a scroll-able text window. Any Tk widget can be embedded and used to form hyper-links. Other options allow for selections and text searches. What's new in 2.4? 1. "eps" canvas item. An encapsulated PostScript canvas item lets you embed an EPS file into the canvas. The "eps" item displays either a EPS preview image found in the file, or a Tk image that you provide. 2. "hierbox" widget. Hierarchical listbox widget. Displays a general ordered tree which may be built on-the-fly or all at once. 3. "tabset" widget. Can be used either as a tab notebook or simple tabset. Tabs can be arranged in a variety of ways: multi-tiered, scrolled, and attached to any of the four sides. Tab labels can contain both images and text (text can be arbitrarily rotated). Notebook pages can be torn-off into separate windows and replaced later. 4. Changes to vectors. New features: o Vector expressions. The vector now has an "expr" operation that lets you perform math (including math library functions) on vectors. There are several new functions (such as "max", "min", "mean" "median", "q1", "q3", "prod", "sum", "adev", "sdev", "skew", ...) vector expr { sin(x)^2 + cos(x)^2 } y expr { log(x) * $value } o New syntax to create and destroy vectors: vector create x vector destroy x The old syntax for creating vectors still works. vector x o Vectors are *not* automatically deleted when their Tcl variable is unset anymore. This means that you can temporarily map vectors to variables and use them as you would an ordinary Tcl array (kind of like "upvar"). proc AddValue { vecName value } { $vecName variable x set x(++end) $value } There's an "-watchunset" flag to restore the old behavior if you need it. vector create x -watchunset yes o Vectors still automatically create Tcl variables by default. I'd like to change this, but it silently breaks lots of code, so it will stay. Bug fixes: o Vector reallocation failed when shrinking the vector. o Vector "destroy" callback made after vector was already freed. o Fixed vector/scalar operations. o Always store results in temporary, so not to overwrite accidently current vector values. 5. Changes to Graph, Barchart, Stripchart widgets. New features: o Drop shadows for text (titles, markers, etc). Drop shadows improve contrast when displaying text over a background with similar color intensities. o Postscript "-preview" option to generate a EPS PostScript preview image that can be read and displayed by the EPS canvas item. o New "-topvariable", "-bottomvariable", "-leftvariable", and "-rightvariable" options. They specify variables to contain the current margin sizes. These variables are updated whenever the graph is redrawn. o New "-aspect" option. Let's you maintain a particular aspect ratio for the the graph. o Image markers can now be stretched and zoomed like bitmap markers. o Bind operation for legend entries, markers, and elements. Much thanks to Julian Loaring for the idea. o New "-xor" option for line markers, lets you draw the line by rubberbanded by XOR-ing without requiring the graph to be redrawn. This can be used, for example, to select regions for zooming. Thanks to Johannes Zellner (joze@krisal.physik.uni-karlsruhe.de) for the idea. o Can attach a scrollbar to an axis. .sbar configure -command { .graph axis view y } .graph axis configure y -scrollcommand { .sbar set } Bug fixes: o Closest line (point) broken when using pens styles. o Marker elastic coordinates were wrong. o PostScript bounding box included the border of the page. o Bad PostScript generated for barchart symbols with stipples. o Wrong dimensions computed with postscript " -maxpect" option. o Text markers fixed. Thanks to De Clarke for the bug report and fix. o Renamed axis configuration from "-range" to "-autorange" to match the documentation. Thanks to Brian Smith for the correction. o Fixed polygon marker pick routine. o Fixed active tab labels overlapping the selected tab. o PostScript graph footer turned off by default. Use -footer option to turn on. .graph postscript configure -footer yes What's incompatible with releases prior to BLT 2.4? 1. Vector names must start with a letter and contain letters, digits, or underscores. Vectors are now associated by their namespaces. A vector name can contain a namespace qualifier. By default they reside in the namespace they were created in. % namespace eval fred { vector create x } ::fred::x % vector create fred::y ::fred::y % vector names ::fred::y ::fred::x The one fly in the ointment is that there still isn't any good way to get notified when the namespace is deleted (and automatically delete any vectors). The two ways around this problem are to a) delete all vectors that you've created in namespace *before* you destroy the namespace, or b) have either the vector's Tcl variable or command reside in the same namespace (this happens by default). That way the vector will be deleted when the command or variable is destroyed. 2. The "-mapped" options throughout the graph have been replaced by the "-hide" option. The many usages of the word "map" was getting confusing. # No longer works. .graph legend configure -mapped no # Instead use this. .graph legend configure -hide yes How to compile and test BLT? See the file "INSTALL" for instructions. Does BLT work under Windows? Yes. Windows NT and Windows 95/98. It builds and runs with MS VC++ 5.0/6.0 and EGCS 1.1.1 under Windows 95/98/NT. Binary only versions are available. What are the differences between the Windows and Unix releases? All commands work: graphs, bgexec, busy, drag&drop etc. except the "container", and "cutbuffer" widgets. The "drag&drop" command still needs a working version of the Tk "send" command. When will...? In general, I can't answer the "When will" questions, mostly out of embarrassment. My estimates of when new features and releases will occur usually turn out to be way way off. What does BLT stand for? Whatever you want it to. Where to send bugs reports, suggestions, etc. ? gah@cadence.com and ghowlett@fast.net (best to send to both addresses) Make sure you include BLT and the version number in the subject line. --gah tkdesk-2.0/blt/blt.h0100644000175000007640000001203410020457430012433 0ustar jccjcc/* * blt.h -- * * Copyright 1991-1998 by Bell Labs Innovations for Lucent * Technologies. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #ifndef _BLT_H #define _BLT_H #define BLT_MAJOR_VERSION 2 #define BLT_MINOR_VERSION 4 #define BLT_VERSION "2.4" #include #include #ifndef EXPORT #define EXPORT #endif #undef EXTERN #ifdef __cplusplus # define EXTERN extern "C" EXPORT #else # define EXTERN extern EXPORT #endif #ifndef _ANSI_ARGS_ # define _ANSI_ARGS_(x) () #endif typedef enum { BLT_VECTOR_NOTIFY_UPDATE = 1, /* The vector's values has been updated */ BLT_VECTOR_NOTIFY_DESTROY /* The vector has been destroyed and the client * should no longer use its data (calling * Blt_FreeVectorId) */ } Blt_VectorNotify; typedef struct Blt_VectorId_ *Blt_VectorId; typedef void (Blt_VectorChangedProc) _ANSI_ARGS_((Tcl_Interp *interp, ClientData clientData, Blt_VectorNotify notify)); typedef struct Blt_Vector { double *valueArr; /* Array of values (possibly malloc-ed) */ int numValues; /* Number of values in the array */ int arraySize; /* Size of the allocated space */ double min, max; /* Minimum and maximum values in the vector */ int dirty; /* Indicates if the vector has been updated */ int reserved; /* Reserved for future use */ } Blt_Vector; typedef double (Blt_VectorIndexProc) _ANSI_ARGS_((Blt_Vector * vecPtr)); typedef enum { BLT_MATH_FUNC_SCALAR = 1, /* The function returns a single double * precision value. */ BLT_MATH_FUNC_VECTOR /* The function processes the entire vector. */ } Blt_MathFuncType; /* * To be safe, use the macros below, rather than the fields of the * structure directly. * * The Blt_Vector C API is in a state of flux. I'm inclined to make * Blt_Vector an opaque type. The original idea was to make the API * as unobtrusive as possible. Instead of calling various access * functions, you get the actual array of doubles. The trade-off is * speed and convenience versus safety. You can easily corrupt the * vector by setting bogus values for any of the Blt_Vector fields. * At least the macros are a reminder it isn't really safe to reset * the data fields, except by the API routines. */ #define Blt_VecData(v) ((v)->valueArr) #define Blt_VecLength(v) ((v)->numValues) #define Blt_VecSize(v) ((v)->arraySize) #define Blt_VecMin(v) ((v)->min) #define Blt_VecMax(v) ((v)->max) #define Blt_VecDirty(v) ((v)->dirty) EXTERN Blt_VectorId Blt_AllocVectorId _ANSI_ARGS_((Tcl_Interp *interp, char *vecName)); EXTERN void Blt_SetVectorChangedProc _ANSI_ARGS_((Blt_VectorId clientId, Blt_VectorChangedProc * proc, ClientData clientData)); EXTERN void Blt_FreeVectorId _ANSI_ARGS_((Blt_VectorId clientId)); EXTERN int Blt_GetVectorById _ANSI_ARGS_((Tcl_Interp *interp, Blt_VectorId clientId, Blt_Vector ** vecPtrPtr)); EXTERN char *Blt_NameOfVectorId _ANSI_ARGS_((Blt_VectorId clientId)); EXTERN int Blt_VectorNotifyPending _ANSI_ARGS_((Blt_VectorId clientId)); EXTERN int Blt_CreateVector _ANSI_ARGS_((Tcl_Interp *interp, char *vecName, int size, Blt_Vector ** vecPtrPtr)); EXTERN int Blt_GetVector _ANSI_ARGS_((Tcl_Interp *interp, char *vecName, Blt_Vector ** vecPtrPtr)); EXTERN int Blt_VectorExists _ANSI_ARGS_((Tcl_Interp *interp, char *vecName)); EXTERN int Blt_ResetVector _ANSI_ARGS_((Blt_Vector * vecPtr, double *dataArr, int nValues, int arraySize, Tcl_FreeProc *freeProc)); EXTERN int Blt_ResizeVector _ANSI_ARGS_((Blt_Vector * vecPtr, int nValues)); EXTERN int Blt_DeleteVectorByName _ANSI_ARGS_((Tcl_Interp *interp, char *vecName)); EXTERN int Blt_DeleteVector _ANSI_ARGS_((Blt_Vector * vecPtr)); EXTERN int Blt_ExprVector _ANSI_ARGS_((Tcl_Interp *interp, char *expression, Blt_Vector * vecPtr)); EXTERN void Blt_InstallIndexProc _ANSI_ARGS_((Tcl_Interp *interp, char *indexName, Blt_VectorIndexProc * procPtr)); EXTERN int Blt_GetOpenPrinter _ANSI_ARGS_((Tcl_Interp *interp, const char *id, Drawable *drawablePtr)); EXTERN int Blt_StartPrintJob _ANSI_ARGS_((Tcl_Interp *interp, const char *id)); EXTERN int Blt_EndPrintJob _ANSI_ARGS_((Tcl_Interp *interp, const char *id)); #endif /*_BLT_H*/ tkdesk-2.0/blt/bltBgexec.c0100644000175000007640000012307610020457430013555 0ustar jccjcc /* * bltBgexec.c -- * * This module implements a background "exec" command for the * BLT toolkit. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * The "bgexec" command was created by George Howlett. */ #include "bltInt.h" #ifndef NO_BGEXEC #include #include #ifdef HAVE_SYS_PARAM_H #include #endif #include #include "bltWait.h" #if (TCL_MAJOR_VERSION == 7) #define FILEHANDLER_USES_TCLFILES 1 #else typedef int Tcl_File; #endif #ifdef __STDC__ static Tcl_CmdProc BgexecCmd; #endif /* __STDC__ */ #ifdef WIN32 typedef struct Process { DWORD pid; HANDLE hProcess; } Process; #else typedef int Process; #endif /* * As of Tcl 7.6, we're using our own version of the old * Tcl_CreatePipeline routine. I would have tried to use * Tcl_OpenCommandChannel but you can't get at the array of * process ids, unless of course you pry open the undocumented * structure PipeStatus as clientData. Nor could I figure out * how to set one side of the pipe to be non-blocking. The whole * channel API seems overly complex for what its supposed to * do. [And maybe that's why it keeps changing every release.] */ extern int Blt_CreatePipeline _ANSI_ARGS_((Tcl_Interp *interp, int argc, char **argv, Process **pidPtrPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr)); #ifdef WIN32 #define read(fd, buf, size) Blt_AsyncRead((fd),(buf),(size)) #define close(fd) CloseHandle((HANDLE)fd) #define Tcl_CreateFileHandler Blt_CreateFileHandler #define Tcl_DeleteFileHandler Blt_DeleteFileHandler #define kill KillProcess #define waitpid WaitProcess #endif #define READ_AGAIN (0) #define READ_EOF (-1) #define READ_ERROR (-2) /* The wait-related definitions are taken from tclUnix.h */ /* * Not all systems declare the errno variable in errno.h. so this * file does it explicitly. The list of system error messages also * isn't generally declared in a header file anywhere. */ #ifdef WIN32 extern int errno; #endif #define TRACE_FLAGS (TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY) #define BLOCK_SIZE 1024 /* Size of allocation blocks for buffer */ #define DEF_BUFFER_SIZE (BLOCK_SIZE * 8) #define MAX_READS 100 /* Maximum number of successful reads * before stopping to let Tk catch up * on events */ #ifndef NSIG #define NSIG 32 /* Number of signals available */ #endif /*NSIG*/ #ifndef SIGINT #define SIGINT 2 #endif /* SIGINT */ #ifndef SIGQUIT #define SIGQUIT 3 #endif /* SIGQUIT */ #ifndef SIGKILL #define SIGKILL 9 #endif /* SIGKILL */ #ifndef SIGTERM #define SIGTERM 14 #endif /* SIGTERM */ typedef struct { int number; char *name; } SignalId; static SignalId signalIds[] = { #ifdef SIGABRT {SIGABRT, "SIGABRT"}, #endif #ifdef SIGALRM {SIGALRM, "SIGALRM"}, #endif #ifdef SIGBUS {SIGBUS, "SIGBUS"}, #endif #ifdef SIGCHLD {SIGCHLD, "SIGCHLD"}, #endif #if defined(SIGCLD) && (!defined(SIGCHLD) || (SIGCLD != SIGCHLD)) {SIGCLD, "SIGCLD"}, #endif #ifdef SIGCONT {SIGCONT, "SIGCONT"}, #endif #if defined(SIGEMT) && (!defined(SIGXCPU) || (SIGEMT != SIGXCPU)) {SIGEMT, "SIGEMT"}, #endif #ifdef SIGFPE {SIGFPE, "SIGFPE"}, #endif #ifdef SIGHUP {SIGHUP, "SIGHUP"}, #endif #ifdef SIGILL {SIGILL, "SIGILL"}, #endif #ifdef SIGINT {SIGINT, "SIGINT"}, #endif #ifdef SIGIO {SIGIO, "SIGIO"}, #endif #if defined(SIGIOT) && (!defined(SIGABRT) || (SIGIOT != SIGABRT)) {SIGIOT, "SIGIOT"}, #endif #ifdef SIGKILL {SIGKILL, "SIGKILL"}, #endif #if defined(SIGLOST) && (!defined(SIGIOT) || (SIGLOST != SIGIOT)) && (!defined(SIGURG) || (SIGLOST != SIGURG)) {SIGLOST, "SIGLOST"}, #endif #ifdef SIGPIPE {SIGPIPE, "SIGPIPE"}, #endif #if defined(SIGPOLL) && (!defined(SIGIO) || (SIGPOLL != SIGIO)) {SIGPOLL, "SIGPOLL"}, #endif #ifdef SIGPROF {SIGPROF, "SIGPROF"}, #endif #if defined(SIGPWR) && (!defined(SIGXFSZ) || (SIGPWR != SIGXFSZ)) {SIGPWR, "SIGPWR"}, #endif #ifdef SIGQUIT {SIGQUIT, "SIGQUIT"}, #endif #ifdef SIGSEGV {SIGSEGV, "SIGSEGV"}, #endif #ifdef SIGSTOP {SIGSTOP, "SIGSTOP"}, #endif #ifdef SIGSYS {SIGSYS, "SIGSYS"}, #endif #ifdef SIGTERM {SIGTERM, "SIGTERM"}, #endif #ifdef SIGTRAP {SIGTRAP, "SIGTRAP"}, #endif #ifdef SIGTSTP {SIGTSTP, "SIGTSTP"}, #endif #ifdef SIGTTIN {SIGTTIN, "SIGTTIN"}, #endif #ifdef SIGTTOU {SIGTTOU, "SIGTTOU"}, #endif #if defined(SIGURG) && (!defined(SIGIO) || (SIGURG != SIGIO)) {SIGURG, "SIGURG"}, #endif #if defined(SIGUSR1) && (!defined(SIGIO) || (SIGUSR1 != SIGIO)) {SIGUSR1, "SIGUSR1"}, #endif #if defined(SIGUSR2) && (!defined(SIGURG) || (SIGUSR2 != SIGURG)) {SIGUSR2, "SIGUSR2"}, #endif #ifdef SIGVTALRM {SIGVTALRM, "SIGVTALRM"}, #endif #ifdef SIGWINCH {SIGWINCH, "SIGWINCH"}, #endif #ifdef SIGXCPU {SIGXCPU, "SIGXCPU"}, #endif #ifdef SIGXFSZ {SIGXFSZ, "SIGXFSZ"}, #endif {-1, "unknown signal"}, }; static int StringToSignal _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *SignalToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)); static Tk_CustomOption signalOption = { StringToSignal, SignalToString, (ClientData)0 }; typedef struct Sink { char *name; /* Name of the sink */ char *doneVar; /* Name of a Tcl variable (malloc'ed) * set to the collected data of the * last UNIX subprocess. */ char *updateVar; /* Name of a Tcl variable (malloc'ed) * updated as data is read from the * pipe. */ char *updateCmd; /* Start of a Tcl command executed * whenever data is read from the * pipe. */ Tcl_File file; /* Used for backward compatability * with Tcl 7.5 */ int fd; /* File descriptor of the pipe. */ int prevEnd; /* Number of bytes read the last time a * buffer was retrieved */ int fixMark; /* Index of fixed newline character in * buffer. If -1, no fix was made. */ int echo; /* Indicates if the pipeline's stderr stream * should be echoed */ char *byteArr; /* Stores command output (malloc-ed): * Initially points to static storage */ int end; /* Number of characters in the buffer */ int arraySize; /* Size of buffer allocated */ char staticSpace[DEF_BUFFER_SIZE]; /* Static space */ } Sink; typedef struct BackgroundInfo { char *statVar; /* Name of a Tcl variable set to the * exit status of the last * process. Setting this variable * triggers the termination of all * subprocesses (regardless whether * they have already completed) */ int signalNum; /* If non-zero, indicates the signal * to send subprocesses when cleaning * up.*/ int keepTrailingNewLine; /* If non-zero, indicates to set Tcl * output variables with trailing * newlines intact */ int interval; /* Interval to poll for the exiting * processes */ /* Private */ Tcl_Interp *interp; /* Interpreter containing variables */ Display *display; /* Held to free background options */ int numProcs; /* Number of processes in pipeline */ Process *procArr; /* Array of process tokens from pipeline. * The token for Unix are pid_t, while * for Win32 they're handles. */ int traced; /* Indicates that the status variable * is currently being traced. */ int detached; /* Indicates that the pipeline is * detached from standard I/O, running * in the background. */ Tk_TimerToken timerToken; /* Token for timer handler which polls * for the exit status of each * sub-process. If zero, there's no * timer handler queued. */ int *exitCodePtr; /* Pointer to a memory location to * contain the last process' exit * code. */ int *donePtr; Sink sink1, sink2; } BackgroundInfo; static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_BOOLEAN, "-echo", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink2.echo), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-output", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink1.doneVar), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-update", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink1.updateVar), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-error", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink2.doneVar), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-lasterror", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink2.updateVar), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-lastoutput", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink1.updateVar), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-onerror", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink2.updateCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-onoutput", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, sink1.updateCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-keepnewline", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, keepTrailingNewLine), 0}, {TK_CONFIG_CUSTOM, "-killsignal", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, signalNum), 0, &signalOption}, {TK_CONFIG_INT, "-check", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(BackgroundInfo, interval), 0}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0} }; static char *VariableProc _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, char *part1, char *part2, int flags)); static void TimerProc _ANSI_ARGS_((ClientData clientData)); static void StdoutProc _ANSI_ARGS_((ClientData clientData, int mask)); static void StderrProc _ANSI_ARGS_((ClientData clientData, int mask)); /* *---------------------------------------------------------------------- * * StringToSignal -- * * Convert a string represent a signal number into its integer * value. * * Results: * The return value is a standard Tcl result. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToSignal(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Signal number */ char *widgRec; /* Background info record */ int offset; /* Offset of vector in Element record */ { int *signalPtr = (int *)(widgRec + offset); int signalNum; if ((string == NULL) || (*string == '\0')) { *signalPtr = 0; return TCL_OK; } if (isdigit(UCHAR(string[0]))) { if (Tcl_GetInt(interp, string, &signalNum) != TCL_OK) { return TCL_ERROR; } } else { char *name; register SignalId *sigPtr; name = string; /* Clip off any "SIG" prefix from the signal name */ if ((name[0] == 'S') && (name[1] == 'I') && (name[2] == 'G')) { name += 3; } signalNum = -1; for (sigPtr = signalIds; sigPtr->number > 0; sigPtr++) { if (strcmp(sigPtr->name + 3, name) == 0) { signalNum = sigPtr->number; break; } } if (signalNum < 0) { Tcl_AppendResult(interp, "unknown signal \"", string, "\"", (char *)NULL); return TCL_ERROR; } } if ((signalNum < 1) || (signalNum > NSIG)) { /* Outside range of signals */ Tcl_AppendResult(interp, "signal number \"", string, "\" is out of range", (char *)NULL); return TCL_ERROR; } *signalPtr = signalNum; return TCL_OK; } /* *---------------------------------------------------------------------- * * SignalToString -- * * Convert the integer signal number into an ASCII string. * * Results: * The string representation of the kill signal is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * SignalToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* BackgroundInfo record */ int offset; /* Offset of signal number in record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { int signalNum = *(int *)(widgRec + offset); if (signalNum == 0) { return ""; } else { char *result; char string[20]; sprintf(string, "%d", signalNum); *freeProcPtr = (Tcl_FreeProc *)free; result = strdup(string); return result; } } #ifdef WIN32 /* *---------------------------------------------------------------------- * * TranslateEOL -- * * For Windows, translate CR/NL combinations to NL alone. * * Results: * None. * * Side Effects: * The size of the byte array may shrink and array contents * shifted as carriage returns are found and removed. * *---------------------------------------------------------------------- */ static void TranslateEOL(sinkPtr) Sink *sinkPtr; { register int count; register char *s, *p; register int i; int nBytes; count = 0; nBytes = sinkPtr->end - sinkPtr->prevEnd; s = p = sinkPtr->byteArr + sinkPtr->prevEnd; for (i = 0; i < nBytes; i++, p++) { if (*p == '\r') { continue; } if (count < i) { *s = *p; } s++, count++; } sinkPtr->end = sinkPtr->prevEnd + count; } /* *---------------------------------------------------------------------- * * WaitProcess -- * * Emulates the waitpid system call under the Win32 API. * * Results: * Returns 0 if the process is still alive, -1 on an error, or * the pid on a clean close. * * Side effects: * Unless WNOHANG is set and the wait times out, the process * information record will be deleted and the process handle * will be closed. * *---------------------------------------------------------------------- */ #define WINDEBUG 0 static int WaitProcess(child, statusPtr, flags) Process child; int *statusPtr; int flags; { int result; DWORD status, exitCode; int timeout; #if WINDEBUG PurifyPrintf("WAITPID(%x)\n", child.hProcess); #endif *statusPtr = 0; if (child.hProcess == INVALID_HANDLE_VALUE) { errno = EINVAL; return -1; } #if WINDEBUG PurifyPrintf("WAITPID: waiting for 0x%x\n", child.hProcess); #endif timeout = (flags & WNOHANG) ? 0 : INFINITE; status = WaitForSingleObject(child.hProcess, timeout); #if WINDEBUG PurifyPrintf("WAITPID: wait status is %d\n", status); #endif switch (status) { case WAIT_FAILED: errno = ECHILD; *statusPtr = ECHILD; result = -1; break; case WAIT_TIMEOUT: if (timeout == 0) { return 0; /* Try again */ } result = 0; break; default: case WAIT_ABANDONED: case WAIT_OBJECT_0: GetExitCodeProcess(child.hProcess, &exitCode); *statusPtr = ((exitCode << 8) & 0xff00); #if WINDEBUG PurifyPrintf("WAITPID: exit code of %d is %d (%x)\n", child.hProcess, *statusPtr, exitCode); #endif result = child.pid; break; } CloseHandle(child.hProcess); return result; } /* *---------------------------------------------------------------------- * * KillProcess -- * * Emulates the UNIX kill system call under Win32 API. * * Results: * Returns 0 if the process is killed, -1 on an error. * * Side effects: * Process is terminated. * *---------------------------------------------------------------------- */ static int KillProcess(Process proc, int signal) { if ((proc.hProcess == NULL) || (proc.hProcess == INVALID_HANDLE_VALUE)) { errno = EINVAL; return -1; } switch (signal) { case SIGINT: /*FALLTHRU*/ case SIGTERM: /*FALLTHRU*/ case SIGQUIT: /*FALLTHRU*/ case SIGKILL: default: if (!TerminateProcess(proc.hProcess, 1)) { #if WINDEBUG PurifyPrintf("can't terminate process (handle=%d): %s\n", proc.hProcess, Blt_LastError()); #endif /* WINDEBUG */ return -1; } } return 0; } #endif /* WIN32 */ /* *---------------------------------------------------------------------- * * GetSinkData -- * * Returns the data currently saved in the buffer * *---------------------------------------------------------------------- */ static char * GetSinkData(sinkPtr) Sink *sinkPtr; { sinkPtr->byteArr[sinkPtr->end] = '\0'; return sinkPtr->byteArr; } /* *---------------------------------------------------------------------- * * LastRead -- * * Returns the data saved from the last time this routine * was called. * *---------------------------------------------------------------------- */ static char * LastRead(sinkPtr) Sink *sinkPtr; { char *string; sinkPtr->byteArr[sinkPtr->end] = '\0'; string = sinkPtr->byteArr + sinkPtr->prevEnd; sinkPtr->prevEnd = sinkPtr->end; return string; } /* *---------------------------------------------------------------------- * * FlushSink -- * * Flushes the buffer, resetting it to empty. * This is used when we don't want to save all the data from * the pipeline. * *---------------------------------------------------------------------- */ static void FlushSink(sinkPtr) Sink *sinkPtr; { sinkPtr->byteArr[0] = '\0'; sinkPtr->end = sinkPtr->prevEnd = 0; } /* *---------------------------------------------------------------------- * * InitSink -- * * Initializes the buffer's storage. * * Results: * None. * * Side effects: * Storage is cleared. * *---------------------------------------------------------------------- */ static void InitSink(sinkPtr, name) Sink *sinkPtr; char *name; { sinkPtr->name = name; sinkPtr->echo = FALSE; sinkPtr->fd = -1; sinkPtr->file = (Tcl_File)NULL; sinkPtr->byteArr = sinkPtr->staticSpace; sinkPtr->fixMark = -1; sinkPtr->arraySize = DEF_BUFFER_SIZE; FlushSink(sinkPtr); } /* *---------------------------------------------------------------------- * * ResetSinkBuffer -- * * Resets the buffer's storage, freeing any malloc'ed space. * * Results: * None. * *---------------------------------------------------------------------- */ static void ResetSinkBuffer(sinkPtr) Sink *sinkPtr; { if (sinkPtr->byteArr != sinkPtr->staticSpace) { free(sinkPtr->byteArr); } InitSink(sinkPtr); } /* *---------------------------------------------------------------------- * * ExtendSinkBuffer -- * * Doubles the size of the current buffer. * * Results: * None. * *---------------------------------------------------------------------- */ static int ExtendSinkBuffer(sinkPtr) Sink *sinkPtr; { char *newPtr; /* * Allocate a new array, double the old size */ sinkPtr->arraySize += sinkPtr->arraySize; newPtr = (char *)malloc(sizeof(char) * sinkPtr->arraySize); if (newPtr == NULL) { return TCL_ERROR; } strcpy(newPtr, sinkPtr->byteArr); if (sinkPtr->byteArr != sinkPtr->staticSpace) { free((char *)sinkPtr->byteArr); } sinkPtr->byteArr = newPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * * ReadBytes -- * * Reads and appends any available data from a given file descriptor * to the buffer. * * Results: * Returns TCL_OK when EOF is found, TCL_RETURN if reading * data would block, and TCL_ERROR if an error occurred. * *---------------------------------------------------------------------- */ static int ReadBytes(sinkPtr) Sink *sinkPtr; { int nBytes, bytesLeft; register int i, n; char *array; /* * ------------------------------------------------------------------ * * Worry about indefinite postponement. * * Typically we want to stay in the read loop as long as it takes * to collect all the data that's currently available. But if * it's coming in at a constant high rate, we need to arbitrarily * break out at some point. This allows for both setting the * update variable and the Tk program to handle idle events. * * ------------------------------------------------------------------ */ for (i = 0; i < MAX_READS; i++) { /* * Allocate a larger buffer when the number of remaining bytes * is below the threshold BLOCK_SIZE. */ bytesLeft = sinkPtr->arraySize - sinkPtr->end; if (bytesLeft < BLOCK_SIZE) { if (ExtendSinkBuffer(sinkPtr) != TCL_OK) { return READ_ERROR; } /* Size of buffer has changed. */ bytesLeft = sinkPtr->arraySize - sinkPtr->end; } array = sinkPtr->byteArr + sinkPtr->end; /* * Read into a buffer but make sure we leave room for a * trailing NUL byte. */ nBytes = read(sinkPtr->fd, array, bytesLeft - 1); if (nBytes == 0) { /* EOF: break out of loop. */ return READ_EOF; } if (nBytes < 0) { /* * Either an error has occurred or no more data is * currently available to read. */ #ifdef O_NONBLOCK if (errno == EAGAIN) { #else if (errno == EWOULDBLOCK) { #endif /*O_NONBLOCK*/ return READ_AGAIN; } sinkPtr->byteArr[0] = '\0'; return READ_ERROR; } /* Clean out NUL bytes, make spaces */ for (n = 0; n < nBytes; n++) { if (array[n] == 0) { array[n] = ' '; } } sinkPtr->end += nBytes; sinkPtr->byteArr[sinkPtr->end] = '\0'; } return nBytes; } /* *---------------------------------------------------------------------- * * FixNewline -- * * Clips off the trailing newline in the buffer (if one exists). * Saves the location in the buffer where the fix was made. * *---------------------------------------------------------------------- */ static void FixNewline(sinkPtr) Sink *sinkPtr; { sinkPtr->fixMark = -1; if (sinkPtr->end > 0) { int mark = sinkPtr->end - 1; if (sinkPtr->byteArr[mark] == '\n') { sinkPtr->byteArr[mark] = '\0'; sinkPtr->fixMark = mark; } } } /* *---------------------------------------------------------------------- * * UnfixNewline -- * * Restores the previously clipped newline in the buffer. * The fixMark field indicates whether one was clipped. * *---------------------------------------------------------------------- */ static void UnfixNewline(sinkPtr) Sink *sinkPtr; { if (sinkPtr->fixMark >= 0) { sinkPtr->byteArr[sinkPtr->fixMark] = '\n'; sinkPtr->fixMark = -1; } } #define IsOpenSink(sinkPtr) ((sinkPtr)->fd != -1) static void CloseSink(bgPtr, sinkPtr) BackgroundInfo *bgPtr; Sink *sinkPtr; { if (IsOpenSink(sinkPtr)) { #ifndef WIN32 close(sinkPtr->fd); #endif #ifdef FILEHANDLER_USES_TCLFILES Tcl_DeleteFileHandler(sinkPtr->file); Tcl_FreeFile(sinkPtr->file); #else Tcl_DeleteFileHandler(sinkPtr->fd); #endif sinkPtr->file = (Tcl_File)NULL; sinkPtr->fd = -1; #if WINDEBUG PurifyPrintf("CloseSink: set done var %s\n", sinkPtr->name); #endif if (sinkPtr->doneVar != NULL) { /* * If data is to be collected, set the "done" variable with * the contents of the buffer. */ if (!bgPtr->keepTrailingNewLine) { FixNewline(sinkPtr); } if (Tcl_SetVar(bgPtr->interp, sinkPtr->doneVar, GetSinkData(sinkPtr), TCL_GLOBAL_ONLY) == NULL) { Tk_BackgroundError(bgPtr->interp); } } #if WINDEBUG PurifyPrintf("CloseSink %s: done\n", sinkPtr->name); #endif } } static int CollectData(bgPtr, sinkPtr) BackgroundInfo *bgPtr; Sink *sinkPtr; { int status; int flags; /* * If there is no output variable (-update used alone) there's no * need to accumulate the output in memory. Reset the counters so * we only keep what's last read. */ if ((bgPtr->detached) && (sinkPtr->doneVar == NULL)) { FlushSink(sinkPtr); } flags = (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG); status = ReadBytes(sinkPtr); #ifdef WIN32 TranslateEOL(sinkPtr); #endif if (((sinkPtr->updateCmd != NULL) || (sinkPtr->updateVar != NULL) || (sinkPtr->echo)) && (sinkPtr->prevEnd < sinkPtr->end)) { char *data; if (!bgPtr->keepTrailingNewLine) { FixNewline(sinkPtr); } data = LastRead(sinkPtr); #if WINDEBUG_0 PurifyPrintf("read %s", data); #endif if (data[0] != '\0') { if (sinkPtr->echo) { Tcl_Channel channel; channel = Tcl_GetStdChannel(TCL_STDERR); if (channel == NULL) { Tcl_AppendResult(bgPtr->interp, "can't get stderr channel", (char *)NULL); Tk_BackgroundError(bgPtr->interp); sinkPtr->echo = FALSE; } Tcl_Write(channel, data, -1); if (sinkPtr->fixMark >= 0) { Tcl_Write(channel, "\n", -1); } Tcl_Flush(channel); } if (sinkPtr->updateCmd != NULL) { Tcl_DString dString; int result; Tcl_DStringInit(&dString); Tcl_DStringAppend(&dString, sinkPtr->updateCmd, -1); Tcl_DStringAppend(&dString, " ", -1); Tcl_DStringAppendElement(&dString, data); result = Tcl_GlobalEval(bgPtr->interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result != TCL_OK) { Tk_BackgroundError(bgPtr->interp); } } if (sinkPtr->updateVar != NULL) { if (Tcl_SetVar(bgPtr->interp, sinkPtr->updateVar, data, flags) == NULL) { Tk_BackgroundError(bgPtr->interp); } } } if (!bgPtr->keepTrailingNewLine) { UnfixNewline(sinkPtr); } } if (status >= 0) { return TCL_RETURN; } if (status == READ_ERROR) { Tcl_AppendResult(bgPtr->interp, "can't read data from ", sinkPtr->name, ": ", Tcl_PosixError(bgPtr->interp), (char *)NULL); Tk_BackgroundError(bgPtr->interp); } /* * Either EOF or an error has occurred. In either case, * close the sink. */ CloseSink(bgPtr, sinkPtr); #if WINDEBUG PurifyPrintf("CollectData %s: done\n", sinkPtr->name); #endif return TCL_OK; } /* *---------------------------------------------------------------------- * * CreateSinkHandler -- * * Creates a file handler for the given sink. The file * descriptor is also set for non-blocking I/O. * * Results: * None. * * Side effects: * The memory allocated to the BackgroundInfo structure released. * *---------------------------------------------------------------------- */ static int CreateSinkHandler(bgPtr, sinkPtr, proc) BackgroundInfo *bgPtr; Sink *sinkPtr; Tcl_FileProc *proc; { #ifndef WIN32 int flags; flags = fcntl(sinkPtr->fd, F_GETFL); #ifdef O_NONBLOCK flags |= O_NONBLOCK; #else flags |= O_NDELAY; #endif if (fcntl(sinkPtr->fd, F_SETFL, flags) < 0) { Tcl_AppendResult(bgPtr->interp, "can't set file descriptor ", Blt_Itoa(sinkPtr->fd), " to non-blocking:", Tcl_PosixError(bgPtr->interp), (char *)NULL); return TCL_ERROR; } #endif /* WIN32 */ #ifdef FILEHANDLER_USES_TCLFILES sinkPtr->file = Tcl_GetFile((ClientData)sinkPtr->fd, TCL_UNIX_FD); Tcl_CreateFileHandler(sinkPtr->file, TK_READABLE, proc, (ClientData)bgPtr); #else Tcl_CreateFileHandler(sinkPtr->fd, TK_READABLE, proc, (ClientData)bgPtr); #endif /* FILEHANDLER_USES_TCLFILES */ return TCL_OK; } static void DisableTriggers(bgPtr) BackgroundInfo *bgPtr; /* Background info record. */ { if (bgPtr->traced) { Tcl_UntraceVar(bgPtr->interp, bgPtr->statVar, TRACE_FLAGS, VariableProc, (ClientData)bgPtr); bgPtr->traced = FALSE; } if (IsOpenSink(&(bgPtr->sink1))) { CloseSink(bgPtr, &(bgPtr->sink1)); } if (IsOpenSink(&(bgPtr->sink2))) { CloseSink(bgPtr, &(bgPtr->sink2)); } if (bgPtr->timerToken != (Tk_TimerToken) 0) { Tk_DeleteTimerHandler(bgPtr->timerToken); bgPtr->timerToken = 0; } if (bgPtr->donePtr != NULL) { *bgPtr->donePtr = TRUE; } } /* *---------------------------------------------------------------------- * * DestroyBackgroundInfo -- * * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release * to clean up the internal structure (BackgroundInfo) at a safe * time (when no one is using it anymore). * * Results: * None.b * * Side effects: * The memory allocated to the BackgroundInfo structure released. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void DestroyBackgroundInfo(bgPtr) BackgroundInfo *bgPtr; /* Background info record. */ { DisableTriggers(bgPtr); ResetSinkBuffer(&(bgPtr->sink2)); ResetSinkBuffer(&(bgPtr->sink1)); if (bgPtr->procArr != NULL) { register int i; for (i = 0; i < bgPtr->numProcs; i++) { if (bgPtr->signalNum > 0) { kill(bgPtr->procArr[i], bgPtr->signalNum); } #ifdef WIN32 Tcl_DetachPids(1, (Tcl_Pid *)&(bgPtr->procArr[i].pid)); #else #if (TCL_MAJOR_VERSION == 7) Tcl_DetachPids(1, &(bgPtr->procArr[i])); #else Tcl_DetachPids(1, (Tcl_Pid *)bgPtr->procArr[i]); #endif /* TCL_MAJOR_VERSION == 7 */ #endif /* WIN32 */ } free((char *)bgPtr->procArr); } Tk_FreeOptions(configSpecs, (char *)bgPtr, bgPtr->display, 0); free(bgPtr->statVar); Tcl_ReapDetachedProcs(); free((char *)bgPtr); } /* * ---------------------------------------------------------------------- * * VariableProc -- * * Kills all currently running subprocesses (given the specified * signal). This procedure is called when the user sets the status * variable associated with this group of child subprocesses. * * Results: * Always returns NULL. Only called from a variable trace. * * Side effects: * The subprocesses are signaled for termination using the * specified kill signal. Additionally, any resources allocated * to track the subprocesses is released. * * ---------------------------------------------------------------------- */ /* ARGSUSED */ static char * VariableProc(clientData, interp, part1, part2, flags) ClientData clientData; /* File output information. */ Tcl_Interp *interp; char *part1, *part2; /* Unused */ int flags; { if (flags & TRACE_FLAGS) { BackgroundInfo *bgPtr = (BackgroundInfo *)clientData; /* Kill all child processes that remain alive. */ if ((bgPtr->procArr != NULL) && (bgPtr->signalNum > 0)) { register int i; for (i = 0; i < bgPtr->numProcs; i++) { kill(bgPtr->procArr[i], bgPtr->signalNum); } } } return NULL; } /* *---------------------------------------------------------------------- * * TimerProc -- * * This is a timer handler procedure which gets called * periodically to reap any of the sub-processes if they have * terminated. After the last process has terminated, the * contents of standard output are stored * in the output variable, which triggers the cleanup proc (using * a variable trace). The status the last process to exit is * written to the status variable. * * Results: * None. Called from the Tk event loop. * * Side effects: * Many. The contents of procArr is shifted, leaving only those * sub-processes which have not yet terminated. If there are * still subprocesses left, this procedure is placed in the timer * queue again. Otherwise the output and possibly the status * variables are updated. The former triggers the cleanup * routine which will destroy the information and resources * associated with these background processes. * *---------------------------------------------------------------------- */ static void TimerProc(clientData) ClientData clientData; { BackgroundInfo *bgPtr = (BackgroundInfo *)clientData; register int i; int lastPid, pid; WAIT_STATUS_TYPE waitStatus, lastStatus; int nLeft; /* Number of processes still not reaped */ char *mesg, mesgStr[200]; Tcl_DString dString; int code; char *result; lastPid = -1; *((int *)&waitStatus) = 0; *((int *)&lastStatus) = 0; nLeft = 0; for (i = 0; i < bgPtr->numProcs; i++) { #ifdef WIN32 pid = WaitProcess(bgPtr->procArr[i], (int *)&waitStatus, WNOHANG); #else pid = waitpid(bgPtr->procArr[i], (int *)&waitStatus, WNOHANG); #endif if (pid == 0) { /* Process has not terminated yet */ if (nLeft < i) { bgPtr->procArr[nLeft] = bgPtr->procArr[i]; } nLeft++; /* Count the number of processes left */ } else if (pid > 0) { /* * Save the status information associated with the subprocess. * We'll use it only if this is the last subprocess to be reaped. */ lastStatus = waitStatus; lastPid = pid; } } bgPtr->numProcs = nLeft; if (nLeft > 0) { /* Keep polling for the status of the children that are left */ bgPtr->timerToken = Tk_CreateTimerHandler(bgPtr->interval, TimerProc, (ClientData)bgPtr); #if WINDEBUG PurifyPrintf("schedule TimerProc(numProcs=%d)\n", nLeft); #endif return; } /* * All child processes have completed. Set the status variable * with the status of the last process reaped. The status is a * list of an error token, the exit status, and a message. */ code = WEXITSTATUS(lastStatus); Tcl_DStringInit(&dString); if (WIFEXITED(lastStatus)) { Tcl_DStringAppendElement(&dString, "EXITED"); mesg = "child completed normally"; } else if (WIFSIGNALED(lastStatus)) { Tcl_DStringAppendElement(&dString, "KILLED"); mesg = Tcl_SignalMsg((int)(WTERMSIG(lastStatus))); code = -1; } else if (WIFSTOPPED(lastStatus)) { Tcl_DStringAppendElement(&dString, "STOPPED"); mesg = Tcl_SignalMsg((int)(WSTOPSIG(lastStatus))); code = -1; } else { Tcl_DStringAppendElement(&dString, "UNKNOWN"); sprintf(mesgStr, "child completed with unknown status 0x%x", *((int *)&lastStatus)); mesg = mesgStr; } Tcl_DStringAppendElement(&dString, Blt_Itoa(lastPid)); Tcl_DStringAppendElement(&dString, Blt_Itoa(code)); Tcl_DStringAppendElement(&dString, mesg); if (bgPtr->exitCodePtr != NULL) { *bgPtr->exitCodePtr = code; } DisableTriggers(bgPtr); result = Tcl_SetVar(bgPtr->interp, bgPtr->statVar, Tcl_DStringValue(&dString), TCL_GLOBAL_ONLY); Tcl_DStringFree(&dString); if (result == NULL) { Tk_BackgroundError(bgPtr->interp); } if (bgPtr->detached) { DestroyBackgroundInfo(bgPtr); } } /* *---------------------------------------------------------------------- * * Stdoutproc -- * * This procedure is called when output from the detached command * is available. The output is read and saved in a buffer in the * BackgroundInfo structure. * * Results: * None. * * Side effects: * Data is stored in the buffer. This character array may * be increased as more space is required to contain the output * of the command. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void StdoutProc(clientData, mask) ClientData clientData; /* File output information. */ int mask; /* Not used. */ { BackgroundInfo *bgPtr = (BackgroundInfo *)clientData; if (CollectData(bgPtr, &(bgPtr->sink1)) != TCL_RETURN) { /* * We're here if we've seen EOF or an error has occurred. In * either case, set up a timer handler to periodically poll * for exit status of each process. Initially check at the * next idle interval. */ bgPtr->timerToken = Tk_CreateTimerHandler(0, TimerProc, (ClientData)bgPtr); } } /* *---------------------------------------------------------------------- * * StderrProc -- * * This procedure is called when error from the detached command * is available. The error is read and saved in a buffer in the * BackgroundInfo structure. * * Results: * None. * * Side effects: * Data is stored in the buffer. This character array may * be increased as more space is required to contain the stderr * of the command. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static void StderrProc(clientData, mask) ClientData clientData; /* File output information. */ int mask; /* Not used. */ { BackgroundInfo *bgPtr = (BackgroundInfo *)clientData; CollectData(bgPtr, &(bgPtr->sink2)); } /* *---------------------------------------------------------------------- * * BgexecCmd -- * * This procedure is invoked to process the "bgexec" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static int BgexecCmd(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { int *outFdPtr, *errFdPtr; int nProcs; Process *pidPtr; char *lastArg; Tk_Window tkwin; BackgroundInfo *bgPtr; int i; int detached; if (argc < 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " varName ?options? command ?arg...?\"", (char *)NULL); return TCL_ERROR; } /* * Check if the command is to be run detached (indicated a '&' as * the last argument of the command) */ lastArg = argv[argc - 1]; detached = ((lastArg[0] == '&') && (lastArg[1] == '\0')); if (detached) { argc--; argv[argc] = NULL; /* Remove the '&' argument */ } for (i = 2; i < argc; i += 2) { /* Count the number of option-value pairs */ if ((argv[i][0] != '-') || (argv[i][1] == '-')) { break; /* Not an option or "--" */ } } if (i > argc) { i = argc; } tkwin = Tk_MainWindow(interp); bgPtr = (BackgroundInfo *)calloc(1, sizeof(BackgroundInfo)); assert(bgPtr); /* Initialize the background information record */ bgPtr->interp = interp; bgPtr->signalNum = SIGKILL; bgPtr->numProcs = -1; bgPtr->interval = 1000; bgPtr->detached = detached; bgPtr->statVar = strdup(argv[1]); bgPtr->display = Tk_Display(tkwin); InitSink(&(bgPtr->sink1), "stdout"); InitSink(&(bgPtr->sink2), "stderr"); if (Tk_ConfigureWidget(interp, tkwin, configSpecs, i - 2, argv + 2, (char *)bgPtr, 0) != TCL_OK) { free((char *)bgPtr); return TCL_ERROR; } if (argc <= i) { Tcl_AppendResult(interp, "missing command to execute: should be \"", argv[0], " varName ?options? command ?arg...?\"", (char *)NULL); /* Try to clean up any detached processes */ Tcl_ReapDetachedProcs(); Tk_FreeOptions(configSpecs, (char *)bgPtr, bgPtr->display, 0); free((char *)bgPtr); return TCL_ERROR; } if (argv[i][0] == '-') { i++; /* If the last option was "--", skip it. */ } /* * Put a trace on the exit status variable. The will also * allow the user to prematurely terminate the pipeline by * simply setting it. */ Tcl_TraceVar(interp, bgPtr->statVar, TRACE_FLAGS, VariableProc, (ClientData)bgPtr); bgPtr->traced = TRUE; outFdPtr = errFdPtr = (int *)NULL; #ifdef WIN32 if ((bgPtr->sink1.doneVar != NULL) || (bgPtr->sink1.updateVar != NULL) || (bgPtr->sink1.updateCmd != NULL)) { outFdPtr = &(bgPtr->sink1.fd); } #else outFdPtr = &(bgPtr->sink1.fd); #endif if ((bgPtr->sink2.doneVar != NULL) || (bgPtr->sink2.updateVar != NULL) || (bgPtr->sink2.updateCmd != NULL) || (bgPtr->sink2.echo)) { errFdPtr = &(bgPtr->sink2.fd); } nProcs = Blt_CreatePipeline(interp, argc - i, argv + i, &pidPtr, (int *)NULL, outFdPtr, errFdPtr); if (nProcs < 0) { goto error; } bgPtr->procArr = pidPtr; bgPtr->numProcs = nProcs; if (bgPtr->sink1.fd == -1) { /* * If output has been redirected, start polling immediately * for the exit status of each process. Normally, this is * done only after stdout has been closed by the last process. * The default polling interval is every 1 second. */ bgPtr->timerToken = Tk_CreateTimerHandler(bgPtr->interval, TimerProc, (ClientData)bgPtr); } else if (CreateSinkHandler(bgPtr, &(bgPtr->sink1), StdoutProc) != TCL_OK) { goto error; } if ((bgPtr->sink2.fd != -1) && (CreateSinkHandler(bgPtr, &(bgPtr->sink2), StderrProc) != TCL_OK)) { goto error; } if (bgPtr->detached) { /* Return a list of the child process ids */ for (i = 0; i < nProcs; i++) { int pid; #ifdef WIN32 pid = bgPtr->procArr[i].pid; #else pid = bgPtr->procArr[i]; #endif Tcl_AppendElement(interp, Blt_Itoa(pid)); } } else { int exitCode; int done; bgPtr->exitCodePtr = &exitCode; bgPtr->donePtr = &done; exitCode = done = 0; while (!done) { Tk_DoOneEvent(0); } DisableTriggers(bgPtr); if ((exitCode == 0) && (bgPtr->sink1.doneVar == NULL)) { /* Return the output of the command */ Tcl_SetResult(interp, GetSinkData(&(bgPtr->sink1)), TCL_VOLATILE); } /* Clean up resources used. */ DestroyBackgroundInfo(bgPtr); if (exitCode != 0) { Tcl_AppendResult(interp, "child process exited abnormally", (char *)NULL); return TCL_ERROR; } } return TCL_OK; error: DisableTriggers(bgPtr); DestroyBackgroundInfo(bgPtr); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * Blt_BgexecInit -- * * This procedure is invoked to initialize the "bgexec" Tcl * command. See the user documentation for details on what it * does. * * Results: * None. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ int Blt_BgexecInit(interp) Tcl_Interp *interp; { static Blt_CmdSpec cmdSpec = {"bgexec", BgexecCmd, }; if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) { return TCL_ERROR; } return TCL_OK; } #endif /* NO_BGEXEC */ tkdesk-2.0/blt/bltBusy.c0100644000175000007640000010434110020457430013274 0ustar jccjcc/* * bltBusy.c -- * * This module implements busy windows for the BLT toolkit. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * The "busy" command was created by George Howlett. */ #include "bltInt.h" #ifndef NO_BUSY #include #include #define BUSYDEBUG 0 #ifndef TK_REPARENTED #define TK_REPARENTED 0 #endif #define BUSY_THREAD_KEY "BLT Busy Data" typedef struct { Display *display; /* Display of busy window */ Tcl_Interp *interp; /* Interpreter where "busy" command was * created. It's used to key the * searches in the window hierarchy. See the * "windows" command. */ Tk_Window busyTkWin; /* Busy window: Transparent window used * to block delivery of events to windows * underneath it. */ Tk_Window parent; /* Parent window of the busy * window. It may be the reference * window (if the reference is a * toplevel) or a mutual ancestor of * the reference window */ Tk_Window refTkWin; /* Reference window of the busy window. * It is used to manage the size and * position of the busy window. */ int x, y; /* Position of the reference window */ int width, height; /* Size of the reference window. Retained to * know if the reference window has been * reconfigured to a new size. */ int isBusy; /* Indicates whether the transparent * window should be displayed. This * can be different from what * Tk_IsMapped says because the a * sibling reference window may be * unmapped, forcing the busy window * to be also hidden. */ int menuBar; /* Menu bar flag. */ Tk_Cursor cursor; /* Cursor for the busy window. */ Tcl_HashEntry *hashPtr; /* Used the delete the busy window entry * out of the global hash table. */ } Busy; #ifdef WIN32 #define DEF_BUSY_CURSOR "wait" #else #define DEF_BUSY_CURSOR "watch" #endif static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_CURSOR, "-cursor", "busyCursor", "BusyCursor", DEF_BUSY_CURSOR, Tk_Offset(Busy, cursor), TK_CONFIG_NULL_OK}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; typedef struct ThreadData { Tcl_HashTable busyTable; /* Hash table of busy window * structures keyed by the address of * the reference Tk window */ } ThreadData; static void BusyGeometryProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static void BusyCustodyProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static Tk_GeomMgr busyMgrInfo = { "busy", /* Name of geometry manager used by winfo */ BusyGeometryProc, /* Procedure to for new geometry requests */ BusyCustodyProc, /* Procedure when window is taken away */ }; /* Forward declarations */ static void DestroyBusy _ANSI_ARGS_((DestroyData dataPtr)); static void BusyEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); #ifdef __STDC__ static Tk_EventProc BusyEventProc; static Tk_EventProc RefWinEventProc; static Tcl_CmdProc BusyCmd; static Tcl_InterpDeleteProc BusyInterpDeleteProc; #endif static void ShowBusyWindow(busyPtr) Busy *busyPtr; { busyPtr->isBusy = TRUE; if (busyPtr->busyTkWin != NULL) { Tk_MapWindow(busyPtr->busyTkWin); /* * Always restack the busy window, just in case new sibling * windows have been created. Can't use Tk_RestackWindow * because it doesn't work under Win32. */ XRaiseWindow(Tk_Display(busyPtr->busyTkWin), Tk_WindowId(busyPtr->busyTkWin)); } #ifdef WIN32 { POINT point; /* * Under Win32, cursors aren't associated with windows. Tk * fakes this by watching Motion events on its windows. So Tk * will automatically change the cursor when the pointer * enters the Busy window. But Windows doesn't immediately * change the cursor; it waits for the cursor position to * change or a system call. We need to change the cursor * before the application starts processing, so set the cursor * position redundantly back to the current position. */ GetCursorPos(&point); SetCursorPos(point.x, point.y); } #endif /* WIN32 */ } static void HideBusyWindow(busyPtr) Busy *busyPtr; { busyPtr->isBusy = FALSE; if (busyPtr->busyTkWin != NULL) { Tk_UnmapWindow(busyPtr->busyTkWin); } #ifdef WIN32 { POINT point; /* * Under Win32, cursors aren't associated with windows. Tk * fakes this by watching Motion events on its windows. So Tk * will automatically change the cursor when the pointer * enters the Busy window. But Windows doesn't immediately * change the cursor; it waits for the cursor position to * change or a system call. We need to change the cursor * before the application starts processing, so set the cursor * position redundantly back to the current position. */ GetCursorPos(&point); SetCursorPos(point.x, point.y); } #endif /* WIN32 */ } /* *---------------------------------------------------------------------- * * BusyEventProc -- * * This procedure is invoked by the Tk dispatcher for events on * the busy window itself. We're only concerned with destroy * events. * * It might be necessary (someday) to watch resize events. Right * now, I don't think there's any point in it. * * Results: * None. * * Side effects: * When a busy window is destroyed, all internal structures * associated with it released at the next idle point. * *---------------------------------------------------------------------- */ static void BusyEventProc(clientData, eventPtr) ClientData clientData; /* Busy window record */ XEvent *eventPtr; /* Event which triggered call to routine */ { Busy *busyPtr = (Busy *)clientData; if (eventPtr->type == DestroyNotify) { busyPtr->busyTkWin = NULL; Tcl_EventuallyFree((ClientData)busyPtr, DestroyBusy); } } /* * ---------------------------------------------------------------------------- * * BusyCustodyProc -- * * This procedure is invoked when the busy window has been stolen * by another geometry manager. The information and memory * associated with the busy window is released. I don't know why * anyone would try to pack a busy window, but this should keep * everything sane, if it is. * * Results: * None. * * Side effects: * The Busy structure is freed at the next idle point. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ static void BusyCustodyProc(clientData, tkwin) ClientData clientData; /* Information about the busy window. */ Tk_Window tkwin; /* Not used. */ { Busy *busyPtr = (Busy *)clientData; Tk_DeleteEventHandler(busyPtr->busyTkWin, StructureNotifyMask, BusyEventProc, (ClientData)busyPtr); HideBusyWindow(busyPtr); busyPtr->busyTkWin = NULL; Tcl_EventuallyFree((ClientData)busyPtr, DestroyBusy); } /* * ---------------------------------------------------------------------------- * * BusyGeometryProc -- * * This procedure is invoked by Tk_GeometryRequest for busy * windows. Busy windows never request geometry, so it's * unlikely that this routine will ever be called. The routine * exists simply as a place holder for the GeomProc in the * Geometry Manager structure. * * Results: * None. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ static void BusyGeometryProc(clientData, tkwin) ClientData clientData; /* Information about window that got new * preferred geometry. */ Tk_Window tkwin; /* Other Tk-related information about the * window. */ { /* Should never get here */ } /* * ------------------------------------------------------------------ * * RefWinEventProc -- * * This procedure is invoked by the Tk dispatcher for the * following events on the reference window. If the reference and * parent windows are the same, only the first event is * important. * * 1) ConfigureNotify - The reference window has been resized or * moved. Move and resize the busy window * to be the same size and position of the * reference window. * * 2) DestroyNotify - The reference window was destroyed. Destroy * the busy window and the free resources * used. * * 3) MapNotify - The reference window was (re)shown. Map the * busy window again. * * 4) UnmapNotify - The reference window was hidden. Unmap the * busy window. * * Results: * None. * * Side effects: * When the reference window gets deleted, internal structures get * cleaned up. When it gets resized, the busy window is resized * accordingly. If it's displayed, the busy window is displayed. And * when it's hidden, the busy window is unmapped. * * ------------------------------------------------------------------- */ static void RefWinEventProc(clientData, eventPtr) ClientData clientData; /* Busy window record */ register XEvent *eventPtr; /* Event which triggered call to routine */ { register Busy *busyPtr = (Busy *)clientData; switch (eventPtr->type) { case DestroyNotify: /* * Arrange for the busy structure to be removed at a proper time. */ Tcl_EventuallyFree((ClientData)busyPtr, DestroyBusy); break; case ConfigureNotify: if ((busyPtr->width != Tk_Width(busyPtr->refTkWin)) || (busyPtr->height != Tk_Height(busyPtr->refTkWin)) || (busyPtr->x != Tk_X(busyPtr->refTkWin)) || (busyPtr->y != Tk_Y(busyPtr->refTkWin))) { int x, y; busyPtr->width = Tk_Width(busyPtr->refTkWin); busyPtr->height = Tk_Height(busyPtr->refTkWin); busyPtr->x = Tk_X(busyPtr->refTkWin); busyPtr->y = Tk_Y(busyPtr->refTkWin); x = y = 0; if (busyPtr->parent != busyPtr->refTkWin) { Tk_Window ancestor; for (ancestor = busyPtr->refTkWin; ancestor != busyPtr->parent; ancestor = Tk_Parent(ancestor)) { x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width; y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width; } } #if BUSYDEBUG PurifyPrintf("menubar2: width=%d, height=%d\n", busyPtr->width, busyPtr->height); #endif if (busyPtr->busyTkWin != NULL) { Tk_MoveResizeWindow(busyPtr->busyTkWin, x, y, busyPtr->width, busyPtr->height); if (busyPtr->isBusy) { ShowBusyWindow(busyPtr); } } } break; case MapNotify: if ((busyPtr->parent != busyPtr->refTkWin) && (busyPtr->isBusy)) { ShowBusyWindow(busyPtr); } break; case UnmapNotify: if (busyPtr->parent != busyPtr->refTkWin) { HideBusyWindow(busyPtr); } break; } } /* * ------------------------------------------------------------------ * * ConfigureBusy -- * * This procedure is called from the Tk event dispatcher. It * releases X resources and memory used by the busy window and * updates the internal hash table. * * Results: * None. * * Side effects: * Memory and resources are released and the Tk event handler * is removed. * * ------------------------------------------------------------------- */ static int ConfigureBusy(interp, busyPtr, argc, argv) Tcl_Interp *interp; Busy *busyPtr; int argc; char **argv; { Tk_Cursor oldCursor; oldCursor = busyPtr->cursor; if (Tk_ConfigureWidget(interp, busyPtr->refTkWin, configSpecs, argc, argv, (char *)busyPtr, 0) != TCL_OK) { return TCL_ERROR; } if (busyPtr->cursor != oldCursor) { if (busyPtr->cursor == None) { Tk_UndefineCursor(busyPtr->busyTkWin); } else { Tk_DefineCursor(busyPtr->busyTkWin, busyPtr->cursor); } } return TCL_OK; } /* * ------------------------------------------------------------------ * * CreateBusy -- * * Creates a child transparent window that obscures its parent * window thereby effectively blocking device events. The size * and position of the busy window is exactly that of the reference * window. * * We want to create sibling to the window to be blocked. If the * busy window is a child of the window to be blocked, Enter/Leave * events can sneak through. Futhermore under WIN32, messages of * transparent windows are sent directly to the parent. The only * exception to this are toplevels, since we can't make a sibling. * Fortunately, toplevel windows rarely receive events that need * blocking. * * Results: * Returns a pointer to the new busy window structure. * * Side effects: * When the busy window is eventually displayed, it will screen * device events (in the area of the reference window) from reaching * its parent window and its children. User feed back can be * achieved by changing the cursor. * * ------------------------------------------------------------------- */ static Busy * CreateBusy(interp, refTkWin) Tcl_Interp *interp; /* Interpreter to report error to */ Tk_Window refTkWin; /* Window hosting the busy window */ { Busy *busyPtr; int length; char *fmt, *name; Tk_Window busyTkWin; Window parentWin; Tk_Window child, parent; Tk_FakeWin *winPtr; int x, y; busyPtr = (Busy *)calloc(1, sizeof(Busy)); assert(busyPtr); x = y = 0; length = strlen(Tk_Name(refTkWin)); name = (char *)malloc(length + 6); if (Tk_IsTopLevel(refTkWin)) { fmt = "_Busy"; /* Child */ parent = refTkWin; } else { Tk_Window ancestor; fmt = "%s_Busy"; /* Sibling */ parent = Tk_Parent(refTkWin); for (ancestor = refTkWin; ancestor != parent; ancestor = Tk_Parent(ancestor)) { x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width; y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width; if (Tk_IsTopLevel(ancestor)) { break; } } } for (child = Blt_FirstChild(parent); child != NULL; child = Blt_NextChild(child)) { Tk_MakeWindowExist(child); } sprintf(name, fmt, Tk_Name(refTkWin)); busyTkWin = Tk_CreateWindow(interp, parent, name, (char *)NULL); free((char *)name); if (busyTkWin == NULL) { return NULL; } Tk_MakeWindowExist(refTkWin); busyPtr->display = Tk_Display(refTkWin); busyPtr->refTkWin = refTkWin; busyPtr->parent = parent; busyPtr->interp = interp; busyPtr->width = Tk_Width(refTkWin); busyPtr->height = Tk_Height(refTkWin); busyPtr->x = Tk_X(refTkWin); busyPtr->y = Tk_Y(refTkWin); busyPtr->cursor = None; busyPtr->busyTkWin = busyTkWin; busyPtr->isBusy = FALSE; Tk_SetClass(busyTkWin, "Busy"); #if (TK_MAJOR_VERSION > 4) Blt_SetWindowInstanceData(busyTkWin, (ClientData)busyPtr); #endif winPtr = (Tk_FakeWin *) refTkWin; if (winPtr->flags & TK_REPARENTED) { /* * This works around a bug in the implementation of menubars * for non-MacIntosh window systems (Win32 and X11). Tk * doesn't reset the pointers to the parent window when the * menu is reparented (winPtr->parentPtr points to the * wrong window). We get around this by determining the parent * via the native API calls. */ #ifdef WIN32 { HWND hWnd; RECT region; hWnd = GetParent(Tk_GetHWND(Tk_WindowId(refTkWin))); parentWin = (Window) hWnd; if (GetWindowRect(hWnd, ®ion)) { busyPtr->width = region.right - region.left; busyPtr->height = region.bottom - region.top; #if BUSYDEBUG PurifyPrintf("menubar: width=%d, height=%d\n", busyPtr->width, busyPtr->height); #endif } } #else parentWin = Blt_GetParent(Tk_Display(refTkWin), Tk_WindowId(refTkWin)); #endif } else { parentWin = Tk_WindowId(parent); #ifdef WIN32 parentWin = (Window) Tk_GetHWND(parentWin); #endif } Blt_MakeTransparentWindowExist(busyTkWin, parentWin, TRUE); #if BUSYDEBUG PurifyPrintf("menubar1: width=%d, height=%d\n", busyPtr->width, busyPtr->height); #endif Tk_MoveResizeWindow(busyTkWin, x, y, busyPtr->width, busyPtr->height); /* Only worry if the busy window is destroyed. */ Tk_CreateEventHandler(busyTkWin, StructureNotifyMask, BusyEventProc, (ClientData)busyPtr); /* * Indicate that the busy window's geometry is being managed. * This will also notify us if the busy window is ever packed. */ Tk_ManageGeometry(busyTkWin, &busyMgrInfo, (ClientData)busyPtr); if (busyPtr->cursor != None) { Tk_DefineCursor(busyTkWin, busyPtr->cursor); } /* Track the reference window to see if it is resized or destroyed. */ Tk_CreateEventHandler(refTkWin, StructureNotifyMask, RefWinEventProc, (ClientData)busyPtr); return (busyPtr); } /* * ------------------------------------------------------------------ * * DestroyBusy -- * * This procedure is called from the Tk event dispatcher. It * releases X resources and memory used by the busy window and * updates the internal hash table. * * Results: * None. * * Side effects: * Memory and resources are released and the Tk event handler * is removed. * * ------------------------------------------------------------------- */ static void DestroyBusy(dataPtr) DestroyData dataPtr; /* Busy window structure record */ { Busy *busyPtr = (Busy *)dataPtr; Tk_FreeOptions(configSpecs, (char *)busyPtr, busyPtr->display, 0); if (busyPtr->hashPtr != NULL) { Tcl_DeleteHashEntry(busyPtr->hashPtr); } Tk_DeleteEventHandler(busyPtr->refTkWin, StructureNotifyMask, RefWinEventProc, (ClientData)busyPtr); if (busyPtr->busyTkWin != NULL) { Tk_DeleteEventHandler(busyPtr->busyTkWin, StructureNotifyMask, BusyEventProc, (ClientData)busyPtr); Tk_ManageGeometry(busyPtr->busyTkWin, (Tk_GeomMgr *) NULL, (ClientData)busyPtr); Tk_DestroyWindow(busyPtr->busyTkWin); } free((char *)busyPtr); } /* * ------------------------------------------------------------------ * * GetBusy -- * * Returns the busy window structure associated with the reference * window, keyed by its path name. The clientData argument is * the main window of the interpreter, used to search for the * reference window in its own window hierarchy. * * Results: * If path name represents a reference window with a busy window, a * pointer to the busy window structure is returned. Otherwise, * NULL is returned and an error message is left in * interp->result. * * ------------------------------------------------------------------- */ static int GetBusy(dataPtr, interp, pathName, busyPtrPtr) ThreadData *dataPtr; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ char *pathName; /* Path name of parent window */ Busy **busyPtrPtr; /* Will contain address of busy window if * found. */ { Tcl_HashEntry *hPtr; Tk_Window tkwin; tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_FindHashEntry(&(dataPtr->busyTable), (char *)tkwin); if (hPtr == NULL) { Tcl_AppendResult(interp, "can't find busy window \"", pathName, "\"", (char *)NULL); return TCL_ERROR; } *busyPtrPtr = ((Busy *)Tcl_GetHashValue(hPtr)); return TCL_OK; } /* * ------------------------------------------------------------------ * * HoldBusy -- * * Creates (if necessary) and maps a busy window, thereby * preventing device events from being be received by the parent * window and its children. * * Results: * Returns a standard TCL result. If path name represents a busy * window, it is unmapped and TCL_OK is returned. Otherwise, * TCL_ERROR is returned and an error message is left in * interp->result. * * Side effects: * The busy window is created and displayed, blocking events from * the parent window and its children. * * ------------------------------------------------------------------- */ static int HoldBusy(dataPtr, interp, argc, argv) ThreadData *dataPtr; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Window name and option pairs */ { Tk_Window tkwin; Tcl_HashEntry *hPtr; Busy *busyPtr; int isNew; int result; tkwin = Tk_NameToWindow(interp, argv[0], Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_CreateHashEntry(&(dataPtr->busyTable), (char *)tkwin, &isNew); if (isNew) { busyPtr = (Busy *)CreateBusy(interp, tkwin); if (busyPtr == NULL) { return TCL_ERROR; } Tcl_SetHashValue(hPtr, (char *)busyPtr); busyPtr->hashPtr = hPtr; } else { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); } result = ConfigureBusy(interp, busyPtr, argc - 1, argv + 1); /* Don't map the busy window unless the reference window is also * displayed */ if (Tk_IsMapped(busyPtr->refTkWin)) { ShowBusyWindow(busyPtr); } else { HideBusyWindow(busyPtr); } return result; } /* * ------------------------------------------------------------------ * * StatusOp -- * * Returns the status of the busy window; whether it's blocking * events or not. * * Results: * Returns a standard TCL result. If path name represents a busy * window, the status is returned via interp->result and TCL_OK * is returned. Otherwise, TCL_ERROR is returned and an error * message is left in interp->result. * * ------------------------------------------------------------------- */ /*ARGSUSED*/ static int StatusOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report error to */ int argc; /* Not used. */ char **argv; { ThreadData *dataPtr = (ThreadData *)clientData; Busy *busyPtr; if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) { return TCL_ERROR; } Tcl_Preserve((ClientData)busyPtr); Tcl_SetResult(interp, busyPtr->isBusy ? "1" : "0", TCL_STATIC); Tcl_Release((ClientData)busyPtr); return TCL_OK; } /* * ------------------------------------------------------------------ * * ForgetOp -- * * Destroys the busy window associated with the reference window and * arranges for internal resources to the released when they're * not being used anymore. * * Results: * Returns a standard TCL result. If path name represents a busy * window, it is destroyed and TCL_OK is returned. Otherwise, * TCL_ERROR is returned and an error message is left in * interp->result. * * Side effects: * The busy window is removed. Other related memory and resources * are eventually released by the Tk dispatcher. * * ------------------------------------------------------------------- */ static int ForgetOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { ThreadData *dataPtr = (ThreadData *)clientData; Busy *busyPtr; register int i; for (i = 2; i < argc; i++) { if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) { return TCL_ERROR; } /* Unmap the window even though it will be soon destroyed */ HideBusyWindow(busyPtr); Tcl_EventuallyFree((ClientData)busyPtr, DestroyBusy); } return TCL_OK; } /* * ------------------------------------------------------------------ * * ReleaseOp -- * * Unmaps the busy window, thereby permitting device events * to be received by the parent window and its children. * * Results: * Returns a standard TCL result. If path name represents a busy * window, it is unmapped and TCL_OK is returned. Otherwise, * TCL_ERROR is returned and an error message is left in * interp->result. * * Side effects: * The busy window is hidden, allowing the parent window and * its children to receive events again. * * ------------------------------------------------------------------- */ static int ReleaseOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { ThreadData *dataPtr = (ThreadData *)clientData; Busy *busyPtr; int i; for (i = 2; i < argc; i++) { if (GetBusy(dataPtr, interp, argv[i], &busyPtr) != TCL_OK) { return TCL_ERROR; } HideBusyWindow(busyPtr); } return TCL_OK; } /* * ------------------------------------------------------------------ * * WindowsOp -- * * Reports the names of all widgets with busy windows attached to * them, matching a given pattern. If no pattern is given, all * busy widgets are listed. * * Results: * Returns a TCL list of the names of the widget with busy windows * attached to them, regardless if the widget is currently busy * or not. * * ------------------------------------------------------------------- */ static int WindowsOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { ThreadData *dataPtr = (ThreadData *)clientData; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; Busy *busyPtr; for (hPtr = Tcl_FirstHashEntry(&(dataPtr->busyTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); if (busyPtr->interp == interp) { if ((argc != 3) || (Tcl_StringMatch(Tk_PathName(busyPtr->refTkWin), argv[2]))) { Tcl_AppendElement(interp, Tk_PathName(busyPtr->refTkWin)); } } } return TCL_OK; } /* * ------------------------------------------------------------------ * * IsBusyOp -- * * Reports the names of all widgets with busy windows attached to * them, matching a given pattern. If no pattern is given, all * busy widgets are listed. * * Results: * Returns a TCL list of the names of the widget with busy windows * attached to them, regardless if the widget is currently busy * or not. * * ------------------------------------------------------------------- */ static int IsBusyOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; { ThreadData *dataPtr = (ThreadData *)clientData; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; Busy *busyPtr; for (hPtr = Tcl_FirstHashEntry(&(dataPtr->busyTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); if (busyPtr->interp == interp) { if ((busyPtr->isBusy) && ((argc != 3) || (Tcl_StringMatch(Tk_PathName(busyPtr->refTkWin), argv[2])))) { Tcl_AppendElement(interp, Tk_PathName(busyPtr->refTkWin)); } } } return TCL_OK; } /* * ------------------------------------------------------------------ * * HoldOp -- * * Creates (if necessary) and maps a busy window, thereby * preventing device events from being be received by the parent * window and its children. The argument vector may contain * option-value pairs of configuration options to be set. * * Results: * Returns a standard TCL result. * * Side effects: * The busy window is created and displayed, blocking events from the * parent window and its children. * * ------------------------------------------------------------------- */ static int HoldOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Window name and option pairs */ { ThreadData *dataPtr = (ThreadData *)clientData; register int i, count; if ((argv[1][0] == 'h') && (strcmp(argv[1], "hold") == 0)) { argc--, argv++; /* Command used "hold" keyword */ } for (i = 1; i < argc; i++) { /* * Find the end of the option-value pairs for this window. */ for (count = i + 1; count < argc; count += 2) { if (argv[count][0] != '-') { break; } } if (count > argc) { count = argc; } if (HoldBusy(dataPtr, interp, count - i, argv + i) != TCL_OK) { return TCL_ERROR; } i = count; } return TCL_OK; } /* ARGSUSED*/ static int CgetOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Widget pathname and option switch */ { ThreadData *dataPtr = (ThreadData *)clientData; Busy *busyPtr; int result; if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) { return TCL_ERROR; } Tcl_Preserve((ClientData)busyPtr); result = Tk_ConfigureValue(interp, busyPtr->refTkWin, configSpecs, (char *)busyPtr, argv[3], 0); Tcl_Release((ClientData)busyPtr); return result; } /* *---------------------------------------------------------------------- * * ConfigureOp -- * * This procedure is called to process an argv/argc list in order * to configure (or reconfigure) a busy window. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is * returned, then interp->result contains an error message. * * Side effects: * Configuration information get set for busyPtr; old resources * get freed, if there were any. The busy window destroyed and * recreated in a new parent window. * *---------------------------------------------------------------------- */ static int ConfigureOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Reference window path name and options */ { ThreadData *dataPtr = (ThreadData *)clientData; Busy *busyPtr; int result; if (GetBusy(dataPtr, interp, argv[2], &busyPtr) != TCL_OK) { return TCL_ERROR; } Tcl_Preserve((ClientData)busyPtr); if (argc == 3) { result = Tk_ConfigureInfo(interp, busyPtr->refTkWin, configSpecs, (char *)busyPtr, (char *)NULL, 0); } else if (argc == 4) { result = Tk_ConfigureInfo(interp, busyPtr->refTkWin, configSpecs, (char *)busyPtr, argv[3], 0); } else { result = ConfigureBusy(interp, busyPtr, argc - 3, argv + 3); } Tcl_Release((ClientData)busyPtr); return result; } /* * ----------------------------------------------------------------------- * * BusyInterpDeleteProc -- * * This is called when the interpreter hosting the "busy" command * is destroyed. * * Results: * None. * * Side effects: * Destroys all the hash table managing the busy windows. * * ------------------------------------------------------------------------ */ /* ARGSUSED */ static void BusyInterpDeleteProc(clientData, interp) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; { ThreadData *dataPtr = (ThreadData *)clientData; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; Busy *busyPtr; for (hPtr = Tcl_FirstHashEntry(&(dataPtr->busyTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { busyPtr = (Busy *)Tcl_GetHashValue(hPtr); busyPtr->hashPtr = NULL; DestroyBusy((DestroyData)busyPtr); } Tcl_DeleteHashTable(&(dataPtr->busyTable)); Tcl_DeleteAssocData(interp, BUSY_THREAD_KEY); free((char *)dataPtr); } /* *-------------------------------------------------------------- * * Busy Sub-command specification: * * - Name of the sub-command. * - Minimum number of characters needed to unambiguously * recognize the sub-command. * - Pointer to the function to be called for the sub-command. * - Minimum number of arguments accepted. * - Maximum number of arguments accepted. * - String to be displayed for usage (arguments only). * *-------------------------------------------------------------- */ static Blt_OpSpec busyOps[] = { {"cget", 2, (Blt_Operation)CgetOp, 4, 4, "window option",}, {"configure", 2, (Blt_Operation)ConfigureOp, 3, 0, "window ?options?...",}, {"forget", 1, (Blt_Operation)ForgetOp, 2, 0, "?window?...",}, {"hold", 3, (Blt_Operation)HoldOp, 3, 0, "window ?options?... ?window options?...",}, {"isbusy", 1, (Blt_Operation)IsBusyOp, 2, 3, "?pattern?",}, {"release", 1, (Blt_Operation)ReleaseOp, 2, 0, "?window?...",}, {"status", 1, (Blt_Operation)StatusOp, 3, 3, "window",}, {"windows", 1, (Blt_Operation)WindowsOp, 2, 3, "?pattern?",}, }; static int nBusyOps = sizeof(busyOps) / sizeof(Blt_OpSpec); /* *---------------------------------------------------------------------- * * BusyCmd -- * * This procedure is invoked to process the "busy" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ static int BusyCmd(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* Interpreter associated with command */ int argc; char **argv; { Blt_Operation proc; int result; if ((argc > 1) && (argv[1][0] == '.')) { return (HoldOp(clientData, interp, argc, argv)); } proc = Blt_GetOperation(interp, nBusyOps, busyOps, BLT_OPER_ARG1, argc, argv); if (proc == NULL) { return TCL_ERROR; } result = (*proc) (clientData, interp, argc, argv); return (result); } static ThreadData * GetBusyCmdData(interp) Tcl_Interp *interp; { ThreadData *dataPtr; Tcl_InterpDeleteProc *proc; dataPtr = (ThreadData *)Tcl_GetAssocData(interp, BUSY_THREAD_KEY, &proc); if (dataPtr == NULL) { dataPtr = (ThreadData *)malloc(sizeof(ThreadData)); assert(dataPtr); Tcl_SetAssocData(interp, BUSY_THREAD_KEY, BusyInterpDeleteProc, (ClientData)dataPtr); Tcl_InitHashTable(&(dataPtr->busyTable), TCL_ONE_WORD_KEYS); } return dataPtr; } int Blt_BusyInit(interp) Tcl_Interp *interp; { static Blt_CmdSpec cmdSpec = {"busy", BusyCmd, }; ThreadData *dataPtr; dataPtr = GetBusyCmdData(interp); cmdSpec.clientData = (ClientData)dataPtr; if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) { return TCL_ERROR; } return TCL_OK; } #endif /* NO_BUSY */ tkdesk-2.0/blt/bltChain.c0100644000175000007640000002355010020457430013376 0ustar jccjcc/* * bltChain.c -- * * The module implements a generic linked list package. * * Copyright 1991-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #include "bltInt.h" #include "bltChain.h" /* *---------------------------------------------------------------------- * * Blt_ChainCreate -- * * Creates a new linked list structure and initializes its pointers * * Results: * Returns a pointer to the newly created list structure. * *---------------------------------------------------------------------- */ Blt_Chain * Blt_ChainCreate() { Blt_Chain *chainPtr; chainPtr = (Blt_Chain *) malloc(sizeof(Blt_Chain)); if (chainPtr != NULL) { Blt_ChainInit(chainPtr); } return (chainPtr); } /* *---------------------------------------------------------------------- * * Blt_ChainNewLink -- * * Creates a list entry holder. This routine does not insert * the entry into the chain, nor does it no attempt to maintain * consistency of the keys. For example, more than one entry * may use the same key. * * Results: * The return value is the pointer to the newly created entry. * * Side Effects: * The key is not copied, only the Uid is kept. It is assumed * this key will not change in the life of the entry. * *---------------------------------------------------------------------- */ Blt_ChainLink * Blt_ChainNewLink() { Blt_ChainLink *linkPtr; linkPtr = (Blt_ChainLink *) malloc(sizeof(Blt_ChainLink)); assert(linkPtr); linkPtr->clientData = (ClientData)NULL; linkPtr->nextPtr = linkPtr->prevPtr = NULL; return linkPtr; } /* *---------------------------------------------------------------------- * * Blt_ChainReset -- * * Removes all the entries from a chain, removing pointers to the * objects and keys (not the objects or keys themselves). The * entry counter is reset to zero. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ChainReset(chainPtr) Blt_Chain *chainPtr; /* Chain to clear */ { if (chainPtr != NULL) { Blt_ChainLink *oldPtr; Blt_ChainLink *linkPtr = chainPtr->headPtr; while (linkPtr != NULL) { oldPtr = linkPtr; linkPtr = linkPtr->nextPtr; free((char *)oldPtr); } Blt_ChainInit(chainPtr); } } /* *---------------------------------------------------------------------- * * Blt_ChainDestroy * * Frees all chain structures * * Results: * Returns a pointer to the newly created chain structure. * *---------------------------------------------------------------------- */ void Blt_ChainDestroy(chainPtr) Blt_Chain *chainPtr; { if (chainPtr != NULL) { Blt_ChainReset(chainPtr); free((char *)chainPtr); } } /* *---------------------------------------------------------------------- * * Blt_ChainInit -- * * Initializes a linked list. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ChainInit(chainPtr) Blt_Chain *chainPtr; { chainPtr->nLinks = 0; chainPtr->headPtr = chainPtr->tailPtr = NULL; } /* *---------------------------------------------------------------------- * * Blt_ChainLinkAfter -- * * Inserts an entry following a given entry. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ChainLinkAfter(chainPtr, linkPtr, afterPtr) Blt_Chain *chainPtr; Blt_ChainLink *linkPtr, *afterPtr; { if (chainPtr->headPtr == NULL) { chainPtr->tailPtr = chainPtr->headPtr = linkPtr; } else { if (afterPtr == NULL) { /* Prepend to the front of the chain */ linkPtr->nextPtr = chainPtr->headPtr; linkPtr->prevPtr = NULL; chainPtr->headPtr->prevPtr = linkPtr; chainPtr->headPtr = linkPtr; } else { linkPtr->nextPtr = afterPtr->nextPtr; linkPtr->prevPtr = afterPtr; if (afterPtr == chainPtr->tailPtr) { chainPtr->tailPtr = linkPtr; } else { afterPtr->nextPtr->prevPtr = linkPtr; } afterPtr->nextPtr = linkPtr; } } chainPtr->nLinks++; } /* *---------------------------------------------------------------------- * * Blt_ChainLinkBefore -- * * Inserts an entry preceding a given entry. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ChainLinkBefore(chainPtr, linkPtr, beforePtr) Blt_Chain *chainPtr; /* Chain to contain new entry */ Blt_ChainLink *linkPtr; /* New entry to be inserted */ Blt_ChainLink *beforePtr; /* Entry to link before */ { if (chainPtr->headPtr == NULL) { chainPtr->tailPtr = chainPtr->headPtr = linkPtr; } else { if (beforePtr == NULL) { /* Append onto the end of the chain */ linkPtr->nextPtr = NULL; linkPtr->prevPtr = chainPtr->tailPtr; chainPtr->tailPtr->nextPtr = linkPtr; chainPtr->tailPtr = linkPtr; } else { linkPtr->prevPtr = beforePtr->prevPtr; linkPtr->nextPtr = beforePtr; if (beforePtr == chainPtr->headPtr) { chainPtr->headPtr = linkPtr; } else { beforePtr->prevPtr->nextPtr = linkPtr; } beforePtr->prevPtr = linkPtr; } } chainPtr->nLinks++; } /* *---------------------------------------------------------------------- * * Blt_ChainUnlinkLink -- * * Unlinks an entry from the given chain. The entry itself is * not deallocated, but only removed from the chain. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ChainUnlinkLink(chainPtr, linkPtr) Blt_Chain *chainPtr; Blt_ChainLink *linkPtr; { if (chainPtr->headPtr == linkPtr) { chainPtr->headPtr = linkPtr->nextPtr; } if (chainPtr->tailPtr == linkPtr) { chainPtr->tailPtr = linkPtr->prevPtr; } if (linkPtr->nextPtr != NULL) { linkPtr->nextPtr->prevPtr = linkPtr->prevPtr; } if (linkPtr->prevPtr != NULL) { linkPtr->prevPtr->nextPtr = linkPtr->nextPtr; } chainPtr->nLinks--; } /* *---------------------------------------------------------------------- * * Blt_ChainDeleteLink -- * * Unlinks and deletes the given entry. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ChainDeleteLink(chainPtr, linkPtr) Blt_Chain *chainPtr; Blt_ChainLink *linkPtr; { Blt_ChainUnlinkLink(chainPtr, linkPtr); free((char *)linkPtr); } Blt_ChainLink * Blt_ChainAppend(chainPtr, clientData) Blt_Chain *chainPtr; ClientData clientData; { Blt_ChainLink *linkPtr; linkPtr = Blt_ChainNewLink(); Blt_ChainSetValue(linkPtr, clientData); Blt_ChainAppendLink(chainPtr, linkPtr); return linkPtr; } Blt_ChainLink * Blt_ChainPrepend(chainPtr, clientData) Blt_Chain *chainPtr; ClientData clientData; { Blt_ChainLink *linkPtr; linkPtr = Blt_ChainNewLink(); Blt_ChainSetValue(linkPtr, clientData); Blt_ChainPrependLink(chainPtr, linkPtr); return linkPtr; } /* *---------------------------------------------------------------------- * * Blt_ChainGetNthLink -- * * Find the entry based upon a given position in chain. * * Results: * Returns the pointer to the link, if that numbered element * exists. Otherwise NULL. * *---------------------------------------------------------------------- */ Blt_ChainLink * Blt_ChainGetNthLink(chainPtr, position) Blt_Chain *chainPtr; /* Chain to traverse */ int position; /* Index of link to select from front * or back of the chain. */ { Blt_ChainLink *linkPtr; if (chainPtr != NULL) { for (linkPtr = chainPtr->headPtr; linkPtr != NULL; linkPtr = linkPtr->nextPtr) { if (position == 0) { return linkPtr; } position--; } } return NULL; } /* *---------------------------------------------------------------------- * * Blt_ChainSort -- * * Find the entry based upon a given position in chain. * * Results: * Returns the pointer to the link, if that numbered element * exists. Otherwise NULL. * *---------------------------------------------------------------------- */ void Blt_ChainSort(chainPtr, proc) Blt_Chain *chainPtr; /* Chain to traverse */ Blt_ChainCompareProc *proc; { Blt_ChainLink **linkArr; register Blt_ChainLink *linkPtr; register int i; if (chainPtr->nLinks < 2) { return; } linkArr = (Blt_ChainLink **) malloc(sizeof(Blt_ChainLink *) * (chainPtr->nLinks + 1)); if (linkArr == NULL) { return; /* Out of memory. */ } i = 0; for (linkPtr = chainPtr->headPtr; linkPtr != NULL; linkPtr = linkPtr->nextPtr) { linkArr[i++] = linkPtr; } qsort((char *)linkArr, chainPtr->nLinks, sizeof(Blt_ChainLink *), (QSortCompareProc *)proc); /* Rethread the chain. */ linkPtr = linkArr[0]; chainPtr->headPtr = linkPtr; linkPtr->prevPtr = NULL; for (i = 1; i < chainPtr->nLinks; i++) { linkPtr->nextPtr = linkArr[i]; linkPtr->nextPtr->prevPtr = linkPtr; linkPtr = linkPtr->nextPtr; } chainPtr->tailPtr = linkPtr; linkPtr->nextPtr = NULL; free((char *)linkArr); } tkdesk-2.0/blt/bltChain.h0100644000175000007640000000676310020457430013412 0ustar jccjcc/* * bltChain.h -- * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #ifndef _BLT_CHAIN_H #define _BLT_CHAIN_H typedef struct Blt_Chain Blt_Chain; /* * A Blt_ChainLink is the container structure for the Blt_Chain. */ typedef struct Blt_ChainLink { struct Blt_ChainLink *prevPtr; /* Link to the previous link */ struct Blt_ChainLink *nextPtr; /* Link to the next link */ ClientData clientData; /* Pointer to the data object */ } Blt_ChainLink; typedef int (Blt_ChainCompareProc) _ANSI_ARGS_((Blt_ChainLink **, Blt_ChainLink **)); /* * A Blt_Chain is a doubly chained list structure. */ struct Blt_Chain { Blt_ChainLink *headPtr; /* Pointer to first element in chain */ Blt_ChainLink *tailPtr; /* Pointer to last element in chain */ int nLinks; /* Number of elements in chain */ }; extern void Blt_ChainInit _ANSI_ARGS_((Blt_Chain * chainPtr)); extern Blt_Chain *Blt_ChainCreate _ANSI_ARGS_(()); extern void Blt_ChainDestroy _ANSI_ARGS_((Blt_Chain * chainPtr)); extern Blt_ChainLink *Blt_ChainNewLink _ANSI_ARGS_((void)); extern Blt_ChainLink *Blt_ChainAppend _ANSI_ARGS_((Blt_Chain * chainPtr, ClientData clientData)); extern Blt_ChainLink *Blt_ChainPrepend _ANSI_ARGS_((Blt_Chain * chainPtr, ClientData clientData)); extern void Blt_ChainReset _ANSI_ARGS_((Blt_Chain * chainPtr)); extern void Blt_ChainLinkAfter _ANSI_ARGS_((Blt_Chain * chainPtr, Blt_ChainLink * linkPtr, Blt_ChainLink * afterLinkPtr)); extern void Blt_ChainLinkBefore _ANSI_ARGS_((Blt_Chain * chainPtr, Blt_ChainLink * linkPtr, Blt_ChainLink * beforeLinkPtr)); extern void Blt_ChainUnlinkLink _ANSI_ARGS_((Blt_Chain * chainPtr, Blt_ChainLink * linkPtr)); extern void Blt_ChainDeleteLink _ANSI_ARGS_((Blt_Chain * chainPtr, Blt_ChainLink * linkPtr)); extern Blt_ChainLink *Blt_ChainGetNthLink _ANSI_ARGS_((Blt_Chain * chainPtr, int n)); extern void Blt_ChainSort _ANSI_ARGS_((Blt_Chain * chainPtr, Blt_ChainCompareProc * proc)); #define Blt_ChainGetLength(c) (((c) == NULL) ? 0 : (c)->nLinks) #define Blt_ChainFirstLink(c) (((c) == NULL) ? NULL : (c)->headPtr) #define Blt_ChainLastLink(c) (((c) == NULL) ? NULL : (c)->tailPtr) #define Blt_ChainPrevLink(l) ((l)->prevPtr) #define Blt_ChainNextLink(l) ((l)->nextPtr) #define Blt_ChainGetValue(l) ((l)->clientData) #define Blt_ChainSetValue(l, value) ((l)->clientData = (ClientData)(value)) #define Blt_ChainAppendLink(c, l) \ (Blt_ChainLinkBefore((c), (l), (Blt_ChainLink *)NULL)) #define Blt_ChainPrependLink(c, l) \ (Blt_ChainLinkAfter((c), (l), (Blt_ChainLink *)NULL)) #endif /* _BLT_CHAIN_H */ tkdesk-2.0/blt/bltConfig.c0100644000175000007640000007372110020457430013566 0ustar jccjcc/* * bltConfig.c -- * * This module implements custom configuration options for the BLT * toolkit. * * Copyright 1991-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #include "bltInt.h" #include static int StringToFill _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int flags)); static char *FillToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); Tk_CustomOption bltFillOption = { StringToFill, FillToString, (ClientData)0 }; static int StringToPad _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *PadToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)); Tk_CustomOption bltPadOption = { StringToPad, PadToString, (ClientData)0 }; static int StringToDistance _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int flags)); static char *DistanceToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); Tk_CustomOption bltDistanceOption = { StringToDistance, DistanceToString, (ClientData)PIXELS_NONNEGATIVE }; Tk_CustomOption bltPositiveDistanceOption = { StringToDistance, DistanceToString, (ClientData)PIXELS_POSITIVE }; Tk_CustomOption bltAnyDistanceOption = { StringToDistance, DistanceToString, (ClientData)PIXELS_ANY }; static int StringToCount _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int flags)); static char *CountToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); Tk_CustomOption bltCountOption = { StringToCount, CountToString, (ClientData)COUNT_NONNEGATIVE }; Tk_CustomOption bltPositiveCountOption = { StringToCount, CountToString, (ClientData)COUNT_POSITIVE }; static int StringToDashes _ANSI_ARGS_((ClientData, Tcl_Interp *, Tk_Window, char *, char *, int)); static char *DashesToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); Tk_CustomOption bltDashesOption = { StringToDashes, DashesToString, (ClientData)0 }; static int StringToShadow _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *ShadowToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)); Tk_CustomOption bltShadowOption = { StringToShadow, ShadowToString, (ClientData)0 }; static int StringToUid _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int flags)); static char *UidToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); Tk_CustomOption bltUidOption = { StringToUid, UidToString, (ClientData)0 }; static int StringToState _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int flags)); static char *StateToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); Tk_CustomOption bltStateOption = { StringToState, StateToString, (ClientData)0 }; static int StringToList _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int flags)); static char *ListToString _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); Tk_CustomOption bltListOption = { StringToList, ListToString, (ClientData)0 }; /* *---------------------------------------------------------------------- * * Blt_NameOfFill -- * * Converts the integer representing the fill style into a string. * *---------------------------------------------------------------------- */ char * Blt_NameOfFill(fill) Fill fill; { switch (fill) { case FILL_X: return "x"; case FILL_Y: return "y"; case FILL_NONE: return "none"; case FILL_BOTH: return "both"; default: return "unknown value"; } } /* *---------------------------------------------------------------------- * * StringToFill -- * * Converts the fill style string into its numeric representation. * * Valid style strings are: * * "none" Use neither plane. * "x" X-coordinate plane. * "y" Y-coordinate plane. * "both" Use both coordinate planes. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToFill(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Fill style string */ char *widgRec; /* Cubicle structure record */ int offset; /* Offset of style in record */ { Fill *fillPtr = (Fill *)(widgRec + offset); unsigned int length; char c; c = string[0]; length = strlen(string); if ((c == 'n') && (strncmp(string, "none", length) == 0)) { *fillPtr = FILL_NONE; } else if ((c == 'x') && (strncmp(string, "x", length) == 0)) { *fillPtr = FILL_X; } else if ((c == 'y') && (strncmp(string, "y", length) == 0)) { *fillPtr = FILL_Y; } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) { *fillPtr = FILL_BOTH; } else { Tcl_AppendResult(interp, "bad argument \"", string, "\": should be \"none\", \"x\", \"y\", or \"both\"", (char *)NULL); return TCL_ERROR; } return (TCL_OK); } /* *---------------------------------------------------------------------- * * FillToString -- * * Returns the fill style string based upon the fill flags. * * Results: * The fill style string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * FillToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record */ int offset; /* Offset of fill in widget record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { Fill fill = *(Fill *)(widgRec + offset); return (Blt_NameOfFill(fill)); } /* *---------------------------------------------------------------------- * * Blt_StringToFlag -- * * Converts the fill style string into its numeric representation. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ int Blt_StringToFlag(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Bit mask to be tested in status word */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Fill style string */ char *widgRec; /* Cubicle structure record */ int offset; /* Offset of style in record */ { unsigned int mask = (unsigned int)clientData; /* Bit to be tested */ int *flagPtr = (int *)(widgRec + offset); int bool; if (Tcl_GetBoolean(interp, string, &bool) != TCL_OK) { return TCL_ERROR; } if (bool) { *flagPtr |= mask; } else { *flagPtr &= ~mask; } return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_FlagToString -- * * Returns the fill style string based upon the fill flags. * * Results: * The fill style string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ char * Blt_FlagToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Bit mask to be test in status word */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record */ int offset; /* Offset of fill in widget record */ Tcl_FreeProc **freeProcPtr; /* Unused */ { unsigned int mask = (unsigned int)clientData; /* Bit to be tested */ unsigned int bool = *(unsigned int *)(widgRec + offset); return (bool & mask) ? "1" : "0"; } /* *---------------------------------------------------------------------- * * Blt_GetPixels -- * * Like Tk_GetPixels, but checks for negative, zero. * * Results: * A standard Tcl result. * *---------------------------------------------------------------------- */ int Blt_GetPixels(interp, tkwin, string, check, valuePtr) Tcl_Interp *interp; Tk_Window tkwin; char *string; int check; /* Can be PIXELS_POSITIVE, PIXELS_NONNEGATIVE, * or PIXELS_ANY, */ int *valuePtr; { int length; if (Tk_GetPixels(interp, tkwin, string, &length) != TCL_OK) { return TCL_ERROR; } if (length >= SHRT_MAX) { Tcl_AppendResult(interp, "bad distance \"", string, "\": ", "too big to represent", (char *)NULL); return TCL_ERROR; } switch (check) { case PIXELS_NONNEGATIVE: if (length < 0) { Tcl_AppendResult(interp, "bad distance \"", string, "\": ", "can't be negative", (char *)NULL); return TCL_ERROR; } break; case PIXELS_POSITIVE: if (length <= 0) { Tcl_AppendResult(interp, "bad distance \"", string, "\": ", "must be positive", (char *)NULL); return TCL_ERROR; } break; case PIXELS_ANY: break; } *valuePtr = length; return TCL_OK; } /* *---------------------------------------------------------------------- * * StringToDistance -- * * Like TK_CONFIG_PIXELS, but adds an extra check for negative * values. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToDistance(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Indicated how to check distance */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Window */ char *string; /* Pixel value string */ char *widgRec; /* Widget record */ int offset; /* Offset of pixel size in record */ { int *valuePtr = (int *)(widgRec + offset); return Blt_GetPixels(interp, tkwin, string, (int)clientData, valuePtr); } /* *---------------------------------------------------------------------- * * DistanceToString -- * * Returns the string representing the positive pixel size. * * Results: * The pixel size string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * DistanceToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record */ int offset; /* Offset in widget record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { int value = *(int *)(widgRec + offset); char *result; result = strdup(Blt_Itoa(value)); assert(result); *freeProcPtr = (Tcl_FreeProc *)free; return (result); } int Blt_GetInt(interp, string, check, valuePtr) Tcl_Interp *interp; char *string; int check; /* Can be COUNT_POSITIVE, COUNT_NONNEGATIVE, * or COUNT_ANY, */ int *valuePtr; { int count; if (Tcl_GetInt(interp, string, &count) != TCL_OK) { return TCL_ERROR; } switch (check) { case COUNT_NONNEGATIVE: if (count < 0) { Tcl_AppendResult(interp, "bad value \"", string, "\": ", "can't be negative", (char *)NULL); return TCL_ERROR; } break; case COUNT_POSITIVE: if (count <= 0) { Tcl_AppendResult(interp, "bad value \"", string, "\": ", "must be positive", (char *)NULL); return TCL_ERROR; } break; case COUNT_ANY: break; } *valuePtr = count; return TCL_OK; } /* *---------------------------------------------------------------------- * * StringToCount -- * * Like TK_CONFIG_PIXELS, but adds an extra check for negative * values. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToCount(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Indicated how to check distance */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Pixel value string */ char *widgRec; /* Widget record */ int offset; /* Offset of pixel size in record */ { int *valuePtr = (int *)(widgRec + offset); return Blt_GetInt(interp, string, (int)clientData, valuePtr); } /* *---------------------------------------------------------------------- * * CountToString -- * * Returns the string representing the positive pixel size. * * Results: * The pixel size string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * CountToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record */ int offset; /* Offset in widget record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { int value = *(int *)(widgRec + offset); char *result; result = strdup(Blt_Itoa(value)); assert(result); *freeProcPtr = (Tcl_FreeProc *)free; return (result); } /* *---------------------------------------------------------------------- * * StringToPad -- * * Convert a string into two pad values. The string may be in one of * the following forms: * * n - n is a non-negative integer. This sets both * pad values to n. * {n m} - both n and m are non-negative integers. side1 * is set to n, side2 is set to m. * * Results: * If the string is successfully converted, TCL_OK is returned. * Otherwise, TCL_ERROR is returned and an error message is left in * interp->result. * * Side Effects: * The padding structure passed is updated with the new values. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToPad(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Window */ char *string; /* Pixel value string */ char *widgRec; /* Widget record */ int offset; /* Offset of pad in widget */ { Pad *padPtr = (Pad *)(widgRec + offset); int nElem; int pad, result; char **padArr; if (Tcl_SplitList(interp, string, &nElem, &padArr) != TCL_OK) { return TCL_ERROR; } result = TCL_ERROR; if ((nElem < 1) || (nElem > 2)) { Tcl_AppendResult(interp, "wrong # elements in padding list", (char *)NULL); goto error; } if (Blt_GetPixels(interp, tkwin, padArr[0], PIXELS_NONNEGATIVE, &pad) != TCL_OK) { goto error; } padPtr->side1 = pad; if ((nElem > 1) && (Blt_GetPixels(interp, tkwin, padArr[1], PIXELS_NONNEGATIVE, &pad) != TCL_OK)) { goto error; } padPtr->side2 = pad; result = TCL_OK; error: free((char *)padArr); return (result); } /* *---------------------------------------------------------------------- * * PadToString -- * * Converts the two pad values into a Tcl list. Each pad has two * pixel values. For vertical pads, they represent the top and bottom * margins. For horizontal pads, they're the left and right margins. * All pad values are non-negative integers. * * Results: * The padding list is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * PadToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Structure record */ int offset; /* Offset of pad in record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { Pad *padPtr = (Pad *)(widgRec + offset); char *result; char string[200]; sprintf(string, "%d %d", padPtr->side1, padPtr->side2); result = strdup(string); if (result == NULL) { return "out of memory"; } *freeProcPtr = (Tcl_FreeProc *)free; return (result); } /* *---------------------------------------------------------------------- * * StringToShadow -- * * Convert a string into two pad values. The string may be in one of * the following forms: * * n - n is a non-negative integer. This sets both * pad values to n. * {n m} - both n and m are non-negative integers. side1 * is set to n, side2 is set to m. * * Results: * If the string is successfully converted, TCL_OK is returned. * Otherwise, TCL_ERROR is returned and an error message is left in * interp->result. * * Side Effects: * The padding structure passed is updated with the new values. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToShadow(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Window */ char *string; /* Pixel value string */ char *widgRec; /* Widget record */ int offset; /* Offset of pad in widget */ { Shadow *shadowPtr = (Shadow *) (widgRec + offset); XColor *colorPtr; int dropOffset; colorPtr = NULL; dropOffset = 0; if ((string != NULL) && (string[0] != '\0')) { int nElem; char **elemArr; if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) { return TCL_ERROR; } if ((nElem < 1) || (nElem > 2)) { Tcl_AppendResult(interp, "wrong # elements in drop shadow value", (char *)NULL); free((char *)elemArr); return TCL_ERROR; } colorPtr = Tk_GetColor(interp, tkwin, Tk_GetUid(elemArr[0])); if (colorPtr == NULL) { free((char *)elemArr); return TCL_ERROR; } dropOffset = 1; if (nElem == 2) { if (Blt_GetPixels(interp, tkwin, elemArr[1], PIXELS_NONNEGATIVE, &dropOffset) != TCL_OK) { Tk_FreeColor(colorPtr); free((char *)elemArr); return TCL_ERROR; } } free((char *)elemArr); } if (shadowPtr->color != NULL) { Tk_FreeColor(shadowPtr->color); } shadowPtr->color = colorPtr; shadowPtr->offset = dropOffset; return TCL_OK; } /* *---------------------------------------------------------------------- * * ShadowToString -- * * Converts the two pad values into a Tcl list. Each pad has two * pixel values. For vertical pads, they represent the top and bottom * margins. For horizontal pads, they're the left and right margins. * All pad values are non-negative integers. * * Results: * The padding list is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * ShadowToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Structure record */ int offset; /* Offset of pad in record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { Shadow *shadowPtr = (Shadow *) (widgRec + offset); char *result; result = ""; if (shadowPtr->color != NULL) { char string[200]; sprintf(string, "%s %d", Tk_NameOfColor(shadowPtr->color), shadowPtr->offset); result = strdup(string); *freeProcPtr = (Tcl_FreeProc *)free; } return result; } /* *---------------------------------------------------------------------- * * GetDashes -- * * Converts a Tcl list of dash values into a dash list ready for * use with XSetDashes. * * A valid list dash values can have zero through 11 elements * (PostScript limit). Values must be between 1 and 255. Although * a list of 0 (like the empty string) means no dashes. * * Results: * A standard Tcl result. If the list represented a valid dash * list TCL_OK is returned and *dashesPtr* will contain the * valid dash list. Otherwise, TCL_ERROR is returned and * interp->result will contain an error message. * * *---------------------------------------------------------------------- */ static int GetDashes(interp, string, dashesPtr) Tcl_Interp *interp; char *string; Dashes *dashesPtr; { if ((string == NULL) || (*string == '\0')) { dashesPtr->nValues = 0; } else if (strcmp(string, "dash") == 0) { /* 5 2 */ dashesPtr->nValues = 2; dashesPtr->valueArr[0] = 5; dashesPtr->valueArr[1] = 2; } else if (strcmp(string, "dot") == 0) { /* 1 */ dashesPtr->nValues = 1; dashesPtr->valueArr[0] = 1; } else if (strcmp(string, "dashdot") == 0) { /* 2 4 2 */ dashesPtr->nValues = 3; dashesPtr->valueArr[0] = 2; dashesPtr->valueArr[1] = 4; dashesPtr->valueArr[2] = 2; } else if (strcmp(string, "dashdotdot") == 0) { /* 2 4 2 2 */ dashesPtr->nValues = 4; dashesPtr->valueArr[0] = 2; dashesPtr->valueArr[1] = 4; dashesPtr->valueArr[2] = 2; dashesPtr->valueArr[3] = 2; } else { int nValues; char **strArr; long int value; register int i; if (Tcl_SplitList(interp, string, &nValues, &strArr) != TCL_OK) { return TCL_ERROR; } if (nValues > 11) { /* This is the postscript limit */ Tcl_AppendResult(interp, "too many values in dash list \"", string, "\"", (char *)NULL); free((char *)strArr); return TCL_ERROR; } for (i = 0; i < nValues; i++) { if (Tcl_ExprLong(interp, strArr[i], &value) != TCL_OK) { free((char *)strArr); return TCL_ERROR; } /* * Backward compatibility: * Allow list of 0 to turn off dashes */ if ((value == 0) && (nValues == 1)) { break; } if ((value < 1) || (value > 255)) { Tcl_AppendResult(interp, "dash value \"", strArr[i], "\" is out of range", (char *)NULL); free((char *)strArr); return TCL_ERROR; } dashesPtr->valueArr[i] = (unsigned char)value; } dashesPtr->nValues = i; free((char *)strArr); } /* Make sure the array ends with a NUL byte */ dashesPtr->valueArr[dashesPtr->nValues] = '\0'; return TCL_OK; } /* *---------------------------------------------------------------------- * * StringToDashes -- * * Convert the list of dash values into a dashes array. * * Results: * The return value is a standard Tcl result. * * Side Effects: * The Dashes structure is updated with the new dash list. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToDashes(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* New dash value list */ char *widgRec; /* Widget record */ int offset; /* offset to Dashes structure */ { Dashes *dashesPtr = (Dashes *)(widgRec + offset); return (GetDashes(interp, string, dashesPtr)); } /* *---------------------------------------------------------------------- * * DashesToString -- * * Convert the dashes array into a list of values. * * Results: * The string representing the dashes returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * DashesToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget record */ int offset; /* offset of Dashes in record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { Dashes *dashesPtr = (Dashes *)(widgRec + offset); Tcl_DString dStr; register int i; char *result; char string[200]; if (dashesPtr->nValues == 0) { return ""; } Tcl_DStringInit(&dStr); for (i = 0; i < dashesPtr->nValues; i++) { sprintf(string, "%d", (int)dashesPtr->valueArr[i]); Tcl_DStringAppendElement(&dStr, string); } result = Tcl_DStringValue(&dStr); if (result == dStr.staticSpace) { result = strdup(result); } *freeProcPtr = (Tcl_FreeProc *)free; return (result); } /* *---------------------------------------------------------------------- * * StringToUid -- * * Converts the string to a BLT Uid. Blt Uid's are hashed, reference * counted strings. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToUid(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Fill style string */ char *widgRec; /* Cubicle structure record */ int offset; /* Offset of style in record */ { Tk_Uid *uidPtr = (Tk_Uid *)(widgRec + offset); Tk_Uid newId; newId = NULL; if ((string != NULL) && (*string != '\0')) { newId = Blt_GetUid(string); } if (*uidPtr != NULL) { Blt_FreeUid(*uidPtr); } *uidPtr = newId; return TCL_OK; } /* *---------------------------------------------------------------------- * * UidToString -- * * Returns the fill style string based upon the fill flags. * * Results: * The fill style string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * UidToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record */ int offset; /* Offset of fill in widget record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { Tk_Uid uid = *(Tk_Uid *)(widgRec + offset); return (uid == NULL) ? "" : uid; } /* *---------------------------------------------------------------------- * * StringToState -- * * Converts the string to a state value. Valid states are * disabled, normal. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToState(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* String representation of option value */ char *widgRec; /* Widget structure record */ int offset; /* Offset of field in record */ { int *statePtr = (int *)(widgRec + offset); if (strcmp(string, "normal") == 0) { *statePtr = STATE_NORMAL; } else if (strcmp(string, "disabled") == 0) { *statePtr = STATE_DISABLED; } else if (strcmp(string, "active") == 0) { *statePtr = STATE_ACTIVE; } else { Tcl_AppendResult(interp, "bad state \"", string, "\": should be normal, active, or disabled", (char *)NULL); return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * StateToString -- * * Returns the string representation of the state configuration field * * Results: * The string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * StateToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record */ int offset; /* Offset of fill in widget record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { int state = *(int *)(widgRec + offset); switch (state) { case STATE_ACTIVE: return "active"; case STATE_DISABLED: return "disabled"; case STATE_NORMAL: return "normal"; default: return "???"; } } /* *---------------------------------------------------------------------- * * StringToList -- * * Converts the string to a list. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToList(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* String representation of option value */ char *widgRec; /* Widget structure record */ int offset; /* Offset of field in record */ { char ***listPtr = (char ***)(widgRec + offset); char **elemArr; int nElem; if (*listPtr != NULL) { free((char *)*listPtr); *listPtr = NULL; } if ((string == NULL) || (*string == '\0')) { return TCL_OK; } if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) { return TCL_ERROR; } if (nElem > 0) { *listPtr = elemArr; } return TCL_OK; } /* *---------------------------------------------------------------------- * * ListToString -- * * Returns the string representation of the state configuration field * * Results: * The string is returned. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static char * ListToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Widget structure record. */ int offset; /* Offset of fill in widget record. */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { char **list = *(char ***)(widgRec + offset); register char **p; char *result; Tcl_DString dString; if (list == NULL) { return ""; } Tcl_DStringInit(&dString); for (p = list; *p != NULL; p++) { Tcl_DStringAppendElement(&dString, *p); } result = Tcl_DStringValue(&dString); if (result == dString.staticSpace) { result = strdup(result); } Tcl_DStringFree(&dString); *freeProcPtr = (Tcl_FreeProc *)free; return result; } tkdesk-2.0/blt/bltDnd.c0100644000175000007640000024054710020457430013070 0ustar jccjcc/* * bltDnd.c -- * * This module implements a drag-and-drop mechanism for the Tk * Toolkit. Allows widgets to be registered as drag&drop sources * and targets for handling "drag-and-drop" operations between * Tcl/Tk applications. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * The "drag&drop" command was created by Michael J. McLennan. */ #include "bltInt.h" #ifndef NO_DRAGDROP #include "bltChain.h" #include #ifdef WIN32 #define MAX_PROP_SIZE 255 /* Maximum size of property. */ typedef HWND WINDOW; #else #define MAX_PROP_SIZE 1000 /* Maximum size of property. */ typedef Window WINDOW; static Atom dndAtom; #endif /* * Each "drag&drop" target widget is tagged with a "BltDrag&DropTarget" * property in XA_STRING format. This property identifies the window * as a "drag&drop" target. It's formated as a Tcl list and contains * the following information: * * "INTERP_NAME TARGET_NAME DATA_TYPE DATA_TYPE ..." * * INTERP_NAME Name of the target application's interpreter. * TARGET_NAME Path name of widget registered as the drop target. * DATA_TYPE One or more "types" handled by the target. * * When the user invokes the "drag" operation, the window hierarchy * is progressively examined. Window information is cached during * the operation, to minimize X server traffic. Windows carrying a * "BltDrag&DropTarget" property are identified. When the token is * dropped over a valid site, the drop information is sent to the * application * via the usual "send" command. If communication fails, the drag&drop * facility automatically posts a rejection symbol on the token window. */ #define INTERP_NAME 0 #define TARGET_NAME 1 #define WINDOW_ID 2 #define DATA_TYPE 3 /* Error Proc used to report drag&drop background errors */ #define DEF_ERROR_PROC "bgerror" /* * CONFIG PARAMETERS */ #define DEF_DND_BUTTON_BG_COLOR RGB_YELLOW #define DEF_DND_BUTTON_BG_MONO STD_MONO_NORMAL_BG #define DEF_DND_BUTTON_NUMBER "3" #define DEF_DND_PACKAGE_COMMAND (char *)NULL #define DEF_DND_SELF_TARGET "no" #define DEF_DND_SEND "all" #define DEF_DND_SITE_COMMAND (char *)NULL #define DEF_TOKEN_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG #define DEF_TOKEN_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG #define DEF_TOKEN_ACTIVE_BORDERWIDTH "3" #define DEF_TOKEN_ACTIVE_RELIEF "sunken" #define DEF_TOKEN_ANCHOR "se" #define DEF_TOKEN_BG_COLOR STD_COLOR_NORMAL_BG #define DEF_TOKEN_BG_MONO STD_MONO_NORMAL_BG #define DEF_TOKEN_BORDERWIDTH "3" #define DEF_TOKEN_CURSOR "top_left_arrow" #define DEF_TOKEN_OUTLINE_COLOR RGB_BLACK #define DEF_TOKEN_OUTLINE_MONO RGB_BLACK #define DEF_TOKEN_REJECT_BG_COLOR STD_COLOR_NORMAL_BG #define DEF_TOKEN_REJECT_BG_MONO RGB_WHITE #define DEF_TOKEN_REJECT_FG_COLOR RGB_RED #define DEF_TOKEN_REJECT_FG_MONO RGB_BLACK #define DEF_TOKEN_REJECT_STIPPLE_COLOR (char *)NULL #define DEF_TOKEN_REJECT_STIPPLE_MONO RGB_GREY50 #define DEF_TOKEN_RELIEF "raised" #if HAVE_NAMESPACES static char dragDropCmd[] = "blt::drag&drop"; #else static char dragDropCmd[] = "drag&drop"; #endif static char className[] = "DragDropToken"; /* CLASS NAME of token window */ static char propName[] = "BltDrag&DropTarget"; /* Property name */ static Tcl_HashTable sourceTable; static Tcl_HashTable targetTable; static char *errorCmd; static int nActive; static int locX, locY; static int initialized = FALSE; /* * Percent substitutions */ typedef struct SubstDescriptors { char letter; /* character like 'x' in "%x" */ char *value; /* value to be substituted in place of "%x" */ } SubstDescriptors; /* * AnyWindow -- * * This structure represents a window hierarchy examined during * a single "drag" operation. It's used to cache information * to reduce the round-trip calls to the server needed to query * window geometry information and grab the target property. */ typedef struct AnyWindow { WINDOW nativeWindow; /* Native window: HWINDOW (Win32) or * Window (X11). */ int initialized; /* If non-zero, the rest of this structure's * information had been previously built. */ int x1, y1, x2, y2; /* Extents of the window (upper-left and * lower-right corners). */ struct AnyWindow *parentPtr; /* Parent node. NULL if root. Used to * compute offset for X11 windows. */ Blt_Chain *chainPtr; /* List of this window's children. If NULL, * there are no children. */ char **targetInfo; /* An array of target window drag&drop * information: target interpreter, * pathname, and optionally possible * type matches. NULL if the window is * not a drag&drop target or is not a * valid match for the drop source. */ } AnyWindow; /* * Drag&Drop Registration Data */ typedef struct Token { /* * This is a goof in the Tk API. It assumes that only an official * Tk "toplevel" widget will ever become a toplevel window (i.e. a * window whose parent is the root window). Because under Win32, * Tk tries to use the widget record associated with the TopLevel * as a Tk frame widget, to read its menu name. What this means * is that any widget that's going to be a toplevel, must also look * like a frame. Therefore we've copied the frame widget structure * fields into the token. */ Tk_Window tkwin; /* Window that embodies the frame. NULL * means that the window has been destroyed * but the data structures haven't yet been * cleaned up. */ Display *display; /* Display containing widget. Used, among * other things, so that resources can be * freed even after tkwin has gone away. */ Tcl_Interp *interp; /* Interpreter associated with widget. Used * to delete widget command. */ Tcl_Command widgetCmd; /* Token for frame's widget command. */ char *className; /* Class name for widget (from configuration * option). Malloc-ed. */ int mask; /* Either FRAME or TOPLEVEL; used to select * which configuration options are valid for * widget. */ char *screenName; /* Screen on which widget is created. Non-null * only for top-levels. Malloc-ed, may be * NULL. */ char *visualName; /* Textual description of visual for window, * from -visual option. Malloc-ed, may be * NULL. */ char *colormapName; /* Textual description of colormap for window, * from -colormap option. Malloc-ed, may be * NULL. */ char *menuName; /* Textual description of menu to use for * menubar. Malloc-ed, may be NULL. */ Colormap colormap; /* If not None, identifies a colormap * allocated for this window, which must be * freed when the window is deleted. */ Tk_3DBorder border; /* Structure used to draw 3-D border and * background. NULL means no background * or border. */ int borderWidth; /* Width of 3-D border (if any). */ int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */ int highlightWidth; /* Width in pixels of highlight to draw * around widget when it has the focus. * 0 means don't draw a highlight. */ XColor *highlightBgColorPtr; /* Color for drawing traversal highlight * area when highlight is off. */ XColor *highlightColorPtr; /* Color for drawing traversal highlight. */ int width; /* Width to request for window. <= 0 means * don't request any size. */ int height; /* Height to request for window. <= 0 means * don't request any size. */ Tk_Cursor cursor; /* Current cursor for window, or None. */ char *takeFocus; /* Value of -takefocus option; not used in * the C code, but used by keyboard traversal * scripts. Malloc'ed, but may be NULL. */ int isContainer; /* 1 means this window is a container, 0 means * that it isn't. */ char *useThis; /* If the window is embedded, this points to * the name of the window in which it is * embedded (malloc'ed). For non-embedded * windows this is NULL. */ int flags; /* Various flags; see below for * definitions. */ /* Token specific fields */ int lastX, lastY; /* last position of token window */ int active; /* non-zero => over target window */ Tk_TimerToken timer; /* token for routine to hide tokenwin */ GC rejectFgGC; /* GC used to draw rejection fg: (\) */ GC rejectBgGC; /* GC used to draw rejection bg: (\) */ /* User-configurable fields */ Tk_Anchor anchor; /* Position of token win relative to mouse */ Tk_3DBorder outline; /* Outline border around token window */ Tk_3DBorder normalBorder; /* Border/background for token window */ Tk_3DBorder activeBorder; /* Border/background for token window */ int activeRelief; int activeBorderWidth; /* Border width in pixels */ XColor *rejectFg; /* Color used to draw rejection fg: (\) */ XColor *rejectBg; /* Color used to draw rejection bg: (\) */ Pixmap rejectStipple; /* Stipple used to draw rejection: (\) */ } Token; typedef struct { Tcl_Interp *interp; /* Interpreter associated with the Tk source * widget. */ Tk_Window tkwin; /* Tk window registered as the drag&drop * source. */ Display *display; /* Drag&drop source window display */ Tcl_HashTable handlerTable; /* Table of data handlers (converters) * registered for this source. */ int button; /* Button used to invoke drag operation. */ Token token; /* Token used to provide special cursor. */ int pkgCmdInProgress; /* Indicates if a pkgCmd is currently active. */ char *pkgCmd; /* Tcl command executed at start of "drag" * operation to gather information about * the source data. */ char *pkgCmdResult; /* Result returned by the most recent * pkgCmd. */ char *siteCmd; /* Tcl command executed to update token * window. */ AnyWindow *rootPtr; /* Cached window information: Gathered * and used during the "drag" operation * to see if the mouse pointer is over a * valid target. */ int selfTarget; /* Indicated if the source should drop onto * itself. */ Tk_Cursor cursor; /* cursor restored after dragging */ char **sendTypes; /* list of data handler names or "all" */ Tcl_HashEntry *hashPtr; AnyWindow *windowPtr; /* Last target examined. If NULL, mouse * pointer is not currently over a valid * target. */ } Source; typedef struct { Tcl_Interp *interp; Tk_Window tkwin; /* drag&drop target window */ Display *display; /* drag&drop target window display */ Tcl_HashTable handlerTable; /* Table of data handlers (converters) * registered for this target. */ Tcl_HashEntry *hashPtr; } Target; extern Tk_CustomOption bltListOption; static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_INT, "-button", "buttonBinding", "ButtonBinding", DEF_DND_BUTTON_NUMBER, Tk_Offset(Source, button), 0}, {TK_CONFIG_STRING, "-packagecmd", "packageCommand", "Command", DEF_DND_PACKAGE_COMMAND, Tk_Offset(Source, pkgCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background", DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Source, token.rejectBg), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background", DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Source, token.rejectBg), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground", DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Source, token.rejectFg), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground", DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Source, token.rejectFg), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple", DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Source, token.rejectStipple), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple", DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Source, token.rejectStipple), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BOOLEAN, "-selftarget", "selfTarget", "SelfTarget", DEF_DND_SELF_TARGET, Tk_Offset(Source, selfTarget), 0}, {TK_CONFIG_CUSTOM, "-send", "send", "Send", DEF_DND_SEND, Tk_Offset(Source, sendTypes), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_STRING, "-sitecmd", "siteCommand", "Command", DEF_DND_SITE_COMMAND, Tk_Offset(Source, siteCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_ANCHOR, "-tokenanchor", "tokenAnchor", "Anchor", DEF_TOKEN_ANCHOR, Tk_Offset(Source, token.anchor), 0}, {TK_CONFIG_BORDER, "-tokenactivebackground", "tokenActiveBackground", "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR, Tk_Offset(Source, token.activeBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-tokenactivebackground", "tokenActiveBackground", "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO, Tk_Offset(Source, token.activeBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BORDER, "-tokenbg", "tokenBackground", "Background", DEF_TOKEN_BG_COLOR, Tk_Offset(Source, token.normalBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-tokenbg", "tokenBackground", "Background", DEF_TOKEN_BG_MONO, Tk_Offset(Source, token.normalBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BORDER, "-tokenoutline", "tokenOutline", "Outline", DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(Source, token.outline), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-tokenoutline", "tokenOutline", "Outline", DEF_TOKEN_OUTLINE_MONO, Tk_Offset(Source, token.outline), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_PIXELS, "-tokenborderwidth", "tokenBorderWidth", "BorderWidth", DEF_TOKEN_BORDERWIDTH, Tk_Offset(Source, token.borderWidth), 0}, {TK_CONFIG_CURSOR, "-tokencursor", "tokenCursor", "Cursor", DEF_TOKEN_CURSOR, Tk_Offset(Source, token.cursor), TK_CONFIG_NULL_OK}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0}, }; static Tk_ConfigSpec tokenConfigSpecs[] = { {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR, Tk_Offset(Token, activeBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO, Tk_Offset(Token, activeBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "activeRelief", DEF_TOKEN_ACTIVE_RELIEF, Tk_Offset(Token, activeRelief), 0}, {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_TOKEN_ANCHOR, Tk_Offset(Token, anchor), 0}, {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth", "ActiveBorderWidth", DEF_TOKEN_ACTIVE_BORDERWIDTH, Tk_Offset(Token, activeBorderWidth), 0}, {TK_CONFIG_BORDER, "-background", "background", "Background", DEF_TOKEN_BG_COLOR, Tk_Offset(Token, normalBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-background", "background", "Background", DEF_TOKEN_BG_MONO, Tk_Offset(Token, normalBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_TOKEN_BORDERWIDTH, Tk_Offset(Token, borderWidth), 0}, {TK_CONFIG_CURSOR, "-cursor", "cursor", "Cursor", DEF_TOKEN_CURSOR, Tk_Offset(Token, cursor), TK_CONFIG_NULL_OK}, {TK_CONFIG_BORDER, "-outline", "outline", "Outline", DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(Token, outline), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-outline", "outline", "Outline", DEF_TOKEN_OUTLINE_MONO, Tk_Offset(Token, outline), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background", DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, rejectBg), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background", DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Token, rejectBg), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground", DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Token, rejectFg), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground", DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, rejectFg), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple", DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Token, rejectStipple), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple", DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Token, rejectStipple), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_TOKEN_RELIEF, Tk_Offset(Token, relief), 0}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0}, }; /* * Forward Declarations */ static int DragDropCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc, char **argv)); static void TokenEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void TargetEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void MoveToken _ANSI_ARGS_((Source * srcPtr, Token *tokenPtr)); static void UpdateToken _ANSI_ARGS_((ClientData clientData)); static void HideToken _ANSI_ARGS_((Token *tokenPtr)); static void RejectToken _ANSI_ARGS_((Token *tokenPtr)); static int GetSource _ANSI_ARGS_((Tcl_Interp *interp, char *name, Source **srcPtrPtr)); static Source *CreateSource _ANSI_ARGS_((Tcl_Interp *interp, char *pathname, int *newEntry)); static void DestroySource _ANSI_ARGS_((Source * srcPtr)); static void SourceEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static int ConfigureSource _ANSI_ARGS_((Tcl_Interp *interp, Source * srcPtr, int argc, char **argv, int flags)); static int ConfigureToken _ANSI_ARGS_((Tcl_Interp *interp, Source * srcPtr, int argc, char **argv)); static Target *CreateTarget _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin)); static Target *FindTarget _ANSI_ARGS_((Tk_Window tkwin)); static void DestroyTarget _ANSI_ARGS_((DestroyData dataPtr)); static int OverTarget _ANSI_ARGS_((Source * srcPtr, int x, int y)); static void AddTargetProperty _ANSI_ARGS_((Tcl_Interp *interp, Target * targetPtr)); static void DndSend _ANSI_ARGS_((Source * srcPtr)); static void InitRoot _ANSI_ARGS_((Source * srcPtr)); static void RemoveWindow _ANSI_ARGS_((AnyWindow *wr)); static void QueryWindow _ANSI_ARGS_((Display *display, AnyWindow * windowPtr)); static char *ExpandPercents _ANSI_ARGS_((char *str, SubstDescriptors * subs, int nsubs, Tcl_DString * dStrPtr)); #ifdef WIN32 #if _MSC_VER #include #endif typedef struct PropertyInfo { char *prefix; int prefixSize; char *propReturn; } PropertyInfo; static BOOL CALLBACK GetEnumWindowsProc(HWND hWnd, LPARAM clientData) { Blt_Chain *chainPtr = (Blt_Chain *) clientData; Blt_ChainAppend(chainPtr, (ClientData)hWnd); return TRUE; } static WINDOW GetNativeWindow(tkwin) Tk_Window tkwin; { return (WINDOW) Tk_GetHWND(Tk_WindowId(tkwin)); } /* * ------------------------------------------------------------------------ * * GetWindowZOrder -- * * Returns a list of the child windows according to their stacking * order. The window handles are ordered from top to bottom. * * ------------------------------------------------------------------------ */ static Blt_Chain * GetWindowZOrder(display, parent) Display *display; HWND parent; { Blt_Chain *chainPtr; HWND hWnd; chainPtr = Blt_ChainCreate(); for (hWnd = GetTopWindow(parent); hWnd != NULL; hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) { Blt_ChainAppend(chainPtr, (ClientData)hWnd); } return chainPtr; } /* * ------------------------------------------------------------------------ * * GetEnumPropsExProc -- * * ------------------------------------------------------------------------ */ static BOOL CALLBACK GetEnumPropsExProc(HWND hwnd, LPCTSTR atom, HANDLE hData, DWORD clientData) { PropertyInfo *infoPtr = (PropertyInfo *) clientData; if (strncmp(infoPtr->prefix, atom, infoPtr->prefixSize) == 0) { assert(infoPtr->propReturn == NULL); infoPtr->propReturn = (char *)atom; return FALSE; } return TRUE; } /* * ------------------------------------------------------------------------ * * GetPropData -- * * This is a bad Windows hack to pass property information between * applications. (Ab)Normally the property data (one-word value) is * stored in the data handle. But the data content is available only * within the application. The pointer value is meaningless outside * of the current application address space. Not really useful at all. * * So the trick here is to encode the property name with all the * necessary information and to loop through all the properties * of a window, looking for one that starts with our property name * prefix. The downside is that the property name is limited to * 255 bytes. But that should be enough. It's also slower since * we examine each property until we find ours. * * We'll plug in the OLE stuff later. * * ------------------------------------------------------------------------ */ static char * GetPropData(hWnd, atom) HWND hWnd; char *atom; { PropertyInfo propInfo; if (hWnd == NULL) { return NULL; } propInfo.prefix = atom; propInfo.prefixSize = strlen(atom); propInfo.propReturn = NULL; EnumPropsEx(hWnd, GetEnumPropsExProc, (DWORD) & propInfo); return propInfo.propReturn; } static char * GetProperty(display, hWnd) Display *display; HWND hWnd; { ATOM atom; atom = (ATOM) GetProp(hWnd, propName); if (atom != (ATOM) 0) { char buffer[MAX_PROP_SIZE + 1]; UINT nBytes; nBytes = GlobalGetAtomName(atom, buffer, MAX_PROP_SIZE); if (nBytes > 0) { buffer[nBytes] = '\0'; return strdup(buffer); } } return NULL; } static void SetProperty(tkwin, data) Tk_Window tkwin; char *data; { HWND hWnd; ATOM atom; hWnd = Tk_GetHWND(Tk_WindowId(tkwin)); if (hWnd == NULL) { return; } atom = (ATOM) GetProp(hWnd, propName); if (atom != 0) { GlobalDeleteAtom(atom); } atom = GlobalAddAtom(data); if (atom != (ATOM) 0) { SetProp(hWnd, propName, (HANDLE) atom); } } static void RemoveProperty(tkwin) Tk_Window tkwin; { HWND hWnd; ATOM atom; hWnd = Tk_GetHWND(Tk_WindowId(tkwin)); if (hWnd == NULL) { return; } atom = (ATOM) GetProp(hWnd, propName); if (atom != 0) { GlobalDeleteAtom(atom); } RemoveProp(hWnd, propName); } /* *---------------------------------------------------------------------- * * GetWindowRegion -- * *---------------------------------------------------------------------- */ /*ARGSUSED*/ static int GetWindowRegion( Display *display, /* Not used. */ HWND hWnd, int *x1Ptr, int *y1Ptr, int *x2Ptr, int *y2Ptr) { RECT region; if (GetWindowRect(hWnd, ®ion)) { *x1Ptr = region.left; *y1Ptr = region.top; *x2Ptr = region.right; *y2Ptr = region.bottom; return IsWindowVisible(hWnd); } return FALSE; } /* *---------------------------------------------------------------------- * * TkWinChildProc -- * * Callback from Windows whenever an event occurs on a child * window. * * Results: * Standard Windows return value. * * Side effects: * May process events off the Tk event queue. * *---------------------------------------------------------------------- */ #ifdef notdef LRESULT CALLBACK TkWinChildProc(hwnd, message, wParam, lParam) HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; { LRESULT result; switch (message) { case WM_USER: result = GenerateDragEvent(hwnd, message, wParam, lParam); break; case WM_SETCURSOR: /* * Short circuit the WM_SETCURSOR message since we set * the cursor elsewhere. */ result = TRUE; break; case WM_CREATE: case WM_ERASEBKGND: case WM_WINDOWPOSCHANGED: result = 0; break; case WM_PAINT: result = DefWindowProc(hwnd, message, wParam, lParam); break; default: if (!Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) { result = DefWindowProc(hwnd, message, wParam, lParam); } break; } /* * Handle any newly queued events before returning control to Windows. */ Tcl_ServiceAll(); return result; } #endif #else static WINDOW GetNativeWindow(tkwin) Tk_Window tkwin; { return Tk_WindowId(tkwin); } /* * ------------------------------------------------------------------------ * * GetWindowZOrder -- * * Returns a chain of the child windows according to their stacking * order. The window ids are ordered from top to bottom. * * ------------------------------------------------------------------------ */ static Blt_Chain * GetWindowZOrder(display, window) Display *display; Window window; { Blt_Chain *chainPtr; Window *childArr; unsigned int nChildren; Window dummy; chainPtr = NULL; if ((XQueryTree(display, window, &dummy, &dummy, &childArr, &nChildren)) && (nChildren > 0)) { register int i; chainPtr = Blt_ChainCreate(); for (i = 0; i < nChildren; i++) { /* * XQuery returns windows in bottom to top order. * We only care about the top window. */ Blt_ChainPrepend(chainPtr, (ClientData)childArr[i]); } if (childArr != NULL) { XFree((char *)childArr); /* done with list of kids */ } } return chainPtr; } static char * GetProperty(display, window) Display *display; Window window; { char *data; int result, actualFormat; Atom actualType; unsigned long nItems, bytesAfter; if (window == None) { return NULL; } data = NULL; result = XGetWindowProperty(display, window, dndAtom, 0, MAX_PROP_SIZE, False, XA_STRING, &actualType, &actualFormat, &nItems, &bytesAfter, (unsigned char **)&data); if ((result != Success) || (actualFormat != 8) || (actualType != XA_STRING)) { if (data != NULL) { XFree((char *)data); data = NULL; } } return data; } static void SetProperty(tkwin, data) Tk_Window tkwin; char *data; { XChangeProperty(Tk_Display(tkwin), Tk_WindowId(tkwin), dndAtom, XA_STRING, 8, PropModeReplace, (unsigned char *)data, strlen(data) + 1); } static int GetWindowRegion(display, window, x1Ptr, y1Ptr, x2Ptr, y2Ptr) Display *display; Window window; int *x1Ptr, *y1Ptr, *x2Ptr, *y2Ptr; { XWindowAttributes winAttrs; if (XGetWindowAttributes(display, window, &winAttrs)) { *x1Ptr = winAttrs.x; *y1Ptr = winAttrs.y; *x2Ptr = winAttrs.x + winAttrs.width - 1; *y2Ptr = winAttrs.y + winAttrs.height - 1; } return (winAttrs.map_state == IsViewable); } #endif /* WIN32 */ /* * ------------------------------------------------------------------------ * * ChangeToken -- * * ------------------------------------------------------------------------ */ static void ChangeToken(tokenPtr, active) Token *tokenPtr; int active; { int relief; Tk_3DBorder border; int borderWidth; Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin), tokenPtr->outline, 0, 0, Tk_Width(tokenPtr->tkwin), Tk_Height(tokenPtr->tkwin), 0, TK_RELIEF_FLAT); if (active) { relief = tokenPtr->activeRelief; border = tokenPtr->activeBorder; borderWidth = tokenPtr->activeBorderWidth; } else { relief = tokenPtr->relief; border = tokenPtr->normalBorder; borderWidth = tokenPtr->borderWidth; } Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin), border, 2, 2, Tk_Width(tokenPtr->tkwin) - 4, Tk_Height(tokenPtr->tkwin) - 4, borderWidth, relief); } /* * ------------------------------------------------------------------------ * * TokenEventProc -- * * Invoked by the Tk dispatcher to handle widget events. * Manages redraws for the drag&drop token window. * * ------------------------------------------------------------------------ */ static void TokenEventProc(clientData, eventPtr) ClientData clientData; /* data associated with widget */ XEvent *eventPtr; /* information about event */ { Token *tokenPtr = (Token *)clientData; if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { if (tokenPtr->tkwin != NULL) { ChangeToken(tokenPtr, tokenPtr->active); } } else if (eventPtr->type == DestroyNotify) { tokenPtr->tkwin = NULL; } } /* * ------------------------------------------------------------------------ * * HideToken -- * * Unmaps the drag&drop token. Invoked directly at the end of a * successful communication, or after a delay if the communication * fails (allowing the user to see a graphical picture of failure). * * ------------------------------------------------------------------------ */ static void HideToken(tokenPtr) Token *tokenPtr; { if (tokenPtr->tkwin != NULL) { Tk_UnmapWindow(tokenPtr->tkwin); } tokenPtr->timer = NULL; } /* * ------------------------------------------------------------------------ * * RaiseToken -- * * ------------------------------------------------------------------------ */ static void RaiseToken(tokenPtr) Token *tokenPtr; { Blt_MapTopLevelWindow(tokenPtr->tkwin); Blt_RaiseTopLevelWindow(tokenPtr->tkwin); } /* * ------------------------------------------------------------------------ * * MoveToken -- * * Invoked during "drag" operations to move a token window to its * current "drag" coordinate. * * ------------------------------------------------------------------------ */ static void MoveToken(srcPtr, tokenPtr) Source *srcPtr; /* drag&drop source window data */ Token *tokenPtr; { int x, y; int maxX, maxY; int vx, vy, vw, vh; Screen *screenPtr; /* Adjust current location for virtual root windows. */ Tk_GetVRootGeometry(srcPtr->tkwin, &vx, &vy, &vw, &vh); x = tokenPtr->lastX + vx - 3; y = tokenPtr->lastY + vy - 3; screenPtr = Tk_Screen(srcPtr->tkwin); maxX = WidthOfScreen(screenPtr) - Tk_Width(tokenPtr->tkwin); maxY = HeightOfScreen(screenPtr) - Tk_Height(tokenPtr->tkwin); Blt_TranslateAnchor(x, y, Tk_Width(tokenPtr->tkwin), Tk_Height(tokenPtr->tkwin), tokenPtr->anchor, &x, &y); if (x > maxX) { x = maxX; } else if (x < 0) { x = 0; } if (y > maxY) { y = maxY; } else if (y < 0) { y = 0; } if ((x != Tk_X(tokenPtr->tkwin)) || (y != Tk_Y(tokenPtr->tkwin))) { Tk_MoveToplevelWindow(tokenPtr->tkwin, x, y); } RaiseToken(tokenPtr); } static Tk_Cursor GetWidgetCursor(interp, tkwin) Tk_Window tkwin; Tcl_Interp *interp; { char *cursorName; Tk_Cursor cursor; cursor = None; if (Tcl_VarEval(interp, Tk_PathName(tkwin), " cget -cursor", (char *)NULL) != TCL_OK) { return None; } cursorName = Tcl_GetStringResult(interp); if ((cursorName != NULL) && (cursorName[0] != '\0')) { cursor = Tk_GetCursor(interp, tkwin, Tk_GetUid(cursorName)); } Tcl_ResetResult(interp); return cursor; } /* * ------------------------------------------------------------------------ * * UpdateToken -- * * Invoked when the event loop is idle to determine whether or not * the current drag&drop token position is over another drag&drop * target. * * ------------------------------------------------------------------------ */ static void UpdateToken(clientData) ClientData clientData; /* widget data */ { Source *srcPtr = (Source *)clientData; Token *tokenPtr = &(srcPtr->token); ChangeToken(tokenPtr, tokenPtr->active); /* * If the source has a site command, then invoke it to * modify the appearance of the token window. Pass any * errors onto the drag&drop error handler. */ if (srcPtr->siteCmd) { char buffer[200]; Tcl_DString cmdStr; int result; SubstDescriptors subs[2]; sprintf(buffer, "%d", tokenPtr->active); subs[0].letter = 's'; subs[0].value = buffer; subs[1].letter = 't'; subs[1].value = Tk_PathName(tokenPtr->tkwin); Tcl_DStringInit(&cmdStr); result = Tcl_Eval(srcPtr->interp, ExpandPercents(srcPtr->siteCmd, subs, 2, &cmdStr)); Tcl_DStringFree(&cmdStr); if ((result != TCL_OK) && (errorCmd != NULL) && (*errorCmd)) { (void)Tcl_VarEval(srcPtr->interp, errorCmd, " {", Tcl_GetStringResult(srcPtr->interp), "}", (char *)NULL); } } } /* * ------------------------------------------------------------------------ * * RejectToken -- * * Draws a rejection mark on the current drag&drop token, and arranges * for the token to be unmapped after a small delay. * * ------------------------------------------------------------------------ */ static void RejectToken(tokenPtr) Token *tokenPtr; { int divisor = 6; /* controls size of rejection symbol */ int w, h, lineWidth, x, y, margin; margin = 2 * tokenPtr->borderWidth; w = Tk_Width(tokenPtr->tkwin) - 2 * margin; h = Tk_Height(tokenPtr->tkwin) - 2 * margin; lineWidth = (w < h) ? w / divisor : h / divisor; lineWidth = (lineWidth < 1) ? 1 : lineWidth; w = h = lineWidth * (divisor - 1); x = (Tk_Width(tokenPtr->tkwin) - w) / 2; y = (Tk_Height(tokenPtr->tkwin) - h) / 2; /* * Draw the rejection symbol background (\) on the token window... */ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->rejectBgGC, lineWidth + 4, LineSolid, CapButt, JoinBevel); XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->rejectBgGC, x, y, w, h, 0, 23040); XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->rejectBgGC, x + lineWidth, y + lineWidth, x + w - lineWidth, y + h - lineWidth); /* * Draw the rejection symbol foreground (\) on the token window... */ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->rejectFgGC, lineWidth, LineSolid, CapButt, JoinBevel); XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->rejectFgGC, x, y, w, h, 0, 23040); XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->rejectFgGC, x + lineWidth, y + lineWidth, x + w - lineWidth, y + h - lineWidth); /* * Arrange for token window to disappear eventually. */ tokenPtr->timer = Tk_CreateTimerHandler(1000, (Tcl_TimerProc *) HideToken, (ClientData)tokenPtr); } /* * ------------------------------------------------------------------------ * * ConfigureToken -- * * ------------------------------------------------------------------------ */ static int ConfigureToken(interp, srcPtr, argc, argv) Tcl_Interp *interp; Source *srcPtr; int argc; char **argv; { Token *tokenPtr; tokenPtr = &(srcPtr->token); if (Tk_ConfigureWidget(interp, srcPtr->tkwin, tokenConfigSpecs, argc, argv, (char *)tokenPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; } return ConfigureSource(interp, srcPtr, 0, (char **)NULL, TK_CONFIG_ARGV_ONLY); } /* * ------------------------------------------------------------------------ * * CreateToken -- * * ------------------------------------------------------------------------ */ static int CreateToken(interp, srcPtr) Tcl_Interp *interp; Source *srcPtr; { XSetWindowAttributes attrs; Tk_Window tkwin; char string[200]; static int nextTokenId = 0; unsigned int mask; Token *tokenPtr = &(srcPtr->token); sprintf(string, "dd-token%d", ++nextTokenId); /* Create toplevel on parent's screen. */ tkwin = Tk_CreateWindow(interp, srcPtr->tkwin, string, ""); if (tkwin == NULL) { return TCL_ERROR; } Tk_SetClass(tkwin, className); Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask, TokenEventProc, (ClientData)tokenPtr); attrs.override_redirect = True; attrs.backing_store = WhenMapped; attrs.save_under = True; mask = CWOverrideRedirect | CWSaveUnder | CWBackingStore; Tk_ChangeWindowAttributes(tkwin, mask, &attrs); Tk_SetInternalBorder(tkwin, tokenPtr->borderWidth + 2); tokenPtr->tkwin = tkwin; #ifdef WIN32 { Tk_FakeWin *winPtr = (Tk_FakeWin *) tkwin; winPtr->dummy18 = (ClientData)tokenPtr; } #endif /* WIN32 */ Tk_MakeWindowExist(tkwin); return TCL_OK; } /* * ------------------------------------------------------------------------ * * CreateSource -- * * Looks for a Source record in the hash table for drag&drop source * widgets. Creates a new record if the widget name is not already * registered. Returns a pointer to the desired record. * * ------------------------------------------------------------------------ */ static Source * CreateSource(interp, pathName, newPtr) Tcl_Interp *interp; char *pathName; /* widget pathname for desired record */ int *newPtr; /* returns non-zero => new record created */ { Tcl_HashEntry *hPtr; Tk_Window tkwin; Source *srcPtr; tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp)); if (tkwin == NULL) { return NULL; } hPtr = Tcl_CreateHashEntry(&sourceTable, (char *)tkwin, newPtr); if (!(*newPtr)) { return (Source *) Tcl_GetHashValue(hPtr); } srcPtr = (Source *) calloc(1, sizeof(Source)); assert(srcPtr); srcPtr->tkwin = tkwin; srcPtr->display = Tk_Display(tkwin); srcPtr->interp = interp; srcPtr->token.anchor = TK_ANCHOR_SE; srcPtr->token.relief = TK_RELIEF_RAISED; srcPtr->token.activeRelief = TK_RELIEF_SUNKEN; srcPtr->token.borderWidth = srcPtr->token.activeBorderWidth = 3; srcPtr->hashPtr = hPtr; Tcl_InitHashTable(&(srcPtr->handlerTable), TCL_STRING_KEYS); if (ConfigureSource(interp, srcPtr, 0, (char **)NULL, 0) != TCL_OK) { DestroySource(srcPtr); return NULL; } Tcl_SetHashValue(hPtr, (ClientData)srcPtr); /* * Arrange for the window to unregister itself when it * is destroyed. */ Tk_CreateEventHandler(tkwin, StructureNotifyMask, SourceEventProc, (ClientData)srcPtr); return srcPtr; } /* * ------------------------------------------------------------------------ * * DestroySource -- * * Looks for a Source record in the hash table for drag&drop source * widgets. Destroys the record if found. * * ------------------------------------------------------------------------ */ static void DestroySource(srcPtr) Source *srcPtr; { Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; char *cmd; Tk_CancelIdleCall(UpdateToken, (ClientData)srcPtr); if (srcPtr->token.timer) { Tk_DeleteTimerHandler(srcPtr->token.timer); } Tk_FreeOptions(configSpecs, (char *)srcPtr, srcPtr->display, 0); if (srcPtr->token.rejectFgGC != NULL) { Tk_FreeGC(srcPtr->display, srcPtr->token.rejectFgGC); } if (srcPtr->token.rejectBgGC != NULL) { Tk_FreeGC(srcPtr->display, srcPtr->token.rejectBgGC); } if (srcPtr->pkgCmdResult) { free(srcPtr->pkgCmdResult); } if (srcPtr->rootPtr != NULL) { RemoveWindow(srcPtr->rootPtr); } if (srcPtr->cursor != None) { Tk_FreeCursor(srcPtr->display, srcPtr->cursor); } if (srcPtr->token.cursor != None) { Tk_FreeCursor(srcPtr->display, srcPtr->token.cursor); } free((char *)srcPtr->sendTypes); for (hPtr = Tcl_FirstHashEntry(&(srcPtr->handlerTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd != NULL) { free(cmd); } } Tcl_DeleteHashTable(&(srcPtr->handlerTable)); if (srcPtr->hashPtr != NULL) { Tcl_DeleteHashEntry(srcPtr->hashPtr); } free((char *)srcPtr); } /* * ------------------------------------------------------------------------ * * GetSource -- * * Looks for a Source record in the hash table for drag&drop source * widgets. Returns a pointer to the desired record. * * ------------------------------------------------------------------------ */ static int GetSource(interp, pathName, srcPtrPtr) Tcl_Interp *interp; char *pathName; /* widget pathname for desired record */ Source **srcPtrPtr; { Tcl_HashEntry *hPtr; Tk_Window tkwin; tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_FindHashEntry(&sourceTable, (char *)tkwin); if (hPtr == NULL) { Tcl_AppendResult(interp, "window \"", pathName, "\" has not been initialized as a drag&drop source", (char *)NULL); return TCL_ERROR; } *srcPtrPtr = (Source *)Tcl_GetHashValue(hPtr); return TCL_OK; } /* * ------------------------------------------------------------------------ * * ConfigureSource -- * * Called to process an (argc,argv) list to configure (or * reconfigure) a drag&drop source widget. * * ------------------------------------------------------------------------ */ static int ConfigureSource(interp, srcPtr, argc, argv, flags) Tcl_Interp *interp; /* current interpreter */ register Source *srcPtr; /* drag&drop source widget record */ int argc; /* number of arguments */ char **argv; /* argument strings */ int flags; /* flags controlling interpretation */ { unsigned long gcMask; XGCValues gcValues; GC newGC; Tcl_DString dString; Tcl_CmdInfo cmdInfo; int result; /* * Handle the bulk of the options... */ if (Tk_ConfigureWidget(interp, srcPtr->tkwin, configSpecs, argc, argv, (char *)srcPtr, flags) != TCL_OK) { return TCL_ERROR; } /* * Check the button binding for valid range (0 or 1-5) */ if ((srcPtr->button < 0) || (srcPtr->button > 5)) { Tcl_SetResult(interp, "button number must be 1-5, or 0 for no bindings", TCL_STATIC); return TCL_ERROR; } /* * Set up the rejection foreground GC for the token window... */ gcValues.foreground = srcPtr->token.rejectFg->pixel; gcValues.subwindow_mode = IncludeInferiors; gcValues.graphics_exposures = False; gcMask = GCForeground | GCSubwindowMode | GCGraphicsExposures; if (srcPtr->token.rejectStipple != None) { gcValues.stipple = srcPtr->token.rejectStipple; gcValues.fill_style = FillStippled; gcMask |= GCForeground | GCStipple | GCFillStyle; } newGC = Tk_GetGC(srcPtr->tkwin, gcMask, &gcValues); if (srcPtr->token.rejectFgGC != NULL) { Tk_FreeGC(srcPtr->display, srcPtr->token.rejectFgGC); } srcPtr->token.rejectFgGC = newGC; /* * Set up the rejection background GC for the token window... */ gcValues.foreground = srcPtr->token.rejectBg->pixel; gcValues.subwindow_mode = IncludeInferiors; gcValues.graphics_exposures = False; gcMask = GCForeground | GCSubwindowMode | GCGraphicsExposures; newGC = Tk_GetGC(srcPtr->tkwin, gcMask, &gcValues); if (srcPtr->token.rejectBgGC != NULL) { Tk_FreeGC(srcPtr->display, srcPtr->token.rejectBgGC); } srcPtr->token.rejectBgGC = newGC; /* * Reset the border width in case it has changed... */ if (srcPtr->token.tkwin) { Tk_SetInternalBorder(srcPtr->token.tkwin, srcPtr->token.borderWidth + 2); } if (!Tcl_GetCommandInfo(interp, "blt::Drag&DropInit", &cmdInfo)) { static char cmd[] = "source [file join $blt_library bltDragdrop.tcl]"; if (Tcl_GlobalEval(interp, cmd) != TCL_OK) { Tcl_AddErrorInfo(interp, "\n (while loading bindings for blt::drag&drop)"); return TCL_ERROR; } } Tcl_DStringInit(&dString); Blt_DStringAppendElements(&dString, "blt::Drag&DropInit", Tk_PathName(srcPtr->tkwin), Blt_Itoa(srcPtr->button), (char *)NULL); result = Tcl_Eval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); return result; } /* * ------------------------------------------------------------------------ * * SourceEventProc -- * * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received * on a registered drag&drop source widget. * * ------------------------------------------------------------------------ */ static void SourceEventProc(clientData, eventPtr) ClientData clientData; /* drag&drop registration list */ XEvent *eventPtr; /* event description */ { Source *srcPtr = (Source *) clientData; if (eventPtr->type == DestroyNotify) { DestroySource(srcPtr); } } /* * ------------------------------------------------------------------------ * * FindTarget -- * * Looks for a Target record in the hash table for drag&drop * target widgets. Creates a new record if the widget name is * not already registered. Returns a pointer to the desired * record. * * ------------------------------------------------------------------------ */ static Target * FindTarget(tkwin) Tk_Window tkwin; /* Widget pathname for desired record */ { Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&targetTable, (char *)tkwin); if (hPtr == NULL) { return NULL; } return (Target *) Tcl_GetHashValue(hPtr); } /* * ------------------------------------------------------------------------ * * CreateTarget -- * * Looks for a Target record in the hash table for drag&drop * target widgets. Creates a new record if the widget name is * not already registered. Returns a pointer to the desired * record. * * ------------------------------------------------------------------------ */ static Target * CreateTarget(interp, tkwin) Tcl_Interp *interp; Tk_Window tkwin; /* Widget pathname for desired record */ { Target *targetPtr; int isNew; targetPtr = (Target *) calloc(1, sizeof(Target)); assert(targetPtr); targetPtr->display = Tk_Display(tkwin); targetPtr->tkwin = tkwin; Tcl_InitHashTable(&(targetPtr->handlerTable), TCL_STRING_KEYS); targetPtr->hashPtr = Tcl_CreateHashEntry(&targetTable, (char *)tkwin, &isNew); Tcl_SetHashValue(targetPtr->hashPtr, (ClientData)targetPtr); /* * Arrange for the target to removed if the host window is destroyed. */ Tk_CreateEventHandler(tkwin, StructureNotifyMask, TargetEventProc, (ClientData)targetPtr); /* * If this is a new target, attach a property to identify * window as "drag&drop" target, and arrange for the window * to un-register itself when it is destroyed. */ Tk_MakeWindowExist(targetPtr->tkwin); AddTargetProperty(interp, targetPtr); return targetPtr; } /* * ------------------------------------------------------------------------ * * DestroyTarget -- * * ------------------------------------------------------------------------ */ static void DestroyTarget(data) DestroyData data; { Target *targetPtr = (Target *)data; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; char *cmd; for (hPtr = Tcl_FirstHashEntry(&(targetPtr->handlerTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd != NULL) { free((char *)cmd); } } Tcl_DeleteHashTable(&(targetPtr->handlerTable)); if (targetPtr->hashPtr != NULL) { Tcl_DeleteHashEntry(targetPtr->hashPtr); } Tk_DeleteEventHandler(targetPtr->tkwin, StructureNotifyMask, TargetEventProc, (ClientData)targetPtr); free((char *)targetPtr); } /* * ------------------------------------------------------------------------ * * TargetEventProc -- * * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received * on a registered drag&drop target widget. * * ------------------------------------------------------------------------ */ static void TargetEventProc(clientData, eventPtr) ClientData clientData; /* drag&drop registration list */ XEvent *eventPtr; /* event description */ { Target *targetPtr = (Target *) clientData; if (eventPtr->type == DestroyNotify) { #ifdef WIN32 /* * Under Win32 the properties must be removed before the window * can be destroyed. */ RemoveProperty(targetPtr->tkwin); #endif DestroyTarget((DestroyData)targetPtr); } } /* * ------------------------------------------------------------------------ * * DndSend -- * * Invoked after a drop operation to send data to the drop * application. * * ------------------------------------------------------------------------ */ static void DndSend(srcPtr) register Source *srcPtr; /* drag&drop source record */ { int status; SubstDescriptors subs[3]; Tcl_DString dString; Tcl_HashEntry *hPtr; char *dataType; char **targetInfo; char *cmd; /* See if current position is over drop point. */ if (!OverTarget(srcPtr, srcPtr->token.lastX, srcPtr->token.lastY)) { return; } targetInfo = srcPtr->windowPtr->targetInfo; /* Let TkDesk know we found a valid drop target */ Tcl_SetVar2(srcPtr->interp, "tkdesk", "drop_on_item", "1", TCL_GLOBAL_ONLY); Tcl_DStringInit(&dString); Blt_DStringAppendElements(&dString, "send", targetInfo[INTERP_NAME], dragDropCmd, "location", (char *)NULL); Tcl_DStringAppendElement(&dString, Blt_Itoa(srcPtr->token.lastX)); Tcl_DStringAppendElement(&dString, Blt_Itoa(srcPtr->token.lastY)); status = Tcl_Eval(srcPtr->interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (status != TCL_OK) { goto reject; } if (targetInfo[DATA_TYPE] == NULL) { Tcl_HashSearch cursor; hPtr = Tcl_FirstHashEntry(&(srcPtr->handlerTable), &cursor); dataType = Tcl_GetHashKey(&(srcPtr->handlerTable), hPtr); } else { hPtr = Tcl_FindHashEntry(&(srcPtr->handlerTable), targetInfo[DATA_TYPE]); dataType = targetInfo[DATA_TYPE]; } /* Start building the command line here, before we invoke any Tcl * commands. The is because the Tcl command may let in another * drag event and change the target property data. */ Tcl_DStringInit(&dString); Blt_DStringAppendElements(&dString, "send", targetInfo[INTERP_NAME], dragDropCmd, "target", targetInfo[TARGET_NAME], "handle", dataType, (char *)NULL); cmd = NULL; if (hPtr != NULL) { cmd = (char *)Tcl_GetHashValue(hPtr); } if (cmd != NULL) { Tcl_DString cmdString; subs[0].letter = 'i'; subs[0].value = targetInfo[INTERP_NAME]; subs[1].letter = 'w'; subs[1].value = targetInfo[TARGET_NAME]; subs[2].letter = 'v'; subs[2].value = srcPtr->pkgCmdResult; Tcl_DStringInit(&cmdString); status = Tcl_Eval(srcPtr->interp, ExpandPercents(cmd, subs, 3, &cmdString)); Tcl_DStringFree(&cmdString); Tcl_DStringAppendElement(&dString, Tcl_GetStringResult(srcPtr->interp)); } else { Tcl_DStringAppendElement(&dString, srcPtr->pkgCmdResult); } /* * Part 2: Now tell target application to handle the data. */ status = Tcl_Eval(srcPtr->interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (status != TCL_OK) { goto reject; } HideToken(&(srcPtr->token)); return; reject: /* * Give failure information to user. If an error occurred and an * error proc is defined, then use it to handle the error. */ RejectToken(&(srcPtr->token)); if (errorCmd != NULL) { Tcl_VarEval(srcPtr->interp, errorCmd, " {", Tcl_GetStringResult(srcPtr->interp), "}", (char *)NULL); } } /* * ------------------------------------------------------------------------ * * InitRoot -- * * Invoked at the start of a "drag" operation to capture the * positions of all windows on the current root. Queries the * entire window hierarchy and determines the placement of each * window. Queries the "BltDrag&DropTarget" property info where * appropriate. This information is used during the drag * operation to determine when the drag&drop token is over a * valid drag&drop target. * * Results: * Returns the record for the root window, which contains records * for all other windows as children. * * ------------------------------------------------------------------------ */ static void InitRoot(srcPtr) Source *srcPtr; { srcPtr->rootPtr = (AnyWindow *) calloc(1, sizeof(AnyWindow)); assert(srcPtr->rootPtr); #ifdef WIN32 srcPtr->rootPtr->nativeWindow = GetDesktopWindow(); #else srcPtr->rootPtr->nativeWindow = DefaultRootWindow(srcPtr->display); #endif srcPtr->windowPtr = NULL; QueryWindow(srcPtr->display, srcPtr->rootPtr); } /* * ------------------------------------------------------------------------ * * FindTopWindow -- * * Searches for the topmost window at a given pair of X-Y coordinates. * * Results: * Returns a pointer to the node representing the window containing * the point. If one can't be found, NULL is returned. * * ------------------------------------------------------------------------ */ static AnyWindow * FindTopWindow(srcPtr, x, y) Source *srcPtr; int x, y; { AnyWindow *rootPtr; register Blt_ChainLink *linkPtr; register AnyWindow *windowPtr; rootPtr = srcPtr->rootPtr; if (!rootPtr->initialized) { QueryWindow(srcPtr->display, rootPtr); } if ((x < rootPtr->x1) || (x > rootPtr->x2) || (y < rootPtr->y1) || (y > rootPtr->y2)) { return NULL; /* Point is not over window */ } windowPtr = rootPtr; /* * The window list is ordered top to bottom, so stop when we find * the first child that contains the X-Y coordinate. It will be * the topmost window in that hierarchy. If none exists, then we * already have the topmost window. */ descend: for (linkPtr = Blt_ChainFirstLink(rootPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rootPtr = (AnyWindow *) Blt_ChainGetValue(linkPtr); if (!rootPtr->initialized) { QueryWindow(srcPtr->display, rootPtr); } if ((x >= rootPtr->x1) && (x <= rootPtr->x2) && (y >= rootPtr->y1) && (y <= rootPtr->y2)) { /* * Remember the last window containing the pointer and * descend into its window hierarchy. We'll look for a * child that also contains the pointer. */ windowPtr = rootPtr; goto descend; } } return windowPtr; } /* * ------------------------------------------------------------------------ * * OverTarget -- * * Checks to see if a compatible drag&drop target exists at the * given position. A target is "compatible" if it is a drag&drop * window, and if it has a handler that is compatible with the * current source window. * * Results: * Returns a pointer to a structure describing the target, or NULL * if no compatible target is found. * * ------------------------------------------------------------------------ */ static int OverTarget(srcPtr, x, y) Source *srcPtr; /* drag&drop source window */ int x, y; /* current drag&drop location * (in virtual coords) */ { int virtX, virtY; int dummy; AnyWindow *newPtr, *oldPtr; char **elemArr; int nElems; char *data; int result; /* * If no window info has been been gathered yet for this target, * then abort this call. This probably means that the token is * moved before it has been properly built. */ if (srcPtr->rootPtr == NULL) { return FALSE; } if (srcPtr->sendTypes == NULL) { return FALSE; /* Send is turned off. */ } /* Adjust current location for virtual root windows. */ Tk_GetVRootGeometry(srcPtr->tkwin, &virtX, &virtY, &dummy, &dummy); x += virtX; y += virtY; oldPtr = srcPtr->windowPtr; srcPtr->windowPtr = NULL; newPtr = FindTopWindow(srcPtr, x, y); if (newPtr == NULL) { return FALSE; /* Not over a window. */ } if ((!srcPtr->selfTarget) && (GetNativeWindow(srcPtr->tkwin) == newPtr->nativeWindow)) { return FALSE; /* If the self-target flag isn't set, * don't allow the source window to * drop onto itself. */ } if (newPtr == oldPtr) { srcPtr->windowPtr = oldPtr; /* No need to collect the target information if we're still * over the same window. */ return (newPtr->targetInfo != NULL); } /* See if this window has a "BltDrag&DropTarget" property. */ data = GetProperty(srcPtr->display, newPtr->nativeWindow); if (data == NULL) { return FALSE; /* No such property on window. */ } result = Tcl_SplitList(srcPtr->interp, data, &nElems, &elemArr); XFree((char *)data); if (result != TCL_OK) { return FALSE; /* Malformed property list. */ } srcPtr->windowPtr = newPtr; /* Interpreter name, target name, type1, type2, ... */ if (nElems > 2) { register char **s; int count; register int i; /* * The candidate target has a list of possible types. * Compare this with what types the source is willing to * transmit and compress the list down to just the matching * types. It's up to the target to request the specific type * it wants. */ count = 2; for (i = 2; i < nElems; i++) { for (s = srcPtr->sendTypes; *s != NULL; s++) { if (((**s == 'a') && (strcmp(*s, "all") == 0)) || ((**s == elemArr[i][0]) && (strcmp(*s, elemArr[i]) == 0))) { elemArr[count++] = elemArr[i]; } } } if (count == 2) { free((char *)elemArr); fprintf(stderr, "source/target mismatch: No matching types\n"); return FALSE; /* No matching data type. */ } elemArr[count] = NULL; } newPtr->targetInfo = elemArr; return TRUE; } /* * ------------------------------------------------------------------------ * * RemoveWindow -- * * ------------------------------------------------------------------------ */ static void RemoveWindow(windowPtr) AnyWindow *windowPtr; /* window rep to be freed */ { AnyWindow *childPtr; Blt_ChainLink *linkPtr; /* Throw away leftover slots. */ for (linkPtr = Blt_ChainFirstLink(windowPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { childPtr = (AnyWindow *) Blt_ChainGetValue(linkPtr); RemoveWindow(childPtr); } Blt_ChainDestroy(windowPtr->chainPtr); if (windowPtr->targetInfo != NULL) { free((char *)windowPtr->targetInfo); } free((char *)windowPtr); } /* * ------------------------------------------------------------------------ * * QueryWindow -- * * Invoked during "drag" operations. Digs into the root window * hierarchy and caches the resulting information. * If a point coordinate lies within an uninitialized AnyWindow, * this routine is called to query window coordinates and * drag&drop info. If this particular window has any children, * more uninitialized AnyWindow structures are allocated. * Further queries will cause these structures to be initialized * in turn. * * ------------------------------------------------------------------------ */ static void QueryWindow(display, windowPtr) Display *display; AnyWindow *windowPtr; /* window rep to be initialized */ { int visible; if (windowPtr->initialized) { return; } /* * Query for the window coordinates. */ visible = GetWindowRegion(display, windowPtr->nativeWindow, &(windowPtr->x1), &(windowPtr->y1), &(windowPtr->x2), &(windowPtr->y2)); if (visible) { Blt_ChainLink *linkPtr; Blt_Chain *chainPtr; AnyWindow *childPtr; #ifndef WIN32 /* Add offset from parent's origin to coordinates */ if (windowPtr->parentPtr != NULL) { windowPtr->x1 += windowPtr->parentPtr->x1; windowPtr->y1 += windowPtr->parentPtr->y1; windowPtr->x2 += windowPtr->parentPtr->x1; windowPtr->y2 += windowPtr->parentPtr->y1; } #endif /* * Collect a list of child windows, sorted in z-order. The * topmost window will be first in the list. */ chainPtr = GetWindowZOrder(display, windowPtr->nativeWindow); /* Add and initialize extra slots if needed. */ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { childPtr = (AnyWindow *) calloc(1, sizeof(AnyWindow)); assert(childPtr); childPtr->initialized = FALSE; childPtr->nativeWindow = (WINDOW) Blt_ChainGetValue(linkPtr); childPtr->parentPtr = windowPtr; Blt_ChainSetValue(linkPtr, (ClientData)childPtr); } windowPtr->chainPtr = chainPtr; } else { /* If it's not viewable don't bother doing anything else. */ windowPtr->x1 = windowPtr->y1 = windowPtr->x2 = windowPtr->y2 = -1; windowPtr->chainPtr = NULL; } windowPtr->initialized = TRUE; } /* * ------------------------------------------------------------------------ * * AddTargetProperty -- * * Attaches a drag&drop property to the given target window. * This property allows us to recognize the window later as a * valid target. It also stores important information including * the interpreter managing the target and the pathname of the * target window. Usually this routine is called when the target * is first registered or first exposed (so that the X-window * really exists). * * ------------------------------------------------------------------------ */ static void AddTargetProperty(interp, targetPtr) Tcl_Interp *interp; Target *targetPtr; /* drag&drop target window data */ { Tcl_DString dString; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; if (targetPtr->tkwin == NULL) { return; } Tcl_DStringInit(&dString); /* * Each target window's dnd property contains * * 1. name of the application (ie. the interpreter's name). * 2. Tk path name of the target window. * 3. List of all the data types that can be handled. If none * are listed, then all can be handled. */ Tcl_DStringAppendElement(&dString, Tk_Name(Tk_MainWindow(interp))); Tcl_DStringAppendElement(&dString, Tk_PathName(targetPtr->tkwin)); for (hPtr = Tcl_FirstHashEntry(&(targetPtr->handlerTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { Tcl_DStringAppendElement(&dString, Tcl_GetHashKey(&(targetPtr->handlerTable), hPtr)); } SetProperty(targetPtr->tkwin, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); } /* * ------------------------------------------------------------------------ * * ExpandPercents -- * * Expands all percent substitutions found in the input "str" * that match specifications in the "subs" list. Any percent * field that is not found in the "subs" list is left alone. * Returns a string that remains valid until the next call to * this routine. * * ------------------------------------------------------------------------ */ static char * ExpandPercents(string, subsArr, nSubs, dStrPtr) char *string; /* Incoming command string */ SubstDescriptors *subsArr; /* Array of known substitutions */ int nSubs; /* Number of elements in subs array */ Tcl_DString *dStrPtr; { register char *chunk, *p; char letter; char percentSign; int i; /* * Scan through the copy of the input string, look for * the next '%' character, and try to make the substitution. * Continue doing this to the end of the string. */ chunk = p = string; while ((p = strchr(p, '%')) != NULL) { /* Copy up to the percent sign. Repair the string afterwards */ percentSign = *p; *p = '\0'; Tcl_DStringAppend(dStrPtr, chunk, -1); *p = percentSign; /* Search for a matching substition rule */ letter = *(p + 1); for (i = 0; i < nSubs; i++) { if (subsArr[i].letter == letter) { break; } } if (i < nSubs) { /* Make the substitution */ Tcl_DStringAppend(dStrPtr, subsArr[i].value, -1); } else { /* Copy in the %letter verbatim */ char verbatim[3]; verbatim[0] = '%'; verbatim[1] = letter; verbatim[2] = '\0'; Tcl_DStringAppend(dStrPtr, verbatim, -1); } p += 2; /* Skip % + letter */ if (letter == '\0') { p += 1; /* Premature % substitution (end of string) */ } chunk = p; } /* Pick up last chunk if a substition wasn't the last thing in the string */ if (*chunk != '\0') { Tcl_DStringAppend(dStrPtr, chunk, -1); } return Tcl_DStringValue(dStrPtr); } static int DragOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { int x, y; Token *tokenPtr; int status; Source *srcPtr; SubstDescriptors subst[2]; int active; char *result; /* * HANDLE: drag&drop drag */ if (argc != 5) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " drag pathname x y\"", (char *)NULL); return TCL_ERROR; } if ((GetSource(interp, argv[2], &srcPtr) != TCL_OK) || (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) { return TCL_ERROR; } tokenPtr = &(srcPtr->token); tokenPtr->lastX = locX = x; /* Save drag&drop location */ tokenPtr->lastY = locY = y; /* If HideToken() is pending, then do it now! */ if (tokenPtr->timer != 0) { Tk_DeleteTimerHandler(tokenPtr->timer); HideToken(tokenPtr); } /* * If pkgCmd is in progress, then ignore subsequent calls * until it completes. Only perform drag if pkgCmd * completed successfully and token window is mapped. */ if ((!Tk_IsMapped(tokenPtr->tkwin)) && (!srcPtr->pkgCmdInProgress)) { Tcl_DString cmdStr; /* * No list of send handlers? Then source is disabled. * Abort drag quietly. */ if (srcPtr->sendTypes == NULL) { return TCL_OK; } /* * No token command? Then cannot build token. * Signal error. */ if (srcPtr->pkgCmd == NULL) { Tcl_AppendResult(interp, "missing -packagecmd: ", argv[2], (char *)NULL); return TCL_ERROR; } /* * Execute token command to initialize token window. */ srcPtr->pkgCmdInProgress = TRUE; subst[0].letter = 'W'; subst[0].value = Tk_PathName(srcPtr->tkwin); subst[1].letter = 't'; subst[1].value = Tk_PathName(tokenPtr->tkwin); Tcl_DStringInit(&cmdStr); status = Tcl_Eval(srcPtr->interp, ExpandPercents(srcPtr->pkgCmd, subst, 2, &cmdStr)); Tcl_DStringFree(&cmdStr); srcPtr->pkgCmdInProgress = FALSE; result = Tcl_GetStringResult(interp); /* * Null string from the package command? * Then quietly abort the drag&drop operation. */ if (result[0] == '\0') { return TCL_OK; } /* Save result of token command for send command. */ if (srcPtr->pkgCmdResult != NULL) { free((char *)srcPtr->pkgCmdResult); } srcPtr->pkgCmdResult = strdup(result); if (status != TCL_OK) { /* * Token building failed. If an error handler is defined, * then signal the error. Otherwise, abort quietly. */ if ((errorCmd != NULL) && (errorCmd[0] != '\0')) { return Tcl_VarEval(interp, errorCmd, " {", result, "}", (char *)NULL); } return TCL_OK; } /* Install token cursor. */ if (tokenPtr->cursor != None) { Tk_Cursor cursor; /* Save the old cursor */ cursor = GetWidgetCursor(srcPtr->interp, srcPtr->tkwin); if (srcPtr->cursor != None) { Tk_FreeCursor(srcPtr->display, srcPtr->cursor); } srcPtr->cursor = cursor; /* Temporarily install the token cursor */ Tk_DefineCursor(srcPtr->tkwin, tokenPtr->cursor); } /* * Get ready to drag token window... * 1) Cache info for all windows on root * 2) Map token window to begin drag operation */ if (srcPtr->rootPtr != NULL) { RemoveWindow(srcPtr->rootPtr); } InitRoot(srcPtr); nActive++; /* One more drag&drop window active */ if (Tk_WindowId(tokenPtr->tkwin) == None) { Tk_MakeWindowExist(tokenPtr->tkwin); } if (!Tk_IsMapped(tokenPtr->tkwin)) { Tk_MapWindow(tokenPtr->tkwin); } RaiseToken(tokenPtr); } /* Arrange to update status of token window. */ Tk_CancelIdleCall(UpdateToken, (ClientData)srcPtr); active = OverTarget(srcPtr, x, y); if (tokenPtr->active != active) { tokenPtr->active = active; Tk_DoWhenIdle(UpdateToken, (ClientData)srcPtr); } MoveToken(srcPtr, tokenPtr); /* Move token window to current drag point. */ return TCL_OK; } /* * HANDLE: drag&drop drop */ static int DropOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { Source *srcPtr; Token *tokenPtr; int x, y; if (argc < 5) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " drop pathname x y\"", (char *)NULL); return TCL_ERROR; } if ((GetSource(interp, argv[2], &srcPtr) != TCL_OK) || (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) { return TCL_ERROR; } tokenPtr = &(srcPtr->token); tokenPtr->lastX = locX = x; /* Save drag&drop location */ tokenPtr->lastY = locY = y; /* Put the cursor back to its usual state. */ if (srcPtr->cursor == None) { Tk_UndefineCursor(srcPtr->tkwin); } else { Tk_DefineCursor(srcPtr->tkwin, srcPtr->cursor); } Tk_CancelIdleCall(UpdateToken, (ClientData)srcPtr); /* * Make sure that token window was not dropped before it * was either mapped or packed with info. */ if ((Tk_IsMapped(tokenPtr->tkwin)) && (!srcPtr->pkgCmdInProgress)) { int active; active = OverTarget(srcPtr, tokenPtr->lastX, tokenPtr->lastY); if (tokenPtr->active != active) { tokenPtr->active = active; UpdateToken((ClientData)srcPtr); } if (srcPtr->sendTypes != NULL) { if (tokenPtr->active) { DndSend(srcPtr); } else { HideToken(tokenPtr); } } nActive--; /* One fewer active token window. */ } return TCL_OK; } /* * HANDLE: drag&drop errors ?? */ static int ErrorsOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { if (argc == 3) { if (errorCmd) { free((char *)errorCmd); } errorCmd = strdup(argv[2]); } else if (argc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " errors ?proc?\"", (char *)NULL); return TCL_ERROR; } Tcl_SetResult(interp, errorCmd, TCL_VOLATILE); return TCL_OK; } /* * HANDLE: drag&drop active */ static int ActiveOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { if (argc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " active\"", (char *)NULL); return TCL_ERROR; } Tcl_SetResult(interp, (nActive > 0) ? "1" : "0", TCL_STATIC); return TCL_OK; } /* * HANDLE: drag&drop location ? ? */ static int LocationOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { if ((argc != 2) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " location ?x y?\"", (char *)NULL); return TCL_ERROR; } if (argc == 4) { int x, y; if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) { return TCL_ERROR; } locX = x; locY = y; } Tcl_AppendElement(interp, Blt_Itoa(locX)); Tcl_AppendElement(interp, Blt_Itoa(locY)); return TCL_OK; } /* * HANDLE: drag&drop token */ static int TokenOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { Source *srcPtr; if (GetSource(interp, argv[2], &srcPtr) != TCL_OK) { return TCL_ERROR; } if ((argc > 3) && (ConfigureToken(interp, srcPtr, argc - 3, argv + 3) != TCL_OK)) { return TCL_ERROR; } Tcl_SetResult(interp, Tk_PathName(srcPtr->token.tkwin), TCL_STATIC); return TCL_OK; } static int HandlerOpOp(srcPtr, interp, argc, argv) Source *srcPtr; Tcl_Interp *interp; int argc; char **argv; { Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; int isNew; char *cmd; /* * HANDLE: drag&drop source handler \ * ?? ?...? */ if (argc == 4) { /* Show source handler data types */ for (hPtr = Tcl_FirstHashEntry(&(srcPtr->handlerTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { Tcl_AppendElement(interp, Tcl_GetHashKey(&(srcPtr->handlerTable), hPtr)); } return TCL_OK; } hPtr = Tcl_CreateHashEntry(&(srcPtr->handlerTable), argv[4], &isNew); /* * HANDLE: drag&drop source handler * * Create the new type if it doesn't already * exist, and return the code associated with it. */ if (argc == 5) { cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd == NULL) { cmd = ""; } Tcl_SetResult(interp, cmd, TCL_STATIC); return TCL_OK; } /* * HANDLE: drag&drop source handler \ * ?...? * * Create the new type and set its command */ cmd = Tcl_Concat(argc - 5, argv + 5); Tcl_SetHashValue(hPtr, (ClientData)cmd); return TCL_OK; } /* * HANDLE: drag&drop source * drag&drop source ?options...? * drag&drop source handler ?? ? ...? */ static int SourceOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { Source *srcPtr; int isNew; Token *tokenPtr; if (argc == 2) { Tcl_HashSearch cursor; Tcl_HashEntry *hPtr; Tk_Window tkwin; for (hPtr = Tcl_FirstHashEntry(&sourceTable, &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { tkwin = (Tk_Window)Tcl_GetHashKey(&sourceTable, hPtr); Tcl_AppendElement(interp, Tk_PathName(tkwin)); } return TCL_OK; } /* * Find or create source info... */ srcPtr = CreateSource(interp, argv[2], &isNew); if (srcPtr == NULL) { return TCL_ERROR; } tokenPtr = &(srcPtr->token); if (argc > 3) { char c; int length; int status; /* * HANDLE: drag&drop source ?options...? */ c = argv[3][0]; length = strlen(argv[3]); if (c == '-') { if (argc == 3) { status = Tk_ConfigureInfo(interp, tokenPtr->tkwin, configSpecs, (char *)srcPtr, (char *)NULL, 0); } else if (argc == 4) { status = Tk_ConfigureInfo(interp, tokenPtr->tkwin, configSpecs, (char *)srcPtr, argv[3], 0); } else { status = ConfigureSource(interp, srcPtr, argc - 3, argv + 3, TK_CONFIG_ARGV_ONLY); } if (status != TCL_OK) { return TCL_ERROR; } } else if ((c == 'h') && strncmp(argv[3], "handler", length) == 0) { return HandlerOpOp(srcPtr, interp, argc, argv); } else { Tcl_AppendResult(interp, "bad operation \"", argv[3], "\": must be \"handler\" or a configuration option", (char *)NULL); return TCL_ERROR; } } if (isNew) { /* * Create the window for the drag&drop token... */ if (CreateToken(interp, srcPtr) != TCL_OK) { DestroySource(srcPtr); return TCL_ERROR; } } return TCL_OK; } /* * HANDLE: drag&drop target ?? ?handling info...? */ static int TargetOp(interp, argc, argv) Tcl_Interp *interp; int argc; char **argv; { SubstDescriptors subst[2]; Tk_Window tkwin; Tcl_HashEntry *hPtr; Target *targetPtr; int isNew; if (argc == 2) { Tcl_HashSearch cursor; for (hPtr = Tcl_FirstHashEntry(&targetTable, &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { tkwin = (Tk_Window)Tcl_GetHashKey(&targetTable, hPtr); Tcl_AppendElement(interp, Tk_PathName(tkwin)); } return TCL_OK; } tkwin = Tk_NameToWindow(interp, argv[2], Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } targetPtr = FindTarget(tkwin); if (targetPtr == NULL) { targetPtr = CreateTarget(interp, tkwin); } if (targetPtr == NULL) { return TCL_ERROR; } if ((argc >= 4) && (strcmp(argv[3], "handler") == 0)) { /* * HANDLE: drag&drop target handler * drag&drop target handler ? ...? */ if (argc == 4) { Tcl_HashSearch cursor; for (hPtr = Tcl_FirstHashEntry(&(targetPtr->handlerTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { Tcl_AppendElement(interp, Tcl_GetHashKey(&(targetPtr->handlerTable), hPtr)); } return TCL_OK; } else if (argc >= 6) { char *cmd; /* * Process handler definition */ hPtr = Tcl_CreateHashEntry(&(targetPtr->handlerTable), argv[4], &isNew); cmd = Tcl_Concat(argc - 5, argv + 5); if (hPtr != NULL) { char *oldCmd; oldCmd = (char *)Tcl_GetHashValue(hPtr); if (oldCmd != NULL) { free(oldCmd); } } Tcl_SetHashValue(hPtr, (ClientData)cmd); /* * Update the target property on the window. */ AddTargetProperty(interp, targetPtr); return TCL_OK; } Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ", argv[1], " ", argv[2], " ", argv[3], " data command ?arg arg...?", (char *)NULL); return TCL_ERROR; } else if ((argc >= 4) && (strcmp(argv[3], "handle") == 0)) { /* * HANDLE: drag&drop target handle ?? */ Tcl_DString cmdStr; int result; char *cmd; if (argc < 5 || argc > 6) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ", argv[1], " ", argv[2], " handle data ?value?", (char *)NULL); return TCL_ERROR; } hPtr = Tcl_FindHashEntry(&(targetPtr->handlerTable), argv[4]); if (hPtr == NULL) { Tcl_AppendResult(interp, "target cannot handle datatype: ", argv[4], (char *)NULL); return TCL_ERROR; /* no handler found */ } cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd != NULL) { subst[0].letter = 'W'; subst[0].value = Tk_PathName(targetPtr->tkwin); subst[1].letter = 'v'; if (argc > 5) { subst[1].value = argv[5]; } else { subst[1].value = ""; } Tcl_DStringInit(&cmdStr); result = Tcl_Eval(interp, ExpandPercents(cmd, subst, 2, &cmdStr)); Tcl_DStringFree(&cmdStr); return result; } return TCL_OK; } Tcl_AppendResult(interp, "usage: ", argv[0], " target ", argv[2], " handler ?data command arg arg...?\n or: ", argv[0], " target ", argv[2], " handle ", (char *)NULL); return TCL_ERROR; } /* * ------------------------------------------------------------------------ * * DragDropCmd -- * * Invoked by TCL whenever the user issues a drag&drop command. * Handles the following syntax: * * drag&drop source * drag&drop source ?options...? * drag&drop source handler ?? ? ...? * * drag&drop target * drag&drop target handler ? ...? * drag&drop target handle ?? * * drag&drop token * drag&drop drag * drag&drop drop * * drag&drop errors ?? * drag&drop active * drag&drop location ? ? * * ------------------------------------------------------------------------ */ static int DragDropCmd(clientData, interp, argc, argv) ClientData clientData; /* Registration information */ Tcl_Interp *interp; /* current interpreter */ int argc; /* number of arguments */ char **argv; /* argument strings */ { int length; char c; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " oper ?args?\"", (char *)NULL); return TCL_ERROR; } c = argv[1][0]; length = strlen(argv[1]); if ((c == 's') && strncmp(argv[1], "source", length) == 0) { return SourceOp(interp, argc, argv); } else if ((c == 't') && (length >= 2) && (strncmp(argv[1], "target", length) == 0)) { return TargetOp(interp, argc, argv); } else if ((c == 't') && (length >= 2) && (strncmp(argv[1], "token", length) == 0)) { return TokenOp(interp, argc, argv); } else if ((c == 'd') && strncmp(argv[1], "drag", length) == 0) { return DragOp(interp, argc, argv); } else if ((c == 'd') && strncmp(argv[1], "drop", length) == 0) { return DropOp(interp, argc, argv); } else if ((c == 'e') && strncmp(argv[1], "errors", length) == 0) { return ErrorsOp(interp, argc, argv); } else if ((c == 'a') && strncmp(argv[1], "active", length) == 0) { return ActiveOp(interp, argc, argv); } else if ((c == 'l') && strncmp(argv[1], "location", length) == 0) { return LocationOp(interp, argc, argv); } /* * Report improper command arguments */ Tcl_AppendResult(interp, "bad operation \"", argv[1], "\": must be active, drag, drop, errors, location, ", "source, target or token", (char *)NULL); return TCL_ERROR; } /* * ------------------------------------------------------------------------ * * Blt_DragDropInit -- * * Adds the drag&drop command to the given interpreter. Should * be invoked to properly install the command whenever a new * interpreter is created. * * ------------------------------------------------------------------------ */ int Blt_DragDropInit(interp) Tcl_Interp *interp; /* interpreter to be updated */ { static Blt_CmdSpec cmdSpec = { "drag&drop", DragDropCmd, }; if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) { return TCL_ERROR; } if (!initialized) { Tcl_InitHashTable(&sourceTable, TCL_ONE_WORD_KEYS); Tcl_InitHashTable(&targetTable, TCL_ONE_WORD_KEYS); errorCmd = strdup(DEF_ERROR_PROC); nActive = 0; locX = locY = 0; initialized = TRUE; #ifndef WIN32 dndAtom = XInternAtom(Tk_Display(Tk_MainWindow(interp)), propName, False); #endif } return TCL_OK; } #endif /* NO_DRAGDROP */ tkdesk-2.0/blt/bltInit.c0100644000175000007640000003357010020457430013262 0ustar jccjcc/* * bltInit.c -- * * This module initials the BLT toolkit, registering its commands * with the Tcl/Tk interpreter. * * Copyright 1991-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #include #ifdef __STDC__ static Tcl_MathProc MinMathProc, MaxMathProc; #endif /* Exclude commands not needed by TkDesk: */ #define NO_HTEXT #define NO_GRAPH #define NO_TABLE #define NO_WINOP #define NO_BITMAP #define NO_DEBUG #define NO_WATCH #define NO_VECTOR #define NO_SPLINE #define NO_BELL #define NO_CUTBUFFER #define NO_TILEFRAME #define NO_TILEBUTTON #define NO_TILESCROLLBAR #define NO_BITMAP #define NO_EPS #define NO_HIERBOX #define NO_TABSET #define NO_CONTAINER static Tcl_AppInitProc *initProcArr[] = { #ifndef NO_GRAPH Blt_GraphInit, #endif #ifndef NO_TABLE Blt_TableInit, #endif #ifndef NO_HIERBOX Blt_HierboxInit, #endif #ifndef NO_TABSET Blt_TabsetInit, #endif #ifndef NO_HTEXT Blt_HtextInit, #endif #ifndef NO_BUSY Blt_BusyInit, #endif #ifndef NO_WINOP Blt_WinopInit, #endif #ifndef NO_BITMAP Blt_BitmapInit, #endif #ifndef NO_BGEXEC Blt_BgexecInit, #endif #ifndef NO_DRAGDROP Blt_DragDropInit, #endif #ifndef NO_DND Blt_DndInit, #endif #ifndef NO_DEBUG Blt_DebugInit, #endif #ifndef NO_WATCH Blt_WatchInit, #endif #ifndef NO_CONTAINER Blt_ContainerInit, #endif #ifndef NO_VECTOR Blt_VectorInit, #endif #ifndef NO_SPLINE Blt_SplineInit, #endif #ifndef NO_BELL Blt_BeepInit, #endif #ifndef NO_CUTBUFFER Blt_CutbufferInit, #endif #ifndef NO_PRINTER Blt_PrinterInit, #endif #ifndef NO_TILEFRAME Blt_FrameInit, #endif #ifndef NO_TILEBUTTON Blt_ButtonInit, #endif #ifndef NO_TILESCROLLBAR Blt_ScrollbarInit, #endif #if (BLT_MAJOR_VERSION == 3) #ifndef NO_MOUNTAIN Blt_MountainInit, #endif #endif #ifndef NO_TED Blt_TedInit, #endif (Tcl_AppInitProc *) NULL }; /* ARGSUSED */ static int MinMathProc(clientData, interp, argsPtr, resultPtr) ClientData clientData; /* Unused */ Tcl_Interp *interp; Tcl_Value *argsPtr; Tcl_Value *resultPtr; { Tcl_Value *op1Ptr, *op2Ptr; op1Ptr = argsPtr, op2Ptr = argsPtr + 1; if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) { resultPtr->intValue = MIN(op1Ptr->intValue, op2Ptr->intValue); resultPtr->type = TCL_INT; } else { double a, b; a = (op1Ptr->type == TCL_INT) ? (double)op1Ptr->intValue : op1Ptr->doubleValue; b = (op2Ptr->type == TCL_INT) ? (double)op2Ptr->intValue : op2Ptr->doubleValue; resultPtr->doubleValue = MIN(a, b); resultPtr->type = TCL_DOUBLE; } return TCL_OK; } /*ARGSUSED*/ static int MaxMathProc(clientData, interp, argsPtr, resultPtr) ClientData clientData; /* Unused */ Tcl_Interp *interp; Tcl_Value *argsPtr; Tcl_Value *resultPtr; { Tcl_Value *op1Ptr, *op2Ptr; op1Ptr = argsPtr, op2Ptr = argsPtr + 1; if ((op1Ptr->type == TCL_INT) && (op2Ptr->type == TCL_INT)) { resultPtr->intValue = MAX(op1Ptr->intValue, op2Ptr->intValue); resultPtr->type = TCL_INT; } else { double a, b; a = (op1Ptr->type == TCL_INT) ? (double)op1Ptr->intValue : op1Ptr->doubleValue; b = (op2Ptr->type == TCL_INT) ? (double)op2Ptr->intValue : op2Ptr->doubleValue; resultPtr->doubleValue = MAX(a, b); resultPtr->type = TCL_DOUBLE; } return TCL_OK; } #ifndef BLT_LIBRARY #ifdef WIN32 #define BLT_LIBRARY "c:/Program Files/Tcl/lib/blt"##BLT_VERSION #else #define BLT_LIBRARY "" #endif #endif static char libPath[1024] = { BLT_LIBRARY }; /* * Script to set the BLT library path in the variable global "blt_library" * * Checks the usual locations for a file (bltGraph.tcl) from the BLT * library. The places searched in order are * * $BLT_LIBRARY * $BLT_LIBRARY/blt2.4 * $BLT_LIBRARY/.. * $BLT_LIBRARY/../blt2.4 * $blt_libPath * $blt_libPath/blt2.4 * $blt_libPath/.. * $blt_libPath/../blt2.4 * $tk_library * $tk_library/blt2.4 * $tk_library/.. * $tk_library/../blt2.4 * $env(TK_LIBRARY) * $env(TK_LIBRARY)/blt2.4 * $env(TK_LIBRARY)/.. * $env(TK_LIBRARY)/../blt2.4 * * The Tcl variable "blt_library" is set to the discovered path. * If the file wasn't found, no error is returned. The actual * usage of $blt_library is purposely deferred so that it can be * set from within a script. */ static char initScript[] = {"\n\ global blt_library blt_libPath tk_library env \n\ set blt_library {}\n\ set path {}\n\ foreach var { env(BLT_LIBRARY) blt_libPath tk_library env(TK_LIBRARY) } { \n\ if { ![info exists $var] } { \n\ continue \n\ } \n\ set path [set $var] \n\ if { [file readable [file join $path bltDnd.tcl]] } { \n\ set blt_library $path\n\ break \n\ } \n\ set path [file join $path blt$blt_version ] \n\ if { [file readable [file join $path bltGraph.tcl]] } { \n\ set blt_library $path\n\ break \n\ } \n\ set path [file dirname [set $var]] \n\ if { [file readable [file join $path bltGraph.tcl]] } { \n\ set blt_library $path\n\ break \n\ } \n\ set path [file join $path blt$blt_version ] \n\ if { [file readable [file join $path bltGraph.tcl]] } { \n\ set blt_library $path\n\ break \n\ } \n\ } \n\ if { $blt_library != \"\" } { \n\ global auto_path \n\ lappend auto_path $blt_library \n\ }\n\ unset var path\n\ \n" }; static int GetVersionInfo(interp) Tcl_Interp *interp; { Tcl_DString ds; char *value; int exact = TRUE; #ifdef WIN32 HKEY key; DWORD result; #ifndef BLT_REGISTRY_KEY #define BLT_REGISTRY_KEY "Software\\BLT\\" BLT_VERSION "\\" TCL_VERSION #endif #endif /*WIN32*/ /* * Check that the version of Tk we've loaded is the same that BLT was * compiled and linked against. */ if (Tcl_PkgRequire(interp, "Tk", TK_VERSION, exact) == NULL) { return TCL_ERROR; } /* Set the version variable. We'll use it in the following script. */ value = Tcl_SetVar(interp, "blt_version", BLT_VERSION, TCL_GLOBAL_ONLY); if (value == NULL) { return TCL_ERROR; } Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, libPath, -1); #ifdef WIN32 result = RegOpenKeyEx( HKEY_LOCAL_MACHINE, /* Parent key. */ BLT_REGISTRY_KEY, /* Path to sub-key. */ 0, /* Reserved. */ KEY_READ, /* Security access mask. */ &key); /* Resulting key.*/ if (result == ERROR_SUCCESS) { DWORD size; /* Query once to get the size of the string needed */ result = RegQueryValueEx(key, "BLT_LIBRARY", NULL, NULL, NULL, &size); if (result == ERROR_SUCCESS) { Tcl_DStringSetLength(&ds, size); /* And again to collect the string. */ RegQueryValueEx(key, "BLT_LIBRARY", NULL, NULL, (LPBYTE) Tcl_DStringValue(&ds), &size); RegCloseKey(key); } } #endif /* WIN32 */ value = Tcl_SetVar(interp, "blt_libPath", Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG); Tcl_DStringFree(&ds); if (value == NULL) { return TCL_ERROR; } if (Tcl_Eval(interp, initScript) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } #if (TCL_MAJOR_VERSION > 7) /*LINTLIBRARY*/ #ifdef WIN32 extern int bltPlatformId; /* *---------------------------------------------------------------------- * * DllMain -- * * This wrapper function is used by Windows to invoke the * initialization code for the DLL. * * Results: * Returns TRUE; * * Side effects: * None. * *---------------------------------------------------------------------- */ BOOL APIENTRY DllMain(hInst, reason, reserved) HINSTANCE hInst; /* Library instance handle. */ DWORD reason; /* Reason this function is being called. */ LPVOID reserved; /* Not used. */ { return TRUE; } #endif EXPORT int Blt_Init(interp) Tcl_Interp *interp; /* Interpreter to add extra commands */ { register Tcl_AppInitProc **procPtrPtr; Tcl_ValueType argTypes[2]; Tcl_Namespace *spacePtr; if (GetVersionInfo(interp) != TCL_OK) { return TCL_ERROR; } spacePtr = Tcl_CreateNamespace(interp, "blt::tile", (ClientData)0, (Tcl_NamespaceDeleteProc *) NULL); if (spacePtr == NULL) { return TCL_ERROR; } for (procPtrPtr = initProcArr; *procPtrPtr != NULL; procPtrPtr++) { if ((**procPtrPtr) (interp) != TCL_OK) { Tcl_DeleteNamespace(spacePtr); return TCL_ERROR; } } if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) { return TCL_ERROR; } argTypes[0] = argTypes[1] = TCL_EITHER; Tcl_CreateMathFunc(interp, "min", 2, argTypes, MinMathProc,(ClientData)0); Tcl_CreateMathFunc(interp, "max", 2, argTypes, MaxMathProc,(ClientData)0); #ifndef NO_EPS Blt_InitEpsCanvasItem(interp); #endif return TCL_OK; } #else /*LINTLIBRARY*/ EXPORT int Blt_Init(interp) Tcl_Interp *interp; /* Interpreter to add extra commands */ { register Tcl_AppInitProc **procPtrPtr; Tcl_ValueType argTypes[2]; #ifdef ITCL_NAMESPACES Itcl_Namespace dummy, spaceId; /* Token for "blt" namespace * created, used to delete the * namespace on errors. */ #endif if (GetVersionInfo(interp) != TCL_OK) { return TCL_ERROR; } #ifdef ITCL_NAMESPACES if (Itcl_CreateNamesp(interp, "blt", (ClientData)0, (Itcl_DeleteProc *) 0, &spaceId) != TCL_OK) { return TCL_ERROR; } if (Itcl_CreateNamesp(interp, "blt::tile", (ClientData)0, (Itcl_DeleteProc *) 0, &dummy) != TCL_OK) { return TCL_ERROR; } #endif if (Tcl_PkgProvide(interp, "BLT", BLT_VERSION) != TCL_OK) { return TCL_ERROR; } for (procPtrPtr = initProcArr; *procPtrPtr != NULL; procPtrPtr++) { if ((**procPtrPtr) (interp) != TCL_OK) { #ifdef ITCL_NAMESPACES Itcl_DeleteNamesp(spaceId); #endif return TCL_ERROR; } } argTypes[0] = argTypes[1] = TCL_EITHER; Tcl_CreateMathFunc(interp, "min", 2, argTypes, MinMathProc,(ClientData)0); Tcl_CreateMathFunc(interp, "max", 2, argTypes, MaxMathProc,(ClientData)0); #ifndef NO_EPS Blt_InitEpsCanvasItem(interp); #endif return TCL_OK; } #endif /* TCL_MAJOR_VERION >= 8 */ /*LINTLIBRARY*/ EXPORT int Blt_SafeInit(interp) Tcl_Interp *interp; /* Interpreter to add extra commands */ { return Blt_Init(interp); } /* *---------------------------------------------------------------------- * * Blt_InitCmd -- * * Given the name of a command, return a pointer to the * clientData field of the command. * * Results: * A standard TCL result. If the command is found, TCL_OK * is returned and clientDataPtr points to the clientData * field of the command (if the clientDataPtr in not NULL). * * Side effects: * If the command is found, clientDataPtr is set to the address * of the clientData of the command. If not found, an error * message is left in interp->result. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ Tcl_Command Blt_InitCmd(interp, nameSpace, specPtr) Tcl_Interp *interp; char *nameSpace; Blt_CmdSpec *specPtr; { char *cmdPath; Tcl_DString dString; Tcl_Command cmdToken; Tcl_DStringInit(&dString); #if HAVE_NAMESPACES if (nameSpace != NULL) { Tcl_DStringAppend(&dString, nameSpace, -1); } Tcl_DStringAppend(&dString, "::", -1); #endif /* HAVE_NAMESPACES */ Tcl_DStringAppend(&dString, specPtr->name, -1); cmdPath = Tcl_DStringValue(&dString); #if HAVE_NAMESPACES cmdToken = Tcl_FindCommand(interp, cmdPath, (Tcl_Namespace *)NULL, 0); #else cmdToken = NULL; #endif /* HAVE_NAMESPACES */ if (cmdToken != NULL) { Tcl_DStringFree(&dString); return cmdToken; /* Assume command was already initialized */ } cmdToken = Tcl_CreateCommand(interp, cmdPath, specPtr->cmdProc, specPtr->clientData, specPtr->cmdDeleteProc); Tcl_DStringFree(&dString); #if (HAVE_NAMESPACES) && (TCL_MAJOR_VERSION > 7) { Tcl_Namespace *nsPtr; int dontResetList = 0; nsPtr = Tcl_FindNamespace(interp, nameSpace, (Tcl_Namespace *)NULL, TCL_LEAVE_ERR_MSG); if (nsPtr == NULL) { return NULL; } if (Tcl_Export(interp, nsPtr, specPtr->name, dontResetList) != TCL_OK) { return NULL; } } #endif /* TCL_MAJOR_VERSION > 7 */ return cmdToken; } /* *---------------------------------------------------------------------- * * Blt_InitCmds -- * * Given the name of a command, return a pointer to the * clientData field of the command. * * Results: * A standard TCL result. If the command is found, TCL_OK * is returned and clientDataPtr points to the clientData * field of the command (if the clientDataPtr in not NULL). * * Side effects: * If the command is found, clientDataPtr is set to the address * of the clientData of the command. If not found, an error * message is left in interp->result. * *---------------------------------------------------------------------- */ int Blt_InitCmds(interp, nameSpace, specPtr, nCmds) Tcl_Interp *interp; char *nameSpace; Blt_CmdSpec *specPtr; int nCmds; { register int i; for (i = 0; i < nCmds; i++) { if (Blt_InitCmd(interp, nameSpace, specPtr) == NULL) { return TCL_ERROR; } specPtr++; } return TCL_OK; } tkdesk-2.0/blt/bltInt.h0100644000175000007640000010424610020457430013115 0ustar jccjcc/* * bltInt.h -- * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #ifndef _BLT_INT_H #define _BLT_INT_H #ifdef WIN32 #define STRICT #define WIN32_LEAN_AND_MEAN #include #undef STRICT #undef WIN32_LEAN_AND_MEAN #include #endif /* WIN32 */ #include #include #define _VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #define TCL_VERSION_NUMBER _VERSION(TCL_MAJOR_VERSION, TCL_MINOR_VERSION, TCL_RELEASE_SERIAL) #define TK_VERSION_NUMBER _VERSION(TK_MAJOR_VERSION, TK_MINOR_VERSION, TK_RELEASE_SERIAL) #include "config.h" #include "bltTkInt.h" #include #include #include /* #ifdef WIN32 #include "bltWinConfig.h" #else #include "bltConfig.h" #endif */ #include "blt.h" #ifdef HAVE_STDLIB_H #include #endif /* HAVE_STDLIB_H */ #ifdef HAVE_STRING_H #include #endif /* HAVE_STRING_H */ #ifdef HAVE_ERRNO_H #include #endif /* HAVE_ERRNO_H */ #ifdef HAVE_CTYPE_H #include #endif /* HAVE_CTYPE_H */ #ifdef HAVE_MEMORY_H #include #endif /* HAVE_MEMORY_H */ #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #ifdef HAVE_MALLOC_H #include #endif /* HAVE_MALLOC_H */ #ifdef HAVE_FLOAT_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* M_PI */ #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 #endif #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880 #endif /* M_SQRT2 */ #ifndef M_SQRT1_2 #define M_SQRT1_2 0.70710678118654752440 #endif /* M_SQRT1_2 */ #ifndef SHRT_MAX #define SHRT_MAX 0x7FFF #endif /* SHRT_MAX */ #ifndef SHRT_MIN #define SHRT_MIN -(SHRT_MAX) #endif /* SHRT_MAX */ #ifndef USHRT_MAX #define USHRT_MAX 0xFFFF #endif /* USHRT_MAX */ #ifndef INT_MAX #define INT_MAX 2147483647 #endif /* INT_MAX */ #ifndef HAVE_FLOAT_H /* * ---------------------------------------------------------------------- * * DBL_MIN, DBL_MAX -- * * DBL_MAX and DBL_MIN are the largest and smaller double * precision numbers that can be represented by the floating * point hardware. If the compiler is ANSI, they can be found in * float.h. Otherwise, we use HUGE_VAL or HUGE to determine * them. * * ---------------------------------------------------------------------- */ /* * Don't want to include __infinity (definition of HUGE_VAL (SC1.x)) */ #ifdef sun #define DBL_MAX 1.7976931348623157E+308 #define DBL_MIN 2.2250738585072014E-308 #define DBL_EPSILON 2.2204460492503131e-16 #else #ifndef DBL_EPSILON #define DBL_EPSILON BLT_DBL_EPSILON #endif #ifdef HUGE_VAL #define DBL_MAX HUGE_VAL #define DBL_MIN (1/HUGE_VAL) #else #ifdef HUGE #define DBL_MAX HUGE #define DBL_MIN (1/HUGE) #else /* * Punt: Assume values simple and relatively small */ #define DBL_MAX 3.40282347E+38 #define DBL_MIN 1.17549435E-38 #endif /*HUGE*/ #endif /*HUGE_VAL*/ #endif /*sun*/ #endif /*!HAVE_FLOAT_H*/ #undef INLINE #ifdef __GNUC__ #define INLINE inline #else #define INLINE #endif #undef EXPORT #define EXPORT #undef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #undef MAX #define MAX(a,b) (((a)>(b))?(a):(b)) /* * ---------------------------------------------------------------------- * * The following are macros replacing math library functions: * "fabs", "fmod", "abs", "rint", and "exp10". * * Although many of these routines may be in your math library, * they aren't used in libtcl.a or libtk.a. This makes it * difficult to dynamically load the BLT library as a shared * object unless the math library is also shared (which isn't * true on several systems). We can avoid the problem by * replacing the "exotic" math routines with macros. * * ---------------------------------------------------------------------- */ #undef ABS #define ABS(x) (((x)<0)?(-(x)):(x)) #undef EXP10 #define EXP10(x) (pow(10.0,(x))) #undef FABS #define FABS(x) (((x)<0.0)?(-(x)):(x)) #undef SIGN #define SIGN(x) (((x) < 0.0) ? -1 : 1) /* * Be careful when using the next two macros. They both assume the floating * point number is less than the size of an int. That means, for example, you * can't use these macros with numbers bigger than than 2^31-1. */ #undef FMOD #define FMOD(x,y) ((x)-(((int)((x)/(y)))*y)) #undef ROUND #define ROUND(x) ((int)((x) + (((x)<0.0) ? -0.5 : 0.5))) #define TRUE 1 #define FALSE 0 /* * The macro below is used to modify a "char" value (e.g. by casting * it to an unsigned character) so that it can be used safely with * macros such as isspace. */ #define UCHAR(c) ((unsigned char) (c)) #define PIXELS_NONNEGATIVE 0 #define PIXELS_POSITIVE 1 #define PIXELS_ANY 2 #define COUNT_NONNEGATIVE 0 #define COUNT_POSITIVE 1 #define COUNT_ANY 2 #define SCROLL_MODE_CANVAS (1<<0) #define SCROLL_MODE_LISTBOX (1<<1) #define SCROLL_MODE_HIERBOX (1<<2) #define RGB_ANTIQUEWHITE1 "#ffefdb" #define RGB_BISQUE1 "#ffe4c4" #define RGB_BISQUE2 "#eed5b7" #define RGB_BISQUE3 "#cdb79e" #define RGB_BLACK "#000000" #define RGB_BLUE "#0000ff" #define RGB_GREEN "#00ff00" #define RGB_GREY "#b0b0b0" #define RGB_GREY15 "#262626" #define RGB_GREY50 "#7f7f7f" #define RGB_GREY64 "#a3a3a3" #define RGB_GREY70 "#b3b3b3" #define RGB_GREY75 "#bfbfbf" #define RGB_GREY77 "#c3c3c3" #define RGB_GREY82 "#d1d1d1" #define RGB_GREY85 "#d9d9d9" #define RGB_GREY90 "#e5e5e5" #define RGB_GREY95 "#f2f2f2" #define RGB_LIGHTBLUE0 "#e4f7ff" #define RGB_LIGHTBLUE1 "#bfefff" #define RGB_LIGHTBLUE2 "#b2dfee" #define RGB_LIGHTSKYBLUE1 "#b0e2ff" #define RGB_MAROON "#b03060" #define RGB_NAVYBLUE "#000080" #define RGB_PINK "#ffc0cb" #define RGB_RED "#ff0000" #define RGB_WHITE "#ffffff" #define RGB_YELLOW "#ffff00" #ifdef OLD_TK_COLORS #define STD_COLOR_NORMAL_BG RGB_BISQUE1 #define STD_COLOR_ACTIVE_BG RGB_BISQUE2 #define STD_COLOR_SELECT_BG RGB_LIGHTBLUE2 #define STD_COLOR_DISABLE_FG RGB_GREY64 #else #define STD_COLOR_NORMAL_BG RGB_GREY85 #define STD_COLOR_ACTIVE_BG RGB_GREY64 #define STD_COLOR_SELECT_BG RGB_GREY77 #define STD_COLOR_DISABLE_FG RGB_GREY64 #endif /* OLD_TK_COLORS */ #define STD_COLOR_INDICATOR RGB_MAROON #define STD_COLOR_ACTIVE_FG RGB_BLACK #define STD_COLOR_NORMAL_FG RGB_BLACK #define STD_COLOR_SELECT_FG RGB_BLACK #define STD_COLOR_SHADOW RGB_GREY64 #define STD_MONO_ACTIVE_BG RGB_BLACK #define STD_MONO_ACTIVE_FG RGB_WHITE #define STD_MONO_NORMAL_BG RGB_WHITE #define STD_MONO_NORMAL_FG RGB_BLACK #define STD_MONO_SELECT_BG RGB_BLACK #define STD_MONO_SELECT_FG RGB_WHITE #define STD_MONO_SHADOW RGB_BLACK #define STD_SELECT_BORDERWIDTH "2" #define STD_BORDERWIDTH "2" #define STD_FONT_HUGE "*-Helvetica-Medium-R-Normal-*-18-180-*" #define STD_FONT_LARGE "*-Helvetica-Medium-R-Normal-*-14-140-*" #define STD_FONT "*-Helvetica-Medium-R-Normal-*-12-120-*" #define STD_FONT_SMALL "*-Helvetica-Medium-R-Normal-*-10-100-*" #ifdef WIN32 #undef STD_FONT #undef STD_FONT_SMALL #undef STD_FONT_LARGE #undef STD_COLOR_NORMAL_BG #undef STD_COLOR_NORMAL_FG #undef STD_COLOR_TEXT_FG #undef STD_COLOR_SELECT_BG #define STD_FONT "Arial 8" #define STD_FONT_SMALL "Arial 6" #define STD_FONT_LARGE "Arial 12" #define STD_COLOR_NORMAL_BG "SystemButtonFace" #define STD_COLOR_NORMAL_FG "SystemButtonText" #define STD_COLOR_TEXT_FG "SystemWindowText" #define STD_COLOR_SELECT_BG "SystemHighlight" #endif /* WIN32 */ #define LineWidth(w) (((w) > 1) ? (w) : 0) #undef VARARGS #ifdef __cplusplus #define ANYARGS (...) #define VARARGS(first) (first, ...) #define VARARGS2(first, second) (first, second, ...) #else #define ANYARGS () #define VARARGS(first) () #define VARARGS2(first, second) () #endif /* __cplusplus */ #if defined(ITCL_NAMESPACES) || (TCL_MAJOR_VERSION >= 8) #define HAVE_NAMESPACES 1 #else #define HAVE_NAMESPACES 0 #endif #ifdef TCL_UTF_MAX #define HAVE_UTF 1 #else #define HAVE_UTF 0 #endif /* TCL_UTF_MAX */ typedef char *DestroyData; /* * Tcl/Tk Backward compatibility section. */ #if (TCL_MAJOR_VERSION > 7) #define NO_FLAGS 0 #define Blt_FindPhoto(interp, name) Tk_FindPhoto(interp, name) #else #define Tcl_GetStringResult(interp) ((interp)->result) typedef struct Tcl_Namespace Tcl_Namespace; typedef struct Tcl_CallFrame *Tcl_CallFrame; #define Blt_FindPhoto(interp, name) Tk_FindPhoto(name) #endif /* TCL_MAJOR_VERSION > 7 */ #undef panic #define panic(mesg) Blt_Panic("%s:%d %s", __FILE__, __LINE__, (mesg)) /* * Since the Tcl/Tk distribution doesn't perform any asserts, dynamic * loading can fail to find the __assert function. As a workaround, * we'll include our own. */ #undef assert #ifdef NDEBUG #define assert(EX) ((void)0) #else extern void Blt_Assert _ANSI_ARGS_((char *testExpr, char *fileName, int lineNum)); #ifdef __STDC__ #define assert(EX) (void)((EX) || (Blt_Assert(#EX, __FILE__, __LINE__), 0)) #else #define assert(EX) (void)((EX) || (Blt_Assert("EX", __FILE__, __LINE__), 0)) #endif /* __STDC__ */ #endif /* NDEBUG */ typedef int *Blt_Tile; /* Opaque type for tiles */ typedef int (QSortCompareProc) _ANSI_ARGS_((const void *, const void *)); /* * ---------------------------------------------------------------------- * * Blt_CmdSpec -- * * ---------------------------------------------------------------------- */ typedef struct { char *name; /* Name of command */ Tcl_CmdProc *cmdProc; Tcl_CmdDeleteProc *cmdDeleteProc; ClientData clientData; } Blt_CmdSpec; /* * ---------------------------------------------------------------------- * * Blt_Operation -- * * Generic function prototype of CmdOptions. * * ---------------------------------------------------------------------- */ typedef int (*Blt_Operation) _ANSI_ARGS_(ANYARGS); /* * ---------------------------------------------------------------------- * * Blt_OpSpec -- * * Structure to specify a set of operations for a Tcl command. * This is passed to the Blt_GetOperation procedure to look * for a function pointer associated with the operation name. * * ---------------------------------------------------------------------- */ typedef struct { char *name; /* Name of operation */ int minChars; /* Minimum # characters to disambiguate */ Blt_Operation proc; int minArgs; /* Minimum # args required */ int maxArgs; /* Maximum # args required */ char *usage; /* Usage message */ } Blt_OpSpec; typedef enum { BLT_OPER_ARG0, /* Operation is the first argument. */ BLT_OPER_ARG1, /* Operation is the second argument. */ BLT_OPER_ARG2, /* Operation is the third argument. */ BLT_OPER_ARG3, /* Operation is the fourth argument. */ BLT_OPER_ARG4 /* Operation is the fifth argument. */ } Blt_OpIndex; extern Blt_Operation Blt_GetOperation _ANSI_ARGS_((Tcl_Interp *interp, int nSpecs, Blt_OpSpec *specArr, Blt_OpIndex argIndex, int nArgs, char **argArr)); extern int Blt_LookupOperation _ANSI_ARGS_((Blt_OpSpec *specArr, int nSpecs, char *string)); /* * ---------------------------------------------------------------------- * * Pad -- * * Specifies vertical and horizontal padding. * * Padding can be specified on a per side basis. The fields * side1 and side2 refer to the opposite sides, either * horizontally or vertically. * * side1 side2 * x left right * y top bottom * * ---------------------------------------------------------------------- */ typedef struct { short int side1, side2; } Pad; #define padLeft padX.side1 #define padRight padX.side2 #define padTop padY.side1 #define padBottom padY.side2 #define PADDING(x) ((x).side1 + (x).side2) /* * ---------------------------------------------------------------------- * * The following enumerated values are used as bit flags. * * * ---------------------------------------------------------------------- */ typedef enum { FILL_NONE, /* Neither coordinate plane is specified */ FILL_X, /* Horizontal plane */ FILL_Y, /* Vertical plane */ FILL_BOTH /* Both vertical and horizontal planes */ } Fill; /* * ---------------------------------------------------------------------- * * Dashes -- * * List of dash values (maximum 11 based upon PostScript limit). * * ---------------------------------------------------------------------- */ typedef struct { char valueArr[12]; int nValues; int offset; } Dashes; extern void Blt_SetDashes _ANSI_ARGS_((Display *display, GC gc, Dashes *dashesPtr)); extern Dashes *Blt_GetDashes _ANSI_ARGS_((GC gc)); /* * ------------------------------------------------------------------- * * Point2D -- * * Represents a single coordinate in 2D space. * * ------------------------------------------------------------------- */ typedef struct { double x, y; } Point2D; /* * ------------------------------------------------------------------- * * Point3D -- * * Represents a single coordinate in 3D space. * * ------------------------------------------------------------------- */ typedef struct { double x, y, z; } Point3D; /* * ------------------------------------------------------------------- * * Dim2D -- * * Represents the dimension of something in 2D space. * * ------------------------------------------------------------------- */ typedef struct { short int width, height; } Dim2D; /* *---------------------------------------------------------------------- * * ImageRegion -- * * Designates a rectangular region of an image. Used to copy * parts of images. * *---------------------------------------------------------------------- */ typedef struct ImageRegion { int x, y; /* Upper left corner coordinates of * the rectangle defining the * region. */ int width, height; /* Dimensions of the rectangular region. */ } ImageRegion; /* * ------------------------------------------------------------------- * * ColorPair -- * * Holds a pair of foreground, background colors. * * ------------------------------------------------------------------- */ typedef struct { XColor *fgColor, *bgColor; } ColorPair; #define COLOR_NONE (XColor *)0 #define COLOR_DEFAULT (XColor *)1 #define COLOR_ALLOW_DEFAULTS 1 extern int Blt_GetColorPair _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, char *fgColor, char *bgColor, ColorPair *pairPtr, int colorFlag)); extern void Blt_FreeColorPair _ANSI_ARGS_((ColorPair *pairPtr)); #define STATE_NORMAL 0 #define STATE_ACTIVE (1<<0) #define STATE_DISABLED (1<<1) #define STATE_EMPHASIS (1<<2) typedef struct BindTable *BindTable; typedef ClientData (BindPickProc) _ANSI_ARGS_((ClientData clientData, int x, int y)); typedef void (BindTagProc) _ANSI_ARGS_((BindTable bindTable, ClientData object, ClientData *tagArr, int *nTagsPtr)); /* * Binding structure information: */ struct BindTable { int flags; Tk_BindingTable bindingTable; /* Table of all bindings currently defined. * NULL means that no bindings exist, so the * table hasn't been created. Each "object" * used for this table is either a Tk_Uid for * a tag or the address of an item named by * id. */ ClientData currentItem; /* The item currently containing the mouse * pointer, or NULL if none. */ ClientData newItem; /* The item that is about to become the * current one, or NULL. This field is * used to detect deletions of the new * current item pointer that occur during * Leave processing of the previous current * tab. */ ClientData focusItem; XEvent pickEvent; /* The event upon which the current choice * of the current tab is based. Must be saved * so that if the current item is deleted, * we can pick another. */ int activePick; /* The pick event has been initialized so * that we can repick it */ int state; /* Last known modifier state. Used to * defer picking a new current object * while buttons are down. */ ClientData clientData; Tk_Window tkwin; BindPickProc *pickProc; /* Routine to report the item the mouse is * currently over. */ BindTagProc *tagProc; /* Routine to report tags picked items. */ }; #include "bltText.h" extern void Blt_DestroyBindingTable _ANSI_ARGS_((BindTable table)); extern BindTable Blt_CreateBindingTable _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, ClientData clientData, BindPickProc * pickProc, BindTagProc * tagProc)); extern int Blt_ConfigureBindings _ANSI_ARGS_((Tcl_Interp *interp, BindTable table, ClientData item, int argc, char **argv)); extern void Blt_PickCurrentItem _ANSI_ARGS_((BindTable table)); extern void Blt_DeleteBindings _ANSI_ARGS_((BindTable table, ClientData object)); #define Blt_SetFocusItem(bindPtr, object) \ ((bindPtr)->focusItem = (ClientData)(object)) #define Blt_GetCurrentItem(bindPtr) ((bindPtr)->currentItem) #define Blt_GetLatestItem(bindPtr) ((bindPtr)->newItem) #define Blt_GetBindingData(bindPtr) ((bindPtr)->clientData) /* * ---------------------------------------------------------------------- * * X11/Xosdefs.h requires XNOSTDHDRS be set for some systems. * This is a guess. If I can't find STDC headers or unistd.h, * assume that this is non-POSIX and non-STDC environment. * (needed for Encore Umax 3.4 ?) * * ---------------------------------------------------------------------- */ #if !defined(STDC_HEADERS) && !defined(HAVE_UNISTD_H) #define XNOSTDHDRS 1 #endif /* * ---------------------------------------------------------------------- * * Assume we need to declare free if there's no stdlib.h or malloc.h * * ---------------------------------------------------------------------- */ #if !defined(HAVE_STDLIB_H) && !defined(HAVE_MALLOC_H) extern void free _ANSI_ARGS_((void *)); #endif /* * ---------------------------------------------------------------------- * * On some systems "strdup" and "strcasecmp" are in the C library, * but have no declarations in the C header files. Make sure we * supply them here. * * ---------------------------------------------------------------------- */ #ifdef NO_DECL_STRDUP extern char *strdup _ANSI_ARGS_((CONST char *s)); #endif #ifdef NO_DECL_DRAND48 extern double drand48 _ANSI_ARGS_((void)); extern void srand48 _ANSI_ARGS_((long seed)); #endif #ifdef NO_DECL_STRCASECMP extern int strcasecmp _ANSI_ARGS_((CONST char *s1, CONST char *s2)); #endif extern int Blt_StringToFlag _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int flags)); extern char *Blt_FlagToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *string, int offset, Tcl_FreeProc **freeProc)); extern Tk_Uid Blt_GetUid _ANSI_ARGS_((char *string)); extern void Blt_FreeUid _ANSI_ARGS_((Tk_Uid uid)); extern Tk_Uid Blt_FindUid _ANSI_ARGS_((char *string)); extern char *Blt_Itoa _ANSI_ARGS_((int value)); extern char *Blt_Dtoa _ANSI_ARGS_((Tcl_Interp *interp, double value)); extern void Blt_InitHexTable _ANSI_ARGS_((char *table)); extern GC Blt_GetPrivateGC _ANSI_ARGS_((Tk_Window tkwin, unsigned long gcMask, XGCValues *valuePtr)); extern GC Blt_GetPrivateGCFromDrawable _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, unsigned long gcMask, XGCValues *valuePtr)); extern void Blt_FreePrivateGC _ANSI_ARGS_((Display *display, GC gc)); extern Tk_Window Blt_FindChild _ANSI_ARGS_((Tk_Window parent, char *name)); extern Tk_Window Blt_FirstChild _ANSI_ARGS_((Tk_Window parent)); extern Tk_Window Blt_NextChild _ANSI_ARGS_((Tk_Window tkwin)); extern void Blt_RelinkWindow _ANSI_ARGS_((Tk_Window tkwin, Tk_Window newParent, int x, int y)); extern Tk_Window Blt_Toplevel _ANSI_ARGS_((Tk_Window tkwin)); extern int Blt_GetPixels _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, char *string, int check, int *valuePtr)); extern int Blt_GetCount _ANSI_ARGS_((Tcl_Interp *interp, char *string, int check, int *valuePtr)); extern char *Blt_NameOfFill _ANSI_ARGS_((Fill fill)); extern int Blt_GetXY _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, char *string, int *x, int *y)); extern Tcl_Command Blt_InitCmd _ANSI_ARGS_((Tcl_Interp *interp, char *nameSpace, Blt_CmdSpec *specPtr)); extern int Blt_InitCmds _ANSI_ARGS_((Tcl_Interp *interp, char *nameSpace, Blt_CmdSpec *specPtr, int nCmds)); extern int Blt_ConfigModified _ANSI_ARGS_(TCL_VARARGS(Tk_ConfigSpec *, specs)); extern void Blt_DStringAppendElements _ANSI_ARGS_(TCL_VARARGS(Tcl_DString *, args)); extern void Blt_MakeTransparentWindowExist _ANSI_ARGS_((Tk_Window tkwin, Window parent, int isBusy)); extern Window Blt_GetParent _ANSI_ARGS_((Display *display, Window tkwin)); extern void Blt_GetBoundingBox _ANSI_ARGS_((int width, int height, double theta, int *widthPtr, int *heightPtr, XPoint *pointArr)); extern void Blt_InitEpsCanvasItem _ANSI_ARGS_((Tcl_Interp *interp)); extern Pixmap Blt_RotateBitmap _ANSI_ARGS_((Tk_Window tkwin, Pixmap bitmap, int width, int height, double theta, int *widthPtr, int *heightPtr)); extern Pixmap Blt_ScaleBitmap _ANSI_ARGS_((Tk_Window tkwin, Pixmap srcBitmap, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight)); extern Pixmap Blt_ScaleBitmapRegion _ANSI_ARGS_((Tk_Window tkwin, Pixmap srcBitmap, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, ImageRegion *regionPtr)); extern void Blt_TranslateAnchor _ANSI_ARGS_((int x, int y, int width, int height, Tk_Anchor anchor, int *transXPtr, int *transYPtr)); typedef void (Blt_TileChangedProc) _ANSI_ARGS_((ClientData clientData, Blt_Tile tile)); extern Blt_Tile Blt_GetTile _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, char *imageName)); extern void Blt_FreeTile _ANSI_ARGS_((Blt_Tile tile)); extern char *Blt_NameOfTile _ANSI_ARGS_((Blt_Tile tile)); extern void Blt_SetTileChangedProc _ANSI_ARGS_((Blt_Tile tile, Blt_TileChangedProc *changeProc, ClientData clientData)); extern Pixmap Blt_PixmapOfTile _ANSI_ARGS_((Blt_Tile tile)); extern void Blt_SizeOfTile _ANSI_ARGS_((Blt_Tile tile, int *widthPtr, int *heightPtr)); extern void Blt_SetTileOrigin _ANSI_ARGS_((Tk_Window tkwin, GC gc, int x, int y)); extern int Blt_ConfigureWidgetComponent _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin, char *name, char *class, Tk_ConfigSpec *specs, int argc, char **argv, char *widgRec, int flags)); extern void Blt_HSV _ANSI_ARGS_((XColor *colorPtr, double *huePtr, double *valPtr, double *satPtr)); extern void Blt_RGB _ANSI_ARGS_((double hue, double sat, double val, XColor *colorPtr)); extern int Blt_NaturalSpline _ANSI_ARGS_((double *xCntl, double *yCtrl, int nCtrls, double *X, double *Y, int nPoints)); extern int Blt_QuadraticSpline _ANSI_ARGS_((double *xCntl, double *yCtrl, int nCtrls, double *X, double *Y, int nPoints, double epsilon)); extern int Blt_ParseFlag _ANSI_ARGS_((ClientData, Tcl_Interp *, Tk_Window, char *, char *, int)); extern char *Blt_FlagPrint _ANSI_ARGS_((ClientData, Tk_Window, char *, int, Tcl_FreeProc **)); extern Window Blt_GetRealWindowId _ANSI_ARGS_((Tk_Window tkwin)); extern int Blt_RootX _ANSI_ARGS_((Tk_Window tkwin)); extern int Blt_RootY _ANSI_ARGS_((Tk_Window tkwin)); extern void Blt_MapTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin)); extern void Blt_UnmapTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin)); extern void Blt_RaiseTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin)); extern void Blt_ResizeTopLevelWindow _ANSI_ARGS_((Tk_Window tkwin, int width, int height)); extern ClientData Blt_GetWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin)); extern void Blt_SetWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin, ClientData instanceData)); extern void Blt_DeleteWindowInstanceData _ANSI_ARGS_((Tk_Window tkwin)); extern int Blt_AdjustViewport _ANSI_ARGS_((int offset, int worldSize, int windowSize, int scrollUnits, int scrollMode)); extern int Blt_GetScrollInfo _ANSI_ARGS_((Tcl_Interp *interp, int argc, char **argv, int *offsetPtr, int worldSize, int windowSize, int scrollUnits, int scrollMode)); extern void Blt_UpdateScrollbar _ANSI_ARGS_((Tcl_Interp *interp, char *scrollCmd, double firstFract, double lastFract)); extern int Blt_ReparentWindow _ANSI_ARGS_((Display *display, Window window, Window newParent, int x, int y)); EXTERN void Blt_Panic _ANSI_ARGS_(TCL_VARARGS(char *, args)); #ifndef TCL_NAMESPACE_ONLY #define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY #endif EXTERN Tcl_Namespace *Tcl_GetCurrentNamespace _ANSI_ARGS_((Tcl_Interp *interp)); EXTERN Tcl_Namespace *Tcl_GetGlobalNamespace _ANSI_ARGS_((Tcl_Interp *interp)); EXTERN Tcl_Command Tcl_FindCommand _ANSI_ARGS_((Tcl_Interp *interp, char *name, Tcl_Namespace *nsPtr, int flags)); #if (TCL_MAJOR_VERSION >= 8) EXTERN Tcl_Namespace *Tcl_CreateNamespace _ANSI_ARGS_((Tcl_Interp *interp, char *name, ClientData clientData, Tcl_NamespaceDeleteProc * nsDelProc)); EXTERN void Tcl_DeleteNamespace _ANSI_ARGS_((Tcl_Namespace *nsPtr)); EXTERN Tcl_Namespace *Tcl_FindNamespace _ANSI_ARGS_((Tcl_Interp *interp, char *name, Tcl_Namespace *context, int flags)); EXTERN int Tcl_Export _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Namespace *nsPtr, char *name, int resetFlag)); EXTERN Tcl_Var Tcl_FindNamespaceVar _ANSI_ARGS_((Tcl_Interp *interp, char *name, Tcl_Namespace *contextNsPtr, int flags)); EXTERN void Tcl_PopCallFrame _ANSI_ARGS_((Tcl_Interp *interp)); EXTERN int Tcl_PushCallFrame _ANSI_ARGS_((Tcl_Interp *interp, Tcl_CallFrame * framePtr, Tcl_Namespace *nsPtr, int isProcCallFrame)); extern Tcl_HashTable *Blt_GetArrayVariableTable _ANSI_ARGS_(( Tcl_Interp *interp, char *varName, int flags)); #endif /* TCL_MAJOR_VERSION >= 8 */ extern Tcl_Namespace *Blt_NamespaceOfVariable _ANSI_ARGS_((Tcl_Interp *interp, char *varName)); extern Tcl_CallFrame *Blt_EnterNamespace _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Namespace *nsPtr)); extern void Blt_LeaveNamespace _ANSI_ARGS_((Tcl_Interp *interp, Tcl_CallFrame * framePtr)); extern int Blt_ParseQualifiedName _ANSI_ARGS_((Tcl_Interp *interp, char *name, Tcl_Namespace **nsPtrPtr, char **namePtr)); extern Tcl_Command Blt_CreateCommand _ANSI_ARGS_((Tcl_Interp *interp, char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc)); #if HAVE_JPEGLIB_H extern int Blt_JPEGToPhoto _ANSI_ARGS_((Tcl_Interp *interp, char *fileName, Tk_PhotoHandle photo)); #endif /* HAVE_JPEGLIB_H */ /* * Define this if you want to be able to tile to the main window "." * This will cause a conflict with Tk if you try to compile and link * statically. */ #undef TILE_MAINWINDOW #ifndef WIN32 #define NO_PRINTER #endif #define NO_TED #ifndef NO_BEEP extern Tcl_AppInitProc Blt_BeepInit; #endif #ifndef NO_BGEXEC extern Tcl_AppInitProc Blt_BgexecInit; #endif #ifndef NO_BITMAP extern Tcl_AppInitProc Blt_BitmapInit; #endif #ifndef NO_BITMAP extern Tcl_AppInitProc Blt_BitmapInit; #endif #ifndef NO_BUSY extern Tcl_AppInitProc Blt_BusyInit; #endif #ifndef NO_CONTAINER extern Tcl_AppInitProc Blt_ContainerInit; #endif #ifndef NO_CUTBUFFER extern Tcl_AppInitProc Blt_CutbufferInit; #endif #ifndef NO_DEBUG extern Tcl_AppInitProc Blt_DebugInit; #endif #ifndef NO_DRAGDROP extern Tcl_AppInitProc Blt_DragDropInit; #endif #ifndef NO_DND extern Tcl_AppInitProc Blt_DndInit; #endif #ifndef NO_GRAPH extern Tcl_AppInitProc Blt_GraphInit; #endif #ifndef NO_HIERBOX extern Tcl_AppInitProc Blt_HierboxInit; #endif #ifndef NO_HTEXT extern Tcl_AppInitProc Blt_HtextInit; #endif #ifdef WIN32 #ifndef NO_PRINTER extern Tcl_AppInitProc Blt_PrinterInit; #endif #endif #ifndef NO_TABLE extern Tcl_AppInitProc Blt_TableInit; #endif #ifndef NO_VECTOR extern Tcl_AppInitProc Blt_VectorInit; #endif #ifndef NO_WINOP extern Tcl_AppInitProc Blt_WinopInit; #endif #ifndef NO_WATCH extern Tcl_AppInitProc Blt_WatchInit; #endif #ifndef NO_SPLINE extern Tcl_AppInitProc Blt_SplineInit; #endif #ifndef NO_TABSET extern Tcl_AppInitProc Blt_TabsetInit; #endif #ifndef NO_TILEFRAME extern Tcl_AppInitProc Blt_FrameInit; #endif #ifndef NO_TILEBUTTON extern Tcl_AppInitProc Blt_ButtonInit; #endif #ifndef NO_TILESCROLLBAR extern Tcl_AppInitProc Blt_ScrollbarInit; #endif #if (BLT_MAJOR_VERSION == 3) #ifndef NO_MOUNTAIN extern Tcl_AppInitProc Blt_MountainInit; #endif #endif #ifndef NO_TED extern Tcl_AppInitProc Blt_TedInit; #endif #ifdef WIN32 #ifdef CHECK_UNICODE_CALLS #define _UNICODE #define UNICODE #define __TCHAR_DEFINED typedef float *_TCHAR; #define _TCHAR_DEFINED typedef float *TCHAR; #endif /* CHECK_UNICODE_CALLS */ extern double hypot(double x, double y); extern int Blt_AsyncRead(int fd, char *buffer, unsigned int size); extern int Blt_AsyncWrite(int fd, char *buffer, unsigned int size); extern void Blt_CreateFileHandler(int fd, int flags, Tcl_FileProc * proc, ClientData clientData); extern void Blt_DeleteFileHandler(int fd); extern int Blt_GetPlatformId(void); extern char *Blt_LastError(void); extern int Blt_GetOpenPrinter(Tcl_Interp *interp, const char *id, Drawable *drawablePtr); extern int Blt_OpenPrinterDoc(Tcl_Interp *interp, const char *id); extern int Blt_ClosePrinterDoc(Tcl_Interp *interp, const char *id); extern void Blt_GetPrinterScale(HDC dc, double *xRatio, double *yRatio); #ifdef _MSC_VER #define strncasecmp(s1,s2,n) _strnicmp(s1,s2,n) #define strcasecmp(s1,s2) _stricmp(s1,s2) #endif #undef EXPORT #define EXPORT __declspec(dllexport) #ifndef _MSC_VER /* * Add missing definitions from windgi.h, windowsx.h, and winspool.h */ #include #endif #define malloc(s) Tcl_Alloc(s) #define realloc(p, s) Tcl_Realloc((p), (s)) #define free Tcl_Free #define strdup(s) Blt_EmulateStrdup(s) #define calloc(s,n) Blt_EmulateCalloc((s),(n)) #define XCopyArea Blt_EmulateXCopyArea #define XCopyPlane Blt_EmulateXCopyPlane #define XDrawArcs Blt_EmulateXDrawArcs #define XDrawLine Blt_EmulateXDrawLine #define XDrawLines Blt_EmulateXDrawLines #define XDrawPoints Blt_EmulateXDrawPoints #define XDrawRectangle Blt_EmulateXDrawRectangle #define XDrawRectangles Blt_EmulateXDrawRectangles #define XDrawSegments Blt_EmulateXDrawSegments #define XDrawString Blt_EmulateXDrawString #define XFillArcs Blt_EmulateXFillArcs #define XFillRectangle Blt_EmulateXFillRectangle #define XFillRectangles Blt_EmulateXFillRectangles #define XFree Blt_EmulateXFree #define XGetWindowAttributes Blt_EmulateXGetWindowAttributes #define XLowerWindow Blt_EmulateXLowerWindow #define XMaxRequestSize Blt_EmulateXMaxRequestSize #define XRaiseWindow Blt_EmulateXRaiseWindow #define XReparentWindow Blt_EmulateXReparentWindow #define XSetDashes Blt_EmulateXSetDashes #define XUnmapWindow Blt_EmulateXUnmapWindow #define XWarpPointer Blt_EmulateXWarpPointer EXTERN char *Blt_EmulateStrdup(const char *string); EXTERN void *Blt_EmulateCalloc(unsigned int size, unsigned int nElems); EXTERN GC Blt_EmulateXCreateGC(Display *display, Drawable drawable, unsigned long mask, XGCValues *valuesPtr); EXTERN void Blt_EmulateXCopyArea(Display *display, Drawable src, Drawable dest, GC gc, int src_x, int src_y, unsigned int width, unsigned int height, int dest_x, int dest_y); EXTERN void Blt_EmulateXCopyPlane(Display *display, Drawable src, Drawable dest, GC gc, int src_x, int src_y, unsigned int width, unsigned int height, int dest_x, int dest_y, unsigned long plane); EXTERN void Blt_EmulateXDrawArcs(Display *display, Drawable drawable, GC gc, XArc *arcArr, int nArcs); EXTERN void Blt_EmulateXDrawLine(Display *display, Drawable drawable, GC gc, int x1, int y1, int x2, int y2); EXTERN void Blt_EmulateXDrawLines(Display *display, Drawable drawable, GC gc, XPoint *pointArr, int nPoints, int mode); EXTERN void Blt_EmulateXDrawPoints(Display *display, Drawable drawable, GC gc, XPoint *pointArr, int nPoints, int mode); EXTERN void Blt_EmulateXDrawRectangle(Display *display, Drawable drawable, GC gc, int x, int y, unsigned int width, unsigned int height); EXTERN void Blt_EmulateXDrawRectangles(Display *display, Drawable drawable, GC gc, XRectangle *rectArr, int nRects); EXTERN void Blt_EmulateXDrawSegments(Display *display, Drawable drawable, GC gc, XSegment *segArr, int nSegments); EXTERN void Blt_EmulateXDrawSegments(Display *display, Drawable drawable, GC gc, XSegment *segArr, int nSegments); EXTERN void Blt_EmulateXDrawString(Display *display, Drawable drawable, GC gc, int x, int y, _Xconst char *string, int length); EXTERN void Blt_EmulateXFillArcs(Display *display, Drawable drawable, GC gc, XArc *arcArr, int nArcs); EXTERN void Blt_EmulateXFillRectangle(Display *display, Drawable drawable, GC gc, int x, int y, unsigned int width, unsigned int height); EXTERN void Blt_EmulateXFillRectangles(Display *display, Drawable drawable, GC gc, XRectangle *rectArr, int nRects); EXTERN void Blt_EmulateXFree(void *ptr); EXTERN Status Blt_EmulateXGetWindowAttributes(Display *display, Window window, XWindowAttributes * attrsPtr); EXTERN void Blt_EmulateXLowerWindow(Display *display, Window window); EXTERN void Blt_EmulateXMapWindow(Display *display, Window window); EXTERN long Blt_EmulateXMaxRequestSize(Display *display); EXTERN void Blt_EmulateXRaiseWindow(Display *display, Window window); EXTERN void Blt_EmulateXReparentWindow(Display *display, Window window, Window parent, int x, int y); EXTERN void Blt_EmulateXSetDashes(Display *display, GC gc, int dashOffset, _Xconst char *dashList, int n); EXTERN void Blt_EmulateXUnmapWindow(Display *display, Window window); EXTERN void Blt_EmulateXWarpPointer(Display *display, Window srcWindow, Window destWindow, int srcX, int srcY, unsigned int srcWidth, unsigned int srcHeight, int destX, int destY); extern unsigned char *Blt_GetBitmapData _ANSI_ARGS_((Display *display, Pixmap bitmap, int width, int height, int *pitchPtr)); extern HFONT Blt_CreateRotatedFont _ANSI_ARGS_((Tk_Window tkwin, unsigned long font, double theta)); extern HPALETTE Blt_GetSystemPalette _ANSI_ARGS_((void)); extern HPEN Blt_GCToPen _ANSI_ARGS_((HDC dc, GC gc)); #endif /* WIN32 */ #endif /*_BLT_INT_H*/ tkdesk-2.0/blt/bltList.c0100644000175000007640000003340510020457430013267 0ustar jccjcc/* * bltList.c -- * * The module implements generic linked lists. * * Copyright 1991-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #include "bltInt.h" #include "bltList.h" static struct Blt_ListEntry * FindString(listPtr, key) Blt_List *listPtr; /* List to search */ char *key; /* Key to match */ { register struct Blt_ListEntry *entryPtr; char c; c = key[0]; for (entryPtr = listPtr->headPtr; entryPtr != NULL; entryPtr = entryPtr->nextPtr) { if ((c == entryPtr->key.string[0]) && (strcmp(key, entryPtr->key.string) == 0)) { return entryPtr; } } return NULL; } static struct Blt_ListEntry * FindOneWord(listPtr, key) Blt_List *listPtr; /* List to search */ char *key; /* Key to match */ { register struct Blt_ListEntry *entryPtr; for (entryPtr = listPtr->headPtr; entryPtr != NULL; entryPtr = entryPtr->nextPtr) { if (key == entryPtr->key.oneWordValue) { return entryPtr; } } return NULL; } static struct Blt_ListEntry * FindArray(listPtr, key) Blt_List *listPtr; /* List to search */ char *key; /* Key to match */ { register struct Blt_ListEntry *entryPtr; int nBytes; nBytes = sizeof(int) * listPtr->type; for (entryPtr = listPtr->headPtr; entryPtr != NULL; entryPtr = entryPtr->nextPtr) { if (memcmp(key, entryPtr->key.words, nBytes) == 0) { return entryPtr; } } return NULL; } /* *---------------------------------------------------------------------- * * Blt_ListCreate -- * * Creates a new linked list structure and initializes its pointers * * Results: * Returns a pointer to the newly created list structure. * *---------------------------------------------------------------------- */ Blt_List * Blt_ListCreate(type) int type; { Blt_List *listPtr; listPtr = (Blt_List *)malloc(sizeof(Blt_List)); if (listPtr != NULL) { Blt_InitList(listPtr, type); } return (listPtr); } /* *---------------------------------------------------------------------- * * Blt_ListNewEntry -- * * Creates a list entry holder. This routine does not insert * the entry into the list, nor does it no attempt to maintain * consistency of the keys. For example, more than one entry * may use the same key. * * Results: * The return value is the pointer to the newly created entry. * * Side Effects: * The key is not copied, only the Uid is kept. It is assumed * this key will not change in the life of the entry. * *---------------------------------------------------------------------- */ Blt_ListEntry Blt_ListNewEntry(listPtr, key) Blt_List *listPtr; char *key; /* Unique key to reference object */ { register struct Blt_ListEntry *entryPtr; int keySize; if (listPtr->type == TCL_STRING_KEYS) { keySize = strlen(key) + 1; } else { keySize = sizeof(int) * listPtr->type; } entryPtr = (struct Blt_ListEntry *) calloc(1, sizeof(struct Blt_ListEntry) + keySize - 4); assert(entryPtr); entryPtr->clientData = (ClientData)NULL; entryPtr->nextPtr = entryPtr->prevPtr = NULL; entryPtr->listPtr = listPtr; switch (listPtr->type) { case TCL_STRING_KEYS: strcpy(entryPtr->key.string, key); break; case TCL_ONE_WORD_KEYS: entryPtr->key.oneWordValue = key; break; default: memcpy(entryPtr->key.words, key, keySize); break; } return entryPtr; } /* *---------------------------------------------------------------------- * * FreeEntry -- * * Free the memory allocated for the entry. * * Results: * None. * *---------------------------------------------------------------------- */ static void FreeEntry(entryPtr) struct Blt_ListEntry *entryPtr; { free((char *)entryPtr); } /* *---------------------------------------------------------------------- * * Blt_ListReset -- * * Removes all the entries from a list, removing pointers to the * objects and keys (not the objects or keys themselves). The * entry counter is reset to zero. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ListReset(listPtr) Blt_List *listPtr; /* List to clear */ { if (listPtr != NULL) { register struct Blt_ListEntry *oldPtr; register struct Blt_ListEntry *entryPtr = listPtr->headPtr; while (entryPtr != NULL) { oldPtr = entryPtr; entryPtr = entryPtr->nextPtr; FreeEntry(oldPtr); } Blt_InitList(listPtr, listPtr->type); } } /* *---------------------------------------------------------------------- * * Blt_ListDestroy * * Frees all list structures * * Results: * Returns a pointer to the newly created list structure. * *---------------------------------------------------------------------- */ void Blt_ListDestroy(listPtr) Blt_List *listPtr; { if (listPtr != NULL) { Blt_ListReset(listPtr); free((char *)listPtr); } } /* *---------------------------------------------------------------------- * * Blt_InitList -- * * Initializes a linked list. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_InitList(listPtr, type) Blt_List *listPtr; int type; { listPtr->nEntries = 0; listPtr->headPtr = listPtr->tailPtr = NULL; listPtr->type = type; } /* *---------------------------------------------------------------------- * * Blt_ListLinkAfter -- * * Inserts an entry following a given entry. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ListLinkAfter(listPtr, entryPtr, afterPtr) Blt_List *listPtr; struct Blt_ListEntry *entryPtr; struct Blt_ListEntry *afterPtr; { if (listPtr->headPtr == NULL) { listPtr->tailPtr = listPtr->headPtr = entryPtr; } else { if (afterPtr == NULL) { /* Prepend to the front of the list */ entryPtr->nextPtr = listPtr->headPtr; entryPtr->prevPtr = NULL; listPtr->headPtr->prevPtr = entryPtr; listPtr->headPtr = entryPtr; } else { entryPtr->nextPtr = afterPtr->nextPtr; entryPtr->prevPtr = afterPtr; if (afterPtr == listPtr->tailPtr) { listPtr->tailPtr = entryPtr; } else { afterPtr->nextPtr->prevPtr = entryPtr; } afterPtr->nextPtr = entryPtr; } } entryPtr->listPtr = listPtr; listPtr->nEntries++; } /* *---------------------------------------------------------------------- * * Blt_ListLinkBefore -- * * Inserts an entry preceding a given entry. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ListLinkBefore(listPtr, entryPtr, beforePtr) Blt_List *listPtr; /* List to contain new entry */ struct Blt_ListEntry *entryPtr; /* New entry to be inserted */ struct Blt_ListEntry *beforePtr; /* Entry to link before */ { if (listPtr->headPtr == NULL) { listPtr->tailPtr = listPtr->headPtr = entryPtr; } else { if (beforePtr == NULL) { /* Append onto the end of the list */ entryPtr->nextPtr = NULL; entryPtr->prevPtr = listPtr->tailPtr; listPtr->tailPtr->nextPtr = entryPtr; listPtr->tailPtr = entryPtr; } else { entryPtr->prevPtr = beforePtr->prevPtr; entryPtr->nextPtr = beforePtr; if (beforePtr == listPtr->headPtr) { listPtr->headPtr = entryPtr; } else { beforePtr->prevPtr->nextPtr = entryPtr; } beforePtr->prevPtr = entryPtr; } } entryPtr->listPtr = listPtr; listPtr->nEntries++; } /* *---------------------------------------------------------------------- * * Blt_ListUnlinkEntry -- * * Unlinks an entry from the given list. The entry itself is * not deallocated, but only removed from the list. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ListUnlinkEntry(entryPtr) struct Blt_ListEntry *entryPtr; { Blt_List *listPtr; listPtr = entryPtr->listPtr; if (listPtr != NULL) { if (listPtr->headPtr == entryPtr) { listPtr->headPtr = entryPtr->nextPtr; } if (listPtr->tailPtr == entryPtr) { listPtr->tailPtr = entryPtr->prevPtr; } if (entryPtr->nextPtr != NULL) { entryPtr->nextPtr->prevPtr = entryPtr->prevPtr; } if (entryPtr->prevPtr != NULL) { entryPtr->prevPtr->nextPtr = entryPtr->nextPtr; } entryPtr->listPtr = NULL; listPtr->nEntries--; } } /* *---------------------------------------------------------------------- * * Blt_ListFind -- * * Find the first entry matching the key given. * * Results: * Returns the pointer to the entry. If no entry matching * the key given is found, then NULL is returned. * *---------------------------------------------------------------------- */ Blt_ListEntry Blt_ListFind(listPtr, key) Blt_List *listPtr; /* List to search */ char *key; /* Key to match */ { if (listPtr != NULL) { switch (listPtr->type) { case TCL_STRING_KEYS: return FindString(listPtr, key); case TCL_ONE_WORD_KEYS: return FindOneWord(listPtr, key); default: return FindArray(listPtr, key); } } return NULL; } /* *---------------------------------------------------------------------- * * Blt_ListDeleteEntry -- * * Unlinks and deletes the given entry. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ListDeleteEntry(entryPtr) struct Blt_ListEntry *entryPtr; { Blt_ListUnlinkEntry(entryPtr); FreeEntry(entryPtr); } /* *---------------------------------------------------------------------- * * Blt_ListDelete -- * * Find the entry and free the memory allocated for the entry. * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ListDelete(listPtr, key) Blt_List *listPtr; char *key; { struct Blt_ListEntry *entryPtr; entryPtr = Blt_ListFind(listPtr, key); if (entryPtr != NULL) { Blt_ListDeleteEntry(entryPtr); } } Blt_ListEntry Blt_ListAppend(listPtr, key, clientData) Blt_List *listPtr; char *key; ClientData clientData; { struct Blt_ListEntry *entryPtr; entryPtr = Blt_ListNewEntry(listPtr, key); Blt_ListSetValue(entryPtr, clientData); Blt_ListAppendEntry(listPtr, entryPtr); return entryPtr; } Blt_ListEntry Blt_ListPrepend(listPtr, key, clientData) Blt_List *listPtr; char *key; ClientData clientData; { struct Blt_ListEntry *entryPtr; entryPtr = Blt_ListNewEntry(listPtr, key); Blt_ListSetValue(entryPtr, clientData); Blt_ListPrependEntry(listPtr, entryPtr); return entryPtr; } /* *---------------------------------------------------------------------- * * Blt_ListGetNthEntry -- * * Find the entry based upon a given position in list. * * Results: * Returns the pointer to the entry, if that numbered element * exists. Otherwise NULL. * *---------------------------------------------------------------------- */ Blt_ListEntry Blt_ListGetNthEntry(listPtr, position, direction) Blt_List *listPtr; /* List to traverse */ int position; /* Index of entry to select from front * or back of the list. */ int direction; { register struct Blt_ListEntry *entryPtr; if (listPtr != NULL) { if (direction > 0) { for (entryPtr = listPtr->headPtr; entryPtr != NULL; entryPtr = entryPtr->nextPtr) { if (position == 0) { return entryPtr; } position--; } } else { for (entryPtr = listPtr->tailPtr; entryPtr != NULL; entryPtr = entryPtr->prevPtr) { if (position == 0) { return entryPtr; } position--; } } } return NULL; } /* *---------------------------------------------------------------------- * * Blt_ListSort -- * * Find the entry based upon a given position in list. * * Results: * Returns the pointer to the entry, if that numbered element * exists. Otherwise NULL. * *---------------------------------------------------------------------- */ void Blt_ListSort(listPtr, proc) Blt_List *listPtr; /* List to traverse */ Blt_ListCompareProc *proc; { struct Blt_ListEntry **entryArr; register struct Blt_ListEntry *entryPtr; register int i; if (listPtr->nEntries < 2) { return; } entryArr = (struct Blt_ListEntry **) malloc(sizeof(struct Blt_ListEntry *) * (listPtr->nEntries + 1)); if (entryArr == NULL) { return; /* Out of memory. */ } i = 0; for (entryPtr = listPtr->headPtr; entryPtr != NULL; entryPtr = entryPtr->nextPtr) { entryArr[i++] = entryPtr; } qsort((char *)entryArr, listPtr->nEntries, sizeof(struct Blt_ListEntry *), (QSortCompareProc *)proc); /* Rethread the list. */ entryPtr = entryArr[0]; listPtr->headPtr = entryPtr; entryPtr->prevPtr = NULL; for (i = 1; i < listPtr->nEntries; i++) { entryPtr->nextPtr = entryArr[i]; entryPtr->nextPtr->prevPtr = entryPtr; entryPtr = entryPtr->nextPtr; } listPtr->tailPtr = entryPtr; entryPtr->nextPtr = NULL; free((char *)entryArr); } tkdesk-2.0/blt/bltList.h0100644000175000007640000001055310020457430013273 0ustar jccjcc/* * bltList.h -- * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #ifndef _BLT_LIST_H #define _BLT_LIST_H typedef struct Blt_List Blt_List; /* * A Blt_ListEntry is the container structure for the Blt_List. */ typedef struct Blt_ListEntry { struct Blt_ListEntry *prevPtr; /* Link to the previous entry */ struct Blt_ListEntry *nextPtr; /* Link to the next entry */ ClientData clientData; /* Pointer to the data object */ struct Blt_List *listPtr; /* List to eventually insert entry */ union { /* Key has one of these forms: */ char *oneWordValue; /* One-word value for key. */ int *words[1]; /* Multiple integer words for key. * The actual size will be as large * as necessary for this table's * keys. */ char string[4]; /* String for key. The actual size * will be as large as needed to hold * the key. */ } key; /* MUST BE LAST FIELD IN RECORD!! */ } *Blt_ListEntry; typedef int (Blt_ListCompareProc) _ANSI_ARGS_((Blt_ListEntry *entry1Ptr, Blt_ListEntry *entry2Ptr)); /* * A Blt_List is a doubly chained list structure. */ struct Blt_List { struct Blt_ListEntry *headPtr; /* Pointer to first element in list */ struct Blt_ListEntry *tailPtr; /* Pointer to last element in list */ int nEntries; /* Number of elements in list */ int type; /* Type of keys in list */ }; extern void Blt_InitList _ANSI_ARGS_((Blt_List *listPtr, int type)); extern Blt_List *Blt_ListCreate _ANSI_ARGS_((int type)); extern void Blt_ListDestroy _ANSI_ARGS_((Blt_List *listPtr)); extern Blt_ListEntry Blt_ListNewEntry _ANSI_ARGS_((Blt_List *listPtr, char *key)); extern Blt_ListEntry Blt_ListAppend _ANSI_ARGS_((Blt_List *listPtr, char *key, ClientData clientData)); extern Blt_ListEntry Blt_ListPrepend _ANSI_ARGS_((Blt_List *listPtr, char *key, ClientData clientData)); extern void Blt_ListReset _ANSI_ARGS_((Blt_List *listPtr)); extern void Blt_ListLinkAfter _ANSI_ARGS_((Blt_List *listPtr, Blt_ListEntry entry, Blt_ListEntry afterEntry)); extern void Blt_ListLinkBefore _ANSI_ARGS_((Blt_List *listPtr, Blt_ListEntry entry, Blt_ListEntry beforeEntry)); extern void Blt_ListUnlinkEntry _ANSI_ARGS_((Blt_ListEntry entry)); extern Blt_ListEntry Blt_ListFind _ANSI_ARGS_((Blt_List *listPtr, char *name)); extern void Blt_ListDeleteEntry _ANSI_ARGS_((Blt_ListEntry entry)); extern void Blt_ListDelete _ANSI_ARGS_((Blt_List *listPtr, char *name)); extern Blt_ListEntry Blt_ListGetNthEntry _ANSI_ARGS_((Blt_List *listPtr, int position, int direction)); extern void Blt_ListSort _ANSI_ARGS_((Blt_List *listPtr, Blt_ListCompareProc * proc)); #define Blt_ListGetLength(list) (((list) == NULL) ? 0 : (list)->nEntries) #define Blt_ListFirstEntry(list) (((list) == NULL) ? NULL : (list)->headPtr) #define Blt_ListLastEntry(list) (((list) == NULL) ? NULL : (list)->tailPtr) #define Blt_ListPrevEntry(entry) ((entry)->prevPtr) #define Blt_ListNextEntry(entry) ((entry)->nextPtr) #define Blt_ListGetKey(entry) (((entry)->listPtr->type == TCL_STRING_KEYS) \ ? (entry)->key.string : (entry)->key.oneWordValue) #define Blt_ListGetValue(entry) ((entry)->clientData) #define Blt_ListSetValue(entry, value) \ ((entry)->clientData = (ClientData)(value)) #define Blt_ListAppendEntry(list, entry) \ (Blt_ListLinkBefore((list), (entry), (Blt_ListEntry)NULL)) #define Blt_ListPrependEntry(list, entry) \ (Blt_ListLinkAfter((list), (entry), (Blt_ListEntry)NULL)) #endif /* _BLT_LIST_H */ tkdesk-2.0/blt/bltText.c0100644000175000007640000012103510020457430013275 0ustar jccjcc /* * bltText.c -- * * This module implements multi-line, rotate-able text for the BLT toolkit. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #include "bltInt.h" #include #define WINDEBUG 0 #define ROTATE_0 0 #define ROTATE_90 1 #define ROTATE_180 2 #define ROTATE_270 3 static Tcl_HashTable bitmapGCTable; static int initialized; static GC GetBitmapGC _ANSI_ARGS_((Tk_Window tkwin)); static void DrawTextSegments(display, drawable, gc, x, y, layoutPtr) Display *display; Drawable drawable; GC gc; register int x, y; /* Origin of text */ TextLayout *layoutPtr; { register TextSegment *segPtr = layoutPtr->segArr; register int i; for (i = 0; i < layoutPtr->nSegments; i++, segPtr++) { #if HAVE_UTF Tk_DrawChars(display, drawable, gc, layoutPtr->font, segPtr->text, segPtr->count, x + segPtr->x, y + segPtr->y); #else XDrawString(display, drawable, gc, x + segPtr->x, y + segPtr->y, segPtr->text, segPtr->count); #endif /*HAVE_UTF*/ } } /* * ----------------------------------------------------------------- * * Blt_GetTextLayout -- * * Get the extents of a possibly multiple-lined text string. * * Results: * Returns via *widthPtr* and *heightPtr* the dimensions of * the text string. * * ----------------------------------------------------------------- */ TextLayout * Blt_GetTextLayout(string, stylePtr) char string[]; TextStyle *stylePtr; { int maxHeight, maxWidth; int count; /* Count # of characters on each line */ int nSegments; int width; /* Running dimensions of the text */ TextSegment *segPtr; TextLayout *layoutPtr; int lineHeight; int size; register char *p; register int i; Tk_FontMetrics fontMetrics; Tk_GetFontMetrics(stylePtr->font, &fontMetrics); lineHeight = fontMetrics.linespace + stylePtr->leader + stylePtr->shadow.offset; nSegments = 0; for (p = string; *p != '\0'; p++) { if (*p == '\n') { nSegments++; } } if (*(p - 1) != '\n') { nSegments++; } size = sizeof(TextLayout) + (sizeof(TextSegment) * (nSegments - 1)); layoutPtr = (TextLayout *)calloc(1, size); layoutPtr->nSegments = nSegments; layoutPtr->font = stylePtr->font; nSegments = count = 0; width = maxWidth = 0; maxHeight = stylePtr->padTop; segPtr = layoutPtr->segArr; for (p = string; *p != '\0'; p++) { if (*p == '\n') { if (count > 0) { width = Tk_TextWidth(stylePtr->font, string, count) + stylePtr->shadow.offset; if (width > maxWidth) { maxWidth = width; } } segPtr->width = width; segPtr->count = count; segPtr->y = maxHeight + fontMetrics.ascent; segPtr->text = string; segPtr++; nSegments++; maxHeight += lineHeight; string = p + 1; /* Start the string on the next line */ count = 0; /* Reset to indicate the start of a new line */ continue; } count++; } if (nSegments < layoutPtr->nSegments) { width = Tk_TextWidth(stylePtr->font, string, count) + stylePtr->shadow.offset; if (width > maxWidth) { maxWidth = width; } segPtr->width = width; segPtr->count = count; segPtr->y = maxHeight + fontMetrics.ascent; segPtr->text = string; maxHeight += lineHeight; nSegments++; } maxHeight += stylePtr->padBottom; maxWidth += PADDING(stylePtr->padX); segPtr = layoutPtr->segArr; for (i = 0; i < nSegments; i++, segPtr++) { switch (stylePtr->justify) { default: case TK_JUSTIFY_LEFT: /* No offset for left justified text strings */ segPtr->x = stylePtr->padLeft; break; case TK_JUSTIFY_RIGHT: segPtr->x = (maxWidth - segPtr->width) - stylePtr->padRight; break; case TK_JUSTIFY_CENTER: segPtr->x = (maxWidth - segPtr->width) / 2; break; } } layoutPtr->width = maxWidth; layoutPtr->height = maxHeight - stylePtr->leader; return layoutPtr; } /* * ----------------------------------------------------------------- * * Blt_GetTextExtents -- * * Get the extents of a possibly multiple-lined text string. * * Results: * Returns via *widthPtr* and *heightPtr* the dimensions of * the text string. * * ----------------------------------------------------------------- */ void Blt_GetTextExtents(stylePtr, string, widthPtr, heightPtr) TextStyle *stylePtr; char string[]; int *widthPtr, *heightPtr; { int count; /* Count # of characters on each line */ int width, height; int w, lineHeight; register char *p; Tk_FontMetrics fontMetrics; if (string == NULL) { return; /* NULL string? */ } Tk_GetFontMetrics(stylePtr->font, &fontMetrics); lineHeight = fontMetrics.linespace + stylePtr->leader + stylePtr->shadow.offset; count = 0; width = height = 0; for (p = string; *p != '\0'; p++) { if (*p == '\n') { if (count > 0) { w = Tk_TextWidth(stylePtr->font, string, count) + stylePtr->shadow.offset; if (w > width) { width = w; } } height += lineHeight; string = p + 1; /* Start the string on the next line */ count = 0; /* Reset to indicate the start of a new line */ continue; } count++; } if ((count > 0) && (*(p - 1) != '\n')) { height += lineHeight; w = Tk_TextWidth(stylePtr->font, string, count) + stylePtr->shadow.offset; if (w > width) { width = w; } } *widthPtr = width + PADDING(stylePtr->padX); *heightPtr = height + PADDING(stylePtr->padY); } /* * ----------------------------------------------------------------- * * Blt_GetBoundingBox * * Computes the dimensions of the bounding box surrounding a * rectangle rotated about its center. If pointArr isn't NULL, * the coordinates of the rotated rectangle are also returned. * * The dimensions are determined by rotating the rectangle, and * doubling the maximum x-coordinate and y-coordinate. * * w = 2 * maxX, h = 2 * maxY * * Since the rectangle is centered at 0,0, the coordinates of * the bounding box are (-w/2,-h/2 w/2,-h/2, w/2,h/2 -w/2,h/2). * * 0 ------- 1 * | | * | x | * | | * 3 ------- 2 * * Results: * The width and height of the bounding box containing the * rotated rectangle are returned. * * ----------------------------------------------------------------- */ void Blt_GetBoundingBox(width, height, theta, rotWidthPtr, rotHeightPtr, pointArr) int width, height; /* Unrotated region */ double theta; /* Rotation of box */ int *rotWidthPtr, *rotHeightPtr; /* (out) Bounding box region */ XPoint *pointArr; /* (out) Points of the rotated box */ { register int i; double sinTheta, cosTheta; double xMax, yMax; register double x, y; Point2D corner[4]; theta = FMOD(theta, 360.0); if (FMOD(theta, (double)90.0) == 0.0) { int ll, ur, ul, lr; int rotWidth, rotHeight; int quadrant; /* Handle right-angle rotations specifically */ quadrant = (int)(theta / 90.0); switch (quadrant) { case ROTATE_270: /* 270 degrees */ ul = 3, ur = 0, lr = 1, ll = 2; rotWidth = height; rotHeight = width; break; case ROTATE_90: /* 90 degrees */ ul = 1, ur = 2, lr = 3, ll = 0; rotWidth = height; rotHeight = width; break; case ROTATE_180: /* 180 degrees */ ul = 2, ur = 3, lr = 0, ll = 1; rotWidth = width; rotHeight = height; break; default: case ROTATE_0: /* 0 degrees */ ul = 0, ur = 1, lr = 2, ll = 3; rotWidth = width; rotHeight = height; break; } if (pointArr != NULL) { int sx, sy; x = (double)rotWidth *0.5; y = (double)rotHeight *0.5; sx = ROUND(x); sy = ROUND(y); pointArr[ll].x = pointArr[ul].x = -sx; pointArr[ur].y = pointArr[ul].y = -sy; pointArr[lr].x = pointArr[ur].x = sx; pointArr[ll].y = pointArr[lr].y = sy; } *rotWidthPtr = rotWidth; *rotHeightPtr = rotHeight; return; } /* Set the four corners of the rectangle whose center is the origin */ corner[1].x = corner[2].x = (double)width *0.5; corner[0].x = corner[3].x = -corner[1].x; corner[2].y = corner[3].y = (double)height *0.5; corner[0].y = corner[1].y = -corner[2].y; theta = (-theta / 180.0) * M_PI; sinTheta = sin(theta), cosTheta = cos(theta); xMax = yMax = 0.0; /* Rotate the four corners and find the maximum X and Y coordinates */ for (i = 0; i < 4; i++) { x = (corner[i].x * cosTheta) - (corner[i].y * sinTheta); y = (corner[i].x * sinTheta) + (corner[i].y * cosTheta); if (x > xMax) { xMax = x; } if (y > yMax) { yMax = y; } if (pointArr != NULL) { pointArr[i].x = ROUND(x); pointArr[i].y = ROUND(y); } } /* * By symmetry, the width and height of the bounding box are * twice the maximum x and y coordinates. */ *rotWidthPtr = (int)((xMax + xMax) + 0.5); *rotHeightPtr = (int)((yMax + yMax) + 0.5); } /* * ----------------------------------------------------------------- * * Blt_TranslateAnchor -- * * Translate the coordinates of a given bounding box based * upon the anchor specified. The anchor indicates where * the given xy position is in relation to the bounding box. * * nw --- n --- ne * | | * w center e * | | * sw --- s --- se * * The coordinates returned are translated to the origin of the * bounding box (suitable for giving to XCopyArea, XCopyPlane, etc.) * * Results: * The translated coordinates of the bounding box are returned. * * ----------------------------------------------------------------- */ void Blt_TranslateAnchor(x, y, width, height, anchor, transXPtr, transYPtr) int x, y; /* Window coordinates of anchor */ int width, height; /* Extents of the bounding box */ Tk_Anchor anchor; /* Direction of the anchor */ int *transXPtr, *transYPtr; { switch (anchor) { case TK_ANCHOR_NW: /* Upper left corner */ break; case TK_ANCHOR_W: /* Left center */ y -= (height / 2); break; case TK_ANCHOR_SW: /* Lower left corner */ y -= height; break; case TK_ANCHOR_N: /* Top center */ x -= (width / 2); break; case TK_ANCHOR_CENTER: /* Center */ x -= (width / 2); y -= (height / 2); break; case TK_ANCHOR_S: /* Bottom center */ x -= (width / 2); y -= height; break; case TK_ANCHOR_NE: /* Upper right corner */ x -= width; break; case TK_ANCHOR_E: /* Right center */ x -= width; y -= (height / 2); break; case TK_ANCHOR_SE: /* Lower right corner */ x -= width; y -= height; break; } *transXPtr = x; *transYPtr = y; } #ifdef WIN32 /* * ----------------------------------------------------------------- * * Blt_RotateBitmap -- * * Creates a new bitmap containing the rotated image of the given * bitmap. We also need a special GC of depth 1, so that we do * not need to rotate more than one plane of the bitmap. * * Results: * Returns a new bitmap containing the rotated image. * * ----------------------------------------------------------------- */ Pixmap Blt_RotateBitmap(tkwin, srcBitmap, srcWidth, srcHeight, theta, destWidthPtr, destHeightPtr) Tk_Window tkwin; Pixmap srcBitmap; /* Source bitmap to be rotated */ int srcWidth, srcHeight; /* Width and height of the source bitmap */ double theta; /* Right angle rotation to perform */ int *destWidthPtr, *destHeightPtr; { Display *display; /* X display */ Window root; /* Root window drawable */ Pixmap destBitmap; int destWidth, destHeight; HDC srcDC, destDC; TkWinDCState srcState, destState; register int x, y; /* Destination bitmap coordinates */ register int sx, sy; /* Source bitmap coordinates */ unsigned long pixel; TkWinDrawable *drawPtr; BITMAPINFO *bmPtr; int size; unsigned char *srcBits, *destBits; int srcBytesPerRow, destBytesPerRow; display = Tk_Display(tkwin); root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); Blt_GetBoundingBox(srcWidth, srcHeight, theta, &destWidth, &destHeight, (XPoint *)NULL); destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1); if (destBitmap == None) { return None; /* Can't allocate pixmap. */ } srcDC = TkWinGetDrawableDC(display, srcBitmap, &srcState); drawPtr = (TkWinDrawable *) srcBitmap; destDC = TkWinGetDrawableDC(display, destBitmap, &destState); size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 2; bmPtr = (BITMAPINFO *) calloc(1, size); bmPtr->bmiColors[0].rgbBlue = bmPtr->bmiColors[0].rgbGreen = bmPtr->bmiColors[0].rgbRed = 0; bmPtr->bmiColors[1].rgbBlue = bmPtr->bmiColors[1].rgbGreen = bmPtr->bmiColors[1].rgbRed = 0xFF; bmPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmPtr->bmiHeader.biPlanes = 1; bmPtr->bmiHeader.biBitCount = 1; bmPtr->bmiHeader.biCompression = BI_RGB; bmPtr->bmiHeader.biWidth = srcWidth; bmPtr->bmiHeader.biHeight = srcHeight; srcBytesPerRow = ((srcWidth + 31) & ~31) / 8; srcBits = (unsigned char *)malloc(srcHeight * srcBytesPerRow); if (!GetDIBits(destDC, drawPtr->bitmap.handle, 0, srcHeight, (LPVOID) srcBits, bmPtr, DIB_RGB_COLORS)) { #ifdef notdef PurifyPrintf("can't setDIBits: %s\n", Blt_LastError()); #endif goto error; } bmPtr->bmiHeader.biWidth = destWidth; bmPtr->bmiHeader.biHeight = destHeight; destBytesPerRow = ((destWidth + 31) & ~31) / 8; destBits = (unsigned char *)calloc(destHeight, destBytesPerRow); #define GetBit(x, y) \ srcBits[(srcBytesPerRow * y) + (x / 8)] & (0x80 >> (x % 8)) #define SetBit(x, y) \ destBits[(destBytesPerRow * y) + (x / 8)] |= (0x80 >> (x % 8)) theta = FMOD(theta, 360.0); if (FMOD(theta, (double)90.0) == 0.0) { int quadrant; /* Handle right-angle rotations specifically */ quadrant = (int)(theta / 90.0); switch (quadrant) { case ROTATE_90: /* 90 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { sx = y, sy = destWidth - x - 1; pixel = GetBit(sx, sy); if (pixel) { SetBit(x, y); } } } break; case ROTATE_0: /* 0 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { sx = destWidth - x - 1, sy = destHeight - y - 1; pixel = GetBit(sx, sy); if (pixel) { SetBit(x, y); } } } break; case ROTATE_270: /* 270 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { sx = destHeight - y - 1, sy = x; pixel = GetBit(sx, sy); if (pixel) { SetBit(x, y); } } } break; case ROTATE_180: /* 180 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { pixel = GetBit(x, y); if (pixel) { SetBit(x, y); } } } break; default: /* The calling routine should never let this happen. */ break; } } else { double radians, sinTheta, cosTheta; double srcCX, srcCY; /* Center of source rectangle */ double destCX, destCY; /* Center of destination rectangle */ double tx, ty; double rx, ry; /* Angle of rotation for x and y coordinates */ radians = (theta / 180.0) * M_PI; sinTheta = sin(-radians), cosTheta = cos(-radians); /* * Coordinates of the centers of the source and destination rectangles */ srcCX = srcWidth * 0.5; srcCY = srcHeight * 0.5; destCX = destWidth * 0.5; destCY = destHeight * 0.5; /* Rotate each pixel of dest image, placing results in source image */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { /* Translate origin to center of destination image */ tx = x - destCX; ty = y - destCY; /* Rotate the coordinates about the origin */ rx = (tx * cosTheta) - (ty * sinTheta); ry = (tx * sinTheta) + (ty * cosTheta); /* Translate back to the center of the source image */ rx += srcCX; ry += srcCY; sx = ROUND(rx); sy = ROUND(ry); /* * Verify the coordinates, since the destination image can be * bigger than the source */ if ((sx >= srcWidth) || (sx < 0) || (sy >= srcHeight) || (sy < 0)) { continue; } pixel = GetBit(sx, sy); if (pixel) { SetBit(x, y); } } } } drawPtr = (TkWinDrawable *) destBitmap; if (!SetDIBits(destDC, drawPtr->bitmap.handle, 0, destHeight, (LPVOID) destBits, bmPtr, DIB_RGB_COLORS)) { #if WINDEBUG PurifyPrintf("can't setDIBits: %s\n", Blt_LastError()); #endif } error: free((char *)bmPtr); free((char *)destBits); free((char *)srcBits); TkWinReleaseDrawableDC(srcBitmap, srcDC, &srcState); TkWinReleaseDrawableDC(destBitmap, destDC, &destState); *destWidthPtr = destWidth; *destHeightPtr = destHeight; return destBitmap; } #else /* * ----------------------------------------------------------------- * * Blt_RotateBitmap -- * * Creates a new bitmap containing the rotated image of the given * bitmap. We also need a special GC of depth 1, so that we do * not need to rotate more than one plane of the bitmap. * * Results: * Returns a new bitmap containing the rotated image. * * ----------------------------------------------------------------- */ Pixmap Blt_RotateBitmap(tkwin, srcBitmap, srcWidth, srcHeight, theta, destWidthPtr, destHeightPtr) Tk_Window tkwin; Pixmap srcBitmap; /* Source bitmap to be rotated */ int srcWidth, srcHeight; /* Width and height of the source bitmap */ double theta; /* Right angle rotation to perform */ int *destWidthPtr, *destHeightPtr; { Display *display; /* X display */ Window root; /* Root window drawable */ Pixmap destBitmap; int destWidth, destHeight; XImage *src, *dest; register int x, y; /* Destination bitmap coordinates */ register int sx, sy; /* Source bitmap coordinates */ unsigned long pixel; GC bitmapGC; display = Tk_Display(tkwin); root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); /* Create a bitmap and image big enough to contain the rotated text */ Blt_GetBoundingBox(srcWidth, srcHeight, theta, &destWidth, &destHeight, (XPoint *)NULL); destBitmap = Tk_GetPixmap(display, root, destWidth, destHeight, 1); bitmapGC = GetBitmapGC(tkwin); XSetForeground(display, bitmapGC, 0x0); XFillRectangle(display, destBitmap, bitmapGC, 0, 0, destWidth, destHeight); src = XGetImage(display, srcBitmap, 0, 0, srcWidth, srcHeight, 1, ZPixmap); dest = XGetImage(display, destBitmap, 0, 0, destWidth, destHeight, 1, ZPixmap); theta = FMOD(theta, 360.0); if (FMOD(theta, (double)90.0) == 0.0) { int quadrant; /* Handle right-angle rotations specifically */ quadrant = (int)(theta / 90.0); switch (quadrant) { case ROTATE_270: /* 270 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { sx = y, sy = destWidth - x - 1; pixel = XGetPixel(src, sx, sy); if (pixel) { XPutPixel(dest, x, y, pixel); } } } break; case ROTATE_180: /* 180 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { sx = destWidth - x - 1, sy = destHeight - y - 1; pixel = XGetPixel(src, sx, sy); if (pixel) { XPutPixel(dest, x, y, pixel); } } } break; case ROTATE_90: /* 90 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { sx = destHeight - y - 1, sy = x; pixel = XGetPixel(src, sx, sy); if (pixel) { XPutPixel(dest, x, y, pixel); } } } break; case ROTATE_0: /* 0 degrees */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { pixel = XGetPixel(src, x, y); if (pixel) { XPutPixel(dest, x, y, pixel); } } } break; default: /* The calling routine should never let this happen. */ break; } } else { double radians, sinTheta, cosTheta; double srcCX, srcCY; /* Offset from the center of * the source rectangle. */ double destCX, destCY; /* Offset to the center of the destination * rectangle. */ double tx, ty; /* Translated coordinates from center */ double rx, ry; /* Angle of rotation for x and y coordinates */ radians = (theta / 180.0) * M_PI; sinTheta = sin(radians), cosTheta = cos(radians); /* * Coordinates of the centers of the source and destination rectangles */ srcCX = srcWidth * 0.5; srcCY = srcHeight * 0.5; destCX = destWidth * 0.5; destCY = destHeight * 0.5; /* Rotate each pixel of dest image, placing results in source image */ for (x = 0; x < destWidth; x++) { for (y = 0; y < destHeight; y++) { /* Translate origin to center of destination image */ tx = x - destCX; ty = y - destCY; /* Rotate the coordinates about the origin */ rx = (tx * cosTheta) - (ty * sinTheta); ry = (tx * sinTheta) + (ty * cosTheta); /* Translate back to the center of the source image */ rx += srcCX; ry += srcCY; sx = ROUND(rx); sy = ROUND(ry); /* * Verify the coordinates, since the destination image can be * bigger than the source */ if ((sx >= srcWidth) || (sx < 0) || (sy >= srcHeight) || (sy < 0)) { continue; } pixel = XGetPixel(src, sx, sy); if (pixel) { XPutPixel(dest, x, y, pixel); } } } } /* Write the rotated image into the destination bitmap */ XPutImage(display, destBitmap, bitmapGC, dest, 0, 0, 0, 0, destWidth, destHeight); /* Clean up temporary resources used */ XDestroyImage(src), XDestroyImage(dest); *destWidthPtr = destWidth; *destHeightPtr = destHeight; return destBitmap; } #endif /* WIN32 */ /* * ----------------------------------------------------------------- * * Blt_CreateTextBitmap -- * * Draw a bitmap, using the the given window coordinates * as an anchor for the text bounding box. * * Results: * Returns the bitmap representing the text string. * * Side Effects: * Bitmap is drawn using the given font and GC in the * drawable at the given coordinates, anchor, and rotation. * * ----------------------------------------------------------------- */ Pixmap Blt_CreateTextBitmap(tkwin, layoutPtr, stylePtr, bmWidthPtr, bmHeightPtr) Tk_Window tkwin; TextLayout *layoutPtr; /* Text string to draw */ TextStyle *stylePtr; /* Text attributes: rotation, color, font, * linespacing, justification, etc. */ int *bmWidthPtr; int *bmHeightPtr; /* Extents of rotated text string */ { int width, height; Pixmap bitmap; Display *display; Window root; GC bitmapGC; #ifdef WIN32 HDC src; TkWinDCState state; #endif display = Tk_Display(tkwin); width = layoutPtr->width + 2; height = layoutPtr->height + 2; /* Create a temporary bitmap to contain the text string */ root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); bitmap = Tk_GetPixmap(display, root, width, height, 1); assert(bitmap != None); if (bitmap == None) { return None; /* Can't allocate pixmap. */ } /* Clear the pixmap and draw the text string into it */ bitmapGC = GetBitmapGC(tkwin); #ifdef WIN32 src = TkWinGetDrawableDC(display, bitmap, &state); PatBlt(src, 0, 0, width, height, WHITENESS); TkWinReleaseDrawableDC(bitmap, src, &state); #else XSetForeground(display, bitmapGC, 0); XFillRectangle(display, bitmap, bitmapGC, 0, 0, width, height); #endif /* WIN32 */ XSetFont(display, bitmapGC, Tk_FontId(stylePtr->font)); XSetForeground(display, bitmapGC, 1); DrawTextSegments(display, bitmap, bitmapGC, 0, 0, layoutPtr); #ifdef WIN32 /* * Under Win32 when drawing into a bitmap, the bits are * reversed. Which is why we are inverting the bitmap here. */ src = TkWinGetDrawableDC(display, bitmap, &state); PatBlt(src, 0, 0, layoutPtr->width, layoutPtr->height, DSTINVERT); TkWinReleaseDrawableDC(bitmap, src, &state); #endif if (stylePtr->theta != 0.0) { Pixmap rotBitmap; /* Replace the text pixmap with a rotated one */ rotBitmap = Blt_RotateBitmap(tkwin, bitmap, layoutPtr->width, layoutPtr->height, stylePtr->theta, bmWidthPtr, bmHeightPtr); assert(rotBitmap); if (rotBitmap != None) { Tk_FreePixmap(display, bitmap); return rotBitmap; } } *bmWidthPtr = layoutPtr->width, *bmHeightPtr = layoutPtr->height; return bitmap; } #ifdef WIN32 /* * ----------------------------------------------------------------------- * * Blt_ScaleBitmapRegion -- * * Creates a new scaled bitmap from another bitmap. The new bitmap * is bounded by a specified region. Only this portion of the bitmap * is scaled from the original bitmap. * * By bounding scaling to a region we can generate a new bitmap * which is no bigger than the specified viewport. * * Results: * The new scaled bitmap is returned. * * Side Effects: * A new pixmap is allocated. The caller must release this. * * ----------------------------------------------------------------------- */ Pixmap Blt_ScaleBitmapRegion(tkwin, srcBitmap, srcWidth, srcHeight, destWidth, destHeight, regionPtr) Tk_Window tkwin; Pixmap srcBitmap; int srcWidth, srcHeight, destWidth, destHeight; ImageRegion *regionPtr; { TkWinDCState srcState, destState; HDC src, dest; Pixmap destBitmap; double xScale, yScale; register int sx, sy; /* Source bitmap coordinates */ Window root; Display *display; /* Create a new bitmap the size of the region and clear it */ display = Tk_Display(tkwin); root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); destBitmap = Tk_GetPixmap(display, root, regionPtr->width, regionPtr->height, 1); if (destBitmap == None) { return None; } /* Compute scaling factors from destination to source bitmaps */ xScale = (double)srcWidth / (double)destWidth; yScale = (double)srcHeight / (double)destHeight; src = TkWinGetDrawableDC(display, srcBitmap, &srcState); dest = TkWinGetDrawableDC(display, destBitmap, &destState); sx = ROUND(regionPtr->x * xScale); sy = ROUND(regionPtr->y * yScale); srcWidth = ROUND(regionPtr->width * xScale); srcHeight = ROUND(regionPtr->height * yScale); StretchBlt(dest, 0, 0, destWidth, destHeight, src, sx, sy, srcWidth, srcHeight, SRCCOPY); TkWinReleaseDrawableDC(srcBitmap, src, &srcState); TkWinReleaseDrawableDC(destBitmap, dest, &destState); return destBitmap; } #else /* * ----------------------------------------------------------------------- * * Blt_ScaleBitmapRegion -- * * Creates a new scaled bitmap from another bitmap. The new bitmap * is bounded by a specified region. Only this portion of the bitmap * is scaled from the original bitmap. * * By bounding scaling to a region we can generate a new bitmap * which is no bigger than the specified viewport. * * Results: * The new scaled bitmap is returned. * * Side Effects: * A new pixmap is allocated. The caller must release this. * * ----------------------------------------------------------------------- */ Pixmap Blt_ScaleBitmapRegion(tkwin, srcBitmap, srcWidth, srcHeight, destWidth, destHeight, regionPtr) Tk_Window tkwin; Pixmap srcBitmap; int srcWidth, srcHeight, destWidth, destHeight; ImageRegion *regionPtr; { Display *display; Window root; XImage *src, *dest; Pixmap destBitmap; GC bitmapGC; double xScale, yScale; register int x, y; /* Destination bitmap coordinates */ register int sx, sy; /* Source bitmap coordinates */ unsigned long pixel; double tmp; /* Create a new bitmap the size of the region and clear it */ display = Tk_Display(tkwin); root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); destBitmap = Tk_GetPixmap(display, root, regionPtr->width, regionPtr->height, 1); bitmapGC = GetBitmapGC(tkwin); XSetForeground(display, bitmapGC, 0x0); XFillRectangle(display, destBitmap, bitmapGC, 0, 0, regionPtr->width, regionPtr->height); src = XGetImage(display, srcBitmap, 0, 0, srcWidth, srcHeight, 1, ZPixmap); dest = XGetImage(display, destBitmap, 0, 0, regionPtr->width, regionPtr->height, 1, ZPixmap); /* * Scale each pixel of destination image from results of source * image. Verify the coordinates, since the destination image can * be bigger than the source */ xScale = (double)srcWidth / (double)destWidth; yScale = (double)srcHeight / (double)destHeight; for (y = 0; y < regionPtr->height; y++) { tmp = (double)(y + regionPtr->y) * yScale; sy = ROUND(tmp); if (sy >= srcHeight) { continue; } for (x = 0; x < regionPtr->width; x++) { tmp = (double)(x + regionPtr->x) * xScale; sx = ROUND(tmp); if (sx >= srcWidth) { continue; } pixel = XGetPixel(src, sx, sy); if (pixel) { XPutPixel(dest, x, y, pixel); } } } /* Write the rotated image into the destination bitmap */ XPutImage(display, destBitmap, bitmapGC, dest, 0, 0, 0, 0, regionPtr->width, regionPtr->height); XDestroyImage(src), XDestroyImage(dest); return destBitmap; } #endif /* WIN32 */ /* * ----------------------------------------------------------------------- * * Blt_ScaleBitmap -- * * Same as Blt_ScaleBitmapRegion, except that the region is unbounded. * The scaled bitmap will be a fully scaled version of the original, * not a portion of it. * * Results: * The new scaled bitmap is returned. * * Side Effects: * A new pixmap is allocated. The caller must release this. * * ----------------------------------------------------------------------- */ Pixmap Blt_ScaleBitmap(tkwin, srcBitmap, srcWidth, srcHeight, scaledWidth, scaledHeight) Tk_Window tkwin; Pixmap srcBitmap; int srcWidth, srcHeight, scaledWidth, scaledHeight; { ImageRegion region; region.x = region.y = 0; region.width = scaledWidth; region.height = scaledHeight; return Blt_ScaleBitmapRegion(tkwin, srcBitmap, srcWidth, srcHeight, scaledWidth, scaledHeight, ®ion); } /*LINTLIBRARY*/ void Blt_InitTextStyle(stylePtr) TextStyle *stylePtr; { /* Initialize these attributes to zero */ stylePtr->leader = 0; stylePtr->shadow.offset = 0; stylePtr->padLeft = stylePtr->padRight = 0; stylePtr->padTop = stylePtr->padBottom = 0; stylePtr->shadow.color = stylePtr->activeColor = stylePtr->color = (XColor *)NULL; stylePtr->theta = 0.0; stylePtr->state = 0; stylePtr->anchor = TK_ANCHOR_CENTER; stylePtr->justify = TK_JUSTIFY_CENTER; stylePtr->font = NULL; } void Blt_SetDrawTextStyle(stylePtr, font, gc, normalColor, activeColor, shadowColor, theta, anchor, justify, leader, shadowOffset) TextStyle *stylePtr; Tk_Font font; GC gc; XColor *normalColor, *activeColor, *shadowColor; double theta; Tk_Anchor anchor; Tk_Justify justify; int leader, shadowOffset; { Blt_InitTextStyle(stylePtr); stylePtr->gc = gc; stylePtr->color = normalColor; stylePtr->activeColor = activeColor; stylePtr->shadow.color = shadowColor; stylePtr->font = font; stylePtr->theta = theta; stylePtr->anchor = anchor; stylePtr->justify = justify; stylePtr->leader = leader; stylePtr->shadow.offset = shadowOffset; } void Blt_SetPrintTextStyle(stylePtr, font, fgColor, activeColor, shadowColor, theta, anchor, justify, leader, shadowOffset) TextStyle *stylePtr; Tk_Font font; XColor *fgColor, *activeColor, *shadowColor; double theta; Tk_Anchor anchor; Tk_Justify justify; int leader, shadowOffset; { Blt_InitTextStyle(stylePtr); stylePtr->color = fgColor; stylePtr->activeColor = activeColor; stylePtr->shadow.color = shadowColor; stylePtr->font = font; stylePtr->theta = theta; stylePtr->anchor = anchor; stylePtr->justify = justify; stylePtr->leader = leader; stylePtr->shadow.offset = shadowOffset; } /* * ----------------------------------------------------------------- * * DrawText -- * * Draw a text string, possibly rotated, using the the given * window coordinates as an anchor for the text bounding box. * If the text is not rotated, simply use the X text drawing * routines. Otherwise, generate a bitmap of the rotated text. * * Results: * Returns the x-coordinate to the right of the text. * * Side Effects: * Text string is drawn using the given font and GC at the * the given window coordinates. * * The Stipple, FillStyle, and TSOrigin fields of the GC are * modified for rotated text. This assumes the GC is private, * *not* shared (via Tk_GetGC) * * ----------------------------------------------------------------- */ void Blt_DrawTextLayout(tkwin, drawable, layoutPtr, stylePtr, x, y) Tk_Window tkwin; Drawable drawable; TextLayout *layoutPtr; TextStyle *stylePtr; /* Text attribute information */ int x, y; /* Window coordinates to draw text */ { int width, height; double theta; Display *display; Pixmap bitmap; int active; display = Tk_Display(tkwin); theta = FMOD(stylePtr->theta, (double)360.0); if (theta < 0.0) { theta += 360.0; } active = stylePtr->state & STATE_ACTIVE; if (theta == 0.0) { /* * This is the easy case of no rotation. Simply draw the text * using the standard drawing routines. Handle offset printing * for engraved (disabled) and shadowed text. */ width = layoutPtr->width, height = layoutPtr->height; Blt_TranslateAnchor(x, y, width, height, stylePtr->anchor, &x, &y); if (stylePtr->state & (STATE_DISABLED | STATE_EMPHASIS)) { TkBorder *borderPtr = (TkBorder *) stylePtr->border; XColor *color1, *color2; color1 = borderPtr->lightColor, color2 = borderPtr->darkColor; if (stylePtr->state & STATE_EMPHASIS) { XColor *hold; hold = color1, color1 = color2, color2 = hold; } if (color1 != NULL) { XSetForeground(display, stylePtr->gc, color1->pixel); } DrawTextSegments(display, drawable, stylePtr->gc, x + 1, y + 1, layoutPtr); if (color2 != NULL) { XSetForeground(display, stylePtr->gc, color2->pixel); } DrawTextSegments(display, drawable, stylePtr->gc, x, y, layoutPtr); /* Reset the foreground color back to its original setting, * so not to invalidate the GC cache. */ XSetForeground(display, stylePtr->gc, stylePtr->color->pixel); return; /* Done */ } if ((stylePtr->shadow.offset > 0) && (stylePtr->shadow.color != NULL)) { XSetForeground(display, stylePtr->gc, stylePtr->shadow.color->pixel); DrawTextSegments(display, drawable, stylePtr->gc, x + stylePtr->shadow.offset, y + stylePtr->shadow.offset, layoutPtr); XSetForeground(display, stylePtr->gc, stylePtr->color->pixel); } if (active) { XSetForeground(display, stylePtr->gc, stylePtr->activeColor->pixel); } DrawTextSegments(display, drawable, stylePtr->gc, x, y, layoutPtr); if (active) { XSetForeground(display, stylePtr->gc, stylePtr->color->pixel); } return; /* Done */ } #ifdef WIN32 if (Blt_DrawRotatedText(display, drawable, x, y, theta, stylePtr, layoutPtr)) { return; } #endif /* * Rotate the text by writing the text into a bitmap and rotating * the bitmap. Set the clip mask and origin in the GC first. And * make sure we restore the GC because it may be shared. */ stylePtr->theta = theta; bitmap = Blt_CreateTextBitmap(tkwin, layoutPtr, stylePtr, &width, &height); if (bitmap == None) { return; } Blt_TranslateAnchor(x, y, width, height, stylePtr->anchor, &x, &y); theta = FMOD(theta, (double)90.0); XSetClipMask(display, stylePtr->gc, bitmap); if (stylePtr->state & (STATE_DISABLED | STATE_EMPHASIS)) { TkBorder *borderPtr = (TkBorder *) stylePtr->border; XColor *color1, *color2; color1 = borderPtr->lightColor, color2 = borderPtr->darkColor; if (stylePtr->state & STATE_EMPHASIS) { XColor *hold; hold = color1, color1 = color2, color2 = hold; } if (color1 != NULL) { XSetForeground(display, stylePtr->gc, color1->pixel); } XSetClipOrigin(display, stylePtr->gc, x + 1, y + 1); XCopyPlane(display, bitmap, drawable, stylePtr->gc, 0, 0, width, height, x + 1, y + 1, 1); if (color2 != NULL) { XSetForeground(display, stylePtr->gc, color2->pixel); } XSetClipOrigin(display, stylePtr->gc, x, y); XCopyPlane(display, bitmap, drawable, stylePtr->gc, 0, 0, width, height, x, y, 1); XSetForeground(display, stylePtr->gc, stylePtr->color->pixel); } else { if ((stylePtr->shadow.offset > 0) && (stylePtr->shadow.color != NULL)) { XSetClipOrigin(display, stylePtr->gc, x + stylePtr->shadow.offset, y + stylePtr->shadow.offset); XSetForeground(display, stylePtr->gc, stylePtr->shadow.color->pixel); XCopyPlane(display, bitmap, drawable, stylePtr->gc, 0, 0, width, height, x + stylePtr->shadow.offset, y + stylePtr->shadow.offset, 1); XSetForeground(display, stylePtr->gc, stylePtr->color->pixel); } if (active) { XSetForeground(display, stylePtr->gc, stylePtr->activeColor->pixel); } XSetClipOrigin(display, stylePtr->gc, x, y); XCopyPlane(display, bitmap, drawable, stylePtr->gc, 0, 0, width, height, x, y, 1); if (active) { XSetForeground(display, stylePtr->gc, stylePtr->color->pixel); } } XSetClipMask(display, stylePtr->gc, None); Tk_FreePixmap(display, bitmap); } void Blt_DrawText2(tkwin, drawable, string, stylePtr, x, y, areaPtr) Tk_Window tkwin; Drawable drawable; char string[]; TextStyle *stylePtr; /* Text attribute information */ int x, y; /* Window coordinates to draw text */ Dim2D *areaPtr; { TextLayout *layoutPtr; int width, height; double theta; if ((string == NULL) || (*string == '\0')) { return; /* Empty string, do nothing */ } layoutPtr = Blt_GetTextLayout(string, stylePtr); Blt_DrawTextLayout(tkwin, drawable, layoutPtr, stylePtr, x, y); theta = FMOD(stylePtr->theta, (double)360.0); if (theta < 0.0) { theta += 360.0; } width = layoutPtr->width; height = layoutPtr->height; if (theta != 0.0) { Blt_GetBoundingBox(width, height, theta, &width, &height, (XPoint *)NULL); } free((char *)layoutPtr); areaPtr->width = width; areaPtr->height = height; } void Blt_DrawText(tkwin, drawable, string, stylePtr, x, y) Tk_Window tkwin; Drawable drawable; char string[]; TextStyle *stylePtr; /* Text attribute information */ int x, y; /* Window coordinates to draw text */ { TextLayout *layoutPtr; if ((string == NULL) || (*string == '\0')) { return; /* Empty string, do nothing */ } layoutPtr = Blt_GetTextLayout(string, stylePtr); Blt_DrawTextLayout(tkwin, drawable, layoutPtr, stylePtr, x, y); free((char *)layoutPtr); } static GC GetBitmapGC(tkwin) Tk_Window tkwin; { int isNew; GC gc; Display *dpy; Tcl_HashEntry *hPtr; if (!initialized) { Tcl_InitHashTable(&bitmapGCTable, TCL_ONE_WORD_KEYS); initialized = TRUE; } dpy = Tk_Display(tkwin); hPtr = Tcl_CreateHashEntry(&bitmapGCTable, (char *)dpy, &isNew); if (isNew) { Pixmap bitmap; XGCValues gcValues; unsigned int gcMask; Window root; root = RootWindow(dpy, Tk_ScreenNumber(tkwin)); bitmap = Tk_GetPixmap(dpy, root, 1, 1, 1); gcValues.foreground = gcValues.background = 0; gcMask = (GCForeground | GCBackground); gc = Blt_GetPrivateGCFromDrawable(tkwin, bitmap, gcMask, &gcValues); Tk_FreePixmap(dpy, bitmap); Tcl_SetHashValue(hPtr, (ClientData)gc); } else { gc = (GC)Tcl_GetHashValue(hPtr); } return gc; } void Blt_ResetTextStyle(tkwin, stylePtr) Tk_Window tkwin; TextStyle *stylePtr; { GC newGC; XGCValues gcValues; unsigned long gcMask; gcMask = GCFont; gcValues.font = Tk_FontId(stylePtr->font); if (stylePtr->color != NULL) { gcMask |= GCForeground; gcValues.foreground = stylePtr->color->pixel; } newGC = Tk_GetGC(tkwin, gcMask, &gcValues); if (stylePtr->gc != NULL) { Tk_FreeGC(Tk_Display(tkwin), stylePtr->gc); } stylePtr->gc = newGC; } void Blt_FreeTextStyle(display, stylePtr) Display *display; TextStyle *stylePtr; { if (stylePtr->gc != NULL) { Tk_FreeGC(display, stylePtr->gc); } } tkdesk-2.0/blt/bltText.h0100644000175000007640000001660310020457430013306 0ustar jccjcc/* * bltText.h -- * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #ifndef _BLT_TEXT_H #define _BLT_TEXT_H #if (TK_MAJOR_VERSION == 4) /* * The following structure is used by Tk_GetFontMetrics() to return * information about the properties of a Tk_Font. */ typedef struct Tk_FontMetrics { int ascent; /* The amount in pixels that the tallest * letter sticks up above the baseline, plus * any extra blank space added by the designer * of the font. */ int descent; /* The largest amount in pixels that any * letter sticks below the baseline, plus any * extra blank space added by the designer of * the font. */ int linespace; /* The sum of the ascent and descent. How * far apart two lines of text in the same * font should be placed so that none of the * characters in one line overlap any of the * characters in the other line. */ } Tk_FontMetrics; typedef XFontStruct *Tk_Font; #define Tk_FontId(font) ((font)->fid) #define Tk_TextWidth(font, str, len) (XTextWidth((font),(str),(len))) #define Tk_GetFontMetrics(font, fmPtr) \ ((fmPtr)->ascent = (font)->ascent, \ (fmPtr)->descent = (font)->descent, \ (fmPtr)->linespace = (font)->ascent + (font)->descent) #define Tk_NameOfFont(font) (Tk_NameOfFontStruct(font)) #define Tk_DrawChars(dpy, draw, gc, font, str, len, x, y) \ TkDisplayChars((dpy),(draw),(gc),(font),(str),(len),(x),(y), 0, DEF_TEXT_FLAGS) #define Tk_MeasureChars(font, text, len, maxPixels, flags, lenPtr) \ TkMeasureChars((font),(text), (len), 0, maxPixels, 0,(flags), (lenPtr)) extern int TkMeasureChars _ANSI_ARGS_((Tk_Font font, char *source, int maxChars, int startX, int maxX, int tabOrigin, int flags, int *nextXPtr)); extern void TkDisplayChars _ANSI_ARGS_((Display *display, Drawable drawable, GC gc, Tk_Font font, char *string, int length, int x, int y, int tabOrigin, int flags)); /* * FLAGS passed to TkMeasureChars: */ #define TK_WHOLE_WORDS (1<<0) #define TK_AT_LEAST_ONE (1<<1) #define TK_PARTIAL_OK (1<<2) #define TK_IGNORE_NEWLINES (1<<3) #define TK_IGNORE_TABS (1<<4) #define NO_FLAGS 0 #endif /* TK_MAJOR_VERSION == 4 */ #define DEF_TEXT_FLAGS (TK_PARTIAL_OK | TK_IGNORE_NEWLINES) /* * ---------------------------------------------------------------------- * * TextSegment -- * * ---------------------------------------------------------------------- */ typedef struct { char *text; /* Text to be displayed */ int count; /* Number of bytes in text. The actual * character count may differ because of * multi-byte UTF encodings. */ int x, y; /* X-Y offset of the baseline from the * upper-left corner of the bbox. */ int sx, sy; /* See bltWinUtil.c */ int width; /* Width of line in pixels. This information * is used to draw PostScript strings the * same width as X */ } TextSegment; /* * ---------------------------------------------------------------------- * * TextLayout -- * * ---------------------------------------------------------------------- */ typedef struct { Tk_Font font; /* Default font for the text */ int nSegments; /* # segments of text */ short int width, height; /* Dimensions of text bounding box */ TextSegment segArr[1]; /* Information about each segment of text */ } TextLayout; typedef struct Shadow { XColor *color; int offset; } Shadow; /* * ---------------------------------------------------------------------- * * TextStyle -- * * Represents a convenient structure to hold text attributes * which determine how a text string is to be displayed on the * window, or drawn with PostScript commands. The alternative * is to pass lots of parameters to the drawing and printing * routines. This seems like a more efficient and less cumbersome * way of passing parameters. * * ---------------------------------------------------------------------- */ typedef struct TextStyle { unsigned int state; /* If non-zero, indicates to draw text * in the active color */ short int width, height; /* Extents of text */ XColor *color; /* Normal color */ XColor *activeColor; /* Active color */ Tk_Font font; /* Font to use to draw text */ Tk_3DBorder border; /* Background color of text. This is also * used for drawing disabled text. */ Shadow shadow; /* Drop shadow color and offset */ Tk_Justify justify; /* Justification of the text string. This * only matters if the text is composed * of multiple lines. */ GC gc; /* GC used to draw the text */ double theta; /* Rotation of text in degrees. */ Tk_Anchor anchor; /* Indicates how the text is anchored around * its x and y coordinates. */ Pad padX, padY; /* # pixels padding of around text region */ short int leader; /* # pixels spacing between lines of text */ } TextStyle; extern TextLayout *Blt_GetTextLayout _ANSI_ARGS_((char *string, TextStyle *stylePtr)); extern void Blt_GetTextExtents _ANSI_ARGS_((TextStyle *stylePtr, char *text, int *widthPtr, int *heightPtr)); extern void Blt_InitTextStyle _ANSI_ARGS_((TextStyle *stylePtr)); extern void Blt_ResetTextStyle _ANSI_ARGS_((Tk_Window tkwin, TextStyle *stylePtr)); extern void Blt_FreeTextStyle _ANSI_ARGS_((Display *display, TextStyle *stylePtr)); extern void Blt_SetDrawTextStyle _ANSI_ARGS_((TextStyle *stylePtr, Tk_Font font, GC gc, XColor *normalColor, XColor *activeColor, XColor *shadowColor, double theta, Tk_Anchor anchor, Tk_Justify justify, int leader, int shadowOffset)); extern void Blt_SetPrintTextStyle _ANSI_ARGS_((TextStyle *stylePtr, Tk_Font font, XColor *fgColor, XColor *bgColor, XColor *shadowColor, double theta, Tk_Anchor anchor, Tk_Justify justify, int leader, int shadowOffset)); extern void Blt_DrawText _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, char *string, TextStyle *stylePtr, int x, int y)); extern void Blt_DrawTextLayout _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, TextLayout *layoutPtr, TextStyle *stylePtr, int x, int y)); extern void Blt_DrawText2 _ANSI_ARGS_((Tk_Window tkwin, Drawable drawable, char *string, TextStyle *stylePtr, int x, int y, Dim2D * dimPtr)); extern Pixmap Blt_CreateTextBitmap _ANSI_ARGS_((Tk_Window tkwin, TextLayout *layoutPtr, TextStyle *stylePtr, int *widthPtr, int *heightPtr)); extern int Blt_DrawRotatedText _ANSI_ARGS_((Display *display, Drawable drawable, int x, int y, double theta, TextStyle *stylePtr, TextLayout *layoutPtr)); #endif /* _BLT_TEXT_H */ tkdesk-2.0/blt/bltTkInt.h0100644000175000007640000001762610020457430013421 0ustar jccjcc/* * bltTkInt.h -- * * Contains copies of internal Tk structures. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #ifndef _BLT_TKINT_H #define _BLT_TKINT_H typedef struct TkFontAttributes { Tk_Uid family; /* Font family. The most important field. */ int pointsize; /* Pointsize of font, 0 for default size, or * negative number meaning pixel size. */ int weight; /* Weight flag; see below for def'n. */ int slant; /* Slant flag; see below for def'n. */ int underline; /* Non-zero for underline font. */ int overstrike; /* Non-zero for overstrike font. */ } TkFontAttributes; typedef struct TkFontMetrics { int ascent; /* From baseline to top of font. */ int descent; /* From baseline to bottom of font. */ int maxWidth; /* Width of widest character in font. */ int fixed; /* Non-zero if this is a fixed-width font, * 0 otherwise. */ } TkFontMetrics; typedef struct TkFont { /* * Fields used and maintained exclusively by generic code. */ #if (TK_VERSION_NUMBER >= _VERSION(8,1,0)) int resourceRefCount; /* Number of active uses of this font (each * active use corresponds to a call to * Tk_AllocFontFromTable or Tk_GetFont). * If this count is 0, then this TkFont * structure is no longer valid and it isn't * present in a hash table: it is being * kept around only because there are objects * referring to it. The structure is freed * when resourceRefCount and objRefCount * are both 0. */ int objRefCount; /* The number of Tcl objects that reference * this structure. */ #else int refCount; /* Number of users of the TkFont. */ #endif Tcl_HashEntry *cacheHashPtr;/* Entry in font cache for this structure, * used when deleting it. */ Tcl_HashEntry *namedHashPtr;/* Pointer to hash table entry that * corresponds to the named font that the * tkfont was based on, or NULL if the tkfont * was not based on a named font. */ #if (TK_VERSION_NUMBER >= _VERSION(8,1,0)) Screen *screen; /* The screen where this font is valid. */ #endif /* TK_VERSION_NUMBER >= 8.1.0 */ int tabWidth; /* Width of tabs in this font (pixels). */ int underlinePos; /* Offset from baseline to origin of * underline bar (used for drawing underlines * on a non-underlined font). */ int underlineHeight; /* Height of underline bar (used for drawing * underlines on a non-underlined font). */ /* * Fields in the generic font structure that are filled in by * platform-specific code. */ Font fid; /* For backwards compatibility with XGCValues * structures. Remove when TkGCValues is * implemented. */ TkFontAttributes fa; /* Actual font attributes obtained when the * the font was created, as opposed to the * desired attributes passed in to * TkpGetFontFromAttributes(). The desired * metrics can be determined from the string * that was used to create this font. */ TkFontMetrics fm; /* Font metrics determined when font was * created. */ #if (TK_VERSION_NUMBER >= _VERSION(8,1,0)) struct TkFont *nextPtr; /* Points to the next TkFont structure with * the same name. All fonts with the * same name (but different displays) are * chained together off a single entry in * a hash table. */ #endif /* TK_VERSION_NUMBER >= 8.1.0 */ } TkFont; /* * This structure is used by the Mac and Window porting layers as * the internal representation of a clip_mask in a GC. */ typedef struct TkRegion_ *TkRegion; typedef struct TkpClipMask { int type; /* One of TKP_CLIP_PIXMAP or TKP_CLIP_REGION */ union { Pixmap pixmap; TkRegion region; } value; } TkpClipMask; #define TKP_CLIP_PIXMAP 0 #define TKP_CLIP_REGION 1 #ifdef WIN32 /* * The TkWinDrawable is the internal implementation of an X Drawable (either * a Window or a Pixmap). The following constants define the valid Drawable * types. */ #define TWD_BITMAP 1 #define TWD_WINDOW 2 #define TWD_WINDC 3 struct TkWindow; typedef struct { int type; HWND handle; struct TkWindow *winPtr; } TkWinWindow; typedef struct { int type; HBITMAP handle; Colormap colormap; int depth; } TkWinBitmap; typedef struct { int type; HDC hdc; } TkWinDC; typedef union { int type; TkWinWindow window; TkWinBitmap bitmap; TkWinDC winDC; } TkWinDrawable; /* * The TkWinDCState is used to save the state of a device context * so that it can be restored later. */ typedef struct TkWinDCState { HPALETTE palette; } TkWinDCState; extern HDC TkWinGetDrawableDC(Display *display, Drawable drawable, TkWinDCState * state); extern HDC TkWinReleaseDrawableDC(Drawable drawable, HDC dc, TkWinDCState * state); extern HWND Tk_GetHWND _ANSI_ARGS_((Window window)); extern HINSTANCE Tk_GetHINSTANCE _ANSI_ARGS_((void)); extern Window Tk_AttachHWND _ANSI_ARGS_((Tk_Window tkwin, HWND hWnd)); #endif /* WIN32 */ /* * The Border structure used internally by the Tk_3D* routines. * The following is a copy of it from tk3d.c. */ typedef struct { Screen *screen; /* Screen on which the border will be used. */ Visual *visual; /* Visual for all windows and pixmaps using * the border. */ int depth; /* Number of bits per pixel of drawables where * the border will be used. */ Colormap colormap; /* Colormap out of which pixels are * allocated. */ int refCount; /* Number of different users of * this border. */ #if (TK_VERSION_NUMBER >= _VERSION(8,1,0)) int objRefCount; /* The number of Tcl objects that reference * this structure. */ #endif /* TK_VERSION_NUMBER >= 8.1.0 */ XColor *bgColor; /* Background color (intensity between * lightColorPtr and darkColorPtr). */ XColor *darkColor; /* Color for darker areas (must free when * deleting structure). NULL means shadows * haven't been allocated yet.*/ XColor *lightColor; /* Color used for lighter areas of border * (must free this when deleting structure). * NULL means shadows haven't been allocated * yet. */ Pixmap shadow; /* Stipple pattern to use for drawing * shadows areas. Used for displays with * <= 64 colors or where colormap has filled * up. */ GC bgGC; /* Used (if necessary) to draw areas in * the background color. */ GC darkGC; /* Used to draw darker parts of the * border. None means the shadow colors * haven't been allocated yet.*/ GC lightGC; /* Used to draw lighter parts of * the border. None means the shadow colors * haven't been allocated yet. */ Tcl_HashEntry *hashPtr; /* Entry in borderTable (needed in * order to delete structure). */ struct TkBorder *nextPtr; /* Points to the next TkBorder structure with * the same color name. Borders with the * same name but different screens or * colormaps are chained together off a * single entry in borderTable. */ } TkBorder; #endif /* BLT_TKINT_H */ tkdesk-2.0/blt/bltUnixDnd.c0100644000175000007640000043643410020457430013736 0ustar jccjcc/* * bltUnixDnd.c -- * * This module implements a drag-and-drop manager for the BLT * Toolkit. Allows widgets to be registered as drag&drop sources * and targets for handling "drag-and-drop" operations between * Tcl/Tk applications. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * The "drag&drop" command was created by Michael J. McLennan. */ #include "bltInt.h" #ifndef NO_DRAGDROP #include "bltChain.h" #include #include #define DND_THREAD_KEY "BLT Dnd Data" #define PACK(lo,hi) (((hi) << 16) | ((lo) & 0x0000FFFF)) #define UNPACK(x,lo,hi) ((lo) = (x & 0x0000FFFF), (hi) = (x >> 16)) #define WATCH_ENTER (1<<0) #define WATCH_LEAVE (1<<1) #define WATCH_MOTION (1<<2) #define WATCH_MASK (WATCH_ENTER | WATCH_LEAVE | WATCH_MOTION) #define DRAG_ENTER 0x1001 #define DRAG_LEAVE 0x1002 #define DRAG_MOTION 0x1003 #define DROP_REQUEST 0x1004 #define DRAG_RESPONSE 0x1005 #define DROP_RESPONSE 0x1006 #define DROP_START 0x1007 #define MESG_INDEX_WINDOW 1 /* Index of window id in message. */ #define MESG_INDEX_TIMESTAMP 2 /* Index of transaction timestamp. */ #define MESG_INDEX_POINT 3 /* Index of root X-Y coordinate. */ #define MESG_INDEX_STATE 4 /* Index of button #/key state. */ #define MESG_INDEX_RESP 3 /* Index of mouse event response. */ #define MESG_INDEX_FORMAT 3 /* Index of button #/key state. */ #define MESG_INDEX_PROPERTY 4 /* Index of button #/key state. */ #define DRAG_CAN_DROP 0 #define DRAG_CANT_DROP 1 #define DRAG_CANCEL -1 #define ACTION_CANCEL -1 #define ACTION_COPY 0 #define ACTION_LINK 1 #define ACTION_MOVE 2 #define DROP_FAIL -1 #define DROP_OK 0 #define DROP_CONTINUE 1 #define DROP_TIMEOUT 2 #define PROP_WATCH_FLAGS 0 #define PROP_DATA_FORMATS 1 #define PROP_MAX_SIZE 1000 /* Maximum size of property. */ #define PROTO_BLT 0 #define PROTO_XDND 1 #define TOKEN_OFFSET 0 #define TOKEN_REDRAW (1<<0) #define TOKEN_STATUS_REJECT -1 #define TOKEN_STATUS_NORMAL 0 #define TOKEN_STATUS_ACTIVE 1 /* * Each widget representing a drag & drop target is tagged with * a "BltDndTarget" property in XA_STRING format. This property * identifies the window as a target. It's formated as a Tcl list * and contains the following information: * * "flags DATA_TYPE DATA_TYPE ..." * * "INTERP_NAME TARGET_NAME WINDOW_ID DATA_TYPE DATA_TYPE ..." * * INTERP_NAME Name of the target application's interpreter. * TARGET_NAME Path name of widget registered as the drop target. * WINDOW_ID Window Id of the target's communication window. * Used to forward Enter/Leave/Motion event information * to the target. * DATA_TYPE One or more "types" handled by the target. * * When the user invokes the "drag" operation, the window hierarchy * is progressively examined. Window information is cached during * the operation, to minimize X server traffic. Windows carrying a * "BltDndTarget" property are identified. When the token is dropped * over a valid site, the drop information is sent to the application * via the usual "send" command. If communication fails, the drag&drop * facility automatically posts a rejection symbol on the token window. */ /* * Drop Protocol: * * Source Target * ------ ------ * ButtonRelease-? event. * Invokes blt::dnd drop * + * Send "drop" message to target (via * ClientMessage). Contains X-Y, key/ --> Gets "drop" message. * button state, source window XID. Invokes LeaveCmd proc. * Gets property from source of ordered * matching formats. * + * Invokes DropCmd proc. Arguments * are X-Y coordinate, key/button * state, transaction timestamp, * list of matching formats. * + * Target selects format and invokes * blt::dnd pull to transfer the data * in the selected format. * + * Sends "drop start" message to * source. Contains selected format * Gets "drop start" message. <-- (as atom), ?action?, target window * Invokes data handler for the ID, transaction timestamp. * selected format. + * + Waits for property to change on * Places first packet of data in its window. Time out set for * property on target window. --> no response. * + + * Waits for response property After each packet, sets zero-length * change. Time out set for no resp. <-- property on source window. * If non-zero length packet, error + * occurred, packet is error message. Sends "drop finished" message. * Contains transaction timestamp, * Gets "drop finished" message. <-- status, ?action?. * Invokes FinishCmd proc. */ /* Configuration Parameters */ #define DEF_TARGET_ENTER_COMMAND (char *)NULL #define DEF_TARGET_MOTION_COMMAND (char *)NULL #define DEF_TARGET_LEAVE_COMMAND (char *)NULL #define DEF_DND_BUTTON_BG_COLOR RGB_YELLOW #define DEF_DND_BUTTON_BG_MONO STD_MONO_NORMAL_BG #define DEF_DND_BUTTON_NUMBER "3" #define DEF_DND_PACKAGE_COMMAND (char *)NULL #define DEF_DND_SELF_TARGET "no" #define DEF_DND_SEND (char *)NULL #define DEF_DND_IS_TARGET "no" #define DEF_DND_IS_SOURCE "no" #define DEF_DND_SITE_COMMAND (char *)NULL #define DEF_TOKEN_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG #define DEF_TOKEN_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG #define DEF_TOKEN_ACTIVE_BORDERWIDTH "3" #define DEF_TOKEN_ACTIVE_RELIEF "sunken" #define DEF_TOKEN_ANCHOR "se" #define DEF_TOKEN_BG_COLOR STD_COLOR_NORMAL_BG #define DEF_TOKEN_BG_MONO STD_MONO_NORMAL_BG #define DEF_TOKEN_BORDERWIDTH "3" #define DEF_TOKEN_CURSOR "top_left_arrow" #define DEF_TOKEN_REJECT_BG_COLOR STD_COLOR_NORMAL_BG #define DEF_TOKEN_REJECT_BG_MONO RGB_WHITE #define DEF_TOKEN_REJECT_FG_COLOR RGB_RED #define DEF_TOKEN_REJECT_FG_MONO RGB_BLACK #define DEF_TOKEN_REJECT_STIPPLE_COLOR (char *)NULL #define DEF_TOKEN_REJECT_STIPPLE_MONO RGB_GREY50 #define DEF_TOKEN_RELIEF "raised" static int StringToCursors _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *CursorsToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)); Tk_CustomOption cursorsOption = { StringToCursors, CursorsToString, (ClientData)0 }; typedef struct ThreadData { Tcl_HashTable dndTable; /* Hash table of dnd structures keyed by * the address of the reference Tk window */ Tk_Window mainWindow; Display *display; Atom mesgAtom; /* Atom signifying a drag-and-drop message. */ Atom formatsAtom; /* Source formats property atom. */ Atom targetAtom; /* Target property atom. */ Atom commAtom; /* Communication property atom. */ #ifdef HAVE_XDND Tcl_HashTable handlerTable; /* Table of toplevel windows with XdndAware * properties attached to them. */ Atom XdndActionListAtom; Atom XdndAwareAtom; Atom XdndEnterAtom; Atom XdndFinishedAtom; Atom XdndLeaveAtom; Atom XdndPositionAtom; Atom XdndSelectionAtom; Atom XdndStatusAtom; Atom XdndTypeListAtom; Atom XdndActionCopyAtom; Atom XdndActionMoveAtom; Atom XdndActionLinkAtom; Atom XdndActionAskAtom; Atom XdndActionPrivateAtom; Atom XdndActionDescriptionAtom; #endif } ThreadData; typedef struct DropPending { Tcl_DString dString; Window window; /* Source/Target window */ Display *display; Atom commAtom; /* Data communication property atom. */ int packetSize; Tcl_TimerToken timerToken; int status; /* Status of transaction: CONTINUE, OK, FAIL, * or TIMEOUT. */ int timestamp; /* Timestamp of the transaction. */ int offset; int protocol; /* Drag-and-drop protocol used by the source: * either PROTO_BLT or PROTO_XDND. */ } DropPending; /* * SubstDescriptors -- * * Structure to hold letter-value pairs for percent substitutions. */ typedef struct SubstDescriptors { char letter; /* character like 'x' in "%x" */ char *value; /* value to be substituted in place of "%x" */ } SubstDescriptors; /* * Drag&Drop Registration Data */ typedef struct Token { Tk_Window tkwin; /* Window that embodies the token. NULL * means that the window has been destroyed * but the data structures haven't yet been * cleaned up. */ Display *display; /* Display containing widget. Used, among * other things, so that resources can be * freed even after tkwin has gone away. */ Tcl_Interp *interp; /* Interpreter associated with widget. Used * to delete widget command. */ Tk_3DBorder border; /* Structure used to draw 3-D border and * background. NULL means no background * or border. */ int borderWidth; /* Width of 3-D border (if any). */ int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */ int flags; /* Various flags; see below for * definitions. */ /* Token specific fields */ int x, y; /* Last position of token window */ int selectX, selectY; /* Starting position of token window */ int status; /* Indicates the current status of the token: * 0 is normal, 1 is active. */ int lastStatus; /* Indicates the last status of the token. */ Tk_TimerToken timer; /* token for routine to hide tokenwin */ GC fillGC; /* GC used to draw rejection fg: (\) */ GC outlineGC; /* GC used to draw rejection bg: (\) */ int width, height; int reqWidth, reqHeight; /* User-configurable fields */ Tk_Anchor anchor; /* Position of token win relative to mouse */ Tk_3DBorder normalBorder; /* Border/background for token window */ Tk_3DBorder activeBorder; /* Border/background for token window */ int activeRelief; int activeBorderWidth; /* Border width in pixels */ XColor *fillColor; /* Color used to draw rejection fg: (\) */ XColor *outlineColor; /* Color used to draw rejection bg: (\) */ Pixmap rejectStipple; /* Stipple used to draw rejection: (\) */ int nSteps; } Token; /* * Winfo -- * * This structure represents a window hierarchy examined during a single * "drag" operation. It's used to cache information to reduce the round * trip calls to the server needed to query window geometry information * and grab the target property. */ typedef struct Winfo { Window window; /* Window in hierarchy. */ int initialized; /* If zero, the rest of this structure's * information hasn't been set. */ int x1, y1, x2, y2; /* Extents of the window (upper-left and * lower-right corners). */ struct Winfo *parentPtr; /* Parent node. NULL if root. Used to * compute offset for X11 windows. */ Blt_Chain *chainPtr; /* List of this window's children. If NULL, * there are no children. */ int isTarget; /* Indicates if this window is a drag&drop * target. */ int lookedForProperty; /* Indicates if this window */ int eventFlags; /* Retrieved from the target's drag&drop * property, indicates what kinds of pointer * events should be relayed to the target via * ClientMessages. Possible values are OR-ed * combinations of the following bits: * 001 Enter events. * 010 Motion events. * 100 Leave events. */ char *matches; } Winfo; /* * Dnd -- * * This structure represents the drag&drop manager. It is associated * with a widget as a drag&drop source, target, or both. It contains * both the source and target components, since a widget can be both * a drag source and a drop target. */ typedef struct { Tcl_Interp *interp; /* Interpreter associated with the drag&drop * manager. */ Tk_Window tkwin; /* Tk window representing the drag&drop * manager (can be source and/or target). */ Display *display; /* Display for drag&drop widget. Saved to free * resources after window has been destroyed. */ int isSource; /* Indicates if this drag&drop manager can act * as a drag source. */ int isTarget; /* Indicates if this drag&drop manager can act * as a drop target. */ int targetPropertyExists; /* Indicates is the drop target property has * been set. */ int timestamp; /* Id of the current drag&drop transaction. */ int x, y; /* Last known location of the mouse pointer. */ Tcl_HashEntry *hashPtr; ThreadData *dataPtr; /* Source component. */ Tcl_HashTable getDataTable; /* Table of data handlers (converters) * registered for this source. */ int reqButton; /* Button used to invoke drag operation. */ int button; /* Last button press detected. */ int keyState; /* Last key state. */ Tk_Cursor cursor; /* Cursor restored after dragging */ int selfTarget; /* Indicated if the source should drop onto * itself. */ char **reqFormats; /* List of requested data formats. The * list should be ordered with the more * desireable formats first. You can also * temporarily turn off a source by setting * the value to the empty string. */ Winfo *rootPtr; /* Cached window information: Gathered * and used during the "drag" operation * to see if the mouse pointer is over a * valid target. */ Winfo *windowPtr; /* Points to information about the last * target the pointer was over. If NULL, * the pointer was not over a valid target. */ char *packageCmd; /* Tcl command executed at start of the drag * operation to initialize token. */ int pkgCmdInProgress; /* Indicates if a token package command is * currently active. The user may invoke * "update" or "tkwait" commands from within * the package command script. This allows the * "drag" operation to preempt itself. */ char *resultCmd; /* Tcl command executed at the end of the * "drop" operation to indicate its status. */ char *siteCmd; /* Tcl command executed to update token * window. */ int canceled; /* Indicated if the drag operation was * canceled. */ Token *tokenPtr; /* Token used to provide special cursor. */ Tcl_TimerToken timerToken; Tk_Cursor *cursors; /* Array of drag-and-drop cursors. */ int cursorPos; /* Target component. */ Tcl_HashTable setDataTable; /* Table of data handlers (converters) * registered for this target. */ char *enterCmd; /* Tcl proc called when the mouse enters the * target. */ char *leaveCmd; /* Tcl proc called when the mouse leaves the * target. */ char *motionCmd; /* Tcl proc called when the mouse is moved * over the target. */ char *dropCmd; /* Tcl proc called when the mouse button * is released over the target. */ char *matchingFormats; int lastId; /* The last transaction id used. This is used * to cache the above formats string. */ DropPending *pendingPtr; /* Points to structure containing information * about a current drop in progress. If NULL, * no drop is in progress. */ } Dnd; typedef struct XDndHandler { Tk_Window tkwin; /* Toplevel window of the drop target. */ int refCount; /* # of targets referencing this structure. */ Dnd *dndPtr; /* Last drop target selected. Used the * implement Enter/Leave events for targets. * If NULL, indicates that no drop target was * previously selected. */ int lastRepsonse; /* Indicates what the last response was. */ Window window; /* Window id of the top-level window (ie. * the wrapper). */ char **formatArr; /* List of formats available from source. * Must be pruned down to matching list. */ ThreadData *dataPtr; int x, y; } XDndHandler; extern Tk_CustomOption bltListOption; static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_CUSTOM, "-allowformats", "allowFormats", "AllowFormats", DEF_DND_SEND, Tk_Offset(Dnd, reqFormats), TK_CONFIG_NULL_OK, &bltListOption}, {TK_CONFIG_INT, "-button", "buttonNumber", "ButtonNumber", DEF_DND_BUTTON_NUMBER, Tk_Offset(Dnd, reqButton), 0}, {TK_CONFIG_CUSTOM, "-cursors", "cursors", "cursors", DEF_TOKEN_CURSOR, Tk_Offset(Dnd, cursors), TK_CONFIG_NULL_OK, &cursorsOption }, {TK_CONFIG_STRING, "-onenter", "onEnter", "OnEnter", DEF_DND_PACKAGE_COMMAND, Tk_Offset(Dnd, enterCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-onmotion", "onMotion", "OnMotion", DEF_DND_PACKAGE_COMMAND, Tk_Offset(Dnd, motionCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-onleave", "onLeave", "OnLeave", DEF_DND_PACKAGE_COMMAND, Tk_Offset(Dnd, leaveCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-ondrop", "onDrop", "OnDrop", DEF_DND_PACKAGE_COMMAND, Tk_Offset(Dnd, dropCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-package", "packageCommand", "PackageCommand", DEF_DND_PACKAGE_COMMAND, Tk_Offset(Dnd, packageCmd), TK_CONFIG_NULL_OK }, {TK_CONFIG_STRING, "-result", "result", "Result", DEF_DND_PACKAGE_COMMAND, Tk_Offset(Dnd, resultCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-selftarget", "selfTarget", "SelfTarget", DEF_DND_SELF_TARGET, Tk_Offset(Dnd, selfTarget), 0}, {TK_CONFIG_STRING, "-site", "siteCommand", "Command", DEF_DND_SITE_COMMAND, Tk_Offset(Dnd, siteCmd), TK_CONFIG_NULL_OK}, {TK_CONFIG_BOOLEAN, "-source", "source", "Source", DEF_DND_IS_SOURCE, Tk_Offset(Dnd, isSource), 0}, {TK_CONFIG_BOOLEAN, "-target", "target", "Target", DEF_DND_IS_TARGET, Tk_Offset(Dnd, isTarget), 0}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0}, }; static Tk_ConfigSpec tokenConfigSpecs[] = { {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR, Tk_Offset(Token, activeBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO, Tk_Offset(Token, activeBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "activeRelief", DEF_TOKEN_ACTIVE_RELIEF, Tk_Offset(Token, activeRelief), 0}, {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", DEF_TOKEN_ANCHOR, Tk_Offset(Token, anchor), 0}, {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth", "ActiveBorderWidth", DEF_TOKEN_ACTIVE_BORDERWIDTH, Tk_Offset(Token, activeBorderWidth), 0}, {TK_CONFIG_BORDER, "-background", "background", "Background", DEF_TOKEN_BG_COLOR, Tk_Offset(Token, normalBorder), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-background", "background", "Background", DEF_TOKEN_BG_MONO, Tk_Offset(Token, normalBorder), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_TOKEN_BORDERWIDTH, Tk_Offset(Token, borderWidth), 0}, {TK_CONFIG_COLOR, "-outline", "outline", "Outline", DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, outlineColor), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-outline", "outline", "Outline", DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Token, outlineColor), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-fill", "fill", "Fill", DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Token, fillColor), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-fill", "fill", "Fill", DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, fillColor), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple", DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Token, rejectStipple), TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK}, {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple", DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Token, rejectStipple), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_TOKEN_RELIEF, Tk_Offset(Token, relief), 0}, {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0}, }; /* * Forward Declarations */ static int DndCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc, char **argv)); static void TokenEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void MoveToken _ANSI_ARGS_((Dnd *dndPtr)); static void DisplayToken _ANSI_ARGS_((ClientData clientData)); static void HideToken _ANSI_ARGS_((Dnd *dndPtr)); static void DrawRejectSymbol _ANSI_ARGS_((Dnd *dndPtr)); static int GetDnd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, char *name, Dnd **dndPtrPtr)); static Dnd *CreateDnd _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin)); static void DestroyDnd _ANSI_ARGS_((Dnd *dndPtr)); static int DndEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static int ConfigureToken _ANSI_ARGS_((Tcl_Interp *interp, Dnd *dndPtr, int argc, char **argv, int flags)); static Winfo *OverTarget _ANSI_ARGS_((Dnd *dndPtr)); static void AddTargetProperty _ANSI_ARGS_((Tcl_Interp *interp, Dnd *dndPtr)); static Winfo *InitRoot _ANSI_ARGS_((Dnd *dndPtr)); static void FreeWinfo _ANSI_ARGS_((Winfo *wr)); static void GetWinfo _ANSI_ARGS_((Display *display, Winfo * windowPtr)); static void ExpandPercents _ANSI_ARGS_((char *str, SubstDescriptors * subs, int nsubs, Tcl_DString * dStrPtr)); /* * ---------------------------------------------------------------------------- * * StringToCursors -- * * Converts the resize mode into its numeric representation. Valid * mode strings are "none", "expand", "shrink", or "both". * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToCursors(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* String representing cursors. */ char *widgRec; /* Structure record */ int offset; /* Offset of field in record. */ { Tk_Cursor **cursorPtrPtr = (Tk_Cursor **)(widgRec + offset); int result = TCL_OK; int nElems; char **elemArr; if (*cursorPtrPtr != NULL) { free((char *)*cursorPtrPtr); *cursorPtrPtr = NULL; } if (string == NULL) { return TCL_OK; } if (Tcl_SplitList(interp, string, &nElems, &elemArr) != TCL_OK) { return TCL_ERROR; } if (nElems > 0) { Tk_Cursor *cursorArr; register int i; cursorArr = (Tk_Cursor *)calloc(nElems + 1, sizeof(Tk_Cursor)); for (i = 0; i < nElems; i++) { cursorArr[i] = Tk_GetCursor(interp, tkwin, Tk_GetUid(elemArr[i])); if (cursorArr[i] == None) { *cursorPtrPtr = cursorArr; result = TCL_ERROR; break; } } free((char *)elemArr); *cursorPtrPtr = cursorArr; } return result; } /* * ---------------------------------------------------------------------------- * * CursorsToString -- * * Returns resize mode string based upon the resize flags. * * Results: * The resize mode string is returned. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static char * CursorsToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Cursor record */ int offset; /* Offset of record. */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation scheme to use */ { Tk_Cursor *cursorArr = *(Tk_Cursor **)(widgRec + offset); Tk_Cursor *cursorPtr; Tcl_DString dString; char *result; if (cursorArr == NULL) { return ""; } Tcl_DStringInit(&dString); for (cursorPtr = cursorArr; *cursorPtr != NULL; cursorPtr++) { Tcl_DStringAppendElement(&dString, Tk_NameOfCursor(Tk_Display(tkwin), *cursorPtr)); } result = strdup(Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); *freeProcPtr = (Tcl_FreeProc *)free; return result; } /* ARGSUSED */ static int XSendEventErrorProc(clientData, errEventPtr) ClientData clientData; XErrorEvent *errEventPtr; { int *errorPtr = (int *)clientData; *errorPtr = TCL_ERROR; return 0; } static void SendClientMsg(display, window, mesgAtom, data0, data1, data2, data3, data4) Display *display; Window window; Atom mesgAtom; int data0, data1, data2, data3, data4; { XEvent event; Tk_ErrorHandler handler; int result; int any = -1; event.xclient.type = ClientMessage; event.xclient.serial = 0; event.xclient.send_event = True; event.xclient.display = display; event.xclient.window = window; event.xclient.message_type = mesgAtom; event.xclient.format = 32; event.xclient.data.l[0] = data0; event.xclient.data.l[1] = data1; event.xclient.data.l[2] = data2; event.xclient.data.l[3] = data3; event.xclient.data.l[4] = data4; result = TCL_OK; handler = Tk_CreateErrorHandler(display, any, X_SendEvent, any, XSendEventErrorProc, (ClientData)&result); if (!XSendEvent(display, window, False, ClientMessage, &event)) { result = TCL_ERROR; } Tk_DeleteErrorHandler(handler); XSync(display, False); if (result != TCL_OK) { fprintf(stderr, "XSendEvent response to drop: Protocol failed\n"); } } /* * ------------------------------------------------------------------------ * * GetWindowZOrder -- * * Returns a chain of the child windows according to their stacking * order. The window ids are ordered from top to bottom. * * ------------------------------------------------------------------------ */ static Blt_Chain * GetWindowZOrder(display, window) Display *display; Window window; { Blt_Chain *chainPtr; Window *childArr; unsigned int nChildren; Window dummy; chainPtr = NULL; if ((XQueryTree(display, window, &dummy, &dummy, &childArr, &nChildren)) && (nChildren > 0)) { register int i; chainPtr = Blt_ChainCreate(); for (i = 0; i < nChildren; i++) { /* * XQuery returns windows in bottom to top order. We only care * about the top window. */ Blt_ChainPrepend(chainPtr, (ClientData)childArr[i]); } if (childArr != NULL) { XFree((char *)childArr); /* done with list of kids */ } } return chainPtr; } static int GetMaxPropertySize(display) Display *display; { int size; #if (XT_REVISION < 6) size = XMaxRequestSize(display); #else size = MAX(XExtendedMaxRequestSize(display), XMaxRequestSize(display)); #endif size *= 4; /* Convert to bytes. */ size -= 32; return size; } /* * ------------------------------------------------------------------------ * * GetProperty -- * * Returns the data associated with the named property on the * given window. All data is assumed to be 8-bit string data. * * ------------------------------------------------------------------------ */ static char * GetProperty(display, window, atom) Display *display; Window window; Atom atom; { char *data; int result, format; Atom typeAtom; unsigned long nItems, bytesAfter; if (window == None) { return NULL; } data = NULL; result = XGetWindowProperty( display, /* Display of window. */ window, /* Window holding the property. */ atom, /* Name of property. */ 0, /* Offset of data (for multiple reads). */ GetMaxPropertySize(display), /* Maximum number of items to read. */ False, /* If true, delete the property. */ XA_STRING, /* Desired type of property. */ &typeAtom, /* (out) Actual type of the property. */ &format, /* (out) Actual format of the property. */ &nItems, /* (out) # of items in specified format. */ &bytesAfter, /* (out) # of bytes remaining to be read. */ (unsigned char **)&data); if ((result != Success) || (format != 8) || (typeAtom != XA_STRING)) { if (data != NULL) { XFree((char *)data); data = NULL; } } return data; } /* * ------------------------------------------------------------------------ * * SetProperty -- * * Associates the given data with the a property on a given window. * All data is assumed to be 8-bit string data. * * ------------------------------------------------------------------------ */ static void SetProperty(tkwin, atom, data) Tk_Window tkwin; Atom atom; char *data; { XChangeProperty(Tk_Display(tkwin), Tk_WindowId(tkwin), atom, XA_STRING, 8, PropModeReplace, (unsigned char *)data, strlen(data) + 1); } /* * ------------------------------------------------------------------------ * * GetWindowRegion -- * * Queries for the upper-left and lower-right corners of the * given window. * * Results: * Returns if the window is currently viewable. The coordinates * of the window are returned via parameters. * * ------------------------------------------------------------------------ */ static int GetWindowRegion(display, window, x1Ptr, y1Ptr, x2Ptr, y2Ptr) Display *display; Window window; int *x1Ptr, *y1Ptr, *x2Ptr, *y2Ptr; { XWindowAttributes winAttrs; if (XGetWindowAttributes(display, window, &winAttrs)) { *x1Ptr = winAttrs.x; *y1Ptr = winAttrs.y; *x2Ptr = winAttrs.x + winAttrs.width - 1; *y2Ptr = winAttrs.y + winAttrs.height - 1; } return (winAttrs.map_state == IsViewable); } /* * ------------------------------------------------------------------------ * * FindTopWindow -- * * Searches for the topmost window at a given pair of X-Y coordinates. * * Results: * Returns a pointer to the node representing the window containing * the point. If one can't be found, NULL is returned. * * ------------------------------------------------------------------------ */ static Winfo * FindTopWindow(dndPtr, x, y) Dnd *dndPtr; int x, y; { Winfo *rootPtr; register Blt_ChainLink *linkPtr; register Winfo *windowPtr; rootPtr = dndPtr->rootPtr; if (!rootPtr->initialized) { GetWinfo(dndPtr->display, rootPtr); } if ((x < rootPtr->x1) || (x > rootPtr->x2) || (y < rootPtr->y1) || (y > rootPtr->y2)) { return NULL; /* Point is not over window */ } windowPtr = rootPtr; /* * The window list is ordered top to bottom, so stop when we find the * first child that contains the X-Y coordinate. It will be the topmost * window in that hierarchy. If none exists, then we already have the * topmost window. */ descend: for (linkPtr = Blt_ChainFirstLink(rootPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rootPtr = (Winfo *) Blt_ChainGetValue(linkPtr); if (!rootPtr->initialized) { GetWinfo(dndPtr->display, rootPtr); } if (rootPtr->window == Blt_GetRealWindowId(dndPtr->tokenPtr->tkwin)) { continue; /* Don't examine the token window. */ } if ((x >= rootPtr->x1) && (x <= rootPtr->x2) && (y >= rootPtr->y1) && (y <= rootPtr->y2)) { /* * Remember the last window containing the pointer and descend * into its window hierarchy. We'll look for a child that also * contains the pointer. */ windowPtr = rootPtr; goto descend; } } return windowPtr; } /* * ------------------------------------------------------------------------ * * GetWidgetCursor -- * * Queries a widget for its current cursor. The given window * may or may not be a Tk widget that has a -cursor option. * * Results: * Returns the current cursor of the widget. * * ------------------------------------------------------------------------ */ static Tk_Cursor GetWidgetCursor(interp, tkwin) Tcl_Interp *interp; /* Interpreter to evaluate widget command. */ Tk_Window tkwin; /* Window of drag&drop source. */ { Tk_Cursor cursor; Tcl_DString dString, savedResult; cursor = None; Tcl_DStringInit(&dString); Blt_DStringAppendElements(&dString, Tk_PathName(tkwin), "cget", "-cursor", (char *)NULL); Tcl_DStringInit(&savedResult); Tcl_DStringGetResult(interp, &savedResult); if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)) == TCL_OK) { char *name; name = Tcl_GetStringResult(interp); if ((name != NULL) && (name[0] != '\0')) { cursor = Tk_GetCursor(interp, tkwin, Tk_GetUid(name)); } } Tcl_DStringResult(interp, &savedResult); Tcl_DStringFree(&dString); return cursor; } /* * ------------------------------------------------------------------------ * * NameOfAction -- * * Converts a numeric drop result into its string representation. * * Results: * Returns a static string representing the drop result. * * ------------------------------------------------------------------------ */ static char * NameOfStatus(status) int status; { switch (status) { case TOKEN_STATUS_ACTIVE: return "active"; case TOKEN_STATUS_NORMAL: return "normal"; break; case TOKEN_STATUS_REJECT: return "reject"; default: return "unknown status value"; } } /* * ------------------------------------------------------------------------ * * NameOfAction -- * * Converts a numeric drop result into its string representation. * * Results: * Returns a static string representing the drop result. * * ------------------------------------------------------------------------ */ static char * NameOfAction(action) int action; { switch (action) { case ACTION_CANCEL: return "cancel"; case ACTION_MOVE: return "move"; break; case ACTION_LINK: return "link"; case ACTION_COPY: return "copy"; default: return "unknown action"; } } /* * ------------------------------------------------------------------------ * * GetAction -- * * Converts a string to its numeric drop result value. * * Results: * Returns the drop result. * * ------------------------------------------------------------------------ */ static int GetAction(string) char *string; { char c; c = string[0]; if ((c == 'c') && (strcmp(string, "cancel") == 0)) { return ACTION_CANCEL; } else if ((c == 'm') && (strcmp(string, "move") == 0)) { return ACTION_MOVE; } else if ((c == 'l') && (strcmp(string, "link") == 0)) { return ACTION_LINK; } else if ((c == 'c') && (strcmp(string, "copy") == 0)) { return ACTION_COPY; } else { return ACTION_COPY; } } /* * ------------------------------------------------------------------------ * * GetDragResult -- * * Converts a string to its numeric drag result value. * * Results: * Returns the drag result. * * ------------------------------------------------------------------------ */ static int GetDragResult(interp, string) Tcl_Interp *interp; char *string; { char c; int bool; c = string[0]; if ((c == 'c') && (strcmp(string, "cancel") == 0)) { return DRAG_CANCEL; } else if (Tcl_GetBoolean(interp, string, &bool) != TCL_OK) { Tcl_BackgroundError(interp); return DRAG_CANCEL; } return bool; } static void AnimateActiveCursor(clientData) ClientData clientData; { Dnd *dndPtr = (Dnd *)clientData; Tk_Cursor cursor; dndPtr->cursorPos++; cursor = dndPtr->cursors[dndPtr->cursorPos]; if (cursor == None) { cursor = dndPtr->cursors[1]; dndPtr->cursorPos = 1; } Tk_DefineCursor(dndPtr->tkwin, cursor); dndPtr->timerToken = Tk_CreateTimerHandler(100, AnimateActiveCursor, (ClientData)dndPtr); } static void StartActiveCursor(dndPtr) Dnd *dndPtr; { if (dndPtr->timerToken != NULL) { Tk_DeleteTimerHandler(dndPtr->timerToken); } if (dndPtr->cursors != NULL) { Tk_Cursor cursor; dndPtr->cursorPos = 1; cursor = dndPtr->cursors[1]; if (cursor != None) { Tk_DefineCursor(dndPtr->tkwin, cursor); dndPtr->timerToken = Tk_CreateTimerHandler(125, AnimateActiveCursor, (ClientData)dndPtr); } } } static void StopActiveCursor(dndPtr) Dnd *dndPtr; { if (dndPtr->cursorPos > 0) { dndPtr->cursorPos = 0; } if (dndPtr->cursors != NULL) { Tk_DefineCursor(dndPtr->tkwin, dndPtr->cursors[0]); } if (dndPtr->timerToken != NULL) { Tk_DeleteTimerHandler(dndPtr->timerToken); dndPtr->timerToken = NULL; } } /* *---------------------------------------------------------------------- * * EventuallyRedrawToken -- * * Queues a request to redraw the widget at the next idle point. * * Results: * None. * * Side effects: * Information gets redisplayed. Right now we don't do selective * redisplays: the whole window will be redrawn. * *---------------------------------------------------------------------- */ static void EventuallyRedrawToken(dndPtr) Dnd *dndPtr; { Token *tokenPtr; if (dndPtr->tokenPtr == NULL) { return; } tokenPtr = dndPtr->tokenPtr; if ((tokenPtr->tkwin != NULL) && (tokenPtr->tkwin != NULL) && !(tokenPtr->flags & TOKEN_REDRAW)) { tokenPtr->flags |= TOKEN_REDRAW; Tk_DoWhenIdle(DisplayToken, (ClientData)dndPtr); } } /* * ------------------------------------------------------------------------ * * RaiseToken -- * * ------------------------------------------------------------------------ */ static void RaiseToken(tokenPtr) Token *tokenPtr; { Blt_MapTopLevelWindow(tokenPtr->tkwin); Blt_RaiseTopLevelWindow(tokenPtr->tkwin); } /* * ------------------------------------------------------------------------ * * DisplayToken -- * * ------------------------------------------------------------------------ */ static void DisplayToken(clientData) ClientData clientData; { Dnd *dndPtr = (Dnd *)clientData; Token *tokenPtr = dndPtr->tokenPtr; int relief; Tk_3DBorder border; int borderWidth; tokenPtr->flags &= ~TOKEN_REDRAW; if (tokenPtr->reqWidth == 0) { tokenPtr->reqWidth = Tk_ReqWidth(tokenPtr->tkwin); } if (tokenPtr->reqHeight == 0) { tokenPtr->reqHeight = Tk_ReqHeight(tokenPtr->tkwin); } if (tokenPtr->status == TOKEN_STATUS_NORMAL) { relief = tokenPtr->relief; border = tokenPtr->normalBorder; borderWidth = tokenPtr->borderWidth; StopActiveCursor(dndPtr); } else { relief = tokenPtr->activeRelief; border = tokenPtr->activeBorder; borderWidth = tokenPtr->activeBorderWidth; if ((dndPtr->cursors != NULL) && (dndPtr->cursorPos == 0)) { StartActiveCursor(dndPtr); } } Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin), border, 0, 0, Tk_Width(tokenPtr->tkwin), Tk_Height(tokenPtr->tkwin), borderWidth, relief); tokenPtr->lastStatus = tokenPtr->status; if (tokenPtr->status == TOKEN_STATUS_REJECT) { DrawRejectSymbol(dndPtr); } } /* * ------------------------------------------------------------------------ * * FadeToken -- * * Fades the token into the target. * * ------------------------------------------------------------------------ */ static void FadeToken(dndPtr) Dnd *dndPtr; /* drag&drop source window data */ { Token *tokenPtr = dndPtr->tokenPtr; int w, h; int dx, dy; Window window; if (tokenPtr->status == TOKEN_STATUS_REJECT) { tokenPtr->nSteps = 1; return; } if (tokenPtr->nSteps == 1) { HideToken(dndPtr); return; } tokenPtr->timer = Tk_CreateTimerHandler(10, (Tcl_TimerProc *)FadeToken, (ClientData)dndPtr); tokenPtr->nSteps--; w = tokenPtr->reqWidth * tokenPtr->nSteps / 10; h = tokenPtr->reqHeight * tokenPtr->nSteps / 10; if (w < 1) { w = 1; } if (h < 1) { h = 1; } dx = (tokenPtr->reqWidth - w) / 2; dy = (tokenPtr->reqHeight - h) / 2; window = Blt_GetRealWindowId(tokenPtr->tkwin); XMoveResizeWindow(dndPtr->display, window, tokenPtr->x + dx, tokenPtr->y + dy, (unsigned int)w, (unsigned int)h); tokenPtr->width = w, tokenPtr->height = h; } /* * ------------------------------------------------------------------------ * * SnapToken -- * * Snaps the token back to the source. * * ------------------------------------------------------------------------ */ static void SnapToken(dndPtr) Dnd *dndPtr; /* drag&drop source window data */ { Token *tokenPtr = dndPtr->tokenPtr; if (tokenPtr->nSteps == 1) { HideToken(dndPtr); return; } tokenPtr->timer = Tk_CreateTimerHandler(10, (Tcl_TimerProc *)SnapToken, (ClientData)dndPtr); tokenPtr->nSteps--; tokenPtr->x -= (tokenPtr->x - tokenPtr->selectX) / tokenPtr->nSteps; tokenPtr->y -= (tokenPtr->y - tokenPtr->selectY) / tokenPtr->nSteps; if ((tokenPtr->x != Tk_X(tokenPtr->tkwin)) || (tokenPtr->y != Tk_Y(tokenPtr->tkwin))) { Tk_MoveToplevelWindow(tokenPtr->tkwin, tokenPtr->x, tokenPtr->y); } RaiseToken(tokenPtr); } /* * ------------------------------------------------------------------------ * * HideToken -- * * Unmaps the drag&drop token. Invoked directly at the end of a * successful communication, or after a delay if the communication * fails (allowing the user to see a graphical picture of failure). * * ------------------------------------------------------------------------ */ static void HideToken(dndPtr) Dnd *dndPtr; { Token *tokenPtr = dndPtr->tokenPtr; /* Reset the cursor back to its normal state. */ StopActiveCursor(dndPtr); if (dndPtr->cursor == None) { Tk_UndefineCursor(dndPtr->tkwin); } else { Tk_DefineCursor(dndPtr->tkwin, dndPtr->cursor); } if (tokenPtr->tkwin != NULL) { Tk_UnmapWindow(tokenPtr->tkwin); Blt_ResizeTopLevelWindow(tokenPtr->tkwin, tokenPtr->reqWidth, tokenPtr->reqHeight); } tokenPtr->timer = NULL; tokenPtr->status = TOKEN_STATUS_NORMAL; } /* * ------------------------------------------------------------------------ * * MoveToken -- * * Invoked during "drag" operations to move a token window to its * current "drag" coordinate. * * ------------------------------------------------------------------------ */ static void MoveToken(dndPtr) Dnd *dndPtr; /* drag&drop source window data */ { Token *tokenPtr = dndPtr->tokenPtr; int x, y; int maxX, maxY; int vx, vy, vw, vh; Screen *screenPtr; int anchor; /* Adjust current location for virtual root windows. */ Tk_GetVRootGeometry(dndPtr->tkwin, &vx, &vy, &vw, &vh); x = dndPtr->x + vx - TOKEN_OFFSET; y = dndPtr->y + vy - TOKEN_OFFSET; screenPtr = Tk_Screen(dndPtr->tkwin); maxX = WidthOfScreen(screenPtr) - Tk_Width(tokenPtr->tkwin); maxY = HeightOfScreen(screenPtr) - Tk_Height(tokenPtr->tkwin); anchor = (tokenPtr->status == TOKEN_STATUS_ACTIVE) ? TK_ANCHOR_NE : TK_ANCHOR_SE; Blt_TranslateAnchor(x, y, Tk_Width(tokenPtr->tkwin), Tk_Height(tokenPtr->tkwin), tokenPtr->anchor, &x, &y); if (x > maxX) { x = maxX; } else if (x < 0) { x = 0; } if (y > maxY) { y = maxY; } else if (y < 0) { y = 0; } tokenPtr->x = x, tokenPtr->y = y; if ((x != Tk_X(tokenPtr->tkwin)) || (y != Tk_Y(tokenPtr->tkwin))) { Tk_MoveToplevelWindow(tokenPtr->tkwin, x, y); } RaiseToken(tokenPtr); } /* * ------------------------------------------------------------------------ * * ChangeToken -- * * Invoked when the event loop is idle to determine whether or not * the current drag&drop token position is over another drag&drop * target. * * ------------------------------------------------------------------------ */ static void ChangeToken(dndPtr) Dnd *dndPtr; { Token *tokenPtr = dndPtr->tokenPtr; EventuallyRedrawToken(dndPtr); /* * If the source has a site command, then invoke it to * modify the appearance of the token window. Pass any * errors onto the drag&drop error handler. */ if (dndPtr->siteCmd) { Tcl_Interp *interp = dndPtr->interp; Tcl_DString dString, savedResult; int result; SubstDescriptors subs[3]; subs[0].letter = 's'; subs[0].value = NameOfStatus(tokenPtr->status); subs[1].letter = 'W'; subs[1].value = Tk_PathName(tokenPtr->tkwin); subs[2].letter = 't'; subs[2].value = Blt_Itoa(dndPtr->timestamp); ExpandPercents(dndPtr->siteCmd, subs, 3, &dString); Tcl_DStringInit(&savedResult); Tcl_DStringGetResult(interp, &savedResult); result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result != TCL_OK) { Tcl_BackgroundError(interp); } Tcl_DStringResult(interp, &savedResult); } } /* * ------------------------------------------------------------------------ * * DrawRejectSymbol -- * * Draws a rejection mark on the current drag&drop token, and arranges * for the token to be unmapped after a small delay. * * ------------------------------------------------------------------------ */ static void DrawRejectSymbol(dndPtr) Dnd *dndPtr; { Token *tokenPtr = dndPtr->tokenPtr; int divisor = 6; /* controls size of rejection symbol */ int w, h, lineWidth, x, y, margin; margin = 2 * tokenPtr->borderWidth; w = Tk_Width(tokenPtr->tkwin) - 2 * margin; h = Tk_Height(tokenPtr->tkwin) - 2 * margin; lineWidth = (w < h) ? w / divisor : h / divisor; lineWidth = (lineWidth < 1) ? 1 : lineWidth; w = h = lineWidth * (divisor - 1); x = (Tk_Width(tokenPtr->tkwin) - w) / 2; y = (Tk_Height(tokenPtr->tkwin) - h) / 2; /* * Draw the rejection symbol background (\) on the token window... */ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->outlineGC, lineWidth + 2, LineSolid, CapButt, JoinBevel); XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->outlineGC, x, y, w, h, 0, 23040); XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->outlineGC, x + lineWidth, y + lineWidth, x + w - lineWidth, y + h - lineWidth); /* * Draw the rejection symbol foreground (\) on the token window... */ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->fillGC, lineWidth, LineSolid, CapButt, JoinBevel); XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->fillGC, x, y, w, h, 0, 23040); XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin), tokenPtr->fillGC, x + lineWidth, y + lineWidth, x + w - lineWidth, y + h - lineWidth); tokenPtr->status = TOKEN_STATUS_REJECT; /* * Arrange for token window to disappear eventually. */ tokenPtr->timer = Tk_CreateTimerHandler(1000, (Tcl_TimerProc *)HideToken, (ClientData)dndPtr); RaiseToken(tokenPtr); } /* * ------------------------------------------------------------------------ * * CreateToken -- * * Looks for a Source record in the hash table for drag&drop source * widgets. Creates a new record if the widget name is not already * registered. Returns a pointer to the desired record. * * ------------------------------------------------------------------------ */ static void DestroyToken(destroyData) DestroyData destroyData; { Dnd *dndPtr = (Dnd *)destroyData; Token *tokenPtr = dndPtr->tokenPtr; if (tokenPtr != NULL) { if (tokenPtr->flags & TOKEN_REDRAW) { Tk_CancelIdleCall(DisplayToken, (ClientData)dndPtr); } Tk_FreeOptions(tokenConfigSpecs, (char *)tokenPtr, dndPtr->display, 0); if (tokenPtr->timer) { Tk_DeleteTimerHandler(tokenPtr->timer); } if (tokenPtr->fillGC != NULL) { Tk_FreeGC(dndPtr->display, tokenPtr->fillGC); } if (tokenPtr->outlineGC != NULL) { Tk_FreeGC(dndPtr->display, tokenPtr->outlineGC); } if (tokenPtr->tkwin != NULL) { Tk_DeleteEventHandler(tokenPtr->tkwin, ExposureMask | StructureNotifyMask, TokenEventProc, (ClientData)dndPtr); Tk_DestroyWindow(tokenPtr->tkwin); } free((char *)tokenPtr); } dndPtr->tokenPtr = NULL; } /* * ------------------------------------------------------------------------ * * TokenEventProc -- * * Invoked by the Tk dispatcher to handle widget events. * Manages redraws for the drag&drop token window. * * ------------------------------------------------------------------------ */ static void TokenEventProc(clientData, eventPtr) ClientData clientData; /* data associated with widget */ XEvent *eventPtr; /* information about event */ { Dnd *dndPtr = (Dnd *)clientData; Token *tokenPtr = dndPtr->tokenPtr; if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { if (tokenPtr->tkwin != NULL) { EventuallyRedrawToken(dndPtr); } } else if (eventPtr->type == DestroyNotify) { tokenPtr->tkwin = NULL; if (tokenPtr->flags & TOKEN_REDRAW) { tokenPtr->flags &= ~TOKEN_REDRAW; Tk_CancelIdleCall(DisplayToken, (ClientData)dndPtr); } Tcl_EventuallyFree((ClientData)dndPtr, DestroyToken); } } /* * ------------------------------------------------------------------------ * * CreateToken -- * * Looks for a Source record in the hash table for drag&drop source * widgets. Creates a new record if the widget name is not already * registered. Returns a pointer to the desired record. * * ------------------------------------------------------------------------ */ static int CreateToken(interp, dndPtr) Tcl_Interp *interp; Dnd *dndPtr; { XSetWindowAttributes attrs; Tk_Window tkwin; unsigned int mask; Token *tokenPtr; tokenPtr = (Token *)calloc(1, sizeof(Token)); assert(tokenPtr); tokenPtr->anchor = TK_ANCHOR_SE; tokenPtr->relief = TK_RELIEF_RAISED; tokenPtr->activeRelief = TK_RELIEF_SUNKEN; tokenPtr->borderWidth = tokenPtr->activeBorderWidth = 3; /* Create toplevel on parent's screen. */ tkwin = Tk_CreateWindow(interp, dndPtr->tkwin, "dndtoken", ""); if (tkwin == NULL) { free((char *)tokenPtr); return TCL_ERROR; } tokenPtr->tkwin = tkwin; Tk_SetClass(tkwin, "DndToken"); Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask, TokenEventProc, (ClientData)dndPtr); attrs.override_redirect = True; attrs.backing_store = WhenMapped; attrs.save_under = True; mask = CWOverrideRedirect | CWSaveUnder | CWBackingStore; Tk_ChangeWindowAttributes(tkwin, mask, &attrs); Tk_SetInternalBorder(tkwin, tokenPtr->borderWidth + 2); Tk_MakeWindowExist(tkwin); dndPtr->tokenPtr = tokenPtr; return TCL_OK; } /* * ------------------------------------------------------------------------ * * ConfigureToken -- * * Called to process an (argc,argv) list to configure (or * reconfigure) a drag&drop source widget. * * ------------------------------------------------------------------------ */ static int ConfigureToken(interp, dndPtr, argc, argv, flags) Tcl_Interp *interp; /* current interpreter */ Dnd *dndPtr; /* Drag&drop source widget record */ int argc; /* number of arguments */ char **argv; /* argument strings */ int flags; /* flags controlling interpretation */ { unsigned long gcMask; XGCValues gcValues; GC newGC; Token *tokenPtr = dndPtr->tokenPtr; Tk_MakeWindowExist(tokenPtr->tkwin); if (Tk_ConfigureWidget(interp, tokenPtr->tkwin, tokenConfigSpecs, argc, argv, (char *)tokenPtr, flags) != TCL_OK) { return TCL_ERROR; } /* * Set up the rejection outline GC for the token window... */ gcValues.foreground = tokenPtr->outlineColor->pixel; gcValues.subwindow_mode = IncludeInferiors; gcValues.graphics_exposures = False; gcValues.line_style = LineSolid; gcValues.cap_style = CapButt; gcValues.join_style = JoinBevel; gcMask = GCForeground | GCSubwindowMode | GCLineStyle | GCCapStyle | GCJoinStyle | GCGraphicsExposures; newGC = Tk_GetGC(dndPtr->tkwin, gcMask, &gcValues); if (tokenPtr->outlineGC != NULL) { Tk_FreeGC(dndPtr->display, tokenPtr->outlineGC); } tokenPtr->outlineGC = newGC; /* * Set up the rejection fill GC for the token window... */ gcValues.foreground = tokenPtr->fillColor->pixel; if (tokenPtr->rejectStipple != None) { gcValues.stipple = tokenPtr->rejectStipple; gcValues.fill_style = FillStippled; gcMask |= GCStipple | GCFillStyle; } newGC = Tk_GetGC(dndPtr->tkwin, gcMask, &gcValues); if (tokenPtr->fillGC != NULL) { Tk_FreeGC(dndPtr->display, tokenPtr->fillGC); } tokenPtr->fillGC = newGC; /* * Reset the border width in case it has changed... */ Tk_SetInternalBorder(tokenPtr->tkwin, tokenPtr->borderWidth + 2); return TCL_OK; } static int GetFormattedData(dndPtr, format, timestamp, dStrPtr) Dnd *dndPtr; char *format; int timestamp; Tcl_DString *dStrPtr; { Tcl_Interp *interp = dndPtr->interp; Tcl_HashEntry *hPtr; char *formatCmd; SubstDescriptors subs[3]; Tcl_DString savedResult; Tcl_DString dString; int result; /* Find the data converter for the prescribed format. */ hPtr = Tcl_FindHashEntry(&(dndPtr->getDataTable), format); if (hPtr == NULL) { Tcl_AppendResult(interp, "can't find format \"", format, "\" in source \"", Tk_PathName(dndPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } formatCmd = (char *)Tcl_GetHashValue(hPtr); subs[0].letter = 't'; subs[0].value = Blt_Itoa(timestamp); subs[1].letter = 'W'; subs[1].value = Tk_PathName(dndPtr->tkwin); ExpandPercents(formatCmd, subs, 2, &dString); Tcl_DStringInit(&savedResult); /* Save the interpreter result. */ Tcl_DStringGetResult(interp, &savedResult); /* Invoke the converter, the interpreter result is the formatted data. */ result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result != TCL_OK) { Tcl_BackgroundError(interp); return TCL_ERROR; } Tcl_DStringInit(dStrPtr); Tcl_DStringGetResult(interp, dStrPtr); /* Restore the interpreter result. */ Tcl_DStringResult(interp, &savedResult); return TCL_OK; } /* * ------------------------------------------------------------------------ * * DestroyDnd -- * * Free resources allocated for the drag&drop window. * * ------------------------------------------------------------------------ */ static void DestroyDnd(dndPtr) Dnd *dndPtr; { Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; char *cmd; Tk_FreeOptions(configSpecs, (char *)dndPtr, dndPtr->display, 0); Tk_DeleteGenericHandler(DndEventProc, (ClientData)dndPtr); for (hPtr = Tcl_FirstHashEntry(&(dndPtr->getDataTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd != NULL) { free(cmd); } } Tcl_DeleteHashTable(&(dndPtr->getDataTable)); for (hPtr = Tcl_FirstHashEntry(&(dndPtr->setDataTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd != NULL) { free(cmd); } } Tcl_DeleteHashTable(&(dndPtr->setDataTable)); if (dndPtr->rootPtr != NULL) { FreeWinfo(dndPtr->rootPtr); } if (dndPtr->cursor != None) { Tk_FreeCursor(dndPtr->display, dndPtr->cursor); } if (dndPtr->reqFormats != NULL) { free((char *)dndPtr->reqFormats); } if (dndPtr->matchingFormats != NULL) { free((char *)dndPtr->matchingFormats); } if (dndPtr->hashPtr != NULL) { Tcl_DeleteHashEntry(dndPtr->hashPtr); } if (dndPtr->tokenPtr != NULL) { DestroyToken((DestroyData)dndPtr); } if (dndPtr->tkwin != NULL) { XDeleteProperty(dndPtr->display, Tk_WindowId(dndPtr->tkwin), dndPtr->dataPtr->targetAtom); XDeleteProperty(dndPtr->display, Tk_WindowId(dndPtr->tkwin), dndPtr->dataPtr->commAtom); } free((char *)dndPtr); } /* * ------------------------------------------------------------------------ * * GetDnd -- * * Looks for a Source record in the hash table for drag&drop source * widgets. Returns a pointer to the desired record. * * ------------------------------------------------------------------------ */ static int GetDnd(clientData, interp, pathName, dndPtrPtr) ClientData clientData; Tcl_Interp *interp; char *pathName; /* widget pathname for desired record */ Dnd **dndPtrPtr; { ThreadData *dataPtr = (ThreadData *)clientData; Tcl_HashEntry *hPtr; Tk_Window tkwin; tkwin = Tk_NameToWindow(interp, pathName, dataPtr->mainWindow); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_FindHashEntry(&(dataPtr->dndTable), (char *)tkwin); if (hPtr == NULL) { Tcl_AppendResult(interp, "window \"", pathName, "\" is not a drag&drop source/target", (char *)NULL); return TCL_ERROR; } *dndPtrPtr = (Dnd *)Tcl_GetHashValue(hPtr); return TCL_OK; } /* * ------------------------------------------------------------------------ * * CreateDnd -- * * Looks for a Source record in the hash table for drag&drop source * widgets. Creates a new record if the widget name is not already * registered. Returns a pointer to the desired record. * * ------------------------------------------------------------------------ */ static Dnd * CreateDnd(interp, tkwin) Tcl_Interp *interp; Tk_Window tkwin; /* widget for desired record */ { Dnd *dndPtr; dndPtr = (Dnd *) calloc(1, sizeof(Dnd)); assert(dndPtr); dndPtr->interp = interp; dndPtr->display = Tk_Display(tkwin); dndPtr->tkwin = tkwin; Tk_MakeWindowExist(tkwin); Tcl_InitHashTable(&(dndPtr->setDataTable), TCL_STRING_KEYS); Tcl_InitHashTable(&(dndPtr->getDataTable), TCL_STRING_KEYS); Tk_CreateGenericHandler(DndEventProc, (ClientData)dndPtr); return dndPtr; } static int ConfigureDnd(interp, dndPtr) Tcl_Interp *interp; Dnd *dndPtr; { Tcl_CmdInfo cmdInfo; Tcl_DString dString; int button, result; if (!Tcl_GetCommandInfo(interp, "blt::DndInit", &cmdInfo)) { static char cmd[] = "source [file join $blt_library bltDnd.tcl]"; /* * If the "DndInit" routine hasn't been sourced, do it now. */ if (Tcl_GlobalEval(interp, cmd) != TCL_OK) { Tcl_AddErrorInfo(interp, "\n (while loading bindings for blt::drag&drop)"); return TCL_ERROR; } } if (dndPtr->isTarget) { if (!dndPtr->targetPropertyExists) { AddTargetProperty(interp, dndPtr); } } else if (dndPtr->targetPropertyExists) { XDeleteProperty(dndPtr->display, Tk_WindowId(dndPtr->tkwin), dndPtr->dataPtr->targetAtom); dndPtr->targetPropertyExists = FALSE; } if (dndPtr->isSource) { /* Check the button binding for valid range (0 or 1-5) */ if ((dndPtr->reqButton < 0) || (dndPtr->reqButton > 5)) { Tcl_SetResult(interp, "button must be 1-5, or 0 for no bindings", TCL_STATIC); return TCL_ERROR; } button = dndPtr->reqButton; } else { button = 0; } Tcl_DStringInit(&dString); Blt_DStringAppendElements(&dString, "blt::DndInit", Tk_PathName(dndPtr->tkwin), Blt_Itoa(button), (char *)NULL); result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * RestrictProc -- * * This procedure filters incoming events when a "send" command * is outstanding. It defers all events except those containing * send commands and results. * * Results: * False is returned except for property-change events on a * commWindow. * * Side effects: * None. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static Tk_RestrictAction RestrictProc(clientData, eventPtr) ClientData clientData; /* Drag-and-drop manager. */ register XEvent *eventPtr; /* Event that just arrived. */ { Dnd *dndPtr = (Dnd *)clientData; if (eventPtr->xproperty.window != Tk_WindowId(dndPtr->tkwin)) { return TK_PROCESS_EVENT; /* Event not in our window. */ } if ((eventPtr->type == PropertyNotify) && (eventPtr->xproperty.state == PropertyNewValue)) { return TK_PROCESS_EVENT; /* This is the one we want to process. */ } if (eventPtr->type == Expose) { return TK_PROCESS_EVENT; /* Let expose events also get * handled. */ } return TK_DEFER_EVENT; /* Defer everything else. */ } /* *---------------------------------------------------------------------- * * TimeoutProc -- * * Procedure called when the timer event elapses. Used to wait * between attempts checking for the designated window. * * Results: * None. * * Side Effects: * Sets a flag, indicating the timeout occurred. * *---------------------------------------------------------------------- */ static void TimeoutProc(clientData) ClientData clientData; { int *statusPtr = (int *)clientData; /* An unusually long amount of time has elapsed since the drag * start message was sent. Assume that the other party has died * and abort the operation. */ #ifdef notdef fprintf(stderr, "TimeoutProc\n"); #endif *statusPtr = DROP_TIMEOUT; } #define WAIT_INTERVAL 2000 /* Twenty seconds. */ /* * ------------------------------------------------------------------------ * * TargetPropertyEventProc -- * * Invoked by the Tk dispatcher to handle widget events. * Manages redraws for the drag&drop token window. * * ------------------------------------------------------------------------ */ static void TargetPropertyEventProc(clientData, eventPtr) ClientData clientData; /* Data associated with transaction. */ XEvent *eventPtr; /* information about event */ { DropPending *pendingPtr = (DropPending *)clientData; char *data; int result, format; Atom typeAtom; unsigned long nItems, bytesAfter; #ifdef notdef fprintf(stderr, "TargetPropertyEventProc\n"); #endif if ((eventPtr->type != PropertyNotify) || (eventPtr->xproperty.atom != pendingPtr->commAtom) || (eventPtr->xproperty.state != PropertyNewValue)) { return; } Tk_DeleteTimerHandler(pendingPtr->timerToken); data = NULL; result = XGetWindowProperty( eventPtr->xproperty.display, /* Display of window. */ eventPtr->xproperty.window, /* Window holding the property. */ eventPtr->xproperty.atom, /* Name of property. */ 0, /* Offset of data (for multiple reads). */ pendingPtr->packetSize, /* Maximum number of items to read. */ False, /* If true, delete the property. */ XA_STRING, /* Desired type of property. */ &typeAtom, /* (out) Actual type of the property. */ &format, /* (out) Actual format of the property. */ &nItems, /* (out) # of items in specified format. */ &bytesAfter, /* (out) # of bytes remaining to be read. */ (unsigned char **)&data); #ifdef notdef fprintf(stderr, "TargetPropertyEventProc: result=%d, typeAtom=%d, format=%d, nItems=%d\n", result, typeAtom, format, nItems); #endif pendingPtr->status = DROP_FAIL; if ((result == Success) && (typeAtom == XA_STRING) && (format == 8)) { pendingPtr->status = DROP_OK; #ifdef notdef fprintf(stderr, "data found is (%s)\n", data); #endif Tcl_DStringAppend(&(pendingPtr->dString), data, -1); XFree(data); if (nItems == pendingPtr->packetSize) { /* Normally, we'll receive the data in one chunk. But if * more are required, reset the timer and go back into the * wait loop again. */ pendingPtr->timerToken = Tk_CreateTimerHandler(WAIT_INTERVAL, TimeoutProc, (ClientData)&(pendingPtr->status)); pendingPtr->status = DROP_CONTINUE; } } /* Set an empty, zero-length value on the source's property. This * acts as a handshake, indicating that the target received the * latest chunk. */ #ifdef notdef fprintf(stderr, "TargetPropertyEventProc: set response property\n"); #endif XChangeProperty(pendingPtr->display, pendingPtr->window, pendingPtr->commAtom, XA_STRING, 8, PropModeReplace, (unsigned char *)"", 0); } #ifdef HAVE_XDND static int XDndSelectionProc(clientData, interp, portion) ClientData clientData; Tcl_Interp *interp; char *portion; { DropPending *pendingPtr = (DropPending *)clientData; Tcl_DStringAppend(&(pendingPtr->dString), portion, -1); #ifdef notdef fprintf(stderr, "-> XDndGetSelectionProc\n"); #endif return TCL_OK; } #endif /* HAVE_XDND */ static void CompleteDataTransaction(dndPtr, format, pendingPtr) Dnd *dndPtr; char *format; DropPending *pendingPtr; { ThreadData *dataPtr = dndPtr->dataPtr; Tk_Window tkwin; Atom formatAtom; #ifdef notdef fprintf(stderr, "-> CompleteDataTransaction\n"); #endif /* Check if the source is local to the application. */ tkwin = Tk_IdToWindow(dndPtr->display, pendingPtr->window); if (tkwin != NULL) { Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&(dndPtr->dataPtr->dndTable), (char *)tkwin); if (hPtr != NULL) { Dnd *srcPtr; srcPtr = (Dnd *)Tcl_GetHashValue(hPtr); GetFormattedData(srcPtr, format, pendingPtr->timestamp, &(pendingPtr->dString)); } return; } formatAtom = XInternAtom(pendingPtr->display, format, False); if (pendingPtr->protocol == PROTO_XDND) { #ifdef HAVE_XDND if (Tk_GetSelection(dndPtr->interp, dndPtr->tkwin, dataPtr->XdndSelectionAtom, formatAtom, XDndSelectionProc, (ClientData)pendingPtr) != TCL_OK) { pendingPtr->status = DROP_FAIL; } #endif pendingPtr->status = DROP_OK; } else { Tk_RestrictProc *prevProc; ClientData prevArg; SendClientMsg(pendingPtr->display, pendingPtr->window, dataPtr->mesgAtom, DROP_START, Tk_WindowId(dndPtr->tkwin), pendingPtr->timestamp, formatAtom, pendingPtr->commAtom); pendingPtr->commAtom = dndPtr->dataPtr->commAtom; pendingPtr->status = DROP_CONTINUE; pendingPtr->display = dndPtr->display; prevProc = Tk_RestrictEvents(RestrictProc, (ClientData)dndPtr, &prevArg); Tk_CreateEventHandler(dndPtr->tkwin, PropertyChangeMask, TargetPropertyEventProc, (ClientData)pendingPtr); pendingPtr->timerToken = Tk_CreateTimerHandler(WAIT_INTERVAL, TimeoutProc, (ClientData)&(pendingPtr->status)); /* * ---------------------------------------------------------- * * Enter a loop processing X events until the all the data is * received or the source is declared to be dead (i.e. we * timeout). While waiting for a result, restrict handling to * just property-related events so that the transfer is * synchronous with respect to other events in the widget. * * ---------------------------------------------------------- */ while (pendingPtr->status == DROP_CONTINUE) { /* Wait for property event. */ Tcl_DoOneEvent(TCL_ALL_EVENTS); } Tk_RestrictEvents(prevProc, prevArg, &prevArg); Tk_DeleteTimerHandler(pendingPtr->timerToken); Tk_DeleteEventHandler(dndPtr->tkwin, PropertyChangeMask, TargetPropertyEventProc, (ClientData)pendingPtr); } #ifdef notdef fprintf(stderr, "<- CompleteDataTransaction\n"); #endif } /* * ------------------------------------------------------------------------ * * SourcePropertyEventProc -- * * Invoked by the Tk dispatcher when a PropertyNotify event occurs * on the source window. The event acts as a handshake between the * target and the source. The source acknowledges the target has * received the last packet of data and sends the next packet. * * Note a special case. If the data is divisible by the packetsize, * then an extra zero-length packet is sent to mark the end of the * data. A packetsize length packet indicates more is to follow. * * Normally the property is empty (zero-length). But if an * errored occurred on the target, it will contain the error * message. * * ------------------------------------------------------------------------ */ static void SourcePropertyEventProc(clientData, eventPtr) ClientData clientData; /* data associated with widget */ XEvent *eventPtr; /* information about event */ { DropPending *pendingPtr = (DropPending *)clientData; char *data; int result, format; Atom typeAtom; unsigned long nItems, bytesAfter; int size, bytesLeft; unsigned char *p; #ifdef notdef fprintf(stderr, "-> SourcePropertyEventProc\n"); #endif if ((eventPtr->xproperty.atom != pendingPtr->commAtom) || (eventPtr->xproperty.state != PropertyNewValue)) { return; } Tk_DeleteTimerHandler(pendingPtr->timerToken); data = NULL; result = XGetWindowProperty( eventPtr->xproperty.display, /* Display of window. */ eventPtr->xproperty.window, /* Window holding the property. */ eventPtr->xproperty.atom, /* Name of property. */ 0, /* Offset of data (for multiple reads). */ pendingPtr->packetSize, /* Maximum number of items to read. */ True, /* If true, delete the property. */ XA_STRING, /* Desired type of property. */ &typeAtom, /* (out) Actual type of the property. */ &format, /* (out) Actual format of the property. */ &nItems, /* (out) # of items in specified format. */ &bytesAfter, /* (out) # of bytes remaining to be read. */ (unsigned char **)&data); if ((result != Success) || (typeAtom != XA_STRING) || (format != 8)) { pendingPtr->status = DROP_FAIL; #ifdef notdef fprintf(stderr, "<- SourcePropertyEventProc: wrong format\n"); #endif return; /* Wrong data format. */ } if (nItems > 0) { pendingPtr->status = DROP_FAIL; Tcl_DStringFree(&(pendingPtr->dString)); Tcl_DStringAppend(&(pendingPtr->dString), data, -1); XFree(data); #ifdef notdef fprintf(stderr, "<- SourcePropertyEventProc: error\n"); #endif return; /* Error occurred on target. */ } bytesLeft = Tcl_DStringLength(&(pendingPtr->dString)) - pendingPtr->offset; if (bytesLeft <= 0) { #ifdef notdef fprintf(stderr, "<- SourcePropertyEventProc: done\n"); #endif pendingPtr->status = DROP_OK; size = 0; } else { size = MIN(bytesLeft, pendingPtr->packetSize); pendingPtr->status = DROP_CONTINUE; } p = (unsigned char *)Tcl_DStringValue(&(pendingPtr->dString)) + pendingPtr->offset; XChangeProperty(pendingPtr->display, pendingPtr->window, pendingPtr->commAtom, XA_STRING, 8, PropModeReplace, p, size); pendingPtr->offset += size; pendingPtr->timerToken = Tk_CreateTimerHandler(WAIT_INTERVAL, TimeoutProc, (ClientData)&(pendingPtr->status)); #ifdef notdef fprintf(stderr, "<- SourcePropertyEventProc\n"); #endif } static void SendDataToTarget(dndPtr, pendingPtr) Dnd *dndPtr; DropPending *pendingPtr; { int size; Tk_RestrictProc *prevProc; ClientData prevArg; #ifdef notdef fprintf(stderr, "-> SendDataToTarget\n"); #endif Tk_CreateEventHandler(dndPtr->tkwin, PropertyChangeMask, SourcePropertyEventProc, (ClientData)pendingPtr); pendingPtr->timerToken = Tk_CreateTimerHandler(WAIT_INTERVAL, TimeoutProc, (ClientData)&(pendingPtr->status)); size = MIN(Tcl_DStringLength(&(pendingPtr->dString)), pendingPtr->packetSize); prevProc = Tk_RestrictEvents(RestrictProc, (ClientData)dndPtr, &prevArg); /* * Setting the property starts the process. The target will * see the PropertyChange event and respond accordingly. */ XChangeProperty(dndPtr->display, pendingPtr->window, pendingPtr->commAtom, XA_STRING, 8, PropModeReplace, (unsigned char *)Tcl_DStringValue(&(pendingPtr->dString)), size); pendingPtr->offset += size; /* * Enter a loop processing X events until the result comes * in or the target is declared to be dead. While waiting * for a result, look only at property-related events so that * the handshake is synchronous with respect to other events in * the application. */ pendingPtr->status = DROP_CONTINUE; while (pendingPtr->status == DROP_CONTINUE) { /* Wait for the property change event. */ Tcl_DoOneEvent(TCL_ALL_EVENTS); } Tk_RestrictEvents(prevProc, prevArg, &prevArg); Tk_DeleteTimerHandler(pendingPtr->timerToken); Tk_DeleteEventHandler(dndPtr->tkwin, PropertyChangeMask, SourcePropertyEventProc, (ClientData)pendingPtr); #ifdef notdef fprintf(stderr, "<- SendDataToTarget\n"); #endif } static void DoDrop(dndPtr, eventPtr) Dnd *dndPtr; XEvent *eventPtr; { ThreadData *dataPtr = dndPtr->dataPtr; Tcl_Interp *interp = dndPtr->interp; struct DropRequest { int mesg; /* DROP_RESPONSE message. */ Window window; /* Target window. */ int timestamp; /* Transaction timestamp. */ Atom formatAtom; /* Format requested. */ } *dropPtr; char *format; DropPending pending; dropPtr = (struct DropRequest *)eventPtr->xclient.data.l; format = XGetAtomName(dndPtr->display, dropPtr->formatAtom); #ifdef notdef fprintf(stderr, "DoDrop %s 0x%x\n", Tk_PathName(dndPtr->tkwin), dropPtr->window); #endif if (GetFormattedData(dndPtr, format, dropPtr->timestamp, &(pending.dString)) != TCL_OK) { Tcl_BackgroundError(interp); /* Send empty string to break target's wait loop. */ XChangeProperty(dndPtr->display, dropPtr->window, dataPtr->commAtom, XA_STRING, 8, PropModeReplace, (unsigned char *)"", 0); return; } pending.window = dropPtr->window; pending.display = dndPtr->display; pending.commAtom = dataPtr->commAtom; pending.offset = 0; pending.packetSize = GetMaxPropertySize(dndPtr->display); SendDataToTarget(dndPtr, &pending); Tcl_DStringFree(&(pending.dString)); #ifdef notdef fprintf(stderr, "<- DoDrop\n"); #endif } static void DropFinished(dndPtr, eventPtr) Dnd *dndPtr; XEvent *eventPtr; { Tcl_Interp *interp = dndPtr->interp; Tcl_DString dString, savedResult; int result; SubstDescriptors subs[3]; struct DropResponse { int mesg; /* DROP_RESPONSE message. */ Window window; /* Target window. */ int timestamp; /* Transaction timestamp. */ int result; /* Result of transaction. */ } *dropPtr; #ifdef notdef fprintf(stderr, "DropFinished\n"); #endif dropPtr = (struct DropResponse *)eventPtr->xclient.data.l; subs[0].letter = 'a'; subs[0].value = NameOfAction(dropPtr->result); subs[1].letter = 'W'; subs[1].value = Tk_PathName(dndPtr->tkwin); subs[2].letter = 't'; subs[2].value = Blt_Itoa(dropPtr->timestamp); ExpandPercents(dndPtr->resultCmd, subs, 3, &dString); Tcl_DStringInit(&savedResult); Tcl_DStringGetResult(interp, &savedResult); result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result != TCL_OK) { Tcl_BackgroundError(interp); } Tcl_DStringResult(interp, &savedResult); } static void FreeFormats(dndPtr) Dnd *dndPtr; { if (dndPtr->matchingFormats != NULL) { free(dndPtr->matchingFormats); dndPtr->matchingFormats = NULL; } dndPtr->lastId = None; } static char * GetSourceFormats(dndPtr, window, timestamp) Dnd *dndPtr; Window window; int timestamp; { if (dndPtr->lastId != timestamp) { char *data; FreeFormats(dndPtr); data = GetProperty(dndPtr->display, window, dndPtr->dataPtr->formatsAtom); if (data != NULL) { dndPtr->matchingFormats = strdup(data); XFree(data); } dndPtr->lastId = timestamp; } if (dndPtr->matchingFormats == NULL) { return ""; } return dndPtr->matchingFormats; } static int InvokeCallback(dndPtr, cmd, x, y, formats, button, keyState) Dnd *dndPtr; char *cmd; int x, y; char *formats; int button; int keyState; { Tcl_DString dString, savedResult; Tcl_Interp *interp = dndPtr->interp; int result; Tcl_DStringInit(&dString); Tcl_DStringAppendElement(&dString, cmd); Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin)); x -= Blt_RootX(dndPtr->tkwin); /* Send coordinates relative to target. */ y -= Blt_RootY(dndPtr->tkwin); Tcl_DStringAppendElement(&dString, "x"); Tcl_DStringAppendElement(&dString, Blt_Itoa(x)); Tcl_DStringAppendElement(&dString, "y"); Tcl_DStringAppendElement(&dString, Blt_Itoa(y)); Tcl_DStringAppendElement(&dString, "formats"); if (formats == NULL) { formats = ""; } Tcl_DStringAppendElement(&dString, formats); Tcl_DStringAppendElement(&dString, "button"); Tcl_DStringAppendElement(&dString, Blt_Itoa(button)); Tcl_DStringAppendElement(&dString, "state"); Tcl_DStringAppendElement(&dString, Blt_Itoa(keyState)); Tcl_Preserve(interp); Tcl_DStringInit(&savedResult); Tcl_DStringGetResult(interp, &savedResult); result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result == TCL_OK) { result = GetDragResult(interp, Tcl_GetStringResult(interp)); } else { result = DRAG_CANCEL; Tcl_BackgroundError(interp); } Tcl_DStringResult(interp, &savedResult); Tcl_Release(interp); return result; } /* * ------------------------------------------------------------------------ * * AcceptDrop -- * * Invokes a Tcl procedure to handle the target's side of the * drop. A Tcl procedure is invoked, either one designated for * this target by the user (-ondrop) or a default Tcl procedure. * It is passed the following arguments: * * widget The path name of the target. * x X-coordinate of the mouse relative to the * widget. * y Y-coordinate of the mouse relative to the * widget. * formats A list of data formats acceptable to both * the source and target. * button Button pressed. * state Key state. * action Requested action from source. * * If the Tcl procedure returns "cancel", this indicates that the drop was * not accepted by the target and the reject symbol should be displayed. * Otherwise one of the following strings may be recognized: * * "cancel" Drop was canceled. * "copy" Source data has been successfully copied. * "link" Target has made a link to the data. It's * Ok for the source to remove it's association * with the data, but not to delete the data * itself. * "move" Source data has been successfully copied, * it's Ok for the source to delete its * association with the data and the data itself. * * The result is relayed back to the source via another client message. * The source may or may not be waiting for the result. * * Results: * None. * * Side Effects: * A Tcl procedure is invoked in the target to handle the drop event. * The result of the drop is sent (via another ClientMessage) to the * source. * * ------------------------------------------------------------------------ */ static int AcceptDrop(dndPtr, x, y, formats, button, keyState) Dnd *dndPtr; /* Target where the drop event occurred. */ int x, y; char *formats; int button, keyState; { Tcl_Interp *interp = dndPtr->interp; char *cmd; Tcl_DString dString, savedResult; int result; if (dndPtr->leaveCmd != NULL) { InvokeCallback(dndPtr, dndPtr->leaveCmd, x, y, formats, button, keyState); } cmd = dndPtr->dropCmd; if (cmd == NULL) { cmd = "blt::DndStdDrop"; } Tcl_DStringInit(&dString); Tcl_DStringAppendElement(&dString, cmd); Tcl_DStringAppendElement(&dString, Tk_PathName(dndPtr->tkwin)); x -= Blt_RootX(dndPtr->tkwin); y -= Blt_RootY(dndPtr->tkwin); Tcl_DStringAppendElement(&dString, "x"); Tcl_DStringAppendElement(&dString, Blt_Itoa(x)); Tcl_DStringAppendElement(&dString, "y"); Tcl_DStringAppendElement(&dString, Blt_Itoa(y)); Tcl_DStringAppendElement(&dString, "formats"); Tcl_DStringAppendElement(&dString, formats); Tcl_DStringAppendElement(&dString, "button"); Tcl_DStringAppendElement(&dString, Blt_Itoa(button)); Tcl_DStringAppendElement(&dString, "state"); Tcl_DStringAppendElement(&dString, Blt_Itoa(keyState)); Tcl_Preserve(interp); Tcl_DStringInit(&savedResult); Tcl_DStringGetResult(interp, &savedResult); result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); if (result == TCL_OK) { result = GetAction(Tcl_GetStringResult(interp)); } else { result = ACTION_CANCEL; Tcl_BackgroundError(interp); } Tcl_DStringResult(interp, &savedResult); Tcl_Release(interp); return result; } /* * ------------------------------------------------------------------------ * * HandleDropEvent -- * * Invokes a Tcl procedure to handle the target's side of the * drop. This routine is triggered via a client message from the * drag source indicating that the token was dropped over this * target. The fields of the incoming message are: * * data.l[0] Message type. * data.l[1] Window Id of the source. * data.l[2] Screen X-coordinate of the pointer. * data.l[3] Screen Y-coordinate of the pointer. * data.l[4] Id of the drag&drop transaction. * * A Tcl procedure is invoked, either one designated for this * target by the user (-ondrop) or a default Tcl procedure. It * is passed the following arguments: * * widget The path name of the target. * x X-coordinate of the mouse relative to the * widget. * y Y-coordinate of the mouse relative to the * widget. * formats A list of data formats acceptable to both * the source and target. * * If the Tcl procedure returns "cancel", this indicates that the drop was * not accepted by the target and the reject symbol should be displayed. * Otherwise one of the following strings may be recognized: * * "cancel" Drop was canceled. * "copy" Source data has been successfully copied. * "link" Target has made a link to the data. It's * Ok for the source to remove it's association * with the data, but not to delete the data * itself. * "move" Source data has been successfully copied, * it's Ok for the source to delete its * association with the data and the data itself. * * The result is relayed back to the source via another client message. * The source may or may not be waiting for the result. * * Results: * None. * * Side Effects: * A Tcl procedure is invoked in the target to handle the drop event. * The result of the drop is sent (via another ClientMessage) to the * source. * * ------------------------------------------------------------------------ */ static void HandleDropEvent(dndPtr, eventPtr) Dnd *dndPtr; /* Target where the drop event occurred. */ XEvent *eventPtr; /* Message sent from the drag source. */ { int button, keyState; int x, y; char *formats; int result; struct DropInfo { int mesg; /* DROP_START message. */ Window window; /* Source window. */ int timestamp; /* Transaction timestamp. */ int point; /* Root X-Y coordinate of pointer. */ int flags; /* Button/keystate information. */ } *dropPtr; DropPending pending; dropPtr = (struct DropInfo *)eventPtr->xclient.data.l; UNPACK(dropPtr->point, x, y); UNPACK(dropPtr->flags, button, keyState); /* Set up temporary bookkeeping for the drop transaction */ memset (&pending, 0, sizeof(pending)); pending.window = dropPtr->window; pending.display = eventPtr->xclient.display; pending.timestamp = dropPtr->timestamp; pending.protocol = PROTO_BLT; pending.packetSize = GetMaxPropertySize(pending.display); Tcl_DStringInit(&(pending.dString)); formats = GetSourceFormats(dndPtr, dropPtr->window, dropPtr->timestamp); dndPtr->pendingPtr = &pending; result = AcceptDrop(dndPtr, x, y, formats, button, keyState); dndPtr->pendingPtr = NULL; /* Target-to-Source: Drop result message. */ SendClientMsg(dndPtr->display, dropPtr->window, dndPtr->dataPtr->mesgAtom, DROP_RESPONSE, Tk_WindowId(dndPtr->tkwin), dropPtr->timestamp, result, 0); FreeFormats(dndPtr); } /* * ------------------------------------------------------------------------ * * HandleDragEvent -- * * Invokes one of 3 Tcl procedures to handle the target's side of * the drag operation. This routine is triggered via a ClientMessage * from the drag source indicating that the token as either entered, * moved, or left this target. The source sends messages only if * Tcl procedures on the target have been defined to watch the * events. The message_type field can be either * * DRAG_ENTER The mouse has entered the target. * DRAG_MOTION The mouse has moved within the target. * DRAG_LEAVE The mouse has left the target. * * The data fields are as follows: * data.l[0] Message type. * data.l[1] Window Id of the source. * data.l[2] Timestamp of the drag&drop transaction. * data.l[3] Root X-Y coordinate of the pointer. * data.l[4] Button and key state information. * * For any of the 3 Tcl procedures, the following arguments * are passed: * * widget The path name of the target. * x X-coordinate of the mouse in the widget. * y Y-coordinate of the mouse in the widget. * formats A list of data formats acceptable to both * the source and target. * * If the Tcl procedure returns "cancel", this indicates that the drag * operation has been canceled and the reject symbol should be displayed. * Otherwise it should return a boolean: * * true Target will accept drop. * false Target will not accept the drop. * * The purpose of the Enter and Leave procedure is to allow the * target to provide visual feedback that the drop can occur or not. * The Motion procedure is for cases where the drop area is a smaller * area within the target, such as a canvas item on a canvas. The * procedure can determine (based upon the X-Y coordinates) whether * the pointer is over the canvas item and return a value accordingly. * * The result of the Tcl procedure is then relayed back to the * source by a ClientMessage. * * Results: * None. * * Side Effects: * A Tcl procedure is invoked in the target to handle the drag event. * The result of the drag is sent (via another ClientMessage) to the * source. * * ------------------------------------------------------------------------ */ static void HandleDragEvent(dndPtr, eventPtr) Dnd *dndPtr; /* Target where the drag event occurred. */ XEvent *eventPtr; /* Message sent from the drag source. */ { char *cmd; int resp; int x, y; int button, keyState; char *formats; struct DragInfo { int mesg; /* Drag-and-drop message type. */ Window window; /* Source window. */ int timestamp; /* Transaction timestamp. */ int point; /* Root X-Y coordinate of pointer. */ int flags; /* Button/keystate information. */ } *dragPtr; dragPtr = (struct DragInfo *)eventPtr->xclient.data.l; cmd = NULL; switch (dragPtr->mesg) { case DRAG_ENTER: cmd = dndPtr->enterCmd; break; case DRAG_MOTION: cmd = dndPtr->motionCmd; break; case DRAG_LEAVE: cmd = dndPtr->leaveCmd; break; } if (cmd == NULL) { return; /* Nothing to do. */ } UNPACK(dragPtr->point, x, y); UNPACK(dragPtr->flags, button, keyState); formats = GetSourceFormats(dndPtr, dragPtr->window, dragPtr->timestamp); resp = InvokeCallback(dndPtr, cmd, x, y, formats, button, keyState); /* Target-to-Source: Drag result message. */ SendClientMsg(dndPtr->display, dragPtr->window, dndPtr->dataPtr->mesgAtom, DRAG_RESPONSE, Tk_WindowId(dndPtr->tkwin), dragPtr->timestamp, resp, 0); } /* * ------------------------------------------------------------------------ * * DndEventProc -- * * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received * on a registered drag&drop source widget. * * ------------------------------------------------------------------------ */ static int DndEventProc(clientData, eventPtr) ClientData clientData; /* Drag&drop record. */ XEvent *eventPtr; /* Event description. */ { Dnd *dndPtr = (Dnd *)clientData; if (eventPtr->xany.window != Tk_WindowId(dndPtr->tkwin)) { return 0; } if (eventPtr->type == DestroyNotify) { dndPtr->tkwin = NULL; DestroyDnd(dndPtr); return 0; /* Other handlers have to see this event too. */ } else if (eventPtr->type == ButtonPress) { dndPtr->keyState = eventPtr->xbutton.state; dndPtr->button = eventPtr->xbutton.button; return 0; } else if (eventPtr->type == ButtonRelease) { dndPtr->keyState = eventPtr->xbutton.state; dndPtr->button = eventPtr->xbutton.button; return 0; } else if (eventPtr->type == MotionNotify) { dndPtr->keyState = eventPtr->xmotion.state; return 0; } else if ((eventPtr->type == ClientMessage) && (eventPtr->xclient.message_type == dndPtr->dataPtr->mesgAtom)) { int result; switch((unsigned int)eventPtr->xclient.data.l[0]) { case DROP_START: DoDrop(dndPtr, eventPtr); return 1; case DROP_RESPONSE: result = eventPtr->xclient.data.l[MESG_INDEX_RESP]; if (result != DROP_OK) { dndPtr->tokenPtr->status = TOKEN_STATUS_REJECT; EventuallyRedrawToken(dndPtr); } else { dndPtr->tokenPtr->nSteps = 10; FadeToken(dndPtr); } if (dndPtr->resultCmd != NULL) { DropFinished(dndPtr, eventPtr); } return 1; case DRAG_RESPONSE: dndPtr->tokenPtr->status = eventPtr->xclient.data.l[MESG_INDEX_RESP]; ChangeToken(dndPtr); return 1; case DROP_REQUEST: HandleDropEvent(dndPtr, eventPtr); return 1; case DRAG_ENTER: case DRAG_MOTION: case DRAG_LEAVE: HandleDragEvent(dndPtr, eventPtr); return 1; } } return 0; } static void SendPointerMessage(dndPtr, eventType, windowPtr, x, y) Dnd *dndPtr; /* Source drag&drop manager. */ int eventType; /* Type of event to relay. */ Winfo *windowPtr; /* Generic window information. */ int x, y; /* Root coordinates of mouse. */ { /* Source-to-Target: Pointer event messages. */ SendClientMsg( dndPtr->display, /* Display of recipient window. */ windowPtr->window, /* Recipient window. */ dndPtr->dataPtr->mesgAtom, /* Message type. */ eventType, /* Data 1 */ Tk_WindowId(dndPtr->tkwin), /* Data 2 */ dndPtr->timestamp, /* Data 3 */ PACK(x, y), /* Data 4 */ PACK(dndPtr->button, dndPtr->keyState)); /* Data 5 */ /* Don't wait the response. */ } static void RelayEnterEvent(dndPtr, windowPtr, x, y) Dnd *dndPtr; Winfo *windowPtr; int x, y; { if ((windowPtr != NULL) && (windowPtr->eventFlags & WATCH_ENTER)) { SendPointerMessage(dndPtr, DRAG_ENTER, windowPtr, x, y); } } static void RelayLeaveEvent(dndPtr, windowPtr, x, y) Dnd *dndPtr; Winfo *windowPtr; int x, y; { if ((windowPtr != NULL) && (windowPtr->eventFlags & WATCH_LEAVE)) { SendPointerMessage(dndPtr, DRAG_LEAVE, windowPtr, x, y); } } static void RelayMotionEvent(dndPtr, windowPtr, x, y) Dnd *dndPtr; Winfo *windowPtr; int x, y; { if ((windowPtr != NULL) && (windowPtr->eventFlags & WATCH_MOTION)) { SendPointerMessage(dndPtr, DRAG_MOTION, windowPtr, x, y); } } static void RelayDropEvent(dndPtr, windowPtr, x, y) Dnd *dndPtr; Winfo *windowPtr; int x, y; { SendPointerMessage(dndPtr, DROP_REQUEST, windowPtr, x, y); } /* * ------------------------------------------------------------------------ * * FreeWinfo -- * * ------------------------------------------------------------------------ */ static void FreeWinfo(windowPtr) Winfo *windowPtr; /* window rep to be freed */ { Winfo *childPtr; Blt_ChainLink *linkPtr; for (linkPtr = Blt_ChainFirstLink(windowPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { childPtr = (Winfo *) Blt_ChainGetValue(linkPtr); FreeWinfo(childPtr); /* Recursively free children. */ } if (windowPtr->matches != NULL) { free(windowPtr->matches); } Blt_ChainDestroy(windowPtr->chainPtr); free((char *)windowPtr); } /* * ------------------------------------------------------------------------ * * GetWinfo -- * * Invoked during "drag" operations. Digs into the root window * hierarchy and caches the window-related information. * If the current point lies over an uninitialized window (i.e. * one that already has an allocated Winfo structure, but has * not been filled in yet), this routine is called to query * window coordinates. If the window has any children, more * uninitialized Winfo structures are allocated. Further queries * will cause these structures to be initialized in turn. * * ------------------------------------------------------------------------ */ static void GetWinfo(display, windowPtr) Display *display; Winfo *windowPtr; /* window rep to be initialized */ { int visible; if (windowPtr->initialized) { return; } /* Query for the window coordinates. */ visible = GetWindowRegion(display, windowPtr->window, &(windowPtr->x1), &(windowPtr->y1), &(windowPtr->x2), &(windowPtr->y2)); if (visible) { Blt_ChainLink *linkPtr; Blt_Chain *chainPtr; Winfo *childPtr; /* Add offset from parent's origin to coordinates */ if (windowPtr->parentPtr != NULL) { windowPtr->x1 += windowPtr->parentPtr->x1; windowPtr->y1 += windowPtr->parentPtr->y1; windowPtr->x2 += windowPtr->parentPtr->x1; windowPtr->y2 += windowPtr->parentPtr->y1; } /* * Collect a list of child windows, sorted in z-order. The * topmost window will be first in the list. */ chainPtr = GetWindowZOrder(display, windowPtr->window); /* Add and initialize extra slots if needed. */ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { childPtr = (Winfo *) calloc(1, sizeof(Winfo)); assert(childPtr); childPtr->initialized = FALSE; childPtr->window = (Window)Blt_ChainGetValue(linkPtr); childPtr->parentPtr = windowPtr; Blt_ChainSetValue(linkPtr, (ClientData)childPtr); } windowPtr->chainPtr = chainPtr; } else { /* If it's not viewable don't bother doing anything else. */ windowPtr->x1 = windowPtr->y1 = windowPtr->x2 = windowPtr->y2 = -1; windowPtr->chainPtr = NULL; } windowPtr->initialized = TRUE; } /* * ------------------------------------------------------------------------ * * InitRoot -- * * Invoked at the start of a "drag" operation to capture the * positions of all windows on the current root. Queries the * entire window hierarchy and determines the placement of each * window. Queries the "BltDndTarget" property info where * appropriate. This information is used during the drag * operation to determine when the drag&drop token is over a * valid drag&drop target. * * Results: * Returns the record for the root window, which contains records * for all other windows as children. * * ------------------------------------------------------------------------ */ static Winfo * InitRoot(dndPtr) Dnd *dndPtr; { Winfo *rootPtr; rootPtr = (Winfo *) calloc(1, sizeof(Winfo)); assert(rootPtr); rootPtr->window = DefaultRootWindow(dndPtr->display); dndPtr->windowPtr = NULL; GetWinfo(dndPtr->display, rootPtr); return rootPtr; } static int ParseProperty(interp, dndPtr, windowPtr, data) Tcl_Interp *interp; Dnd *dndPtr; Winfo *windowPtr; char *data; { int nElems; char **elemArr; int eventFlags; Tcl_DString dString; int count; register int i; if (Tcl_SplitList(interp, data, &nElems, &elemArr) != TCL_OK) { return TCL_ERROR; /* Malformed property list. */ } if (nElems < 1) { Tcl_AppendResult(interp, "Malformed property \"", data, "\"", (char *)NULL); goto error; } if (Tcl_GetInt(interp, elemArr[PROP_WATCH_FLAGS], &eventFlags) != TCL_OK) { goto error; } /* target flags, type1, type2, ... */ /* * The target property contains a list of possible formats. * Compare this with what formats the source is willing to * convert and compress the list down to just the matching * formats. It's up to the target to request the specific * type (or types) that it wants. */ count = 0; Tcl_DStringInit(&dString); if (dndPtr->reqFormats == NULL) { Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; char *fmt; for (i = 1; i < nElems; i++) { for(hPtr = Tcl_FirstHashEntry(&(dndPtr->getDataTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { fmt = Tcl_GetHashKey(&(dndPtr->getDataTable), hPtr); if ((*fmt == elemArr[i][0]) && (strcmp(fmt, elemArr[i]) == 0)) { Tcl_DStringAppendElement(&dString, elemArr[i]); count++; break; } } } } else { register char **s; for (i = 1; i < nElems; i++) { for (s = dndPtr->reqFormats; *s != NULL; s++) { if ((**s == elemArr[i][0]) && (strcmp(*s, elemArr[i]) == 0)) { Tcl_DStringAppendElement(&dString, elemArr[i]); count++; } } } } if (count == 0) { #ifdef notdef fprintf(stderr, "source/target mismatch: No matching types\n"); #endif return TCL_BREAK; } if (eventFlags != 0) { SetProperty(dndPtr->tkwin, dndPtr->dataPtr->formatsAtom, Tcl_DStringValue(&dString)); windowPtr->matches = NULL; } else { windowPtr->matches = strdup(Tcl_DStringValue(&dString)); } Tcl_DStringFree(&dString); windowPtr->eventFlags = eventFlags; return TCL_OK; error: free((char *)elemArr); return TCL_ERROR; } /* * ------------------------------------------------------------------------ * * OverTarget -- * * Checks to see if a compatible drag&drop target exists at the * given position. A target is "compatible" if it is a drag&drop * window, and if it has a handler that is compatible with the * current source window. * * Results: * Returns a pointer to a structure describing the target, or NULL * if no compatible target is found. * * ------------------------------------------------------------------------ */ static Winfo * OverTarget(dndPtr) Dnd *dndPtr; /* drag&drop source window */ { Tcl_Interp *interp = dndPtr->interp; int x, y; int vx, vy; int dummy; Winfo *windowPtr; /* * If no window info has been been gathered yet for this target, * then abort this call. This probably means that the token is * moved before it has been properly built. */ if (dndPtr->rootPtr == NULL) { fprintf(stderr, "Not initialized\n"); return NULL; } /* Adjust current location for virtual root windows. */ Tk_GetVRootGeometry(dndPtr->tkwin, &vx, &vy, &dummy, &dummy); x = dndPtr->x + vx; y = dndPtr->y + vy; windowPtr = FindTopWindow(dndPtr, x, y); if (windowPtr == NULL) { return NULL; /* Not over a window. */ } if ((!dndPtr->selfTarget) && (Tk_WindowId(dndPtr->tkwin) == windowPtr->window)) { return NULL; /* If the self-target flag isn't set, * don't allow the source window to * drop onto itself. */ } if (!windowPtr->lookedForProperty) { char *data; int result; windowPtr->lookedForProperty = TRUE; /* See if this window has a "BltDndTarget" property. */ data = GetProperty(dndPtr->display, windowPtr->window, dndPtr->dataPtr->targetAtom); if (data == NULL) { #ifdef notdef fprintf(stderr, "No property on 0x%x\n", windowPtr->window); #endif return NULL; /* No such property on window. */ } result = ParseProperty(interp, dndPtr, windowPtr, data); XFree(data); if (result == TCL_BREAK) { #ifdef notdef fprintf(stderr, "No matching formats\n"); #endif return NULL; } if (result != TCL_OK) { Tcl_BackgroundError(interp); return NULL; /* Malformed property list. */ } windowPtr->isTarget = TRUE; } if (!windowPtr->isTarget) { return NULL; } return windowPtr; } /* * ------------------------------------------------------------------------ * * AddTargetProperty -- * * Attaches a drag&drop property to the given target window. * This property allows us to recognize the window later as a * valid target. It also stores important information including * the interpreter managing the target and the pathname of the * target window. Usually this routine is called when the target * is first registered or first exposed (so that the X-window * really exists). * * ------------------------------------------------------------------------ */ static void AddTargetProperty(interp, dndPtr) Tcl_Interp *interp; Dnd *dndPtr; /* drag&drop target window data */ { Tcl_DString dString; Tcl_HashEntry *hPtr; unsigned int eventFlags; Tcl_HashSearch cursor; char *fmt; char string[200]; Tcl_DStringInit(&dString); /* * Each target window's dnd property contains * * 1. Mouse event flags. * 2. List of all the data types that can be handled. If none * are listed, then all can be handled. */ eventFlags = 0; if (dndPtr->enterCmd != NULL) { eventFlags |= WATCH_ENTER; } if (dndPtr->leaveCmd != NULL) { eventFlags |= WATCH_LEAVE; } if (dndPtr->motionCmd != NULL) { eventFlags |= WATCH_MOTION; } sprintf(string, "0x%x", eventFlags); Tcl_DStringAppendElement(&dString, string); for (hPtr = Tcl_FirstHashEntry(&(dndPtr->setDataTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { fmt = Tcl_GetHashKey(&(dndPtr->setDataTable), hPtr); Tcl_DStringAppendElement(&dString, fmt); } SetProperty(dndPtr->tkwin, dndPtr->dataPtr->targetAtom, Tcl_DStringValue(&dString)); dndPtr->targetPropertyExists = TRUE; Tcl_DStringFree(&dString); } /* * ------------------------------------------------------------------------ * * ExpandPercents -- * * Expands all percent substitutions found in the input "str" * that match specifications in the "subs" list. Any percent * field that is not found in the "subs" list is left alone. * Returns a string that remains valid until the next call to * this routine. * * ------------------------------------------------------------------------ */ static void ExpandPercents(string, subsArr, nSubs, dStrPtr) char *string; /* Incoming command string */ SubstDescriptors *subsArr; /* Array of known substitutions */ int nSubs; /* Number of elements in subs array */ Tcl_DString *dStrPtr; { register char *chunk, *p; char letter; char percentSign; int i; /* * Scan through the copy of the input string, look for * the next '%' character, and try to make the substitution. * Continue doing this to the end of the string. */ Tcl_DStringInit(dStrPtr); chunk = p = string; while ((p = strchr(p, '%')) != NULL) { /* Copy up to the percent sign. Repair the string afterwards */ percentSign = *p; *p = '\0'; Tcl_DStringAppend(dStrPtr, chunk, -1); *p = percentSign; /* Search for a matching substition rule */ letter = *(p + 1); for (i = 0; i < nSubs; i++) { if (subsArr[i].letter == letter) { break; } } if (i < nSubs) { /* Make the substitution */ Tcl_DStringAppend(dStrPtr, subsArr[i].value, -1); } else { /* Copy in the %letter verbatim */ char verbatim[3]; verbatim[0] = '%'; verbatim[1] = letter; verbatim[2] = '\0'; Tcl_DStringAppend(dStrPtr, verbatim, -1); } p += 2; /* Skip % + letter */ if (letter == '\0') { p += 1; /* Premature % substitution (end of string) */ } chunk = p; } /* Pick up last chunk if substition wasn't the last thing in the string. */ if (*chunk != '\0') { Tcl_DStringAppend(dStrPtr, chunk, -1); } } static int DragInit(dndPtr, x, y, timestamp) Dnd *dndPtr; int x, y; int timestamp; { Token *tokenPtr = dndPtr->tokenPtr; if (dndPtr->rootPtr != NULL) { FreeWinfo(dndPtr->rootPtr); } dndPtr->rootPtr = InitRoot(dndPtr); /* Reset information cache. */ dndPtr->timestamp = timestamp; dndPtr->canceled = FALSE; dndPtr->x = x; /* Save current location. */ dndPtr->y = y; if (dndPtr->packageCmd != NULL) { Tcl_DString dString; SubstDescriptors subs[3]; int status; /* Execute command to initialize the token window. */ subs[0].letter = 'W'; subs[0].value = Tk_PathName(dndPtr->tkwin); subs[1].letter = 't'; subs[1].value = Tk_PathName(tokenPtr->tkwin); subs[2].letter = '#'; subs[2].value = Blt_Itoa(dndPtr->timestamp); dndPtr->pkgCmdInProgress = TRUE; ExpandPercents(dndPtr->packageCmd, subs, 3, &dString); status = Tcl_Eval(dndPtr->interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); dndPtr->pkgCmdInProgress = FALSE; if (status != TCL_OK) { return TCL_ERROR; } } if (dndPtr->cursor != None) { Tk_Cursor cursor; /* Save the old cursor */ cursor = GetWidgetCursor(dndPtr->interp, dndPtr->tkwin); if (dndPtr->cursor != None) { Tk_FreeCursor(dndPtr->display, dndPtr->cursor); } dndPtr->cursor = cursor; if (dndPtr->cursors != NULL) { /* Temporarily install the drag-and-drop cursor. */ Tk_DefineCursor(dndPtr->tkwin, dndPtr->cursors[0]); } } if (Tk_WindowId(tokenPtr->tkwin) == None) { Tk_MakeWindowExist(tokenPtr->tkwin); } if (!Tk_IsMapped(tokenPtr->tkwin)) { Tk_MapWindow(tokenPtr->tkwin); } RaiseToken(tokenPtr); return TCL_OK; } /* * ------------------------------------------------------------------------ * * CancelOp -- * * Cancels the current drag&drop operation for the source. Calling * this operation does not affect the transfer of data from the * source to the target, once the drop has been made. From the * source's point of view, the drag&drop operation is already over. * * Example: dnd cancel .widget * * Results: * A standard Tcl result. * * Side Effects: * Hides the token and sets a flag indicating that further "drag" * and "drop" operations should be ignored. * * ------------------------------------------------------------------------ */ static int CancelOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Dnd *dndPtr; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (!dndPtr->isSource) { Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin), "\" is not a registered drag&drop source.", (char *)NULL); return TCL_ERROR; } /* Send the target a Leave message so it can change back. */ RelayLeaveEvent(dndPtr, dndPtr->windowPtr, 0, 0); dndPtr->canceled = TRUE; dndPtr->tokenPtr->nSteps = 10; SnapToken(dndPtr); return TCL_OK; } /* * ------------------------------------------------------------------------ * * CgetOp -- * * Called to process an (argc,argv) list to configure (or * reconfigure) a drag&drop widget. * * ------------------------------------------------------------------------ */ /* ARGSUSED*/ static int CgetOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Dnd *dndPtr; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } return Tk_ConfigureValue(interp, dndPtr->tkwin, configSpecs, (char *)dndPtr, argv[3], 0); } /* * ------------------------------------------------------------------------ * * ConfigureOp -- * * Called to process an (argc,argv) list to configure (or * reconfigure) a drag&drop widget. * * ------------------------------------------------------------------------ */ static int ConfigureOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* current interpreter */ int argc; /* number of arguments */ char **argv; /* argument strings */ { Dnd *dndPtr; int flags; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } flags = TK_CONFIG_ARGV_ONLY; if (argc == 3) { return Tk_ConfigureInfo(interp, dndPtr->tkwin, configSpecs, (char *)dndPtr, (char *)NULL, flags); } else if (argc == 4) { return Tk_ConfigureInfo(interp, dndPtr->tkwin, configSpecs, (char *)dndPtr, argv[3], flags); } if (Tk_ConfigureWidget(interp, dndPtr->tkwin, configSpecs, argc - 3, argv + 3, (char *)dndPtr, flags) != TCL_OK) { return TCL_ERROR; } if (ConfigureDnd(interp, dndPtr) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* * ------------------------------------------------------------------------ * * DeleteOp -- * * Deletes the drag&drop manager from the widget. If a "-source" * or "-target" switch is present, only that component of the * drag&drop manager is shutdown. The manager is not deleted * unless both the target and source components are shutdown. * * Example: dnd delete .widget * * Results: * A standard Tcl result. * * Side Effects: * Deletes the drag&drop manager. Also the source and target window * properties are removed from the widget. * * ------------------------------------------------------------------------ */ static int DeleteOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Dnd *dndPtr; register int i; for(i = 3; i < argc; i++) { if (GetDnd(clientData, interp, argv[i], &dndPtr) != TCL_OK) { return TCL_ERROR; } DestroyDnd(dndPtr); } return TCL_OK; } /* * ------------------------------------------------------------------------ * * SelectOp -- * * Initializes a drag&drop transaction. Typically this operation * is called from a ButtonPress event on a source widget. The * window information cache is initialized, and the token is * initialized and displayed. * * Example: dnd pickup .widget x y * * Results: * A standard Tcl result. * * Side Effects: * The token is initialized and displayed. This may require invoking * a user-defined package command. The window information cache is * also initialized. * * ------------------------------------------------------------------------ */ static int SelectOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Winfo *newPtr; Dnd *dndPtr; int x, y, timestamp; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (!dndPtr->isSource) { Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin), "\" is not a registered drag&drop source.", (char *)NULL); return TCL_ERROR; } if (dndPtr->tokenPtr == NULL) { Tcl_AppendResult(interp, "no drag&drop token created for \"", argv[2], "\"", (char *)NULL); return TCL_ERROR; } if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) { return TCL_ERROR; } if (Tcl_GetInt(interp, argv[5], ×tamp) != TCL_OK) { return TCL_ERROR; } /* * Initialize the window cache and the drop token. A Tcl command may * be invoked to "package" the token. */ if (DragInit(dndPtr, x, y, timestamp) != TCL_OK) { return TCL_ERROR; } newPtr = OverTarget(dndPtr); RelayEnterEvent(dndPtr, newPtr, x, y); dndPtr->windowPtr = newPtr; dndPtr->tokenPtr->status = (newPtr != NULL) ? TOKEN_STATUS_ACTIVE : TOKEN_STATUS_NORMAL; if (dndPtr->tokenPtr->lastStatus != dndPtr->tokenPtr->status) { EventuallyRedrawToken(dndPtr); } MoveToken(dndPtr); /* Move token to current drag point. */ dndPtr->tokenPtr->selectX = dndPtr->tokenPtr->x; dndPtr->tokenPtr->selectY = dndPtr->tokenPtr->y; return TCL_OK; } /* * ------------------------------------------------------------------------ * * DragOp -- * * Continues the drag&drop transaction. Typically this operation * is called from a button Motion event on a source widget. Pointer * event messages are possibly sent to the target, indicating Enter, * Leave, and Motion events. * * Example: dnd drag .widget x y * * Results: * A standard Tcl result. * * Side Effects: * Pointer events are relayed to the target (if the mouse is over * one). * * ------------------------------------------------------------------------ */ static int DragOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Winfo *newPtr, *oldPtr; Dnd *dndPtr; int x, y; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (!dndPtr->isSource) { Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin), "\" is not a registered drag&drop source.", (char *)NULL); return TCL_ERROR; } if (dndPtr->tokenPtr == NULL) { Tcl_AppendResult(interp, "no drag&drop token created for \"", argv[2], "\"", (char *)NULL); return TCL_ERROR; } if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) { return TCL_ERROR; } /* * The following code gets tricky because the package command may * call "update" or "tkwait". A motion event may then trigger * this routine, before the token has been initialized. Until the * package command finishes, no target messages are sent and drops * are silently ignored. Note that we do still track mouse * movements, so that when the package command completes, it will * have the latest pointer position. */ dndPtr->x = x; /* Save current location. */ dndPtr->y = y; if ((dndPtr->canceled) || (dndPtr->pkgCmdInProgress)) { return TCL_OK; /* Re-entered this routine. */ } oldPtr = dndPtr->windowPtr; newPtr = OverTarget(dndPtr); if (newPtr == oldPtr) { RelayMotionEvent(dndPtr, oldPtr, x, y); dndPtr->windowPtr = oldPtr; } else { RelayLeaveEvent(dndPtr, oldPtr, x, y); RelayEnterEvent(dndPtr, newPtr, x, y); dndPtr->windowPtr = newPtr; } dndPtr->tokenPtr->status = (newPtr != NULL) ? TOKEN_STATUS_ACTIVE : TOKEN_STATUS_NORMAL; if (dndPtr->tokenPtr->lastStatus != dndPtr->tokenPtr->status) { EventuallyRedrawToken(dndPtr); } MoveToken(dndPtr); /* Move token to current drag point. */ return TCL_OK; } /* * ------------------------------------------------------------------------ * * DropOp -- * * Finishes the drag&drop transaction by dropping the data on the * selected target. Typically this operation is called from a * ButtonRelease event on a source widget. Note that a Leave message * is always sent to the target so that is can un-highlight itself. * The token is hidden and a drop message is sent to the target. * * Example: dnd drop .widget x y * * Results: * A standard Tcl result. * * Side Effects: * The token is hidden and a drop message is sent to the target. * * ------------------------------------------------------------------------ */ static int DropOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Winfo *windowPtr; Dnd *dndPtr; int x, y; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (!dndPtr->isSource) { Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin), "\" is not a registered drag&drop source.", (char *)NULL); return TCL_ERROR; } if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) { return TCL_ERROR; } if (dndPtr->rootPtr == NULL) { Tcl_AppendResult(interp, "no current drag&drop operation for source \"", Tk_PathName(dndPtr->tkwin), (char *)NULL); return TCL_ERROR; } dndPtr->x = x; /* Save drag&drop location */ dndPtr->y = y; if (!dndPtr->canceled) { windowPtr = OverTarget(dndPtr); if ((!dndPtr->pkgCmdInProgress) && (windowPtr != NULL)) { if (windowPtr->matches != NULL) { SetProperty(dndPtr->tkwin, dndPtr->dataPtr->formatsAtom, windowPtr->matches); } MoveToken(dndPtr); /* Move token to current drag point. */ RelayDropEvent(dndPtr, windowPtr, x, y); #ifdef notdef dndPtr->tokenPtr->nSteps = 10; FadeToken(dndPtr); #endif } else { dndPtr->tokenPtr->nSteps = 10; SnapToken(dndPtr); } } StopActiveCursor(dndPtr); FreeWinfo(dndPtr->rootPtr); dndPtr->rootPtr = NULL; return TCL_OK; } /* * ------------------------------------------------------------------------ * * GetdataOp -- * * Registers one or more data formats with a drag&drop source. * Each format has a Tcl command associated with it. This command * is automatically invoked whenever data is pulled from the source * to a target requesting the data in that particular format. The * purpose of the Tcl command is to get the data from in the * application. When the Tcl command is invoked, special percent * substitutions are made: * * %t Drag&drop transaction timestamp. * %W Source widget. * * If a converter (command) already exists for a format, it * overwrites the existing command. * * Example: dnd getdata .widget "color" { %W cget -bg } * * Results: * A standard Tcl result. * * ------------------------------------------------------------------------ */ static int GetdataOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Dnd *dndPtr; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; int isNew; char *cmd; register int i; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (argc == 3) { /* Return list of source data formats */ for (hPtr = Tcl_FirstHashEntry(&(dndPtr->getDataTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { Tcl_AppendElement(interp, Tcl_GetHashKey(&(dndPtr->getDataTable), hPtr)); } return TCL_OK; } if (argc == 4) { hPtr = Tcl_FindHashEntry(&(dndPtr->getDataTable), argv[3]); if (hPtr == NULL) { Tcl_AppendResult(interp, "can't find handler for format \"", argv[3], "\" for source \"", Tk_PathName(dndPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd == NULL) { cmd = ""; } Tcl_SetResult(interp, cmd, TCL_STATIC); return TCL_OK; } for (i = 3; i < argc; i += 2) { hPtr = Tcl_CreateHashEntry(&(dndPtr->getDataTable), argv[i], &isNew); if (!isNew) { cmd = Tcl_GetHashValue(hPtr); free(cmd); } cmd = strdup(argv[i+1]); Tcl_SetHashValue(hPtr, (ClientData)cmd); } return TCL_OK; } /* * ------------------------------------------------------------------------ * * NamesOp -- * * Returns the names of all the drag&drop managers. If either * a "-source" or "-target" switch is present, only the names of * managers acting as sources or targets respectively are returned. * A pattern argument may also be given. Only those managers * matching the pattern are returned. * * Examples: dnd names * dnd names -source * dnd names -target * dnd names .*label * Results: * A standard Tcl result. A Tcl list of drag&drop manager * names is returned. * * ------------------------------------------------------------------------ */ static int NamesOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { ThreadData *dataPtr = (ThreadData *)clientData; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; Dnd *dndPtr; int findSources, findTargets; findSources = findTargets = TRUE; if (argc > 2) { if ((argv[2][0] == '-') && (strcmp(argv[2], "-source") == 0)) { findTargets = FALSE; argc--, argv++; } else if ((argv[2][0] == '-') && (strcmp(argv[2], "-target") == 0)) { findSources = FALSE; argc--, argv++; } } for (hPtr = Tcl_FirstHashEntry(&(dataPtr->dndTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { dndPtr = (Dnd *)Tcl_GetHashValue(hPtr); if ((argc > 3) && (!Tcl_StringMatch(Tk_PathName(dndPtr->tkwin), argv[3]))) { continue; } if (((findSources) && (dndPtr->isSource)) || ((findTargets) && (dndPtr->isTarget))) { Tcl_AppendElement(interp, Tk_PathName(dndPtr->tkwin)); } } return TCL_OK; } /* * ------------------------------------------------------------------------ * * PullOp -- * * Pulls the current data from the source in the given format. * application. * * dnd pull .widget format data * * Results: * A standard Tcl result. * * Side Effects: * Invokes the target's data converter to store the data. * * ------------------------------------------------------------------------ */ static int PullOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Dnd *dndPtr; /* drag&drop source record */ int result; char *formatCmd; Tcl_HashEntry *hPtr; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (!dndPtr->isTarget) { Tcl_AppendResult(interp, "widget \"", Tk_PathName(dndPtr->tkwin), "\" is not a registered drag&drop target.", (char *)NULL); return TCL_ERROR; } hPtr = Tcl_FindHashEntry(&(dndPtr->setDataTable), argv[3]); if (hPtr == NULL) { Tcl_AppendResult(interp, "can't find format \"", argv[3], "\" in target \"", Tk_PathName(dndPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } formatCmd = (char *)Tcl_GetHashValue(hPtr); if (dndPtr->pendingPtr == NULL) { Tcl_AppendResult(interp, "no drop in progress", (char *)NULL); return TCL_ERROR; } CompleteDataTransaction(dndPtr, argv[3], dndPtr->pendingPtr); result = TCL_OK; if (Tcl_DStringLength(&(dndPtr->pendingPtr->dString)) > 0) { Tcl_DString dString; SubstDescriptors subs[3]; /* Now run the "setdata" callback to save the data. */ subs[0].letter = 'v'; subs[0].value = Tcl_DStringValue(&(dndPtr->pendingPtr->dString)); subs[1].letter = 'W'; subs[1].value = Tk_PathName(dndPtr->tkwin); subs[2].letter = 't'; subs[2].value = Blt_Itoa(dndPtr->pendingPtr->timestamp); ExpandPercents(formatCmd, subs, 3, &dString); result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); } return result; } /* * ------------------------------------------------------------------------ * * SetdataOp -- * * Registers one or more data formats with a drag&drop target. * Each format has a Tcl command associated with it. This command * is automatically invoked whenever data arrives from a source * to be converted to that particular format. The purpose of the * command is to set the data somewhere in the application (either * using a Tcl command or variable). When the Tcl command is invoked, * special percent substitutions are made: * * %t Drag&drop transaction timestamp. * %W Target widget. * %v Data value transfered from the source to * be converted into the correct format. * * If a converter (command) already exists for a format, it * overwrites the existing command. * * Example: dnd setdata .widget color { . configure -bg %v } * * Results: * A standard Tcl result. * * ------------------------------------------------------------------------ */ static int SetdataOp(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { Dnd *dndPtr; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; int isNew; char *cmd; int i; if (GetDnd(clientData, interp, argv[2], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (argc == 3) { /* Show target handler data formats */ for (hPtr = Tcl_FirstHashEntry(&(dndPtr->setDataTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { Tcl_AppendElement(interp, Tcl_GetHashKey(&(dndPtr->setDataTable), hPtr)); } return TCL_OK; } if (argc == 4) { hPtr = Tcl_FindHashEntry(&(dndPtr->setDataTable), argv[3]); if (hPtr == NULL) { Tcl_AppendResult(interp, "can't find handler for format \"", argv[3], "\" for target \"", Tk_PathName(dndPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } cmd = (char *)Tcl_GetHashValue(hPtr); if (cmd == NULL) { cmd = ""; } Tcl_SetResult(interp, cmd, TCL_STATIC); return TCL_OK; } for (i = 3; i < argc; i += 2) { hPtr = Tcl_CreateHashEntry(&(dndPtr->setDataTable), argv[i], &isNew); if (!isNew) { cmd = Tcl_GetHashValue(hPtr); free(cmd); } cmd = strdup(argv[i+1]); Tcl_SetHashValue(hPtr, (ClientData)cmd); } AddTargetProperty(interp, dndPtr); return TCL_OK; } /* * ------------------------------------------------------------------------ * * RegisterOp -- * * dnd register .window * ------------------------------------------------------------------------ */ static int RegisterOp(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { ThreadData *dataPtr = (ThreadData *)clientData; Tk_Window tkwin; Tcl_HashEntry *hPtr; Dnd *dndPtr; int isNew; tkwin = Tk_NameToWindow(interp, argv[2], dataPtr->mainWindow); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Tcl_CreateHashEntry(&(dataPtr->dndTable), (char *)tkwin, &isNew); if (!isNew) { Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin), "\" is already registered as a drag&drop manager", (char *)NULL); return TCL_ERROR; } dndPtr = CreateDnd(interp, tkwin); dndPtr->hashPtr = hPtr; dndPtr->dataPtr = dataPtr; Tcl_SetHashValue(hPtr, (ClientData)dndPtr); if (Tk_ConfigureWidget(interp, dndPtr->tkwin, configSpecs, argc - 3, argv + 3, (char *)dndPtr, 0) != TCL_OK) { return TCL_ERROR; } if (ConfigureDnd(interp, dndPtr) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* * ------------------------------------------------------------------------ * * TokenWindowOp -- * * ------------------------------------------------------------------------ */ static int TokenWindowOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Dnd *dndPtr; int flags; if (GetDnd(clientData, interp, argv[3], &dndPtr) != TCL_OK) { return TCL_ERROR; } flags = 0; if (dndPtr->tokenPtr == NULL) { if (CreateToken(interp, dndPtr) != TCL_OK) { return TCL_ERROR; } } else { flags = TK_CONFIG_ARGV_ONLY; } if (ConfigureToken(interp, dndPtr, argc - 4, argv + 4, flags) != TCL_OK) { return TCL_ERROR; } Tcl_SetResult(interp, Tk_PathName(dndPtr->tokenPtr->tkwin), TCL_STATIC); return TCL_OK; } /* * ------------------------------------------------------------------------ * * TokenCgetOp -- * * Called to process an (argc,argv) list to configure (or * reconfigure) a drag&drop widget. * * ------------------------------------------------------------------------ */ /* ARGSUSED*/ static int TokenCgetOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; /* Not used. */ char **argv; { Dnd *dndPtr; if (GetDnd(clientData, interp, argv[3], &dndPtr) != TCL_OK) { return TCL_ERROR; } if (dndPtr->tokenPtr == NULL) { Tcl_AppendResult(interp, "no token created for \"", argv[3], "\"", (char *)NULL); return TCL_ERROR; } return Tk_ConfigureValue(interp, dndPtr->tokenPtr->tkwin, tokenConfigSpecs, (char *)dndPtr->tokenPtr, argv[4], TK_CONFIG_ARGV_ONLY); } /* * ------------------------------------------------------------------------ * * TokenConfigureOp -- * * ------------------------------------------------------------------------ */ static int TokenConfigureOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Token *tokenPtr; Dnd *dndPtr; int flags; if (GetDnd(clientData, interp, argv[3], &dndPtr) != TCL_OK) { return TCL_ERROR; } flags = TK_CONFIG_ARGV_ONLY; if (dndPtr->tokenPtr == NULL) { Tcl_AppendResult(interp, "no token created for \"", argv[3], "\"", (char *)NULL); return TCL_ERROR; } tokenPtr = dndPtr->tokenPtr; if (argc == 3) { return Tk_ConfigureInfo(interp, tokenPtr->tkwin, tokenConfigSpecs, (char *)tokenPtr, (char *)NULL, flags); } else if (argc == 4) { return Tk_ConfigureInfo(interp, tokenPtr->tkwin, tokenConfigSpecs, (char *)tokenPtr, argv[3], flags); } return ConfigureToken(interp, dndPtr, argc - 4, argv + 4, flags); } static Blt_OpSpec tokenOps[] = { {"cget", 2, (Blt_Operation)TokenCgetOp, 5, 5, "widget option",}, {"configure", 2, (Blt_Operation)TokenConfigureOp, 4, 0, "widget ?option value?...",}, {"window", 5, (Blt_Operation)TokenWindowOp, 4, 0, "widget ?option value?...",}, }; static int nTokenOps = sizeof(tokenOps) / sizeof(Blt_OpSpec); /* * ------------------------------------------------------------------------ * * TokenOp -- * * ------------------------------------------------------------------------ */ static int TokenOp(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Blt_Operation proc; int result; proc = Blt_GetOperation(interp, nTokenOps, tokenOps, BLT_OPER_ARG2, argc, argv); if (proc == NULL) { return TCL_ERROR; } result = (*proc) (clientData, interp, argc, argv); return result; } static Blt_OpSpec dndOps[] = { {"cancel", 2, (Blt_Operation)CancelOp, 3, 3, "widget",}, {"cget", 2, (Blt_Operation)CgetOp, 4, 4, "widget option",}, {"configure", 4, (Blt_Operation)ConfigureOp, 3, 0, "widget ?option value?...",}, #ifdef notdef {"convert", 4, (Blt_Operation)ConvertOp, 5, 5, "widget data format",}, #endif {"delete", 2, (Blt_Operation)DeleteOp, 3, 0,"?-source|-target? widget...",}, {"drag", 3, (Blt_Operation)DragOp, 3, 0, "widget x y ?token?",}, {"drop", 3, (Blt_Operation)DropOp, 3, 0, "widget x y ?token?",}, {"getdata", 1, (Blt_Operation)GetdataOp, 3, 0, "widget ?format command?",}, {"names", 1, (Blt_Operation)NamesOp, 2, 4, "?-source|-target? ?pattern?",}, {"pull", 1, (Blt_Operation)PullOp, 4, 4, "widget format",}, {"register", 1, (Blt_Operation)RegisterOp, 3, 0, "widget ?option value?...",}, {"select", 3, (Blt_Operation)SelectOp, 6, 6, "widget x y timestamp",}, {"setdata", 3, (Blt_Operation)SetdataOp, 3, 0, "widget ?format command?",}, {"token", 5, (Blt_Operation)TokenOp, 3, 0, "args...",}, }; static int nDndOps = sizeof(dndOps) / sizeof(Blt_OpSpec); /* * ------------------------------------------------------------------------ * * DndCmd -- * * Invoked by TCL whenever the user issues a drag&drop command. * * ------------------------------------------------------------------------ */ static int DndCmd(clientData, interp, argc, argv) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; /* current interpreter */ int argc; /* number of arguments */ char **argv; /* argument strings */ { Blt_Operation proc; int result; proc = Blt_GetOperation(interp, nDndOps, dndOps, BLT_OPER_ARG1, argc, argv); if (proc == NULL) { return TCL_ERROR; } result = (*proc) (clientData, interp, argc, argv); return result; } /* * ----------------------------------------------------------------------- * * DndInterpDeleteProc -- * * This is called when the interpreter hosting the "dnd" command is * destroyed. * * Results: * None. * * Side effects: * Destroys the hash table containing the drag&drop managers. * * ------------------------------------------------------------------------ */ /* ARGSUSED */ static void DndInterpDeleteProc(clientData, interp) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; { ThreadData *dataPtr = (ThreadData *)clientData; Dnd *dndPtr; Tcl_HashEntry *hPtr; Tcl_HashSearch cursor; for (hPtr = Tcl_FirstHashEntry(&(dataPtr->dndTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { dndPtr = (Dnd *)Tcl_GetHashValue(hPtr); dndPtr->hashPtr = NULL; DestroyDnd(dndPtr); } Tcl_DeleteHashTable(&(dataPtr->dndTable)); Tcl_DeleteAssocData(interp, DND_THREAD_KEY); free((char *)dataPtr); } static ThreadData * GetDndCmdData(interp) Tcl_Interp *interp; { ThreadData *dataPtr; Tcl_InterpDeleteProc *proc; dataPtr = (ThreadData *)Tcl_GetAssocData(interp, DND_THREAD_KEY, &proc); if (dataPtr == NULL) { Display *display; Tk_Window tkwin; dataPtr = (ThreadData *)malloc(sizeof(ThreadData)); assert(dataPtr); tkwin = Tk_MainWindow(interp); display = Tk_Display(tkwin); dataPtr->mainWindow = tkwin; dataPtr->display = display; Tcl_SetAssocData(interp, DND_THREAD_KEY, DndInterpDeleteProc, (ClientData)dataPtr); Tcl_InitHashTable(&(dataPtr->dndTable), TCL_ONE_WORD_KEYS); dataPtr->mesgAtom = XInternAtom(display, "BLT Dnd Message", False); dataPtr->targetAtom = XInternAtom(display, "BLT Dnd Target", False); dataPtr->formatsAtom = XInternAtom(display, "BLT Dnd Formats",False); dataPtr->commAtom = XInternAtom(display, "BLT Dnd CommData", False); #ifdef HAVE_XDND dataPtr->XdndActionListAtom = XInternAtom(display, "XdndActionList", False); dataPtr->XdndAwareAtom = XInternAtom(display, "XdndAware", False); dataPtr->XdndEnterAtom = XInternAtom(display, "XdndEnter", False); dataPtr->XdndFinishedAtom = XInternAtom(display, "XdndFinished", False); dataPtr->XdndLeaveAtom = XInternAtom(display, "XdndLeave", False); dataPtr->XdndPositionAtom = XInternAtom(display, "XdndPosition", False); dataPtr->XdndSelectionAtom = XInternAtom(display, "XdndSelection", False); dataPtr->XdndStatusAtom = XInternAtom(display, "XdndStatus", False); dataPtr->XdndTypeListAtom = XInternAtom(display, "XdndTypeList", False); #endif /* HAVE_XDND */ } return dataPtr; } /* * ------------------------------------------------------------------------ * * Blt_DndInit -- * * Adds the drag&drop command to the given interpreter. Should * be invoked to properly install the command whenever a new * interpreter is created. * * ------------------------------------------------------------------------ */ int Blt_DndInit(interp) Tcl_Interp *interp; /* interpreter to be updated */ { static Blt_CmdSpec cmdSpec = { "dnd", DndCmd }; ThreadData *dataPtr; dataPtr = GetDndCmdData(interp); cmdSpec.clientData = (ClientData)dataPtr; if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) { return TCL_ERROR; } return TCL_OK; } /* * Registers bitmap outline of dragged data, used to indicate * what is being dragged by source. Bitmap is XOR-ed as cursor/token * is moved around the screen. */ static void Blt_DndSetOutlineBitmap(tkwin, bitmap, x, y) Tk_Window tkwin; Pixmap bitmap; int x, y; { } #ifdef HAVE_XDND static void XDndFreeFormats(handlerPtr) XDndHandler *handlerPtr; { if (handlerPtr->formatArr != NULL) { char **p; for (p = handlerPtr->formatArr; *p != NULL; p++) { XFree(*p); } free((char *)handlerPtr->formatArr); handlerPtr->formatArr = NULL; } } static char ** XDndGetFormats(handlerPtr, eventPtr) XDndHandler *handlerPtr; XEvent *eventPtr; { int flags; Window window; unsigned char *data; Atom typeAtom; Atom format; int nItems, bytesAfter; Atom *atomArr; char *nameArr[XDND_MAX_TYPES + 1]; Display *display; XDndFreeFormats(handlerPtr); display = eventPtr->xclient.display; window = eventPtr->xclient.data.l[0]; flags = eventPtr->xclient.data.l[1]; data = NULL; if (flags & 0x01) { result = XGetWindowProperty( display, /* Display of window. */ window, /* Window holding the property. */ handlerPtr->dataPtr->XdndTypeListAtom, /* Name of property. */ 0, /* Offset of data (for multiple reads). */ XDND_MAX_TYPES, /* Maximum number of items to read. */ False, /* If true, delete the property. */ XA_ATOM, /* Desired type of property. */ &typeAtom, /* (out) Actual type of the property. */ &format, /* (out) Actual format of the property. */ &nItems, /* (out) # of items in specified format. */ &bytesAfter, /* (out) # of bytes remaining to be read. */ (unsigned char **)&data); if ((result != Success) || (format != 32) || (typeAtom != XA_ATOM)) { if (data != NULL) { XFree((char *)data); return (char **)NULL; } } atomArr = (Atom *)data; nAtoms = nItems; } else { atomArr = &(eventPtr->xclient.data.l[2]); nAtoms = 3; } formatArr = (char **)calloc(nAtoms + 1, sizeof(char *)); for (i = 0; (i < nAtoms) && (atomArr[i] != None); i++) { formatArr[i] = XGetAtomName(display, atomArr[i]); } if (data != NULL) { XFree((char *)data); } handlerPtr->formatArr = formatArr; } static char * GetMatchingFormats(dndPtr, formatArr) Dnd *dndPtr; char **formatArr; { int nMatches; nMatches = 0; Tcl_DStringInit(&dString); for (p = formatArr; *p != NULL; p++) { for(hPtr = Tcl_FirstHashEntry(&(dndPtr->setDataTable), &cursor); hPtr != NULL; hPtr = Tcl_NextHashEntry(&cursor)) { fmt = Tcl_GetHashKey(&(dndPtr->setDataTable), hPtr); if ((*fmt == **p) && (strcmp(fmt, *p) == 0)) { Tcl_DStringAppendElement(&dString, *p); nMatches++; break; } } } if (nMatches > 0) { char *string; string = strdup(Tcl_DStringValue(&dString)); Tcl_DStringFree(&dString); return string; } return NULL; } static void XDndPointerEvent(handlerPtr, eventPtr) XDndHandler *handlerPtr; XEvent *eventPtr; { Tk_Window tkwin; int flags; Atom action; Window window; flags = 0; action = None; window = eventPtr->xclient.data.l[MESG_INDEX_XDND_WINDOW]; /* * If the XDND source has no formats specified, don't process any further. * Simply send a "no accept" action with the message. */ if (handlerPtr->formatArr != NULL) { Dnd *newPtr, *oldPtr; int point; int button, keyState; int x, y; char *formats; point = (int)eventPtr->xclient.data.l[MESG_INDEX_XDND_POINT]; UNPACK(point, x, y); /* * See if the mouse pointer currently over a drop target. We first * determine what Tk window is under the mouse, and then check if * that window is registered as a drop target. */ newPtr = NULL; tkwin = Tk_CoordsToWindow(x, y, handlerPtr->tkwin); if (tkwin != NULL) { Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&(handlerPtr->dataPtr->dndTable), (char *)tkwin); if (hPtr != NULL) { newPtr = (Dnd *)Tcl_GetHashValue(hPtr); if (!newPtr->isTarget) { newPtr = NULL; /* Not a DND target. */ } formats = GetMatchingFormats(newPtr, handlerPtr->formatArr); if (formats == NULL) { newPtr = NULL; /* Source has no matching formats. */ } } } button = keyState = 0; oldPtr = handlerPtr->dndPtr; resp = DRAG_CANCEL; if (newPtr == oldPtr) { if ((oldPtr != NULL) && (oldPtr->motionCmd != NULL)) { resp = InvokeCallback(oldPtr, oldPtr->motionCmd, x, y, formats, button, keyState); } } else { if ((oldPtr != NULL) && (oldPtr->leaveCmd != NULL)) { InvokeCallback(oldPtr, oldPtr->leaveCmd, x, y, formats, button, keyState); } if ((newPtr != NULL) && (newPtr->enterCmd != NULL)) { resp = InvokeCallback(newPtr, newPtr->enterCmd, x, y, formats, button, keyState); } handlerPtr->dndPtr = newPtr; /* * Save the current mouse position, since we get them from the * drop message. */ newPtr->x = x; newPtr->y = y; } if (formats != NULL) { free(formats); } flags = XDND_FLAGS_WANT_POSITION_MSGS; if (resp) { flags |= XDND_FLAGS_ACCEPT_DROP; action = handlerPtr->dataPtr->XdndActionCopyAtom; } } /* Target-to-Source: Drag result message. */ SendClientMsg(handlerPtr->display, window, handlerPtr->dataPtr->XdndStatusAtom, handlerPtr->window, flags, 0, 0, action); } static void XDndDropEvent(handlerPtr, eventPtr) XDndHandler *handlerPtr; XEvent *eventPtr; { Tk_Window tkwin; int flags; Atom action; Window window; int timestamp; flags = 0; action = None; window = eventPtr->xclient.data.l[MESG_INDEX_XDND_WINDOW]; timestamp = eventPtr->xclient.data.l[MESG_INDEX_XDND_TIMESTAMP]; /* * If no formats were specified for the XDND source or if the last * motion event did not place the mouse over a valid drop target, * don't process any further. Simply send a "no accept" action with * the message. */ if ((handlerPtr->formatArr != NULL) && (handlerPtr->dndPtr != NULL)) { int button, keyState; Dnd *dndPtr = handlerPtr->dndPtr; DropPending pending; int resp; button = keyState = 0; /* Protocol doesn't supply this information. */ /* Set up temporary bookkeeping for the drop transaction */ memset (&pending, 0, sizeof(pending)); pending.window = window; pending.display = eventPtr->xclient.display; pending.timestamp = timestamp; pending.protocol = PROTO_XDND; pending.packetSize = GetMaxPropertySize(pending.display); Tcl_DStringInit(&(pending.dString)); formats = GetMatchingFormats(handlerPtr->dndPtr, handlerPtr->formatArr); if (formats == NULL) { } dndPtr->pendingPtr = &pending; resp = AcceptDrop(dndPtr, dndPtr->x, dndPtr->y, formats, button, keyState, action); dndPtr->pendingPtr = NULL; if (resp) { flags |= XDND_FLAGS_ACCEPT_DROP; action = handlerPtr->dataPtr->XdndActionCopyAtom; } } /* Target-to-Source: Drag result message. */ SendClientMsg(handlerPtr->display, window, handlerPtr->dataPtr->XdndStatusAtom, handlerPtr->window, flags, 0, 0, action); } /* * ------------------------------------------------------------------------ * * XDndProtoEventProc -- * * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received * on a registered drag&drop source widget. * * ------------------------------------------------------------------------ */ static int XDndProtoEventProc(clientData, eventPtr) ClientData clientData; /* Drag&drop record. */ XEvent *eventPtr; /* Event description. */ { ThreadData *dataPtr = (ThreadData *)clientData; Tk_Window tkwin; Tcl_HashEntry *hPtr; XDndHandler *handlerPtr; int point; int x, y; Atom mesg; if (eventPtr->type != ClientMessage) { return 0; /* Not a ClientMessage event. */ } /* Was the recipient a registered toplevel window? */ hPtr = Tcl_FindHashEntry(&(dataPtr->handlerTable), (char *)eventPtr->xclient.window); if (hPtr == NULL) { return 0; /* No handler registered with window. */ } handlerPtr = (XDndHandler *)Tcl_GetHashValue(hPtr); mesg = eventPtr->xclient.message_type; if (mesg == dataPtr->XdndEnterAtom) { XDndGetFormats(handlerPtr, eventPtr); handlerPtr->dndPtr = NULL; } else if (mesg == dataPtr->XdndPositionAtom) { XDndPointerEvent(handlerPtr, eventPtr); } else if (mesg == dataPtr->XdndLeaveAtom) { XDndFreeFormats(handlerPtr); /* Free up any collected formats. */ if (handlerPtr->dndPtr != NULL) { InvokeCallback(handlerPtr->dndPtr, handlerPtr->dndPtr->leaveCmd, -1, -1, NULL, 0, 0); /* Send leave event to drop target. */ } } else if (mesg == dataPtr->XdndDropAtom) { XDndDropEvent(handlerPtr, eventPtr); } else { fprintf(stderr, "Unknown client message type = 0x%x\n", mesg); return 0; /* Unknown message type. */ } return 1; } static XDndHandler * XDndCreateHandler(dndPtr) Dnd *dndPtr; { Tk_Window tkwin; Window window; Tcl_HashEntry *hPtr; int isNew; XDndHandler *handlerPtr; /* * Find the containing toplevel of this window. See if an XDND * handler is already registered for it. */ tkwin = Blt_GetToplevel(dndPtr->tkwin); window = Blt_GetRealWindowId(tkwin); /* Use the wrapper window as * the real toplevel window. */ hPtr = Tcl_CreateHashEntry(&(dataPtr->XDndHandlerTable), (char *)window, &isNew); if (!isNew) { handlerPtr = (XDndHandler *)Tcl_GetHashEntry(hPtr); handlerPtr->refCount++; } else { handlerPtr = (XDndHandler *)malloc(sizeof(XDndHandler)); handlerPtr->tkwin = tkwin; handlerPtr->dndPtr = NULL; handlerPtr->refCount = 1; handlerPtr->dataPtr = dataPtr; /* FIXME */ SetProperty(window, dataPtr->XdndAwareAtom, "3"); Tcl_SetHashValue(hPtr, (ClientData)handlerPtr); } return handlerPtr; } static void XDndDeleteHandler(dndPtr) Dnd *dndPtr; { Tk_Window tkwin; Window window; Tcl_HashEntry *hPtr; tkwin = Blt_GetToplevel(dndPtr->tkwin); window = Blt_GetRealWindowId(tkwin); /* Use the wrapper window as the real * toplevel window. */ hPtr = Tcl_FindHashEntry(&(dataPtr->XDndHandlerTable), (char *)window); if (hPtr != NULL) { XDndHandler *handlerPtr; handlerPtr = (XDndHandler *)Tcl_GetHashEntry(hPtr); handlerPtr->refCount--; if (handlerPtr->refCount == 0) { XDndFreeFormats(handlerPtr); XDeleteProperty(dndPtr->display, window, dndPtr->dataPtr->XdndAwareAtom); Tcl_DeleteHashEntry(hPtr); free((char *)handlerPtr); } } } #endif /* HAVE_XDND */ #endif /* NO_DRAGDROP */ tkdesk-2.0/blt/bltUnixPipe.c0100644000175000007640000006676010020457430014127 0ustar jccjcc/* * bltUnixPipe.c -- * * Lifted from tclPipe.c and tclUnixPipe.c in the Tcl * distribution, this is the first step toward freedom from the * tyranny of the former Tcl_CreatePipeline API. * * This file contains the generic portion of the command channel * driver as well as various utility routines used in managing * subprocesses. * * [It's not clear why we needed a whole new API for I/O. Channels * are one of the few losing propositions in Tcl. While it's easy * to see that one needs to handle the different platform I/O * semantics in a coherent fashion, it's usually better to pick * an API from one of platforms (hopefully a mature, well-known model) * and crowbar the other platforms to follow that. At least then * you're working from a known set of sematics. With Tcl Channels, * no one's an expert and the interface is incomplete.] * * Copyright (c) 1997 by Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include "bltInt.h" #include #include #include "bltWait.h" #if (TCL_MAJOR_VERSION == 7) typedef pid_t Tcl_Pid; #define FILEHANDLER_USES_TCLFILES 1 #define Tcl_CreateFileHandler Tk_CreateFileHander #define Tcl_DeleteFileHandler Tk_DeleteFileHander static int Tcl_GetChannelHandle(channel, direction, clientDataPtr) Tcl_Channel channel; int direction; ClientData *clientDataPtr; { Tcl_File file; file = Tcl_GetChannelFile(channel, direction); if (file == NULL) { return TCL_ERROR; } *clientDataPtr = (ClientData)Tcl_GetFileInfo(file, NULL); return TCL_OK; } #else typedef int Tcl_File; #endif /* TCL_MAJOR_VERSION == 7 */ /* *---------------------------------------------------------------------- * * OpenFile -- * * Open a file for use in a pipeline. * * Results: * Returns a new TclFile handle or NULL on failure. * * Side effects: * May cause a file to be created on the file system. * *---------------------------------------------------------------------- */ static int OpenFile(fname, mode) char *fname; /* The name of the file to open. */ int mode; /* In what mode to open the file? */ { int fd; fd = open(fname, mode, 0666); if (fd != -1) { fcntl(fd, F_SETFD, FD_CLOEXEC); /* * If the file is being opened for writing, seek to the end * so we can append to any data already in the file. */ if (mode & O_WRONLY) { lseek(fd, 0, SEEK_END); } return fd; } return -1; } /* *---------------------------------------------------------------------- * * CreateTempFile -- * * This function creates a temporary file initialized with an * optional string, and returns a file handle with the file pointer * at the beginning of the file. * * Results: * A handle to a file. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int CreateTempFile(contents) char *contents; /* String to write into temp file, or NULL. */ { char fileName[L_tmpnam]; int fd; size_t length = (contents == NULL) ? 0 : strlen(contents); tmpnam(fileName); fd = OpenFile(fileName, O_RDWR | O_CREAT | O_TRUNC); unlink(fileName); if ((fd >= 0) && (length > 0)) { for (;;) { if (write(fd, contents, length) != -1) { break; } else if (errno != EINTR) { close(fd); return -1; } } lseek(fd, 0, SEEK_SET); } return fd; } /* *---------------------------------------------------------------------- * * CreatePipe -- * * Creates a pipe - simply calls the pipe() function. * * Results: * Returns 1 on success, 0 on failure. * * Side effects: * Creates a pipe. * *---------------------------------------------------------------------- */ static int CreatePipe(inFilePtr, outFilePtr) int *inFilePtr; /* (out) Descriptor for read side of pipe. */ int *outFilePtr; /* (out) Descriptor for write side of pipe. */ { int pipeIds[2]; if (pipe(pipeIds) != 0) { return 0; } fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC); fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC); *inFilePtr = pipeIds[0]; *outFilePtr = pipeIds[1]; return 1; } /* *---------------------------------------------------------------------- * * CloseFile -- * * Implements a mechanism to close a UNIX file. * * Results: * Returns 0 on success, or -1 on error, setting errno. * * Side effects: * The file is closed. * *---------------------------------------------------------------------- */ static int CloseFile(fd) int fd; /* File descriptor to be closed. */ { if ((fd == 0) || (fd == 1) || (fd == 2)) { return 0; /* Don't close stdin, stdout or stderr. */ } #if (TCL_MAJOR_VERSION > 7) Tcl_DeleteFileHandler(fd); #endif return close(fd); } /* *---------------------------------------------------------------------- * * RestoreSignals -- * * This procedure is invoked in a forked child process just before * exec-ing a new program to restore all signals to their default * settings. * * Results: * None. * * Side effects: * Signal settings get changed. * *---------------------------------------------------------------------- */ static void RestoreSignals() { #ifdef SIGABRT signal(SIGABRT, SIG_DFL); #endif #ifdef SIGALRM signal(SIGALRM, SIG_DFL); #endif #ifdef SIGFPE signal(SIGFPE, SIG_DFL); #endif #ifdef SIGHUP signal(SIGHUP, SIG_DFL); #endif #ifdef SIGILL signal(SIGILL, SIG_DFL); #endif #ifdef SIGINT signal(SIGINT, SIG_DFL); #endif #ifdef SIGPIPE signal(SIGPIPE, SIG_DFL); #endif #ifdef SIGQUIT signal(SIGQUIT, SIG_DFL); #endif #ifdef SIGSEGV signal(SIGSEGV, SIG_DFL); #endif #ifdef SIGTERM signal(SIGTERM, SIG_DFL); #endif #ifdef SIGUSR1 signal(SIGUSR1, SIG_DFL); #endif #ifdef SIGUSR2 signal(SIGUSR2, SIG_DFL); #endif #ifdef SIGCHLD signal(SIGCHLD, SIG_DFL); #endif #ifdef SIGCONT signal(SIGCONT, SIG_DFL); #endif #ifdef SIGTSTP signal(SIGTSTP, SIG_DFL); #endif #ifdef SIGTTIN signal(SIGTTIN, SIG_DFL); #endif #ifdef SIGTTOU signal(SIGTTOU, SIG_DFL); #endif } /* *---------------------------------------------------------------------- * * SetupStdFile -- * * Set up stdio file handles for the child process, using the * current standard channels if no other files are specified. * If no standard channel is defined, or if no file is associated * with the channel, then the corresponding standard fd is closed. * * Results: * Returns 1 on success, or 0 on failure. * * Side effects: * Replaces stdio fds. * *---------------------------------------------------------------------- */ static int SetupStdFile(fd, type) int fd; /* File descriptor to dup, or -1. */ int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */ { int targetFd = 0; /* Initializations here needed only to */ int direction = 0; /* prevent warnings about using uninitialized * variables. */ switch (type) { case TCL_STDIN: targetFd = 0; direction = TCL_READABLE; break; case TCL_STDOUT: targetFd = 1; direction = TCL_WRITABLE; break; case TCL_STDERR: targetFd = 2; direction = TCL_WRITABLE; break; } if (fd < 0) { Tcl_Channel channel; channel = Tcl_GetStdChannel(type); if (channel) { Tcl_GetChannelHandle(channel, direction, (ClientData *)&fd); } } if (fd >= 0) { if (fd != targetFd) { if (dup2(fd, targetFd) == -1) { return 0; } /* * Must clear the close-on-exec flag for the target FD, since * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on * the target FD. */ fcntl(targetFd, F_SETFD, 0); } else { /* * Since we aren't dup'ing the file, we need to explicitly clear * the close-on-exec flag. */ fcntl(fd, F_SETFD, 0); } } else { close(targetFd); } return 1; } /* *---------------------------------------------------------------------- * * CreateProcess -- * * Create a child process that has the specified files as its * standard input, output, and error. The child process runs * asynchronously and runs with the same environment variables * as the creating process. * * The path is searched to find the specified executable. * * Results: * The return value is TCL_ERROR and an error message is left in * interp->result if there was a problem creating the child * process. Otherwise, the return value is TCL_OK and *pidPtr is * filled with the process id of the child process. * * Side effects: * A process is created. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static int CreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, pidPtr) Tcl_Interp *interp; /* Interpreter in which to leave errors that * occurred when creating the child process. * Error messages from the child process * itself are sent to errorFile. */ int argc; /* Number of arguments in following array. */ char **argv; /* Array of argument strings. argv[0] * contains the name of the executable * converted to native format (using the * Tcl_TranslateFileName call). Additional * arguments have not been converted. */ int inputFile; /* If non-NULL, gives the file to use as * input for the child process. If inputFile * file is not readable or is NULL, the child * will receive no standard input. */ int outputFile; /* If non-NULL, gives the file that * receives output from the child process. If * outputFile file is not writeable or is * NULL, output from the child will be * discarded. */ int errorFile; /* If non-NULL, gives the file that * receives errors from the child process. If * errorFile file is not writeable or is NULL, * errors from the child will be discarded. * errorFile may be the same as outputFile. */ int *pidPtr; /* If this procedure is successful, pidPtr * is filled with the process id of the child * process. */ { int errPipeIn, errPipeOut; int joinThisError, count, status, fd; char errSpace[200]; int pid; errPipeIn = errPipeOut = -1; pid = -1; /* * Create a pipe that the child can use to return error * information if anything goes wrong. */ if (CreatePipe(&errPipeIn, &errPipeOut) == 0) { Tcl_AppendResult(interp, "can't create pipe: ", Tcl_PosixError(interp), (char *)NULL); goto error; } joinThisError = (errorFile == outputFile); pid = fork(); if (pid == 0) { fd = errPipeOut; /* * Set up stdio file handles for the child process. */ if (!SetupStdFile(inputFile, TCL_STDIN) || !SetupStdFile(outputFile, TCL_STDOUT) || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR)) || (joinThisError && ((dup2(1, 2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) { sprintf(errSpace, "%dforked process can't set up input/output: ", errno); write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } /* * Close the input side of the error pipe. */ RestoreSignals(); execvp(argv[0], &argv[0]); sprintf(errSpace, "%dcan't execute \"%.150s\": ", errno, argv[0]); write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } if (pid == -1) { Tcl_AppendResult(interp, "can't fork child process: ", Tcl_PosixError(interp), (char *)NULL); goto error; } /* * Read back from the error pipe to see if the child started * up OK. The info in the pipe (if any) consists of a decimal * errno value followed by an error message. */ CloseFile(errPipeOut); errPipeOut = -1; fd = errPipeIn; count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1)); if (count > 0) { char *end; errSpace[count] = 0; errno = strtol(errSpace, &end, 10); Tcl_AppendResult(interp, end, Tcl_PosixError(interp), (char *)NULL); goto error; } CloseFile(errPipeIn); *pidPtr = pid; return TCL_OK; error: if (pid != -1) { /* * Reap the child process now if an error occurred during its * startup. */ Tcl_WaitPid((Tcl_Pid)pid, &status, WNOHANG); } if (errPipeIn >= 0) { CloseFile(errPipeIn); } if (errPipeOut >= 0) { CloseFile(errPipeOut); } return TCL_ERROR; } /* *---------------------------------------------------------------------- * * FileForRedirect -- * * This procedure does much of the work of parsing redirection * operators. It handles "@" if specified and allowed, and a file * name, and opens the file if necessary. * * Results: * The return value is the descriptor number for the file. If an * error occurs then NULL is returned and an error message is left * in interp->result. Several arguments are side-effected; see * the argument list below for details. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr) Tcl_Interp *interp; /* Intepreter to use for error reporting. */ char *spec; /* Points to character just after * redirection character. */ char *arg; /* Pointer to entire argument containing * spec: used for error reporting. */ int atOK; /* Non-zero means that '@' notation can be * used to specify a channel, zero means that * it isn't. */ char *nextArg; /* Next argument in argc/argv array, if needed * for file name or channel name. May be * NULL. */ int flags; /* Flags to use for opening file or to * specify mode for channel. */ int *skipPtr; /* Filled with 1 if redirection target was * in spec, 2 if it was in nextArg. */ int *closePtr; /* Filled with one if the caller should * close the file when done with it, zero * otherwise. */ { int writing = (flags & O_WRONLY); Tcl_Channel chan; int fd; int direction; *skipPtr = 1; if ((atOK != 0) && (*spec == '@')) { spec++; if (*spec == '\0') { spec = nextArg; if (spec == NULL) { goto badLastArg; } *skipPtr = 2; } chan = Tcl_GetChannel(interp, spec, NULL); if (chan == NULL) { return -1; } direction = (writing) ? TCL_WRITABLE : TCL_READABLE; if (Tcl_GetChannelHandle(chan, direction, (ClientData *)&fd) != TCL_OK) { fd = -1; } if (fd < 0) { Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan), "\" wasn't opened for ", ((writing) ? "writing" : "reading"), (char *)NULL); return -1; } if (writing) { /* * Be sure to flush output to the file, so that anything * written by the child appears after stuff we've already * written. */ Tcl_Flush(chan); } } else { char *name; Tcl_DString nameString; if (*spec == '\0') { spec = nextArg; if (spec == NULL) { goto badLastArg; } *skipPtr = 2; } name = Tcl_TranslateFileName(interp, spec, &nameString); if (name != NULL) { fd = OpenFile(name, flags); } else { fd = -1; } Tcl_DStringFree(&nameString); if (fd < 0) { Tcl_AppendResult(interp, "can't ", ((writing) ? "write" : "read"), " file \"", spec, "\": ", Tcl_PosixError(interp), (char *)NULL); return -1; } *closePtr = 1; } return fd; badLastArg: Tcl_AppendResult(interp, "can't specify \"", arg, "\" as last word in command", (char *)NULL); return -1; } /* *---------------------------------------------------------------------- * * Blt_CreatePipeline -- * * Given an argc/argv array, instantiate a pipeline of processes * as described by the argv. * * Results: * The return value is a count of the number of new processes * created, or -1 if an error occurred while creating the pipeline. * *pidArrayPtr is filled in with the address of a dynamically * allocated array giving the ids of all of the processes. It * is up to the caller to free this array when it isn't needed * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in * with the file id for the input pipe for the pipeline (if any): * the caller must eventually close this file. If outPipePtr * isn't NULL, then *outPipePtr is filled in with the file id * for the output pipe from the pipeline: the caller must close * this file. If errPipePtr isn't NULL, then *errPipePtr is filled * with a file id that may be used to read error output after the * pipeline completes. * * Side effects: * Processes and pipes are created. * *---------------------------------------------------------------------- */ int Blt_CreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr, outPipePtr, errPipePtr) Tcl_Interp *interp; /* Interpreter to use for error reporting. */ int argc; /* Number of entries in argv. */ char **argv; /* Array of strings describing commands in * pipeline plus I/O redirection with <, * <<, >, etc. Argv[argc] must be NULL. */ int **pidArrayPtr; /* Word at *pidArrayPtr gets filled in with * address of array of pids for processes * in pipeline (first pid is first process * in pipeline). */ int *inPipePtr; /* If non-NULL, input to the pipeline comes * from a pipe (unless overridden by * redirection in the command). The file * id with which to write to this pipe is * stored at *inPipePtr. NULL means command * specified its own input source. */ int *outPipePtr; /* If non-NULL, output to the pipeline goes * to a pipe, unless overriden by redirection * in the command. The file id with which to * read frome this pipe is stored at * *outPipePtr. NULL means command specified * its own output sink. */ int *errPipePtr; /* If non-NULL, all stderr output from the * pipeline will go to a temporary file * created here, and a descriptor to read * the file will be left at *errPipePtr. * The file will be removed already, so * closing this descriptor will be the end * of the file. If this is NULL, then * all stderr output goes to our stderr. * If the pipeline specifies redirection * then the file will still be created * but it will never get any data. */ { int *pidPtr = NULL; /* Points to malloc-ed array holding all * the pids of child processes. */ int nPids; /* Actual number of processes that exist * at *pidPtr right now. */ int cmdCount; /* Count of number of distinct commands * found in argc/argv. */ char *inputLiteral = NULL; /* If non-null, then this points to a * string containing input data (specified * via <<) to be piped to the first process * in the pipeline. */ int inputFd = -1; /* If != NULL, gives file to use as input for * first process in pipeline (specified via < * or <@). */ int inputClose = 0; /* If non-zero, then inputFd should be * closed when cleaning up. */ int outputFd = -1; /* Writable file for output from last command * in pipeline (could be file or pipe). NULL * means use stdout. */ int outputClose = 0; /* If non-zero, then outputFd should be * closed when cleaning up. */ int errorFd = -1; /* Writable file for error output from all * commands in pipeline. NULL means use * stderr. */ int errorClose = 0; /* If non-zero, then errorFd should be * closed when cleaning up. */ char *p; int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput; Tcl_DString execBuffer; int pipeIn; int curInFd, curOutFd, curErrFd; if (inPipePtr != NULL) { *inPipePtr = -1; } if (outPipePtr != NULL) { *outPipePtr = -1; } if (errPipePtr != NULL) { *errPipePtr = -1; } Tcl_DStringInit(&execBuffer); pipeIn = curInFd = curOutFd = -1; nPids = 0; /* * First, scan through all the arguments to figure out the structure * of the pipeline. Process all of the input and output redirection * arguments and remove them from the argument list in the pipeline. * Count the number of distinct processes (it's the number of "|" * arguments plus one) but don't remove the "|" arguments because * they'll be used in the second pass to seperate the individual * child processes. Cannot start the child processes in this pass * because the redirection symbols may appear anywhere in the * command line -- e.g., the '<' that specifies the input to the * entire pipe may appear at the very end of the argument list. */ lastBar = -1; cmdCount = 1; for (i = 0; i < argc; i++) { skip = 0; p = argv[i]; switch (*p++) { case '|': if (*p == '&') { p++; } if (*p == '\0') { if ((i == (lastBar + 1)) || (i == (argc - 1))) { Tcl_SetResult(interp, "illegal use of | or |& in command", TCL_STATIC); goto error; } } lastBar = i; cmdCount++; break; case '<': if (inputClose != 0) { inputClose = 0; CloseFile(inputFd); } if (*p == '<') { inputFd = -1; inputLiteral = p + 1; skip = 1; if (*inputLiteral == '\0') { inputLiteral = argv[i + 1]; if (inputLiteral == NULL) { Tcl_AppendResult(interp, "can't specify \"", argv[i], "\" as last word in command", (char *)NULL); goto error; } skip = 2; } } else { inputLiteral = NULL; inputFd = FileForRedirect(interp, p, 1, argv[i], argv[i + 1], O_RDONLY, &skip, &inputClose); if (inputFd < 0) { goto error; } } break; case '>': atOK = 1; flags = O_WRONLY | O_CREAT | O_TRUNC; errorToOutput = 0; if (*p == '>') { p++; atOK = 0; flags = O_WRONLY | O_CREAT; } if (*p == '&') { if (errorClose != 0) { errorClose = 0; CloseFile(errorFd); } errorToOutput = 1; p++; } if (outputClose != 0) { outputClose = 0; CloseFile(outputFd); } outputFd = FileForRedirect(interp, p, atOK, argv[i], argv[i + 1], flags, &skip, &outputClose); if (outputFd < 0) { goto error; } if (errorToOutput) { errorClose = 0; errorFd = outputFd; } break; case '2': if (*p != '>') { break; } p++; atOK = 1; flags = O_WRONLY | O_CREAT | O_TRUNC; if (*p == '>') { p++; atOK = 0; flags = O_WRONLY | O_CREAT; } if (errorClose != 0) { errorClose = 0; CloseFile(errorFd); } errorFd = FileForRedirect(interp, p, atOK, argv[i], argv[i + 1], flags, &skip, &errorClose); if (errorFd < 0) { goto error; } break; } if (skip != 0) { for (j = i + skip; j < argc; j++) { argv[j - skip] = argv[j]; } argc -= skip; i -= 1; } } if (inputFd == -1) { if (inputLiteral != NULL) { /* * The input for the first process is immediate data coming from * Tcl. Create a temporary file for it and put the data into the * file. */ inputFd = CreateTempFile(inputLiteral); if (inputFd < 0) { Tcl_AppendResult(interp, "can't create input file for command: ", Tcl_PosixError(interp), (char *)NULL); goto error; } inputClose = 1; } else if (inPipePtr != NULL) { /* * The input for the first process in the pipeline is to * come from a pipe that can be written from by the caller. */ if (CreatePipe(&inputFd, inPipePtr) == 0) { Tcl_AppendResult(interp, "can't create input pipe for command: ", Tcl_PosixError(interp), (char *)NULL); goto error; } inputClose = 1; } else { /* * The input for the first process comes from stdin. */ inputFd = 0; } } if (outputFd == -1) { if (outPipePtr != NULL) { /* * Output from the last process in the pipeline is to go to a * pipe that can be read by the caller. */ if (CreatePipe(outPipePtr, &outputFd) == 0) { Tcl_AppendResult(interp, "can't create output pipe for command: ", Tcl_PosixError(interp), (char *)NULL); goto error; } outputClose = 1; } else { /* * The output for the last process goes to stdout. */ outputFd = 1; } } if (errorFd == -1) { if (errPipePtr != NULL) { /* * Stderr from the last process in the pipeline is to go to a * pipe that can be read by the caller. */ if (CreatePipe(errPipePtr, &errorFd) == 0) { Tcl_AppendResult(interp, "can't create error pipe for command: ", Tcl_PosixError(interp), (char *)NULL); goto error; } errorClose = 1; } else { /* * Errors from the pipeline go to stderr. */ errorFd = 2; } } /* * Scan through the argc array, creating a process for each * group of arguments between the "|" characters. */ Tcl_ReapDetachedProcs(); pidPtr = (int *)ckalloc((unsigned)(cmdCount * sizeof(int))); curInFd = inputFd; lastArg = 0; /* Suppress compiler warning */ for (i = 0; i < argc; i = lastArg + 1) { int joinThisError; int pid; /* * Convert the program name into native form. */ argv[i] = Tcl_TranslateFileName(interp, argv[i], &execBuffer); if (argv[i] == NULL) { goto error; } /* * Find the end of the current segment of the pipeline. */ joinThisError = 0; for (lastArg = i; lastArg < argc; lastArg++) { if (argv[lastArg][0] == '|') { if (argv[lastArg][1] == '\0') { break; } if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) { joinThisError = 1; break; } } } argv[lastArg] = NULL; /* * If this is the last segment, use the specified outputFile. * Otherwise create an intermediate pipe. pipeIn will become the * curInFile for the next segment of the pipe. */ if (lastArg == argc) { curOutFd = outputFd; } else { if (CreatePipe(&pipeIn, &curOutFd) == 0) { Tcl_AppendResult(interp, "can't create pipe: ", Tcl_PosixError(interp), (char *)NULL); goto error; } } if (joinThisError != 0) { curErrFd = curOutFd; } else { curErrFd = errorFd; } if (CreateProcess(interp, lastArg - i, argv + i, curInFd, curOutFd, curErrFd, &pid) != TCL_OK) { goto error; } Tcl_DStringFree(&execBuffer); pidPtr[nPids] = pid; nPids++; /* * Close off our copies of file descriptors that were set up for * this child, then set up the input for the next child. */ if ((curInFd >= 0) && (curInFd != inputFd)) { CloseFile(curInFd); } curInFd = pipeIn; pipeIn = -1; if ((curOutFd >= 0) && (curOutFd != outputFd)) { CloseFile(curOutFd); } curOutFd = -1; } *pidArrayPtr = pidPtr; /* * All done. Cleanup open files lying around and then return. */ cleanup: Tcl_DStringFree(&execBuffer); if (inputClose) { CloseFile(inputFd); } if (outputClose) { CloseFile(outputFd); } if (errorClose) { CloseFile(errorFd); } return nPids; /* * An error occurred. There could have been extra files open, such * as pipes between children. Clean them all up. Detach any child * processes that have been created. */ error: if (pipeIn >= 0) { CloseFile(pipeIn); } if ((curOutFd >= 0) && (curOutFd != outputFd)) { CloseFile(curOutFd); } if ((curInFd >= 0) && (curInFd != inputFd)) { CloseFile(curInFd); } if ((inPipePtr != NULL) && (*inPipePtr >= 0)) { CloseFile(*inPipePtr); *inPipePtr = -1; } if ((outPipePtr != NULL) && (*outPipePtr >= 0)) { CloseFile(*outPipePtr); *outPipePtr = -1; } if ((errPipePtr != NULL) && (*errPipePtr >= 0)) { CloseFile(*errPipePtr); *errPipePtr = -1; } if (pidPtr != NULL) { for (i = 0; i < nPids; i++) { if (pidPtr[i] != -1) { #if (TCL_MAJOR_VERSION == 7) Tcl_DetachPids(1, &pidPtr[i]); #else Tcl_DetachPids(1, (Tcl_Pid *)&pidPtr[i]); #endif } } ckfree((char *)pidPtr); } nPids = -1; goto cleanup; } tkdesk-2.0/blt/bltUtil.c0100644000175000007640000006552310020457430013277 0ustar jccjcc/* * bltUtil.c -- * * This module implements utility procedures for the BLT * toolkit. * * Copyright 1991-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #include "bltInt.h" #include #ifndef WIN32 #include #endif #if defined(__STDC__) #include #else #include #endif #ifndef NDEBUG void Blt_Assert(testExpr, fileName, lineNumber) char *testExpr; char *fileName; int lineNumber; { #ifdef WINDEBUG PurifyPrintf("line %d of %s: Assert \"%s\" failed", lineNumber, fileName, testExpr); #endif fprintf(stderr, "line %d of %s: Assert \"%s\" failed", lineNumber, fileName, testExpr); abort(); } /*ARGSUSED*/ void Blt_Panic TCL_VARARGS_DEF(char *, arg1) { va_list argList; char *format; format = TCL_VARARGS_START(char *, arg1, argList); vfprintf(stderr, format, argList); fprintf(stderr, "\n"); fflush(stderr); abort(); } #endif /* *---------------------------------------------------------------------- * * Blt_AdjustViewport -- * * Adjusts the offsets of the viewport according to the scroll mode. * This is to accommodate both "listbox" and "canvas" style scrolling. * * "canvas" The viewport scrolls within the range of world * coordinates. This way the viewport always displays * a full page of the world. If the world is smaller * than the viewport, then (bizarrely) the world and * viewport are inverted so that the world moves up * and down within the viewport. * * "listbox" The viewport can scroll beyond the range of world * coordinates. Every entry can be displayed at the * top of the viewport. This also means that the * scrollbar thumb weirdly shrinks as the last entry * is scrolled upward. * * Results: * The corrected offset is returned. * *---------------------------------------------------------------------- */ int Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits, scrollMode) int offset, worldSize, windowSize; int scrollUnits; int scrollMode; { switch (scrollMode) { case SCROLL_MODE_CANVAS: /* * Canvas-style scrolling allows the world to be scrolled * within the window. */ if (worldSize < windowSize) { if ((worldSize - offset) > windowSize) { offset = worldSize - windowSize; } if (offset > 0) { offset = 0; } } else { if ((offset + windowSize) > worldSize) { offset = worldSize - windowSize; } if (offset < 0) { offset = 0; } } break; case SCROLL_MODE_LISTBOX: if (offset < 0) { offset = 0; } if (offset >= worldSize) { offset = worldSize - scrollUnits; } break; case SCROLL_MODE_HIERBOX: /* * Hierbox-style scrolling allows the world to be scrolled * within the window. */ if ((offset + windowSize) > worldSize) { offset = worldSize - windowSize; } if (offset < 0) { offset = 0; } break; } return offset; } int Blt_GetScrollInfo(interp, argc, argv, offsetPtr, worldSize, windowSize, scrollUnits, scrollMode) Tcl_Interp *interp; int argc; char **argv; int *offsetPtr; int worldSize, windowSize; int scrollUnits; int scrollMode; { char c; unsigned int length; int offset; int count; double fract; offset = *offsetPtr; c = argv[0][0]; length = strlen(argv[0]); if ((c == 's') && (strncmp(argv[0], "scroll", length) == 0)) { /* scroll number unit/page */ if (Tcl_GetInt(interp, argv[1], &count) != TCL_OK) { return TCL_ERROR; } c = argv[2][0]; length = strlen(argv[2]); if ((c == 'u') && (strncmp(argv[2], "units", length) == 0)) { fract = (double)count *scrollUnits; } else if ((c == 'p') && (strncmp(argv[2], "pages", length) == 0)) { /* A page is 90% of the view-able window. */ fract = (double)count *windowSize * 0.9; } else { Tcl_AppendResult(interp, "unknown \"scroll\" units \"", argv[2], "\"", (char *)NULL); return TCL_ERROR; } offset += (int)fract; } else if ((c == 'm') && (strncmp(argv[0], "moveto", length) == 0)) { /* moveto fraction */ if (Tcl_GetDouble(interp, argv[1], &fract) != TCL_OK) { return TCL_ERROR; } offset = (int)(worldSize * fract); } else { /* Treat like "scroll units" */ if (Tcl_GetInt(interp, argv[0], &count) != TCL_OK) { return TCL_ERROR; } fract = (double)count *scrollUnits; offset += (int)fract; return TCL_OK; } *offsetPtr = Blt_AdjustViewport(offset, worldSize, windowSize, scrollUnits, scrollMode); return TCL_OK; } /* * ---------------------------------------------------------------------- * * Blt_UpdateScrollbar -- * * Invoke a Tcl command to the scrollbar, defining the new * position and length of the scroll. See the Tk documentation * for further information on the scrollbar. It is assumed the * scrollbar command prefix is valid. * * Results: * None. * * Side Effects: * Scrollbar is commanded to change position and/or size. * * ---------------------------------------------------------------------- */ void Blt_UpdateScrollbar(interp, scrollCmd, firstFract, lastFract) Tcl_Interp *interp; char *scrollCmd; /* scrollbar command */ double firstFract, lastFract; { char string[200]; Tcl_DString cmdString; Tcl_DStringInit(&cmdString); Tcl_DStringAppend(&cmdString, scrollCmd, -1); sprintf(string, " %f %f", firstFract, lastFract); Tcl_DStringAppend(&cmdString, string, -1); if (Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString)) != TCL_OK) { Tk_BackgroundError(interp); } Tcl_DStringFree(&cmdString); } /* *---------------------------------------------------------------------- * * Blt_ConfigModified -- * * Given the configuration specifications and one or more option * patterns (terminated by a NULL), indicate if any of the matching * configuration options has been reset. * * Results: * Returns 1 if one of the options has changed, 0 otherwise. * *---------------------------------------------------------------------- */ int Blt_ConfigModified TCL_VARARGS_DEF(Tk_ConfigSpec *, arg1) { va_list argList; Tk_ConfigSpec *specs; register Tk_ConfigSpec *specPtr; register char *option; specs = TCL_VARARGS_START(Tk_ConfigSpec *, arg1, argList); while ((option = va_arg(argList, char *)) != NULL) { for (specPtr = specs; specPtr->type != TK_CONFIG_END; specPtr++) { if ((Tcl_StringMatch(specPtr->argvName, option)) && (specPtr->specFlags & TK_CONFIG_OPTION_SPECIFIED)) { va_end(argList); return 1; } } } va_end(argList); return 0; } void Blt_DStringAppendElements TCL_VARARGS_DEF(Tcl_DString *, arg1) { va_list argList; Tcl_DString *dStrPtr; register char *elem; dStrPtr = TCL_VARARGS_START(Tcl_DString *, arg1, argList); while ((elem = va_arg(argList, char *)) != NULL) { Tcl_DStringAppendElement(dStrPtr, elem); } va_end(argList); } /* *---------------------------------------------------------------------- * * Blt_LookupOperation -- * * Performs a binary search on the array of command operation * specifications to find a partial, anchored match for the * given operation string. * * Results: * If the string matches unambiguously the index of the specification * in the array is returned. If the string does not match, even * as an abbreviation, any operation, -1 is returned. If the string * matches, but ambiguously -2 is returned. * *---------------------------------------------------------------------- */ int Blt_LookupOperation(specArr, nSpecs, operation) Blt_OpSpec specArr[]; int nSpecs; char *operation; /* Name of minor operation to search for */ { Blt_OpSpec *specPtr; char c; register int high, low, median; register int compare, length; low = 0; high = nSpecs - 1; c = operation[0]; length = strlen(operation); while (low <= high) { median = (low + high) >> 1; specPtr = specArr + median; /* Test the first character */ compare = c - specPtr->name[0]; if (!compare) { /* Now test the entire string */ compare = strncmp(operation, specPtr->name, length); if ((compare == 0) && (length < specPtr->minChars)) { return -2; /* Ambiguous operation name */ } } if (compare < 0) { high = median - 1; } else if (compare > 0) { low = median + 1; } else { return median; /* Op found. */ } } return -1; /* Can't find operation */ } /* *---------------------------------------------------------------------- * * Blt_GetOperation -- * * Find the command operation given a string name. This is useful * where a group of command operations have the same argument * signature. * * Results: * If found, a pointer to the procedure (function pointer) is * returned. Otherwise NULL is returned and an error message * containing a list of the possible commands is returned in * interp->result. * *---------------------------------------------------------------------- */ Blt_Operation Blt_GetOperation(interp, nSpecs, specArr, iname, nArgs, argArr) Tcl_Interp *interp; /* Interpreter to report errors to */ int nSpecs; /* Number of specifications in array */ Blt_OpSpec specArr[]; /* Operation specification array */ Blt_OpIndex iname; /* Index of the operation name argument */ int nArgs; /* Number of arguments in the argument vector. * This includes any prefixed arguments */ char *argArr[]; /* Argument vector */ { Blt_OpSpec *specPtr; char *string; register int i; register int n; if (nArgs <= iname) { /* No operation argument */ Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL); usage: Tcl_AppendResult(interp, "should be one of...", (char *)NULL); for (n = 0; n < nSpecs; n++) { Tcl_AppendResult(interp, "\n ", (char *)NULL); for (i = 0; i < iname; i++) { Tcl_AppendResult(interp, argArr[i], " ", (char *)NULL); } specPtr = specArr + n; Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, (char *)NULL); } return NULL; } string = argArr[iname]; n = Blt_LookupOperation(specArr, nSpecs, string); if (n == -2) { char c; int length; Tcl_AppendResult(interp, "ambiguous", (char *)NULL); if (iname > 2) { Tcl_AppendResult(interp, " ", argArr[iname - 1], (char *)NULL); } Tcl_AppendResult(interp, " operation \"", string, "\" matches:", (char *)NULL); c = string[0]; length = strlen(string); for (n = 0; n < nSpecs; n++) { specPtr = specArr + n; if ((c == specPtr->name[0]) && (strncmp(string, specPtr->name, length) == 0)) { Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL); } } return NULL; } else if (n == -1) { /* Can't find operation, display help */ Tcl_AppendResult(interp, "bad", (char *)NULL); if (iname > 2) { Tcl_AppendResult(interp, " ", argArr[iname - 1], (char *)NULL); } Tcl_AppendResult(interp, " operation \"", string, "\": ", (char *)NULL); goto usage; } specPtr = specArr + n; if ((nArgs < specPtr->minArgs) || ((specPtr->maxArgs > 0) && (nArgs > specPtr->maxArgs))) { Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL); for (i = 0; i < iname; i++) { Tcl_AppendResult(interp, argArr[i], " ", (char *)NULL); } Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"", (char *)NULL); return NULL; } return specPtr->proc; } #ifndef HAVE_STRDUP /* *---------------------------------------------------------------------- * * strdup -- * * Create a copy of the string from heap storage. * * Results: * Returns a pointer to the need string copy. * *---------------------------------------------------------------------- */ char * strdup(string) char *string; { char *newPtr; newPtr = (char *)malloc(sizeof(char) * (strlen(string) + 1)); if (newPtr != NULL) { strcpy(newPtr, string); } return (newPtr); } #endif /*HAVE_STRDUP*/ #ifdef notdef #ifndef HAVE_STRCASECMP static unsigned char lcase[] = { '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057', '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077', '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137', '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147', '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177', '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207', '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237', '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247', '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257', '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', '\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347', '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', '\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337', '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347', '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377', }; /* *---------------------------------------------------------------------- * * strcasecmp -- * * Compare two strings, disregarding case. * * Results: * Returns a signed integer representing the following: * * zero - two strings are equal * negative - first string is less than second * positive - first string is greater than second * *---------------------------------------------------------------------- */ int strcasecmp(str1, str2) CONST char *str1; CONST char *str2; { unsigned char *s = (unsigned char *)str1; unsigned char *t = (unsigned char *)str2; for ( /* empty */ ; (lcase[*s] == lcase[*t]); s++, t++) { if (*s == '\0') { return 0; } } return (lcase[*s] - lcase[*t]); } /* *---------------------------------------------------------------------- * * strncasecmp -- * * Compare two strings, disregarding case, up to a given length. * * Results: * Returns a signed integer representing the following: * * zero - two strings are equal * negative - first string is less than second * positive - first string is greater than second * *---------------------------------------------------------------------- */ int strncasecmp(str1, str2, length) CONST char *str1; CONST char *str2; size_t length; { register unsigned char *s = (unsigned char *)str1; register unsigned char *t = (unsigned char *)str2; for ( /* empty */ ; (length > 0); s++, t++, length--) { if (lcase[*s] != lcase[*t]) { return (lcase[*s] - lcase[*t]); } if (*s == '\0') { return 0; } } return 0; } #endif /* !HAVE_STRNCASECMP */ #endif static char stringRep[200]; char * Blt_Itoa(value) int value; { sprintf(stringRep, "%d", value); return stringRep; } char * Blt_Dtoa(interp, value) Tcl_Interp *interp; double value; { Tcl_PrintDouble(interp, value, stringRep); return stringRep; } /* -------------- */ /* *---------------------------------------------------------------------- * * Blt_GetPrivateGCFromDrawable -- * * Like Tk_GetGC, but doesn't share the GC with any other widget. * This is needed because the certain GC parameters (like dashes) * can not be set via XCreateGC, therefore there is no way for * Tk's hashing mechanism to recognize that two such GCs differ. * * Results: * A new GC is returned. * *---------------------------------------------------------------------- */ GC Blt_GetPrivateGCFromDrawable(tkwin, drawable, gcMask, valuePtr) Tk_Window tkwin; Drawable drawable; unsigned long gcMask; XGCValues *valuePtr; { Pixmap pixmap; GC newGC; pixmap = None; if (drawable == None) { Drawable root; int depth; root = RootWindow(Tk_Display(tkwin), Tk_ScreenNumber(tkwin)); depth = Tk_Depth(tkwin); if (depth == DefaultDepth(Tk_Display(tkwin), Tk_ScreenNumber(tkwin))) { drawable = root; } else { pixmap = Tk_GetPixmap(Tk_Display(tkwin), root, 1, 1, depth); drawable = pixmap; } } #ifdef WIN32 newGC = Blt_EmulateXCreateGC(Tk_Display(tkwin), drawable, gcMask, valuePtr); #else newGC = XCreateGC(Tk_Display(tkwin), drawable, gcMask, valuePtr); #endif if (pixmap != None) { Tk_FreePixmap(Tk_Display(tkwin), pixmap); } return (newGC); } /* *---------------------------------------------------------------------- * * Blt_GetPrivateGC -- * * Like Tk_GetGC, but doesn't share the GC with any other widget. * This is needed because the certain GC parameters (like dashes) * can not be set via XCreateGC, therefore there is no way for * Tk's hashing mechanism to recognize that two such GCs differ. * * Results: * A new GC is returned. * *---------------------------------------------------------------------- */ GC Blt_GetPrivateGC(tkwin, gcMask, valuePtr) Tk_Window tkwin; unsigned long gcMask; XGCValues *valuePtr; { return Blt_GetPrivateGCFromDrawable(tkwin, Tk_WindowId(tkwin), gcMask, valuePtr); } void Blt_FreePrivateGC(display, gc) Display *display; GC gc; { Tk_FreeXId(display, (XID) XGContextFromGC(gc)); XFreeGC(display, gc); } #ifndef WIN32 void Blt_SetDashes(display, gc, dashesPtr) Display *display; GC gc; Dashes *dashesPtr; { XSetDashes(display, gc, dashesPtr->offset, dashesPtr->valueArr, dashesPtr->nValues); } #endif /* *---------------------------------------------------------------------- * * Blt_Toplevel -- * * Climbs up the widget hierarchy to find the top level window of * the window given. * * Results: * Returns the Tk_Window of the toplevel widget. * *---------------------------------------------------------------------- */ Tk_Window Blt_Toplevel(tkwin) register Tk_Window tkwin; { while (!Tk_IsTopLevel(tkwin)) { tkwin = Tk_Parent(tkwin); } return tkwin; } #ifdef WIN32 int Blt_ReparentWindow(display, window, newParent, x, y) Display *display; Window window, newParent; int x, y; { XReparentWindow(display, window, newParent, x, y); return TCL_OK; } #else /* ARGSUSED */ static int XReparentWindowErrorProc(clientData, errEventPtr) ClientData clientData; XErrorEvent *errEventPtr; { int *errorPtr = (int *)clientData; *errorPtr = TCL_ERROR; return 0; } int Blt_ReparentWindow(display, window, newParent, x, y) Display *display; Window window, newParent; int x, y; { Tk_ErrorHandler handler; int result; int any = -1; result = TCL_OK; handler = Tk_CreateErrorHandler(display, any, X_ReparentWindow, any, XReparentWindowErrorProc, (ClientData)&result); XReparentWindow(display, window, newParent, x, y); Tk_DeleteErrorHandler(handler); XSync(display, False); return result; } #endif /* *---------------------------------------------------------------------- * * Blt_ConfigureWidgetComponent -- * * Configures a component of a widget. This is useful for * widgets that have multiple components which aren't uniquely * identified by a Tk_Window. It allows us, for example, set * resources for axes of the graph widget. The graph really has * only one window, but its convenient to specify components in a * hierarchy of options. * * *graph.x.logScale yes * *graph.Axis.logScale yes * *graph.temperature.scaleSymbols yes * *graph.Element.scaleSymbols yes * * This is really a hack to work around the limitations of the Tk * resource database. It creates a temporary window, needed to * call Tk_ConfigureWidget, using the name of the component. * * Results: * A standard Tcl result. * * Side Effects: * A temporary window is created merely to pass to Tk_ConfigureWidget. * *---------------------------------------------------------------------- */ int Blt_ConfigureWidgetComponent(interp, parent, name, class, specsPtr, argc, argv, widgRec, flags) Tcl_Interp *interp; Tk_Window parent; /* Window to associate with component */ char name[]; /* Name of component */ char class[]; Tk_ConfigSpec *specsPtr; int argc; char *argv[]; char *widgRec; int flags; { Tk_Window tkwin; int result; char *tempName; int temporary = 0; tempName = strdup(name); /* Window name can't start with an upper case letter */ tempName[0] = tolower(name[0]); /* * Create component if a child window by the component's name * doesn't already exist. */ tkwin = Blt_FindChild(parent, tempName); if (tkwin == NULL) { tkwin = Tk_CreateWindow(interp, parent, tempName, (char *)NULL); temporary = 1; } if (tkwin == NULL) { Tcl_AppendResult(interp, "can't find window in \"", Tk_PathName(parent), "\"", (char *)NULL); return TCL_ERROR; } assert(Tk_Depth(tkwin) == Tk_Depth(parent)); free(tempName); Tk_SetClass(tkwin, class); result = Tk_ConfigureWidget(interp, tkwin, specsPtr, argc, argv, widgRec, flags); if (temporary) { Tk_DestroyWindow(tkwin); } return (result); } /* * The hash table below is used to keep track of all the Tk_Uids created * so far. */ static Tcl_HashTable uidTable; static int uidInitialized = 0; /* *---------------------------------------------------------------------- * * Blt_GetUid -- * * Given a string, returns a unique identifier for the string. * A reference count is maintained, so that the identifier * can be freed when it is not needed any more. This can be used * in many places to replace Tcl_GetUid. * * Results: * This procedure returns a Tk_Uid corresponding to the "string" * argument. The Tk_Uid has a string value identical to string * (strcmp will return 0), but it's guaranteed that any other * calls to this procedure with a string equal to "string" will * return exactly the same result (i.e. can compare Tk_Uid * *values* directly, without having to call strcmp on what they * point to). * * Side effects: * New information may be entered into the identifier table. * *---------------------------------------------------------------------- */ Tk_Uid Blt_GetUid(string) char *string; /* String to convert. */ { int isNew; Tcl_HashEntry *hPtr; int refCount; if (!uidInitialized) { Tcl_InitHashTable(&uidTable, TCL_STRING_KEYS); uidInitialized = 1; } hPtr = Tcl_CreateHashEntry(&uidTable, string, &isNew); if (isNew) { refCount = 0; } else { refCount = (int)Tcl_GetHashValue(hPtr); } refCount++; Tcl_SetHashValue(hPtr, (ClientData)refCount); return (Tk_Uid) Tcl_GetHashKey(&uidTable, hPtr); } /* *---------------------------------------------------------------------- * * Blt_FreeUid -- * * Frees the Tk_Uid if there are no more clients using this * identifier. * * Results: * None. * * Side effects: * The identifier may be deleted from the identifier table. * *---------------------------------------------------------------------- */ void Blt_FreeUid(uid) Tk_Uid uid; /* Identifier to release. */ { Tcl_HashEntry *hPtr; if (!uidInitialized) { Tcl_InitHashTable(&uidTable, TCL_STRING_KEYS); uidInitialized = 1; } hPtr = Tcl_FindHashEntry(&uidTable, uid); if (hPtr) { int refCount; refCount = (int)Tcl_GetHashValue(hPtr); refCount--; if (refCount == 0) { Tcl_DeleteHashEntry(hPtr); } else { Tcl_SetHashValue(hPtr, (ClientData)refCount); } } else { fprintf(stderr, "tried to release unknown identifier \"%s\"\n", uid); } } /* *---------------------------------------------------------------------- * * Blt_FindUid -- * * Returns a Tk_Uid associated with a given string, if one exists. * * Results: * A Tk_Uid for the string if one exists. Otherwise NULL. * *---------------------------------------------------------------------- */ Tk_Uid Blt_FindUid(string) char *string; /* String to find. */ { Tcl_HashEntry *hPtr; if (!uidInitialized) { Tcl_InitHashTable(&uidTable, TCL_STRING_KEYS); uidInitialized = 1; } hPtr = Tcl_FindHashEntry(&uidTable, string); if (hPtr == NULL) { return NULL; } return (Tk_Uid) Tcl_GetHashKey(&uidTable, hPtr); } /* *-------------------------------------------------------------- * * Blt_InitHexTable -- * * Table index for the hex values. Initialized once, first time. * Used for translation value or delimiter significance lookup. * * We build the table at run time for several reasons: * * 1. portable to non-ASCII machines. * 2. still reentrant since we set the init flag after setting * table. * 3. easier to extend. * 4. less prone to bugs. * * Results: * None. * *-------------------------------------------------------------- */ void Blt_InitHexTable(tab) char tab[]; { tab['0'] = 0; tab['1'] = 1; tab['2'] = 2; tab['3'] = 3; tab['4'] = 4; tab['5'] = 5; tab['6'] = 6; tab['7'] = 7; tab['8'] = 8; tab['9'] = 9; tab['a'] = tab['A'] = 10; tab['b'] = tab['B'] = 11; tab['c'] = tab['C'] = 12; tab['d'] = tab['D'] = 13; tab['e'] = tab['E'] = 14; tab['f'] = tab['F'] = 15; } tkdesk-2.0/blt/bltWait.h0100644000175000007640000001403210020457430013260 0ustar jccjcc/* * bltWait.h -- * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #ifndef _BLT_WAIT_H #define _BLT_WAIT_H #ifdef HAVE_WAITFLAGS_H # include #endif #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif /* * Define EINPROGRESS in terms of WSAEINPROGRESS. */ #ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS #endif /* * If ENOTSUP is not defined, define it to a value that will never occur. */ #ifndef ENOTSUP #define ENOTSUP -1030507 #endif /* * The following defines redefine the Windows Socket errors as * BSD errors so Tcl_PosixError can do the right thing. */ #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif #ifndef EALREADY #define EALREADY 149 /* operation already in progress */ #endif #ifndef ENOTSOCK #define ENOTSOCK 95 /* Socket operation on non-socket */ #endif #ifndef EDESTADDRREQ #define EDESTADDRREQ 96 /* Destination address required */ #endif #ifndef EMSGSIZE #define EMSGSIZE 97 /* Message too long */ #endif #ifndef EPROTOTYPE #define EPROTOTYPE 98 /* Protocol wrong type for socket */ #endif #ifndef ENOPROTOOPT #define ENOPROTOOPT 99 /* Protocol not available */ #endif #ifndef EPROTONOSUPPORT #define EPROTONOSUPPORT 120 /* Protocol not supported */ #endif #ifndef ESOCKTNOSUPPORT #define ESOCKTNOSUPPORT 121 /* Socket type not supported */ #endif #ifndef EOPNOTSUPP #define EOPNOTSUPP 122 /* Operation not supported on socket */ #endif #ifndef EPFNOSUPPORT #define EPFNOSUPPORT 123 /* Protocol family not supported */ #endif #ifndef EAFNOSUPPORT #define EAFNOSUPPORT 124 /* Address family not supported */ #endif #ifndef EADDRINUSE #define EADDRINUSE 125 /* Address already in use */ #endif #ifndef EADDRNOTAVAIL #define EADDRNOTAVAIL 126 /* Can't assign requested address */ #endif #ifndef ENETDOWN #define ENETDOWN 127 /* Network is down */ #endif #ifndef ENETUNREACH #define ENETUNREACH 128 /* Network is unreachable */ #endif #ifndef ENETRESET #define ENETRESET 129 /* Network dropped connection on reset */ #endif #ifndef ECONNABORTED #define ECONNABORTED 130 /* Software caused connection abort */ #endif #ifndef ECONNRESET #define ECONNRESET 131 /* Connection reset by peer */ #endif #ifndef ENOBUFS #define ENOBUFS 132 /* No buffer space available */ #endif #ifndef EISCONN #define EISCONN 133 /* Socket is already connected */ #endif #ifndef ENOTCONN #define ENOTCONN 134 /* Socket is not connected */ #endif #ifndef ESHUTDOWN #define ESHUTDOWN 143 /* Can't send after socket shutdown */ #endif #ifndef ETOOMANYREFS #define ETOOMANYREFS 144 /* Too many references: can't splice */ #endif #ifndef ETIMEDOUT #define ETIMEDOUT 145 /* Connection timed out */ #endif #ifndef ECONNREFUSED #define ECONNREFUSED 146 /* Connection refused */ #endif #ifndef ELOOP #define ELOOP 90 /* Symbolic link loop */ #endif #ifndef EHOSTDOWN #define EHOSTDOWN 147 /* Host is down */ #endif #ifndef EHOSTUNREACH #define EHOSTUNREACH 148 /* No route to host */ #endif #ifndef ENOTEMPTY #define ENOTEMPTY 93 /* directory not empty */ #endif #ifndef EUSERS #define EUSERS 94 /* Too many users (for UFS) */ #endif #ifndef EDQUOT #define EDQUOT 49 /* Disc quota exceeded */ #endif #ifndef ESTALE #define ESTALE 151 /* Stale NFS file handle */ #endif #ifndef EREMOTE #define EREMOTE 66 /* The object is remote */ #endif #ifndef WIFEXITED # define WIFEXITED(stat) (((*((int *) &(stat))) & 0xff) == 0) #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff) #endif #ifndef WIFSIGNALED # define WIFSIGNALED(stat) (((*((int *) &(stat)))) && ((*((int *) &(stat))) == ((*((int *) &(stat))) & 0x00ff))) #endif #ifndef WTERMSIG # define WTERMSIG(stat) ((*((int *) &(stat))) & 0x7f) #endif #ifndef WIFSTOPPED # define WIFSTOPPED(stat) (((*((int *) &(stat))) & 0xff) == 0177) #endif #ifndef WSTOPSIG # define WSTOPSIG(stat) (((*((int *) &(stat))) >> 8) & 0xff) #endif /* * Define constants for waitpid() system call if they aren't defined * by a system header file. */ #ifndef WNOHANG # define WNOHANG 1 #endif #ifndef WUNTRACED # define WUNTRACED 2 #endif /* * The type of the status returned by wait varies from UNIX system * to UNIX system. The macro below defines it: */ #ifdef AIX # define WAIT_STATUS_TYPE pid_t #else #ifndef NO_UNION_WAIT # define WAIT_STATUS_TYPE union wait #else # define WAIT_STATUS_TYPE int #endif #endif /* * Supply definitions for macros to query wait status, if not already * defined in header files above. */ #ifndef WIFEXITED # define WIFEXITED(stat) (((*((int *) &(stat))) & 0xff) == 0) #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff) #endif #ifndef WIFSIGNALED # define WIFSIGNALED(stat) (((*((int *) &(stat)))) && ((*((int *) &(stat))) == ((*((int *) &(stat))) & 0x00ff))) #endif #ifndef WTERMSIG # define WTERMSIG(stat) ((*((int *) &(stat))) & 0x7f) #endif #ifndef WIFSTOPPED # define WIFSTOPPED(stat) (((*((int *) &(stat))) & 0xff) == 0177) #endif #ifndef WSTOPSIG # define WSTOPSIG(stat) (((*((int *) &(stat))) >> 8) & 0xff) #endif #endif /* _BLT_WAIT_H */ tkdesk-2.0/blt/bltWindow.c0100644000175000007640000012605710020457430013631 0ustar jccjcc/* * bltWindow.c -- * * This module implements additional window functionality for * the BLT toolkit, such as transparent Tk windows, * and reparenting Tk windows. * * Copyright 1991-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. */ #include "bltInt.h" #include #include #include struct TkIdStack; struct TkErrorHandler; struct TkSelectionInfo; struct TkClipboardTarget; typedef struct TkWindow TkWindow; typedef struct TkMainInfo TkMainInfo; typedef struct TkEventHandler TkEventHandler; typedef struct TkSelHandler TkSelHandler; typedef struct TkWinInfo TkWinInfo; typedef struct TkClassProcs TkClassProcs; typedef struct TkWindowPrivate TkWindowPrivate; typedef struct TkGrabEvent TkGrabEvent; typedef struct TkColormap TkColormap; typedef struct TkStressedCmap TkStressedCmap; #ifdef XNQueryInputStyle #define TK_USE_INPUT_METHODS #endif #ifndef TK_REPARENTED #define TK_REPARENTED 0 #endif #if (TK_VERSION_NUMBER >= _VERSION(8,1,0)) /* * One of the following structures is maintained for each display * containing a window managed by Tk. In part, the structure is * used to store thread-specific data, since each thread will have * its own TkDisplay structure. */ typedef struct TkDisplay { Display *display; /* Xlib's info about display. */ struct TkDisplay *nextPtr; /* Next in list of all displays. */ char *name; /* Name of display (with any screen * identifier removed). Malloc-ed. */ Time lastEventTime; /* Time of last event received for this * display. */ /* * Information used primarily by tk3d.c: */ int borderInit; /* 0 means borderTable needs initializing. */ Tcl_HashTable borderTable; /* Maps from color name to TkBorder * structure. */ /* * Information used by tkAtom.c only: */ int atomInit; /* 0 means stuff below hasn't been * initialized yet. */ Tcl_HashTable nameTable; /* Maps from names to Atom's. */ Tcl_HashTable atomTable; /* Maps from Atom's back to names. */ /* * Information used primarily by tkBind.c: */ int bindInfoStale; /* Non-zero means the variables in this * part of the structure are potentially * incorrect and should be recomputed. */ unsigned int modeModMask; /* Has one bit set to indicate the modifier * corresponding to "mode shift". If no * such modifier, than this is zero. */ unsigned int metaModMask; /* Has one bit set to indicate the modifier * corresponding to the "Meta" key. If no * such modifier, then this is zero. */ unsigned int altModMask; /* Has one bit set to indicate the modifier * corresponding to the "Meta" key. If no * such modifier, then this is zero. */ enum { LU_IGNORE, LU_CAPS, LU_SHIFT } lockUsage; /* Indicates how to interpret lock modifier. */ int numModKeyCodes; /* Number of entries in modKeyCodes array * below. */ KeyCode *modKeyCodes; /* Pointer to an array giving keycodes for * all of the keys that have modifiers * associated with them. Malloc'ed, but * may be NULL. */ /* * Information used by tkBitmap.c only: */ int bitmapInit; /* 0 means tables above need initializing. */ int bitmapAutoNumber; /* Used to number bitmaps. */ Tcl_HashTable bitmapNameTable; /* Maps from name of bitmap to the first * TkBitmap record for that name. */ Tcl_HashTable bitmapIdTable;/* Maps from bitmap id to the TkBitmap * structure for the bitmap. */ Tcl_HashTable bitmapDataTable; /* Used by Tk_GetBitmapFromData to map from * a collection of in-core data about a * bitmap to a reference giving an auto- * matically-generated name for the bitmap. */ /* * Information used by tkCanvas.c only: */ int numIdSearches; int numSlowSearches; /* * Used by tkColor.c only: */ int colorInit; /* 0 means color module needs initializing. */ TkStressedCmap *stressPtr; /* First in list of colormaps that have * filled up, so we have to pick an * approximate color. */ Tcl_HashTable colorNameTable; /* Maps from color name to TkColor structure * for that color. */ Tcl_HashTable colorValueTable; /* Maps from integer RGB values to TkColor * structures. */ /* * Used by tkCursor.c only: */ int cursorInit; /* 0 means cursor module need initializing. */ Tcl_HashTable cursorNameTable; /* Maps from a string name to a cursor to the * TkCursor record for the cursor. */ Tcl_HashTable cursorDataTable; /* Maps from a collection of in-core data * about a cursor to a TkCursor structure. */ Tcl_HashTable cursorIdTable; /* Maps from a cursor id to the TkCursor * structure for the cursor. */ char cursorString[20]; /* Used to store a cursor id string. */ Font cursorFont; /* Font to use for standard cursors. * None means font not loaded yet. */ /* * Information used by tkError.c only: */ struct TkErrorHandler *errorPtr; /* First in list of error handlers * for this display. NULL means * no handlers exist at present. */ int deleteCount; /* Counts # of handlers deleted since * last time inactive handlers were * garbage-collected. When this number * gets big, handlers get cleaned up. */ /* * Used by tkEvent.c only: */ struct TkWindowEvent *delayedMotionPtr; /* Points to a malloc-ed motion event * whose processing has been delayed in * the hopes that another motion event * will come along right away and we can * merge the two of them together. NULL * means that there is no delayed motion * event. */ /* * Information used by tkFocus.c only: */ int focusDebug; /* 1 means collect focus debugging * statistics. */ struct TkWindow *implicitWinPtr; /* If the focus arrived at a toplevel window * implicitly via an Enter event (rather * than via a FocusIn event), this points * to the toplevel window. Otherwise it is * NULL. */ struct TkWindow *focusPtr; /* Points to the window on this display that * should be receiving keyboard events. When * multiple applications on the display have * the focus, this will refer to the * innermost window in the innermost * application. This information isn't used * under Unix or Windows, but it's needed on * the Macintosh. */ /* * Information used by tkGC.c only: */ Tcl_HashTable gcValueTable; /* Maps from a GC's values to a TkGC structure * describing a GC with those values. */ Tcl_HashTable gcIdTable; /* Maps from a GC to a TkGC. */ int gcInit; /* 0 means the tables below need * initializing. */ /* * Information used by tkGeometry.c only: */ Tcl_HashTable maintainHashTable; /* Hash table that maps from a master's * Tk_Window token to a list of slaves * managed by that master. */ int geomInit; /* * Information used by tkGet.c only: */ Tcl_HashTable uidTable; /* Stores all Tk_Uids used in a thread. */ int uidInit; /* 0 means uidTable needs initializing. */ /* * Information used by tkGrab.c only: */ struct TkWindow *grabWinPtr; /* Window in which the pointer is currently * grabbed, or NULL if none. */ struct TkWindow *eventualGrabWinPtr; /* Value that grabWinPtr will have once the * grab event queue (below) has been * completely emptied. */ struct TkWindow *buttonWinPtr; /* Window in which first mouse button was * pressed while grab was in effect, or NULL * if no such press in effect. */ struct TkWindow *serverWinPtr; /* If no application contains the pointer then * this is NULL. Otherwise it contains the * last window for which we've gotten an * Enter or Leave event from the server (i.e. * the last window known to have contained * the pointer). Doesn't reflect events * that were synthesized in tkGrab.c. */ TkGrabEvent *firstGrabEventPtr; /* First in list of enter/leave events * synthesized by grab code. These events * must be processed in order before any other * events are processed. NULL means no such * events. */ TkGrabEvent *lastGrabEventPtr; /* Last in list of synthesized events, or NULL * if list is empty. */ int grabFlags; /* Miscellaneous flag values. See definitions * in tkGrab.c. */ /* * Information used by tkGrid.c only: */ int gridInit; /* 0 means table below needs initializing. */ Tcl_HashTable gridHashTable;/* Maps from Tk_Window tokens to * corresponding Grid structures. */ /* * Information used by tkImage.c only: */ int imageId; /* Value used to number image ids. */ /* * Information used by tkMacWinMenu.c only: */ int postCommandGeneration; /* * Information used by tkOption.c only. */ /* * Information used by tkPack.c only. */ int packInit; /* 0 means table below needs initializing. */ Tcl_HashTable packerHashTable; /* Maps from Tk_Window tokens to * corresponding Packer structures. */ /* * Information used by tkPlace.c only. */ int placeInit; /* 0 means tables below need initializing. */ Tcl_HashTable masterTable; /* Maps from Tk_Window toke to the Master * structure for the window, if it exists. */ Tcl_HashTable slaveTable; /* Maps from Tk_Window toke to the Slave * structure for the window, if it exists. */ /* * Information used by tkSelect.c and tkClipboard.c only: */ struct TkSelectionInfo *selectionInfoPtr; /* First in list of selection information * records. Each entry contains information * about the current owner of a particular * selection on this display. */ Atom multipleAtom; /* Atom for MULTIPLE. None means * selection stuff isn't initialized. */ Atom incrAtom; /* Atom for INCR. */ Atom targetsAtom; /* Atom for TARGETS. */ Atom timestampAtom; /* Atom for TIMESTAMP. */ Atom textAtom; /* Atom for TEXT. */ Atom compoundTextAtom; /* Atom for COMPOUND_TEXT. */ Atom applicationAtom; /* Atom for TK_APPLICATION. */ Atom windowAtom; /* Atom for TK_WINDOW. */ Atom clipboardAtom; /* Atom for CLIPBOARD. */ Tk_Window clipWindow; /* Window used for clipboard ownership and to * retrieve selections between processes. NULL * means clipboard info hasn't been * initialized. */ int clipboardActive; /* 1 means we currently own the clipboard * selection, 0 means we don't. */ struct TkMainInfo *clipboardAppPtr; /* Last application that owned clipboard. */ struct TkClipboardTarget *clipTargetPtr; /* First in list of clipboard type information * records. Each entry contains information * about the buffers for a given selection * target. */ /* * Information used by tkSend.c only: */ Tk_Window commTkwin; /* Window used for communication * between interpreters during "send" * commands. NULL means send info hasn't * been initialized yet. */ Atom commProperty; /* X's name for comm property. */ Atom registryProperty; /* X's name for property containing * registry of interpreter names. */ Atom appNameProperty; /* X's name for property used to hold the * application name on each comm window. */ /* * Information used by tkXId.c only: */ struct TkIdStack *idStackPtr; /* First in list of chunks of free resource * identifiers, or NULL if there are no free * resources. */ XID(*defaultAllocProc) _ANSI_ARGS_((Display *display)); /* Default resource allocator for display. */ struct TkIdStack *windowStackPtr; /* First in list of chunks of window * identifers that can't be reused right * now. */ int idCleanupScheduled; /* 1 means a call to WindowIdCleanup has * already been scheduled, 0 means it * hasn't. */ /* * Information used by tkUnixWm.c and tkWinWm.c only: */ int wmTracing; /* Used to enable or disable tracing in * this module. If tracing is enabled, * then information is printed on * standard output about interesting * interactions with the window manager. */ struct TkWmInfo *firstWmPtr;/* Points to first top-level window. */ struct TkWmInfo *foregroundWmPtr; /* Points to the foreground window. */ /* * Information maintained by tkWindow.c for use later on by tkXId.c: */ int destroyCount; /* Number of Tk_DestroyWindow operations * in progress. */ unsigned long lastDestroyRequest; /* Id of most recent XDestroyWindow request; * can re-use ids in windowStackPtr when * server has seen this request and event * queue is empty. */ /* * Information used by tkVisual.c only: */ TkColormap *cmapPtr; /* First in list of all non-default colormaps * allocated for this display. */ /* * Miscellaneous information: */ #ifdef TK_USE_INPUT_METHODS XIM inputMethod; /* Input method for this display */ #endif /* TK_USE_INPUT_METHODS */ Tcl_HashTable winTable; /* Maps from X window ids to TkWindow ptrs. */ int refCount; /* Reference count of how many Tk applications * are using this display. Used to clean up * the display when we no longer have any * Tk applications using it. */ } TkDisplay; #else /* * One of the following structures is maintained for each display * containing a window managed by Tk: */ typedef struct TkDisplay { Display *display; /* Xlib's info about display. */ struct TkDisplay *nextPtr; /* Next in list of all displays. */ char *name; /* Name of display (with any screen * identifier removed). Malloc-ed. */ Time lastEventTime; /* Time of last event received for this * display. */ /* * Information used primarily by tkBind.c: */ int bindInfoStale; /* Non-zero means the variables in this * part of the structure are potentially * incorrect and should be recomputed. */ unsigned int modeModMask; /* Has one bit set to indicate the modifier * corresponding to "mode shift". If no * such modifier, than this is zero. */ unsigned int metaModMask; /* Has one bit set to indicate the modifier * corresponding to the "Meta" key. If no * such modifier, then this is zero. */ unsigned int altModMask; /* Has one bit set to indicate the modifier * corresponding to the "Meta" key. If no * such modifier, then this is zero. */ enum { LU_IGNORE, LU_CAPS, LU_SHIFT } lockUsage; /* Indicates how to interpret lock modifier. */ int numModKeyCodes; /* Number of entries in modKeyCodes array * below. */ KeyCode *modKeyCodes; /* Pointer to an array giving keycodes for * all of the keys that have modifiers * associated with them. Malloc'ed, but * may be NULL. */ /* * Information used by tkError.c only: */ struct TkErrorHandler *errorPtr; /* First in list of error handlers * for this display. NULL means * no handlers exist at present. */ int deleteCount; /* Counts # of handlers deleted since * last time inactive handlers were * garbage-collected. When this number * gets big, handlers get cleaned up. */ /* * Information used by tkSend.c only: */ Tk_Window commTkwin; /* Window used for communication * between interpreters during "send" * commands. NULL means send info hasn't * been initialized yet. */ Atom commProperty; /* X's name for comm property. */ Atom registryProperty; /* X's name for property containing * registry of interpreter names. */ Atom appNameProperty; /* X's name for property used to hold the * application name on each comm window. */ /* * Information used by tkSelect.c and tkClipboard.c only: */ struct TkSelectionInfo *selectionInfoPtr; /* First in list of selection information * records. Each entry contains information * about the current owner of a particular * selection on this display. */ Atom multipleAtom; /* Atom for MULTIPLE. None means * selection stuff isn't initialized. */ Atom incrAtom; /* Atom for INCR. */ Atom targetsAtom; /* Atom for TARGETS. */ Atom timestampAtom; /* Atom for TIMESTAMP. */ Atom textAtom; /* Atom for TEXT. */ Atom compoundTextAtom; /* Atom for COMPOUND_TEXT. */ Atom applicationAtom; /* Atom for TK_APPLICATION. */ Atom windowAtom; /* Atom for TK_WINDOW. */ Atom clipboardAtom; /* Atom for CLIPBOARD. */ Tk_Window clipWindow; /* Window used for clipboard ownership and to * retrieve selections between processes. NULL * means clipboard info hasn't been * initialized. */ int clipboardActive; /* 1 means we currently own the clipboard * selection, 0 means we don't. */ struct TkMainInfo *clipboardAppPtr; /* Last application that owned clipboard. */ struct TkClipboardTarget *clipTargetPtr; /* First in list of clipboard type information * records. Each entry contains information * about the buffers for a given selection * target. */ /* * Information used by tkAtom.c only: */ int atomInit; /* 0 means stuff below hasn't been * initialized yet. */ Tcl_HashTable nameTable; /* Maps from names to Atom's. */ Tcl_HashTable atomTable; /* Maps from Atom's back to names. */ /* * Information used by tkCursor.c only: */ Font cursorFont; /* Font to use for standard cursors. * None means font not loaded yet. */ /* * Information used by tkGrab.c only: */ struct TkWindow *grabWinPtr; /* Window in which the pointer is currently * grabbed, or NULL if none. */ struct TkWindow *eventualGrabWinPtr; /* Value that grabWinPtr will have once the * grab event queue (below) has been * completely emptied. */ struct TkWindow *buttonWinPtr; /* Window in which first mouse button was * pressed while grab was in effect, or NULL * if no such press in effect. */ struct TkWindow *serverWinPtr; /* If no application contains the pointer then * this is NULL. Otherwise it contains the * last window for which we've gotten an * Enter or Leave event from the server (i.e. * the last window known to have contained * the pointer). Doesn't reflect events * that were synthesized in tkGrab.c. */ TkGrabEvent *firstGrabEventPtr; /* First in list of enter/leave events * synthesized by grab code. These events * must be processed in order before any other * events are processed. NULL means no such * events. */ TkGrabEvent *lastGrabEventPtr; /* Last in list of synthesized events, or NULL * if list is empty. */ int grabFlags; /* Miscellaneous flag values. See definitions * in tkGrab.c. */ /* * Information used by tkXId.c only: */ struct TkIdStack *idStackPtr; /* First in list of chunks of free resource * identifiers, or NULL if there are no free * resources. */ XID(*defaultAllocProc) _ANSI_ARGS_((Display *display)); /* Default resource allocator for display. */ struct TkIdStack *windowStackPtr; /* First in list of chunks of window * identifers that can't be reused right * now. */ int idCleanupScheduled; /* 1 means a call to WindowIdCleanup has * already been scheduled, 0 means it * hasn't. */ /* * Information maintained by tkWindow.c for use later on by tkXId.c: */ int destroyCount; /* Number of Tk_DestroyWindow operations * in progress. */ unsigned long lastDestroyRequest; /* Id of most recent XDestroyWindow request; * can re-use ids in windowStackPtr when * server has seen this request and event * queue is empty. */ /* * Information used by tkVisual.c only: */ TkColormap *cmapPtr; /* First in list of all non-default colormaps * allocated for this display. */ /* * Information used by tkFocus.c only: */ #if (TK_MAJOR_VERSION == 4) struct TkWindow *focusWinPtr; /* Window that currently has the focus for * this display, or NULL if none. */ struct TkWindow *implicitWinPtr; /* If the focus arrived at a toplevel window * implicitly via an Enter event (rather * than via a FocusIn event), this points * to the toplevel window. Otherwise it is * NULL. */ struct TkWindow *focusOnMapPtr; /* This points to a toplevel window that is * supposed to receive the X input focus as * soon as it is mapped (needed to handle the * fact that X won't allow the focus on an * unmapped window). NULL means no delayed * focus op in progress. */ int forceFocus; /* Associated with focusOnMapPtr: non-zero * means claim the focus even if some other * application currently has it. */ #else struct TkWindow *implicitWinPtr; /* If the focus arrived at a toplevel window * implicitly via an Enter event (rather * than via a FocusIn event), this points * to the toplevel window. Otherwise it is * NULL. */ struct TkWindow *focusPtr; /* Points to the window on this display that * should be receiving keyboard events. When * multiple applications on the display have * the focus, this will refer to the * innermost window in the innermost * application. This information isn't used * under Unix or Windows, but it's needed on * the Macintosh. */ #endif /* TK_MAJOR_VERSION == 4 */ /* * Used by tkColor.c only: */ TkStressedCmap *stressPtr; /* First in list of colormaps that have * filled up, so we have to pick an * approximate color. */ /* * Used by tkEvent.c only: */ struct TkWindowEvent *delayedMotionPtr; /* Points to a malloc-ed motion event * whose processing has been delayed in * the hopes that another motion event * will come along right away and we can * merge the two of them together. NULL * means that there is no delayed motion * event. */ /* * Miscellaneous information: */ #ifdef TK_USE_INPUT_METHODS XIM inputMethod; /* Input method for this display */ #endif /* TK_USE_INPUT_METHODS */ Tcl_HashTable winTable; /* Maps from X window ids to TkWindow ptrs. */ #if (TK_MAJOR_VERSION > 4) int refCount; /* Reference count of how many Tk applications * are using this display. Used to clean up * the display when we no longer have any * Tk applications using it. */ #endif /* TK_MAJOR_VERSION > 4 */ } TkDisplay; #endif /* TK_VERSION_NUMBER >= _VERSION(8,1,0) */ struct TkWindow { Display *display; TkDisplay *dispPtr; int screenNum; Visual *visual; int depth; Window window; struct TkWindow *childList; struct TkWindow *lastChildPtr; struct TkWindow *parentPtr; struct TkWindow *nextPtr; TkMainInfo *infoPtr; char *pathName; Tk_Uid nameUid; Tk_Uid classUid; XWindowChanges changes; unsigned int dirtyChanges; XSetWindowAttributes atts; unsigned long dirtyAtts; unsigned int flags; TkEventHandler *handlerList; #ifdef TK_USE_INPUT_METHODS XIC inputContext; #endif /* TK_USE_INPUT_METHODS */ ClientData *tagPtr; int nTags; int optionLevel; TkSelHandler *selHandlerList; Tk_GeomMgr *geomMgrPtr; ClientData geomData; int reqWidth, reqHeight; int internalBorderWidth; TkWinInfo *wmInfoPtr; #if (TK_MAJOR_VERSION > 4) TkClassProcs *classProcsPtr; ClientData instanceData; #endif TkWindowPrivate *privatePtr; }; #ifdef WIN32 /* *---------------------------------------------------------------------- * * GetWindowHandle -- * * Returns the XID for the Tk_Window given. Starting in Tk 8.0, * the toplevel widgets are wrapped by another window. * Currently there's no way to get at that window, other than * what is done here: query the X window hierarchy and grab the * parent. * * Results: * Returns the X Window ID of the widget. If it's a toplevel, then * the XID of the wrapper is returned. * *---------------------------------------------------------------------- */ static HWND GetWindowHandle(tkwin) Tk_Window tkwin; { HWND hWnd; hWnd = Tk_GetHWND(Tk_WindowId(tkwin)); #if (TK_MAJOR_VERSION > 4) if (Tk_IsTopLevel(tkwin)) { hWnd = GetParent(hWnd); } #endif /* TK_MAJOR_VERSION > 4 */ return hWnd; } #else Window Blt_GetParent(display, window) Display *display; Window window; { Window root, parent; Window *dummy; unsigned int count; if (XQueryTree(display, window, &root, &parent, &dummy, &count) > 0) { XFree(dummy); return parent; } return None; } int Blt_RootX(tkwin) Tk_Window tkwin; { int x; for (x = 0; tkwin != NULL; tkwin = Tk_Parent(tkwin)) { x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width; } return x; } int Blt_RootY(tkwin) Tk_Window tkwin; { int y; for (y = 0; tkwin != NULL; tkwin = Tk_Parent(tkwin)) { y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width; } return y; } static Window GetWindowId(tkwin) Tk_Window tkwin; { Window window; Tk_MakeWindowExist(tkwin); window = Tk_WindowId(tkwin); #if (TK_MAJOR_VERSION > 4) if (Tk_IsTopLevel(tkwin)) { Window parent; parent = Blt_GetParent(Tk_Display(tkwin), window); if (parent != None) { window = parent; } } #endif /* TK_MAJOR_VERSION > 4 */ return window; } #endif /* WIN32 */ /* *---------------------------------------------------------------------- * * DoConfigureNotify -- * * Generate a ConfigureNotify event describing the current * configuration of a window. * * Results: * None. * * Side effects: * An event is generated and processed by Tk_HandleEvent. * *---------------------------------------------------------------------- */ static void DoConfigureNotify(winPtr) Tk_FakeWin *winPtr; /* Window whose configuration was just * changed. */ { XEvent event; event.type = ConfigureNotify; event.xconfigure.serial = LastKnownRequestProcessed(winPtr->display); event.xconfigure.send_event = False; event.xconfigure.display = winPtr->display; event.xconfigure.event = winPtr->window; event.xconfigure.window = winPtr->window; event.xconfigure.x = winPtr->changes.x; event.xconfigure.y = winPtr->changes.y; event.xconfigure.width = winPtr->changes.width; event.xconfigure.height = winPtr->changes.height; event.xconfigure.border_width = winPtr->changes.border_width; if (winPtr->changes.stack_mode == Above) { event.xconfigure.above = winPtr->changes.sibling; } else { event.xconfigure.above = None; } event.xconfigure.override_redirect = winPtr->atts.override_redirect; Tk_HandleEvent(&event); } /* *-------------------------------------------------------------- * * Blt_MakeTransparentWindowExist -- * * Similar to Tk_MakeWindowExist but instead creates a * transparent window to block for user events from sibling * windows. * * Differences from Tk_MakeWindowExist. * * 1. This is always a "busy" window. There's never a * platform-specific class procedure to execute instead. * 2. The window is transparent and never will contain children, * so colormap information is irrelevant. * * Results: * None. * * Side effects: * When the procedure returns, the internal window associated * with tkwin is guaranteed to exist. This may require the * window's ancestors to be created too. * *-------------------------------------------------------------- */ void Blt_MakeTransparentWindowExist(tkwin, parent, isBusy) Tk_Window tkwin; /* Token for window. */ Window parent; /* Parent window. */ int isBusy; /* */ { struct TkWindow *winPtr = (TkWindow *) tkwin; struct TkWindow *winPtr2; Tcl_HashEntry *hPtr; int notUsed; TkDisplay *dispPtr; long int mask; #ifdef WIN32 HWND hParent; int style; DWORD exStyle; HWND hWnd; #endif /* WIN32 */ if (winPtr->window != None) { return; /* Window already exists. */ } #ifdef notdef if ((winPtr->parentPtr == NULL) || (winPtr->flags & TK_TOP_LEVEL)) { parent = XRootWindow(winPtr->display, winPtr->screenNum); /* TODO: Make the entire screen busy */ } else { if (Tk_WindowId(winPtr->parentPtr) == None) { Tk_MakeWindowExist((Tk_Window)winPtr->parentPtr); } } #endif /* Create a transparent window and put it on top. */ #ifdef WIN32 hParent = (HWND) parent; style = (WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); exStyle = (WS_EX_TRANSPARENT | WS_EX_TOPMOST); #define TK_WIN_CHILD_CLASS_NAME "TkChild" hWnd = CreateWindowEx(exStyle, TK_WIN_CHILD_CLASS_NAME, NULL, style, Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), hParent, NULL, (HINSTANCE) Tk_GetHINSTANCE(), NULL); winPtr->window = Tk_AttachHWND(tkwin, hWnd); #else mask = (!isBusy) ? 0 : (CWDontPropagate | CWEventMask); /* Ignore the important events while the window is mapped. */ #define USER_EVENTS (EnterWindowMask | LeaveWindowMask | KeyPressMask | \ KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask) #define PROP_EVENTS (KeyPressMask | KeyReleaseMask | ButtonPressMask | \ ButtonReleaseMask | PointerMotionMask) winPtr->atts.do_not_propagate_mask = PROP_EVENTS; winPtr->atts.event_mask = USER_EVENTS; winPtr->changes.border_width = 0; winPtr->depth = 0; winPtr->window = XCreateWindow(winPtr->display, parent, winPtr->changes.x, winPtr->changes.y, (unsigned)winPtr->changes.width, /* width */ (unsigned)winPtr->changes.height, /* height */ (unsigned)winPtr->changes.border_width, /* border_width */ winPtr->depth, /* depth */ InputOnly, /* class */ winPtr->visual, /* visual */ mask, /* valuemask */ &(winPtr->atts) /* attributes */ ); #endif /* WIN32 */ dispPtr = winPtr->dispPtr; hPtr = Tcl_CreateHashEntry(&(dispPtr->winTable), (char *)winPtr->window, ¬Used); Tcl_SetHashValue(hPtr, winPtr); winPtr->dirtyAtts = 0; winPtr->dirtyChanges = 0; #ifdef TK_USE_INPUT_METHODS winPtr->inputContext = NULL; #endif /* TK_USE_INPUT_METHODS */ if (!(winPtr->flags & TK_TOP_LEVEL)) { /* * If any siblings higher up in the stacking order have already * been created then move this window to its rightful position * in the stacking order. * * NOTE: this code ignores any changes anyone might have made * to the sibling and stack_mode field of the window's attributes, * so it really isn't safe for these to be manipulated except * by calling Tk_RestackWindow. */ for (winPtr2 = winPtr->nextPtr; winPtr2 != NULL; winPtr2 = winPtr2->nextPtr) { if ((winPtr2->window != None) && !(winPtr2->flags & TK_TOP_LEVEL)) { XWindowChanges changes; changes.sibling = winPtr2->window; changes.stack_mode = Below; XConfigureWindow(winPtr->display, winPtr->window, CWSibling | CWStackMode, &changes); break; } } } /* * Issue a ConfigureNotify event if there were deferred configuration * changes (but skip it if the window is being deleted; the * ConfigureNotify event could cause problems if we're being called * from Tk_DestroyWindow under some conditions). */ if ((winPtr->flags & TK_NEED_CONFIG_NOTIFY) && !(winPtr->flags & TK_ALREADY_DEAD)) { winPtr->flags &= ~TK_NEED_CONFIG_NOTIFY; DoConfigureNotify((Tk_FakeWin *) tkwin); } } /* *---------------------------------------------------------------------- * * Blt_FindChild -- * * Performs a linear search for the named child window in a given * parent window. * * This can be done via Tcl, but not through Tk's C API. It's * simple enough, if you peek into the Tk_Window structure. * * Results: * The child Tk_Window. If the named child can't be found, NULL * is returned. * *---------------------------------------------------------------------- */ /*LINTLIBRARY*/ Tk_Window Blt_FindChild(parent, name) Tk_Window parent; char *name; { register struct TkWindow *winPtr; struct TkWindow *parentPtr = (struct TkWindow *)parent; Tk_Uid nameUid; nameUid = Tk_GetUid(name); for (winPtr = parentPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) { if (nameUid == winPtr->nameUid) { return ((Tk_Window)winPtr); } } return NULL; } /* *---------------------------------------------------------------------- * * Blt_FirstChildWindow -- * * Performs a linear search for the named child window in a given * parent window. * * This can be done via Tcl, but not through Tk's C API. It's * simple enough, if you peek into the Tk_Window structure. * * Results: * The child Tk_Window. If the named child can't be found, NULL * is returned. * *---------------------------------------------------------------------- */ /*LINTLIBRARY*/ Tk_Window Blt_FirstChild(parent) Tk_Window parent; { struct TkWindow *parentPtr = (struct TkWindow *)parent; return (Tk_Window)parentPtr->childList; } /* *---------------------------------------------------------------------- * * Blt_FindChild -- * * Performs a linear search for the named child window in a given * parent window. * * This can be done via Tcl, but not through Tk's C API. It's * simple enough, if you peek into the Tk_Window structure. * * Results: * The child Tk_Window. If the named child can't be found, NULL * is returned. * *---------------------------------------------------------------------- */ /*LINTLIBRARY*/ Tk_Window Blt_NextChild(tkwin) Tk_Window tkwin; { struct TkWindow *winPtr = (struct TkWindow *)tkwin; if (winPtr == NULL) { return NULL; } return (Tk_Window)winPtr->nextPtr; } /* *---------------------------------------------------------------------- * * UnlinkWindow -- * * This procedure removes a window from the childList of its * parent. * * Results: * None. * * Side effects: * The window is unlinked from its childList. * *---------------------------------------------------------------------- */ static void UnlinkWindow(winPtr) struct TkWindow *winPtr; /* Child window to be unlinked. */ { struct TkWindow *prevPtr; prevPtr = winPtr->parentPtr->childList; if (prevPtr == winPtr) { winPtr->parentPtr->childList = winPtr->nextPtr; if (winPtr->nextPtr == NULL) { winPtr->parentPtr->lastChildPtr = NULL; } } else { while (prevPtr->nextPtr != winPtr) { prevPtr = prevPtr->nextPtr; if (prevPtr == NULL) { panic("UnlinkWindow couldn't find child in parent"); } } prevPtr->nextPtr = winPtr->nextPtr; if (winPtr->nextPtr == NULL) { winPtr->parentPtr->lastChildPtr = prevPtr; } } } /* *---------------------------------------------------------------------- * * Blt_RelinkWindow -- * * Relinks a window into a new parent. The window is unlinked * from its original parent's child list and added onto the end * of the new parent's list. * * FIXME: If the window has focus, the focus should be moved * to an ancestor. Otherwise, Tk becomes confused * about which Toplevel turns on focus for the window. * Right now this is done at the Tcl layer. For example, * see blt::CreateTearoff in bltTabset.tcl. * * Results: * None. * * Side effects: * The window is unlinked from its childList. * *---------------------------------------------------------------------- */ void Blt_RelinkWindow(tkwin, newParent, x, y) Tk_Window tkwin; /* Child window to be linked. */ Tk_Window newParent; int x, y; { struct TkWindow *winPtr, *parentWinPtr; if (Blt_ReparentWindow(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(newParent), x, y) != TCL_OK) { return; } winPtr = (struct TkWindow *)tkwin; parentWinPtr = (struct TkWindow *)newParent; winPtr->flags &= ~TK_REPARENTED; UnlinkWindow(winPtr); /* Remove the window from its parent's list */ /* Append the window onto the end of the parent's list of children */ winPtr->parentPtr = parentWinPtr; winPtr->nextPtr = NULL; if (parentWinPtr->childList == NULL) { parentWinPtr->childList = winPtr; } else { parentWinPtr->lastChildPtr->nextPtr = winPtr; } parentWinPtr->lastChildPtr = winPtr; } #if (TK_MAJOR_VERSION == 4) static int initialized = FALSE; static Tcl_HashTable windowTable; void Blt_SetWindowInstanceData(tkwin, instanceData) Tk_Window tkwin; ClientData instanceData; { Tcl_HashEntry *hPtr; int isNew; if (!initialized) { Tcl_InitHashTable(&windowTable, TCL_ONE_WORD_KEYS); initialized = TRUE; } hPtr = Tcl_CreateHashEntry(&windowTable, (char *)tkwin, &isNew); assert(isNew); Tcl_SetHashValue(hPtr, instanceData); } ClientData Blt_GetWindowInstanceData(tkwin) Tk_Window tkwin; { Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&windowTable, (char *)tkwin); if (hPtr == NULL) { return NULL; } return Tcl_GetHashValue(hPtr); } void Blt_DeleteWindowInstanceData(tkwin) Tk_Window tkwin; { Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&windowTable, (char *)tkwin); assert(hPtr); Tcl_DeleteHashEntry(hPtr); } #else void Blt_SetWindowInstanceData(tkwin, instanceData) Tk_Window tkwin; ClientData instanceData; { struct TkWindow *winPtr = (struct TkWindow *)tkwin; winPtr->instanceData = instanceData; } ClientData Blt_GetWindowInstanceData(tkwin) Tk_Window tkwin; { struct TkWindow *winPtr = (struct TkWindow *)tkwin; return winPtr->instanceData; } void Blt_DeleteWindowInstanceData(tkwin) Tk_Window tkwin; { } #endif #ifdef WIN32 /* *---------------------------------------------------------------------- * * Blt_GetRealWindowId -- * * Returns the XID for the Tk_Window given. Starting in Tk 8.0, * the toplevel widgets are wrapped by another window. * Currently there's no way to get at that window, other than * what is done here: query the X window hierarchy and grab the * parent. * * Results: * Returns the X Window ID of the widget. If it's a toplevel, then * the XID of the wrapper is returned. * *---------------------------------------------------------------------- */ Window Blt_GetRealWindowId(tkwin) Tk_Window tkwin; { return (Window) GetWindowHandle(tkwin); } /* *---------------------------------------------------------------------- * * Blt_GetToplevel -- * * Retrieves the toplevel window which is the nearest ancestor of * of the specified window. * * Results: * Returns the toplevel window or NULL if the window has no * ancestor which is a toplevel. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tk_Window Blt_GetToplevel(tkwin) Tk_Window tkwin; /* Window for which the toplevel should be * deterined. */ { while (!Tk_IsTopLevel(tkwin)) { tkwin = Tk_Parent(tkwin); if (tkwin == NULL) { return NULL; } } return tkwin; } /* *---------------------------------------------------------------------- * * Blt_RaiseToLevelWindow -- * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_RaiseTopLevelWindow(tkwin) Tk_Window tkwin; { SetWindowPos(GetWindowHandle(tkwin), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } /* *---------------------------------------------------------------------- * * Blt_MapTopLevelWindow -- * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_MapTopLevelWindow(tkwin) Tk_Window tkwin; { ShowWindow(GetWindowHandle(tkwin), SW_SHOWNORMAL); } /* *---------------------------------------------------------------------- * * Blt_UnmapTopLevelWindow -- * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_UnmapTopLevelWindow(tkwin) Tk_Window tkwin; { ShowWindow(GetWindowHandle(tkwin), SW_HIDE); } #else /* *---------------------------------------------------------------------- * * Blt_GetRealWindowId -- * * Returns the XID for the Tk_Window given. Starting in Tk 8.0, * the toplevel widgets are wrapped by another window. * Currently there's no way to get at that window, other than * what is done here: query the X window hierarchy and grab the * parent. * * Results: * Returns the X Window ID of the widget. If it's a toplevel, then * the XID of the wrapper is returned. * *---------------------------------------------------------------------- */ Window Blt_GetRealWindowId(tkwin) Tk_Window tkwin; { return GetWindowId(tkwin); } /* *---------------------------------------------------------------------- * * Blt_RaiseToLevelWindow -- * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_RaiseTopLevelWindow(tkwin) Tk_Window tkwin; { XRaiseWindow(Tk_Display(tkwin), GetWindowId(tkwin)); } /* *---------------------------------------------------------------------- * * Blt_RaiseToLevelWindow -- * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_ResizeTopLevelWindow(tkwin, width, height) Tk_Window tkwin; int width, height; { XResizeWindow(Tk_Display(tkwin), GetWindowId(tkwin), width, height); } /* *---------------------------------------------------------------------- * * Blt_MapTopLevelWindow -- * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_MapTopLevelWindow(tkwin) Tk_Window tkwin; { XMapWindow(Tk_Display(tkwin), GetWindowId(tkwin)); } /* *---------------------------------------------------------------------- * * Blt_UnmapTopLevelWindow -- * * Results: * None. * *---------------------------------------------------------------------- */ void Blt_UnmapTopLevelWindow(tkwin) Tk_Window tkwin; { XUnmapWindow(Tk_Display(tkwin), GetWindowId(tkwin)); } #endif /* WIN32 */ tkdesk-2.0/blt/diffs/0040755000175000007640000000000010037657326012615 5ustar jccjcctkdesk-2.0/blt/diffs/Makefile.in.diff0100644000175000007640000001520310020457430015551 0ustar jccjcc*** /usr/tmp/TclTk/blt2.4j/src/Makefile.in Tue Sep 21 20:15:29 1999 --- Makefile.in Thu Oct 28 22:28:01 1999 *************** *** 1,158 **** ! # ------------------------------------------------------------------------ ! # Makefile for static version of BLT library ! # ------------------------------------------------------------------------ ! ! # ------------------------------------------------------------------------ ! # C Compiler options ! # ------------------------------------------------------------------------ ! BLT_LIBRARY = @BLT_LIBRARY@ ! CC = @CC@ ! CFLAGS = @CFLAGS@ ! DEFINES = @DEFINES@ ! DEF_BLTINIT = -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" ! EXTRA_CFLAGS = @GCCFLAGS@ ! LDFLAGS = @LDFLAGS@ ! version = @BLT_MAJOR_VERSION@@BLT_MINOR_VERSION@ ! ! # ------------------------------------------------------------------------ ! # Source and targer installation directories ! # ------------------------------------------------------------------------ ! ! bindir = @bindir@ ! exec_prefix = @exec_prefix@ ! includedir = @includedir@ ! libdir = @libdir@ ! prefix = @prefix@ ! srcdir = @srcdir@ ! ! instdirs = $(prefix) $(exec_prefix) $(bindir) $(libdir) $(includedir) ! ! # ------------------------------------------------------------------------ ! # Directories containing Tcl and Tk include files and libraries ! # ------------------------------------------------------------------------ ! ! INCLUDES = -I. -I$(srcdir) @INCLUDES@ ! ! # ------------------------------------------------------------------------ ! # Libraries directives for Tcl, Tk, X11, and BLT ! # ------------------------------------------------------------------------ ! ! LIBRARIES = $(libname).a @LIBS@ ! ! ! # ------------------------------------------------------------------------ ! # You don't need to edit anything beyond this point ! # ------------------------------------------------------------------------ ! ! #N_OBJS = bltTed.o ! V3_OBJS = bltTri.o bltGrMt.o ! ! TK_OBJS = tkButton.o tkFrame.o tkScrollbar.o ! ! OBJS = bltGraph.o bltGrAxis.o bltGrBar.o bltGrElem.o \ ! bltGrGrid.o bltGrHairs.o bltGrLegd.o bltGrLine.o \ ! bltGrMarker.o bltGrMisc.o bltGrPen.o bltGrPs.o \ ! bltBeep.o bltBgexec.o bltBitmap.o bltBusy.o \ ! bltContainer.o bltCutbuffer.o bltDebug.o \ ! bltDnd.o bltUnixDnd.o \ ! bltHierbox.o bltHtext.o bltSpline.o bltTable.o bltTabset.o \ ! bltVector.o bltWatch.o bltWinop.o \ ! bltCanvEps.o bltInit.o bltBind.o bltColor.o \ ! bltConfig.o bltImage.o bltList.o bltChain.o \ ! bltNsUtil.o bltParse.o bltPs.o bltText.o bltTile.o \ ! bltUnixPipe.o bltUtil.o bltWindow.o \ ! $(TK_OBJS) $(N_OBJS) ! ! # GNU Make-specific macro ! SRCS = $(patsubst %.o,$(srcdir)/%.c,$(OBJS)) ! ! demo = bltwish ! headers = blt.h ! libname = libBLT ! ! CC_SWITCHES = $(EXTRA_CFLAGS) $(CFLAGS) $(DEFINES) $(INCLUDES) ! INSTALL = @INSTALL@ ! INSTALL_DATA = @INSTALL_DATA@ RANLIB = @RANLIB@ ! SHELL = /bin/sh AR = ar rc RM = rm -f - LINT = lint - LINTFLAGS = -axhbns - LN_S = @LN_S@ - VPATH = $(srcdir) ! all: build-lib build-demo @SHLIB_TARGET@ ! build-lib: $(libname).a ! build-shared: ! (cd shared; $(MAKE) CFLAGS="$(CFLAGS)" all) ! build-demo: $(demo) ! $(demo): $(libname).a ! $(RM) $(demo) ! $(CC) $(CC_SWITCHES) $(LDFLAGS) \ ! $(srcdir)/bltUnixMain.c -o $(demo) $(LIBRARIES) ! $(libname).a: $(OBJS) $(RM) $@ $(AR) $@ $(OBJS) $(RANLIB) $@ - install: install-dirs install-lib install-demo install-headers - - install-demo: $(demo) - $(INSTALL) -m 0755 bltwish $(bindir)/bltwish$(version) - (cd $(bindir); $(RM) bltwish ; $(LN_S) bltwish$(version) bltwish) - - install-lib: $(libname).a - $(INSTALL_DATA) $(libname).a $(libdir)/$(libname)$(version).a - (cd $(libdir); $(RM) $(libname).a ; \ - $(LN_S) $(libname)$(version).a $(libname).a) - $(RANLIB) $(libdir)/$(libname)$(version).a - (cd shared; $(MAKE) install) - - install-dirs: - @for i in $(instdirs) ; do \ - if test ! -d $$i ; then \ - echo " mkdir $$i" ; \ - mkdir $$i ; \ - fi ; \ - done - - install-headers: - @for i in $(headers) ; do \ - echo "installing $(includedir)/$$i..." ; \ - $(INSTALL_DATA) -m 0444 $(srcdir)/$$i $(includedir) ; \ - done - - lint: - $(LINT) $(LINTFLAGS) $(DEFINES) $(INCLUDES) $(SRCS) - - clean: - $(RM) $(OBJS) $(DEMO_OBJS) $(libname).a $(demo)* *pure* .pure* - (cd shared; $(MAKE) clean) - - distclean: clean - $(RM) $(srcdir)/*.bak $(srcdir)/*\~ $(srcdir)/"#"* Makefile - $(RM) bltConfig.h Makefile TAGS - - bltInit.o: bltInit.c ! $(CC) -c $(CC_SWITCHES) $(DEF_BLTINIT) $< ! .c.o: ! $(CC) -c $(CC_SWITCHES) $< ! ! PUREFLAGS= ! pure: $(libname).a ! $(PURIFYHOME)/purify $(PUREFLAGS) $(CC) $(CC_SWITCHES) \ ! $(srcdir)/bltUnixMain.c -o bltwish $(LIBRARIES) ! ! QUANTIFYFLAGS= ! quant: $(libname).a ! $(QUANTIFYHOME)/quantify $(QUANTIFYFLAGS) $(CC) $(CC_SWITCHES) \ ! $(srcdir)/bltUnixMain.c -o bltwish $(LIBRARIES) --- 1,55 ---- ! # This is the Makefile for the BLT subset needed by TkDesk. ! # It is called from the main Makefile in .., which passes the CFLAGS. ! #---------------------------------------------------------------- ! # Things you can change to personalize the Makefile for your own ! # site (you can make these changes in either Makefile.in or ! # Makefile, but changes to Makefile will get lost if you re-run ! # the configuration script). ! #---------------------------------------------------------------- ! ! # Location of Tcl header files: ! TCL_INCLUDE_DIR = @TCL_INCLUDE_PATH@ ! ! # Location of Tk header files: ! TK_INCLUDE_DIR = @TK_INCLUDE_PATH@ ! ! # Location of X11 header files: ! X_INCLUDE_FLAG = @TK_XINCLUDES@ ! ! # Configuration compiler flags: ! AC_FLAGS = @DEFS@ ! ! # Miscellaneous settings: RANLIB = @RANLIB@ ! CC = @CC@ AR = ar rc RM = rm -f ! #---------------------------------------------------------------- ! # The information below should be usable as is. The configure ! # script won't modify it and you shouldn't need to modify it ! # either. ! #---------------------------------------------------------------- ! CFLAGS = ${CC_OPTS} ${AC_FLAGS} -I.. -I. -I${TCL_INCLUDE_DIR} -I${TK_INCLUDE_DIR} ${X_INCLUDE_FLAG} ! BLT_LIBRARY = $(LIB_DIR) ! libname = libBLT.a ! OBJS = bltBgexec.o bltBusy.o bltUnixPipe.o \ ! bltText.o bltDnd.o bltInit.o bltList.o bltUtil.o \ ! bltConfig.o bltWindow.o bltChain.o bltUnixDnd.o ! lib: $(libname) ! $(libname): $(OBJS) $(RM) $@ $(AR) $@ $(OBJS) $(RANLIB) $@ bltInit.o: bltInit.c ! $(CC) -c $(CFLAGS) -DBLT_LIBRARY=\"$(BLT_LIBRARY)\" $< ! clean: ! $(RM) $(OBJS) $(libname) *\~ "#"* tkdesk-2.0/blt/diffs/bltDnd.c.diff0100644000175000007640000000100410020457430015051 0ustar jccjcc*** /usr/tmp/TclTk/blt2.4j/src/bltDnd.c Tue Sep 21 20:15:34 1999 --- bltDnd.c Sun Nov 7 19:38:52 1999 *************** *** 1614,1619 **** --- 1614,1623 ---- } targetInfo = srcPtr->windowPtr->targetInfo; + /* Let TkDesk know we found a valid drop target */ + Tcl_SetVar2(srcPtr->interp, "tkdesk", "drop_on_item", "1", + TCL_GLOBAL_ONLY); + Tcl_DStringInit(&dString); Blt_DStringAppendElements(&dString, "send", targetInfo[INTERP_NAME], dragDropCmd, "location", (char *)NULL); tkdesk-2.0/blt/diffs/bltInit.c.diff0100644000175000007640000000437210020457430015262 0ustar jccjcc*** /usr/tmp/TclTk/blt2.4j/src/bltInit.c Wed Sep 22 22:32:04 1999 --- bltInit.c Thu Nov 4 13:48:54 1999 *************** *** 31,36 **** --- 31,58 ---- static Tcl_MathProc MinMathProc, MaxMathProc; #endif + /* Exclude commands not needed by TkDesk: */ + + #define NO_HTEXT + #define NO_GRAPH + #define NO_TABLE + #define NO_WINOP + #define NO_BITMAP + #define NO_DEBUG + #define NO_WATCH + #define NO_VECTOR + #define NO_SPLINE + #define NO_BELL + #define NO_CUTBUFFER + #define NO_TILEFRAME + #define NO_TILEBUTTON + #define NO_TILESCROLLBAR + #define NO_BITMAP + #define NO_EPS + #define NO_HIERBOX + #define NO_TABSET + #define NO_CONTAINER + static Tcl_AppInitProc *initProcArr[] = { #ifndef NO_GRAPH *************** *** 216,222 **** continue \n\ } \n\ set path [set $var] \n\ ! if { [file readable [file join $path bltGraph.tcl]] } { \n\ set blt_library $path\n\ break \n\ } \n\ --- 238,244 ---- continue \n\ } \n\ set path [set $var] \n\ ! if { [file readable [file join $path bltDnd.tcl]] } { \n\ set blt_library $path\n\ break \n\ } \n\ *************** *** 369,375 **** --- 391,399 ---- argTypes[0] = argTypes[1] = TCL_EITHER; Tcl_CreateMathFunc(interp, "min", 2, argTypes, MinMathProc,(ClientData)0); Tcl_CreateMathFunc(interp, "max", 2, argTypes, MaxMathProc,(ClientData)0); + #ifndef NO_EPS Blt_InitEpsCanvasItem(interp); + #endif return TCL_OK; } *************** *** 415,421 **** --- 439,447 ---- argTypes[0] = argTypes[1] = TCL_EITHER; Tcl_CreateMathFunc(interp, "min", 2, argTypes, MinMathProc,(ClientData)0); Tcl_CreateMathFunc(interp, "max", 2, argTypes, MaxMathProc,(ClientData)0); + #ifndef NO_EPS Blt_InitEpsCanvasItem(interp); + #endif return TCL_OK; } *************** *** 470,476 **** --- 496,506 ---- Tcl_DStringAppend(&dString, specPtr->name, -1); cmdPath = Tcl_DStringValue(&dString); + #if HAVE_NAMESPACES cmdToken = Tcl_FindCommand(interp, cmdPath, (Tcl_Namespace *)NULL, 0); + #else + cmdToken = NULL; + #endif /* HAVE_NAMESPACES */ if (cmdToken != NULL) { Tcl_DStringFree(&dString); return cmdToken; /* Assume command was already initialized */ tkdesk-2.0/blt/diffs/bltInt.h.diff0100644000175000007640000000107710020457430015115 0ustar jccjcc*** /usr/tmp/TclTk/blt2.4j/src/bltInt.h Tue Sep 21 20:15:40 1999 --- bltInt.h Thu Oct 28 22:19:31 1999 *************** *** 41,57 **** --- 41,60 ---- #define TCL_VERSION_NUMBER _VERSION(TCL_MAJOR_VERSION, TCL_MINOR_VERSION, TCL_RELEASE_SERIAL) #define TK_VERSION_NUMBER _VERSION(TK_MAJOR_VERSION, TK_MINOR_VERSION, TK_RELEASE_SERIAL) + #include "config.h" #include "bltTkInt.h" #include #include #include + /* #ifdef WIN32 #include "bltWinConfig.h" #else #include "bltConfig.h" #endif + */ #include "blt.h" tkdesk-2.0/blt/diffs/bltUnixDnd.c.diff0100644000175000007640000000064610020457430015730 0ustar jccjcc*** /usr/tmp/TclTk/blt2.4j/src/bltUnixDnd.c Wed Sep 22 23:32:03 1999 --- bltUnixDnd.c Mon Nov 8 19:11:05 1999 *************** *** 792,798 **** --- 792,802 ---- { int size; + #if (XT_REVISION < 6) + size = XMaxRequestSize(display); + #else size = MAX(XExtendedMaxRequestSize(display), XMaxRequestSize(display)); + #endif size *= 4; /* Convert to bytes. */ size -= 32; return size; tkdesk-2.0/COPYING0100644000175000007640000004741610037303623011771 0ustar jccjccThis file contains information on usage and redistribution of the individual parts of the program TkDesk. The most important point is that there is definitely not the slightest bit of warranty!! Copyright Holders: ------------------ 1.) tkAppInit.c and files in the subdirectories tcl_lib and tk_lib: * Copyright (c) 1993 The Regents of the University of California. * Copyright (c) 1994 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 2.) blt/ (i.e. all files in the subdirectory blt): * Copyright 1993-1996 by AT&T Bell Laboratories. * Permission to use, copy, modify, and distribute this software * and its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the * names of AT&T Bell Laboratories any of their entities not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * AT&T disclaims all warranties with regard to this software, including * all implied warranties of merchantability and fitness. In no event * shall AT&T be liable for any special, indirect or consequential * damages or any damages whatsoever resulting from loss of use, data * or profits, whether in an action of contract, negligence or other * tortuous action, arising out of or in connection with the use or * performance of this software. 3.) Everything else (unless otherwise stated in individual files): Copyright (C) 1996 Christian Bolik This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (follows hereafter) for more details. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. tkdesk-2.0/CREDITS0100644000175000007640000000444610037303677011763 0ustar jccjcc(Note: I shamelessly cut and pasted this from Linus Torvalds' CREDITS file that he distributes with the Linux sources. I just thought the format looks pretty useful.) This is at least a partial credits-file of people that have contributed to the TkDesk project. It is sorted by name, and formatted in a format that allows for easy grepping and beautification by scripts. The fields are: name (N), email (E), web-address (W), PGP key ID and fingerprint (P), description (D) and snail-mail address (S). I apologize in advance to everybody I forgot to put on this list (and I'm sure I forgot at least some of you). I'd appreciate it if you sent me a short mail to refresh my memory so I can get this list a little more complete. Thanks, Christian Bolik ---------- N: Alan Shackelford E: alan_s@shaknet.clark.net D: Former maintainer of the TkDesk mailing list N: Carl Iorio E: iorio@i-cafe.net D: Afterstep drop script, fix N: J. Chris Coppick E: jchris@users.sourceforge.net D: Contributor and current TkDesk maintainer N: Chris Sterritt E: sterritt@mrj.com D: Former maintainer of the TkDesk mailing list (tkdesk@mrj.com) D: Various installation hints (SGI, Solaris). N: Dave Cinege E: dcinege@psychosis.com D: Some configuration ideas and code snippets. N: David M.Cook E: dmcook@cts.com D: Maintains RPM packages of TkDesk N: Duncan Barclay E: dmlb@ragnet.demon.co.uk D: "Unofficial-official" FreeBSD maintainer of TkDesk N: Erez Strauss E: erez@newplaces.com D: Improved xemacs_load proc, sent in code for XEmacs appbar button. N: Gerald Willmann E: gerald@willmann.stanford.edu D: Former maintainer of the TkDesk mailing list N: John Blair E: jdblair@uab.edu W: http://frodo.tucc.uab.edu D: Author of a very nice article about TkDesk that appeared in D: the 3/98 issue of the Linux Journal! D: Contributed TkDesk's RCS procs (see popup for *.{c,h,cpp,tcl} files). D: Contributed additional mouse button bindings for the info box's D: permission buttons. N: Kazuya Kawasaki E: kawakazu@yk.rim.or.jp D: Japanize patch N: Martin Andrews E: andrewm@dev1.eeg.ccf.org D: Idea and code for auto-raising of the appbar. N: Mikhail Teterin E: mi@ALDAN.ziplink.net D: Patch for Tk's tkRecolorTree routine. N: Johannes Drechsel-Burkhard E: jdb@chello.at D: Contributed initial patches for mouse-wheel support tkdesk-2.0/ChangeLog0100644000175000007640000003663010037654316012514 0ustar jccjcc TkDesk Change Log for Version 2.x: 2004-04-15 J. Chris Coppick Released version 2.0 (Lazarus): Release 2.0 Highlights * Tcl/Tk 8.4 compatibility See the INSTALL file for detailed release requirements. * Dropped support for Tcl/Tk 7.x. * Mouse-wheel support Most text boxes and the file listings are now mouse-wheel enabled. The usage details haven't made it into the documentation yet, so here's the quick summary: o Your X11 server needs to be configured for mouse-wheel support. o The mouse wheel scrolls 5 lines at a time by default o Shift + the mouse wheel scrolls 1 line at a time o Ctrl + the mouse wheel scrolls 1 page at a time * Select two files, press a button, get a diff! This works great in conjunction with tkDiff. The diff command used can be configured in your .tkdesk/System file. The default "diff" button (on the button bar) is a picture of two overlapped pages. * Improved (hopefully) configure script * New TkDesk logo 2004-04-14 J. Chris Coppick * tcldesk/appbar-trash.tcl: Saying 'no' to the new trashdir create dialog was causing problems. Fixed it so that 'no' means the trashdir button still gets created, even though the dir doesn't exist. I imagine this really only matters during testdrives... 2004-04-14 J. Chris Coppick * ChangeLog: Updated entries for release 2.0. 2004-04-14 J. Chris Coppick * ChangeLog: Renamed CHANGES to ChangeLog. New ChangeLog format - now using CVS log info to generate ChangeLog entries. This is terrible of course because now we'll have to get into the habit of providing meaningful log messages. :-) 2004-04-14 J. Chris Coppick * CHANGES: Renamed CHANGES to ChangeLog 2004-04-14 J. Chris Coppick * ChangeLog-1.x: Split out pre-2.0 version changes into a separate log file (ChangeLog-1.x). 2004-04-14 J. Chris Coppick * Makefile.in: Changed 'CHANGES' to 'ChangeLog' (that's not confusing at all...) Removed the export target section as it was out-of-date with respect to the 2.0 distribution, plus the use of CVS obsoletes it, maybe. Removed the floppy target section. 2004-04-14 J. Chris Coppick * tcldesk/help.tcl: Changed references to CHANGES to ChangeLog. 2004-04-14 J. Chris Coppick * tkdesk.main: Updates for TkDesk 2.0 release... Version, date, copyright fixes No longer using clrpick.tcl or tkpatches.tcl. Changed authormail var to current maintainer's address Removed tkPriv refs for tk 8.4 compat. Blt drag-n-drop library file reference fix 2004-04-14 J. Chris Coppick * README: Updates for TkDesk 2.0. 2004-04-14 J. Chris Coppick * INSTALL: Many changes for TkDesk 2.0 (almost a complete rewrite), including updated requirements, etc. 2004-04-14 J. Chris Coppick * CREDITS: Minor updates, additions 2004-04-14 J. Chris Coppick * COPYING: Removed Itcl copyright (Itcl no longer included with TkDesk) TkDesk copyright date fix 2004-04-14 J. Chris Coppick * contrib/follow_me.sh: Contributed shell functions for tying the cd shell command to TkDesk. 2004-04-13 J. Chris Coppick * configure: New autoconf'd version for TkDesk 2.0. 2004-04-13 J. Chris Coppick * configure.in: Fixed double equals in with-blt option help. 2004-04-13 J. Chris Coppick * config.h.in: Generated new version for TkDesk 2.0. 2004-04-13 J. Chris Coppick * tkdeskclient/Makefile.in: fixed clean: target 2004-04-13 J. Chris Coppick * tcldesk/static/: itcl3.0/itcl.tcl, itcl3.0/pkgIndex.tcl, tcl8.0/history.tcl, tcl8.0/init.tcl, tcl8.0/ldAix, tcl8.0/ldAout.tcl, tcl8.0/parray.tcl, tcl8.0/safe.tcl, tcl8.0/tclIndex, tcl8.0/word.tcl, tk8.0/bgerror.tcl, tk8.0/button.tcl, tk8.0/clrpick.tcl, tk8.0/comdlg.tcl, tk8.0/console.tcl, tk8.0/dialog.tcl, tk8.0/entry.tcl, tk8.0/focus.tcl, tk8.0/listbox.tcl, tk8.0/menu.tcl, tk8.0/msgbox.tcl, tk8.0/obsolete.tcl, tk8.0/optMenu.tcl, tk8.0/palette.tcl, tk8.0/prolog.ps, tk8.0/safetk.tcl, tk8.0/scale.tcl, tk8.0/scrlbar.tcl, tk8.0/tclIndex, tk8.0/tearoff.tcl, tk8.0/text.tcl, tk8.0/tk.tcl, tk8.0/tkfbox.tcl, tk8.0/xmfbox.tcl: Removed - do not want to be in the business of redistributing tcl/tk/itcl anymore. 2004-04-13 J. Chris Coppick * Makefile.in: Added -DUSE_NON_CONST to CC_OPTS for Tcl 8.4 compat. Removed STATIC_BUILD (standalone TkDesk) support. 2004-04-13 J. Chris Coppick * tkAppInit.c: Removed HAVE_TCL8_0_H cpp conditional Removed static build (standalone TkDesk) support 2004-04-13 J. Chris Coppick * tcldesk/tclIndex: Generated new version. 2004-04-13 J. Chris Coppick * tcldesk/cpanels.tcl: Using tk_chooseColor instead of dskColorDialog. 2004-04-13 J. Chris Coppick * tcldesk/config.tcl: StrictMotif look-n-feel now off by default. 2004-04-13 J. Chris Coppick * libdesk/ot.c: cast return values of Tcl_GetVar and Tcl_GetVar2 2004-04-13 J. Chris Coppick * libdesk/tixImgXpm.c: postscriptProc member added to Tk_imageType structure init cast fixes 2004-04-13 J. Chris Coppick * libdesk/ot.h: Removed HAVE_TCL8_0_H cpp conditional 2004-04-13 J. Chris Coppick * libdesk/libdesk.h: Removed HAVE_TCL_8_0 cpp conditional. 2004-04-13 J. Chris Coppick * libdesk/init.c: dropped support for Tcl 7.x TkDesk version change 2004-04-13 J. Chris Coppick * libdesk/dsk_ls.c: some type cast fixes 2004-04-13 J. Chris Coppick * tcldesk/doc/: Guide, guide-1.html, guide-10.html, guide-11.html, guide-2.html, guide-3.html, guide-4.html, guide-5.html, guide-6.html, guide-7.html, guide-8.html, guide-9.html, guide.html: Regenerated to reflect changes in source SGML file. 2004-04-13 J. Chris Coppick * tcldesk/doc/License: Minor copyright change. 2004-04-13 J. Chris Coppick * doc/guide.txt: Regenerated to reflect changes in source SGML. Linuxdoc tools aren't quite working right (possibly user error), so a bit of munging was required to make the text acceptable to the TkDesk help browser. 2004-04-13 J. Chris Coppick * doc/guide.sgml: Version, dates, mailing lists, homepage, etc. changes... Some minor rewriting to reflect changes in default configuration, etc., and some rewriting for the heck of it. 2004-04-13 J. Chris Coppick * doc/: cd-tkdesk.1, ed-tkdesk.1, od-tkdesk.1: Version, copyright fixes... 2004-04-13 J. Chris Coppick * doc/tkdesk.1: Fixed version, some grammer... 2004-04-13 J. Chris Coppick * doc/guide.ps: Dropping postscript version of the guide from the distribution. 2004-04-12 J. Chris Coppick * tcldesk/configs/: AppBar_Be, AppBar_Games: Minor updates to stay somewhat in sync with the standard AppBar. 2004-04-12 J. Chris Coppick * tcldesk/configs/AppBar: Mostly minor updates and tweaks. I never really cared much for that comet... 2004-04-12 J. Chris Coppick * tcldesk/images/desk2.xpm: New desk'ish icon. 2004-04-12 J. Chris Coppick * contrib/README: Wording update... 2004-04-12 J. Chris Coppick * contrib/appbar-dialup.tcl.xisp: Contributed alternative version of appbar-dialup.tcl. 2004-04-12 J. Chris Coppick * contrib/mail_hacks: This was a duplicate of the mh_hacks file. 2004-04-12 J. Chris Coppick * tcldesk/images/: maildog_empty.xpm, maildog_new.xpm: Contributing my maildog icons to TkDesk... 2004-04-09 J. Chris Coppick * tcldesk/help.tcl: Another logo fix - removed non-existent bgcol var ($#@!! cut-n-paste error) 2004-04-09 J. Chris Coppick * tcldesk/help.tcl: Fixed logo path. Misc. copyright date fixes. 2004-04-09 J. Chris Coppick * tcldesk/util.tcl: Everybody's a critic. 2004-04-09 J. Chris Coppick * tcldesk/file_ops.tcl: Added dsk_diff_files proc. 2004-04-09 J. Chris Coppick * tcldesk/configs/System: Added tkdesk(cmd,diff) variable definition. 2004-04-09 J. Chris Coppick * tcldesk/configs/ButtonBar: Added dsk_diff_files button for browers and lists. 2004-04-09 J. Chris Coppick * tcldesk/images/twotexts.xpm: Small icon of two overlapping sheets of paper (representing a diff?) 2004-04-09 J. Chris Coppick * tcldesk/images/pda.xpm: Personal Digital Assistant icon 2004-04-08 J. Chris Coppick * tcldesk/cb_tools/bindings.tcl: Added mouse-wheel bindings for generic MouseWheelEnabled bindtag. The default mouse-wheel behavior is as follows: The mouse wheel scrolls 5 lines at a time by default. Shift + the mouse wheel scrolls 1 line at a time. Ctrl + the mouse wheel scrolls 1 page at a time. 2004-04-08 J. Chris Coppick * tcldesk/dsk_Listbox.tcl: Made the dsk_Listbox a "MouseWheelEnabled" widget by modifying the bindtags. 2004-04-07 J. Chris Coppick * tcldesk/cb_tools/bindings.tcl: Adding bindings for mouse-wheels (based on patches provided by Johannes Drechsel-Burkhard). 2004-04-05 J. Chris Coppick * tcldesk/tclIndex: Generated new version. 2004-04-02 J. Chris Coppick * tcldesk/config.tcl: When reading System config, make sure dialogs font gets stored in the Tk option db. 2004-04-02 J. Chris Coppick * tcldesk/cb_tools/bindings.tcl: Exposed privates (oh my!) for tk8.4 fake compatibility: I say "fake" because I'm cheating and using unsupported interfaces. The bindings in question here apparently exist to work around issues with the strictMotif mode, which I'm not convinced is worth the trouble. Either We'll have to find a truly compatible way to work around the issues, or drop the bindings entirely (depending on how many people care about the strictMotif mode). 2004-03-11 J. Chris Coppick * tcldesk/cb_tools/dialog.tcl: Transition away from custom dialog towards tk_dialog (avoids accessing protected vars as of tk 8.4). Now cb_dialog() wraps tk_dialog(), and all the cb_dialog wrappers call tk_dialog() directly. One consequence is that dialogs in TkDesk now respond to the key which is bound to the default dialog button (i.e. ignores the current button focus). Hopefully that's not a huge issue. Maybe we can work around it later. Another consequence is that we must use the tk option database to set the dialog fonts, but we should probably be doing that anyway. Right? 2004-03-11 J. Chris Coppick * tcldesk/cb_tools/help.tcl: Restore lazy state before returning after an error has occurred. 2004-03-11 J. Chris Coppick * tcldesk/tkpatches.tcl: As of tk 8.4, most of the procs re-defined in tkpatches.tcl have been moved into the private 'tk' namespace, and most of them have apparently been re-coded in later releases of Tk obsoleting many of the bug fixes implemented by tkpatches.tcl (no doubt new bugs were added though... :-) ). Therefore, we're removing tkpatches.tcl from the dist. as of TkDesk 2.0. This may result in some bug regressions, but we'll deal with those as they get noticed ("re-noticed?"). 2004-03-10 J. Chris Coppick * tcldesk/: List.tcl, Viewer.tcl: Removed non-tcl/tk 8.x code (and associated tests). Replaced tkMbPost and tkMenuFirstEntry (protected procs as of tk 8.4) with hopefully similar code. (There may still be some widget focus issues to work out...) 2004-03-10 J. Chris Coppick * tcldesk/Editor.tcl: Removed tkTextClosestGap (a protected proc as of tk 8.4) and went back to just using 'index @$x,$y'. Let's see if anyone notices... 2004-03-10 J. Chris Coppick * tcldesk/appbar.tcl: Replaced tkMenuInvoke (a protected proc as of tk 8.4) references with 'event generate ...' which appears to give the same behavior in this instance. 2004-03-08 J. Chris Coppick * tcldesk/clrpick.tcl: clrpick.tcl causes portability issues with tk8.4... The basic functionality is available elsewhere anyway. 2004-03-07 J. Chris Coppick * configure.in: Added config summary output at end of script (and supporting changes). More braindead Itcl library searching. Minor housekeeping. 2004-03-07 J. Chris Coppick * Makefile.in: Added -DUSE_OLD_IMAGE to CC_OPTS to "fix" pixmap support. This needs to be fixed for real at some point. Removed some cruft. Now "make clean" removes the generated version of the tkdesk main script. 2004-03-04 J. Chris Coppick * tkdesk.main: Fixed splash screen image path. 2004-03-04 J. Chris Coppick * tcldesk/update.tcl: Added a file existence check to prevent a check of a file's mtime from generating an error for missing config files (e.g. Local). 2004-03-04 J. Chris Coppick * tcldesk/config.tcl: Commented out some unused code. 2004-03-03 J. Chris Coppick * tcldesk/appbar-trash.tcl: Added checks for trashdir existence/file type. Now creates trashdir on demand. 2004-03-02 J. Chris Coppick * Makefile.in, configure.in: Updated to work with newer versions of autoconf (e.g. 2.57). Only supporting Tcl/Tk 8.x. Added override to allow use with unsupported Tcl/Tk. Added hopefully more flexible configuration logic with regard to Itcl. 2004-02-29 J. Chris Coppick - As of TkDesk 1.2.1, the source is under CVS. The 1.2.1 source is tagged "tkdesk-1_2_1" (also "ver-1-2-1") and includes some fixes released after the 1.2 distribution. Ver. 1.2.1 was never actually released. tkdesk-2.0/ChangeLog-1.x0100644000175000007640000022474410037336012013113 0ustar jccjcc TkDesk Change Log for Version 1.x: 04/26/2002 - Improved (hopefully) the way configure searches for tcl/tk/itcl installs. Added --with-tclconfig, --with-tkconfig, --with-itclconfig options for using special *Config.sh versions. Removed --enable-tcltk8 option. - Changed date format in the CHANGES file from the logical dd.mm.yy to the U.S.-centric mm/dd/yyyy. My apologies to the rest of the world, but it was giving me a headache. What else would you expect from an American anyway? After the next release, I'll expect you all to learn AMERICAN English, drink crappy light beer, and start calling soccer by its real name: "soccer". You should all be playing ice hockey anyway. And not that sissy European pass-around-the-puck ice hockey either, but some real, manly, North-American open-ice body-checking ice hockey. - We'd like to apologize for the previous entry. The maintainer evidently went off his medication. This lapse has been remedied and hopefully there will not be any future repetition of such shameful behavior. 10/07/2001 - Updated references to the TkDesk homepage URL and the maintainer email address. - Updated default Internet->Locations menu. 09/16/2001 - Removed the TODO file pending a re-evaluation of the goals for future TkDesk releases - Merged previously released fixes into ver-1-2-1: appbar-dnd.diff empty-trash.diff exit-save-dd.diff find-dd.diff - Moved the CHANGES file from tcldesk/doc to the TkDesk source root dir. Updated Makefile.in so that the CHANGES will still get installed along with the rest of the user docs. 11/14/1999 Released TkDesk 1.2 11/05/1999 - In the past few days made multiple changes to work with BLT 2.4j to be able to support Tcl/Tk 8.1 and 8.2. 09/14/1999 - In the copy dialog now accept file specifications of the forms "machine:/path" and "user@machine:/path" to allow for scp being used. 07/22/1999 - If desktop items correspond to character or block special files they are now also configured as drop targets. Useful for floppy disks, tape drives etc. in conjunction with the updated copy dialog popup menu (tar, untar, etc.). - The copy dialog's popup menu may now contain separators, denoted by entries consisting of a single "-" in the definition of tkdesk(fileops,popup). (CONFIG) - New option (in "Options/File Lists"): "Dot: Execs Regular". If switched on executable files are treated like regular files wrt popup menus and opening, and display, if their name contains a dot. Default is off. 07/07/1999 - Added a simplistic "desktop" facility to TkDesk. This is a configurable directory that is monitored by TkDesk which creates and deletes desktop items for files and directories in this dir, keeping its contents and associated desktop items (icons) in sync. The directory to use as desktop folder is ~/desktop by default, but can be configured in the "System" config file using tkdesk(deskdir). (CONFIG) - New configure flag: --enable-tkstep. This will use the TkStep libraries when linking tkdesksh (libtkstep, libXpm, libtiff). Note that the version of the TkStep library needs to be compatible with the version of Tk your compiling against. 05/15/1999 - Added new option "Expand Dir Bookmarks" to "Options/Appearance" menu. If activated (default) directories in the "Bookmarks" menu will have cascaded submenus attached to them, if not they won't. - Added new option "Start Netscape" to "Options/Confirmation" menu. If activated (default) TkDesk will ask whether to start Netscape if no answer was received during execution of a dsk_netscape command. If deactivated, a new Netscape browser will be started only if ~/.netscape/lock doesn't exist. 05/03/1999 - Editor window now are correctly centered again around the mouse pointer. - Pressing the right mouse button in the "File to edit:" dialog opens a cascading popup menu from which files can be selected. 04/30/1999 - Further fix for itcl 3.0: "return" in config bodies of public vars doesn't seem to be supported anymore. - Added menu entry "Same Dir..." to the editor's "File" menu to select a file from the cascading menu that's popped up. - Bug fix: Selecting an entry from the popup menu associated with the copy dialog's destination field gave an error. 03/12/1999 - Added new option to the "Find Files" dialog: "Fail-Mask Case Sensitive" (and changed the option "Case Sensitive" to "String Case Sensitive). This new option defaults to true, but deactivated will find all names matching the given file mask, regardless of case. 03/04/1999 - New config variable in "AppBar": tkdesk(appbar,dragger). If set to 1 (default) the small drag/minimize button will be displayed at the top/left of the appbar, if set to 0 it won't. (CONFIG) - New config variable in "System": tkdesk(file_selector_type). May be used to choose the type of the file selector used, regardless of the "strict Motif" setting. Allowed values are "tk" (Win*-like), "motif", and "default". (CONFIG) 02/06/1999 - New config variable: tkdesk(cmd,ps) set in the System config file. This is used by the "Job Control" window. (CONFIG) - It's now possible to search backward in the built-in editor. Added a new checkbutton "Backward" to its search dialog for this purpose. - New config var (System): tkdesk(editor,load_once). If set to 1 (default) files will be loaded only once, if set to 0, the same file may be loaded into multiple buffers (the old behavior). (CONFIG) - Added new entry for this config variable into the editor's "Options" menu. 02/03/1999 - New config variable: tkdesk(font,listboxes), to specify the font to be used by, um, listboxes. (CONFIG) - The internal help viewer now picks up its fonts from $tkdesk(font,text) and tkdesk(font,listboxes), as defined in the System config file. 02/01/1999 - Bug fix: The geometry of the "Periodic Execution" dialog wasn't saved properly. - Changed ed-tkdesk script to now wait until the editor window into which the file has been loaded is closed (provided the built-in editor is used). The old behavior can be obtained by using the "-q" option with ed-tkdesk (same as for gnuclient). - Activated config var tkdesk(focus_width) for entries and buttons. - New config var ("System" config file): tkdesk(scrollbar_width). Can be used to configure the width of TkDesk's scrollbars. (CONFIG) 01/17/1999 - Added "Selection" menu to the built-in editor. Entries allow to shift the selected area to the right/left, and to pipe the selection to a command, either replacing the selection with the result, inserting the result after the selection or to just view the result in a separate window. - Made bindings for accelerators/shortcuts in the editor configurable (to a certain extent). By setting the new config variable tkdesk(editor,bindings) in the System config file to "nedit", the shortcuts will be changed to match those of the nedit editor (which happen to be used also by kedit and many Windows editors). The default setting ("classic") leaves the shortcuts unchanged with respect to previous versions. (CONFIG) 01/06/1999 - Added a status bar to the editor displaying current line, column and size of the buffer. The status bar may be switched on (default) and off from the editor's Options menu. 12/29/1998 - TkDesk now allows to change user and group of files not owned by the current user by running the command through dsk_exec_as_root (which requires knowledge of the root password). - When the mouse pointer is over a symbolic link in a file list window the link destination is displayed in the floating tip window. 12/16/1998 - New config variable: tkdesk(conf_editor,cmd) in the "System" config file. This can be used to specify the editor to use for editing the TkDesk config files (previously this was only possible using the built-in editor). Its value defaults to the value of the tkdesk(editor,cmd) variable (which default to "builtin"). (CONFIG) - The config files may now be reloaded either selectively from the "Reload" submenu of the "File/Configuration" menu, or automatically, provided menu entry "File/Configuration/Reload/Reload Automatically" is checked. If it is, TkDesk checks every $tkdesk(update,file_lists) seconds to see whether any config file has been modified and then reloads only that one modified file. (CONFIG) 12/15/1998 - Rearranged the "Options" menu into a number of submenus. - Moved a few switch-like options from the "System" config file to the "Options" menu, e.g. tkdesk(ask_on_exit), tk_strictMotif etc. (CONFIG) - Now when rereading the "System" config file no longer invoke the tk_setPalette command. This took a *long* time when a lot of widgets had already been created. 12/12/1998 - Added new menu entry "Commands/Environment..." This opens a new (non-modal) "Edit Environment" dialog box which can be used to modify the environment of TkDesk and processes started from it after the modification. 12/10/1998 - Removed 2 memory leaks from dsk_ls.c. There still seems to be a small leak in Tk's text image display code, so with "Add Icons" switched on you may still see TkDesk's memory consumption growing over time (very slowly though). - Removed another "leak" from appbar-load.tcl: load bars moved off the visible area weren't deleted properly, leading to a lot of unused canvas items consuming memory 12/03/1998 - Enhanced the file list tool tips to display the annotation in addition to the file name, if one was added to the file the mouse pointer is over. - Further enhanced the tool tips for files ending with ".url" (these are used for storing WWW URL's): The tool tip adds the URL contained in that file to its display. 11/30/1998 - Bug fix: TkDesk now correctly handles paths like /home/user2, where $HOME is /home/user (these were transformed to ~2 before). - Now display nice "screen tips" windows (a.k.a balloon help) when the mouse pointer is inside a file listbox, and over a file whose name is not complete visible. Similiar to what Netscape and Micro$oft do with their multicolumn listboxes. The tips are displayed only if "Options/Help Options/Balloon Help" is on. 11/24/1998 - When tkdesksh has been built using the Tcl/Tk 8.0 libraries use real image annotations in the file listboxes instead of label widgets. *Much* faster! - Changed default AppBar config file to have the mail header flasher to be disabled by default, as some people found it irritating and currently it's not obvious how to disable it. (CONFIG) 11/18/1998 - The background color of editor windows is no longer changed when read-only files are displayed, I realized that I never really liked that. So, the only indication now is the title bar saying "read-only" and the "Save" brings up the file selection dialog. 11/15/1998 - The "Find" dialog now updates the results listbox during the search, and files can be selected from there while the seach is still in progress. 11/14/1998 - Port is almost complete! Just need to do some thorough testing to track down the remaining compatibility issues, and after that TkDesk on Tcl/Tk 8.0 will be a reality! - When creating new files or directories now check whether the file or dir already exists. - Added "Open With..." to the "File" menu of browser and list windows. - F11 now posts the menu of the rightmost file listbox. 11/12/1998 - Bug fix: "Save all" in the editor always saved the contents of the active buffer to all modified files opened in the same editor window. - Typo in config.tcl: wrote "mkdir" instead of "exec mkdir". 11/07/1998 - Began some serious work on porting TkDesk to Tcl/Tk 8.0 and itcl 3.0 (and BLT 2.4). The current plan is to make the next release compatible with all of Tcl/Tk 7.5, 7.6, and 8.0, and make a cut after that to clean up the sources and move the code to the new itcl syntax. =========================================================================== 09/20/1998 Released TkDesk 1.1 09/20/1998 - Using "." in menu definition lists makes this menu tear-off'able (much in the same way as "-" is used to add a separator). (CONFIG) - Some minor editor/hypersearch fixes. 09/02/1998 - Brought the User's Guide up to date 08/30/1998 - Changed selection search in the editor (Meta-space) to not interpret the selection as a regexp, but as a straight string. Also added the capability to restart at top if no more matches are found. - "Case sensitive" and "Regular expression" checkbuttons in the editor's search window may now be activated thru Ctrl-c/Ctr-r. 08/26/1998 - Added a new option to dsk_find_files: "-doit". This immediately starts the find process when dsk_find_files is invoked. (CONFIG) - The "TkDesk" menu that appears in the editor's menu bar when config files are edited is now labeled "Configuration". It also received 2 new entries to load other config files and search through all of them. - Modified the dsk_edit and ed-tkdesk proc/command so a line number can be passed using vi/emacs notation, e.g. "dsk_edit +10 ~/.xinitrc". (CONFIG) - Added to keyboard navigation: Menu (available on W95 keyboard, for instance). Pops up the currently selected file's popup menu that may also be operated with the keyboard. 08/17/1998 - Added drag/toggle "thingie" to appbar to move the appbar around and minimize it. - Added a "Find..." entry to the "Configuration" menu. Lets you find occurences of a certain string in your configuration files. - Fixed Y2K bug in dsk_ls.c. Thanks to Stephane Legrand for reporting. - If the proc dsk_cb_callback is defined (e.g. in the "Local" config file) if will be called immediately before a new directory will be entered. The path of this directory is passed as the first argument to dsk_cb_callback. (CONFIG) - Added "testdrive" script as suggested by Peter Funk. Thanks Peter! 06/03/1998 - In all dialogs displaying an entry field with a history popup attached to it the latest entry is put in the entry field by default. - Improved appbar's dialup-button: The state of the link is now monitored using the ifconfig command, and a dialog is displayed that asks for a phone number to dial, which will be passed to the command specified in tkdesk(appbar,dialup,cmd_up). Phone numbers entered are kept in a history list. (CONFIG) 05/24/1998 - Made TkDesk server much safer by storing a key in /tmp/tkdesks* that needs to be passed by the client. - Added some entries provided by Dave Cinege to the Popup config file. 05/19/1998 - TkDesk's built-in server now removes all removable /tmp/tkdesks* files on start-up. - Fixed (well, quite temporarily fixed) a problem with an editor window being deleted if a unreadable file was tried to be loaded using "File/Load". 05/12/1998 - Added to keyboard navigation: PgUp, PgDown, Backspace (goes one level up). - Fixed communication between TkDesk server and tkdeskclient: the associated channel is now set to non-blocking, which allows tkdeskclient to read the server's response (i.e. the return value of the executed command). 05/07/1998 - First cut at keyboard navigation: Up, down, home, end keys do what you would expect, a-z cycles case-insensitively through items in the rightmost file listbox, selecting them at the same time. 05/03/1998 - If the editor's HyperSearch dialog is open its search function is automatically invoked when switching to another buffer. Would like to change the regexp as well based on the type of buffer the user switches to, but we don't have that yet. - Now use Tcl's own "clock" command for displaying the current time in the appbar. - Fixed bug with large integers being used as file/directory names. 04/05/1998 - Fixed bug where AppBar became visible and decorated by the window manager when the mail button was created. - Fixed bug "invalid variable name dsk_read_string" when dsk_read_string was used from ButtonBar or Commands config files. - Minor fixes for reloading Commands config file and reloading config files from the editor. - Changed default value of "Auto-save Layout" back to "on", as "off" seemed to cause even more confusion... - Added John Williams' popup menu for ZIP'ed files. Thanks! =========================================================================== 02/26/1998 Pre-released TkDesk 1.0 02/24/1998 - The AppBar's variable tkdesk(appbar,mail,notifier) may now be set to "bell" to let X's bell ring at arrival of new mail. - Changed default to add icons to file lists. Hey, it's become faster since John wrote his article! ;-) - Fixed bug in bltBgexec.c which could lead to an "IOT trap" when being compiled against Tcl 7.5. - Reactivated "cat %s >/dev/audio" as the default sound playing command, but kept "Options/Use Sound" switched off by default. 02/22/1998 - New command line option "-layout file": this loads TkDesk's window layout from "file" and saves it there as well. - TkDesk now remembers which AppBar definition was loaded last when restarted. The configuration menu's entry "AppBar" now also loads the current appbar definition file. - Fixed 2 bugs in the editor's search/replace function. 02/18/1998 - Fixed bug in misc.c where a statically sized buffer was used which could get too small. Now allocate memory dynamically. - Now the popup menu for executables is used for all executable files again. But now add the "Edit" entry for all files which "file" thinks are text or a script. An alternative would be to merge both executable and regular popups, might add this in a later version. - The "Path" and "File-Mask" fields of the "Find Files" dialog now allow multiple paths/masks to be entered by separating them with a space. If paths or masks should contain spaces, they now need to be surrounded by quotes. 02/16/1998 - Added a "Manual Page" entry to the "Help" menu. - Added "Subdirectories" and "Files" cascading submenus to the path entry popup menu of file browser and list windows. - Actions executed from a file's popup menu now execute in that file's path, as opposed to TkDesk's current directory. - Fixed bug: sometimes it was impossible to drop further icons on the desktop due to a missing check. - When clicking "Browse" in the file-selection "pre-dialog", a standard file selection dialog is now opened instead of a TkDesk file list window. Might reactive and refine this in the future though. Tk's built-in file selection dialogs are used if available. 02/11/1998 - The editors default geometry (System: tkdesk(editor,default_geometry)) now may also be specified in the form {w}x{h}[+-]{x}[+-]{y} rather than just {w}x{h}. 01/29/1998 - If directories are put into the "Bookmarks" menu they are added as cascading submenus. - Added two tkdeskclient scripts: op-tkdesk, to open the specified files using their default actions, and pop-tkdesk, to open the specified file's popup menu. - History menus now may also contain an "infinite" number of entries. - Now tkdesksh checks for the DISPLAY environment variable to be set before trying to initialize Tk. 01/20/1998 - Added a "Save" button to the "mail bug report" dialog. - Fixed bug where files containing dollar signs or brackets couldn't be opened from the "Bookmarks" menu. - Bug fix: If nothing was entered in the text field of the "Delete" dialog, the directory currently displayed was deleted.... =8-O 01/12/1998 - Put a new option into the "Options" menu: "Autoraise AppBar". If switched on, the AppBar is automatically raised above all other windows if the mouse pointer enters it. 01/07/1998 - Added menu entry "Execute as root..." to the "Commands" menu and to the file listbox menu. - Removed option "Exec as Superuser" because of above menu entries. - Put the file name into the label of the "Rename" dialog. - Reactivated the appbar's xemacs button coz it required some adjusting to work with b5. 01/04/1998 - Fixes for building TkDesk under Solaris and IRIX. Thanks to Chris Coppick and Chris Sterritt. - The "Info" dialog for the "." and ".." entries now displays the actual directory names rather than "." or "..". - Editor: made cut and paste operations undoable. 01/03/1998 - Fixed bug with updating of the trash disk usage display in the appbar (resulted in a syntax error if usage > 1MB). Thanks to everyone who pointed me to this problem. - Fixed bug in configure script that wouldn't pick up the directories supplied with --with-tcl and --with-tk options. 12/10/1997 - Moved up default setting of tkdesk(library) in tkdesk. The previous location made "test driving" impossible. - Incorporated John Blair's proc "dsk_exec_or_raise" which can be used to deiconify and raise an already running Tcl/Tk application, rather than launch it a second time. It takes three arguments: the interpreter to check for, the command to execute if it's not already running, and optionally the window to raise if it is running. See the default "AppBar" config file for two examples. (CONFIG) =========================================================================== 12/08/1997 Released TkDesk 1.0b5 12/01/1997 - Added new function dsk_path_view. Does exactly the same as dsk_path_exec except it displays the output of the command that's been executed in a given path. Syntax is "dsk_path_view path command". (CONFIG) - Added an entry "Patch" to list tkdesk(fileops,popup) defined in the "Popups" config file. When "Patch" is selected in the "Copy, Move, ..." dialog the source file will be fed into the "patch" command which will be executed in the directory given in the "Destination" entry. (CONFIG) - Introduced environment variable TKDESK_LIBRARY. This may point to the path from where TkDesk should read its run-time library files. 11/30/1997 - dsk_open_dir now returns the object name of the created window. - Added two new functions: "dsk_exec_as_root" and "dsk_view_as_root". Both work exactly like "dsk_exec" and "dsk_view", but use the commands specified in the new config variables tkdesk(cmd,su,exec) and tkdesk(cmd,su,view) (defined in the "System" config file) to put something like "su root -c" before the actual command. This way selected commands may be run with superuser authority (provided you know the password...). (CONFIG) - Added a new entry to the "Options" menu: "Exec as Superuser". If this entry is checked, all commands will be run using the tkdesk(cmd,su,*) variables (see previous item). - Removed the "Local" config file from the distribution. It's really supposed to be used for local extensions and for that reason TkDesk shouldn't depend on code that's in there, really. - The "Use Sound" entry in the "Options" menu is now disabled if no command for playing sound has been defined in the "Sounds" config file (this is the default). (CONFIG) - Added "TkDesk" menu to the file list window menu bar. - Slightly rearranged the "File" menu. - Added an entry "Copy Names Only" to the "File" menu. This copies only the names, not the paths, of all selected files to the X selection. - "." and ".." in paths are now "expanded" before being displayed in a file browser of list window. 11/24/1997 - Extended dsk_netscape to support Netscape 4.0 ("Communicator"), based on code sent to the TkDesk mailing list by Paul H. Wilson. Thanks! The new options are "dsk_netscape rcmd mail|news|send|edit". See default "AppBar" config file for example usage. (CONFIG) 11/23/1997 - The file listbox that displayed the active directory when a command was started is now refreshed when the command exits. - Added a "Remove Entry" button to the "HyperSearch" dialog to remove an expression from the history menu. - The usual file and directory popup menus are now also accessible inside the history menus, the cascading directory and files menus, and the bookmarks menu. They are accessed by pressing the left and right mouse button at the same time (it doesn't matter if one of the two buttons is pressed and held before the other one is pressed). (Oh, MSIE4 has a similar feature? I didn't know... ;-)) - When files are dropped on desk items representing executables the "Quick Drag'n'Drop" option is now honored. - "Tkdesk should now be 100% dockable/wharfable!" Thanks to Kenn Persinger. - The "Close" and "Refresh" buttons are now disabled in the "Disk Usage" window during refreshing. - The right mouse button pressed in the disk usage listbox now brings up the usual directory popup menu. - The "Options" menu has a new entry: "Status in List Windows". This determines whether the file list windows provide a status bar similar to that of the multi-column file browser windows. 11/22/1997 - The editor's "Buffers" menu is now sorted alphabetically and displays the full path of each buffer in braces. - The buffer menu and the cascading directory and files menus now have a "More..." entry at their end if not all entries fit into one menu. That entry opens another menu, and so on. This allows for a virtually infinite number of entries... - Added some editor related entries to the "Commands" menu. - The "Search" and "Replace" fields of the respective editor dialog now also maintain a history list. - Added some editor related entries into the "Commands" menu (including a "Buffers" submenu for the built-in editor). - Added J. Chris Coppick's "mail header flasher" to the appbar's mail button. Please look into the default "AppBar" config file to find out how to configure it. (CONFIG) 11/10/1997 - Changed the "Browse" button of the file selection dialog to open a standard non-modal TkDesk file list window (not for "Save as"). Files can be dragged from here and dropped onto any open editor window, as usual. Might refine this a little in the future. 11/09/1997 - Added a new variable tkdesk(cmd,mail) to the System config file (CONFIG). - Added proc dsk_mail that's used for the standard file popup menu (entry "Mail to..."). (CONFIG) - Added an entry "Mail to" to the editor's File menu to mail the contents of the current buffer. - Added a "Find..." menu entry to the editor's File menu to find files in the directory of the current file. 11/06/1997 - Added a history and "View Output" option to file listbox's "Execute here" menu entry. - Removed variables tkdesk(file_lb,min*) from System config file, they shouldn't be used anymore. (CONFIG) - Added TkInfo to the default AppBar. 10/23/1997 - Added a "Locate..." entry to the appbar "computer" icon. Thanks to Graham Todd for making this available. 09/01/1997 - Integrated John Blair's really nice RCS extension into TkDesk. For example usage, take a look at the "Popups" config file (look for jdb_rcs). (CONFIG) - Also added his patch for letting the middle mouse button clicked on any of the permission buttons in the info window copy the respective setting to the corresponding button of the other permission group (like owner, group, world). - Added a Makefile popup menu to "Popups". (CONFIG) - Based on a proposal sent in by David Nugent, added "%a" to list of shortcuts. As "%A", it expands to the names of all selected files, but without the path names. (CONFIG) - A new variable tkdesk(at_exit) may now be set in the "System" config file that can be set to a script which will be evaluated right before shutdown of TkDesk. (CONFIG) 08/31/1997 - "Set Mask": multiple patterns separated by spaces may now be specified, and a checkbutton "Invert Mask" has also been added to display/select all files NOT matching any pattern. 08/28/1997 - Fixed blatant bug where undo didn't care about which buffer was currently displayed in an editor window. Now undo works on a per buffer basis. The insertion cursor is now also placed properly after an undo. - Greatly improved the "About" box ;-). - The built-in help browser now contains a "Print" button to du just that. 08/27/1997 - New shortcut for config files: "%X". Behaves just like "%x", but doesn't complain if the X selection is empty, and expands to an empty string then. (CONFIG) - Based on a suggestion of Dave Cook, the appearance of the time display may now be configured in several ways: background and foreground colors, borderwidth, and relief. The corresponding variables are defined in AppBar and are called tkdesk(appbar,time,...). (CONFIG) - Following another suggestion made by Graham Todd, added a new variable to the System config file to define the location of the trash directory. Its name is tkdesk(trashdir). (CONFIG) 08/24/1997 - The button bar of file browser and list window may now span multiple lines. Line breaks need to be inserted explicitly by a button definition of "\n" or "break" (similar to the "-" separator definition). (CONFIG) 08/18/1997 - Files that have an annotation associated with them are now underlined in the file listings. - The first 30 characters of an annotation is now included into the file's popup menu, right after the file name. - Added a popup menu for RPM's, thanks to John Williams. 08/13/1997 - Fixed bug where drag&drop token windows could stay on screen, in the worst cases leading to "Bad Window" X errors. 08/11/1997 - Fixed bug with undo not working when pasting text using the mouse into the editor. - An already opened file information window is now reused to display info about subsequently selected files. - The editor now has a "Quick Load" option that allows you to load files into that editor window by just selecting them in a file listbox. - Replaced configuration option tkdesk(editor,word_wrap) with tkdesk(editor,wrap). This can be set to "word", "char", or "none". There's an entry in the editor's Options menu as well. (CONFIG) - Some minor bug fixes. 08/10/1997 OK, I was pretty sloppy with this ChangeLog in the past 3 months, so here's just a quick rundown of changes and features added since May, as far as I can remember... - TkDesk now uses GNU's autoconf for building and installation! This means a simple "./configure; make; make install" should compile and install TkDesk on most any platform withouth a hitch. - Extended popup menu of path entry field to give access to sibling directories. - Added cascading popup menus containing both directories and files to the standard directory popup menu. These may also be added to the "Directories" menu by preceding the directory in the "Directories" config file with an "&" instead of an "*". (CONFIG) - Cascading directory popup menus may now also be added to the appbar, using entries like {&/etc}. See "AppBar" config files for an example. (CONFIG) - The labels of desk-items are now broken into multiple lines at more pleasant places. The line-width of a desk item is set in "System" by tkdesk(desk_items,label_width). (CONFIG) - Revised the find dialog: o Only one path may be entered, as it might contain spaces. o Directories may be dropped over the "Path" field. o If files that contain a certain string are now double-clicked the editor now auto-scrolls to the first location of the search-string. o Added "Owner" and "Group" fields. o The proc dsk_find_files may now be called from any configuration file, with the following arguments: -path , -file , -string or -regexp or... You get the picture. This lets you add preconfigured queries to your appbar, Commands menu etc. (CONFIG) - Added a server to TkDesk. A new stand-alone C program (tkdeskclient) connects to this server to execute Tcl/TkDesk commands. All the xx-tkdesk scripts now use this clients as its much faster than the Tk "send" command. The server can be switched on and off from the Options menu. - The appbar's new dialup-button now displays the time in seconds that the link has been up. - Added entries to the editor's TkDesk menu (appears when a configuration file has been loaded) to load the default version, and evaulate the currently selected lines. - Added more entries to the same menus to configure fonts, colors, icons, and sounds through GUI dialogs. Hey, the first step at a GUI configuration!! OK, the handling of these dialogs is not yet extremely intuitive, but at least consistent: At the bottom, they all have three buttons: "Insert", "Select", and "Close". "Insert" inserts the selected item at the current cursor position in the editor window, possibly replacing any previously selected text. "Select" copies the selected item as a string to the X selection, allowing for pasting the item at arbitrary places. "Close" does just that. - Changed back to auto-loading of TkDesk's tcl library. Split some of the larger files to have smaller "auto-load units". Results in improved start-up time. 05/05/1997 - Changed default configuration to not automatically save the window layout on exit of TkDesk. This seems to cause too much confusion. 05/04/1997 - The trash button in the application bar now optionally also displays the disk usage of the trash folder if tkdesk(appbar,trash,label) is set to 1 in "AppBar" (default). (CONFIG) - The "Print" dialog now also maintains a history. Also added a printer button to the default configuration bar. - Added a "dial-up" special button to the default application bar. It displays the status of the dial-up connection by using two different icons. Related to this button is a new command "dsk_dialup" that toggles the status of the link by executing a command to either bring the link up or down. The button is controlled by variuos variables (tkdesk(appbar,dialup,...)) in "AppBar". (CONFIG) 05/03/1997 - Performance improvement: Directories are now read and tagged in about half the time that was required before. No kidding! - Made icon display ("Add Icons") much faster by creating the actual icons only when they become visible (after thoroughly studying the "text" manual page). - All scrollbars in the file browser and list window can now be displayed only when they are required. Whether to have dynamic (default) or static scrollbars is controlled by the variable tkdesk(dynamic_scrollbars) in "System". (CONFIG) 05/01/1997 - The "TkDesk" menu that appears when editing configuration files now contains additional menu entries for selecting colors, fonts, icons, and sounds. Hopefully makes configuration just a little bit more convenient. 04/29/1997 - Changed selection bindings in the file listboxes to follow the Motif and Tk standards more closely: non-contiguous selection is now bound to , while does range selection. The old bindings may still be used by setting the variable tkdesk(use_old_modifiers) in "System" (CONFIG). 04/28/1997 - The -fvwm command line switch has now become the default and is thus obsolete. This switch was used to switch on icon windows for TkDesk. This can be switched off now by the new -twm switch. - TkDesk now displays the available command line options if "-?" or "-help" is the only argument, and exits. 04/19/1997 - dsk_view now also captures the standard error channel of the executed command. 04/05/1997 - New config variable: If "tkdesk(after_startup)" is set to a Tcl script (in config file "System") it will be evaluated right after start-up of TkDesk has completed. (CONFIG) - The "TkDesk" menu that appears when a TkDesk config file is edited contains another new entry: "Load Default Version". This loads TkDesk's default version of the current config file. - Made configuration of fonts and colors MUCH more stable. If a font or color config is invalid, a message is printed to stderr, and a fallback is used. Now TkDesk should never break again because of missing fonts or colors. Hopefully. 04/04/1997 - If "Add Icons" is selected the icons take on the "selection color" if the corresponding file/directory is selected. - The popup menu of the path entries in file browser and list windows now gives also access to all subdirectories of each parent directory. - Major clean-up of fonts and color used by TkDesk. Removed virtually all hard-coded font and color definitions (there are just a few left in the appbar "specials"). Please take a look at the new default "System" config file as some new variables have been introduced, while others have been removed or renamed. New variables are assigned default values if not set in this config file, old ones are retained for backward compatibility. (CONFIG) - The "TkDesk" menu that appears when a TkDesk config file is edited contains a new entry: "Evaluate Selection". This evaluates any selected expression in the current buffer in the global scope. Useful for quick changes of the editor, utility commands etc. 04/03/1997 - Fixed bug in file selector: file names starting with ~ caused it to choke. - New feature: the "Copy etc." dialog now contains a popup menu giving access to other file operations than copy and move. This menu defaults to link, symlink, diff, untar and cat. It can be configured by defining the list tkdesk(fileops,popup) in "Popup" (CONFIG). Take a look at TkDesk's current default config for an example. - Fixed bug with backspace and undo. - Improved handling of file names containing spaces and brackets. - The popup menu of directories now contains a "Traverse" submenu with the entries "Directories" and "... and Files". The latter also displays the files of each directory. - Directory entries in the "Directories" config file may now also be preceded by a "&". This causes the files to be included in the recursive submenus. (CONFIG) - "AppBar" config file: If an entry has just one element and this starts either with "*" or "&", it is treated the same way as definitions in the "Directories" config file. E.g.: {&/usr/tmp} will create recursive submenus containing directories and files. (CONFIG) 03/23/1997 - TkDesk now remembers the scrollbar position and file selection when file listboxes are refreshed. 03/17/1997 - If entries of the history and bookmarks menus of the application bar are invoked while holding down the Shift-Key, a desk item for invoked entry is created at the current mouse position. - Control-doubleclick on desk items invokes the "Open with" dialog. - The default appbar now contains an entry to lower the appbar. (CONFIG) 03/13/1997 - The editor no longer requires user input if a file is opened read-only or doesn't exist. If it is read-only the title bar contains the string "(read only)" and the window background is the default background color. The buffer is still editable, but selecting "Save" invokes "Save as". 02/24/1997 - Bug fix: Clicking on an icon in file list windows lead to an error. - Many other minor bug fixes. - You can now select different configurable application bars from the default appbar. Look at the definition of the "Application Bar" submenu in the "comet" popup menu for an example. (CONFIG) 01/29/1997 - The editor now uses a Win95'ish 2-phase file selector: the first phase is a simple history-enhanced text entry dialog, whilst the second is the old Motif-style file selector we all love and hate. Yes, I might switch to using the Tk ones at some point. - The variable tkdesk(file_tags,ignore) can now be set in the "FileTags" config file to not display certain files (unless "Show All Files" is set). 01/27/1997 - It is now possible to have directory-specific button bars in the file browser and list windows. The variable to set is tkdesk(dir_button_bar) in the "ButtonBar" config file. Scroll to its end for an example. (CONFIG) 01/26/1997 - Changed code back to dynamic loading of library Tcl files. Reduces start-up time especially for those starting up with the appbar only. - Added bindings for file-specific popup menu to third mouse button in editor windows. - Now a double-click in path entries of file list and browser windows is enough to X-select the complete path. - If nothing is selected and "Add Bookmark" is invoked, the current directory is now added to the Bookmarks menu. 01/21/1997 - Bug fix: if a directory was selected and pressed, the window didn't change its directory. 01/14/1997 - Fixed major memory leak in dsk_ls.c, discovered by J. Chris Coppick. 01/13/1997 - Bug fix: If a file was selected for copy, delete, etc., and its name was changed in the respective dialog still the originally selected file was copied or deleted etc. - Minor bug fix with searching for an annotation. - Fixed bug with 12 hour time display in the appbar. Kudos to Chris Coppick. =========================================================================== 01/06/1997 Released TkDesk 1.0b4 01/03/1997 - Fixed bug with layout saving of toplevel windows. - Fixed bug with update of the appbar's recycle button. 12/21/1996 - Added XEmacs button and extended XEmacs communication proc to the default configuration. Both had been proposed by Erez Strauss (erez@newplaces.com). (CONFIG) - The default appbar's Netscape-button now contains an entry "X-Selection/Desk Item...". This allows for creating desk items that correspond to URLs that have been X-selected (CONFIG). - Wrote a short QuickStart guide that's displayed when TkDesk is started for the first time. It's also available via the "Help" menu. 12/18/1996 - There's a new option now under the "Help" menu: "Use Netscape". TkDesk will use Netscape to display its online help only when this entry is checked. Its setting is auto-saved. - There's a new configuration file: "Local". This should be used as the only point to add your custom code or other fancy code you write to extend TkDesk. This has been suggested by John Blair and is considered a Good Idea. (CONFIG) - The string used in the title of file browser and list windows can now be configured in config file "System". See comment in the default System file for more info (search for "in the title"). (CONFIG) - Swapped the XPM icons used for file browser and list window as someone suggested that it makes more sense this way, and I think he's right. - Removed bug where files couldn't be dropped on the desktop if the internal help window was iconified. 12/15/1996 - Fixed problem with appbar-only startup. - %A-substitution now better deals with spaces in filenames. - The default AppBar's action "New File" now creates a new buffer without any further questions. Thanks to John Williams for this one. 12/10/1996 - File listboxes now scroll to make newly created files or directories visible. - Added "Save all" entry to File-menu of the editor. Does what you think it does. - TkDesk now overrides Xresource settings if tkdesk(color,basic) is set (by default set to #d9d9d9). This was necessary to better cope with standard CDE settings which produced white on white windows. 12/08/1996 - New boolean config variable for 12-hour display instead of the default 24-hour display in the AppBar: tkdesk(appbar,12hour). (CONFIG) - TkDesk didn't use $tkdesk(path,images) to find the icons defined in FileTags for the file listboxes. Now it does. - Added bindings to deal with "dead" keys of non-US keyboards. - Improved handling of directory names with spaces - again! 12/07/1996 - Fixed bug with determining free space on NFS-mounted filesystems. - Display of quota is no longer supported. - Removed possibility of eternal loop if current directory is invalid. - The listbox icons (if "Add Icons" is turned on) now behave more like the textual items, esp. regarding drag&drop. In fact, the only difference is that drag-selection is not yet possible via the listbox icons. - Parsing of the "uptime" command should now be more stable (hopefully). - "Clone Window" now creates a browser window with the same number of file listboxes as the one where the entry was invoked. 12/04/1996 - Removed binding for and from the file listboxes as it got in the way with drag'n'drop bindings. - New checkbutton in the editor's search dialog: "Regular Expr.". This defaults to off as most users will never use the regular expression feature, and search strings may be invalid regexps (e.g. "*/"). - Minor fixes. 11/28/1996 - Made TkDesk work with Tcl 7.6/Tk 4.2. Thanks to Peter Waltenberg for valuable hints. - The balloon help now behaves more like Netscape's does. 10/23/1996 - The delete and copy dialogs now also can handle file specifications containing * and ?, for instance to delete all object files in the current directory. 10/21/1996 - Minor fix in itcl/configure to not remove config.status on exit on some systems. - Auto-save files of the built-in editor (#...#) are now deleted when the file is saved by the user... - New option for the editor: "Create Backups". If this is not checked no backup files will be created. The default can be set in the editor section of the "System" config via the variable tkdesk(editor,do_backups). (CONFIG) - If a file is not writable by the user it is opened read-only. The associated buffer won't contain a cursor. - Editor windows now obey the "Windows At Cursor" setting. - New editor variable in "System" config file: tkdesk(editor,background) to set the background color of editor windows. (CONFIG) 10/20/1996 - New %-shortcut contributed by John Blair: %c will be replaced with the filename's basename: /abc/def/ghi.j ==> %c = ghi (CONFIG) - Bug fix: when switching buffers in the built-in editor an additional newline was added. 10/06/1996 - Improved tab handling of the built-in editor: Either "real tabs" can be inserted or a configurable number of spaces. - If "Auto Indent" is active in the editor, the Tab key binding is automatically invoked if the last character of a line is "{". =========================================================================== 09/26/1996 Released TkDesk 1.0b3 09/25/1996 - The "Execute..." entry of the appbar's comet button's popup menu is now able to also handle directories and files. - Added a new "special" button to the AppBar: "special:trash". This button's image corresponds to the fill state of the trash can. The images to be used can be configured in the AppBar via tkdesk(appbar,trash,empty) and (appbar,trash,full). (CONFIG) - Added an entry "Bookmark" to the popup menu of each file/directory to add that item to the bookmark list. 09/24/1996 - Converted all VOC sound files to AU sound files as these can usually be directly sent to /dev/audio (via "cat %s >/dev/audio"). The drawback of this move is the lower quality. You can pick up higher quality VOC and WAV files from my TkDesk Web page. - Changed default sound command to cat %s >/dev/audio. - Changed the Makefile: all *LIBDIR settings now start with -L to allow for empty settings if the libraries are in default search paths. - The "Execute..." entry of the appbar's comet button's popup menu is now able to also handle directories and files. 09/23/1996 - Picked up another suggestion from the list: as an alternative to dsk_exec one can now use dsk_path_exec to have a command executed in a certain path, for instance the home directory. Apart from that, dsk_path_exec is identical to dsk_exec. The syntax is: dsk_path_exec path cmd. (CONFIG) - The menubutton above every file list symbolizing the displayed directory can now also be dragged around, and dropped onto the root window. 09/22/1996 - Files can now be inserted into a buffer of the built-in editor. - File names copied to the X selection (via "Copy to X Selection" or the "X" button of the button bar) are now separated by blanks instead of newlines to allow for shell command line usage. - Due to popular demand I changed the handling of executable files as follows: If an ex. file has an extension, e.g. ".c", it is handled like a regular, non-executable file. If the file does not have an extension, it is handled as usual: as an executable file. If you have executables with extensions and don't want them to be handled as regular files, you can add an appropriate entry to the tkdesk(popup,regulars) list. - Major CONFIG change: Everything regarding the appearance of directories and files is now contained in the config file FileTags. Settings in the System config file like "set tkdesk(color,directories)" will be overwritten from the settings in this file. In addition to the list tkdesk(file_tags) two more lists have been defined for directories and executables: tkdesk(file_tags,directories) and tkdesk(file_tags, executables). The format of each element of any of these lists is: {{patterns} {color} {font} {list_icon} {desk_icon}}. The latter three subelements are optional. - The position of the vertical scrollbar of file list windows is now remembered for each visited directory and window. 09/21/1996 - TkDesk no longer uses "exec date" to get the current time, but has now its own C-function for this. This should remove the "fork: resource unavailable" messages on some platforms after TkDesk had run for a few days. - If there is a running Netscape, TkDesk will use this one to display the User's Guide etc. This has been suggested in the mailing list. The AppBar config file had to be slightly modified: all "dsk_cbhelp" lines needed to be changed. (CONFIG) - The default AppBar now contains a new entry under the "Edit" menu: "Buffers". This is a submenu listing all buffers of the built-in editor. (CONFIG) 09/18/1996 - Programs started from TkDesk now inherit the standard output channel from TkDesk. Previously everything printed to stdout was simply discarded. - The "Exit" message printed in file browsers' message bar now contains the program's exit code, like "Exit (0): ...". 09/16/1996 - New binding in file listboxes: selects all files having the same extension as the one that is clicked on. This provides a handy way to quickly select all files of a certain type. - The default image type is now XPM and no longer XBM. This allows for using the Motif/CDE pixmaps directly. - NEW MAJOR FEATURE: Files may now be dragged off any file list (including the one of the "Find Files" dialog) and be dropped directly onto the root window a.k.a. desktop to create icons! These desk items (as I prefer to call them to avoid confusion with minimized windows) behave exactly like file list items, i.e. they provide the usual popup menus (with an entry "Remove Item" added), drag&drop facilities etc. 09/10/1996 - Icons for file browser/list windows etc. can now be larger than 32x32. - Improved behavior when renaming/copying/moving files the user does not own. - New option "Ask On Delete". This determines if the "Delete" dialog appears even if "Really Delete" is not selected. 09/08/1996 - Built-in editor: o A backup copy is created when a file is saved (filename~). o Every 500 inputs/deletes an auto-save file is created (#filename#). o At last: Implemented a virtually unlimited UNDO functionality! This is certainly not finished yet, however, it should be mature enough to be useful. o New option: word wrap. Default can be set in the System config file by setting the variable tkdesk(editor,word_wrap). (CONFIG) - The "Open with" dialog now uses the same dialog as "Execute". - The "Execute" dialog now contains a checkbutton "View Output" to view the command's output *after* it's finished. 08/29/1996 - TkDesk now checks if files may be deleted before the shell complains. - Second go at displaying FS status: The available space on a file system is now obtained from the "df" command. If the user has a quota on that FS (and quota is installed), the quota is printed in the status bar as well. - "Control-a" in the editor now selects the whole buffer. - Changed function of mark 0 in the editor: "Alt/Meta-0" now always jumps back after jumping to another mark. 08/26/1996 - The "HyperSearch" dialog of the built-in editor now has a "Sort" checkbutton to optionally sort the "hyperlist". - New feature of the built-in editor: marks. 10 marks can be set via Control- and jumped to via Alt/Meta-. The marks work across buffer and window boundaries. 08/25/1996 - Improved balloon help. The font can now be configured in the "System" config file by setting tkdesk(font,balloon). (CONFIG) - Added some additional bindings to the appbar: Alt/Meta-ButtonPress-1: Move the Appbar Alt/Meta-ButtonPress-2: Popup-menu configuration files Alt/Meta-ButtonPress-3: Popup-menu appbar functions These are automagically added to all appbar buttons. 08/24/1996 - Changed default size of appbar buttons to 48x48. Also use much nicer NeXT-like buttons now, that I found in the AfterStep distribution. - Due to popular demand, the file lists of file browser windows now contain "." and ".." for shortening the currently displayed path. - The default button bar (underneath the menu bars) now contains webbrowser-like back and forward buttons to step through the directory history. (CONFIG) - Fixed the "Mail Bug Report To Author" facility! Pretty embarrassing that I had broken it in 1.0b2... 08/20/1996 - File selection dialog now also follows the value of the "Dialogs At Pointer" option. - The entries of the built-in editor's buffer menu now consist of parent directory and file name. - ~/.tkdesk/_version is now created at first time usage. 08/05/1996 - TkDesk no longer relies on tkEntryPaste/tkTextPaste if $tk_version > 4.0. - Removed tkMenuInvoke error. - Fixed division by zero bug with the appbar's load display. =========================================================================== 07/29/1996 Released TkDesk 1.0b2 07/22/1996 - TkDesk now uses its own version of tk_dialog. This one places the window near the mouse pointer if "Dialogs At Pointer" is set, and removes the *most dangerous* binding for introduced in Tk 4.1, as it doesn't follow the focus. - Added a printer icon to the default appbar where files may be dropped, the printer can be selected and its status observed (CONFIG). 07/21/1996 - now has the same effect if option "Single Click" is set and a directory is clicked on as with the option unset. - Improved handling of broken symbolic links (they can now be d&d'ed). - Default sort type can now be set via the "Options" menu. - Entries of the "Bookmarks" menu are now always sorted alphabetically. - Tkdesk now checks for read-only filesystems before most file operations. - The type char of sockets (=) wasn't stripped. - The "Delete Files" dialog now appears always, even if "Really Delete" is not set. The old behaviour irritated a few people. 07/17/1996 - Files can now also by copy-dragged from the "Find Files" dialog box if "Quick Drag'n'Drop" is set. - The menu of the file listboxes has a new entry: "Command...". This can be used to execute a shell command in the listbox's directory in the background. All the usual %-shortcuts may be used. Thanks to Kevin Rodgers for the inspiration. - The "Make tar.gz" entry of the directories popup menu now creates an archive relative to the directory's path. Thanks to Michael Beach. 07/15/1996 - Further improvements concerning the handling files of with special Tcl-characters (ie: "[]{}) and spaces in their names. - Info bar is now updated when selecting files with . - Some minor fixes. 07/11/1996 - Fixed bug with history menus just having the "dummy" entry. - The "~" in dsk_exec commands is now always replaced by the user's home directory. - History menus of the appbar are now also "tearoffable". - Put the "Commands" history menu back into the default appbar (CONFIG). - Another appbar special (file AppBar): a notifier of new mail. Works similar to xbiff, just better: it has got three states, sound support, and provides the usual appbar functionality. - The dsk_view proc now first checks if the command is executable (i.e. the first element of the command). 07/10/1996 - Removed "Append type char" from file listboxes of file list windows. - New appbar concept: In addition to simple buttons complex "special buttons" can be constructed and handled just like simple buttons (currently only from within the TkDesk code). I.e. they provide popup menus and drag'n'drop functionality just like the other buttons. These special buttons are recognised by TkDesk by a button image-name of the form special:*. - Changed the appbar's date display into a special button with the name special:date. The special word "date" in the config file AppBar is still supported for backward compatibility. (CONFIG) - New appbar special (config file AppBar): an xload-like display (special:load). (CONFIG) - If the public proc dsk_edit is called without arguments a file selection dialog appears now. - Some minor fixes. 07/09/1996 - The current X selection can now be accessed from the config files by %x (CONFIG). - The "Manual Page" entry of TkDesk's default appbar now understands lines like "col(1) socket(2) malloc(3)" (CONFIG). - Incorporated netscape-remote 1.1, written by Ken Hornstein, into TkDesk. Thanks a lot for that neat stuff Ken! - New public command: dsk_netscape "file|url" . Options may be "window" and/or "raise". This command makes use of Ken's netscape-remote extension, and transparently starts Netscape if it's not running on the user's display yet. See config files AppBar and Popups for example use. (CONFIG) - Changed the appbar's earth button into a "Netscape-button". The X- selection can now be used to instruct Netscape to visit certain URLs. Files can be dropped on this button to let (maybe a running) Netscape display them. - The built-in editor has a new option: "Send to Netscape". If this option is set, the edited file is automagically reloaded by Netscape each time the file is saved, e.g. by pressing "Alt/Meta-s". 07/08/1996 - Appbar: If the right mouse button is pressed and released over the the same button of the appbar the corresponding menu remains posted. - File info: The rightmost button of the mode line now cycles through "-,x,t,T". - Improved behaviour when directory of trashcan window is changed. - Selected entries "." and ".." in file list windows are ignored now. - Fixed bug: built-in editor always added a newline when saving files. 07/07/1996 - Improved handling of focus: the models "focus follows mouse" and "click to focus" are now better supported. The default model can be set by the boolean config variable tkdesk(focus_follows_mouse) in System. (CONFIG) - Fixed bug with listboxes going mad when selecting files by dragging the mouse outside the window and releasing the mouse button there. 07/02/1996 - Fixed the file type selection of the "Find Files" dialog. - Added config variable tkdesk(num_lbs) in config file "System" for setting the default number of listboxes of file browser windows (CONFIG). 06/23/1996 - Removed 2 further possibilities for X "BadMatch" errors. - The "previous version" dialog box appeared even if TkDesk had not been used before. - Icons in file lists now support some mouse operations (single click, double click, right click). - Fixed editor bug with searching expressions starting with "-". - Auto-mounting didn't work because TkDesk switched working dir to directory that is to be opened before executing the on_open command. 06/18/1996 - Fixed bug with icon display (first item was assigned 2 icons) - Added default config for tkdesk(editor,default_geometry) =========================================================================== 05/20/1996 Released TkDesk 1.0b1 05/20/1996 - Added new command line option "-default" to read default configuration rather then the user's one in ~/.tkdesk. - Updated the TkDesk User's Guide. Created a PostScript version of the guide which can be found in the "doc" subdirectory of the untar'ed TkDesk package. 05/19/1996 - Added a "Help" button to the default application bar. Executables can be dropped here to display their manual page, if they've got one. The popup menu also offers remote access to TkMan. I could only test this with TkMan 1.7b3, but hopefully it also works with newer version. - Removed the "Preferences" config file. Options are now automatically saved. The settings from your old "Preferences" are read once when 1.0b1 is first started. (CONFIG) - Added a "Don't execute" checkbutton to the "Periodic Execution" dialog to temporarily pause the periodic execution. - The built-in editor now handles regular expressions containing "^" and "$" correctly. - Added a new option "Always In Browser". This affects the "In Browser" checkbutton of the "Open Directory" dialog, plus opening a directory with Control-Doubleclick. - Added an "Auto Save" menu to the "TkDesk" menu. Determines which parts of TkDesk will be automatically saved periodically and when quitting TkDesk. - "Rename File" now checks for existing file first. - Improved reseting of vertical scrollbars after refreshing. - Icons used for file browser/list windows and the help window can now be configured in the "System" config file. (CONFIG) - Implemented a new function/proc for configuration scripts: dsk_read_string label script: Executes script when entered string is not empty. "label" will be displayed in the dialog. (CONFIG) 05/17/1996 - Files can now also be dropped on the menubutton right above each file listbox. Useful when directory contains lots of subdirectories. - Added an "Empty Trash" command to the "Directories" menu. - Made TkDesk more environmentally friendly by adding a "recycle" button to the default application bar. Files can also be dropped here, so you could as well delete the "Trash" icon. Or this button. - If "Quick Drag'n'Drop" is selected, files and directories are no longer tried to be copied on themselves. - When dragging files the mouse has to be moved at least 6 pixels in any direction to invoke the respective copy/move/etc. action. - Files can now be searched for a set of strings, regular expressions, or extended regular expressions. - The default window geometry of the built-in editor can now be configured by setting the variable tkdesk(editor,default_geometry) in the config file "System". (CONFIG) - TkDesk now always uses the built-in editor for editing the configuration files because of the easier reloading of the edited config files (F5/F6). - Built-in editor: Text deleted with Ctrl-k can now be yanked back by the usual Ctrl-y. - TkDesk is now much more stable when run by an "ordinary" user (ie. not superuser). - The editor can now also be opened if no files are selected, from the button bar of the file browser windows. 05/16/1996 - Fixed highlighting of selections in file lists for b/w displays. - The menus of the application bar can now be browsed like normal menu bars (while pressing the right mouse button). - If the only file droppped over a file list is a tar file (compressed or uncompressed) the "Copy"-dialog has an additional button "Untar" to untar the file in the destination directory (hope you like this, Avery :-). Obviously, this does not work if the option "Quick D&D" has been selected. - If you enabled sound for TkDesk (via the config file "Sounds") the "Options" menu has a new entry "Use Sound". - New option "Single Click (Dirs)" allows to open directories with a single mouse click. - New option "Dialogs At Pointer" to always open dialogs (and new file windows) over the mouse pointer. - Changed popup menu for directories to contain entries for opening a new file list or file browser window. - The most annoying, nasty, wicked and ugly bug "error in config for public variable dsk_FileViewer::dir" should now be fixed! But keep fingers crossed... - The "AppBar" config file now contains an option to let the appbar be managed by the window manager, plus a DETAILED description of how to move the appbar. (CONFIG) - Made the file listboxes more Motif-conform and consistent with the rest of TkDesk by adding a small corner frame. - Search paths for image files (for app and button bars) and sound files can now be configured. Multiple search paths are possible. See defs of tkdesk(path,images) and tkdesk(path,sounds) in config file "System". (CONFIG) - Added 5 small sound files to the standard distribution. - The recursive creation of the cascaded directory menus is now abandoned if the resultant menu would no longer fit on screen. 05/14/1996 - The fonts used in the "time- and date-button" of the appbar can now be configured. See default config file "AppBar". (CONFIG) - All file browser and list windows can now be closed, provided the application bar has not been removed. This way it is possible to reduce TkDesk to just the app bar or even the trash icon. - x-permission buttons (File-Info dialog) now toggle through "-, x, s, S" - Scripts in the tkdesk(action_on_open) can now also use the usual "%"- abbreviations (described in "Directories" config file). (CONFIG) - Changed the "Configuration" submenu of the first button of the appbar to contain directly the names of the config files. It is much easier to reload them from the editor window anyway. - Bug fix: Path "/" is now correctly handled in the "Find files" dialog. - Changed method of rereading all config files. - Files/Dirs may now contain "%"'. Just had to change a sprintf to a strcpy...... - Removed disabled "Undo" entry from the editors "Edit" menu until I find time (and motivation...) to implement it. - Some minor bug and glitch fixes. 05/12/1996 - Added an "Up" button to the default button bars. - "Sort by Size/Date/Extension" now leaves the "." and ".." entries at the top of the file lists. - Removed the dsk_statfs command and corresponding configuration options from the Makefile. Should improve portability. Will use the "df" utility for same/extended functionality. - Misc. Makefile changes: no more GNUmakefile, config. opts. now saved to a file which is read by BLT's and itcl's configure scripts, new macro for echo (ECHO) to deal with Solaris's echo. - Fixed "Set Mask" bug. - Cleaned up C-sources (removed stray backslashes, changed initialisation). 05/11/1996 - Bug fix: An error occured when pasting text into an editor window with the middle mouse button if TkDesk was built with Tk 4.0. - Bug fix: Directories couldn't be packed ("Make tar.gz") because internal file name handling always surrounded file names with braces. - The home directory is now abbreviated with ~ in the path entries of file browser and file list windows as well. 05/03/1996 - Rewrote Tk's tkMbMotion so that another menu is only posted if the corresponding menubutton is packed in the same widget as the active one. (I really think this should be standard Tk behaviour.) 05/02/1996 - If the command entered in the "Execute" dialog box starts with dsk_, it is interpreted as a Tcl expression. This allows for instance to invoke the TkDesk editor on a file with "dsk_edit file". - Minor fixes. 04/29/1996 - Button bars of browser and list windows are now more compact. - Updated menu "Others/Help" of file list windows. ============================================================================= 04/28/1996 Released TkDesk 1.0a2 04/27/1996 - New command line option: -startdir . If this option is given, the first file browser will open with directory . - A directory can now also be opened into a browser window (via the "Open Directory" dialog). - dsk_view now also prints the "Launched" and "Exit" status messages as dsk_exec does. - File list windows now may also have a button bar underneath the menu. This is also configured in the config file ButtonBar by setting the variable tkdesk(small_button_bar). The syntax is exactly the same as for tkdesk(button_bar). (CONFIG) - Created directories and files are now automatically selected. - Removed the "indicator" from the menubutton just above the file lists, for various reasons. The raised relief should be indicator enough. - There is now a central proc for printing files (dsk_print). It displays a dialog box where the command which is to be used for printing can be entered. The default can be set by setting the variable tkdesk(cmd,print) in the "System" configuration file. (CONFIG) - The "File" menu has a new entry: "Print..." - Now *all* of TkDesk's configuration files can be reloaded. But note that some changes to the "System" file only become visible when TkDesk is restarted. - The built-in editor has a new menu "TkDesk" which appears when editing one of the configuration files. Such files can then be saved and reloaded by pressing F5 and saved, reloaded and closed by pressing F6. Maybe TkDesk should always use the built-in editor for this reason when editing configs? What do you think? 04/26/1996 - Updated the BLT parts of TkDesk to 2.1. TkDesk now works with Tcl 7.5 and Tk 4.1 too. 04/22/1996 - Modified editor buffers can now be saved when being closed. - Added a new command line option: -iconic. This will iconify all windows (ie browser and list windows) when TkDesk is started. - New command line option "-fvwm" to force fvwm support (esp. icon windows/pixmaps instead bitmaps). - Revamped installation. Now support multiple users. 04/21/1996 - TkDesk now prints a message in all browsers' status bars when a dsk_exec'ed program exits. Handy when unpacking tar files, for instance. Also prints a message when a command is dsk_exed'ed. Using the "Sounds" configuration file different sounds can be associated with these events (dsk_exec_launch and dsk_exec_exit). (CONFIG) - Title bar of "Background" windows is now enumerated to tell how many copy, disk usage etc. jobs are currently running. - FileTags config file can now be reloaded from within TkDesk. - FileTags entries now overrule standard directory/executable tags. - Better date display if appbar is laid out horizontally. - Browser windows may now contain only one listbox (menu "Options/Number of Listboxes"). - Number of listboxes in browser windows is now also saved to _layout, and restored next time tkdesk is started. - Directories have a new entry in their popup menu: "Traverse". This is a cascaded menu that lets you select a directory from the tree starting at the selected directory. Pressing Control at the same time (when releasing the right mouse button) opens a new file list window with the selected directory. Quite nice. - Much improved (but not perfect!) handling of files with special characters in their names, such as spaces,",[,{. 04/20/1996 - Created a reduced tclInt.h for itcl which is compatible with Tcl 7.4 and 7.5 and is automatically included. Should help with config problems. - File modification times are now correctly displayed for files older than a year. - Removed "Commands" from Appbar, didn't work right and was pretty useless anyway. 04/19/1996 - Modified TkDesk's pixmaps to use only 32 colours - thanks to the excellent "pixy" program made available by Romano Giannetti (romano@iet.unipi.it) and Martin Kraemer (Martin.Kraemer@Mch.SNI.De). 04/15/1996 - Revamped bug-mailing facility pf TkDesk. Now users can add their comments. - Some changes in the Makefile regarding libraries to link to TkDesk. - Improved quick drag'n'drop mode. - Files now may end with "+" etc. ... 04/13/1996 - Removed a (self-discovered :-)) bug that could appear when action definitions e.g. in the Popups config file spanned multiple lines. - tkAppInit.c no longer references the variable tcl_RcFileName to avoid incompatibility with Tcl 7.5. Wasn't used anyway. 04/11/1996 - "Directories" configuration file defines two new lists: (CONFIG) o tkdesk(strip_parents): When the path of a file browser window is changed to a directory listed here, or below it, the parent dirs of the listed dir will not be displayed (as is by default the behavior for a user's home directory). Can speed up display, esp. when working inside an AFS tree. o tkdesk(action_on_open): An action can now be invoked whenever one of the directories listed here is opened or refreshed. The action is a Tcl script defined in the same list. This allows for auto-mounting of removable media, to name just one example. - Yes, TkDesk now can transparently access removable media, such as floppy disks and cdroms, made possible by the tkdesk(action_on_open) list just mentioned. 04/08/1996 - Errors when trying to "open" executable files are now caught. - Removed annoying "tkPriv(oldGrab): no such variable" bug that occurred with unpatched versions of Tk 4.0. Made tkpatches.tcl compatible with these. - Added a "tkwait visibility" to avoid "BadMatch" error when placing window interactively (hopefully). - Fixed bug which set tkdesk(active_viewer) to a not existent browser. - Some more bug fixes. 04/07/1996 - "make install" now creates directory $(LIBDIR)/.trash, chmod's all files under $(LIBDIR) as readable for all, and all binaries as executable by all ============================================================================= 03/31/1996 Released TkDesk 1.0a1 tkdesk-2.0/INSTALL0100644000175000007640000001461110037304052011753 0ustar jccjcc# TkDesk - a file manager for Unix and the X Window System # Copyright (C) 1996-1999 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. INSTALL TkDesk 2.x April, 2004 -------------------------------------------------------------------------- Building and Installation of TkDesk 2.x This file describes how to compile and install the source release of TkDesk. Links to the latest source release can be found at the TkDesk homepage: http://tkdesk.sourceforge.net Requirements ------------ [Tcl/Tk] TkDesk now requires Tcl/Tk 8.0 - 8.4. As of version TkDesk 2.0, older versions of Tcl/Tk (ver. 7.x or less) are no longer supported. Tcl/Tk source distributions can be downloaded from: http://tcl.tk/software/tcltk/choose.html [Incr Tcl] Itcl version 3.0.1 or higher is required. Version 3.2 or higher is recommended. If you intend to use Itcl 3.0.1 you'll have to apply the patch contained in the file contrib/itcl_fix.diff. To apply the patch go into the base directory of the unpacked Itcl 3.0.1 distribution, cd to itcl/generic, and issue the command: patch <../tkdesk-1.2/contrib/itcl_fix.diff This assumes you unpacked TkDesk into the same directory as itcl. If you intend to use Tcl/Tk 8.4, make sure your Itcl installation (preferrably version 3.2) was compiled against Tcl/Tk 8.4, or you will likely get some very nasty segmentation faults. Itcl distributions can be found at: http://sourceforge.net/projects/incrtcl/ [BLT] TkDesk also requires a subset of the functions provided by the BLT library. By default the subset of BLT 2.4j that comes with TkDesk will be compiled, but you can also have TkDesk link against an already installed BLT by using the configure flag '--with-blt=', where is the directory that contains libBLT.a (or libBLT.so). If you are using Tcl/Tk 8.4, you should be using BLT version 2.4z or newer. BLT distributions can be found at: http://blt.sourceforge.net/ Compilation ----------- To compile TkDesk you first have to create the Makefiles for your system by running "./configure". If you don't want to install TkDesk underneath /usr/local, which is the default, do "./configure --prefix=". TkDesk's configure looks for the file tclConfig.sh and tkConfig.sh that are installed with Tcl/Tk, usually in the corresponding "lib" directory. It looks in a few "standard" places, but if you installed Tcl/Tk in a non-standard place you need to point configure to these directories using the "--with-tcl=DIR" and "--with-tk=DIR" options. The "--with-tk" option can be omitted if tkConfig.sh is in the same directory that's given to "--with-tcl". Similarly, if you have itcl 3.x installed in a non-standard place you can use the "--with-itcl=DIR" option to have TkDesk pick up the right itclConfig.sh. (Note that some Itcl installations do not provide an itclConfig.sh file. In this case, the configure script with look for Itcl in some standard places, defaulting to the same location used for Tcl/Tk. For example, if you installed Tcl/Tk 8.4, Itcl 3.2, and BLT 2.4z into /usr/local/tcltk, you might invoke configure like this: ./configure --with-tcl=/usr/local/tcltk/lib \ --with-itcl=/usr/local/tcltk/lib \ --with-blt=/usr/local/tcltk/lib TkStep: [NOTE: TkStep support is more or less deprecated as of TkDesk 2.0. If anyone can find a recent distribution of TkStep, or better yet a TkStep homepage, please inform the TkDesk maintainer. Thanks.] If you're compiling with version 8.x of Tcl/Tk and you got the corresponding version of TkStep installed on your system (in the default paths), you can give the option "--enable-tkstep" to configure to have tkdesksh be linked with the TkStep instead of the Tk libraries. Executing "./configure --help" displays a lot of additional options not described here. Not all of these apply to TkDesk and are acknowledged by the build process. When configure exits successfully, review the generated configuration summary and when you're satisfied, run "make". If you get an error message saying that "ranlib" could not be found: This error is ignored by make, and you can safely do the same. Some systems require archives to be ranlib'ed, others don't. Test Drive ---------- After compiling, if you'd like to have a look at TkDesk prior to actually installing it, use the "testdrive" script: ./testdrive Installation ------------ Use the command "make install" to install the TkDesk binaries, scripts, library files, and manual pages. Note that you may have to become the root user first to gain write access to the installation directories. And that's it! You now should be able to start TkDesk from the command line by typing "tkdesk". Where To Go From Here --------------------- There is a short "Quick Start Guide" available that's automatically displayed when TkDesk is run for the first time. It's also available from the browser window's "Help" menu. The TkDesk User's Guide is available both as PostScript in the "doc" subdirectory (of the directory created when you untar'ed the TkDesk package), as well as online via the help button of the application bar or the "Help" menu. You may also want to take a look inside the contrib subdirectory of the TkDesk distribution. It contains some very interesting extensions, patches and configuration files contributed by users of TkDesk. Thank you if you're one of them! For news about releases, patches, etc., please visit the TkDesk homepage: http://tkdesk.sourceforge.net Or you may wish to join the TkDesk users mailing list. To subscribe, please visit: https://lists.sourceforge.net/lists/listinfo/tkdesk-users tkdesk-2.0/Makefile.in0100644000175000007640000001735710037335665013017 0ustar jccjcc# This file is the Makefile for TkDesk. If it has the name "Makefile.in" # then it is a template for a Makefile; to generate the actual Makefile, # run "./configure", which is a configuration script generated by the # "autoconf" program (constructs like "@foo@" will get replaced in the # actual Makefile. #---------------------------------------------------------------- # Things you can change to personalize the Makefile for your own # site (you can make these changes in either Makefile.in or # Makefile, but changes to Makefile will get lost if you re-run # the configuration script). #---------------------------------------------------------------- # Default top-level directories in which to install architecture- # specific files (exec_prefix) and machine-independent files such # as scripts (prefix). The values specified here may be overridden # at configure-time with the --exec-prefix and --prefix options # to the "configure" script. prefix = @prefix@ exec_prefix = @exec_prefix@ # The following definition can be set to non-null for special systems # like AFS with replication. It allows the pathnames used for installation # to be different than those used for actually reference files at # run-time. INSTALL_ROOT is prepended to $prefix and $exec_prefix # when installing files. INSTALL_ROOT = # Directory from which TkDesk will reference its library of Tcl scripts: TKDESK_LIBRARY = @libdir@/TkDesk # Path name to use when installing library scripts: SCRIPT_INSTALL_DIR = $(INSTALL_ROOT)$(TKDESK_LIBRARY) # Directory where tkdesksh will look for its library scripts: SCRIPT_SEARCH_DIR = $(SCRIPT_INSTALL_DIR) # Directory in which to install the program tkdesksh: BIN_INSTALL_DIR = $(INSTALL_ROOT)@bindir@ # Directory where tkdesksh will look for the tkdesk script: BIN_SEARCH_DIR = $(BIN_INSTALL_DIR) # Top-level directory in which to install manual entries: MAN_INSTALL_DIR = $(INSTALL_ROOT)@mandir@ # Directory in which to install manual entry for tkdesk: MAN1_INSTALL_DIR = $(MAN_INSTALL_DIR)/man1 # To change the compiler switches, for example to change from -O # to -g, change the following line: CC_OPTS = -O -DUSE_OLD_IMAGE -DUSE_NON_CONST # To change the linker switches, for example to add -s, # change the following line: LD_OPTS = @LDFLAGS@ # Tcl used to let the configure script choose which program to use # for installing, but there are just too many different versions of # "install" around; better to use the install-sh script that comes # with the distribution, which is slower but guaranteed to work. INSTALL = @srcdir@/install-sh -c INSTALL_PROGRAM = ${INSTALL} -m 755 INSTALL_DATA = ${INSTALL} -m 644 #---------------------------------------------------------------- # The information below is modified by the configure script when # Makefile is generated from Makefile.in. You shouldn't normally # modify any of this stuff by hand. #---------------------------------------------------------------- # Location of Tcl header files: TCL_INCLUDE_DIR = @TCL_INCLUDE_PATH@ # Location of Tk header files: TK_INCLUDE_DIR = @TK_INCLUDE_PATH@ # Location of X11 header files: X_INCLUDE_FLAG = @TK_XINCLUDES@ # Specification of Tcl library: TCL_LIB_SPEC = @TCL_LIB_SPEC@ TCL_LIB_STATIC = @TCL_LIB_STATIC@ # Specification of Tk library: TK_LIB_SPEC = @TK_LIB_SPEC@ TK_LIB_STATIC = @TK_LIB_STATIC@ # Specification if itcl library: ITCL_LIB_SPEC = @ITCL_LIB_SPEC@ ITCL_LIB_STATIC = @ITCL_LIB_STATIC@ # Specification if BLT library: BLT_LIB_SPEC = @BLT_LIB_SPEC@ BLT_LIB_STATIC = @BLT_LIB_STATIC@ # Complete library option string to pass to linker: LIBS = $(BLT_LIB_SPEC) $(ITCL_LIB_SPEC) $(TK_LIB_SPEC) $(TCL_LIB_SPEC) @TK_LIBS@ @LIBS@ LIBS_STATIC = $(BLT_LIB_STATIC) $(ITCL_LIB_STATIC) $(TK_LIB_STATIC) $(TCL_LIB_STATIC) @TK_LIBS@ @LIBS@ # Miscellaneous settings: AC_FLAGS = @DEFS@ SRC_DIR = @srcdir@ RANLIB = @RANLIB@ CC = @CC@ SHELL = /bin/sh # Command for creating directories. Alternatives include "mkdir" # and "mkdirhier". MKDIR = mkdir -p #---------------------------------------------------------------- # The information below should be usable as is. The configure # script won't modify it and you shouldn't need to modify it # either. #---------------------------------------------------------------- CFLAGS = ${CC_OPTS} ${AC_FLAGS} -I. -I${TCL_INCLUDE_DIR} -I${TK_INCLUDE_DIR} ${X_INCLUDE_FLAG} LDFLAGS = ${LD_OPTS} PKGNAME = tkdesk VERSION = 1.2 FVERSION = 12 PKGDIR = $(PKGNAME)-$(VERSION) all: shell client tkdesk_script @echo "========= Build complete." @echo " " @echo "You can try out TkDesk before installing it by executing:" @echo "./testdrive" shell: makelibs tkAppInit.o $(CC) $(LDFLAGS) @TCL_LD_SEARCH_FLAGS@ -o tkdesksh \ tkAppInit.o libdesk/libdesk.a netscape-remote/libnetscape.a \ $(LIBS) static: makelibs tkAppInit.o client tkdesk_script $(CC) $(LDFLAGS) @TCL_LD_SEARCH_FLAGS@ -static -o tkdesksh \ tkAppInit.o libdesk/libdesk.a netscape-remote/libnetscape.a \ blt/libBLT.a $(LIBS_STATIC) makelibs: @NEED_BLT_LIB@ lib_netscape lib_libdesk lib_blt: cd blt; $(MAKE) CC_OPTS="$(CC_OPTS)" LIB_DIR="$(SCRIPT_SEARCH_DIR)" lib lib_itcl: cd itcl; $(MAKE) CC_OPTS="$(CC_OPTS)" lib lib_netscape: cd netscape-remote; $(MAKE) CC_OPTS="$(CC_OPTS)" lib lib_libdesk: cd libdesk; $(MAKE) CC_OPTS="$(CC_OPTS)" lib client: cd tkdeskclient; $(MAKE) CC_OPTS="$(CC_OPTS)" LD_OPTS="$(LD_OPTS)" all tkdesk_script: @echo '#!/bin/sh' >tkdesk @echo '#-*- tcl -*- \' >>tkdesk @echo "PATH=$(BIN_SEARCH_DIR):"'$$PATH ;#\' >>tkdesk @echo "exec tkdesksh \"\$$0\" \"\$$@\"" >>tkdesk @echo "" >>tkdesk @echo "set tkdesk(library) \"$(SCRIPT_SEARCH_DIR)\"" >>tkdesk @echo "set tkdesk(in_development) 0" >>tkdesk @echo "set tkdesk(debug) 0" >>tkdesk @cat tkdesk.main >>tkdesk @chmod 755 tkdesk rm_tkdesk_script: @rm -f tkdesk install: tkdesksh client rm_tkdesk_script tkdesk_script dirs @chmod +x install-sh @echo "=== Installing the executables..." @for f in tkdesksh tkdesk tkdeskclient/tkdeskclient tools/pauseme tools/ed-tkdesk tools/cd-tkdesk tools/od-tkdesk tools/op-tkdesk tools/pop-tkdesk; do \ echo "installing $$f" ;\ rm -f $(BIN_INSTALL_DIR)/`basename $$f` ;\ $(INSTALL_PROGRAM) $$f $(BIN_INSTALL_DIR) ;\ done @echo "=== Installing TkDesk's library..." @cd ./tcldesk; tar cf - * | (cd $(SCRIPT_INSTALL_DIR); tar xvf -) @cd .. @cp ChangeLog $(SCRIPT_INSTALL_DIR)/doc/ChangeLog @echo "=== Setting permissions..." find $(SCRIPT_INSTALL_DIR) -type f -exec chmod a+r {} \; find $(SCRIPT_INSTALL_DIR) -type d -exec chmod a+rx {} \; @echo "=== Creating index..." cd $(SCRIPT_INSTALL_DIR); $(BIN_INSTALL_DIR)/tkdesksh mkindex @echo "=== Installing the manual pages..." @for f in tkdesk.1 cd-tkdesk.1 ed-tkdesk.1 od-tkdesk.1; do \ echo "installing $$f" ;\ rm -f $(MAN1_INSTALL_DIR)/$$f ;\ $(INSTALL_DATA) doc/$$f $(MAN1_INSTALL_DIR) ;\ done @echo "=== Installation complete." uninstall: @echo "=== Uninstalling TkDesk's library..." -rm -r $(SCRIPT_INSTALL_DIR) @echo "=== Uninstalling TkDesk's executables..." -rm $(BIN_INSTALL_DIR)/tkdesksh $(BIN_INSTALL_DIR)/tkdesk $(BIN_INSTALL_DIR)/pauseme $(BIN_INSTALL_DIR)/ed-tkdesk $(BIN_INSTALL_DIR)/cd-tkdesk $(BIN_INSTALL_DIR)/od-tkdesk $(BIN_INSTALL_DIR)/op-tkdesk $(BIN_INSTALL_DIR)/pop-tkdesk $(BIN_INSTALL_DIR)/tkdeskclient @echo "=== Done." dirs: @for i in $(SCRIPT_INSTALL_DIR) $(BIN_INSTALL_DIR) $(MAN1_INSTALL_DIR); \ do \ if [ ! -d $$i ] ; then \ echo "Making directory $$i"; \ $(MKDIR) $$i; \ chmod 755 $$i; \ fi; \ done; clean: cd blt; $(MAKE) clean cd netscape-remote; $(MAKE) clean cd libdesk ; $(MAKE) clean cd tkdeskclient ; $(MAKE) clean cd tcldesk ; rm -f #*# rm -f *.o tkdesk tkdesksh tags: find libdesk tcldesk \( -name "*.c" -o -name "*.h" -o -name "*.tcl" \) -print | etags - tkdesk-2.0/README0100644000175000007640000000570710037557475011633 0ustar jccjcc README TkDesk 2.0 April, 2004 -------------------------------------------------------------------------- This is release 2.0 of TkDesk. Release Summary This release is a first attempt at providing compatibility with Tcl/Tk 8.4. There are some other minor fixes and new features (see the ChangeLog file for details), but the main intent is to provide compatability support for the latest versions of Tcl/Tk, Itcl, and BLT. As of this release, TkDesk may no longer provide compatability support for versions of Tcl/Tk older that 8.0. Release 2.0 Highlights * Tcl/Tk 8.4 compatibility See the INSTALL file for detailed release requirements. * Dropped support for Tcl/Tk 7.x. * Mouse-wheel support Most text boxes and the file listings are now mouse-wheel enabled. The usage details haven't made it into the documentation yet, so here's the quick summary: o Your X11 server needs to be configured for mouse-wheel support. o The mouse wheel scrolls 5 lines at a time by default o Shift + the mouse wheel scrolls 1 line at a time o Ctrl + the mouse wheel scrolls 1 page at a time * Select two files, press a button, get a diff! This works great in conjunction with tkDiff. The diff command used can be configured in your .tkdesk/System file. The default "diff" button (on the button bar) is a picture of two overlapped pages. * Improved (hopefully) configure script * New TkDesk logo What is TkDesk? TkDesk is a graphical desktop and file manager for several types of UNIX (such as Linux) and the X Window System. It offers a very rich set of file operations and services, and gives the user the ability to configure most aspects of TkDesk in a powerful way. The reason for this is the use of Tcl/Tk as the configuration and (for the biggest part of TkDesk) implementation language. TkDesk was conceived and originally implemented by Christian Bolik. Licensing For licensing details, see the file COPYING. Apart for the exceptions detailed in there, TkDesk is released under the terms of the GNU General Public License. TkDesk is copyrighted by Christian Bolik. How do I install TkDesk? See the file INSTALL for instructions on how to install TkDesk. For the impatient, here's the summary: Install Tcl/Tk 8.4, along with the latest releases of Incr Tcl and BLT, if you don't have have them, and then run "./configure", "make", and "make install". (The absolute latest versions of Tcl/Tk, Itcl & BLT may not be needed. See the INSTALL file for detailed requirements.) Is there a Web page for TkDesk? Yes, please take a look at: http://tkdesk.sourceforge.net Latest source releases and patches will be available first from there, along with TkDesk news, mailing-list information, etc. Hope you enjoy TkDesk! J. Chris Coppick jchris@users.sourceforge.net tkdesk-2.0/config.h.in0100644000175000007640000000747510037130661012762 0ustar jccjcc/* config.h.in. Generated from configure.in by autoheader. */ /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_DIRENT_H /* Define to 1 if you have the header file. */ #undef HAVE_ERRNO_H /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the header file. */ #undef HAVE_FLOAT_H /* Define to 1 if you have the `gethostname' function. */ #undef HAVE_GETHOSTNAME /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the header file. */ #undef HAVE_MALLOC_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_NDIR_H /* Define to 1 if you have the `socket' function. */ #undef HAVE_SOCKET /* Define to 1 if you have the `statfs' function. */ #undef HAVE_STATFS /* Define to 1 if you have the `statvfs' function. */ #undef HAVE_STATVFS /* Define to 1 if you have the header file. */ #undef HAVE_STATVFS_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strcasecmp' function. */ #undef HAVE_STRCASECMP /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_DIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MOUNT_H /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STATVFS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_VFS_H /* Define to 1 if you have that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the header file. */ #undef HAVE_WAITFLAGS_H /* Define if union wait type is defined incorrectly. */ #undef NO_UNION_WAIT /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define as the return type of signal handlers (`int' or `void'). */ #undef RETSIGTYPE /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME /* Define to `int' if does not define. */ #undef mode_t /* Define to `int' if does not define. */ #undef pid_t /* Define to `unsigned' if does not define. */ #undef size_t tkdesk-2.0/configure0100755000175000007640000050470010037131203012630 0ustar jccjcc#! /bin/sh # From configure.in Revision: 1.3 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.57 for TkDesk ver-2-0. # # Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 # Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi # Support unset when possible. if (FOO=FOO; unset FOO) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" # Sed expression to map a string onto a valid variable name. as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` exec 6>&1 # # Initializations. # ac_default_prefix=/usr/local ac_config_libobj_dir=. cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. # This variable seems obsolete. It should probably be removed, and # only ac_max_sed_lines should be used. : ${ac_max_here_lines=38} # Identity of this package. PACKAGE_NAME='TkDesk' PACKAGE_TARNAME='tkdesk' PACKAGE_VERSION='ver-2-0' PACKAGE_STRING='TkDesk ver-2-0' PACKAGE_BUGREPORT='' ac_unique_file="tkdesk.main" # Factoring default headers for most tests. ac_includes_default="\ #include #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_INTTYPES_H # include #else # if HAVE_STDINT_H # include # endif #endif #if HAVE_UNISTD_H # include #endif" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS TCL_LD_SEARCH_FLAGS TCL_EXEC_PREFIX TK_EXEC_PREFIX TCL_INCLUDE_PATH TK_INCLUDE_PATH TK_XINCLUDES TCL_LIB_SPEC TK_LIB_SPEC TK_LIBS TCL_LIB_STATIC TK_LIB_STATIC ITCL_LIB_SPEC ITCL_LIB_STATIC NEED_BLT_LIB BLT_LIB_SPEC BLT_LIB_STATIC TKDESK CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE RANLIB ac_ct_RANLIB CPP EGREP LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_option in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval "enable_$ac_feature=no" ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "enable_$ac_feature='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "with_$ac_package='$ac_optarg'" ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval "with_$ac_package=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` eval "$ac_envvar='$ac_optarg'" export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute paths. for ac_var in exec_prefix prefix do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* | NONE | '' ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # Be sure to have absolute paths. for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ localstatedir libdir includedir oldincludedir infodir mandir do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_confdir=`(dirname "$0") 2>/dev/null || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 { (exit 1); exit 1; }; } else { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi fi (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 { (exit 1); exit 1; }; } srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` ac_env_build_alias_set=${build_alias+set} ac_env_build_alias_value=$build_alias ac_cv_env_build_alias_set=${build_alias+set} ac_cv_env_build_alias_value=$build_alias ac_env_host_alias_set=${host_alias+set} ac_env_host_alias_value=$host_alias ac_cv_env_host_alias_set=${host_alias+set} ac_cv_env_host_alias_value=$host_alias ac_env_target_alias_set=${target_alias+set} ac_env_target_alias_value=$target_alias ac_cv_env_target_alias_set=${target_alias+set} ac_cv_env_target_alias_value=$target_alias ac_env_CC_set=${CC+set} ac_env_CC_value=$CC ac_cv_env_CC_set=${CC+set} ac_cv_env_CC_value=$CC ac_env_CFLAGS_set=${CFLAGS+set} ac_env_CFLAGS_value=$CFLAGS ac_cv_env_CFLAGS_set=${CFLAGS+set} ac_cv_env_CFLAGS_value=$CFLAGS ac_env_LDFLAGS_set=${LDFLAGS+set} ac_env_LDFLAGS_value=$LDFLAGS ac_cv_env_LDFLAGS_set=${LDFLAGS+set} ac_cv_env_LDFLAGS_value=$LDFLAGS ac_env_CPPFLAGS_set=${CPPFLAGS+set} ac_env_CPPFLAGS_value=$CPPFLAGS ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} ac_cv_env_CPPFLAGS_value=$CPPFLAGS ac_env_CPP_set=${CPP+set} ac_env_CPP_value=$CPP ac_cv_env_CPP_set=${CPP+set} ac_cv_env_CPP_value=$CPP # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures TkDesk ver-2-0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] _ACEOF cat <<_ACEOF Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] _ACEOF cat <<\_ACEOF X features: --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of TkDesk ver-2-0:";; esac cat <<\_ACEOF Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-tkstep link using TkStep libraries --enable-unsupported override checks for unsupported versions of Tcl/Tk, etc. Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-tcl=DIR read Tcl configuration from DIR --with-tk=DIR read Tk configuration from DIR --with-itcl=DIR read itcl configuration from DIR --with-tclconfig=FILE use specific tcl config file [tclConfig.sh] --with-tkconfig=FILE use specific tk config file [tkConfig.sh] --with-itclconfig=FILE use specific itcl config file [itclConfig.sh] --with-blt=DIR link with BLT library installed in DIR --with-x use the X Window System Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. ac_popdir=`pwd` for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d $ac_dir || continue ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be # absolute. ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` cd $ac_dir # Check for guested configure; otherwise get Cygnus style configure. if test -f $ac_srcdir/configure.gnu; then echo $SHELL $ac_srcdir/configure.gnu --help=recursive elif test -f $ac_srcdir/configure; then echo $SHELL $ac_srcdir/configure --help=recursive elif test -f $ac_srcdir/configure.ac || test -f $ac_srcdir/configure.in; then echo $ac_configure --help else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi cd $ac_popdir done fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF TkDesk configure ver-2-0 generated by GNU Autoconf 2.57 Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit 0 fi exec 5>config.log cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by TkDesk $as_me ver-2-0, which was generated by GNU Autoconf 2.57. Invocation command line was $ $0 $@ _ACEOF { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` hostinfo = `(hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_sep= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" # Get rid of the leading space. ac_sep=" " ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Be sure not to use single quotes in there, as some shells, # such as our DU 5.0 friend, will then `close' the trap. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, { (set) 2>&1 | case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in *ac_space=\ *) sed -n \ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" ;; *) sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------- ## ## Output files. ## ## ------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo sed "/^$/d" confdefs.h | sort echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core core.* *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo >confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . $cache_file;; *) . ./$cache_file;; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in `(set) 2>&1 | sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val="\$ac_cv_env_${ac_var}_value" eval ac_new_val="\$ac_env_${ac_var}_value" case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" # Check whether --with-tcl or --without-tcl was given. if test "${with_tcl+set}" = set; then withval="$with_tcl" TCLCONF_PATH=$withval fi; # Check whether --with-tk or --without-tk was given. if test "${with_tk+set}" = set; then withval="$with_tk" TKCONF_PATH=$withval fi; # Check whether --with-itcl or --without-itcl was given. if test "${with_itcl+set}" = set; then withval="$with_itcl" ITCLCONF_PATH=$withval fi; # Check whether --with-tclconfig or --without-tclconfig was given. if test "${with_tclconfig+set}" = set; then withval="$with_tclconfig" TCLCONF_FILE=$withval else TCLCONF_FILE=tclConfig.sh fi; # Check whether --with-tkconfig or --without-tkconfig was given. if test "${with_tkconfig+set}" = set; then withval="$with_tkconfig" TKCONF_FILE=$withval else TKCONF_FILE=tkConfig.sh fi; # Check whether --with-itclconfig or --without-itclconfig was given. if test "${with_itclconfig+set}" = set; then withval="$with_itclconfig" ITCLCONF_FILE=$withval else ITCLCONF_FILE=itclConfig.sh fi; # Check whether --with-blt or --without-blt was given. if test "${with_blt+set}" = set; then withval="$with_blt" BLT_LIB_PATH=$withval fi; # Check whether --enable-tkstep or --disable-tkstep was given. if test "${enable_tkstep+set}" = set; then enableval="$enable_tkstep" ENABLE_TKSTEP=yes else ENABLE_TKSTEP=no fi; # Check whether --enable-unsupported or --disable-unsupported was given. if test "${enable_unsupported+set}" = set; then enableval="$enable_unsupported" ENABLE_UNSUPPORTED=yes else ENABLE_UNSUPPORTED=no fi; # # Read tclConfig.sh and tkConfig.sh: # echo "$as_me:$LINENO: checking for $TCLCONF_FILE" >&5 echo $ECHO_N "checking for $TCLCONF_FILE... $ECHO_C" >&6 if test -z "$TCLCONF_PATH" ; then TCLCONF_PATH="$HOME/lib /usr/local/lib /usr/lib /usr/X11/lib /usr/share/lib" fi success=0 for d in $TCLCONF_PATH ; do if test -r $d/$TCLCONF_FILE ; then TCLCONF_PATH=$d success=1 break fi done if test $success = 1 ; then echo "$as_me:$LINENO: result: $TCLCONF_PATH/$TCLCONF_FILE" >&5 echo "${ECHO_T}$TCLCONF_PATH/$TCLCONF_FILE" >&6 else echo "$as_me:$LINENO: result: not found" >&5 echo "${ECHO_T}not found" >&6 echo { { echo "$as_me:$LINENO: error: could not find $TCLCONF_FILE - Please restart configure with the argument --with-tcl=dir, where dir is the path of tclConfig.sh. You may use --with-tclconfig=file to use a specific Tcl config file name. If you cannot locate this file, maybe Tcl/Tk needs to be installed first. Note that TkDesk 2.x requires Tcl/Tk 8.x." >&5 echo "$as_me: error: could not find $TCLCONF_FILE - Please restart configure with the argument --with-tcl=dir, where dir is the path of tclConfig.sh. You may use --with-tclconfig=file to use a specific Tcl config file name. If you cannot locate this file, maybe Tcl/Tk needs to be installed first. Note that TkDesk 2.x requires Tcl/Tk 8.x." >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: checking for $TKCONF_FILE" >&5 echo $ECHO_N "checking for $TKCONF_FILE... $ECHO_C" >&6 if test -z "$TKCONF_PATH" ; then TKCONF_PATH="$TCLCONF_PATH $HOME/lib /usr/local/lib /usr/lib /usr/X11/lib /usr/share/lib" fi success=0 for d in $TKCONF_PATH $TKCONF_PATH/lib ; do if test -r $d/$TKCONF_FILE ; then TKCONF_PATH=$d success=1 break fi done if test $success = 1 ; then echo "$as_me:$LINENO: result: $TKCONF_PATH/$TKCONF_FILE" >&5 echo "${ECHO_T}$TKCONF_PATH/$TKCONF_FILE" >&6 else echo "$as_me:$LINENO: result: not found" >&5 echo "${ECHO_T}not found" >&6 echo { { echo "$as_me:$LINENO: error: could not find $TKCONF_FILE - Please restart configure with the argument --with-tk=dir, where dir is the path of tkConfig.sh. You may use --with-tkconfig=file to use a specific Tk config file name. If you cannot locate this file, maybe Tcl/Tk needs to be installed first. Note that TkDesk 2.x requires 8.x." >&5 echo "$as_me: error: could not find $TKCONF_FILE - Please restart configure with the argument --with-tk=dir, where dir is the path of tkConfig.sh. You may use --with-tkconfig=file to use a specific Tk config file name. If you cannot locate this file, maybe Tcl/Tk needs to be installed first. Note that TkDesk 2.x requires 8.x." >&2;} { (exit 1); exit 1; }; } fi . $TCLCONF_PATH/$TCLCONF_FILE . $TKCONF_PATH/$TKCONF_FILE TCL_LIB_STATIC=${TCLCONF_PATH}/libtcl${TCL_VERSION}.a TK_LIB_STATIC=${TKCONF_PATH}/libtk${TK_VERSION}.a # # Check for the TkStep libraries (if needed)... # if test "$ENABLE_TKSTEP" == "yes" ; then TK_LIB_SPEC="-ltkstep -lXpm -ltiff" { echo "$as_me:$LINENO: will link using TkStep libraries" >&5 echo "$as_me: will link using TkStep libraries" >&6;} fi # # Check version of installed Tcl/Tk libraries: # echo "$as_me:$LINENO: checking version of Tcl" >&5 echo $ECHO_N "checking version of Tcl... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $TCL_VERSION" >&5 echo "${ECHO_T}$TCL_VERSION" >&6 if test "$TCL_MAJOR_VERSION" -lt 8; then echo if test "$ENABLE_UNSUPPORTED" == "yes" ; then { echo "$as_me:$LINENO: WARNING: you may be attempting to use TkDesk 2.x with an unsupported version of Tcl. Good luck. Hope it works out for you." >&5 echo "$as_me: WARNING: you may be attempting to use TkDesk 2.x with an unsupported version of Tcl. Good luck. Hope it works out for you." >&2;} else { { echo "$as_me:$LINENO: error: TkDesk version 2.x requires Tcl version 8 or higher. Please upgrade your Tcl or, if that is not possible, you might prefer to use TkDesk version 1.2 instead." >&5 echo "$as_me: error: TkDesk version 2.x requires Tcl version 8 or higher. Please upgrade your Tcl or, if that is not possible, you might prefer to use TkDesk version 1.2 instead." >&2;} { (exit 1); exit 1; }; } fi fi echo "$as_me:$LINENO: checking version of Tk" >&5 echo $ECHO_N "checking version of Tk... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $TK_VERSION" >&5 echo "${ECHO_T}$TK_VERSION" >&6 if test "$TK_MAJOR_VERSION" -lt 8; then echo if test "$ENABLE_UNSUPPORTED" == "yes" ; then { echo "$as_me:$LINENO: WARNING: you may be attempting to use TkDesk 2.x with an unsupported version of Tk. Good luck. Hope it works out for you." >&5 echo "$as_me: WARNING: you may be attempting to use TkDesk 2.x with an unsupported version of Tk. Good luck. Hope it works out for you." >&2;} else { { echo "$as_me:$LINENO: error: TkDesk version 2.x requires Tk version 8 or higher. Please upgrade your Tk or, if that is not possible, you might prefer to use TkDesk version 1.2 instead." >&5 echo "$as_me: error: TkDesk version 2.x requires Tk version 8 or higher. Please upgrade your Tk or, if that is not possible, you might prefer to use TkDesk version 1.2 instead." >&2;} { (exit 1); exit 1; }; } fi fi # # Check for itcl 3.x # # Sometimes the itclConfig.sh file is missing (e.g. on my redhat 9 install) # so if we can't find it we'll take a look around for the itcl libs and # includes anyway. # First look for an itclConfig.sh... echo "$as_me:$LINENO: checking for $ITCLCONF_FILE" >&5 echo $ECHO_N "checking for $ITCLCONF_FILE... $ECHO_C" >&6 if test -z "$ITCLCONF_PATH" ; then ITCLCONF_PATH="$TCLCONF_PATH $HOME/lib /usr/local/lib /usr/lib /usr/X11/lib /usr/share/lib" fi success=0 for d in $ITCLCONF_PATH ; do if test -r $d/$ITCLCONF_FILE ; then ITCLCONF_PATH=$d success=1 break fi done if test $success = 1 ; then echo "$as_me:$LINENO: result: $ITCLCONF_PATH/$ITCLCONF_FILE" >&5 echo "${ECHO_T}$ITCLCONF_PATH/$ITCLCONF_FILE" >&6 . $ITCLCONF_PATH/$ITCLCONF_FILE echo "$as_me:$LINENO: checking version of Itcl" >&5 echo $ECHO_N "checking version of Itcl... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $ITCL_VERSION" >&5 echo "${ECHO_T}$ITCL_VERSION" >&6 if test "$ITCL_MAJOR_VERSION" -ne 3; then echo if test "$ENABLE_UNSUPPORTED" == "yes" ; then { echo "$as_me:$LINENO: WARNING: you may be attempting to use TkDesk 2.x with an unsupported version of Itcl. Good luck. Hope it works out for you." >&5 echo "$as_me: WARNING: you may be attempting to use TkDesk 2.x with an unsupported version of Itcl. Good luck. Hope it works out for you." >&2;} else { { echo "$as_me:$LINENO: error: found Itcl, but it appears to be the wrong version. TkDesk 2.x requires itcl 3.x to be installed. Please get it from: http://www.incrtcl.sourceforge.net." >&5 echo "$as_me: error: found Itcl, but it appears to be the wrong version. TkDesk 2.x requires itcl 3.x to be installed. Please get it from: http://www.incrtcl.sourceforge.net." >&2;} { (exit 1); exit 1; }; } fi fi else echo "$as_me:$LINENO: result: not found" >&5 echo "${ECHO_T}not found" >&6 ITCLCONF_PATH=[N/A] ITCL_VERSION="no" ITCL_MAJOR_VERSION=0 { echo "$as_me:$LINENO: WARNING: could not find $ITCLCONF_FILE - going to keep going, but I am not making any promises." >&5 echo "$as_me: WARNING: could not find $ITCLCONF_FILE - going to keep going, but I am not making any promises." >&2;} fi if test "$ITCL_VERSION" != "no" ; then # ITCL_LIB_SPEC already set in itclConfig.sh ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl${ITCL_VERSION}.a else # Guess... [This is annoying] ITCL_GUESSING="[I guessed]" if test -f "${TCLCONF_PATH}/libitcl.so" -o -f "${TCLCONF_PATH}/libitcl.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl.a fi if test -f "${TCLCONF_PATH}/libitcl3.0.so" -o -f "${TCLCONF_PATH}/libitcl3.0.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl3.0" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl3.0.a fi if test -f "${TCLCONF_PATH}/libitcl3.1.so" -o -f "${TCLCONF_PATH}/libitcl3.1.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl3.1" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl3.1.a fi if test -f "${TCLCONF_PATH}/libitcl3.2.so" -o -f "${TCLCONF_PATH}/libitcl3.2.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl3.2" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl3.2.a fi fi # # Check whether to link with installed BLT # echo "$as_me:$LINENO: checking for BLT library" >&5 echo $ECHO_N "checking for BLT library... $ECHO_C" >&6 if test -z "$BLT_LIB_PATH" ; then BLT_LIB_SPEC=blt/libBLT.a BLT_LIB_STATIC=blt/libBLT.a NEED_BLT_LIB=lib_blt echo "$as_me:$LINENO: result: ./blt" >&5 echo "${ECHO_T}./blt" >&6 else BLT_LIB_SPEC="-L$BLT_LIB_PATH -lBLT" BLT_LIB_STATIC=$BLT_LIB_PATH/libBLT.a NEED_BLT_LIB= if test ! -f $BLT_LIB_PATH/libBLT.a \ -a ! -f $BLT_LIB_PATH/libBLT.so then if test ! -f $BLT_LIB_PATH/lib/libBLT.a \ -a ! -f $BLT_LIB_PATH/lib/libBLT.so then echo { { echo "$as_me:$LINENO: error: could not find libBLT.a or libBLT.so in $BLT_LIB_PATH or in $BLT_LIB_PATH/lib." >&5 echo "$as_me: error: could not find libBLT.a or libBLT.so in $BLT_LIB_PATH or in $BLT_LIB_PATH/lib." >&2;} { (exit 1); exit 1; }; } else BLT_LIB_PATH=$BLT_LIB_PATH/lib fi fi echo "$as_me:$LINENO: result: $BLT_LIB_PATH" >&5 echo "${ECHO_T}$BLT_LIB_PATH" >&6 fi # # Determine include directories # TCL_INCLUDE_PATH=$TCL_EXEC_PREFIX/include TK_INCLUDE_PATH=$TCL_INCLUDE_PATH # # Perform substitutions # # Extract the first word of "tkdesk", so it can be a program name with args. set dummy tkdesk; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_path_TKDESK+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else case $TKDESK in [\\/]* | ?:[\\/]*) ac_cv_path_TKDESK="$TKDESK" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_TKDESK="$as_dir/$ac_word$ac_exec_ext" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done ;; esac fi TKDESK=$ac_cv_path_TKDESK if test -n "$TKDESK"; then echo "$as_me:$LINENO: result: $TKDESK" >&5 echo "${ECHO_T}$TKDESK" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CC" && break done CC=$ac_ct_CC fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. echo "$as_me:$LINENO: checking for C compiler default output" >&5 echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 (eval $ac_link_default) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Find the output, starting from the most likely. This scheme is # not robust to junk in `.', hence go to wildcards (a.*) only as a last # resort. # Be careful to initialize this variable, since it used to be cached. # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. ac_cv_exeext= # b.out is created by i960 compilers. for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; conftest.$ac_ext ) # This is the source file. ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` # FIXME: I believe we export ac_cv_exeext for Libtool, # but it would be cool to find out if it's true. Does anybody # maintain Libtool? --akim. export ac_cv_exeext break;; * ) break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6 # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6 echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` export ac_cv_exeext break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6 rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6 OBJEXT=$ac_cv_objext ac_objext=$OBJEXT echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS CFLAGS="-g" echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_g=no fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 if test "${ac_cv_prog_cc_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_stdc=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF # Don't try gcc -ansi; that turns off useful extensions and # breaks some systems' header files. # AIX -qlanglvl=ansi # Ultrix and OSF/1 -std1 # HP-UX 10.20 and later -Ae # HP-UX older versions -Aa -D_HPUX_SOURCE # SVR4 -Xc -D__EXTENSIONS__ for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_stdc=$ac_arg break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext done rm -f conftest.$ac_ext conftest.$ac_objext CC=$ac_save_CC fi case "x$ac_cv_prog_cc_stdc" in x|xno) echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6 ;; *) echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 CC="$CC $ac_cv_prog_cc_stdc" ;; esac # Some people use a C++ compiler to compile C. Since we use `exit', # in C++ we need to declare it. In case someone uses the same compiler # for both compiling C and C++ we need to have the C++ compiler decide # the declaration of exit, since it's the most demanding environment. cat >conftest.$ac_ext <<_ACEOF #ifndef __cplusplus choke me #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then for ac_declaration in \ ''\ '#include ' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f $ac_dir/shtool; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} { (exit 1); exit 1; }; } fi ac_config_guess="$SHELL $ac_aux_dir/config.guess" ac_config_sub="$SHELL $ac_aux_dir/config.sub" ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in ./ | .// | /cC/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi done done ;; esac done fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL=$ac_install_sh fi fi echo "$as_me:$LINENO: result: $INSTALL" >&5 echo "${ECHO_T}$INSTALL" >&6 # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo "$as_me:$LINENO: checking whether ln -s works" >&5 echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 else echo "$as_me:$LINENO: result: no, using $LN_S" >&5 echo "${ECHO_T}no, using $LN_S" >&6 fi echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.make <<\_ACEOF all: @echo 'ac_maketemp="$(MAKE)"' _ACEOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` if test -n "$ac_maketemp"; then eval ac_cv_prog_make_${ac_make}_set=yes else eval ac_cv_prog_make_${ac_make}_set=no fi rm -f conftest.make fi if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 SET_MAKE= else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 SET_MAKE="MAKE=${MAKE-make}" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then echo "$as_me:$LINENO: result: $RANLIB" >&5 echo "${ECHO_T}$RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 echo "${ECHO_T}$ac_ct_RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi RANLIB=$ac_ct_RANLIB else RANLIB="$ac_cv_prog_RANLIB" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi echo "$as_me:$LINENO: result: $CPP" >&5 echo "${ECHO_T}$CPP" >&6 ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking for X" >&5 echo $ECHO_N "checking for X... $ECHO_C" >&6 # Check whether --with-x or --without-x was given. if test "${with_x+set}" = set; then withval="$with_x" fi; # $have_x is `yes', `no', `disabled', or empty when we do not yet know. if test "x$with_x" = xno; then # The user explicitly disabled X. have_x=disabled else if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then # Both variables are already set. have_x=yes else if test "${ac_cv_have_x+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # One or both of the vars are not set, and there is no cached value. ac_x_includes=no ac_x_libraries=no rm -fr conftest.dir if mkdir conftest.dir; then cd conftest.dir # Make sure to not put "make" in the Imakefile rules, since we grep it out. cat >Imakefile <<'_ACEOF' acfindx: @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' _ACEOF if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. for ac_extension in a so sl; do if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && test -f $ac_im_libdir/libX11.$ac_extension; then ac_im_usrlibdir=$ac_im_libdir; break fi done # Screen out bogus values from the imake configuration. They are # bogus both because they are the default anyway, and because # using them would break gcc on systems where it needs fixed includes. case $ac_im_incroot in /usr/include) ;; *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; esac case $ac_im_usrlibdir in /usr/lib | /lib) ;; *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; esac fi cd .. rm -fr conftest.dir fi # Standard set of common directories for X headers. # Check X11 before X11Rn because it is often a symlink to the current release. ac_x_header_dirs=' /usr/X11/include /usr/X11R6/include /usr/X11R5/include /usr/X11R4/include /usr/include/X11 /usr/include/X11R6 /usr/include/X11R5 /usr/include/X11R4 /usr/local/X11/include /usr/local/X11R6/include /usr/local/X11R5/include /usr/local/X11R4/include /usr/local/include/X11 /usr/local/include/X11R6 /usr/local/include/X11R5 /usr/local/include/X11R4 /usr/X386/include /usr/x386/include /usr/XFree86/include/X11 /usr/include /usr/local/include /usr/unsupported/include /usr/athena/include /usr/local/x11r5/include /usr/lpp/Xamples/include /usr/openwin/include /usr/openwin/share/include' if test "$ac_x_includes" = no; then # Guess where to find include files, by looking for Intrinsic.h. # First, try using that file with no special directory specified. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # We can compile using X headers with no special include directory. ac_x_includes= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 for ac_dir in $ac_x_header_dirs; do if test -r "$ac_dir/X11/Intrinsic.h"; then ac_x_includes=$ac_dir break fi done fi rm -f conftest.err conftest.$ac_ext fi # $ac_x_includes = no if test "$ac_x_libraries" = no; then # Check for the libraries. # See if we find them without any special options. # Don't add to $LIBS permanently. ac_save_LIBS=$LIBS LIBS="-lXt $LIBS" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { XtMalloc (0) ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then LIBS=$ac_save_LIBS # We can link X programs with no special library path. ac_x_libraries= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 LIBS=$ac_save_LIBS for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` do # Don't even attempt the hair of trying to link an X program! for ac_extension in a so sl; do if test -r $ac_dir/libXt.$ac_extension; then ac_x_libraries=$ac_dir break 2 fi done done fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext fi # $ac_x_libraries = no if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then # Didn't find X anywhere. Cache the known absence of X. ac_cv_have_x="have_x=no" else # Record where we found X for the cache. ac_cv_have_x="have_x=yes \ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" fi fi fi eval "$ac_cv_have_x" fi # $with_x != no if test "$have_x" != yes; then echo "$as_me:$LINENO: result: $have_x" >&5 echo "${ECHO_T}$have_x" >&6 no_x=yes else # If each of the values was on the command line, it overrides each guess. test "x$x_includes" = xNONE && x_includes=$ac_x_includes test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries # Update the cache value to reflect the command line values. ac_cv_have_x="have_x=yes \ ac_x_includes=$x_includes ac_x_libraries=$x_libraries" echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5 echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 fi ac_header_dirent=no for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5 echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include <$ac_hdr> int main () { if ((DIR *) 0) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 _ACEOF ac_header_dirent=$ac_hdr; break fi done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo "$as_me:$LINENO: checking for library containing opendir" >&5 echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 if test "${ac_cv_search_opendir+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_opendir=no cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char opendir (); int main () { opendir (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_opendir="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_opendir" = no; then for ac_lib in dir; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char opendir (); int main () { opendir (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_opendir="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 echo "${ECHO_T}$ac_cv_search_opendir" >&6 if test "$ac_cv_search_opendir" != no; then test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" fi else echo "$as_me:$LINENO: checking for library containing opendir" >&5 echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 if test "${ac_cv_search_opendir+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_func_search_save_LIBS=$LIBS ac_cv_search_opendir=no cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char opendir (); int main () { opendir (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_opendir="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_opendir" = no; then for ac_lib in x; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char opendir (); int main () { opendir (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_search_opendir="-l$ac_lib" break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext done fi LIBS=$ac_func_search_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 echo "${ECHO_T}$ac_cv_search_opendir" >&6 if test "$ac_cv_search_opendir" != no; then test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" fi fi echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6 if test "${ac_cv_prog_egrep+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | (grep -E '(a|b)') >/dev/null 2>&1 then ac_cv_prog_egrep='grep -E' else ac_cv_prog_egrep='egrep' fi fi echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 echo "${ECHO_T}$ac_cv_prog_egrep" >&6 EGREP=$ac_cv_prog_egrep echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 if test "${ac_cv_header_sys_wait_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int main () { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_sys_wait_h=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_sys_wait_h=no fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 if test $ac_cv_header_sys_wait_h = yes; then cat >>confdefs.h <<\_ACEOF #define HAVE_SYS_WAIT_H 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in errno.h fcntl.h float.h limits.h malloc.h memory.h sys/time.h statvfs.h sys/statvfs.h unistd.h waitflags.h sys/vfs.h sys/param.h sys/mount.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc in yes:no ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; no:yes ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} ( cat <<\_ASBOX ## ------------------------------------ ## ## Report this to bug-autoconf@gnu.org. ## ## ------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for mode_t" >&5 echo $ECHO_N "checking for mode_t... $ECHO_C" >&6 if test "${ac_cv_type_mode_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((mode_t *) 0) return 0; if (sizeof (mode_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_mode_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_mode_t=no fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_mode_t" >&5 echo "${ECHO_T}$ac_cv_type_mode_t" >&6 if test $ac_cv_type_mode_t = yes; then : else cat >>confdefs.h <<_ACEOF #define mode_t int _ACEOF fi echo "$as_me:$LINENO: checking for size_t" >&5 echo $ECHO_N "checking for size_t... $ECHO_C" >&6 if test "${ac_cv_type_size_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((size_t *) 0) return 0; if (sizeof (size_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_size_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_size_t=no fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 echo "${ECHO_T}$ac_cv_type_size_t" >&6 if test $ac_cv_type_size_t = yes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned _ACEOF fi echo "$as_me:$LINENO: checking for pid_t" >&5 echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 if test "${ac_cv_type_pid_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((pid_t *) 0) return 0; if (sizeof (pid_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_pid_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_pid_t=no fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 echo "${ECHO_T}$ac_cv_type_pid_t" >&6 if test $ac_cv_type_pid_t = yes; then : else cat >>confdefs.h <<_ACEOF #define pid_t int _ACEOF fi echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 if test "${ac_cv_header_time+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_time=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_time=no fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 echo "${ECHO_T}$ac_cv_header_time" >&6 if test $ac_cv_header_time = yes; then cat >>confdefs.h <<\_ACEOF #define TIME_WITH_SYS_TIME 1 _ACEOF fi echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 if test "${ac_cv_struct_tm+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { struct tm *tp; tp->tm_sec; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_struct_tm=time.h else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_struct_tm=sys/time.h fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 echo "${ECHO_T}$ac_cv_struct_tm" >&6 if test $ac_cv_struct_tm = sys/time.h; then cat >>confdefs.h <<\_ACEOF #define TM_IN_SYS_TIME 1 _ACEOF fi echo "$as_me:$LINENO: checking return type of signal handlers" >&5 echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 if test "${ac_cv_type_signal+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #ifdef signal # undef signal #endif #ifdef __cplusplus extern "C" void (*signal (int, void (*)(int)))(int); #else void (*signal ()) (); #endif int main () { int i; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_signal=void else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_signal=int fi rm -f conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 echo "${ECHO_T}$ac_cv_type_signal" >&6 cat >>confdefs.h <<_ACEOF #define RETSIGTYPE $ac_cv_type_signal _ACEOF for ac_func in gethostname gettimeofday socket strdup strcasecmp statvfs statfs do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for socket in -lsocket" >&5 echo $ECHO_N "checking for socket in -lsocket... $ECHO_C" >&6 if test "${ac_cv_lib_socket_socket+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket -lnsl $LIBS" cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char socket (); int main () { socket (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_socket_socket=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_socket_socket=no fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_socket_socket" >&5 echo "${ECHO_T}$ac_cv_lib_socket_socket" >&6 if test $ac_cv_lib_socket_socket = yes; then LIBS="-lsocket -lnsl" fi # # Custom Tests: union wait and long string declaration # # # Check whether defines the type "union wait" # correctly. It's needed because of weirdness in HP-UX where # "union wait" is defined in both the BSD and SYS-V environments. # Checking the usability of WIFEXITED seems to do the trick. # echo "$as_me:$LINENO: checking whether union wait is defined correctly" >&5 echo $ECHO_N "checking whether union wait is defined correctly... $ECHO_C" >&6 if test "${blt_cv_struct_wait_works+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF #line $LINENO "configure" /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { union wait x; WIFEXITED(x); /* Generates compiler error if WIFEXITED * uses an int. */ ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then blt_cv_struct_wait_works="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 blt_cv_struct_wait_works="no" fi rm -f conftest.$ac_objext conftest.$ac_ext fi if test "${blt_cv_struct_wait_works}" = "no"; then cat >>confdefs.h <<\_ACEOF #define NO_UNION_WAIT 1 _ACEOF fi echo "$as_me:$LINENO: result: $blt_cv_struct_wait_works" >&5 echo "${ECHO_T}$blt_cv_struct_wait_works" >&6 ac_config_files="$ac_config_files Makefile libdesk/Makefile blt/Makefile netscape-remote/Makefile tkdeskclient/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/; s/:*\${srcdir}:*/:/; s/:*@srcdir@:*/:/; s/^\([^=]*=[ ]*\):*/\1/; s/:*$//; s/^[^=]*=[ ]*$//; }' fi DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_i=`echo "$ac_i" | sed 's/\$U\././;s/\.o$//;s/\.obj$//'` # 2. Add them. ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi # Support unset when possible. if (FOO=FOO; unset FOO) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" # Sed expression to map a string onto a valid variable name. as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH exec 6>&1 # Open the log real soon, to keep \$[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. Logging --version etc. is OK. exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX } >&5 cat >&5 <<_CSEOF This file was extended by TkDesk $as_me ver-2-0, which was generated by GNU Autoconf 2.57. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ _CSEOF echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 echo >&5 _ACEOF # Files that config.status was made for. if test -n "$ac_config_files"; then echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS fi if test -n "$ac_config_headers"; then echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS fi if test -n "$ac_config_links"; then echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS fi if test -n "$ac_config_commands"; then echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS fi cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ TkDesk config.status ver-2-0 configured by $0, generated by GNU Autoconf 2.57, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." srcdir=$srcdir INSTALL="$INSTALL" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "x$1" : 'x\([^=]*\)='` ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ac_shift=: ;; -*) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; *) # This is not an option, so the user has probably given explicit # arguments. ac_option=$1 ac_need_defaults=false;; esac case $ac_option in # Handling of the options. _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --vers* | -V ) echo "$ac_cs_version"; exit 0 ;; --he | --h) # Conflict between --help and --header { { echo "$as_me:$LINENO: error: ambiguous option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "libdesk/Makefile" ) CONFIG_FILES="$CONFIG_FILES libdesk/Makefile" ;; "blt/Makefile" ) CONFIG_FILES="$CONFIG_FILES blt/Makefile" ;; "netscape-remote/Makefile" ) CONFIG_FILES="$CONFIG_FILES netscape-remote/Makefile" ;; "tkdeskclient/Makefile" ) CONFIG_FILES="$CONFIG_FILES tkdeskclient/Makefile" ;; "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason to put it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Create a temporary directory, and hook for its removal unless debugging. $debug || { trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./confstat$$-$RANDOM (umask 077 && mkdir $tmp) } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "\$CONFIG_FILES"; then # Protect against being on the right side of a sed subst in config.status. sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF s,@SHELL@,$SHELL,;t t s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t s,@exec_prefix@,$exec_prefix,;t t s,@prefix@,$prefix,;t t s,@program_transform_name@,$program_transform_name,;t t s,@bindir@,$bindir,;t t s,@sbindir@,$sbindir,;t t s,@libexecdir@,$libexecdir,;t t s,@datadir@,$datadir,;t t s,@sysconfdir@,$sysconfdir,;t t s,@sharedstatedir@,$sharedstatedir,;t t s,@localstatedir@,$localstatedir,;t t s,@libdir@,$libdir,;t t s,@includedir@,$includedir,;t t s,@oldincludedir@,$oldincludedir,;t t s,@infodir@,$infodir,;t t s,@mandir@,$mandir,;t t s,@build_alias@,$build_alias,;t t s,@host_alias@,$host_alias,;t t s,@target_alias@,$target_alias,;t t s,@DEFS@,$DEFS,;t t s,@ECHO_C@,$ECHO_C,;t t s,@ECHO_N@,$ECHO_N,;t t s,@ECHO_T@,$ECHO_T,;t t s,@LIBS@,$LIBS,;t t s,@TCL_LD_SEARCH_FLAGS@,$TCL_LD_SEARCH_FLAGS,;t t s,@TCL_EXEC_PREFIX@,$TCL_EXEC_PREFIX,;t t s,@TK_EXEC_PREFIX@,$TK_EXEC_PREFIX,;t t s,@TCL_INCLUDE_PATH@,$TCL_INCLUDE_PATH,;t t s,@TK_INCLUDE_PATH@,$TK_INCLUDE_PATH,;t t s,@TK_XINCLUDES@,$TK_XINCLUDES,;t t s,@TCL_LIB_SPEC@,$TCL_LIB_SPEC,;t t s,@TK_LIB_SPEC@,$TK_LIB_SPEC,;t t s,@TK_LIBS@,$TK_LIBS,;t t s,@TCL_LIB_STATIC@,$TCL_LIB_STATIC,;t t s,@TK_LIB_STATIC@,$TK_LIB_STATIC,;t t s,@ITCL_LIB_SPEC@,$ITCL_LIB_SPEC,;t t s,@ITCL_LIB_STATIC@,$ITCL_LIB_STATIC,;t t s,@NEED_BLT_LIB@,$NEED_BLT_LIB,;t t s,@BLT_LIB_SPEC@,$BLT_LIB_SPEC,;t t s,@BLT_LIB_STATIC@,$BLT_LIB_STATIC,;t t s,@TKDESK@,$TKDESK,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t s,@INSTALL_DATA@,$INSTALL_DATA,;t t s,@LN_S@,$LN_S,;t t s,@SET_MAKE@,$SET_MAKE,;t t s,@RANLIB@,$RANLIB,;t t s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t s,@CPP@,$CPP,;t t s,@EGREP@,$EGREP,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_lines=48 ac_sed_frag=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_lines # Line after last line for current file. ac_more_lines=: ac_sed_cmds= while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag else sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag fi if test ! -s $tmp/subs.frag; then ac_more_lines=false else # The purpose of the label and of the branching condition is to # speed up the sed processing (if there are no `@' at all, there # is no need to browse any of the substitutions). # These are the two extra sed commands mentioned above. (echo ':t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" else ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" fi ac_sed_frag=`expr $ac_sed_frag + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_lines` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi fi # test -n "$CONFIG_FILES" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be # absolute. ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_builddir$INSTALL ;; esac if test x"$ac_file" != x-; then { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} rm -f "$ac_file" fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then configure_input= else configure_input="$ac_file. " fi configure_input=$configure_input"Generated from `echo $ac_file_in | sed 's,.*/,,'` by configure." # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo $f;; *) # Relative if test -f "$f"; then # Build tree echo $f elif test -f "$srcdir/$f"; then # Source tree echo $srcdir/$f else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s,@configure_input@,$configure_input,;t t s,@srcdir@,$ac_srcdir,;t t s,@abs_srcdir@,$ac_abs_srcdir,;t t s,@top_srcdir@,$ac_top_srcdir,;t t s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t s,@builddir@,$ac_builddir,;t t s,@abs_builddir@,$ac_abs_builddir,;t t s,@top_builddir@,$ac_top_builddir,;t t s,@abs_top_builddir@,$ac_abs_top_builddir,;t t s,@INSTALL@,$ac_INSTALL,;t t " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file else cat $tmp/out rm -f $tmp/out fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # # CONFIG_HEADER section. # # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where # NAME is the cpp macro being defined and VALUE is the value it is being given. # # ac_d sets the value in "#define NAME VALUE" lines. ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' ac_dB='[ ].*$,\1#\2' ac_dC=' ' ac_dD=',;t' # ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_uB='$,\1#\2define\3' ac_uC=' ' ac_uD=',;t' for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo $f;; *) # Relative if test -f "$f"; then # Build tree echo $f elif test -f "$srcdir/$f"; then # Source tree echo $srcdir/$f else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } # Remove the trailing spaces. sed 's/[ ]*$//' $ac_file_inputs >$tmp/in _ACEOF # Transform confdefs.h into two sed scripts, `conftest.defines' and # `conftest.undefs', that substitutes the proper values into # config.h.in to produce config.h. The first handles `#define' # templates, and the second `#undef' templates. # And first: Protect against being on the right side of a sed subst in # config.status. Protect against being in an unquoted here document # in config.status. rm -f conftest.defines conftest.undefs # Using a here document instead of a string reduces the quoting nightmare. # Putting comments in sed scripts is not portable. # # `end' is used to avoid that the second main sed command (meant for # 0-ary CPP macros) applies to n-ary macro definitions. # See the Autoconf documentation for `clear'. cat >confdef2sed.sed <<\_ACEOF s/[\\&,]/\\&/g s,[\\$`],\\&,g t clear : clear s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp t end s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp : end _ACEOF # If some macros were called several times there might be several times # the same #defines, which is useless. Nevertheless, we may not want to # sort them, since we want the *last* AC-DEFINE to be honored. uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs rm -f confdef2sed.sed # This sed command replaces #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. cat >>conftest.undefs <<\_ACEOF s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, _ACEOF # Break up conftest.defines because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS echo ' :' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.defines >/dev/null do # Write a limited-size here document to $tmp/defines.sed. echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#define' lines. echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/defines.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail rm -f conftest.defines mv conftest.tail conftest.defines done rm -f conftest.defines echo ' fi # grep' >>$CONFIG_STATUS echo >>$CONFIG_STATUS # Break up conftest.undefs because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #undef templates' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.undefs >/dev/null do # Write a limited-size here document to $tmp/undefs.sed. echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#undef' echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/undefs.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail rm -f conftest.undefs mv conftest.tail conftest.undefs done rm -f conftest.undefs cat >>$CONFIG_STATUS <<\_ACEOF # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then echo "/* Generated by configure. */" >$tmp/config.h else echo "/* $ac_file. Generated by configure. */" >$tmp/config.h fi cat $tmp/in >>$tmp/config.h rm -f $tmp/in if test x"$ac_file" != x-; then if diff $ac_file $tmp/config.h >/dev/null 2>&1; then { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 echo "$as_me: $ac_file is unchanged" >&6;} else ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } rm -f $ac_file mv $tmp/config.h $ac_file fi else cat $tmp/config.h rm -f $tmp/config.h fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi echo echo $PACKAGE_NAME $PACKAGE_VERSION echo eval echo Override Version Checks....... : $ENABLE_UNSUPPORTED eval echo Use TkStep.................... : $ENABLE_TKSTEP eval echo Tcl Config Path............... : $TCLCONF_PATH/$TCLCONF_FILE eval echo Tk Config Path................ : $TKCONF_PATH/$TKCONF_FILE eval echo Tcl Include Path.............. : $TCL_INCLUDE_PATH eval echo Tk Include Path............... : $TK_INCLUDE_PATH eval echo Tcl Library Spec.............. : $TCL_LIB_SPEC eval echo Tcl Static Library Spec....... : $TCL_LIB_STATIC eval echo Tk Library Spec............... : $TK_LIB_SPEC eval echo Tk Static Library Spec........ : $TK_LIB_STATIC eval echo Itcl Library Spec............. : $ITCL_LIB_SPEC $ITCL_GUESSING eval echo Itcl Static Library Spec...... : $ITCL_LIB_STATIC $ITCL_GUESSING eval echo BLT Library Spec.............. : $BLT_LIB_SPEC eval echo BLT Static Library Spec....... : $BLT_LIB_STATIC echo eval eval echo Will install TkDesk binaries in ${bindir}. eval eval echo Will install TkDesk man pages under ${prefix}/man/. eval eval echo Will install the TkDesk library under ${exec_prefix}/lib/TkDesk. if test "x$TKDESK" != "x" ; then echo Warning: You have an old copy of TkDesk at $TKDESK. fi echo echo configure complete, now type \'make\' echo tkdesk-2.0/configure.in0100644000175000007640000002550610037131140013234 0ustar jccjccdnl Process this file with autoconf to produce a configure script. AC_INIT(TkDesk, [ver-2-0]) AC_PREREQ([2.57]) AC_REVISION(1.4) AC_CONFIG_SRCDIR([tkdesk.main]) AC_CONFIG_HEADERS(config.h) AC_ARG_WITH(tcl, [AC_HELP_STRING([--with-tcl=DIR],[read Tcl configuration from DIR])], TCLCONF_PATH=$withval) AC_ARG_WITH(tk, [AC_HELP_STRING([--with-tk=DIR],[read Tk configuration from DIR])], TKCONF_PATH=$withval) AC_ARG_WITH(itcl, [AC_HELP_STRING([--with-itcl=DIR],[read itcl configuration from DIR])], ITCLCONF_PATH=$withval) AC_ARG_WITH(tclconfig, [AC_HELP_STRING([--with-tclconfig=FILE], [use specific tcl config file [tclConfig.sh]])], TCLCONF_FILE=$withval, TCLCONF_FILE=tclConfig.sh) AC_ARG_WITH(tkconfig, [AC_HELP_STRING([--with-tkconfig=FILE], [use specific tk config file [tkConfig.sh]])], TKCONF_FILE=$withval, TKCONF_FILE=tkConfig.sh) AC_ARG_WITH(itclconfig, [AC_HELP_STRING([--with-itclconfig=FILE], [use specific itcl config file [itclConfig.sh]])], ITCLCONF_FILE=$withval, ITCLCONF_FILE=itclConfig.sh) AC_ARG_WITH(blt, [AC_HELP_STRING([--with-blt=DIR],[link with BLT library installed in DIR])], BLT_LIB_PATH=$withval) AC_ARG_ENABLE(tkstep, [AC_HELP_STRING([--enable-tkstep],[link using TkStep libraries])], ENABLE_TKSTEP=yes, ENABLE_TKSTEP=no) AC_ARG_ENABLE(unsupported, [AC_HELP_STRING([--enable-unsupported],[override checks for unsupported versions of Tcl/Tk, etc.])], ENABLE_UNSUPPORTED=yes, ENABLE_UNSUPPORTED=no) # # Read tclConfig.sh and tkConfig.sh: # AC_MSG_CHECKING(for $TCLCONF_FILE) if test -z "$TCLCONF_PATH" ; then TCLCONF_PATH="$HOME/lib /usr/local/lib /usr/lib /usr/X11/lib /usr/share/lib" fi success=0 for d in $TCLCONF_PATH ; do if test -r $d/$TCLCONF_FILE ; then TCLCONF_PATH=$d success=1 break fi done if test $success = 1 ; then AC_MSG_RESULT($TCLCONF_PATH/$TCLCONF_FILE) else AC_MSG_RESULT([not found]) echo AC_MSG_ERROR([could not find $TCLCONF_FILE - Please restart configure with the argument --with-tcl=dir, where dir is the path of tclConfig.sh. You may use --with-tclconfig=file to use a specific Tcl config file name. If you cannot locate this file, maybe Tcl/Tk needs to be installed first. Note that TkDesk 2.x requires Tcl/Tk 8.x.]) fi AC_MSG_CHECKING(for $TKCONF_FILE) if test -z "$TKCONF_PATH" ; then TKCONF_PATH="$TCLCONF_PATH $HOME/lib /usr/local/lib /usr/lib /usr/X11/lib /usr/share/lib" fi success=0 for d in $TKCONF_PATH $TKCONF_PATH/lib ; do if test -r $d/$TKCONF_FILE ; then TKCONF_PATH=$d success=1 break fi done if test $success = 1 ; then AC_MSG_RESULT($TKCONF_PATH/$TKCONF_FILE) else AC_MSG_RESULT([not found]) echo AC_MSG_ERROR([could not find $TKCONF_FILE - Please restart configure with the argument --with-tk=dir, where dir is the path of tkConfig.sh. You may use --with-tkconfig=file to use a specific Tk config file name. If you cannot locate this file, maybe Tcl/Tk needs to be installed first. Note that TkDesk 2.x requires 8.x.]) fi dnl Look Mom! No security! :-) . $TCLCONF_PATH/$TCLCONF_FILE . $TKCONF_PATH/$TKCONF_FILE TCL_LIB_STATIC=${TCLCONF_PATH}/libtcl${TCL_VERSION}.a TK_LIB_STATIC=${TKCONF_PATH}/libtk${TK_VERSION}.a # # Check for the TkStep libraries (if needed)... # if test "$ENABLE_TKSTEP" == "yes" ; then TK_LIB_SPEC="-ltkstep -lXpm -ltiff" AC_MSG_NOTICE([will link using TkStep libraries]) fi # # Check version of installed Tcl/Tk libraries: # AC_MSG_CHECKING(version of Tcl) AC_MSG_RESULT($TCL_VERSION) if test "$TCL_MAJOR_VERSION" -lt 8; then echo if test "$ENABLE_UNSUPPORTED" == "yes" ; then AC_MSG_WARN([you may be attempting to use TkDesk 2.x with an unsupported version of Tcl. Good luck. Hope it works out for you.]) else AC_MSG_ERROR([TkDesk version 2.x requires Tcl version 8 or higher. Please upgrade your Tcl or, if that is not possible, you might prefer to use TkDesk version 1.2 instead.]) fi fi AC_MSG_CHECKING(version of Tk) AC_MSG_RESULT($TK_VERSION) if test "$TK_MAJOR_VERSION" -lt 8; then echo if test "$ENABLE_UNSUPPORTED" == "yes" ; then AC_MSG_WARN([you may be attempting to use TkDesk 2.x with an unsupported version of Tk. Good luck. Hope it works out for you.]) else AC_MSG_ERROR([TkDesk version 2.x requires Tk version 8 or higher. Please upgrade your Tk or, if that is not possible, you might prefer to use TkDesk version 1.2 instead.]) fi fi # # Check for itcl 3.x # # Sometimes the itclConfig.sh file is missing (e.g. on my redhat 9 install) # so if we can't find it we'll take a look around for the itcl libs and # includes anyway. # First look for an itclConfig.sh... AC_MSG_CHECKING(for $ITCLCONF_FILE) if test -z "$ITCLCONF_PATH" ; then ITCLCONF_PATH="$TCLCONF_PATH $HOME/lib /usr/local/lib /usr/lib /usr/X11/lib /usr/share/lib" fi success=0 for d in $ITCLCONF_PATH ; do if test -r $d/$ITCLCONF_FILE ; then ITCLCONF_PATH=$d success=1 break fi done if test $success = 1 ; then AC_MSG_RESULT($ITCLCONF_PATH/$ITCLCONF_FILE) . $ITCLCONF_PATH/$ITCLCONF_FILE AC_MSG_CHECKING(version of Itcl) AC_MSG_RESULT($ITCL_VERSION) if test "$ITCL_MAJOR_VERSION" -ne 3; then echo if test "$ENABLE_UNSUPPORTED" == "yes" ; then AC_MSG_WARN([you may be attempting to use TkDesk 2.x with an unsupported version of Itcl. Good luck. Hope it works out for you.]) else AC_MSG_ERROR([found Itcl, but it appears to be the wrong version. TkDesk 2.x requires itcl 3.x to be installed. Please get it from: http://www.incrtcl.sourceforge.net.]) fi fi else AC_MSG_RESULT([not found]) ITCLCONF_PATH=[[N/A]] ITCL_VERSION="no" ITCL_MAJOR_VERSION=0 AC_MSG_WARN([could not find $ITCLCONF_FILE - going to keep going, but I am not making any promises.]) fi if test "$ITCL_VERSION" != "no" ; then # ITCL_LIB_SPEC already set in itclConfig.sh ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl${ITCL_VERSION}.a else # Guess... [This is annoying] ITCL_GUESSING="[[I guessed]]" if test -f "${TCLCONF_PATH}/libitcl.so" -o -f "${TCLCONF_PATH}/libitcl.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl.a fi if test -f "${TCLCONF_PATH}/libitcl3.0.so" -o -f "${TCLCONF_PATH}/libitcl3.0.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl3.0" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl3.0.a fi if test -f "${TCLCONF_PATH}/libitcl3.1.so" -o -f "${TCLCONF_PATH}/libitcl3.1.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl3.1" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl3.1.a fi if test -f "${TCLCONF_PATH}/libitcl3.2.so" -o -f "${TCLCONF_PATH}/libitcl3.2.a" then ITCL_LIB_SPEC="-L${TCLCONF_PATH} -litcl3.2" ITCL_LIB_STATIC=${TCLCONF_PATH}/libitcl3.2.a fi fi # # Check whether to link with installed BLT # AC_MSG_CHECKING(for BLT library) if test -z "$BLT_LIB_PATH" ; then BLT_LIB_SPEC=blt/libBLT.a BLT_LIB_STATIC=blt/libBLT.a NEED_BLT_LIB=lib_blt AC_MSG_RESULT(./blt) else BLT_LIB_SPEC="-L$BLT_LIB_PATH -lBLT" BLT_LIB_STATIC=$BLT_LIB_PATH/libBLT.a NEED_BLT_LIB= if test ! -f $BLT_LIB_PATH/libBLT.a \ -a ! -f $BLT_LIB_PATH/libBLT.so then if test ! -f $BLT_LIB_PATH/lib/libBLT.a \ -a ! -f $BLT_LIB_PATH/lib/libBLT.so then echo AC_MSG_ERROR([could not find libBLT.a or libBLT.so in $BLT_LIB_PATH or in $BLT_LIB_PATH/lib.]) else BLT_LIB_PATH=$BLT_LIB_PATH/lib fi fi AC_MSG_RESULT($BLT_LIB_PATH) fi # # Determine include directories # TCL_INCLUDE_PATH=$TCL_EXEC_PREFIX/include TK_INCLUDE_PATH=$TCL_INCLUDE_PATH # # Perform substitutions # AC_SUBST(TCL_LD_SEARCH_FLAGS) AC_SUBST(TCL_EXEC_PREFIX) AC_SUBST(TK_EXEC_PREFIX) AC_SUBST(TCL_INCLUDE_PATH) AC_SUBST(TK_INCLUDE_PATH) AC_SUBST(TK_XINCLUDES) AC_SUBST(TCL_LIB_SPEC) AC_SUBST(TK_LIB_SPEC) AC_SUBST(TK_LIBS) AC_SUBST(TCL_LIB_STATIC) AC_SUBST(TK_LIB_STATIC) AC_SUBST(ITCL_LIB_SPEC) AC_SUBST(ITCL_LIB_STATIC) AC_SUBST(NEED_BLT_LIB) AC_SUBST(BLT_LIB_SPEC) AC_SUBST(BLT_LIB_STATIC) dnl Checks for programs. AC_PATH_PROG(TKDESK, tkdesk) AC_PROG_CC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_RANLIB dnl Checks for header files. AC_PATH_X AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(errno.h fcntl.h float.h limits.h malloc.h memory.h sys/time.h statvfs.h sys/statvfs.h unistd.h waitflags.h sys/vfs.h sys/param.h sys/mount.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_TYPE_PID_T AC_HEADER_TIME AC_STRUCT_TM dnl Checks for library functions. AC_TYPE_SIGNAL AC_CHECK_FUNCS(gethostname gettimeofday socket strdup strcasecmp statvfs statfs) AC_CHECK_LIB(socket, socket, LIBS="-lsocket -lnsl", , -lnsl) AC_SUBST(LIBS) # # Custom Tests: union wait and long string declaration # # # Check whether defines the type "union wait" # correctly. It's needed because of weirdness in HP-UX where # "union wait" is defined in both the BSD and SYS-V environments. # Checking the usability of WIFEXITED seems to do the trick. # AC_MSG_CHECKING([whether union wait is defined correctly]) AC_CACHE_VAL(blt_cv_struct_wait_works, [AC_TRY_COMPILE([#include #include ], [ union wait x; WIFEXITED(x); /* Generates compiler error if WIFEXITED * uses an int. */ ], [blt_cv_struct_wait_works="yes"], [blt_cv_struct_wait_works="no"])]) if test "${blt_cv_struct_wait_works}" = "no"; then AC_DEFINE(NO_UNION_WAIT,1,[Define if union wait type is defined incorrectly.]) fi AC_MSG_RESULT([$blt_cv_struct_wait_works]) AC_CONFIG_FILES([Makefile libdesk/Makefile blt/Makefile netscape-remote/Makefile tkdeskclient/Makefile]) AC_OUTPUT echo echo $PACKAGE_NAME $PACKAGE_VERSION echo eval echo Override Version Checks....... : $ENABLE_UNSUPPORTED eval echo Use TkStep.................... : $ENABLE_TKSTEP eval echo Tcl Config Path............... : $TCLCONF_PATH/$TCLCONF_FILE eval echo Tk Config Path................ : $TKCONF_PATH/$TKCONF_FILE eval echo Tcl Include Path.............. : $TCL_INCLUDE_PATH eval echo Tk Include Path............... : $TK_INCLUDE_PATH eval echo Tcl Library Spec.............. : $TCL_LIB_SPEC eval echo Tcl Static Library Spec....... : $TCL_LIB_STATIC eval echo Tk Library Spec............... : $TK_LIB_SPEC eval echo Tk Static Library Spec........ : $TK_LIB_STATIC eval echo Itcl Library Spec............. : $ITCL_LIB_SPEC $ITCL_GUESSING eval echo Itcl Static Library Spec...... : $ITCL_LIB_STATIC $ITCL_GUESSING eval echo BLT Library Spec.............. : $BLT_LIB_SPEC eval echo BLT Static Library Spec....... : $BLT_LIB_STATIC echo eval eval echo Will install TkDesk binaries in ${bindir}. eval eval echo Will install TkDesk man pages under ${prefix}/man/. eval eval echo Will install the TkDesk library under ${exec_prefix}/lib/TkDesk. if test "x$TKDESK" != "x" ; then echo Warning: You have an old copy of TkDesk at $TKDESK. fi echo echo configure complete, now type \'make\' echo tkdesk-2.0/install-sh0100755000175000007640000000421210020457430012723 0ustar jccjcc#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5; it is not part of GNU. # # $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ # # This script is compatible with the BSD install script, but was written # from scratch. # # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" instcmd="$mvprog" chmodcmd="" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; *) if [ x"$src" = x ] then src=$1 else dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` fi # Make a temp file name in the proper directory. dstdir=`dirname $dst` dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp # and set any options; do chmod last to preserve setuid bits if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi # Now rename the file to the real destination. $doit $rmcmd $dst $doit $mvcmd $dsttmp $dst exit 0 tkdesk-2.0/license.terms0100644000175000007640000000361310020457430013421 0ustar jccjccNote: These terms apply to the file tkAppInit.c only. See the file COPYING for more information. Christian Bolik ----------------------------------------------------------------------------- This software is copyrighted by the Regents of the University of California, Sun Microsystems, Inc., and other parties. The following terms apply to all files associated with the software unless explicitly disclaimed in individual files. The authors hereby grant permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. RESTRICTED RIGHTS: Use, duplication or disclosure by the government is subject to the restrictions as set forth in subparagraph (c) (1) (ii) of the Rights in Technical Data and Computer Software Clause as DFARS 252.227-7013 and FAR 52.227-19. tkdesk-2.0/testdrive0100755000175000007640000000013210020457430012653 0ustar jccjcc#!/bin/sh TKDESK_LIBRARY=`pwd`/tcldesk export TKDESK_LIBRARY ./tkdesksh tkdesk -default tkdesk-2.0/tkAppInit.c0100644000175000007640000001077510037125405013003 0ustar jccjcc/* ============================================================================ * * File: tkAppInit.c * Project: TkDesk * Started: 02.10.94 * Changed: 09.07.96 * * Description: Initializes Tcl, Tk and TkDesk. Is a modified tkAppInit.c. * * ---------------------------------------------------------------------------- * * Functions: * Tcl_AppInit -- * * * ========================================================================= */ /* ========== Here follows the original header from tkAppInit.c: =========== */ /* * tkAppInit.c -- * * Provides a default version of the Tcl_AppInit procedure for * use in wish and similar Tk-based applications. * * Copyright (c) 1993 The Regents of the University of California. * Copyright (c) 1994 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. */ /* #ifndef lint static char sccsid[] = "@(#) tkAppInit.c 1.12 94/12/17 16:30:56"; #endif */ #include "config.h" #include "tk.h" #include "libdesk/ot.h" /* * The following variable is a special hack that is needed in order for * Sun shared libraries to be used for Tcl. * * [Is this still needed? --JCC] */ #ifdef NEED_MATHERR extern int matherr(); int *tclDummyMathPtr = (int *) matherr; #endif /* *---------------------------------------------------------------------- * * main -- * * This is the main program for the application. * * Results: * None: Tk_Main never returns here, so this procedure never * returns either. * * Side effects: * Whatever the application does. * *---------------------------------------------------------------------- */ int main(argc, argv) int argc; /* Number of command-line arguments. */ char **argv; /* Values of command-line arguments. */ { if (getenv ("DISPLAY") == NULL) { fprintf (stderr, "\nTkDesk requires the X Window System to run and the DISPLAY\n"); fprintf (stderr, "environment variable to be set. If X is already running you\n"); fprintf (stderr, "can set the DISPLAY variable for instance like this:\n\n"); fprintf (stderr, "DISPLAY=:0.0; export DISPLAY\n\n"); exit (1); } Tk_Main(argc, argv, Tcl_AppInit); return 0; /* Needed only to prevent compiler warning. */ } /* *---------------------------------------------------------------------- * * Tcl_AppInit -- * * This procedure performs application-specific initialization. * Most applications, especially those that incorporate additional * packages, will have their own version of this procedure. * * Results: * Returns a standard Tcl completion code, and leaves an error * message in interp->result if an error occurs. * * Side effects: * Depends on the startup script. * *---------------------------------------------------------------------- */ int Tcl_AppInit(interp) Tcl_Interp *interp; /* Interpreter for application. */ { Tk_Window main; extern int Itcl_Init _ANSI_ARGS_((Tcl_Interp *)); extern int Blt_Init _ANSI_ARGS_((Tcl_Interp *)); extern int Netscape_remote_Init _ANSI_ARGS_((Tcl_Interp *)); extern int Dsk_Init _ANSI_ARGS_((Tcl_Interp *)); extern Tk_ImageType tixPixmapImageType; main = Tk_MainWindow(interp); if (Tcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Tk_Init(interp) == TCL_ERROR) { return TCL_ERROR; } Tk_CreateImageType(&tixPixmapImageType); /* * Call the init procedures for included packages. Each call should * look like this: * * if (Mod_Init(interp) == TCL_ERROR) { * return TCL_ERROR; * } * * where "Mod" is the name of the module. */ if (Itcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Blt_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Ot_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Netscape_remote_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Dsk_Init(interp) == TCL_ERROR) { return TCL_ERROR; } /* * Call Tcl_CreateCommand for application-specific commands, if * they weren't already created by the init procedures called above. */ /* * Specify a user-specific startup file to invoke if the application * is run interactively. Typically the startup file is "~/.apprc" * where "app" is the name of the application. If this line is deleted * then no user-specific startup file will be run under any conditions. */ /*tcl_RcFileName = "~/.tkdeskrc";*/ return TCL_OK; } tkdesk-2.0/tkdesk.main0100644000175000007640000010732710037304716013073 0ustar jccjcc # Set the following variable to 0 to enable dynamic auto-loading of TkDesk's # library files. This usually improves start-up time. set tkdesk(no_auto_loading) 1 # TkDesk - a file manager for Unix and the X Window System # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ============================================================================= # # START FILE FOR TKDESK # # ============================================================================= # wm withdraw . wm group . . wm command . "$argv0 $argv" # # ==== Init =================================================================== # set tkdesk(version) "2.0b" set tkdesk(date) "April 15, 2004" if [info exists env(TKDESK_LIBRARY)] { set tkdesk(library) $env(TKDESK_LIBRARY) } if ![info exists tkdesk(library)] { set tkdesk(library) "./tcldesk" } if {[string index $tkdesk(library) 0] == "~"} { catch {set tkdesk(library) [glob $tkdesk(library)]} } if ![file exists $tkdesk(library)] { puts "" puts "Can't find the TkDesk run-time library. Please either edit" puts "the first lines of the tkdesk script to point to where you" puts "installed the library, or set the environment variable" puts "TKDESK_LIBRARY accordingly. Also please check that the" puts "correct tkdesksh is being used by the script." puts "" exit 2 } if {![info exists dskC_version] || $dskC_version != $tkdesk(version)} { puts "" puts "The tkdesk script seems to be using an older version of tkdesksh." puts "Please edit the third line of this script to point to the absolute" puts "path and file name of tkdesksh. Both are usually located in the" puts "same directory." puts "" puts "An alternative is also to modify your PATH environment variable to" puts "list the directory where you installed Tkdesk $tkdesk(version) before" puts "the one that contains the older tkdesksh." puts "" puts "The easiest method is of course to simply delete the old tkdesksh." puts "" exit 3 } # ------------------------------------------------------- # Following is the bitmap used by dsk_welcome: set tkdesk(welcome_bm) { #define tkdesk_width 253 #define tkdesk_height 66 static char tkdesk_bits[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0xf8,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x1f,0x00,0x00,0xe0,0x00,0x00,0x00, 0x00,0x00,0x00,0xc0,0xff,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xff,0x0f,0x00,0x00,0xe0,0x00, 0x00,0x00,0x00,0x00,0x00,0xc0,0xff,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xff,0x0f,0x00,0x00, 0xe0,0x00,0xfe,0xff,0xff,0xff,0xff,0x0f,0xfc,0x07,0x00,0x00,0x00,0xff,0xff, 0xff,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x0f, 0x00,0x00,0xe0,0x00,0xfe,0xff,0xff,0xff,0xff,0x07,0xf0,0x07,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xe0,0x0f,0x00,0x00,0xe0,0x00,0xfe,0xff,0xff,0xff,0xff,0x07,0xf0,0x03,0x00, 0x00,0x00,0xff,0xff,0xff,0xff,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0xe0,0x07,0x00,0x00,0xe0,0x00,0xff,0x00,0xfc,0x01,0xf8,0x07,0xf0, 0x03,0x00,0x00,0x00,0xc0,0x3f,0x00,0xfc,0x1f,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xe0,0x07,0x00,0x00,0xe0,0x00,0x3f,0x00,0xfc,0x01,0xe0, 0x07,0xf8,0x03,0x00,0x00,0x00,0xc0,0x1f,0x00,0xc0,0x3f,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x07,0x00,0x00,0xe0,0x00,0x0f,0x00,0xfc, 0x01,0xc0,0x07,0xf8,0x03,0x00,0x00,0x00,0xc0,0x1f,0x00,0x00,0xff,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x07,0x00,0x00,0xe0,0x00,0x07, 0x00,0xfe,0x00,0x80,0x03,0xf8,0x01,0x00,0x00,0x00,0xc0,0x1f,0x00,0x00,0xfe, 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x03,0x00,0x00,0xe0, 0x80,0x07,0x00,0xfe,0x00,0x80,0x03,0xf8,0x01,0x00,0x00,0x00,0xe0,0x1f,0x00, 0x00,0xfc,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x03,0x00, 0x00,0xe0,0x80,0x03,0x00,0xfe,0x00,0x80,0x03,0xfc,0x01,0x00,0x00,0x00,0xe0, 0x0f,0x00,0x00,0xf8,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8, 0x03,0x00,0x00,0xe0,0x80,0x03,0x00,0xfe,0x00,0x80,0x03,0xfc,0x01,0x00,0x00, 0x00,0xe0,0x0f,0x00,0x00,0xf0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0xf8,0x03,0x00,0x00,0xe0,0x80,0x01,0x00,0xfe,0x00,0x80,0x01,0xfc,0x01, 0x00,0x00,0x00,0xe0,0x0f,0x00,0x00,0xf0,0x07,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xf8,0x03,0x00,0x00,0xe0,0x80,0x01,0x00,0x7f,0x00,0x80,0x01, 0xfc,0x00,0x00,0x00,0x00,0xe0,0x0f,0x00,0x00,0xe0,0x0f,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0xf8,0x01,0x00,0x00,0xe0,0xc0,0x01,0x00,0x7f,0x00, 0x80,0x01,0xfc,0x00,0x00,0x00,0x00,0xf0,0x07,0x00,0x00,0xe0,0x0f,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x01,0x00,0x00,0xe0,0x00,0x00,0x00, 0x7f,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0xf0,0x07,0x00,0x00,0xe0,0x1f, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x01,0x00,0x00,0xe0,0x00, 0x00,0x00,0x7f,0x00,0x00,0x00,0xfe,0x00,0x80,0x0f,0x00,0xf0,0x07,0x00,0x00, 0xc0,0x1f,0x00,0x00,0xf0,0x07,0x00,0x00,0x7f,0x00,0x00,0xfc,0x01,0x00,0x1f, 0xe0,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0x7e,0x00,0xe0,0x1f,0x00,0xf0,0x07, 0x00,0x00,0xc0,0x1f,0x00,0x00,0xfe,0x3f,0x00,0xe0,0xff,0x03,0x00,0xfc,0x00, 0xc0,0x3f,0xe0,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0x7e,0x00,0xf0,0x3f,0x00, 0xf8,0x07,0x00,0x00,0xc0,0x3f,0x00,0xc0,0xff,0x7f,0x00,0xf0,0xff,0x07,0x00, 0xfc,0x00,0xe0,0x7f,0xe0,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0x7f,0x00,0xfc, 0x3f,0x00,0xf8,0x03,0x00,0x00,0xc0,0x3f,0x00,0xe0,0x07,0xff,0x00,0xf8,0xf0, 0x0f,0x00,0xfe,0x00,0xf8,0x7f,0xe0,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0x7f, 0x00,0xfe,0x3f,0x00,0xf8,0x03,0x00,0x00,0xc0,0x3f,0x00,0xf8,0x01,0xfe,0x00, 0x3c,0xe0,0x1f,0x00,0xfe,0x00,0xfc,0x7f,0xe0,0x00,0x00,0x80,0x3f,0x00,0x00, 0x00,0x3f,0x00,0xff,0x3f,0x00,0xf8,0x03,0x00,0x00,0xc0,0x3f,0x00,0xfc,0x00, 0xfc,0x01,0x3e,0xc0,0x1f,0x00,0x7e,0x00,0xfe,0x7f,0xe0,0x00,0x00,0xc0,0x1f, 0x00,0x00,0x00,0x3f,0x80,0xc7,0x1f,0x00,0xf8,0x03,0x00,0x00,0xc0,0x3f,0x00, 0x7e,0x00,0xfc,0x01,0x1e,0x80,0x1f,0x00,0x7e,0x00,0x8f,0x3f,0xe0,0x00,0x00, 0xc0,0x1f,0x00,0x00,0x80,0x3f,0xc0,0x01,0x0f,0x00,0xfc,0x01,0x00,0x00,0xc0, 0x3f,0x00,0x3f,0x00,0xfc,0x01,0x1e,0x80,0x1f,0x00,0x7f,0x80,0x03,0x1e,0xe0, 0x00,0x00,0xc0,0x1f,0x00,0x00,0x80,0x3f,0xe0,0x00,0x00,0x00,0xfc,0x01,0x00, 0x00,0xc0,0x3f,0x80,0x1f,0x00,0xfc,0x01,0x1f,0x00,0x1f,0x00,0x7f,0xc0,0x01, 0x00,0xe0,0x00,0x00,0xc0,0x1f,0x00,0x00,0x80,0x3f,0x70,0x00,0x00,0x00,0xfc, 0x01,0x00,0x00,0xc0,0x3f,0xc0,0x1f,0x00,0xfc,0x01,0x1f,0x00,0x06,0x00,0x7f, 0xe0,0x00,0x00,0xe0,0x00,0x00,0xe0,0x0f,0x00,0x00,0x80,0x1f,0x38,0x00,0x00, 0x00,0xfc,0x01,0x00,0x00,0xc0,0x3f,0xc0,0x0f,0x00,0xfe,0x01,0x3f,0x00,0x00, 0x00,0x3f,0x70,0x00,0x00,0xe0,0x00,0x00,0xe0,0x0f,0x00,0x00,0xc0,0x1f,0x1c, 0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0xc0,0x3f,0xe0,0x0f,0x00,0xfe,0x00,0x3f, 0x00,0x00,0x80,0x3f,0x38,0x00,0x00,0xe0,0x00,0x00,0xe0,0x0f,0x00,0x00,0xc0, 0x1f,0x0e,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0xe0,0x3f,0xf0,0x07,0x00,0xff, 0x00,0x7f,0x00,0x00,0x80,0x3f,0x1c,0x00,0x00,0xe0,0x00,0x00,0xe0,0x0f,0x00, 0x00,0xc0,0x1f,0x07,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0xe0,0x1f,0xf0,0x07, 0x00,0x7f,0x00,0xff,0x00,0x00,0x80,0x3f,0x0e,0x00,0x00,0xe0,0x00,0x00,0xf0, 0x07,0x00,0x00,0xc0,0x8f,0x03,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0xe0,0x1f, 0xf8,0x03,0x80,0x3f,0x00,0xff,0x01,0x00,0x80,0x1f,0x07,0x00,0x00,0xe0,0x00, 0x00,0xf0,0x07,0x00,0x00,0xc0,0xcf,0x03,0x00,0x00,0x00,0xff,0x00,0x00,0x00, 0xe0,0x1f,0xf8,0x03,0xc0,0x1f,0x00,0xfe,0x03,0x00,0x80,0x9f,0x07,0x00,0x00, 0xe0,0x00,0x00,0xf0,0x07,0x00,0x00,0xe0,0xef,0x07,0x00,0x00,0x00,0x7f,0x00, 0x00,0x00,0xf0,0x1f,0xf8,0x03,0xf0,0x0f,0x00,0xfe,0x07,0x00,0xc0,0xdf,0x0f, 0x00,0x00,0xe0,0x00,0x00,0xf0,0x07,0x00,0x00,0xe0,0xef,0x07,0x00,0x00,0x00, 0x7f,0x00,0x00,0x00,0xf0,0x0f,0xfc,0x01,0xf8,0x03,0x00,0xfc,0x0f,0x00,0xc0, 0xdf,0x0f,0x00,0x00,0xe0,0x00,0x00,0xf0,0x07,0x00,0x00,0xe0,0xf7,0x07,0x00, 0x00,0x00,0x7f,0x00,0x00,0x00,0xf0,0x0f,0xfc,0x01,0xfe,0x01,0x00,0xf8,0x1f, 0x00,0xc0,0xef,0x0f,0x00,0x00,0xe0,0x00,0x00,0xf8,0x03,0x00,0x00,0xe0,0xe7, 0x0f,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0xf8,0x0f,0xfc,0xc1,0x3f,0x00,0x00, 0xf0,0x3f,0x00,0xc0,0xcf,0x1f,0x00,0x00,0xe0,0x00,0x00,0xf8,0x03,0x00,0x00, 0xf0,0xe7,0x0f,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0xf8,0x07,0xfc,0xf9,0x0f, 0x00,0x00,0xe0,0x7f,0x00,0xe0,0xcf,0x1f,0x00,0x00,0xe0,0x00,0x00,0xf8,0x03, 0x00,0x00,0xf0,0xe7,0x0f,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0xfc,0x07,0xfe, 0xff,0x01,0x00,0x00,0xc0,0x7f,0x00,0xe0,0xcf,0x1f,0x00,0x00,0xe0,0x00,0x00, 0xf8,0x03,0x00,0x00,0xf0,0xc7,0x1f,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0xfc, 0x03,0xfe,0x1f,0x00,0x00,0x00,0x80,0xff,0x00,0xe0,0x8f,0x3f,0x00,0x00,0xe0, 0x00,0x00,0xfc,0x01,0x00,0x00,0xf0,0xc3,0x1f,0x00,0x00,0x80,0x3f,0x00,0x00, 0x00,0xfe,0x03,0xfe,0x01,0x00,0x00,0x00,0x00,0xff,0x00,0xe0,0x87,0x3f,0x00, 0x00,0xe0,0x00,0x00,0xfc,0x01,0x00,0x00,0xf8,0xc3,0x1f,0x00,0x00,0xc0,0x3f, 0x00,0x00,0x00,0xfe,0x01,0xfe,0x00,0x00,0x00,0x00,0x00,0xfe,0x01,0xf0,0x87, 0x3f,0x00,0x00,0xe0,0x00,0x00,0xfc,0x01,0x00,0x00,0xf8,0x83,0x3f,0x00,0x00, 0xc0,0x1f,0x00,0x00,0x00,0xff,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0xfc,0x01, 0xf0,0x07,0x7f,0x00,0x00,0xe0,0x00,0x00,0xfc,0x01,0x00,0x00,0xf8,0x83,0x3f, 0x00,0x00,0xc0,0x1f,0x00,0x00,0x80,0xff,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, 0xfc,0x01,0xf0,0x07,0x7f,0x00,0x00,0xe0,0x00,0x00,0xfc,0x01,0x00,0x00,0xf8, 0x01,0x7f,0x00,0x00,0xc0,0x1f,0x00,0x00,0xc0,0x7f,0x00,0xfe,0x00,0x00,0x18, 0x00,0x00,0xfc,0x01,0xf0,0x03,0xfe,0x00,0x00,0xe0,0x00,0x00,0xfe,0x00,0x00, 0x00,0xfc,0x01,0x7f,0x00,0x06,0xe0,0x1f,0x00,0x00,0xc0,0x3f,0x00,0xfe,0x00, 0x00,0x1c,0x78,0x00,0xf8,0x01,0xf8,0x03,0xfe,0x00,0x0c,0xe0,0x00,0x00,0xfe, 0x00,0x00,0x00,0xfc,0x01,0xff,0x00,0x06,0xe0,0x0f,0x00,0x00,0xf0,0x1f,0x00, 0xfe,0x01,0x00,0x1e,0x7c,0x00,0xf8,0x01,0xf8,0x03,0xfe,0x01,0x0c,0xe0,0x00, 0x00,0xfe,0x00,0x00,0x00,0xfc,0x01,0xfe,0x00,0x07,0xe0,0x0f,0x00,0x00,0xf8, 0x0f,0x00,0xfe,0x01,0x00,0x0e,0x7e,0x00,0xf8,0x01,0xf8,0x03,0xfc,0x01,0x0e, 0xe0,0x00,0x00,0xfe,0x00,0x00,0x00,0xfc,0x00,0xfe,0x01,0x03,0xe0,0x0f,0x00, 0x00,0xfc,0x07,0x00,0xfc,0x01,0x80,0x07,0xfe,0x00,0xf8,0x00,0xf8,0x01,0xfc, 0x03,0x06,0xe0,0x00,0x00,0x7f,0x00,0x00,0x00,0xfc,0x00,0xfc,0x81,0x03,0xe0, 0x0f,0x00,0x00,0xff,0x01,0x00,0xfc,0x03,0xc0,0x03,0xfe,0x00,0xf8,0x00,0xf8, 0x01,0xf8,0x03,0x07,0xe0,0x00,0x00,0x7f,0x00,0x00,0x00,0xfe,0x00,0xfc,0xc3, 0x01,0xf0,0x0f,0x00,0xe0,0xff,0x00,0x00,0xfc,0x07,0xe0,0x01,0xfe,0x01,0x7c, 0x00,0xfc,0x01,0xf8,0x87,0x03,0xe0,0x00,0x80,0xff,0x00,0x00,0x00,0xfe,0x00, 0xf8,0xe7,0x00,0xf0,0x1f,0x00,0xfe,0x3f,0x00,0x00,0xf8,0x0f,0xfc,0x00,0xfc, 0x03,0x7c,0x00,0xfc,0x01,0xf0,0xcf,0x01,0xe0,0x00,0xe0,0xff,0x03,0x00,0x00, 0xfe,0x00,0xf8,0xff,0xe0,0xff,0xff,0xff,0xff,0x07,0x00,0x00,0xf0,0xff,0x7f, 0x00,0xf8,0x0f,0x3e,0x00,0xfc,0x01,0xf0,0xff,0x01,0xe0,0x00,0xff,0xff,0x7f, 0x00,0x00,0x7e,0x00,0xf0,0x7f,0xe0,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xe0, 0xff,0x1f,0x00,0xf0,0xff,0x1f,0x00,0xfc,0x00,0xe0,0xff,0x00,0xe0,0x00,0xff, 0xff,0x7f,0x00,0x00,0x7f,0x00,0xe0,0x3f,0xe0,0xff,0xff,0xff,0x07,0x00,0x00, 0x00,0xc0,0xff,0x07,0x00,0xe0,0xff,0x07,0x00,0xfe,0x00,0xc0,0x7f,0x00,0xe0, 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x80,0x0f,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0xff,0x00,0x00,0x1e,0x00,0x00,0x1f, 0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0}; } # # ----------------------------------------------------------------------------- # # Proc: dsk_welcome # Desc: Shows an informational "welcome" window when starting up. # In: cmd: "show" or "hide" # Out: "" # Side FX: none # proc dsk_welcome {cmd} { global tkdesk #set bgcol gray set bgcol gray82 set t .dsk_welcome if {$cmd == "hide"} { catch "destroy $t" return "" } toplevel $t -bg $bgcol wm withdraw $t wm group $t . wm command $t frame $t.f -bd 2 -relief raised -bg $bgcol pack $t.f frame $t.f1 -bd 2 -relief groove -bg $bgcol pack $t.f1 -in $t.f -padx 8 -pady 8 label $t.lLabel -text "Welcome to..." -anchor center -fg black -bg $bgcol catch {$t.lLabel config -font -*-helvetica-medium-r-*-*-14-*} pack $t.lLabel -in $t.f1 -padx 8 -pady 8 -fill x -expand yes if {[file exists $tkdesk(library)/images/tkdesk_logo.ppm]} { image create photo welcome_bm -file $tkdesk(library)/images/tkdesk_logo.ppm label $t.lTkDesk -image welcome_bm -bg $bgcol -relief raised -bd 10 } else { image create bitmap welcome_bm -data $tkdesk(welcome_bm) -foreground blue3 label $t.lTkDesk -image welcome_bm -bg $bgcol } pack $t.lTkDesk -in $t.f1 -padx 6 -pady 2 -fill x -expand yes message $t.lVersion -text "Version $tkdesk(version), dated $tkdesk(date)\nCopyright (C) 1996 Christian Bolik\n\nTkDesk comes with ABSOLUTELY NO WARRANTY.\nSee menu entry Help/License for details." \ -bg $bgcol -fg black \ -aspect 1000 -justify center catch {$t.lVersion config -font -*-helvetica-medium-r-*-*-14-*} pack $t.lVersion -in $t.f1 -padx 8 -pady 8 -fill x -expand yes if $tkdesk(in_development) { label $t.lDevel -text "...in Development..." \ -bg $bgcol -fg black catch {$t.lDevel config -font -*-helvetica-bold-o-*-*-14-*} pack $t.lDevel -in $t.f1 -padx 8 -pady 8 -fill x -expand yes } frame $t.f2 -bd 2 -relief groove -bg $bgcol pack $t.f2 -in $t.f -fill x -padx 8 -pady 8 label $t.lProgress -text "Loading..." -anchor w \ -bg $bgcol -fg black -width 25 pack $t.lProgress -in $t.f2 -fill x -anchor w -padx 8 update idletasks set x [expr [winfo screenwidth $t]/2 - [winfo reqwidth $t]/2 \ - [winfo vrootx [winfo parent $t]]] set y [expr [winfo screenheight $t]/2 - [winfo reqheight $t]/2 \ - [winfo vrooty [winfo parent $t]]] wm geom $t +$x+$y wm title $t "Starting..." wm deiconify $t update return "" } proc dsk_progress {string} { if ![winfo exists .dsk_welcome] return .dsk_welcome.lProgress config -text $string update idletasks } set tkdesk(error_source) "" proc tkerror {err} { global tkdesk errorCode errorInfo tk_patchLevel tcl_patchLevel global blt_version itcl::version set code $errorCode set info $errorInfo if [winfo exists .dsk_tkerror] return cd ~ dsk_lazy force dsk_sound dsk_bug if {$tkdesk(error_source) == "" || $tkdesk(in_development)} { set ans [tk_dialog [toplevel .dsk_tkerror] \ "Oh no, an error!" \ "Sorry, but you have just found an error in TkDesk. You can ignore it or choose one of the actions below." \ @$tkdesk(library)/images/xbm/bomb.xbm \ 0 " Ignore " " Inspect " " Mail Author "] } else { set ans [tk_dialog [toplevel .dsk_tkerror] \ "Configuration Error" \ "Error while executing action from \"$tkdesk(error_source)\":\n$err" \ error 0 " OK " " Inspect "] set tkdesk(error_source) "" } switch $ans { 1 {dsk_editor string "Error: $err\n\nerrorCode: $code\n\nerrorInfo: $info"} 2 { set subject "Error in TkDesk $tkdesk(version)" set msg "What did you try to do when this bug occured:\n" append msg "\n\n-----------------------------------------------" append msg "\nError in TkDesk $tkdesk(version), using " append msg "Tcl [set tcl_patchLevel], " append msg "Tk [set tk_patchLevel], " if [info exists itcl::version] { append msg "Itcl [set itcl::version], " } else { append msg "Itcl 1.5, " } append msg "BLT [set blt_version].\nPlatform: " catch {append msg "[exec uname -a]\n"} append msg "\n" append msg "Error: $err\n" append msg "errorCode: $code\n" append msg "errorInfo: $info" dsk_mailbug $subject $msg } } return "" } proc dsk_mailbug {subject msg} { global tkdesk dsk_mailbug catch {destroy .dsk_mailbug} toplevel [set t .dsk_mailbug] wm withdraw $t frame $t.f1 -bd 1 -relief raised pack $t.f1 -fill x label $t.ls -text "Subject:" pack $t.ls -in $t.f1 -side left -padx 4 -pady 4 entry $t.es -width 10 -bd 2 -relief sunken $t.es insert end $subject pack $t.es -in $t.f1 -side left -fill x -expand yes \ -padx 4 -pady 4 -ipady 2 frame $t.f2 -bd 1 -relief raised pack $t.f2 -fill both -expand yes frame $t.f2a pack $t.f2a -in $t.f2 -fill both -expand yes -padx 4 -pady 4 text $t.tm -width 10 -height 4 -bd 2 -relief sunken -setgrid 1 \ -yscroll "$t.sb set" $t.tm insert end "$msg" $t.tm mark set insert 2.0 pack $t.tm -in $t.f2a -side left -fill both -expand yes scrollbar $t.sb -orient vertical -command "$t.tm yview" pack $t.sb -in $t.f2a -side left -fill y frame $t.f3 -bd 1 -relief raised pack $t.f3 -fill x button $t.bc -text "Cancel" \ -command "set dsk_mailbug(mail) 0" button $t.bs -text "Save..." \ -command "set dsk_mailbug(mail) 2" button $t.bm -text "Send Mail to $tkdesk(maintmail)" \ -command "set dsk_mailbug(mail) 1" pack $t.bc $t.bs $t.bm -in $t.f3 -side left -padx 4 -pady 4 -ipady 1 wm protocol $t WM_DELETE_WINDOW "set dsk_mailbug(mail) 0" if {$msg != ""} { wm title $t "Mail Bug Report to Author" } else { wm title $t "Send Mail to Author" } wm geometry $t 38x15 cb_centerToplevel $t wm deiconify $t focus $t.tm grab $t set dsk_mailbug(mail) "" tkwait variable dsk_mailbug(mail) if {$dsk_mailbug(mail) == 1} { catch {set fd [open "|mail -s \"$subject\" $tkdesk(maintmail)" w]} em if ![info exists fd] { catch {set fd [open "|mail $tkdesk(maintmail)" w]} em } if ![info exists fd] { cb_error "Couldn't execute: mail $tkdesk(maintmail). Please send your bug report manually." tkwait variable dsk_mailbug(mail) } else { set msg [$t.tm get 1.0 end] puts -nonewline $fd $msg close $fd } } elseif {$dsk_mailbug(mail) == 2} { while 1 { set f [cb_fileSelector -label "Save bug report"] if {$f != ""} { set err [catch {set fd [open $f "w"]}] if $err { cb_error "Couldn't open $f for writing." } else { set msg [$t.tm get 1.0 end] puts -nonewline $fd $msg close $fd break } } else { break } } } destroy $t } proc dsk_widget_config {} { global tkdesk dsk_Listbox :: selcolor [cb_col $tkdesk(color,listsel)] dsk_Listbox :: modifier $tkdesk(use_old_modifiers) # "System" Settings dsk_FileListbox :: font [cb_font $tkdesk(font,file_lbs)] dsk_FileListbox :: tag config dir \ [cb_col $tkdesk(color,directories)] \ [cb_font $tkdesk(font,directories)] dsk_FileListbox :: tag config exec \ [cb_col $tkdesk(color,executables)] \ [cb_font $tkdesk(font,executables)] dsk_FileListbox :: tag config sym \ [cb_col $tkdesk(color,symlinks)] \ [cb_font $tkdesk(font,symlinks)] dsk_FileListbox :: tag config symdir \ [cb_col $tkdesk(color,symdirectories)] \ [cb_font $tkdesk(font,symdirectories)] dsk_FileListbox :: tag config symexec \ [cb_col $tkdesk(color,symexecutables)] \ [cb_font $tkdesk(font,symexecutables)] # "Preferences" Settings dsk_FileListbox :: showall $tkdesk(show_all_files) dsk_FileListbox :: longlist $tkdesk(long_listing) dsk_FileListbox :: topfolders $tkdesk(folders_on_top) dsk_FileListbox :: typechar $tkdesk(append_type_char) dsk_FileListbox :: addicons $tkdesk(add_icons) dsk_FileListbox :: sort $tkdesk(default_sort) # "FileTags" Settings dsk_FileListbox_fileTags } # # ----------------------------------------------------------------------------- # if {$tkdesk(library) == "./tcldesk"} { # need an absolute library path set tkdesk(library) [pwd]/tcldesk } # See if the user just wants to learn TkDesk's command line arguments: if {$argv == "-?" || $argv == "--help"} { puts -nonewline \ "TkDesk - A Graphical Desktop and File Manager for UNIX and X11 Version $tkdesk(version), dated $tkdesk(date) Copyright (C) 1996 Christian Bolik Usage: tkdesk \[options\] Options may be any combination of the following: -configdir Read user configuration from . -debug Enable so-called debug mode. -default Use default user configuration found in: $tkdesk(library)/configs -develop Enable development mode. -iconic Iconify all windows of TkDesk on start-up. -layout Read the window layout from . -startdir Display in first file browser. -twm Disable use of colored icon windows. " exit 0 } dsk_welcome show # store Tcl version for compatibility handling set tkdesk(tcl_version) [info tclversion] # "Uglify" ;-) namespaced BLT: if {$tkdesk(tcl_version) >= 8.0} { namespace import blt::* } # Update BLT lib path # (Fix this: depending on certain BLT lib files is a bit unreliable...) if {![file exists $blt_library/bltDragdrop.tcl] && ![file exists $blt_library/dragdrop.tcl]} { set blt_library $tkdesk(library) #puts "blt_library: $blt_library" } if {[info command bgexec] != ""} { rename bgexec blt_bgexec } if {[info command busy] != ""} { rename busy blt_busy } if {[info command drag&drop] != ""} { # this must be handled differently due to internal bindings: proc blt_drag&drop {args} { eval drag&drop $args } } # # Init cb_tools # dsk_progress "Initializing cb_tools..." set cb_tools(path) [glob "$tkdesk(library)/cb_tools"] source $cb_tools(path)/tools.tcl cb_tools_init $cb_tools(path) \ $tkdesk(in_development) \ $tkdesk(no_auto_loading) #cb_debug trace wm #cb_debug trace update #cb_debug trace cb_image # # Init tcldesk # if {[info command grab-orig] == ""} { rename grab grab-orig proc grab {args} { set ret "" catch {set ret [eval grab-orig $args]} return $ret } } if {[info command itcl_class-orig] == ""} { rename itcl_class itcl_class-orig proc itcl_class {name def} { catch {rename $name {}} uplevel 1 itcl_class-orig $name [list $def] } } # Provide backward-compatibility for "history:" # (force auto-loading of ::history for Tcl 8.0) catch {history} if {[info command history-orig] == "" && [info command history] != ""} { rename history history-orig proc history {args} { set ret {} set err [catch {set ret [eval dir_history $args]}] if $err { set ret [eval history-orig $args] } return $ret } } # Give options set in tk_setPalette higher priority (to override weird # CDE resources, for instance): catch {tk_setPalette} if [regsub {widgetDefault} [info body tk_setPalette] {interactive} nb] { proc tk_setPalette [info args tk_setPalette] $nb unset nb } dsk_progress "Initializing tcldesk..." foreach c [itcl_info classes dsk_*] { catch {rename $c {}} } if $tkdesk(no_auto_loading) { set tkdesk(color,listsel) white set libfiles { config.tcl Frame.tcl Toplevel.tcl Common.tcl Desktop.tcl DiskUsage.tcl Editor.tcl FileInfo.tcl FileListbox.tcl HistEntry.tcl List.tcl Periodic.tcl Viewer.tcl action.tcl annotations.tcl appbar-date.tcl appbar-dialup.tcl appbar-load.tcl appbar-mail.tcl appbar-trash.tcl appbar.tcl bookmarks.tcl copy.tcl cpanels.tcl bltDnd.tcl bltDragdrop.tcl delete.tcl diary.tcl dsk_Listbox.tcl envedit.tcl file_ops.tcl find.tcl help.tcl history.tcl itcl_reload.tcl jdb_rcs.tcl jobs.tcl popup.tcl server.tcl sound.tcl update.tcl util.tcl } foreach f $libfiles { #puts "sourcing $tkdesk(library)/$f" dsk_progress "Initializing tcldesk... ($f)" source $tkdesk(library)/$f } } else { set auto_path "$tkdesk(library) $auto_path" } if $tkdesk(in_development) { blt_drag&drop errors tkerror } else { blt_drag&drop errors "" } # Disable BLT's built-in d&d bindings (except for motion): catch { bind BltDrag&DropButton2 {} bind BltDrag&DropButton2 {} } # # ==== Set misc. global variables ============================================ # # Christian is still the author, of course, but I'm not sure he wants # to see these emails anymore. I'm not even sure this address is still # valid... #set tkdesk(authormail) "Christian.Bolik@mainz.netsurf.de" set tkdesk(authormail) "jchris@users.sourceforge.net" set tkdesk(maintmail) "jchris@users.sourceforge.net" set tkdesk(startdir) [pwd] set tkdesk(initdir) $tkdesk(startdir) set tkdesk(menu,control) 0 ;# used to track in menu invocations set tkdesk(file_lb,control) 0 ;# used to track in file LBs set tkdesk(active_viewer) "" ;# contains name of window last entered set tkdesk(button_bar) "" ;# list for the button bar set tkdesk(xmaster) 0 ;# X "master" app set tkdesk(status) lazy ;# busy or lazy set tkdesk(_dd_x) 0 set tkdesk(_dd_y) 0 set tkdesk_filelb_cmd "" set tkdesk(systype) "" catch {set tkdesk(systype) [exec uname]} proc _menuEntryHeight {} { menu .m .m add command -label "TkDesk:"; update; set rw1 [winfo reqheight .m] .m add command -label "Cool!"; update; set rw2 [winfo reqheight .m] #puts "$rw2 - $rw1" return [expr $rw2 - $rw1] } set tkdesk(_max_menu_entries) \ [expr [winfo screenheight .] / ([_menuEntryHeight] + 1)] # Temp dir to use by this TkDesk: set tkdesk(tmpdir) /tmp/tkdesk[pid] set err [catch { exec rm -rf $tkdesk(tmpdir) exec mkdir $tkdesk(tmpdir) } errmsg] if {$err} { puts stderr "TkDesk: Couldn't create temp dir $tkdesk(tmpdir)" puts stderr "TkDesk: errmsg: $errmsg" puts stderr "TkDesk: Exiting" exit 1 } # use icon windows by default set tkdesk(fvwm) 1 # Overwrite cb_tools' busy and lazy commands: set cb_tools(busy_cmd) dsk_busy set cb_tools(lazy_cmd) dsk_lazy # cd to user's home dir cause this is unlikely to be moved/deleted cd $env(HOME) # # ==== Process command line arguments ========================================= # dsk_busy dsk_progress "Processing command line options..." dsk_debug "Processing cmdline arguments" set tkdesk(iconic) 0 if {$argc > 0} { for {set i 0} {$i < [llength $argv]} {incr i} { set o [lindex $argv $i] switch -glob -- $o { -conf* { incr i set tkdesk(configdir) [lindex $argv $i] if {[string index $tkdesk(configdir) 0] != "/" && \ [string index $tkdesk(configdir) 0] != "~"} { set tkdesk(configdir) $tkdesk(startdir)/$tkdesk(configdir) } } -deb* { set tkdesk(debug) 1 dsk_debug "Debugging enabled (hopefully :-))" } -def* { set tkdesk(configdir) $tkdesk(library)/configs dsk_debug "Reading default configuration." } -dev* { set tkdesk(in_development) 1 dsk_debug "Development mode." } -fvwm { set tkdesk(fvwm) 1 dsk_debug "Fvwm mode forced." } -ic* { set tkdesk(iconic) 1 dsk_debug "Iconic startup." } -lay* { incr i set tkdesk(layout_file) [lindex $argv $i] if {[string index $tkdesk(layout_file) 0] != "/" && \ [string index $tkdesk(layout_file) 0] != "~"} { set tkdesk(layout_file) \ $tkdesk(startdir)/$tkdesk(layout_file) } } -sta* { incr i set tkdesk(user,startdir) [lindex $argv $i] } -twm { set tkdesk(fvwm) 0 dsk_debug "Fvwm mode switched off." } -xm* { set tkdesk(xmaster) 1 } default { if {[string index $o 0] == "-"} { puts "Don't know what $o means. Proceding anyway." } else { set tkdesk(initdir) $o dsk_debug " tkdesk(initdir) = $tkdesk(initdir)" } } } } } # # ==== Read config files: ===================================================== # dsk_progress "Reading configuration..." dsk_read_config set tkdesk(config_done) 1 if $tkdesk(no_auto_loading) { # adjust widget config if not auto-loading dsk_widget_config } if {[winfo depth .] == 1} { set tkdesk(append_type_char) 1 dsk_FileListbox :: typechar $tkdesk(append_type_char) } # # ==== Start-up =============================================================== # dsk_debug "Now starting up" if ![info exists tkdesk(restarted)] { dsk_sound dsk_welcome_begin } # # ---- Enable history management: # dsk_progress "Enabling history..." # path history: dsk_history dir_history -size $tkdesk(history_size) # interactive command history: dsk_history cmd_history -size $tkdesk(history_size) # periodic execution history dsk_history pcmd_history -size $tkdesk(history_size) \ -callback dsk_periodic_cb # hypersearch history: dsk_history hsearch_history -size $tkdesk(history_size) \ -callback dsk_editor_hsearch_cb # opened file history: dsk_history file_history -size $tkdesk(history_size) \ -command "dsk_open .fv0" # executed commands history: dsk_history exec_history -size $tkdesk(history_size) \ -command "eval dsk_exec" # read strings history: dsk_history string_history -size $tkdesk(history_size) # print command history: dsk_history printer_history -size $tkdesk(history_size) # file mask history: dsk_history mask_history -size $tkdesk(history_size) # "execute here" history: dsk_history xhere_history -size $tkdesk(history_size) # mail history: dsk_history mail_history -size $tkdesk(history_size) # search history: dsk_history search_history -size $tkdesk(history_size) # replace history: dsk_history replace_history -size $tkdesk(history_size) # dial-up phone number history: dsk_history dialup_history -size $tkdesk(history_size) set histlist {dir_history cmd_history pcmd_history hsearch_history \ file_history exec_history string_history printer_history \ mask_history xhere_history mail_history search_history \ replace_history dialup_history} set hi 0 if [info exists tkdesk(history_list)] { set hlist [lindex $histlist 0] set i 0 foreach h $tkdesk(history_list) { set ll 0 catch {set ll [llength [lindex $h 0]]} if {[regexp {^\#.*history$} [lindex $h 0]] && $i > 0} { incr hi set hlist [lindex $histlist $hi] } elseif {$i > 0} { $hlist add $h } incr i } } set tkdesk(histlist) $histlist unset histlist hi # # ---- Possibly restore the window layout of the last session: # dsk_progress "Restoring layout..." dsk_restore_layout # # ---- Open the initial file viewer window: # if {![info exists tkdesk(layout)] || !$tkdesk(have_window)} { # disable at_pointer placement for initial window: set ap $tkdesk(at_pointer) set tkdesk(at_pointer) 0 dsk_progress "Creating a file browser..." set dir [dir_history get] if {$dir != ""} { set dir [lindex $dir end] } else { if [info exists tkdesk(user,startdir)] { set dir $tkdesk(user,startdir) } else { set dir $tkdesk(initdir) } } set tkdesk(geometry,fbrowser) \ $tkdesk(file_lb,width)x$tkdesk(file_lb,height)+100+50 dsk_FileViewer .fv[dsk_FileViewer :: id] \ -dir $dir -num_lbs $tkdesk(num_lbs) set tkdesk(geometry,fbrowser) "" # create the Trash window: #set twin .dfl[dsk_FileList :: id] #dsk_progress "Creating the trash window..." #dsk_FileList $twin -dir $tkdesk(configdir)/.trash -dontmap 1 #update #wm iconify $twin #unset twin # and the app bar dsk_progress "Creating the Application Bar..." dsk_appbar set tkdesk(at_pointer) $ap } if ![info exists tkdesk(restarted)] { # # ---- Start the periodic update of the file listboxes: # dsk_progress "Starting background procs..." after [expr $tkdesk(update,file_lists) * 1000] dsk_update_flbs # ... and the periodic config-save: if !$tkdesk(xmaster) { after [expr $tkdesk(update,config) * 60000] dsk_persave_config } } dsk_progress "Ready." if ![info exists tkdesk(restarted)] { dsk_sound dsk_welcome_end } dsk_welcome hide dsk_lazy if [info exists tkdesk(first_time_user)] { dsk_help quick } if [info exists tkdesk(after_startup)] { set tkdesk(error_source) "System" eval $tkdesk(after_startup) set tkdesk(error_source) "" } # # ==== Misc. Procs ============================================================ # # # ----------------------------------------------------------------------------- # # Proc: dsk_exit # Args: ask (opt.) set to 1 if it should ask by all means # Returns: "" # Desc: Exits TkDesk. # Side-FX: May lead to the termination of tkdesk. # proc dsk_exit {{ask 0}} { global tkdesk if {($tkdesk(ask_on_exit) && !$tkdesk(in_development)) || $ask} { if !$tkdesk(xmaster) { set msg "Are you sure you want to exit TkDesk?" } else { set msg "This will end your X Session! Is this o.k.?" } if {[cb_yesno $msg] != 0} { return } } foreach obj [itcl_info objects -class dsk_Editor] { if [winfo exists [$obj getToplevel]] { if {[$obj close_win] == "cancel"} { return } } } if [info exists tkdesk(at_exit)] { set tkdesk(error_source) "System" eval $tkdesk(at_exit) set tkdesk(error_source) "" } dsk_sound dsk_quit_tkdesk dsk_save_config catch {dsk_shutdown_server} catch {exec rm -rf $tkdesk(tmpdir)} exit 0 } # # ----------------------------------------------------------------------------- # # Proc: dsk_restart # Args: - # Returns: "" # Desc: Restarts TkDesk. # Side-FX: none # proc dsk_restart {} { global argv0 tkdesk dsk_save_config # clean up cb_image !reset foreach obj [itcl_info objects -class dsk_Editor] { if {[$obj close_win] == "cancel"} { return } } catch {eval itcl_unload [itcl_info classes]} foreach w [winfo children .] {destroy $w} # and now restart set tkdesk(restarted) 1 uplevel #0 "cd $tkdesk(startdir)" uplevel #0 "source $argv0" } # ----------------------------------------------------------------------------- # _dsk_server_connect: # Gets invoked when a client connects to our server port. # tkdesk-2.0/contrib/0040755000175000007640000000000010037657326012401 5ustar jccjcctkdesk-2.0/contrib/ColorSwitch.tar.gz0100644000175000007640000002463710020457430015763 0ustar jccjccaM7cAϾbw( Ž"GѠ_Ő,+Y0UC>U[]`E ё|W.v_eY<&URNc.d]U9c1އk6N&BVIeIf5{JߓK"L+1'''ьG3_4ϻq>ٸ"r| X,TQ䲚[U~QRƩLB<+f8ƒm#tJUI*/J&Y`Dy$|[qBg/4: lbUX?6*2Y)3N.٣|S,SjZs3^< YMYk6ѣAP1$SK2P"V(|.TWGkRU`.֫mui@7^Jih\KjOe6QbDzg=P`W짹i]h+4[r/QPۗG^JSU˻/6~wiqBԭniO)o)*>)Y-E!רw G*Y- DkRml]RsnSY&1;lRliae^([߾~˒u.u|A׻Biya~?xuqv6R)i*ouΦ*!t:c)Ю=tk@8@_*X΅..A&&5 2+Uٲ(Q8|aذfÀh8:G޾ٓH[eO@wx} {x<>6rnZ*8ps_{Xj{ wzզC&ic]6`3hWs*1r9p *5_}lW"Uw?\ D%wI|K&]wL~)—uE=.Zwas-JKW@f1:W;H{YL`7w˪ƒ㸖z<3; DmSuuwɼB w۶~n$;WB)35ckEm+3 V\jV,"Vl׶9plEzQ/2NUe/_8pPilNO=y&orsUČF` པJJ:Yc@Hk㸫!}݁swVC"Qڇ^f +[.)@? f¿DOѾ⠒+<&T͞| eB-R+:ەCW8?y(&uWp7 HaJ pTev5Lzz$s5Jn~}.?Y sr9ˣS1bZ)P f@."-p>= $ÿ%޵My \0 [ ȱͳ*Nz:IZf m~q1i^xV8~g}[k!⊙0{-$͇2vz=̒ҊdNcoݨ V͵tۡ C\vk5Xaƺ-H a F%U"S2Q`EB+:>A2MU{0@\@ўkO]טdWd:m=.P6M mc:-x丩d 3Kr[INg >k~TςOqcG03*ha-0mpv6ףhȻ1:H$DkCni׆G9_V%89f\McKüuQ{LrNycs+;U]Y6Hy\VY BpDp) 7:0ܞ댹I7&v~@F5NjzSU#ܲQ+^Ljn l, r%Vv}ݏf6$)pw4iSSc4@僧[4-9_Ppm)OW5O$!>)5q8Ⱦ1y6;~FF"]ʹA6]gDǹi5jf'3N.4cvw.臩yr.lkޑO_k,\Q_ب,*Y u5:{c([kver{Inu.ܤLH0w62lԉZ]$|:ۛyzݴuI`gaA-0mO~Æc9 Y}G]G8ژ ޑXTe?[{64ȳ;wƒ[eSOSfG'ӘZZ?l3Hh_4GJAK$~A(_R$TG&}1mM59-)xEiP#>iS5J~]U0`Q +BPQcC$%g nJaȔ8M~⡥Fy Cq}k䠚lX䫌 AEDP^z#q F39C'B܁{\ W,=Xxp5C߻!@ȁ=|ir* H^dcZI:Pd!a-3}*5;LE [""OG*+`(D P*!%z atVI IshQ'P.Ё܃G\w  PI'UѽB1;!*B4 ў^.Q>=Wj6f,lM:G2' <0쒅#e2[Vp|E9{1ˊL@H_{Jh퟉;o&:C}*5Wj#}뱾P/әzsΩ?'(p"'"" DEATQDEATQJDT"# QJDT"DDGTzDGTzfFJJJJJ>Q>Q#rDTQ9"*Gf}Q9"*GDe@TDe@TDe@TDe` ʀcrLT1Q9&*Dh Q9&*'D儨rBTN Q9!*'FjC2zy]A% _f7k~+SX;P80r p`p E0 A]YUk3}DH#މ9x86z53z2>a7%QQ @|.Ѡ.))#SB3k3@܁{Bug0ڸk @΀^U ?MunyS7?s]@u>V.귁O#[c@yA/QEt??gU¡}'N`6|>~qZai2^?/,' ӿr70PU+<_F&URS ?^IUA--is>;L7w9$N^qݾw`AU^ob.t 3.;zv}Ụౘ؛Z8'Z VOoq|p{d>fP{#ivOt=,N8$}Eߒկ7;zy%{c_Sp*Y_:)rgֺgw\ %Сyj;)b ӆWcD!ʻܦ`į';݋Sn3U<18PAL}=-0+W>$yDbo1Y\˒!ͧeŶ| =3Ic n|%h^{]=85 &CتeQ0AS'qe' i3?DH+۞\[3 qZЛо t(PE'ٹAq|iY?yثͲɳI@6 cg SR]}T_p G/NmIƨѣP(IR5-L")'`,γlB3:Qi"cXg, "rqo 6;2ə=u4#=9!N߲Ka.t-Qt @[2PR}uytSdoNfu*-b1CS u/  Ț)xOKd&.:WYv^8SUSJA'Q[B0\ɜ"ǵLqxSiYaSbMh1k{ܕ敺O\r௅?VA"rn0qVU]`깢`Fw`cVEG%c c+f3Mfˮx~X)\/)v"UjI+S?$yP (0|5!i : 8&H N=نP+f5 'D\}3C@,*/̦q!Ǎ|q]Z^QlTSbϏh;1rv".3ɧN8@fDK*89je9]\+?{\h@/zd5CQ?[IvINW?#YWR dzyyWGN顕=ө\Fl%TS$68PəTjvï4-X¨::o Fѣzfֳ v)[mgG'%W3v _cI >uiⓄ9kd,"05tщ 4h(J,,E|{[۴?Za#Xv_}=Hl܏T%$G9KW1)@G?]IT2+at};1xD"HB /,K~oA։󤀉0CWYx(#1v[4~ z xpp @pj.¡YyDmƗ\w$TrUk L)׎ъqsLt`M47qe򪹮:U#y4a2Buաlɍp%bMO%NU*.b?(=N>hY.Tf Q~4'4$$Y=X׹8RQ4$篎+2UA<-65Lf6DJURQ< !{C8ɝ<)_g@ tQԵi,<22,v&p*hkOsڦNr̮uKPFaUO͆$s'S:@ 3qi! 39O#nA+a:[G:I TŹkr4O/1U  h7  昮fW06 k\c$+}^=Gl@IGꡙ u"4 ( JB8snbNkh׵h1 સSGd&޺$d_Z7l5R`;Z#P.-H9| v *%Ȁb>m(\#cBL `ko>$S Ys^m"`q_&jeLPm/CFUpWpz}NnRcAޒ\7&[pIm/=qoY/.YIټjS#'u9Jxf'MY@oKr>z֊pY8{Ce\I7a~E2+mg=]ksܟh[5Pv]>~tojaomσlIii|zG%k?loѾҡΗ AUWl)?gU.e7vt:bO67C.5=v tv)?km_r.{ԝw"3|caP^_-+Wǻ"e U֠7]ڏwDpk82`}.,qzs֬@ 0"w]v;eɟA\T)]G%^UM45PEO 5M4PEG>4xPjߦ\swuߥx"`97"rNM4Y^|T:pK4(yPF{{{NK?187zٿNxJ9lǒT TCXOk\.~0w`f6oo]i`× U1B>{ [iMH|mk[ֶmmk[ֶmmk[ֶmmk[ֶ?pEtkdesk-2.0/contrib/README0100644000175000007640000000077210036601147013251 0ustar jccjccThis directory contains patches and additions to TkDesk, and even complete stand-alone programs, to be used together with TkDesk. All of these were sent to the TkDesk mailing list. Thanks to everybody who made their code available! Please note that all of the files contained in this directory are not supported by the TkDesk author or by the TkDesk maintainers, but by the contribution authors themselves (if they're supported at all). To find out what's here, please look into the individual files. tkdesk-2.0/contrib/Stickers0100644000175000007640000004564510020457430014110 0ustar jccjcc#!/usr/bin/wish # -*-Mode: Tcl;-*- # Stickers package # One of: Stickers, ToDo manager, Schedule/Calendar, Addresses book, # one global variable stk contains all the information about the stickers. # # This is a GPL Program. See GPL licenses agreement. # It comes WITHOUT any warranty of any kind, what so ever. # # Written By: Erez Strauss, # All Rights reserved, DORAN Software Technologies Ltd., (C) 1997. # mailto: Erez Strauss # http://www.newplaces.com/~erez/stickers/ # # ToDo: # Move all text messages to private variables, enable multiple languages. # Merge with taskspace code (as improved todo management tool). # More Help text. # .lsm / README file, .rpm package., .tar.gz file, Web site. # Printing, Keywords/Subjects. # Import/Export file in few formats: Text, HTML, Postscript. # Tile/Cascade/One locations,same size # Setting the location of new window near the last active window, or # settable by the user. # Code comments. # Stickers mail to. # Handling Sticky windows geometry, and virtual desktops like fvwmPager. # Stickers Alarm, date time. # User defined fields. # # Done: # July 11 1997 - v0.6 - Stickers List - Sort (Number, Dates, Color). # July 11 1997 - v0.6 - Stickers List - Search stickers text for regular expresion. # # # Note: All the $stk(*) variables (not including $stk(-*)) are saved # in the users private ~/.stickers file. # So changes from within this file will be overwritten by those in # the private file. # # program starts at stk_start, last function in this file. # Function: stk_save # Purpose: save the stk(*) values into the user stickers file. # Note: all the stk(-*) names are NOT saved. proc stk_save {} { global stk if [file exists $stk(file)] { file copy -force $stk(file) $stk(file)~ } set fd [open $stk(file) w] puts $fd {# Stickers file.} foreach e [lsort [array names stk]] { # For those variable that are private to the program, # and are not to be saved in the private user file. if {[string index $e 0] == "-"} continue puts $fd "set stk($e) [list $stk($e)]" } puts $fd {# End of the tickers file.} close $fd } # Function: stk_read # Purpose: read user stickers file # Note: overwrites the program defaults values. proc stk_read {} { global stk if [file exists $stk(file)] { source $stk(file) } } # Function: # Purpose: return list of stickers IDs proc stk_ids {} { global stk set ids {} foreach n [array names stk *,text] { lappend ids [string range $n 0 [expr [string length $n] - 6]] } return [lsort -integer $ids] } # Function: stk_id_next # Purpose: find the next id for a new sticker id. proc stk_id_next {} { global stk set id [lindex [stk_ids] end] if {$id == {}} {return 1} return [expr $id + 1] } # Function: stk_ids_sort # Purpose: Return a list of ids proc stk_ids_sort {} { global stk set ids [stk_ids] return [lsort -command "stk_cmp $stk(sort,key)" $ids] } proc stk_cmp {field a b} { global stk if {$field == "id"} {return [expr $a - $b]} if {$field == "size"} {return [expr [string length $stk($a,text)] - [string length $stk($b,text)]]} if {$field == "mdate" || $field == "cdate"} {return [expr $stk($b,$field) - $stk($a,$field)]} return [expr $stk($a,$field) - $stk($b,$field)] } # Function: stk_win # Purpose: Create the sticker window for that id. proc stk_win {id} { global stk set w .stk-$id if {[winfo exists $w]} { if $stk($id,mapped) { wm deiconify $w raise $w } { destroy $w } return } if !$stk($id,mapped) return toplevel $w if {$stk($id,title) == {}} { regsub "\n.*$" $stk($id,text) {} t wm title $w "Sticker $id - $t." } { wm title $w $stk($id,title) } wm geometry $w $stk($id,geometry) frame $w.bar -relief raised -borderwidth 2 pack $w.bar -fill x frame $w.data pack $w.data -expand on -fill both # ------------- menubutton $w.bar.file -text File -menu $w.bar.file.m -padx 1 -pady 1 pack $w.bar.file -side left menubutton $w.bar.help -text Help -menu $w.bar.help.m -padx 1 -pady 1 pack $w.bar.help -side right button $w.bar.new -text New -padx 1 -pady 1 -relief flat -bd 0 \ -command stk_win_new pack $w.bar.new -side left button $w.bar.list -text List -relief flat -bd 0 -padx 1 -pady 1 \ -command "stk_list_win" pack $w.bar.list -side left menubutton $w.bar.color -text Color -menu $w.bar.color.m -padx 1 -pady 1 pack $w.bar.color -side left # ------------- menu $w.bar.file.m $w.bar.file.m add command -label New \ -command stk_win_new $w.bar.file.m add command -label Minimize -command "wm iconify $w" $w.bar.file.m add command -label Delete -command "stk_win_delete $id" $w.bar.file.m add command -label Save -command {stk_sync;stk_save} $w.bar.file.m add command -label "Show List" -command stk_list_win $w.bar.file.m add command -label "Set color" -command \ "tk_popup $w.bar.color.m \[winfo pointerx $w] \[winfo pointery $w]" $w.bar.file.m add command -label Quit -command {stk_quit} $w.bar.file.m add command -label Exit -command {stk_sync;stk_save;exit 0} # ------------- menu $w.bar.color.m foreach c $stk(bg,colors) { $w.bar.color.m add command -label $c -command \ "$w.data.text configure -bg \[set stk($id,bg) $c]" } $w.bar.color.m add command -label "Other ..." -command \ "set t \[tk_chooseColor -initialcolor \$stk($id,bg) -title {Background color}] ; if {\$t != {}} {$w.data.text configure -bg \[set stk($id,bg) \$t]}" # ------------- stk_help_menu $w.bar.help.m # ------------- scrollbar $w.data.scrl -command "$w.data.text yview " -width 10 pack $w.data.scrl -side right -fill y # ------------- text $w.data.text -bg $stk($id,bg) -wrap word -yscrollcommand "$w.data.scrl set " $w.data.text insert end $stk($id,text) pack $w.data.text -expand on -fill both # wm geometry $w $stk($id,geometry) } # reset the given window id attributes to default values. proc stk_win_reset {id} { global stk foreach f $stk(fields) { if {![info exists stk($id,$f)]} { set stk($id,$f) $stk($f,default) } } } # Create New window. proc stk_win_new {} { global stk set id [stk_id_next] stk_win_reset $id set stk($id,cdate) [clock seconds] stk_win $id } # Delete one window with the given id. proc stk_win_delete {id} { global stk if {[tk_dialog .rus "Delete onfirmation" \ "Are you sure you want to remove Sticker $id ?" \ question 0 "Yes, delete" Cancel] == 0} { foreach f [array names stk $id,*] { unset stk($f) } destroy .stk-$id } } # create all the windows proc stk_wins {} { global stk foreach id [stk_ids] { stk_win_reset $id stk_win $id } if $stk(list,mapped) stk_list_win } # stk_all_show - Show all the stickers windows. proc stk_all_show {state} { global stk stk_sync foreach id [stk_ids] { set stk($id,mapped) $state stk_win $id } set stk(list,mapped) 1 stk_list_win } # Synchronize the information in the stk array with widgets information. proc stk_sync {} { global stk foreach id [stk_ids] { stk_sync_id $id } if [winfo exists .stk-list] { set stk(list,geometry) [wm geometry .stk-list] set stk(list,mapped) [winfo ismapped .stk-list] } { set stk(list,mapped) 0 } } # Function: stk_sync_id # Purpose: copy all information from the id associated widget to the stk array elements. proc stk_sync_id {id} { global stk if {![winfo exists .stk-$id]} { set stk($id,mapped) 0 return } regsub "\n*$" [.stk-$id.data.text get 1.0 end] "\n" newvalue if {[string compare $stk($id,text) $newvalue]} { set stk($id,mdate) [clock seconds] } set stk($id,text) $newvalue if [winfo ismapped .stk-$id] { set stk($id,geometry) [wm geometry .stk-$id] } set stk($id,mapped) [winfo ismapped .stk-$id] } # Create The windows list window. proc stk_list_win {} { global stk stk_sync set w .stk-list if [winfo exists $w] { wm deiconify $w raise $w $w.data.list delete 0 end } { toplevel $w wm title $w "Stickers List" wm geometry $w $stk(list,geometry) # ------------ frame $w.btns button $w.btns.show -text "Show All" -command {stk_all_show 1} button $w.btns.hide -text "Hide All" -command {stk_all_show 0} pack $w.btns.show -side left -expand 1 pack $w.btns.hide -side right -expand 1 pack $w.btns -side bottom -fill x # ------------ frame $w.bar -relief raised -borderwidth 2 pack $w.bar -fill x # menubuttons File & Help. menubutton $w.bar.file -text File -menu $w.bar.file.m -padx 1 -pady 1 pack $w.bar.file -side left # ------------ menubutton $w.bar.sort -text Sort -menu $w.bar.sort.m -padx 1 -pady 1 pack $w.bar.sort -side left menubutton $w.bar.help -text Help -menu $w.bar.help.m -padx 1 -pady 1 pack $w.bar.help -side right stk_help_menu $w.bar.help.m button $w.bar.new -text New -padx 1 -pady 1 -relief flat -bd 0 \ -command stk_win_new pack $w.bar.new -side left menu $w.bar.file.m $w.bar.file.m add command -label New -command stk_win_new $w.bar.file.m add command -label Minimize -command "wm iconify $w" $w.bar.file.m add command -label Save -command {stk_sync;stk_save} $w.bar.file.m add command -label "Refresh List" -command stk_list_win $w.bar.file.m add command -label Quit -command {stk_quit} $w.bar.file.m add command -label Exit -command {stk_sync;stk_save;exit 0} # ------------ menu $w.bar.sort.m $w.bar.sort.m add command -label "Sticker ID" \ -command {set stk(sort,key) id ; stk_list_win} $w.bar.sort.m add command -label "Modify Date" \ -command {set stk(sort,key) mdate ; stk_list_win} $w.bar.sort.m add command -label "Create Date" \ -command {set stk(sort,key) cdate ; stk_list_win} # $w.bar.sort.m add command -label "Alarm Date" \ -command {set stk(sort,key) alarm ; stk_list_win} # $w.bar.sort.m add command -label "Color" \ -command {set stk(sort,key) color ; stk_list_win} $w.bar.sort.m add command -label "Size" \ -command {set stk(sort,key) size ; stk_list_win} # ------------ frame $w.filter -relief groove -bd 2 pack $w.filter -side bottom -fill x button $w.filter.all -text All -padx 1 -pady 1 \ -command {set stk(list,filter) "" ; stk_list_win} button $w.filter.filter -text Filter -padx 1 -pady 1 -command {stk_list_win} entry $w.filter.pattern -textvariable stk(list,filter) bind $w.filter.pattern "$w.filter.filter invoke" pack $w.filter.all $w.filter.filter -side left pack $w.filter.pattern -side right -fill x # ------------ frame $w.data scrollbar $w.data.scrl -command "$w.data.list yview " -width 10 pack $w.data.scrl -side right -fill y pack $w.data -fill both -expand 1 listbox $w.data.list -bg $stk(list,bg) -takefocus on \ -yscrollcommand "$w.data.scrl set " bind $w.data.list {+ regsub {:.*$} [%W get [%W nearest %y]] {} id if [info exists stk($id,mapped)] { set stk($id,mapped) 1 stk_win $id } { stk_list_win } } bind $w.data.list {+ regsub {:.*$} [%W get [%W nearest %y]] {} id stk_sync_id $id if [info exists stk($id,mapped)] { set stk($id,mapped) 0 stk_win $id } { stk_list_win } } pack $w.data.list -fill both -expand on } foreach id [stk_ids_sort] { if {[string compare $stk(list,filter) ""] && ![stk_contains $id $stk(list,filter)]} continue regsub "\n.*$" $stk($id,text) {} t set t [string range $t 0 39] ;# up to 40 characters in the title. $w.data.list insert end "$id: $stk($id,title) - $t" } focus $w.data.list } # Function: stk_defaults # Purpose: set the default values for many variables. # set stk(fields) {x y title keywords text} # set stk(file) "[glob ~]/.stickers" # stk(,title) # stk(,keywords) # stk(,subject) # stk(,text) # stk(,win_state) {open close} # stk(,status) # stk(,bg) # stk(,mdate) - modify date # stk(,cdate) - create date # stk(,adate) - alarm date proc stk_defaults {} { global stk wm withdraw . # init few internal variables. set stk(-version) 0.6 option add Button*font -Adobe-Helvetica-Bold-R-Normal--*-100-*-*-*-*-*-* set stk(fields) {title keywords text geometry bg mapped} set stk(bg,colors) {yellow white red green blue gray80 gray30 pink} set stk(file) "[glob ~]/.stickers" set stk(title,default) {} set stk(keywords,default) {} set stk(text,default) {} set stk(geometry,default) 240x140+100+50 set stk(bg,default) yellow set stk(mapped,default) 1 # set stk(mdate,default) [clock seconds] set stk(list,geometry) 150x250+100+250 set stk(list,mapped) 0 set stk(list,bg) yellow set stk(list,filter) "" set stk(sort,key) id # Some global keyboard bindings. bind all {stk_quit} bind all {stk_sync;stk_save;exit 0} bind all {stk_sync;stk_save} bind all {stk_list_win} bind all {puts stderr "Next sticker not yet implemented"} bind all {puts stderr "Prev sticker not yet implemented"} bind all {puts stderr "Help window not yet implemented"} bind all {focus %W} } # Draw the startup window, with all the information about this software proc stk_start_msg {state} { global stk set w .start-msg catch {destroy $w} if $state { toplevel $w label $w.msg -padx 10 -pady 10 -bg yellow -bd 5 -relief ridge -text \ "Stickers Version $stk(-version)\n\n By Erez Strauss\n\n DORAN Software Technologies Ltd. erez@newplaces.com\n http://www.newplaces.com\n Free software\n" pack $w.msg -padx 4 -pady 4 bind $w.msg "catch {destroy $w}" bind $w.msg "catch {destroy $w}" stk_toplevel_center $w update } { after 400 catch {destroy $w} } } proc stk_help {type} { set text { The Stickers Program. Unfortunatly there is no help for this subject. for comments e-mail: Erez Strauss } switch $type { 0 { set text { The Stickers program. Thank you for using the Stickers program. This program provide you with a simple to use notes/stickers management utility. You can use stickers with different colors and keep them in different location on the screen for different subject. Each sticker remembers it's location on the screen from one session to the other. The program provides you with a list of the stickers, where each sticker has an id and the first text line as title. The program is using Tcl/Tk as User Interface environment. The program should execute on any environment that can run Tcl/Tk, for example: Linux, other Unix system, Win?? and Mac. The software was developed and tested on Linux system. Please send me an e-mail in case you are using it on other platform, my e-mail address is: Erez Strauss } } 1 { set text { The Stickers Hot Keys. Thank you for using the Stickers program. - Save the ~/.stickers file. - Save & Exit - Popup the stickers list window. - Help window - Quit } } 2 { set text { The Stickers Users Guide Thank you for using the Stickers program. The stickers program is useful tool to manage sticker/note on your computer. You can create with it many stickers, each one of the stickers keeps it location on the screen from one run to the other. The stickers can have any color you wand and they are saved in the ~/.stickers file for each user. This file contains all the information the program needs for the next run. Each sticker window contains two parts: 1. The Menubar were you can find: File menu, New Button, List Button and Help Menu. 2. The text part with scrollbar were you can enter your text for that specific sticker. you can also use the scrollbar to move in the text. The File menu provide you with few sticker operations and few global actions. ... To Be Continued. please send comments and updated to: Erez Strauss } } 3 { set text { The Stickers List Window. This window shows a list of the stickers with their titles. You can press the first mouse button on the list item to display its sticker. The third mouse button is used to hide the specific sticker. The 'Shaw All' and the 'Hide All' act on all the stickers in the system. You can hide the, and show them all in one button press. The Stickers filtering section of the window enables you to search for those stickers that contain the given string/regular-expression. You can set the string in the entry on the right side and activate it by pressing the Filter button. } } } if {![winfo exists .help]} { toplevel .help wm title .help "Stickers Help" frame .help.btns -relief raised -bd 1 pack .help.btns -side bottom -fill both frame .help.bar -relief raised -bd 2 pack .help.bar -side top -fill both frame .help.data -relief raised -bd 1 pack .help.data -side top -fill both -expand 1 scrollbar .help.data.scrl -command ".help.data.text yview " -width 10 pack .help.data.scrl -side right -fill y text .help.data.text -wrap word -yscrollcommand ".help.data.scrl set " -width 60 -height 20 pack .help.data.text -expand 1 -fill both menubutton .help.bar.help -text Help -menu .help.bar.help.m -padx 1 -pady 1 pack .help.bar.help -side right stk_help_menu .help.bar.help.m button .help.btns.ok -text OK -command {destroy .help} pack .help.btns.ok -side left -expand 1 -padx 3m -pady 2m stk_toplevel_center .help } .help.data.text configure -state normal .help.data.text delete 1.0 end .help.data.text insert end $text .help.data.text configure -state disabled raise .help } # Create help menu with a specific widget name. proc stk_help_menu {w} { menu $w $w add command -label "Startup Window" -command {stk_start_msg 1} $w add command -label "About Stickers" -command {stk_help 0} $w add command -label "Hot Keys " -command {stk_help 1} $w add command -label "Users Guide " -command {stk_help 2} $w add command -label "Sticker List " -command {stk_help 3} } # Function: stk_quit # Purpose: Confirm the quit operation. proc stk_quit {} { if {![tk_dialog .quit "Quit Confirmation" "Are you sure you want to end this Stickers session WITHOUT saving the stickers" question 1 "Yes, really Quit" Cancel]} { exit 0 } } # Function: stk_toplevel_center # Purpose: Set window to the center of the window. proc stk_toplevel_center {w} { wm withdraw $w update idletasks set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \ - [winfo vrootx [winfo parent $w]]] set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \ - [winfo vrooty [winfo parent $w]]] wm geom $w +$x+$y wm deiconify $w } # Sticker check regexp proc stk_contains {id exp} { global stk return [regexp -nocase -- $exp $stk($id,text)] } # The stk_start - Stickers Start function, start the program. proc stk_start {} { stk_defaults stk_start_msg 1 stk_read if {[stk_ids] == {}} stk_win_new stk_wins update stk_start_msg 0 } stk_start # End of the sticker program. tkdesk-2.0/contrib/afterstep-drop0100644000175000007640000000444610020457430015252 0ustar jccjcc#!/bin/sh #-*- tcl -*- \ exec /usr/bin/tkdesksh "$0" "$@" # drag and drop file handler for Afterstep Wharf and TkDesk # requires 3 arguments: bg color, pixmap for wharf # and a command to execute on drop. any "%s" in the command will # be converted to the dropped file name. # sample afterstep system.steprc wharf configuration: # *Wharf Vi nil Swallow "dropV" ../bin/dropV "#8e8a9e" ../pixmaps/text.xpm "xterm -T \"Vi %s\" -e vi %s" & # *Wharf Vi nil Exec "" xterm -title Vi -e vi & # note that multiple uses of this script in the wharf require multiple names for it # because the Swallow syntax requires the 5th and 6th args refer to the same name. # Therefore ../bin/dropV above is a symlink to this script. # sources for this program were /usr/bin/tkdesk and comments in # /usr/lib/tkdesk/dd-file.tcl. also see "man 3blt dragdrop" if { $argc > 0 && $argc != 3 } { puts "Requires 3 arguments: bg color, pixmap and command to execute on drop" exit 1 } tk_focusFollowsMouse wm title . Drop wm geometry . 48x48-1-1 if { $argc > 0 } { set bgcolor [lindex $argv 0] set argv [lreplace $argv 0 0] set pixmap [lindex $argv 0] set argv [lreplace $argv 0 0] canvas .c -width 48 -height 48 -bg $bgcolor -highlightthickness 0 image create pixmap im -file $pixmap .c create image 0 0 -anchor nw -image im pack .c } else { set color gray96 label .win -bd 1 -padx 3 -pady 3 -relief groove -bg $color -text " " pack .win } # "Uglify" ;-) namespaced BLT: if {[info command bgexec] != ""} { rename bgexec blt_bgexec } if {[info command busy] != ""} { rename busy blt_busy } if {[info command drag&drop] != ""} { # this must be handled differently due to internal bindings: proc blt_drag&drop {args} { eval drag&drop $args } } else { puts "Error: no drag and drop" exit 1 } proc drop_handler {} { global DragDrop argc argv if { $argc > 0 } { set str [join [concat $argv] " "] regsub -all %s $str $DragDrop(file) str2 exec sh -c "$str2" & } else { .win config -text $DragDrop(file) } update idletasks } blt_drag&drop target . handler file "drop_handler" tkwait window . tkdesk-2.0/contrib/appbar-dialup.tcl.xisp0100644000175000007640000002221010036600735016571 0ustar jccjcc # # appbar-dialup.tcl # # Author: J. Chris Coppick, 1998 # [This contains lots of Christian Bolik's code. # The route checking code was stolen from the PPPui # script by Nathan Meyers.] # # Description: # # This is a drop-in replacement for the TkDesk appbar-dialup.tcl # library module. This version is designed to work "well" with # an external dialup-control program, specifically Xisp, and the # GNU-Linux OS. (The Xisp program can be found at # "http://users.hol.gr/~dbouras/".) This version is also a little # more reality bound, in that it actually checks the availability # of the dialup-network route and uses that to adjust its status, # i.e. the picture on the button and the connection-time display. # Also, this version updates the connection-time in increments of # 5 seconds instead of every second in hopes of saving cycles, or # you can turn off the time display completely since Xisp has its own. # # This may work with other programs besides Xisp, provided they # support using SIGUSR1 and SIGUSR2 to control the dialup link # status. # # Installation: # # In the TkDesk library directory (/usr/local/lib/TkDesk for example) # make a copy of the original appbar-dialup.tcl file, in case you end # up hating this one... # # % cp appbar-dialup.tcl appbar-dialup.tcl.orig # # Then copy the new version over the old... # # % cp appbar-dialup.tcl.jcc appbar-dialup.tcl # # In your Appbar configuration file, use the button type 'special:dialup' # just as before. However, there are some new configuration variables # you may need to set. (See below) The old cmd_up and cmd_down # variables are not used in this version, but it won't hurt to leave # them. The dialup-button image variables *are* still used. # # When you got everything configured, quit TkDesk and restart it. # Note that you cannot just reload the Appbar. The new appbar-dialup.tcl # module needs to be loaded as well, so TkDesk must be restarted. # # Usage: # # The Appbar button type 'special:dialup' creates a button that displays # the current status of the dialup link. The dsk_dialup procedure can # be used with the button to toggle the link on and off # when the button is pushed. Left-clicking on the button pulls down # a normal Appbar menu. This version is designed to launch Xisp as # necessary and signal it to bring the link up and down. # # If the button is pushed when the link is down, then the Xisp program # is launched PROVIDED IT IS NOT ALREADY RUNNING. Then Xisp is sent # a signal that tells it to bring the link up. (Personally, I use # autoStart with Xisp, but that doesn't make any difference.) # # If the button is pushed when the link is up, then Xisp is # sent a link telling it to bring the link down. Note that the # running Xisp program is NOT killed. You have to do that yourself # using the program's Quit button, or whatever. If the program is # iconified, it shouldn't be bothering you anyway and it'll be there # already the next time you need to dialup... IMHO. :-) # # This version monitors the link status independently of button pushes, # so the button image will change when the link status changes, and # not when the button is pushed. # # Variables: # # You can use the following variables in your Appbar file to override # the defaults. # # tkdesk(appbar,dialup,program) - The external program used to control # dialup networking. The default for this is 'xisp -iconic'. # Note that the tkdesk(appbar,dialup,cmd_*) variables are NOT used. # # tkdesk(appbar,dialup,device) - The network device used for dialup. # The default is 'ppp0'. # # tkdesk(appbar,dialup,timer) - A boolean controlling the connection-time # display. Set this to 0 to remove the time display from the dialup # button. The default is 1. # # Bugs: # # This uses the 'pidof' (i.e. 'killall') program to find process pids. # If you have more than one Xisp (or whatever) running on your system, # you will likely lose. I had a better algorithm, but... um, my dog # ate it. Yeah, that's the ticket. My dog ate it right before the # aliens dognapped him. (By the way, on my RedHat distribution, # kill is in /bin, killall is in /usr/bin/, and pidof is in /sbin. # Now I'm a dyed-in-the-wool unix bigot, but still... am I the only # one who thinks there's something wrong with that picture?) # # I did some quick testing and it seems to do the trick. Any problems # you find are more than likely due to some Tcl/Tk bug (heh heh heh...) # ######## # Variable defaults... if [catch {set tkdesk(appbar,dialup,program)}] { set tkdesk(appbar,dialup,program) "xisp -iconic" } if [catch {set tkdesk(appbar,dialup,device)}] { set tkdesk(appbar,dialup,device) "ppp0" } if [catch {set tkdesk(appbar,dialup,timer)}] { set tkdesk(appbar,dialup,timer) 1 } set dsk_appbar(dialup,button) "" set dsk_appbar(dialup,label) "" # special:dialup init... proc _appbar_dialup {but count} { global tkdesk dsk_appbar # set images to use: foreach img {up down} { set bitmap $tkdesk(appbar,dialup,$img) set dsk_appbar(dialup,img,$img) [dsk_image $bitmap] } # create button: set dsk_appbar(dialup,button) $but button $but -activebackground $dsk_appbar(bgcolor) \ -activeforeground $dsk_appbar(fgcolor) \ -cursor top_left_arrow \ -command $dsk_appbar(defaction) \ -padx 0 -pady 0 -highlightthickness 0 \ -image $dsk_appbar(dialup,img,down) set dsk_appbar(dialup,count) $count dsk_dialup_init_timer catch {after cancel $dsk_appbar(dialup,after)} dsk_dialup_update } # User hook to activate/deactivate the dialup link... proc dsk_dialup {} { global tkdesk dsk_appbar set cmd $tkdesk(appbar,dialup,program) if {$cmd == ""} { dsk_errbell cb_error "Please first define tkdesk(appbar,dialup,program) in the AppBar config file." return } if {$dsk_appbar(dialup,state) == "down"} { if [set pid [dsk_dialup_running $cmd]] { dsk_dialup_signal $pid "SIGUSR1" } else { dsk_dialup_exec $cmd # Try twice to find pid of dialing program... after 1000 set pid [dsk_dialup_running $cmd] if {$pid == 0} { after 1000 set pid [dsk_dialup_running $cmd] } if {$pid == 0} { cb_error "$cmd doesn't seem to have started..." return } dsk_dialup_signal $pid "SIGUSR1" } } else { if [set pid [dsk_dialup_running $cmd]] { dsk_dialup_signal $pid "SIGUSR2" } } } # Periodically check the link status... proc dsk_dialup_update {} { global dsk_appbar tkdesk if [dsk_dialup_route_up $tkdesk(appbar,dialup,device)] { dsk_debug "network route is up" set dsk_appbar(dialup,state) up $dsk_appbar(dialup,button) config -image $dsk_appbar(dialup,img,up) if ![winfo exists $dsk_appbar(dialup,label)] { dsk_dialup_init_timer } dsk_dialup_inc_timer } else { dsk_debug "network route is down" set dsk_appbar(dialup,state) down $dsk_appbar(dialup,button) config -image $dsk_appbar(dialup,img,down) dsk_dialup_kill_timer } set dsk_appbar(dialup,after) [after 5000 dsk_dialup_update] } # Create and initialize the connection-time display... proc dsk_dialup_init_timer {} { global dsk_appbar tkdesk if {$tkdesk(appbar,dialup,timer)} { set but $dsk_appbar(dialup,button) set lab $but.l set dsk_appbar(dialup,label) $lab label $lab -text "00:00" -font fixed -cursor top_left_arrow bind $lab "$but config -relief sunken" bind $lab "$but invoke; $but config -relief raised" bind $lab <3> "_appbar_show_menu $dsk_appbar(dialup,count) %X %Y" bind $lab "break" bindtags $lab "appbar $lab all" set x 0 set y [expr [winfo reqheight $but] - [winfo reqheight $lab]] place $lab -x $x -y $y set dsk_appbar(dialup,secsup) 0 } } # Remove the connection-time display... proc dsk_dialup_kill_timer {} { global dsk_appbar if [winfo exists $dsk_appbar(dialup,label)] { dsk_debug "removing label" destroy $dsk_appbar(dialup,label) } } # Update the connection-time display... proc dsk_dialup_inc_timer {} { global dsk_appbar if [winfo exists $dsk_appbar(dialup,label)] { incr dsk_appbar(dialup,secsup) 5 set s [expr $dsk_appbar(dialup,secsup) % 60] set m [expr $dsk_appbar(dialup,secsup) / 60] $dsk_appbar(dialup,label) config -text [format "%02d:%02d" $m $s] } } # Exec an external program... Probably a lot of bugs in this one... :-) proc dsk_dialup_exec {cmd} { catch {eval dsk_exec $cmd} } # Find the pid of a given command... proc dsk_dialup_running {cmd} { if [catch {set pid [exec /sbin/pidof -s [lindex [split $cmd] 0]]}] { set pid 0 } return $pid } # Send a given signal to a given pid... proc dsk_dialup_signal {pid sig} { catch {exec /bin/kill -s $sig $pid} } # Check for a route to a given device. Returns 1 if the route exists, else 0. proc dsk_dialup_route_up {device} { set routes [ open /proc/net/route r ] gets $routes set done 0 while { [ gets $routes line ] >= 0 && !$done } { dsk_debug "looking at route: $line" if { [ string first $device $line ] == 0 } { set done 1 } } close $routes return $done } tkdesk-2.0/contrib/exmh0100644000175000007640000001607010020457430013250 0ustar jccjccFrom tkdesk-owner@mrj.com Thu Oct 16 09:57:29 1997 Return-Path: Received: from mail.wiesbaden.netsurf.de (bolik@localhost [127.0.0.1]) by deepthought.mainz.netsurf.de (8.8.5/8.8.5) with ESMTP id JAA00303 for ; Thu, 16 Oct 1997 09:57:27 +0200 Received: from turtle.mrj.com by wiesbaden.netsurf.de with smtp (Smail3.1.28.1 #10) id m0xHGBw-001ljDa; Fri, 3 Oct 97 23:27 MET Received: (from majordomo@localhost) by turtle.mrj.com (8.6.10/8.6.10) id QAA26474 for tkdesk-outgoing; Fri, 3 Oct 1997 16:30:57 -0400 Received: from frodo.tucc.uab.edu (frodo.tucc.uab.edu [138.26.25.10]) by turtle.mrj.com (8.6.10/8.6.10) with ESMTP id QAA26466 for ; Fri, 3 Oct 1997 16:30:43 -0400 Received: from frodo (localhost [127.0.0.1]) by frodo.tucc.uab.edu (8.8.4/8.8.4) with ESMTP id PAA08236 for ; Fri, 3 Oct 1997 15:27:41 -0500 Message-Id: <199710032027.PAA08236@frodo.tucc.uab.edu> X-Mailer: exmh version 2.0zeta 7/24/97 To: tkdesk@mrj.com Subject: [TkDesk] useful script for users of exmh Reply-To: jdblair@uab.edu X-Url: http://frodo.tucc.uab.edu X-Pgp-Fingerprint: CA F3 47 79 62 05 50 78 6C 8A A2 FA D6 42 4B 19 X-Face: )tQhsYv#dM8;9?k0LWo:ZU!Ar~E4yZ^labAQ_OhdHl"Ij)AOPi,I+_..N*Cb27&Z>hFHp,- XPshW[ Sender: owner-tkdesk@mrj.com Precedence: bulk Content-Type: text X-Mozilla-Status: 0001 Content-Length: 5628 -----BEGIN PGP SIGNED MESSAGE----- Hello, I think any other users of TkDesk that use exmh as their mailer will find this short script really useful. (If you don't know what exmh is, you should probably check it out. You can learn more about it at http://www.sunlabs.com/~bwelch/exmh/) This script is sort of an extended variation on the jdb_raise_or_exec script I just posted. The script performs several functions. If exmh is not already running it starts exmh. If exmh is already running is deiconifies and raises exmh, which will make it visible no matter where its currently lost on your desktop. Following that step, if the X-selection is not empty the script tries to parse an e-mail address out of the selection. If it succeeds in finding an e-mail address it then tells exmh to create a new compose box addressed to the address it found. If there is more than one address in the selection, the first address will be used. I added these procedures to my Local file, then set my appbar config file to run jdb_exmh when I click the mailbox. The upshot of this is that I can select an e-mail address in a document, like a README file or a web page and click the mailbox. I immediately am presented with an exmh compose window with a blank message addressed to the address I selected. Its very convenient. Some details about the script. It is two functions. The first procedure, jdb_exmh, does the checking to see if exmh is running, and tries to parse an address out of the selection. The second procedure does the work to create a new mh message with the proper address, then tells exmh to use it. This procedure can be reused in other scripts (I pulled it from a totally different script I wrote quite a while ago). If you try it out, let me know what you think of it. Here's the script: # # fancy exmh startup script # proc jdb_exmh {} { # by John Blair, jdblair@uab.edu set not_found 1 foreach interp [winfo interps] { if {[string compare $interp exmh] == 0} { set not_found 0 } } # either start exmh or diconify and raise it if $not_found { dsk_exec exmh } else { send -async exmh "wm deiconify .; raise ." } # if the selection isn't empty parse out an address and # create a new exmh compose window if {! [catch {set string [selection get -type STRING]}]} { # this is an embarrasing kludge-- wait for exmh to start if $not_found { after 500 } # I think this regex contains all characters legal in an e-mail # address. Please contact me if I've forgotten any. regexp {[a-zA-Z0-9!%\-]+@[a-zA-Z\.\-]+} $string address if [info exists address] { jdb_email_exmh $address } } } # jdb_email_exmh needs a temporary directory to store a custom created # mh form. If you don't want it to use /tmp, set the variable # 'tkdesk(exmh,tmp)' to some other directory # # tell exmh to create a new message to a given address # proc jdb_email_exmh {address} { global jdb_email_exmh tkdesk # settings set defaultform "To:\ncc:\nSubject:\n-------" set mhlib /usr/lib/mh # get main window name set name [winfo name .] # get the location of the mh directory from exmh send exmh send $name set jdb_email_exmh(exmh,path) {$mhProfile(path)} # does a 'components' file exist? if [file exists "$jdb_email_exmh(exmh,path)/components"] { set file [open "$jdb_email_exmh(exmh,path)/components"] set jdb_email_exmh(exmh,form) [read $file] close $file # then try to load the library components file } elseif [file exists "$mhlib/components"] { set file [open "$mhlib/components"] set jdb_email_exmh(exmh,form) [read $file] close $file } else { set jdb_email_exmh(exmh,form) $defaultform } # write form to temporary file, changing To: line to proper value set pid [pid] if [info exists tkdesk(exmh,tmp)] { set tmpform "$tkdesk(exmh,tmp)/$pid.mhform" } else { set tmpform "/tmp/$pid.mhform" } set form [open $tmpform w] foreach line [split $jdb_email_exmh(exmh,form) \n] { if [regexp -nocase {^to:} $line match] { puts $form "To: $address" } else { puts $form $line } } close $form # tell exmh to open a window send exmh Msg_Compose -form $tmpform # delete the temporary form file delete $tmpform } take care, -john. ....................................................................... .. . ......John.D.Blair... mailto:jdblair@uab.edu phoneto:205.975.7123 . . http://frodo.tucc.uab.edu faxto:205.975.7129 . ..sys|net.admin.... . . the university computer center ..... ..... g.e.e.k.n.i.k...the.university.of.alabama.at.birmingham.... -----BEGIN PGP SIGNATURE----- Version: 2.6.3a Charset: noconv iQCVAwUBNDVVPQJjTpK3AXhBAQF+qwP9Hgk7zZGG+EtkbZs3uDVCQS2bEMIko3zz eQJXPysqcxjiLyKQDGTFmEXaILlmVqYWo8iKHgegM+h43e9AogyoJmjNK++QqiDE 0qVMtwUSuB9H6JWR0JvNeqrbJXqqOQ4N0N9kNuY4BzRLafLpgF7PTJrQUjQM5oYL QhhMAIdrRqI= =8tIF -----END PGP SIGNATURE----- # ---------------------------------------------------------------------- # # To post to the list, send email to 'tkdesk@mrj.com'. # # To unsubscribe from this list, send email to 'majordomo@mrj.com' with # # no subject and the following line in the body of the message: # # unsubscribe tkdesk # # ---------------------------------------------------------------------- # tkdesk-2.0/contrib/follow_me.sh0100644000175000007640000000216410037303470014704 0ustar jccjcc # # Here's some handy shell functions (works in Bash) and aliases # that 1) let TkDesk follow you around as you change dirs in an # xterm (rxvt, whatever), and B) sets the window title of the # xterm to reflect the current dir, etc. # # It also includes support for pushd (pd) and popd. # # You could source this file from your .bashrc (for example). # # Note that in order for the cd-tkdesk command to work, the TkDesk # server must be activated (from the TkDesk options pulldown menu). # # Enjoy, # # J. Chris Coppick # function _cd () { \cd "$@" Xtitle /usr/local/bin/cd-tkdesk $PWD } function _pushd () { \pushd "$@" Xtitle /usr/local/bin/cd-tkdesk $PWD } function _popd () { \popd "$@" Xtitle /usr/local/bin/cd-tkdesk $PWD } function Xtitle () { # if [ -n "$DISPLAY" ] ; then case "$TERM" in *xterm) WHOAMI=`whoami` echo -n "]2;$WHOAMI@$HOSTNAME: $PWD" ;; esac # fi } # Set XTerm title... Xtitle # # Aliases... # alias cd=_cd alias pushd=_pushd alias popd=_popd alias pd=pushd alias pd2='pushd +2' alias pd3='pushd +3' alias pd4='pushd +4' alias pd5='pushd +5' tkdesk-2.0/contrib/fvwm_menu0100644000175000007640000000572210020457430014314 0ustar jccjccFrom - Tue Sep 8 08:14:55 1998 Return-Path: Received: from mail.wiesbaden.netsurf.de (bolik@localhost [127.0.0.1]) by localhost (8.8.5/8.8.5) with ESMTP id HAA00236 for ; Mon, 7 Sep 1998 07:31:40 +0200 Received: from shaknet.clark.net by wiesbaden.netsurf.de with esmtp (Smail3.1.28.1 #10) id m0zFTGf-001lfsa; Sun, 6 Sep 98 03:05 MET DST Received: (from root@localhost) by shaknet.clark.net (8.8.7/8.8.7) id SAA05434 for tkdesk-outgoing; Sat, 5 Sep 1998 18:36:58 -0500 Message-ID: <35F1C368.65F3446@urz.uni-heidelberg.de> Date: Sun, 06 Sep 1998 01:04:08 +0200 From: Christopher Arndt Organization: Universitaet Heidelberg X-Mailer: Mozilla 4.05 [en] (X11; I; Linux 2.0.32 i586) MIME-Version: 1.0 To: mailing list TkDesk Subject: tkdesk Stuff for the contrib dir Content-Type: text/plain; charset=iso-8859-1 X-MIME-Autoconverted: from quoted-printable to 8bit by shaknet.clark.net id SAA05432 Sender: owner-tkdesk@shaknet.clark.net Precedence: bulk Reply-To: Christopher Arndt X-MIME-Autoconverted: from 8bit to quoted-printable by shaknet.clark.net id SAA05434 Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from quoted-printable to 8bit by localhost id HAA00236 X-Mozilla-Status: 8001 Christopher Arndt Majordomo 1.94.4 Hi everybody, this is how I intgrated Tkdesk into fvwm to create a Win95ish start Menu (icons are included in newer fvwm distributions, maybe with different names) ----- excerpt from ~/.fvwm95rc ------------ # Use send to communicate with Tk-based Programms AddToFunc "Send" "I" Exec echo "send -async $0 {$1};exit" | wish #Some Sub-menus for the Start menu # System Settings AddToMenu "Settings" + "&Systemsteuerung %prefs_grp16.xpm" Exec control-panel & + "&Window Manager %mini-windows.xpm%" Exec ed-tkdesk ~/.fvwm95rc & + "&Drucker %printer.xpm%" Exec kljettool & # Search Tools DestroyMenu "Search" AddToMenu "Search" \ "&Dateien/Ordner... %filesearch16.xpm%" Function Send tkdesk {dsk_find_files} # This is for the Start menu of the FvwmTaskBar DestroyMenu "StartMenu" AddToMenu "StartMenu@linux-menu.xpm@^navy^" \ "&Programme%programs.xpm%" Popup AppGroups #+ "&Dokumente%documents.xpm%" Popup DocHistory # Sorry, dont know how to do this + "&Einstellungen%settings.xpm%" Popup Settings + "&Suchen%find1.xpm%" Popup Search + "&Hilfe%help.xpm%" Exec tkinfo & + "&Ausfhren...%run.xpm%" Function Send tkdesk {dsk_ask_exec} # Could also use the TkDesk server here + "" Nop + "Bilschirmschut&z%standby.xpm%" Exec xset s activate & + "&Beenden...%shutdown.xpm%" Exec tklogout # Normally just use fvwms Quit command here ------------end------------------ BTW, it would be great to have a list of all functions of TkDesk that one may want to call from other programms! (like the function descriptions, that are scattered over the Tcl source code) -- Mfg, Chris tkdesk-2.0/contrib/itcl_fix.diff0100644000175000007640000000356510020457430015024 0ustar jccjcc*** /usr/tmp/TclTk/itcl3.0.1/itcl/generic/itcl_ensemble.c.orig Sun Jun 6 17:09:21 1999 --- /usr/tmp/TclTk/itcl3.0.1/itcl/generic/itcl_ensemble.c Mon Oct 25 22:01:41 1999 *************** *** 1626,1632 **** * another "ensemble" command. Use the current ensemble as * the parent, and find or create an ensemble part within it. */ ! ensName = TclGetStringFromObj(objv[1], (int*)NULL); if (ensData) { if (FindEnsemblePart(interp, ensData, ensName, &ensPart) != TCL_OK) { --- 1626,1632 ---- * another "ensemble" command. Use the current ensemble as * the parent, and find or create an ensemble part within it. */ ! ensName = Tcl_GetStringFromObj(objv[1], (int*)NULL); if (ensData) { if (FindEnsemblePart(interp, ensData, ensName, &ensPart) != TCL_OK) { *************** *** 1693,1698 **** --- 1693,1699 ---- } else if (objc > 3) { objPtr = Tcl_NewListObj(objc-2, objv+2); + Tcl_IncrRefCount(objPtr); /* stop Eval trashing it */ status = Tcl_EvalObj(ensInfo->parser, objPtr); Tcl_DecrRefCount(objPtr); /* we're done with the object */ } *************** *** 2103,2108 **** --- 2104,2110 ---- if (prevArgObj) { objPtr = Tcl_DuplicateObj(prevArgObj); + Tcl_IncrRefCount(objPtr); copyPtr->internalRep.twoPtrValue.ptr2 = (VOID *) objPtr; } } *************** *** 2154,2160 **** * keep the string around as if it were the command line * invocation. */ ! argObj = Tcl_NewStringObj(name, -1); /* * Free the old representation and install a new one. --- 2156,2162 ---- * keep the string around as if it were the command line * invocation. */ ! argObj = Tcl_NewStringObj(name, length); /* * Free the old representation and install a new one. tkdesk-2.0/contrib/japanize0100644000175000007640000002275610020457430014120 0ustar jccjcc tkdesk-1.0b4 Japanization Patch (Unofficial Version.05011998) This is the Japanization patch for tkdesk-1.0. (You can get the smooth desktop manager tkdesk-1.0 from the TkDesk website: http://tkdesk.sourceforge.net) If you apply this patch, you can compile tkdesk-1.0 with tcl7.6jp/tk4.2jp and you will be able to input Japanese on built-in editor of tkdesk-1.0. == TIPS == You need tkWStr.h. If there is not tkWStr.h at the directory which there are tcl.h and tk.h too (may be /usr/local/include/), you must copy tkWStr.h by hand from the directory you compiled tk4.2jp (tk4.2/generic/tkWStr.h). To apply this patch, you should do 'cd' to the *parent* directory of 'tkdesk-1.0' (It is made from tkdesk-1.0.tar.gz) and do "patch < thisFile". example: % ls -F tkdesk-1.0/ tkdesk-1.0jp-patch % patch < tkdesk-1.0jp-patch If you find any problems, please tell me about it. Please send e-mail to kawakazu@yk.rim.or.jp Kazuya Kawasaki, (Author Unknown Productions) Special Thanks to : Keisuke Fujii == COPYING == Copyright (C) 1998 Author Unknown Productions This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You can get a copy of the GNU General Public License from the website "http://www.gnu.org/copyleft/gpl.html" if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff -cr tkdesk-1.0/Makefile.in tkdesk-1.0_patched/Makefile.in *** tkdesk-1.0/Makefile.in Fri Feb 27 05:21:14 1998 --- tkdesk-1.0_patched/Makefile.in Thu Apr 30 13:46:35 1998 *************** *** 50,56 **** # To change the compiler switches, for example to change from -O # to -g, change the following line: ! CC_OPTS = -O # To change the linker switches, for example to add -s, # change the following line: --- 50,56 ---- # To change the compiler switches, for example to change from -O # to -g, change the following line: ! CC_OPTS = -O2 -DKANJI -DKINPUT2 # To change the linker switches, for example to add -s, # change the following line: diff -cr tkdesk-1.0/configure tkdesk-1.0_patched/configure *** tkdesk-1.0/configure Fri Feb 27 05:21:14 1998 --- tkdesk-1.0_patched/configure Thu Apr 30 13:46:50 1998 *************** *** 606,621 **** echo $ac_n "checking version of Tcl/Tk""... $ac_c" 1>&6 echo "configure:608: checking version of Tcl/Tk" >&5 echo "$ac_t""$TCL_VERSION/$TK_VERSION" 1>&6 ! if test "$TCL_VERSION" != "7.5" -a "$TCL_VERSION" != "7.6"; then echo ! echo TkDesk requires Tcl/Tk versions 7.5/4.1 or 7.6/4.2. echo Older versions are unlikely to work, while newer ones are not echo yet supported \(but hopefully will be soon\). exit 1 fi ! if test "$TK_VERSION" != "4.1" -a "$TK_VERSION" != "4.2"; then echo ! echo TkDesk requires Tcl/Tk versions 7.5/4.1 or 7.6/4.2. echo Older versions are unlikely to work, while newer ones are not echo yet supported \(but hopefully will be soon\). exit 1 --- 606,621 ---- echo $ac_n "checking version of Tcl/Tk""... $ac_c" 1>&6 echo "configure:608: checking version of Tcl/Tk" >&5 echo "$ac_t""$TCL_VERSION/$TK_VERSION" 1>&6 ! if test "$TCL_VERSION" != "7.5" -a "$TCL_VERSION" != "7.6" -a "$TCL_VERSION" != "7.6jp"; then echo ! echo TkDesk requires Tcl/Tk versions 7.5/4.1 or 7.6/4.2 or 7.6jp/4.2jp. echo Older versions are unlikely to work, while newer ones are not echo yet supported \(but hopefully will be soon\). exit 1 fi ! if test "$TK_VERSION" != "4.1" -a "$TK_VERSION" != "4.2" -a "$TK_VERSION" != "4.2jp"; then echo ! echo TkDesk requires Tcl/Tk versions 7.5/4.1 or 7.6/4.2 or 7.6jp/4.2jp. echo Older versions are unlikely to work, while newer ones are not echo yet supported \(but hopefully will be soon\). exit 1 diff -cr tkdesk-1.0/tcldesk/cb_tools/bindings.tcl tkdesk-1.0_patched/tcldesk/cb_tools/bindings.tcl *** tkdesk-1.0/tcldesk/cb_tools/bindings.tcl Fri Feb 27 05:21:17 1998 --- tkdesk-1.0_patched/tcldesk/cb_tools/bindings.tcl Thu Apr 30 13:50:45 1998 *************** *** 112,141 **** catch {bind Entry {tkEntryInsert %W \]}} catch {bind Entry {tkEntryInsert %W \\}} catch {bind Entry {tkEntryInsert %W @}} - catch {bind Entry {tkEntryInsert %W }} catch {bind Entry {tkEntryInsert %W |}} - catch {bind Entry {tkEntryInsert %W }} - catch {bind Entry {tkEntryInsert %W }} - catch {bind Entry {tkEntryInsert %W }} - catch {bind Entry {tkEntryInsert %W }} - catch {bind Entry {tkEntryInsert %W }} - catch {bind Entry {tkEntryInsert %W }} - catch { - bind Entry { - if {%s == 0} { - tkEntryInsert %W - } else { - tkEntryInsert %W \\ - } - } - } # These might need to be commented out when using XKB: catch {bind Entry {tkEntryInsert %W ~}} catch {bind Entry {tkEntryInsert %W `}} catch {bind Entry {tkEntryInsert %W ~}} catch {bind Entry {tkEntryInsert %W `}} - catch {bind Entry {tkEntryInsert %W }} catch {bind Entry {tkEntryInsert %W ^}} --- 112,124 ---- *************** *** 153,185 **** tkTextInsert %W \\}} catch {bind Text {cb_Text_change_callback %W insert @; \ tkTextInsert %W @}} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} catch {bind Text {cb_Text_change_callback %W insert |; \ tkTextInsert %W |}} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} - catch { - bind Text { - if {%s == 0} { - cb_Text_change_callback %W insert - tkTextInsert %W - } else { - cb_Text_change_callback %W insert \\ - tkTextInsert %W \\ - } - } - } # These might need to be commented out when using XKB: catch {bind Text {cb_Text_change_callback %W insert ~; \ --- 136,143 ---- *************** *** 190,197 **** tkTextInsert %W ~}} catch {bind Text {cb_Text_change_callback %W insert `; \ tkTextInsert %W `}} - catch {bind Text {cb_Text_change_callback %W insert ; \ - tkTextInsert %W }} catch {bind Text {cb_Text_change_callback %W insert ^; \ tkTextInsert %W ^}} --- 148,153 ---- *************** *** 279,285 **** [bind Text ]" bind Text "cb_Text_change_callback %W insert ; \ [bind Text ]" - bind Text "cb_Text_change_callback %W insert ; [bind Text ]" bind Text "cb_Text_change_callback %W delete backchar; \ [bind Text ]" bind Text "cb_Text_change_callback %W delete char; \ --- 235,240 ---- diff -cr tkdesk-1.0/tcldesk/config.tcl tkdesk-1.0_patched/tcldesk/config.tcl *** tkdesk-1.0/tcldesk/config.tcl Fri Feb 27 05:21:16 1998 --- tkdesk-1.0_patched/tcldesk/config.tcl Thu Apr 30 13:47:26 1998 *************** *** 420,426 **** --- 420,428 ---- option add *Checkbutton.font [cb_font $tkdesk(font,labels)] option add *Radiobutton.font [cb_font $tkdesk(font,labels)] option add *Entry.font [cb_font $tkdesk(font,entries)] + option add *Entry.kanjiFont [cb_font $tkdesk(kanjiFont,entries)] option add *Text.font [cb_font $tkdesk(font,entries)] + option add *Text.kanjiFont [cb_font $tkdesk(kanjiFont,text)] option add *Button.font [cb_font $tkdesk(font,buttons)] option add *Menubutton.font [cb_font $tkdesk(font,menubuttons)] option add *Menu.font [cb_font $tkdesk(font,menus)] diff -cr tkdesk-1.0/tcldesk/configs/System tkdesk-1.0_patched/tcldesk/configs/System *** tkdesk-1.0/tcldesk/configs/System Fri Feb 27 05:21:24 1998 --- tkdesk-1.0_patched/tcldesk/configs/System Thu Apr 30 13:51:30 1998 *************** *** 67,73 **** --- 67,75 ---- ### Fonts for entries and text widgets (preferrably mono-spaced): set tkdesk(font,entries) -*-lucidatypewriter-medium-r-*-*-12-*-*-*-*-*-*-* + set tkdesk(kanjiFont,entries) k14 set tkdesk(font,text) $tkdesk(font,entries) + set tkdesk(kanjiFont,text) k14 ### A mono-spaced font for table-like output (disk usage etc.): set tkdesk(font,mono) -*-lucidatypewriter-medium-r-*-*-12-*-*-*-*-*-*-* *************** *** 175,180 **** --- 177,183 ---- ### The following settings are only for the built-in editor: set tkdesk(editor,font) $tkdesk(font,text) + set tkdesk(editor,kanjiFont) $tkdesk(kanjiFont,text) set tkdesk(editor,print_command) "lpr" set tkdesk(editor,default_geometry) "80x25" set tkdesk(editor,auto_indent) 1 tkdesk-2.0/contrib/lock.tcl0100644000175000007640000001377010020457430014024 0ustar jccjcc # lock.tcl # # Author: J. Chris Coppick, 1998 # Description: # # Implements a menu for selecting Xlock modes. # # (I did try just a normal appbar menu with a command for each mode, # but TkDesk, or more specifically Tcl7/Tk4, doesn't handle really # long menus very well. At least, not for me.) # # I hacked this up pretty fast, so caveat receptor. # # Usage: # # Source this from your .tkdesk/Local file and add an entry for # dsk_lock_menu to your appbar. Dsk_lock_menu displays a list # of lock modes for you to choose from. I've included nearly # all the modes available in the Xlockmore distribution, but # the mode list is customizable. The lock program to use # is customizable as well. (As a matter of fact, this code could # be generalized as a way to provide a defined set of arguments # to an arbitrary command, but I'm not going to go there...) # Whatever command is used, it must except the selected mode name # as the first argument after the given command string. For example... # # Correct command string: "xlock -nolock -mode" # # Incorrect command string: "xlock -mode -nolock" # # Variables: # # You can use the following variables in your Appbar file to override # the defaults. # # tkdesk(appbar,lock,program) - The default for this is ~/bin/screenlock # which is a script that I use to wrap xlock. Makes it easier for # me to customize my xlock arguments, add sound, etc. # # tkdesk(appbar,lock,modes) - List of available modes to provide. # # tkdesk(appbar,lock,opts,MODE) - Options can be provided on a per-mode # basis by setting variables of this form where MODE is replaced by # a specific mode name, e.g. 'tkdesk(appbar,lock,opts,hop)'. (See # examples below.) # # Example Appbar: # # {{next/keys.xpm} { # {{Screen Locks}} # - # {{Default} {dsk_exec ~/bin/screenlock &}} # {{Blank Screen} {dsk_exec xlock -nolock -mode blank}} # {{Random} {dsk_exec ~/bin/screenlock random &}} # {{Mode Menu...} {dsk_lock_menu}} # - # {{X Saver On} {dsk_exec xset s on}} # {{X Saver Off} {dsk_exec xset s off}} # }} # ######## # Variable defaults... if [catch {set tkdesk(appbar,lock,program)}] { set tkdesk(appbar,lock,program) {~/bin/screenlock} } if [catch {set tkdesk(appbar,lock,modes)}] { set tkdesk(appbar,lock,modes) [list \ ant ball bat blot bouboule bounce braid bug bubble \ cartoon clock coral crystal daisy dclock deco demon dilemma drift \ eyes fadeplot flag flame forest galaxy grav helix hop hyper \ ico ifs image julia kaleid laser life life1d life3d lightning lisa \ lissie loop mandelbrot marquee maze mountain munch nose pacman penrose \ petal puzzle pyro qix roll rotor shape sierpinski slip sphere spiral \ spline star strange swarm swirl triangle tube turtle vines voters \ wator wire world worm \ ] } # Example per-mode option variables #set tkdesk(appbar,lock,opts,ball) {-size 100} #set tkdesk(appbar,lock,opts,hop) {-sine} # El programmo... global dsk_LockMenu # User hook to launch mode selector... proc dsk_lock_menu {} { global tkdesk dsk_LockMenu set t .dsk_LockMenu if [winfo exists $t] { cb_raise $t return } toplevel $t wm withdraw $t frame $t.f -bd 1 -relief raised pack $t.f -fill x # Command entry... frame $t.f1 pack $t.f1 -in $t.f -fill x -expand yes -padx $tkdesk(pad) label $t.lf -text "Lock Command:" -width 17 -anchor w entry $t.ef -bd 2 -relief sunken -width 40 \ -textvar tkdesk(appbar,lock,program) bind $t.ef { set selectdir [file dirname $tkdesk(appbar,lock,program)] if {$selectdir == ""} { set selectdir [string trimright [dsk_active dir] /] } set selected [cb_fileSelector -label "Select lock command:" -showall 1] if {$selected != ""} { set tkdesk(appbar,lock,program) $selected } } cb_balloonHelp $t.ef "Command to launch Xlock. Use Ctrl-Tab to launch file selector." pack $t.lf $t.ef -in $t.f1 -side left -pady $tkdesk(pad) pack config $t.ef -fill x -ipady 2 -expand yes # Modes listbox... frame $t.flb -bd 1 -relief raised pack $t.flb -in $t.f -fill both -expand yes frame $t.f2 pack $t.f2 -in $t.flb -fill both -expand yes -pady $tkdesk(pad) label $t.llb -text "Modes:" -anchor w pack $t.llb -in $t.f2 -anchor w -padx $tkdesk(pad) set $t.dlb [dsk_Listbox $t.dlb -width 10 -height 10 \ -font $tkdesk(font,file_lbs) \ -bg $tkdesk(color,background)] $t.dlb-frame config -relief flat bind $t.dlb.text "_dsk_lock_exec $t.dlb" # load modes in selector... $t.dlb config -list $tkdesk(appbar,lock,modes) pack $t.dlb -in $t.flb -fill both -expand yes # Buttons... frame $t.f3 pack $t.f3 -in $t.f -expand yes -padx $tkdesk(pad) cb_button $t.bLock -text " Lock " -default 1 \ -command "_dsk_lock_exec $t.dlb" cb_balloonHelp $t.bLock.button "Lock the screen." button $t.bClose -text " Close " -command \ "wm withdraw $t" cb_balloonHelp $t.bClose "Closes this dialog." pack $t.bLock $t.bClose -in $t.f3 -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) pack config $t.bClose -ipady 1 wm title $t "Screen Lock Menu" wm minsize $t 10 10 # dsk_place_window $t dsk_lock_menu 10x10 1 wm protocol $t WM_DELETE_WINDOW "$t.bClose invoke" wm deiconify $t update idletasks } # Launch lock program (and close selector)... proc _dsk_lock_exec {w} { global tkdesk .dsk_LockMenu set sel [$w select get] if {$sel != ""} { set mode [lindex [lindex [$w get] $sel] 0] if {$mode != ""} { .dsk_LockMenu.bClose invoke if [catch {set tkdesk(appbar,lock,opts,$mode)}] { set opts "" } else { set opts $tkdesk(appbar,lock,opts,$mode) } append cmd $tkdesk(appbar,lock,program) " " $mode " " $opts " &" eval dsk_exec $cmd } else { cb_alert "No lock mode selected" } } else { cb_alert "No lock mode selected" } } tkdesk-2.0/contrib/mh_hacks0100644000175000007640000005076710020457430014077 0ustar jccjccFrom POPmail Sun Jun 29 21:56:19 1997 Return-Path: Received: from turtle.mrj.com by wiesbaden.netsurf.de with smtp (Smail3.1.28.1 #10) id m0wiA9M-001lp5a; Sun, 29 Jun 97 04:55 MET DST Received: (from majordomo@localhost) by turtle.mrj.com (8.6.10/8.6.10) id VAA28238 for tkdesk-outgoing; Sat, 28 Jun 1997 21:15:50 -0400 Received: from steve.prima.ruhr.de (root@steve.prima.ruhr.de [141.39.232.6]) by turtle.mrj.com (8.6.10/8.6.10) with ESMTP id VAA28233 for ; Sat, 28 Jun 1997 21:15:43 -0400 Received: (from news@localhost) by steve.prima.ruhr.de (8.8.5/8.8.2) with UUCP id DAA31393 for tkdesk@mrj.com; Sun, 29 Jun 1997 03:13:48 +0200 X-Authentication-Warning: steve.prima.ruhr.de: news set sender to stefan@asgaard.prima.ruhr.de using -f Received: from asgaard.prima.ruhr.de (stefan@localhost [127.0.0.1]) by asgaard.prima.ruhr.de (8.8.4/8.8.4) with ESMTP id DAA09326 for ; Sun, 29 Jun 1997 03:14:02 +0200 Message-Id: <199706290114.DAA09326@asgaard.prima.ruhr.de> X-Mailer: exmh version 1.6.9 8/22/96 To: tkdesk@mrj.com Subject: [TkDesk] AppBar Mail Button Hacks Mime-Version: 1.0 Content-Type: multipart/mixed ; boundary="===_0_Sun_Jun_29_02:40:03_CEST_1997" Date: Sun, 29 Jun 1997 03:14:01 +0200 From: Stefan Gronemeier Sender: owner-tkdesk@mrj.com Precedence: bulk X-Mozilla-Status: 0001 Content-Length: 19566 This is a multipart MIME message. --===_0_Sun_Jun_29_02:40:03_CEST_1997 Content-Type: text/plain; charset=iso-8859-1 Content-Description: mail_hacks.tcl Content-Transfer-Encoding: quoted-printable # Hello ! # # I have hacked up a little extension for TkDesk's Mail button that # can also announce new mail arriving in MH folders (mail sorted into # folders by procmail). # # Since you can mark mail as "unread" with most mail programs, new # mail is announced only once as new, and after that only as old mail. # status information about unread (not announced as new) mail = # is kept in a hidden file (YourMailFolderDir)/.tkdesk_newmail # = # This mail folder hack also contains a hacked-up version of the = # "mail-headers flasher" by J. Chris Coppick (original message = # appended). # # Just copy the following file to ~/.tkdesk/mail_hacks.tcl and # add the following line to ~/.tkdesk/Local : # # uplevel #0 "source $tkdesk(configdir)/mail_hacks.tcl" # # I use it with tkdesk-1.0b4, mh-6.8.4-linux and exmh. # If it does not work for you, you may have to tweak the config = # lines at the beginning of the file. For me, it works perfect ... # # Please let me know if you find major bugs. ########################### MAIL HACKS ##################################= #########################################################################= ### MAIL-HACKS CONFIGURATION #########################################################################= ### MH-Folder Hack (mh-6.8.4-linux / exmh) ### Author: STG ## set to false if you don't use MH set tkdesk(appbar,mail,use_mh) true ## directory where MH keeps the folders ## (lock in $HOME/.mh_profile) set tkdesk(appbar,mail,mh_mail_dir) "~/.MAIL" ## filename where MH puts information about unread mails ## in the folders ## (search for a hidden file in a folder with unread mail ## that contains something like "unseen: 3-6 7 29 31-42") set tkdesk(appbar,mail,mh_mail_seqfile) ".mh_sequences" ## the sequence in .mh_sequences file that marks unseen mails set tkdesk(appbar,mail,mh_unseen) "unseen" ## list of folders you want to be watched by tkdesk set tkdesk(appbar,mail,mh_folders) "inbox from_me tkdesk exmh" ### ### TkDesk mail-headers display ### Author: J. Chris Coppick, 1997 ### # background color for headers window set tkdesk(color,headers_background) black # background color for headers window set tkdesk(color,headers_foreground) yellow # font for headers window set tkdesk(font,headers) 9x15bold # command for summarizing mail contents set tkdesk(cmd,headers) "/usr/bin/mailx -H" # (STG) command for getting headers of MH folders = set tkdesk(cmd,mh_scan) "/usr/local/mh/bin/scan" ### Number of seconds to auto-display mail headers when new mail arrives.= ### Set to zero to disable auto-display of mail headers. set tkdesk(headers_display_time) 5 ### Maximum number lines of mail headers to display ### Set to zero to for unlimited number of lines. set tkdesk(headers_max) 30 #########################################################################= ### MAIL-HACKS #########################################################################= ## STG ## make a list of numbers (1 3 7 8 9 10 13) out of a shortened ## list of numbers (1 3 7-10 13) proc _appbar_expand_mh_sequence {seq} { set exp_seq {} foreach s $seq { set _range [split $s -] if {[llength $_range] > 1} { set _l [lindex $_range 0] set _h [lindex $_range 1] for {set _cnt $_l} { $_cnt <=3D $_h } {incr _cnt} { lappend exp_seq $_cnt } } else { lappend exp_seq $_range } } ## puts stderr "*** $exp_seq" return $exp_seq } ## /STG ### TkDesk MH-Folder Scanning ### Author: Stefan Gronemeier, 1997 (STG) ### ### TkDesk mail-headers display ### Author: J. Chris Coppick, 1997 ### proc _appbar_check_mail {} { global dsk_appbar tkdesk = set b $dsk_appbar(mail,button) if ![winfo exists $b] return = set f $dsk_appbar(mail,folder) if ![file exists $f] { set ns nomail } else { file stat $f stat if {$stat(size) =3D=3D 0} { set ns nomail = # JCC catch {destroy .dsk_mail_headers} = } else { if {$stat(mtime) > $stat(atime)} { set ns newmail } else { set ns oldmail } } } = ## STG if [info exists tkdesk(appbar,mail,use_mh)] { ## list of lines in .tkdesk_newmail sequence file set lines {} = foreach folder $tkdesk(appbar,mail,mh_folders) { set tkdesk(mail,mh,unseen,$folder) "" } ## read $tkdesk(appbar,mail,mh_mail_dir)/.tkdesk_newmail file if ![catch {open "$tkdesk(appbar,mail,mh_mail_dir)/.tkdesk_newmail" r} i= nf] { while {![eof $inf]} { gets $inf line if [string match folder-*:* $line] { set seq [lrange $line 1 end] set l1 [split $line :] set folder [string range [lindex $l1 0] \ 7 [string length [lindex $l1 0]]] set tkdesk(mail,mh,unseen,$folder) \ "[_appbar_expand_mh_sequence $seq]" } } close $inf } = ## scan MH folders for new mail foreach folder $tkdesk(appbar,mail,mh_folders) { ## folder path set fpath "$tkdesk(appbar,mail,mh_mail_dir)/$folder" ## sequence-file set fseq "$fpath/$tkdesk(appbar,mail,mh_mail_seqfile)" dsk_debug "mh_check_sequence $fseq" = if ![catch {open "$fseq" r} inf] { fconfigure $inf -blocking 0 = ## unseen-sequence set _mh_unseen {} set _mh_unseen_full {} ## read sequence file while {![eof $inf]} { gets $inf line if [string match $tkdesk(appbar,mail,mh_unseen):* $line] { ## found unseen-sequence if { [llength $line] > 1 } { set _mh_unseen "[lrange $line 1 end]" set _mh_unseen_full \ [_appbar_expand_mh_sequence $_mh_unseen] } } } close $inf = ### puts stderr "********** $folder * $_mh_unseen_full" = if { $tkdesk(mail,mh,unseen,$folder) !=3D $_mh_unseen_full } { foreach z $_mh_unseen_full { if {[lsearch -exact $tkdesk(mail,mh,unseen,$folder) $z] =3D=3D -1} { set ns newmail break } elseif { $ns =3D=3D "nomail" } { ## mail in MH-folder was already announced as new ## and it is now oldmail, but only if there is no ## newmail in system folder (/var/spool/news/...) set ns oldmail } } } elseif { $ns =3D=3D "nomail" } { if {[llength $_mh_unseen_full] > 0} { set ns oldmail } } ## set tkdesk-newmail sequence to unseen-sequence set tkdesk(mail,mh,unseen,$folder) "$_mh_unseen_full" } } = ## write back .tkdesk_newmail sequence file ## all lines go out unchangend, except for ## 'tkdesk-newmail:', which is set to the ## value of 'unseen:' set inf [open $tkdesk(appbar,mail,mh_mail_dir)/.tkdesk_newmail w]= foreach folder $tkdesk(appbar,mail,mh_folders) { ## avoid empty lines if { [info exists tkdesk(mail,mh,unseen,$folder)] } { puts $inf "folder-$folder: $tkdesk(mail,mh,unseen,$folder= )" } ## puts stderr "folder-$folder: $tkdesk(mail,mh,unseen,$folde= r)" } close $inf } ## /STG = # JCC if {$ns !=3D "nomail"} { dsk_mail_headers } = if {$ns !=3D $dsk_appbar(mail,laststat)} { set dsk_appbar(mail,laststat) $ns $b config -image $dsk_appbar(mail,img,$ns) if {$ns =3D=3D "newmail"} { if {$tkdesk(appbar,mail,newbg) !=3D ""} { $b config -bg $tkdesk(appbar,mail,newbg) } dsk_sound dsk_new_mail beep = # JCC dsk_show_mail_headers = } else { if [info exists tkdesk(color,basic)] { $b config -bg $tkdesk(color,basic) } } } = if [info exists dsk_appbar(mail,afterid)] { catch {after cancel $dsk_appbar(mail,afterid)} } set dsk_appbar(mail,afterid) \ [after [expr $tkdesk(appbar,mail,delay) * 1000] _appbar_check= _mail] } ### TkDesk mail-headers display ### Author: J. Chris Coppick, 1997 ### ### TkDesk MH-Folder Scanning ### Author: Stefan Gronemeier, 1997 (STG) ### proc dsk_mail_headers {} { = global tkdesk dsk_appbar = if {[catch "set tkdesk(cmd,headers)"] || $tkdesk(cmd,headers) =3D=3D = ""} { return } = set t .dsk_mail_headers if [winfo exists $t] { destroy $t } = toplevel $t wm withdraw $t wm title $t {New Mail} frame $t.f -bd 1 -relief raised pack $t.f -fill x = text $t.lb -width 60 -height 10 -font $tkdesk(font,headers) \ -bg $tkdesk(color,headers_background) \ -fg $tkdesk(color,headers_foreground) = pack $t.lb -in $t.f -fill both -expand yes = set cmd $tkdesk(cmd,headers) set cmd [string_replace $cmd \[ \\\[] set cmd [string_replace $cmd \] \\\]] append cmd " 2>/dev/null" if $tkdesk(debug) { catch {puts stderr "$cmd"} } = if [catch "set headers \[exec $cmd]"] { #catch {$t.lb insert end "Mail headers retrieval failed."} } else { } = if ![info exists headers] { set headers "" } = if [info exists tkdesk(appbar,mail,use_mh)] { foreach folder $tkdesk(appbar,mail,mh_folders) { set fpath "$tkdesk(appbar,mail,mh_mail_dir)/$folder" set fseq "$fpath/$tkdesk(appbar,mail,mh_mail_seqfile)" if [catch {open $fseq} inf] { continue } fconfigure $inf -blocking 0 while {![eof $inf]} { gets $inf line if [string match $tkdesk(appbar,mail,mh_unseen):* $line] { if { [llength $line] > 1 } { set _mh_unseen [lrange $line 1 end] foreach _unseen $_mh_unseen { set _range [split $_unseen -] if {[llength $_range] > 1} { set _l [lindex $_range 0] set _h [lindex $_range 1] for {set _cnt $_l} { $_cnt <=3D $_h } {incr _cnt} { set header1 "+$folder:[string trim [exec $tkdesk(cmd,mh_scan) +$f= older = $_cnt] \n]" if { $headers =3D=3D ""} { set headers $header1 } else { set headers "$headers\n$header1" } } } else { set header1 "+$folder:[string trim [exec /$tkdesk(cmd,mh_scan) +$fold= er = $_range] \n]" if { $headers =3D=3D ""} { set headers $header1 } else { set headers "$headers\n$header1" } } } } } } close $inf } } set hl [split $headers \n] set ll [llength $hl] set ml 0 set cnt 0 foreach h $hl { set h [string trim $h { }] set hlen [string length $h] incr cnt if {$hlen > $ml} { set ml $hlen } if {$cnt > $tkdesk(headers_max) && $tkdesk(headers_max) > 0 } { set ll $cnt $t.lb insert end "..." break } else { $t.lb insert end "$h\n" } } $t.lb configure -height $ll -width $ml = cb_centerToplevel $t = if [winfo exists $dsk_appbar(mail,button)] { wm transient $t $dsk_appbar(mail,button) = # # Mouse bindings for showing headers on demand... # bind $dsk_appbar(mail,button) \ "catch {wm deiconify .dsk_mail_headers; raise .dsk_mail_headers}" bind $dsk_appbar(mail,button) \ "catch {wm withdraw .dsk_mail_headers}" = } update idletasks } proc dsk_show_mail_headers {} { = global tkdesk = if [catch {set tkdesk(headers_display_time)}] { return } = if {$tkdesk(headers_display_time) =3D=3D 0 || \ $tkdesk(headers_display_time) =3D=3D ""} { return } = if [winfo exists .dsk_mail_headers] { wm deiconify .dsk_mail_headers raise .dsk_mail_headers update idletasks after [expr 1000 * $tkdesk(headers_display_time)] \ "catch {wm withdraw .dsk_mail_headers}" } } ########################### EOF #########################################= --===_0_Sun_Jun_29_02:40:03_CEST_1997 Content-Type: text/plain; charset=iso-8859-1 Content-Description: mailfolderfun.txt Content-Transfer-Encoding: quoted-printable >From tkdesk-owner@mrj.com Wed Jan 8 01:57:48 1997 Received: (from uucp@localhost) by asgaard.prima.ruhr.de (8.7.5/8.7.3) wi= th = UUCP id BAA00785 for stefan@asgaard.prima.ruhr.de; Wed, 8 Jan 1997 01:57:= 47 = +0100 Received: from turtle.mrj.com (turtle.mrj.com [205.160.13.11]) by = steve.prima.ruhr.de (8.8.2/8.8.2) with SMTP id AAA17628 for = ; Wed, 8 Jan 1997 00:10:58 +0100 Received: (from majordomo@localhost) by turtle.mrj.com (8.6.10/8.6.10) id= = QAA18575 for tkdesk-outgoing; Tue, 7 Jan 1997 16:17:27 -0500 Received: from dreadnought.netjammer.com (dreadnought.netjammer.com = [199.245.28.254]) by turtle.mrj.com (8.6.10/8.6.10) with SMTP id QAA18568= for = ; Tue, 7 Jan 1997 16:17:19 -0500 Received: from shard.snm.com by dreadnought.netjammer.com via smtpd (for turtle.mrj.com [205.160.13.11]) with SMTP; 7 Jan= 1997 = 21:12:01 UT Received: (from jcc@localhost) by snm.com (8.7.5/8.7.3) id QAA24150 for = tkdesk@mrj.com; Tue, 7 Jan 1997 16:19:46 -0500 (EST) Message-Id: <199701072119.QAA24150@snm.com> From: jcc@snm.com (J. Chris Coppick) Date: Tue, 7 Jan 1997 16:19:45 -0500 X-Mailer: Mail User's Shell (7.2.6 alpha(2) 7/9/95) To: tkdesk@mrj.com Subject: Mail Folder Fun Sender: owner-tkdesk@mrj.com Precedence: bulk I like TkDesk's mail button, but I missed my mail-folder flasher, so I wrote one. It'll flash your headers whenever you receive new mail and show them on demand in conjunction with the mail button. For example, if I put the mouse pointer over the mail button and press mouse-button #2, it pops up a window containing a summary of the mail in my incoming folder. When I release the mouse button, the window disappears. If you're stuck with a two-button mouse, you'll probably want to change the bindings. Supported variables (from my System file): # background color for headers window set tkdesk(color,headers_background) black # background color for headers window set tkdesk(color,headers_foreground) yellow # font for headers window set tkdesk(font,headers) 9x15bold # command for summarizing mail contents set tkdesk(cmd,headers) "/usr/bin/mailx -H" ### Number of seconds to auto-display mail headers when new mail arrives.= ### Set to zero to disable auto-display of mail headers. set tkdesk(headers_display_time) 5 You'll need to change the _appbar_check_mail() procedure in "appbar.tcl".= Here's my version. The changes are marked with my initials (JCC). (There's only 5 new lines.) ###### proc _appbar_check_mail {} { global dsk_appbar tkdesk set b $dsk_appbar(mail,button) if ![winfo exists $b] return = set f $dsk_appbar(mail,folder) if ![file exists $f] { set ns nomail } else { file stat $f stat if {$stat(size) =3D=3D 0} { set ns nomail # JCC catch {destroy .dsk_mail_headers} } else { if {$stat(mtime) > $stat(atime)} { set ns newmail } else { set ns oldmail } } } # JCC if {$ns !=3D "nomail"} { dsk_mail_headers } if {$ns !=3D $dsk_appbar(mail,laststat)} { set dsk_appbar(mail,laststat) $ns $b config -image $dsk_appbar(mail,img,$ns) if {$ns =3D=3D "newmail"} { if {$tkdesk(appbar,mail,newbg) !=3D ""} { $b config -bg $tkdesk(appbar,mail,newbg) } dsk_sound dsk_new_mail beep # JCC dsk_show_mail_headers } else { if [info exists tkdesk(color,basic)] { $b config -bg $tkdesk(color,basic) } } } = if [info exists dsk_appbar(mail,afterid)] { catch {after cancel $dsk_appbar(mail,afterid)} } set dsk_appbar(mail,afterid) \ [after [expr $tkdesk(appbar,mail,delay) * 1000] _appbar_check_mail] } ####### Either include this code in your Local config. file or source it from there. I have it in a file called "mail.tcl". Then I put the following in my Local file: source $tkdesk(configdir)/mail.tcl ### ### TkDesk mail-headers display ### Author: J. Chris Coppick, 1997 ### proc dsk_mail_headers {} { global tkdesk dsk_appbar if {[catch "set tkdesk(cmd,headers)"] || $tkdesk(cmd,headers) =3D=3D "= "} { return } = set t .dsk_mail_headers if [winfo exists $t] { destroy $t } toplevel $t wm withdraw $t wm title $t {New Mail} frame $t.f -bd 1 -relief raised pack $t.f -fill x text $t.lb -width 60 -height 10 -font $tkdesk(font,headers) \ -bg $tkdesk(color,headers_background) \ -fg $tkdesk(color,headers_foreground) = pack $t.lb -in $t.f -fill both -expand yes set cmd $tkdesk(cmd,headers) set cmd [string_replace $cmd \[ \\\[] set cmd [string_replace $cmd \] \\\]] append cmd " 2>/dev/null" if $tkdesk(debug) { catch {puts stderr "$cmd"} } if [catch "set headers \[exec $cmd]"] { catch {$t.lb insert end "Mail headers retrieval failed."} } else { set hl [split $headers \n] set ll [llength $hl] set ml 0 foreach h $hl { set h [string trim $h { }] set hlen [string length $h] if {$hlen > $ml} { set ml $hlen } $t.lb insert end "$h\n" } $t.lb configure -height $ll -width $ml } cb_centerToplevel $t if [winfo exists $dsk_appbar(mail,button)] { wm transient $t $dsk_appbar(mail,button) # # Mouse bindings for showing headers on demand... # bind $dsk_appbar(mail,button) \ "catch {wm deiconify .dsk_mail_headers; raise .dsk_mail_headers}= " bind $dsk_appbar(mail,button) \ "catch {wm withdraw .dsk_mail_headers}" } update idletasks } = proc dsk_show_mail_headers {} { global tkdesk if [catch {set tkdesk(headers_display_time)}] { return } if {$tkdesk(headers_display_time) =3D=3D 0 || \ $tkdesk(headers_display_time) =3D=3D ""} { return } if [winfo exists .dsk_mail_headers] { wm deiconify .dsk_mail_headers raise .dsk_mail_headers update idletasks after [expr 1000 * $tkdesk(headers_display_time)] catch {wm withdraw .dsk_mail_headers} } } ####### Enjoy. -- Chris _________________________________________________________________________= ___ "...when I have understanding of computers, = I will be the supreme being." -- Time Bandits # ---------------------------------------------------------------------- = # # To post to the list, send email to 'tkdesk@mrj.com'. = # # To unsubscribe from this list, send email to 'majordomo@mrj.com' with = # # no subject and the following line in the body of the message: = # # unsubscribe tkdesk = # # ---------------------------------------------------------------------- = # --===_0_Sun_Jun_29_02:40:03_CEST_1997-- # ---------------------------------------------------------------------- # # To post to the list, send email to 'tkdesk@mrj.com'. # # To unsubscribe from this list, send email to 'majordomo@mrj.com' with # # no subject and the following line in the body of the message: # # unsubscribe tkdesk # # ---------------------------------------------------------------------- # tkdesk-2.0/contrib/on_delete.patch0100644000175000007640000000247410020457430015346 0ustar jccjcc*** file_ops.tcl.bk Fri Sep 20 18:24:48 1996 --- file_ops.tcl Fri Sep 20 18:24:48 1996 *************** *** 1472,1477 **** --- 1472,1507 ---- set dsk_delete(all) $tkdesk(all_files) set dsk_delete(really) $tkdesk(really_delete) + # + # modifications by jdblair + # support a new config: "action_on_delete" + # + + if [info exists tkdesk(action_on_delete)] { + set newd [string trimright $files /] + set dmatch 0 + foreach d $tkdesk(action_on_delete) { + set dp [lindex $d 0] ;# list of patterns + set da [lindex $d 1] ;# Tcl script to execute + foreach p $dp { + set p [string trimright [cb_tilde $p expand] /] + if [string match $p $newd] { + set err [catch {eval [_expand_pc $da]} errmsg] + if $err { + dsk_errbell + cb_error $errmsg + } + set dmatch 1 + break + } + } + if $dmatch break } + } else { + + # + # end modifications by jdblair + # + if {[string first "$tkdesk(configdir)/.trash" [lindex $files 0]] > -1} { set dsk_delete(really) 1 } *************** *** 1555,1560 **** --- 1585,1594 ---- wm protocol $t WM_DELETE_WINDOW {.dsk_delete.bCancel invoke} dsk_place_window $t dsk_delete 470x98 + + # jdblair mod: + # this trailing close brace ends the original delete functions + } } proc dsk_delete_action {cmd} { tkdesk-2.0/contrib/screenlock0100644000175000007640000000055210020457430014435 0ustar jccjcc#!/bin/sh if [ $# != 0 ]; then mode=$1 shift else mode=nose fi /usr/bin/X11/xlock \ -mode $mode \ -program '/usr/games/fortune' \ -remote \ -usefirst \ -nice 18 \ -info "Are you a god?" \ -msgfont 10x20 \ -bg '#77A26D' \ -fg '#C8C365' \ -username "Wizard: " \ -password "Magic Word: " \ -validate "Invoking spell..." \ -invalid "Bugger off..." \ $* tkdesk-2.0/contrib/tar.tcl0100644000175000007640000002702210020457430013655 0ustar jccjcc# tar.tcl # # Author: J. Chris Coppick, 1996 (Adapted from code by Christian Bolik) # Description: Implements a toplevel for listing and extracting tar files. # # Changes: # # - patched to work with TkDesk 1.2 (J. Chris Coppick, 11.02.99) # # ---------------------------------------------------------------------------- # dsk_tar_browser: # # We'll use the same geometry as the find_files dialog for now, so we # don't have to muck with the layout routines... if ![info exists tkdesk(geometry,dsk_find_files)] { set tkdesk(geometry,dsk_find_files) "" } if [catch {set tkdesk(cmd,zcat)}] { set tkdesk(cmd,zcat) "zcat" } if [catch {set tkdesk(cmd,tar)}] { set tkdesk(cmd,tar) "/bin/tar" } global dsk_tar set dsk_tar(file) "" set dsk_tar(destdir) "" set dsk_tar(compressed) {{*.gz} {*.Z} {*.z} {*.tgz} {*.taz}} proc dsk_tar_browser {{file ""} {destdir ""}} { global tkdesk dsk_tar if {$file != ""} { set dsk_tar(file) $file } if {$destdir != ""} { set dsk_tar(destdir) $destdir } set t .dsk_tar_browser if [winfo exists $t] { cb_raise $t dsk_do_tar_list return } toplevel $t wm withdraw $t frame $t.f -bd 1 -relief raised pack $t.f -fill x # ---- File: frame $t.f1 pack $t.f1 -in $t.f -fill x -expand yes -padx $tkdesk(pad) label $t.lf -text "File:" -width 17 -anchor w entry $t.ef -bd 2 -relief sunken -width 40 -textvar dsk_tar(file) bind $t.ef "$t.bList.button flash ; $t.bList.button invoke" bind $t.ef { set selectdir [file dirname $dsk_tar(file)] if {$selectdir == ""} { set selectdir [string trimright [dsk_active dir] /] } set selected [cb_fileSelector -filter "$selectdir/*.tar*" \ -label "Select archive:" -showall 1] if {$selected != ""} { set dsk_tar(file) $selected } } cb_balloonHelp $t.ef "The archive file to examine." pack $t.lf $t.ef -in $t.f1 -side left -pady $tkdesk(pad) pack config $t.ef -fill x -ipady 2 -expand yes # ---- Destdir: frame $t.f2 pack $t.f2 -in $t.f -fill x -expand yes -padx $tkdesk(pad) label $t.ld -text "Destination:" -width 17 -anchor w entry $t.ed -bd 2 -relief sunken -width 40 -textvar dsk_tar(destdir) bind $t.ed "$t.bExt.button flash ; $t.bExt.button invoke" cb_bindForCompletion $t.ed cb_balloonHelp $t.ed "Destination directory for extractions." frame $t.fd2 -width 8 menubutton $t.mbDir -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/slash.xbm \ -menu $t.mbDir.menu cb_balloonHelp $t.mbDir "This menu contains the custom directories from the Directories menu." menu [set m $t.mbDir.menu] \ -postcommand "_dsk_dmenu $t.mbDir.menu $t.ed" # add dummy entry to work around bug in pre Tk 4.0p2: $m add command -label "dummy" menubutton $t.mbHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $t.mbHist.menu cb_balloonHelp $t.mbHist "This menu contains the directories you have last visited." menu $t.mbHist.menu -postcommand "_dsk_hmenu $t.mbHist.menu $t.ed" # add dummy entry to work around bug in pre Tk 4.0p2: $t.mbHist.menu add command -label "dummy" pack $t.ld $t.ed $t.fd2 $t.mbDir $t.mbHist -in $t.f2 -side left \ -pady $tkdesk(pad) pack config $t.ed -fill x -ipady 2 -expand yes pack config $t.mbDir $t.mbHist -ipadx 2 -ipady 2 # ---- Buttons: frame $t.f5 pack $t.f5 -in $t.f -fill x -expand yes -padx $tkdesk(pad) cb_button $t.bList -text " List " -default 1 \ -command dsk_do_tar_list cb_balloonHelp $t.bList.button "List the contents of the archive." cb_button $t.bExt -text " Extract " -default 1 \ -command dsk_do_tar_ext cb_balloonHelp $t.bExt.button "Extract files from the archive." cb_button $t.bClear -text " Clear Selection " -default 1 \ -command "$t.dlb select clear" cb_balloonHelp $t.bClear.button "Clear archive contents selection." button $t.bClose -text " Close " -command \ "set tkdesk(geometry,dsk_find_files) \[wm geometry $t\] ;\ destroy $t" cb_balloonHelp $t.bClose "Closes this dialog." pack $t.bList $t.bExt $t.bClear $t.bClose -in $t.f5 -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) pack config $t.bExt -ipady 1 pack config $t.bClose -ipady 1 # ---- dsk_Listbox: frame $t.flb -bd 1 -relief raised pack $t.flb -fill both -expand yes frame $t.f6 pack $t.f6 -in $t.flb -fill both -expand yes -pady $tkdesk(pad) label $t.llb -text "Archive Contents:" -anchor w pack $t.llb -in $t.f6 -anchor w -padx $tkdesk(pad) pack [_dsk_tar_lb $t.dlb] -in $t.f6 -fill both -expand yes wm title $t "Archive Browser" wm minsize $t 10 2 dsk_place_window $t dsk_tar_browser 40x20 1 #if {$tkdesk(geometry,dsk_find_files) == ""} { # wm geometry $t 10x5 #} else { # wm geometry $t $tkdesk(geometry,dsk_find_files) #} wm protocol $t WM_DELETE_WINDOW "$t.bClose invoke" wm deiconify $t update idletasks dsk_do_tar_list } # ---------------------------------------------------------------------------- # dsk_do_tar_list: # Lists the archive contents with the parameters set through the dialog. # proc dsk_do_tar_list {} { global dsk_tar tkdesk if {$dsk_tar(file) == ""} { return } set compressed 0 foreach ext $dsk_tar(compressed) { if {[string match $ext $dsk_tar(file)]} { incr compressed } } if {$compressed} { set cmd $tkdesk(cmd,zcat) append cmd " [cb_tilde $dsk_tar(file) expand]" append cmd " | $tkdesk(cmd,tar) tf -" } else { set cmd $tkdesk(cmd,tar) append cmd " tf [cb_tilde $dsk_tar(file) expand]" } set cmd [string_replace $cmd \[ \\\[] set cmd [string_replace $cmd \] \\\]] append cmd " 2>/dev/null" if $tkdesk(debug) { catch {puts stderr "$cmd"} } set t .dsk_tar_browser $t.dlb config -list {} $t.bList.button config -state disabled $t.llb config -text "Listing contents..." set contents [dsk_bgexec $cmd "Listing contents..."] catch {$t.bList.button config -state normal} ;# button may not exist if {$contents != "break"} { set cl [split $contents \n] set ll [llength $cl] if {$ll == 1} { $t.llb config -text "Contains 1 file:" } elseif {$ll == 0} { $t.llb config -text "Empty archive." } else { $t.llb config -text "Contains $ll files:" } $t.dlb config -list $cl } else { catch {$t.llb config -text "List operation abandoned."} } } # ---------------------------------------------------------------------------- # dsk_do_tar_ext: # Extracts the entire archive contents, or just the given files. # proc dsk_do_tar_ext {{files ""}} { global tkdesk dsk_tar if {$dsk_tar(file) == ""} { return } set compressed 0 foreach ext $dsk_tar(compressed) { if {[string match $ext $dsk_tar(file)]} { incr compressed } } if {$compressed} { set cmd $tkdesk(cmd,zcat) append cmd " [cb_tilde $dsk_tar(file) expand]" append cmd " | $tkdesk(cmd,tar) xf -" } else { set cmd $tkdesk(cmd,tar) append cmd " xf [cb_tilde $dsk_tar(file) expand]" } set dest $dsk_tar(destdir) if {$dest == ""} { set dest [string trimright [dsk_active dir] /] } set t .dsk_tar_browser if {$files == ""} { set indices [$t.dlb select get] if {$indices != ""} { foreach i $indices { lappend files [$t.dlb get $i] } } } if {$files == ""} { set ans [cb_okcancel \ "Extract into $dest?"] } elseif {[llength $files] == 1} { set ans [cb_okcancel \ "Extract $files into $dest?"] } else { set ans [cb_okcancel \ "Extract selected files into $dest?"] } if {$ans != 0} { return } append cmd " $files" set cmd [string_replace $cmd \[ \\\[] set cmd [string_replace $cmd \] \\\]] append cmd " 2>/dev/null" if $tkdesk(debug) { catch {puts stderr "$cmd"} } $t.dlb select clear $t.bExt.button config -state disabled set status [dsk_tar_bgexec $cmd $dest "Extracting..."] catch {$t.bExt.button config -state normal} ;# button may not exist if {$status == "break"} { cb_error "Extract failed." } } # ---------------------------------------------------------------------------- # _dsk_tar_lb name: # Creates a dsk_Listbox for the tar dialogs with name $name. # proc _dsk_tar_lb {name} { global tkdesk catch {$name delete} dsk_Listbox $name -width 10 -height 2 -font $tkdesk(font,file_lbs) \ -bg $tkdesk(color,background) set name_frame [$name getFrame] $name_frame config -relief flat $name_frame.text config -tabs {465 right 480 left 575 left 635 left 695 \ left 775 left 795 left} bind $name_frame.text "_dsk_tar_lb_ext $name %s" return $name_frame } # binding for double click: proc _dsk_tar_lb_ext {dlb shift} { global tkdesk set tkdesk(file_lb,control) [expr $shift & 4] set sel [$dlb select get] if {$sel != ""} { set tmpfile [lindex [lindex [$dlb get] $sel] 0] if {$tmpfile != ""} { dsk_do_tar_ext $tmpfile } } } # Version of dsk_bgexec that accepts a working directory argument. proc dsk_tar_bgexec {cmd wd label} { global dsk_bgexec tkdesk set pad 8 # ---- first create the "stand by" window set cnt [incr dsk_bgexec(cnt)] set t ".dsk_bgexec$cnt" catch "destroy $t" toplevel $t wm withdraw $t frame $t.fl -bd 1 -relief raised pack $t.fl -fill x label $t.label -text "$label" \ -font -*-helvetica-bold-r-*-*-14-*-*-*-*-*-*-* pack $t.label -in $t.fl -side left -padx $pad -pady $pad frame $t.fb -bd 1 -relief raised pack $t.fb -fill x button $t.bBreak -text " Stop " \ -command "set dsk_bgexec(out$cnt) break ;\ set dsk_bgexec(stat$cnt) {} ;\ destroy .dsk_bgexec$cnt" pack $t.bBreak -in $t.fb -side top -padx $pad -pady $pad -ipady 2 #grab $t wm title $t "Background #$cnt" wm protocol $t WM_DELETE_WINDOW {# ignore} if {$tkdesk(geometry,dsk_bgexec) == ""} { cb_centerToplevel $t } else { set g [split $tkdesk(geometry,dsk_bgexec) +x] set vw [winfo vrootwidth .] set vh [winfo vrootheight .] set x [lindex $g 2] while {$x < 0} {incr x $vw} while {$x > $vw} {incr x -$vw} set y [lindex $g 3] while {$y < 0} {incr y $vh} while {$y > $vh} {incr y -$vh} wm geometry $t +$x+$y wm deiconify $t } update # ---- now execute cmd catch "unset dsk_bgexec(cmd$cnt)" catch "unset dsk_bgexec(err$cnt)" set owd [pwd] if {[catch "cd $wd"]} { catch "destroy $t" cb_error "Invalid working directory: $wd" return "error" } set tkdesk(bgexec,working) 1 #eval blt_bgexec -errorvar dsk_bgexec(err$cnt) dsk_bgexec(out$cnt) $cmd eval blt_bgexec dsk_bgexec(stat$cnt) \ -output dsk_bgexec(out$cnt) \ -error dsk_bgexec(err$cnt) $cmd & tkwait variable dsk_bgexec(stat$cnt) set tkdesk(bgexec,working) 0 catch {set tkdesk(geometry,dsk_bgexec) [wm geometry $t]} catch "destroy $t" if [catch "cd $owd"] { # maybe $owd has just been deleted cd $tkdesk(startdir) } incr dsk_bgexec(cnt) -1 if ![info exists dsk_bgexec(err$cnt)] { return $dsk_bgexec(out$cnt) } elseif {$dsk_bgexec(err$cnt) != ""} { dsk_errbell cb_error "$dsk_bgexec(err$cnt)" return "error" } else { return $dsk_bgexec(out$cnt) } } tkdesk-2.0/contrib/tar_browser0100644000175000007640000000347310020457430014643 0ustar jccjccFrom: jcc@eiq.com (J. Chris Coppick) Date: Wed, 3 Sep 1997 14:06:48 -0700 ------------------ Here's the tar-file browser... (Update: Since TkDesk 1.2 the browser itself is now in tar.tcl. To use it put a statement like "source /tar.tcl" into the "System" config file where if the path to tar.tcl.) I plugged this into the Popups menu for tar files thusly: {{*.tar} { {{Browse Contents} {dsk_tar_browser %s %D}} {{Long Listing} {dsk_view tar tvf %s}} - {{Extract} {dsk_exec tar xf %s}} {{Extract to /} {dsk_exec tar xf %s -C /}} {{Extract to /usr/tmp} {dsk_exec tar xf %s -C /usr/tmp}} }} {{{*.tar.z} {*.tar.gz} {*.tgz} {*.tar.Z} {*.taz} {*.t.Z}} { {{Browse Contents} {dsk_tar_browser %s %D}} {{Long Listing} {dsk_view gzip -cd %s | tar tvf -}} - {{Extract} {dsk_exec gzip -cd %s | tar xf -}} {{Extract to /} {dsk_exec gzip -cd %s | tar xf - -C /}} {{Extract to /usr/tmp} {dsk_exec gzip -cd %s | tar xf - -C /usr/tmp}} }} Some usage notes: * Uses the variables tkdesk(cmd,tar) and tkdesk(cmd,zcat), which you can set from your System file. The variable dsk_tar(compressed) is used to recognize file extensions on compressed files. * The "List" button attempts to generate a listing of whatever file is named in the "File:" entry. * The "Extract" button attempts to extract either selected files, or the entire archive. Double-click button #1 on a file to quickly extract just that file. A confirmation is requested before any extractions are performed. * The "Clear Selection" button does just that. * You can extract into alternate directories by modifying the "Destination:" entry. * The browser uses the same geometry settings as the find-files dialog, so you don't have to patch TkDesk to use it. tkdesk-2.0/contrib/termite0100644000175000007640000004375310020457430013770 0ustar jccjcc#!/usr/local/bin/wish set VERSION {termite,v 1.1.1.1 2004/02/29 21:52:24 jcc Exp} # # Termite: Eat logs, etc. # # Author: J. Chris Coppick # # Usage: termite \[options\] " # Options: -fp - Filter Pattern" # -csf - Case-Sensitive Filtering" # -hp - Highlighting Pattern" # -hc - Highlighting Color" # -csh - Case-Sensitive Highlighting" # -as - Auto-Scrolling" # -ln - Line Numbering" # # Sample X resources: # # Termite*Font: 7x14bold # Termite*selectBackground: SlateGrey # Termite*selectForeground: yellow # Termite*background: SlateGrey # Termite*foreground: WhiteSmoke # Termite*highlightThickness: 0 # Termite*troughColor: grey # Termite*activeForeground: yellow # Termite*activeBackground: grey # Termite*highlightColor: grey # Termite.scroll.activeBackground: Slategrey # Termite.log.foreground: white # Termite.log.background: black # Termite.log.font: 9x15bold # Termite.log.width: 80 # Termite.log.height: 24 # Termite*hc: red # Termite*ln: false # Termite*as: true # # # Procs... # # # My tkFDialog_Done doesn't challenge the user when appending to an # existing file... # catch {auto_load tkFDialog_Done} set file_append 0 proc tkFDialog_Done {w {selectFilePath ""}} { upvar #0 $w data global tkPriv file_append if ![string compare $selectFilePath ""] { set selectFilePath [file join $data(selectPath) $data(selectFile)] set tkPriv(selectFile) $data(selectFile) set tkPriv(selectPath) $data(selectPath) if {[file exists $selectFilePath] && !$file_append && ![string compare $data(type) save]} { set reply [tk_messageBox -icon warning -type yesno \ -message "File \"$selectFilePath\" already exists.\nDo you want to overwrite it?"] if ![string compare $reply "no"] { return } } } set tkPriv(selectFilePath) $selectFilePath } # # Highlight log text that matches some pattern # proc hilite_text {} { global hpattern hcase_option hcolor scan [.log index end] %d numlines for {set i 1} {$i<=$numlines} {incr i} { .log mark set line ${i}.0 if {[regexp $hcase_option $hpattern [.log get line "line lineend"]]} { .log tag add hilite line "line lineend" } } .log tag configure hilite -foreground $hcolor } # # Create a dialog that allows the user to set highlighting options. # proc hilite_dialog {} { global hnewpattern toplevel .hd -class Dialog wm title .hd "Highlight Settings" wm iconname .hd "Dialog" frame .hd.top -relief raised -bd 1 frame .hd.opts -relief raised -bd 1 frame .hd.bot -relief sunken pack .hd.top -side top -fill both pack .hd.opts -side top -fill both pack .hd.bot -side bottom -fill both label .hd.top.label -text "Pattern: " entry .hd.top.pattern -width 40 -relief sunken -bd 2 \ -textvariable hnewpattern bind .hd.top.pattern {hilite_apply $hnewpattern} pack .hd.top.label .hd.top.pattern -side left -padx 1m -pady 2m checkbutton .hd.opts.case -text "Case Sensitivity" \ -command {hilite_case_toggle} pack .hd.opts.case -side left button .hd.bot.apply -text "Apply" -command {hilite_apply $hnewpattern} button .hd.bot.cancel -text "Cancel" -command {hilite_cancel} pack .hd.bot.apply .hd.bot.cancel -side left -expand 1 -padx 2 -pady 2 } # # Cancel highlighting dialog # proc hilite_cancel {} { destroy .hd } # # Apply highlight changes # proc hilite_apply {hnewpattern} { global hpattern hcase_option highlighting hcolor set hpattern $hnewpattern catch {destroy .hd} set highlighting 1 hilite_text } # # Manage toggle of highlighting states # proc hilite_toggle {} { global highlighting hpattern hcolor if {$highlighting} { if {$hpattern != ""} { hilite_text } else { hilite_dialog } } else { .log tag remove hilite 1.0 end } } # # Manage toggle of highlighting case sensitivity # proc hilite_case_toggle {} { global hcase_option if {$hcase_option == "-nocase"} { set hcase_option "--" } else { set hcase_option "-nocase" } } # # Create a dialog that allows the user to set filtering options. # proc filter_dialog {} { global newpattern toplevel .fd -class Dialog wm title .fd "Filter Settings" wm iconname .fd "Dialog" frame .fd.top -relief raised -bd 1 frame .fd.opts -relief raised -bd 1 frame .fd.bot -relief sunken pack .fd.top -side top -fill both pack .fd.opts -side top -fill both pack .fd.bot -side bottom -fill both label .fd.top.label -text "Pattern: " entry .fd.top.pattern -width 40 -relief sunken -bd 2 -textvariable newpattern bind .fd.top.pattern {filter_apply $newpattern} pack .fd.top.label .fd.top.pattern -side left -padx 1m -pady 2m checkbutton .fd.opts.case -text "Case Sensitivity" \ -command {filter_case_toggle} pack .fd.opts.case -side left button .fd.bot.apply -text "Apply" -command {filter_apply $newpattern} button .fd.bot.cancel -text "Cancel" -command {filter_cancel} pack .fd.bot.apply .fd.bot.cancel -side left -expand 1 -padx 2 -pady 2 } # # Cancel filter dialog # proc filter_cancel {} { destroy .fd } # # Apply filter changes # proc filter_apply {newpattern} { global pattern filtering set pattern $newpattern catch {destroy .fd} if {$pattern != ""} { set filtering 1 force_reread } else { set filtering 0 force_reread } } # # Manage toggle of filter states # proc filter_toggle {} { global filtering pattern bgevent if {$filtering} { if {$pattern != ""} { filter_apply $pattern } else { filter_dialog } } else { force_reread } } # # Manage toggle of filter case sensitivity # proc filter_case_toggle {} { global case_option if {$case_option == "-nocase"} { set case_option "--" } else { set case_option "-nocase" } } # # Clear the log pane # proc clear {} { set filesize 0 .f1.size configure -text 0 .log configure -state normal .log delete 1.0 end .log configure -state disabled } # # Indicate lack of current file # proc set_no_file {} { global logfile gotfile clear set logfile "No File" set gotfile 0 } # # Create a dialog that allows the user to set the print command. # set print_cmd "lp" proc print_dialog {} { global print_cmd new_print_cmd toplevel .pd -class Dialog wm title .pd "Print Command" wm iconname .pd "Dialog" frame .pd.top -relief raised -bd 1 frame .pd.bot -relief sunken pack .pd.top -side top -fill both pack .pd.bot -side bottom -fill both label .pd.top.label -text "Print Command: " set new_print_cmd $print_cmd entry .pd.top.cmd -width 40 -relief sunken -bd 2 -textvariable new_print_cmd bind .pd.top.cmd {destroy .pd; set print_cmd $new_print_cmd;} pack .pd.top.label .pd.top.cmd -side left -padx 1m -pady 2m button .pd.bot.print -text "Print" -command {destroy .pd; set print_cmd $new_print_cmd} button .pd.bot.cancel -text "Cancel" -command {destroy .pd; set print_cmd $print_cmd} pack .pd.bot.print .pd.bot.cancel -side left -expand 1 -padx 2 -pady 2 tkwait variable print_cmd return $print_cmd } # # Print the current selection # proc print_sel {} { if {!$gotfile} { tk_dialog .md "Termite: Error" "No Current File" "" 0 "OK" } elseif [catch {set owned [selection own]}] { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } elseif {$owned == ""} { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } elseif {[set sel [selection get]] == ""} { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } else { set cmd [print_dialog] if {$cmd == ""} { tk_dialog .md "Termite: Error" "No Print Command Specified" "" 0 "OK" } else { if {[catch {set lpid [open "|$cmd" r+]}]} { tk_dialog .md "Termite: Error" "Couldn't open pipe to $cmd" "" 0 "OK" } else { puts $lpid $sel catch {close $lpid} } } } } # # Print the file # proc print_file {} { if {!$gotfile} { tk_dialog .md "Termite: Error" "No Current File" "" 0 "OK" } elseif [catch {set owned [selection own]}] { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } elseif {$owned == ""} { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } elseif {[set sel [selection get]] == ""} { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } else { set cmd [print_dialog] if {$cmd == ""} { tk_dialog .md "Termite: Error" "No Print Command Specified" "" 0 "OK" } else { if {[catch {set lpid [open "|$cmd" r+]}]} { tk_dialog .md "Termite: Error" "Couldn't open pipe to $cmd" "" 0 "OK" } else { puts -nonewline $lpid [.log get 1.0 end] catch {close $lpid} } } } } # # Open new file # proc new_file {} { global logfile gotfile bgevent set filename [tk_getOpenFile] if {$filename != ""} { catch {after cancel $bgevent} clear set logfile $filename set gotfile 1 again } } # # Save to file # proc save_file {} { if {!$gotfile} { tk_dialog .md "Termite: Error" "No Current File" "" 0 "OK" } else { set filename [tk_getSaveFile -title "Save as..."] if {$filename != ""} { if {[catch {set saveid [open "$filename" w]}]} { tk_dialog .md "Termite: Error" "Couldn't open $filename" "" 0 "OK" } else { puts -nonewline $saveid [.log get 1.0 end] close $saveid } } } } # # Append selection to some file # proc save_sel {} { global file_append if [catch {set owned [selection own]}] { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } elseif {$owned == ""} { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } elseif {[set sel [selection get]] == ""} { tk_dialog .md "Termite: Error" "No Selected Text" "" 0 "OK" } else { set file_append 1 set filename [tk_getSaveFile -title "Append selection to..."] set file_append 0 if {$filename != ""} { if {[catch {set saveid [open "$filename" a]}]} { tk_dialog .md "Termite: Error" "Couldn't open $filename" "" 0 "OK" } else { puts $saveid \ "\n*** Excerpt from $logfile \[[exec /bin/date]\] ***" puts $saveid $sel close $saveid } } } } # # Update file percentage, scroll text window # proc set_percent {first last} { .scroll set $first $last .f2.percent configure -text "View Location: [expr round($last*100)]%" } # # Force reset and reread # proc force_reread {} { global bgevent filesize catch {after cancel $bgevent} clear set filesize 0 again } # # Read log entries into text window # proc read_text {fileid} { global filtering pattern case_option numbering set linenum 0 set longline 80 .log configure -state normal while {[gets $fileid logentry] >= 0} { incr linenum 1 if {$filtering} { if {[regexp $case_option $pattern $logentry]} { if {$numbering} { set logentry "${linenum}: ${logentry}" } .log insert end $logentry .log insert end "\n" } } else { if {$numbering} { set logentry "${linenum}: ${logentry}" } .log insert end $logentry .log insert end "\n" } } .log configure -state disabled } # # Does most of the work... gets run every 3 seconds and checks to # see if the file has changed, updates display if necessary # proc again {} { global argv0 filesize autoscroll \ logfile bgevent highlighting gotfile if {!$gotfile} { return } if {[catch {set newsize [file size $logfile]}]} { tk_dialog .md "Termite: Error" "Couldn't access $logfile" "" 0 "OK" set_no_file return } # The file has grown... read the newest file contents and display them if {$newsize > $filesize} { set offset [expr $filesize-$newsize] if {[catch {set fileid [open "$logfile"]}]} { tk_dialog .md "Termite: Error" "Error opening $logfile" "" 0 "OK" set_no_file } else { seek $fileid $offset end read_text $fileid close $fileid set filesize [file size $logfile] if {[catch {set filesize [file size $logfile]}]} { tk_dialog .md "Termite: Error" "Couldn't access $logfile" "" 0 "OK" set_no_file return } .f1.size configure -text $filesize if {$highlighting} { hilite_text } if {$autoscroll} { .log yview moveto 1 } } } # The file has been/is being truncated... sleep for a few seconds, then # do a reality check (reread the file) if {$newsize < $filesize} { exec sleep 2 .log configure -state normal if {[catch {set fileid [open "$logfile"]}]} { tk_dialog .md "Termite: Error" "Error opening $logfile" "" 0 "OK" set_no_file } else { read_text $fileid close $fileid set filesize [file size $logfile] .f1.size configure -text $filesize if {$highlighting} { hilite_text } .log yview moveto 1 } } set bgevent [after 3000 again] } proc usage {} { puts "Usage: termite \[options\] " puts " Options: -fp - Filter Pattern" puts " -csf - Case-Sensitive Filtering" puts " -hp - Highlighting Pattern" puts " -hc - Highlighting Color" puts " -csh - Case-Sensitive Highlighting" puts " -as - Auto-Scrolling" puts " -ln - Line Numbering" puts "" exit 1 } # # Main... # # Resource fun... if {[catch {set resdir $env(XAPPLRESDIR)}]} { set resdir "/usr/lib/X11/app-defaults" } catch {option readfile "${resdir}/Termite" startupFile} # Argument fun... # Setup line numbering option... if {[set oi [lsearch -exact $argv {-ln}]] != -1} { set numbering 1 set argv [lreplace $argv $oi $oi]; incr argc -1 } elseif \ {[regexp -nocase {^(true|1)$} [option get . ln Ln]]} { set numbering 1 } else { set numbering 0 } # Setup autoscroll option... if {[set oi [lsearch -exact $argv {-as}]] != -1} { set autoscroll 1 set argv [lreplace $argv $oi $oi]; incr argc -1 } elseif \ {[regexp -nocase {^(true|1)$} [option get . as As]]} { set autoscroll 1 } else { set autoscroll 0 } # Setup initial filtering... if {[set oi [lsearch -exact $argv {-fp}]] != -1} { set pattern [lindex $argv [expr $oi+1]] set filtering 1 set argv [lreplace $argv $oi [expr $oi+1]]; incr argc -2 } else { set pattern "" set filtering 0 } set newpattern $pattern if {[set oi [lsearch -exact $argv {-csf}]] != -1} { set case_option "--" set argv [lreplace $argv $oi $oi]; incr argc -1 } else { set case_option "-nocase" } # Setup initial highlighting... if {[set oi [lsearch -exact $argv {-hp}]] != -1} { set hpattern [lindex $argv [expr $oi+1]] set highlighting 1 set argv [lreplace $argv $oi [expr $oi+1]]; incr argc -2 } else { set hpattern "" set highlighting 0 } set hnewpattern $hpattern if {[set oi [lsearch -exact $argv {-hc}]] != -1} { set hcolor [lindex $argv [expr $oi+1]] set argv [lreplace $argv $oi [expr $oi+1]]; incr argc -2 } elseif {[set hcolor [option get . hc Hc]] == ""} { set hcolor "red" } if {[set oi [lsearch -exact $argv {-csh}]] != -1} { set hcase_option "--" set argv [lreplace $argv $oi $oi]; incr argc -1 } else { set hcase_option "-nocase" } # Set logfile if {$argc == 1} { set logfile [lindex $argv 0] set gotfile 1 } elseif {$argc == 0} { set logfile "No File" set gotfile 0 } elseif {$argc > 1} { usage } # Titles, etc... wm title . "Termite" wm iconname . "Termite" # Buttons, info, etc... frame .f -relief raised -bd 2 pack .f -side top -fill both frame .f1 -relief groove -bd 2 label .f1.size .f1.size configure -text 0 label .f1.sizelabel -text "File Size: " pack .f1.sizelabel .f1.size -side left -fill both frame .f2 -relief groove -bd 2 label .f2.percent pack .f2.percent -side left -fill both frame .f3 -relief groove -bd 2 label .f3.name -textvariable logfile label .f3.namelabel -text "File: " pack .f3.namelabel .f3.name -side left -fill both pack .f1 .f2 .f3 -in .f -side right -padx 5 -pady 5 menubutton .f.file -text "File" -menu .f.file.menu menu .f.file.menu -tearoff 0 .f.file.menu add command -label "Open" -command {new_file} .f.file.menu add command -label "Save as" -command {save_file} .f.file.menu add command -label "Save selection" -command {save_sel} .f.file.menu add command -label "Print" -command {print_file} .f.file.menu add command -label "Print selection" -command {print_sel} .f.file.menu add command -label "Quit" -command {exit} menubutton .f.opts -text "Settings" -menu .f.opts.menu menu .f.opts.menu .f.opts.menu add command -label "Filter Settings..." -command {filter_dialog} .f.opts.menu add command -label "Highlight Settings..." -command {hilite_dialog} pack .f.file .f.opts -side left -padx 5 -pady 5 # Text pane... text .log -yscroll "set_percent" -relief ridge -bd 10 pack .log -side left -fill both -expand 1 # Scrollbar... scrollbar .scroll -command ".log yview" -width 20 pack .scroll -side right -fill y # Status frame frame .status -relief raised -bd 2 pack .status -side top -after .f -fill both checkbutton .status.autoscroll -text "Autoscroll" -variable autoscroll checkbutton .status.filtering -text "Filtering" -variable filtering \ -command {filter_toggle} checkbutton .status.highlighting -text "Highlighting" -variable highlighting \ -command {hilite_toggle} checkbutton .status.numbering -text "Line Numbers" -variable numbering \ -command {force_reread} pack .status.autoscroll .status.filtering .status.highlighting \ .status.numbering -side left -padx 2 -pady 2 -expand 1 set filesize 0 if {$gotfile} { # Read the file and display... force_reread } tkdesk-2.0/contrib/termite.README0100644000175000007640000000320610020457430014711 0ustar jccjccDate: Fri, 06 Feb 1998 14:28:14 -0800 From: Chris Coppick Sender: owner-tkdesk@mrj.com This posting is not strictly TkDesk related, but I'm contributing this script to the TkDesk distribution, or to whomever wants it. Termite is yet another graphical 'tail -f'-like application. It supports the following features: - Autoscroll to view new, dynamically added contents - Line numbering - Highlighting using a regular expression (e.g. 'error|warning|fail') - Filtering using a regular expression - File/Selection saving/printing It also supports the usual suspects for X resources, as well as resources that mirror the command-line options. As an example, the following command will continously display the end of my system messages file, highlighting errors, etc., in red: termite -hp 'error|warning|fail' -hc red -as /var/adm/messages In TkDesk, I use this as a drop target for my "sys admin'ish" appbar icon, as well as in the popup menu for files with names like "*.log", etc. I originally wrote this several years ago as one of my first Tcl/Tk attempts, then I dragged it back out, dusted it off, and added a few new features, so it consists of a mishmash of old and new code. If some of the code seems particularly silly, I'm sure that's the reason (ahem). I've only tested the new version under Tcl/Tk 7.6/4.2. If it doesn't work under 8.0, etc., feel free to fix it. :-) Enjoy. -- Chris ____________________________________________________________________________ "You're not allowed to call them dinosaurs anymore... It's speciesist. You have to call them pre-petroleum persons." -- Terry Pratchett, "Johnny and the Bomb" tkdesk-2.0/contrib/xemacs_load0100644000175000007640000000715210020457430014567 0ustar jccjccFrom - Tue Sep 8 07:59:38 1998 Return-Path: Received: from mail.wiesbaden.netsurf.de (bolik@localhost [127.0.0.1]) by localhost (8.8.5/8.8.5) with ESMTP id MAA00257 for ; Fri, 4 Sep 1998 12:51:08 +0200 Received: from shaknet.clark.net by wiesbaden.netsurf.de with esmtp (Smail3.1.28.1 #10) id m0zEfFF-001lhRa; Thu, 3 Sep 98 21:40 MET DST Received: (from root@localhost) by shaknet.clark.net (8.8.7/8.8.7) id NAA09972 for tkdesk-outgoing; Thu, 3 Sep 1998 13:21:33 -0500 Date: Thu, 03 Sep 1998 13:22:23 -0400 From: Daniel Martin at cush Subject: tkdesk Some contributed changes to xemacs_load To: tkdesk@shaknet.clark.net Message-id: <87d89d9kmo.fsf@cush.dyn.ml.org> MIME-version: 1.0 (generated by tm-edit 7.108) X-Mailer: Gnus v5.5/XEmacs 20.4 - "Emerald" Content-type: text/plain; charset=US-ASCII Lines: 76 Sender: owner-tkdesk@shaknet.clark.net Precedence: bulk Reply-To: Daniel Martin at cush X-Mozilla-Status: 8001 Daniel Martin at cush Majordomo 1.94.4 I've redone the xemacs_load function so that, in addition to doing what it currently does, it allows things like: xemacs_load gnus function xemacs_load "(switch-to-buffer-other-frame (generate-new-buffer \"New \ File\"))" lisp That is, you can pass lisp code or a function to xemacs_load. I find this very convenient, as I can then replace the communicator mail stuff that's on the Appbar's default mailbox menu with things that call up gnus, which is what I use. # --------------------------------------------------------------------------- # xemacs_load what ?where? # An improved proc provided by Erez Strauss (erez@newplaces.com) to # communicate with a running XEmacs. For this to work you need to put the # lines: # (gnuserv-start) # (require 'ange-ftp) # into your ~/.emacs. The second line allows for FTP through XEmacs. # $what is the file that is to be loaded, $where may be: same, other, frame. proc xemacs_load {what {where same}} { catch {auto_reset} if ![dsk_auto_execok "gnudoit"] { dsk_errbell cb_error "Couldn't find gnudoit. Maybe you don't have XEmacs installed?" return } switch $where { "same" { #default set gnudoitargs "(find-file \"$what\")" set xemacsargs "$what" # set func find-file # exec gnudoit -q (find-file \"$what\") } "other" { set gnudoitargs "(find-file-other-window \"$what\")" set xemacsargs "$what" # set func find-file-other-window # exec gnudoit -q (find-file-other-window \"$what\") } "frame" { set gnudoitargs "(find-file-other-frame \"$what\")" set xemacsargs "$what" # set func find-file-other-frame # exec gnudoit -q (find-file-other-frame \"$what\") } "scratch" { set gnudoitargs "(switch-to-buffer-other-frame \"*scratch*\")" set xemacsargs {*scratch*} # set func switch-to-buffer-other-frame # set what {*scratch*} # exec gnudoit -q (switch-to-buffer-other-frame \"*scratch*\") } "function" { set gnudoitargs "($what)" set xemacsargs "-f $what" } "lisp" { set gnudoitargs "$what" set xemacsargs "-eval $what" } } set err [catch { exec gnudoit -q $gnudoitargs }] if $err { if {[cb_yesno "XEmacs is not yet running on your display. Start it now ?"] == 0} { # start new xemacs eval dsk_exec xemacs $xemacsargs } } } tkdesk-2.0/doc/0040755000175000007640000000000010037657326011506 5ustar jccjcctkdesk-2.0/doc/Makefile0100644000175000007640000000072610020457430013132 0ustar jccjcc# # This Makefile is to generate ASCII text, HTML, and PostScript versions # of the SGML source of the TkDesk User's Guide. You will need the # LinuxDoc-SGML tools to run this yourself (linuxdoc*.rpm, or something # similar). # OUTS = guide.txt guide.html guide.ps all: $(OUTS) guide.txt: guide.sgml sgml2txt -f guide.sgml guide.html: guide.sgml sgml2html guide.sgml guide.ps: guide.sgml sgml2latex --output=ps --papersize=a4 guide.sgml clean: rm -f $(OUTS) tkdesk-2.0/doc/cd-tkdesk.10100644000175000007640000000215310037077413013430 0ustar jccjcc.\" .\" Copyright 1996 by Christian Bolik (Christian.Bolik@mainz.netsurf.de) .\" .TH TKDESK 1 "TkDesk 2.0, 04/15/2004" "" "" .UC 4 .SH NAME cd-tkdesk \- change directory of TkDesk's current file browser or list window .SH SYNOPSIS .B cd-tkdesk [ \fIdirectory ] .br .SH DESCRIPTION If invoked with no argument, cd-tkdesk returns the path of the directory currently displayed in TkDesk's active window. The active window is the file browser or list window that the mouse pointer was last in. .PP If .I directory is given to cd-tkdesk, it will change the directory of TkDesk's currently active window to its value. .PP Cd-tkdesk communicates with TkDesk through a TCP/IP server that needs to be started from within TkDesk using the menu entry "TkDesk Server" from TkDesk's "Options" menu. .SH OPTIONS .IP \fIdirectory The directory the currently active window of TkDesk should switch to. .SH "FILES" .IP tkdeskclient Usually in "/usr/local/bin", this program is used to perform the actual communication with TkDesk. .SH "SEE ALSO" tkdesk(1), ed-tkdesk(1), od-tkdesk(1) .SH "AUTHOR" Christian Bolik (Christian.Bolik@mainz.netsurf.de) tkdesk-2.0/doc/ed-tkdesk.10100644000175000007640000000212010037077413013424 0ustar jccjcc.\" .\" Copyright 1996 by Christian Bolik (Christian.Bolik@mainz.netsurf.de) .\" .TH TKDESK 1 "TkDesk 2.0, 04/15/2004" "" "" .UC 4 .SH NAME ed-tkdesk \- edit a file using TkDesk's editor .SH SYNOPSIS .B ed-tkdesk .RB [ \-q ] [ \fIfile ... ] .br .SH DESCRIPTION If invoked with no argument, ed-tkdesk will open a new TkDesk editor window without loading any file into it. .PP If one or more .I file arguments are given to ed-tkdesk, they will be loaded into a new TkDesk editor window. .PP Ed-tkdesk communicates with TkDesk through a TCP/IP server that needs to be started from within TkDesk using the menu entry "TkDesk Server" from TkDesk's "Options" menu. .SH OPTIONS .IP \-q Load files in the background, i.e. don't wait until associated editor window is closed but return immediately. .IP \fIfile The file that should be opened using TkDesk's editor. .SH "FILES" .IP tkdeskclient Usually in "/usr/local/bin", this program is used to perform the actual communication with TkDesk. .SH "SEE ALSO" tkdesk(1), cd-tkdesk(1), od-tkdesk(1) .SH "AUTHOR" Christian Bolik (Christian.Bolik@mainz.netsurf.de) tkdesk-2.0/doc/guide.sgml0100644000175000007640000023730310037101022013445 0ustar jccjcc
TkDesk User's Guide <author>Originally written by Christian Bolik, now maintained by J. Chris Coppick, <tt>jchris@users.sourceforge.net</tt> <date>Version 2.0, 15 April 2004 <abstract> TkDesk is a graphical, highly configurable and powerful desktop manager for UNIX and the X Window System. This document is meant to be a comprehensive guide to the functions, services and configuration possibilities offered by TkDesk. Please also take a look at the CHANGES file for latest news. A list of answers to frequently asked questions is also included. The TkDesk homepage is at <tt><url url="http://tkdesk.sourceforge.net">.</tt> </abstract> <!-- Table of contents --> <toc> <!-- Begin the document --> <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> Introduction <p> TkDesk is a graphical desktop manager for UNIX (with a slight emphasis on Linux, but it also runs just as well on AIX, Solaris, HP-UX, SGI Irix and other UNIX flavors) and the X Window System. It offers a very complete set of file operations and services, plus gives the user the ability to configure most aspects of TkDesk in a very powerful way. The reason for this is the use of the Tcl scripting language as the configuration and (for the greatest part of TkDesk) implementation language. This makes TkDesk not just configurable but truly <it>programmable</it>. TkDesk has been influenced by various other systems and file managers: NeXT, for laying out the file browser windows, Apple Finder, for the idea of file annotations and, (shock horror), Windows 95, for some other (of course minor and unimportant) inspirations. This is a brief overview of the most prominent features of TkDesk: <itemize> <item> Arbitrary number of automatically refreshed file browsers and file list windows, <item> Configurable file-specific popup-menus, <item> Drag and drop, <item> Files and directories may also be dropped onto the root window, a.k.a. desktop, <item> Configurable application bar, with several displays and cascaded popup menus for each button, files can also be dropped here, <item> History of visited directories, opened files, executed commands, and others, which is automatically saved to disk, <item> Find files through their annotation, name, contents, size, age, or other criteria, <item> A trash can for safe deletion of files and directories, <item> Calculation of disk usage for directory hierarchies, <item> All file operations (find, copy, disk usage, etc.) are carried out in the background, <item> Traversal of directory hierarchies through recursive cascaded menus, <item> Bookmarks, create menu entries for often used files/directories, <item> Comprehensive hypertextish online help, <item> Built-in multi-buffer and undo-capable editor, <item> Remote control of XEmacs, <item> Close coupling with Netscape Navigator for displaying HTML files or selected URLs, <item> Sound support, <item> Powerful on-the-fly configuration of nearly all aspect of TkDesk using Tcl/Tk, this also allows the Tcl-literate to extend TkDesk in arbitrary ways, <item> Free of charge! But see the file <tt>COPYING</tt>, or menu entry <tt>Help/License</tt> for information on usage and redistribution of TkDesk. </itemize> <!-- ********************************************************************** --> <sect1> Acknowledgments <p> Christian Bolik writes: TkDesk uses a number of other freely available packages without which TkDesk would not have been possible. I'd like to say many thanks to the following people: <itemize> <item> Chris Sterritt for setting up and managing the TkDesk mailing list (at the old location <tt><htmlurl url="mailto:majordomo@mrj.com" name="majordomo@mrj.com"></tt> <item> Alan V. Shackelford for seamlessly taking over the list to his machine and responsibility (the list is now at <tt><htmlurl url="mailto:majordomo@shaknet.clark.net" name="majordomo@shaknet.clark.net"></tt>), <item> Ken Hornstein for his wonderful netscape-remote package, <item> Ioi Kim Lan for making an XPM image reader for Tk available, <item> George Howlett for his great BLT, of which parts are used by TkDesk, <item> Michael McLennan for his massively useful <tt>[incr tcl]</tt>, <item> John Ousterhout for Tcl/Tk, which is definitely the best thing since sliced bread, <item> Greg Hankins and Matt Welsh for putting together the most wonderful linuxdoc-sgml package, <item> and of course, Linus Torvalds whose Linux kind of changed my life, really! </itemize> And a very big thank you to the growing TkDesk user community, which provides me with a constant flow of bug reports (getting less now <tt>:-)</tt>), suggestions for enhancements of TkDesk, and lots of motivation and encouragement. Special thanks to Chuck Robey for revising a previous version of this guide. <!-- ********************************************************************** --> <sect1> Using TkDesk's Help System <p> If you check menu entry <tt>Options/Use Netscape for Help</tt>, TkDesk will use that for displaying this User's Guide on-line. Otherwise, to reduce overhead, TkDesk uses its own help system. It features hypertext links, context sensitivity (which is not yet fully utilised by TkDesk) and full text search. The help window consists of four areas: <enum> <item> A <bf>listbox</bf> listing all the section headings. A section can be selected by pressing the left mouse button, <item> the <bf>text display</bf>, which contains the actual help text, <item> a <bf>text entry field</bf> in which a regular expression may be entered (such as <tt>[Ff]eatures</tt>). After hitting <tt>Return</tt>, the whole help text is searched for this expression. Pressing <tt>Return</tt> again continues the search, <item> three <bf>buttons</bf>: &dquot;Print&dquot; prints the complete help volume, &dquot;Back&dquot; jumps back after a hypertext link has been followed (see next paragraph), and &dquot;Close&dquot; to close the help window. </enum> Text that is displayed blue in the help window is a hypertext link. When the left mouse button is clicked over such a link the display will automatically change to the referenced section. You can jump back by pressing the &dquot;Back&dquot; button described above. The following keys are bound when the mouse pointer is inside the help window: <descrip> <tag><tt>Tab</tt></tag> Moves to the next section. <tag><tt>Shift-Tab</tt></tag> Moves to the previous section. <tag><tt>Control-Tab</tt></tag> Moves to the first section. <tag><tt>Control-Shift-Tab</tt></tag> Moves to the last section. <tag><tt>Up, Down</tt></tag> Scrolls one line up/down. <tag><tt>Page up, Page down</tt></tag> Scrolls one page up/down. <tag><tt>Control-Home</tt></tag> Jumps to start of help text. <tag><tt>Control-End</tt></tag> Jumps to end of help text. <tag><tt>Meta/Alt-b</tt></tag> Equivalent to pressing the &dquot;Back&dquot; button. <tag><tt>Meta/Alt-c, Escape</tt></tag> Equivalent to pressing the &dquot;Close&dquot; button. </descrip> <!-- ********************************************************************** --> <sect1> Command Line Options <label id="cmdopts"><p> Usually TkDesk is started simply by executing the command &dquot;<tt>tkdesk</tt>&dquot; from the shell prompt or your X initialisation file. However, you may specify the following options to this command: <descrip> <tag>-configdir <it>dir</it></tag> Reads the configuration from directory <it>dir</it> instead of <tt>~/.tkdesk</tt>. <tag>-default</tag> Reads the default configuration of TkDesk instead of the user's one in <tt>~/.tkdesk</tt>. <tag>-iconic</tag> Iconifies all file browser and file list windows created by TkDesk during start-up. <tag>-layout <it>file</it></tag> Reads the window layout information from file <it>file</it> instead of the default <tt>~/.tkdesk/_layout</tt>. <tag>-startdir <it>dir</it></tag> If this option is given, the first file browser will open with directory <it>dir</it>. <tag>-twm</tag> Don't use icon windows when file browser or list windows are iconified. Some window managers liek twm cannot handle these properly. </descrip> For example, the command &dquot;<tt>tkdesk -twm -iconic</tt>&dquot; tells Tkdesk to not use icon windows and start with all windows iconified. <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> The File Browser Window <p> The file browser window is the largest window when TkDesk is started for the first time. It contains a menu bar, a configurable button bar (for fast access to the most often used file operations and other functions), an entry field (displaying the current path), a horizontal scrollbar, a certain number of file listboxes (three by default), and a status and information bar. <!-- ********************************************************************** --> <sect1> The Menu Bar <p> The following sections describe the entries of the individual menus of the file browser windows. <!-- ********************************************************************** --> <sect2> TkDesk Menu <p> The &dquot;TkDesk&dquot; menu contains the following entries: <descrip> <tag>New Browser...</tag> Asks for a directory for which to open a new file browser window. If the directory is the empty string, the operation is cancelled. By default the checkbox &dquot;In Browser&dquot; is checked, but if you uncheck it a file list window will be opened instead. The button to the right of the text entry field gives access to a menu containing previously visited directories. <tag>Clone Window</tag> Creates a new file browser window with the same directory as the current one (that is, the directory of the file browser from which this menu entry was invoked). <tag>Display AppBar/Hide AppBar</tag> Opens the application bar if it had been closed before, or hides (closes) it otherwise. This menu entry's label changes based on whether the AppBar is currently displayed or not. <tag>Configuration</tag> This is a submenu from which individual or all configuration files of TkDesk can be opened to modify them. <tag>Auto Save</tag> Here you can select which parts of TkDesk are automatically saved periodically and when TkDesk exits. <tag>Save All Now</tag> Saves all parts of TkDesk's configuration no matter what the settings in the previous submenu. <tag>Close Window</tag> Closes the file browser window. <tag>Quit</tag> Quits TkDesk. </descrip> <!-- ********************************************************************** --> <sect2> File Menu <p> This menu provides all the usual file operations plus entries for finding files. The contained menu entries are: <descrip> <tag>Information</tag> Opens a file information window for each currently selected file. This window, which can also be used to add annotations to files, is described in section <ref id="fileinfo" name="File Information">. <tag>New File...</tag> Asks for the name of a file which will be created in the current directory of the file browser or list. If &dquot;Open in Editor&dquot; is checked the new file will automatically be opened in the configured editor. <tag>New Directory...</tag> Asks for the name of a directory which will be created in the current directory of the file browser or list. If &dquot;Open after Creation&dquot; is checked the current window will switch to the new directory after it is created. <tag>Open</tag> Opens the selected files using their default action, which corresponds to the first action defined in their popup menus as configured in the &dquot;Popups&dquot; configuration file. <tag>Print...</tag> Asks for a command to print the currently selected files. The names of the selected files will be appended to the command. The default command can be defined in the configuration file &dquot;System&dquot;. <tag>Copy, Move, Link...</tag> Opens a dialog box for copying, moving, linking, symbolic linking etc. of files. This dialog window is described in more detail in section <ref id="copydel" name="Copying, Moving and Deleting Files">. <tag>Rename...</tag> For each selected file, TkDesk asks for a new name. Before actually renaming TkDesk checks for an existing file with the same name. <tag>Delete...</tag> Opens a dialog to delete files. The section <ref id="copydel" name="Copying, Moving and Deleting Files"> gives more details. <tag>Find Files...</tag> Opens a dialog window which can be used to search for files, using a variety of characteristics. This dialog is described in more detail in section <ref id="findfiles" name="Finding Files">. <tag>Find Annotation...</tag> Lets you search for files with a certain annotation. More details in section <ref id="findfiles" name="Finding Files">. <tag>Copy To X Selection</tag> Copies the complete names of all currently selected files (i.e. including their paths) to the X clipboard. They can then be pasted into any other X application using the middle mouse button. <tag>Copy Names Only</tag> Same as previous one, but copies only the files' names (i.e. not their paths.) <tag>History</tag> Displays a submenu containing recently opened files. <tag>Close Window</tag> Closes the window. </descrip> <!-- ********************************************************************** --> <sect2> Directory Menu <label id="dir"><p> The main purpose of this menu is to select directories which are to be opened. It also manages TkDesk's trash can. The menu contains the following entries: <descrip> <tag>Open...</tag> Asks for a directory. A new file list or file browser window will be created, baesed on the setting of the &dquot;In Browser&dquot; checkbutton. <tag>New...</tag> Asks for the name of a directory which will be created in the current directory of the file browser or list. <tag>Home Directory</tag> Changes the directory of the window from which this entry is invoked to the user's home directory. <tag><it>A number of path entries</it></tag> These can be configured in the configuration file <tt>Directories</tt>. See section <ref id="config" name="Configuration of TkDesk"> for details on how to do this. If one of these menu entries is invoked, the path of the file browser will change to that directory. If such an entry is invoked while at the same time pressing the &dquot;Control&dquot; key, a new file list window will be created (if the option &dquot;Always In Browser&dquot; is selected a file browser window will be created), displaying the contents of the selected directory. <bf>This feature applies to all menus that contain directory names!</bf> <tag>Trees</tag> Contains two cascaded submenus: &dquot;Home&dquot; and &dquot;Root&dquot;. The contents of these submenus is dynamically generated and corresponds to the directory hierarchy rooted either at the user's home directory or at &dquot;/&dquot;. Selecting an entry from these submenus changes the directory of the window to the selected directory. Pressing <tt>Control</tt> at the same time opens a new window with this directory. <tag>Open Trash Can</tag> Opens a file list window displaying the contents of the trash can. When this window is iconified and TkDesk uses icon windows the icon can be used as a Mac-like trash can. <tag>Empty Trash Can</tag> Empties the trash can after confirmation from the user. <bf>This erases the files contained in the trash can for good!</bf> <tag>History</tag> Displays a submenu containing recently opened directories. </descrip> <!-- ********************************************************************** --> <sect2> Commands Menu <p> This menu provides entries to execute commands either once or periodically, edit files, and provides some very basic form of &dquot;job control&dquot;. Its entries are: <descrip> <tag>Execute...</tag> Asks for a command to execute. The button to the right of the command entry contains a history of previously executed commands. Selecting one of these copies its name to the entry widget. Checking the &dquot;View Output&dquot; button will open an editor window after the command completed displaying its output (stdout and stderr). <tag>Execute as root...</tag> The same as the previous entry but TkDesk will ask you for the root password before executing the command. The command will then run under root permission. <tag>Periodic Execution...</tag> Opens a window which can be used to execute a command and watch its output periodically. If the &dquot;Don't execute&dquot; checkbutton is selected, the execution is paused. <tag>Job Control</tag> Opens a window which allows to stop, terminate, kill etc. processes which have been started by TkDesk. <tag>Environment...</tag> Opens the &dquot;Edit Environment&dquot; dialog which can be used to display and modify TkDesk's environment variables. Programs and commands started from TkDesk after any modification in this dialog has been made will pick up the modified environment. <tag>Edit File...</tag> Asks for the name of a file to edit, and opens it in the editor you selected in the &dquot;System&dquot; configuration file (defaults to the built-in editor). The &dquot;Browse...&dquot; button opens a file selector from which a file may be selected. <tag>New File</tag> Opens a new blank editor window. <tag>Edit Selected</tag> Opens all currently selected files in one new editor window. <tag>Buffers</tag> Displays a submenu of all current editor buffers (both files and command output). <tag><it>A number of custom command entries</it></tag> These can be defined in the configuration file <tt>Commands</tt>. See section <ref id="config" name="Configuration of TkDesk"> for details on how to do this. If one of these menu entries is invoked, the corresponding command will be executed. <tag>History</tag> Displays a submenu containing commands recently executed. </descrip> <!-- ********************************************************************** --> <sect2> Bookmarks Menu <p> This menu lets you create bookmarks for often used files and directories. To do this, select at least one file/directory and invoke the &dquot;Add Bookmark&dquot; menu entry. The name(s) of the selected files and directories will now appear alphabetically sorted in the &dquot;Bookmarks&dquot; menu. You can remove any bookmark from the menu by selecting its corresponding entry from the &dquot;Remove Bookmark&dquot; submenu. The bookmarks you add will be automatically saved, if the &dquot;Bookmarks&dquot; entry of the &dquot;Auto Save&dquot; submenu contained in the &dquot;TkDesk&dquot; menu is selected (which is the default). <!-- ********************************************************************** --> <sect2> Options Menu <p> The <tt>Options</tt> menu lets you configure many aspects of TkDesk &dquot;on the fly&dquot;. The entries are: <descrip> <tag>Long Listing</tag> Select this to let the details of all files and directories be displayed in the file listboxes. This slightly decreases performance and for that reason is switched off by default. <tag>Show All Files</tag> Whether to show files in the file lists whose name start with &dquot;.&dquot;. <tag>Add Icons</tag> If selected, small icons will be displayed to the left of the file names in the file listboxes. Turn this off for faster scrolling. <tag>Folders On Top</tag> If selected (default) folders will always appear on top of the file lists. <tag>Append Type Char</tag> Whether to append a file-type specific character to the file names. This is mainly intended for monochrome displays and is then automatically selected. <tag>Single Click (Dirs)</tag> Lets you open directories with a single click of the left mouse button. Individual directories can still be selected by pressing <tt>Control</tt> at the same time. <tag>Always In Browser</tag> If this option is selected, the &dquot;In Browser&dquot; checkbutton of the &dquot;Open Directory&dquot; dialog will always be selected. <tt>Control-Doubleclick</tt> on a directory will open a file browser instead of a file list window. <tag>Status in List Windows</tag> Whether the singly columned file list windows should have a status bar as well. <tag>Sort by ...</tag> Sort all file listboxes by one of these criteria: Name, Name (fold, meaning that upper and lower case characters will be &dquot;folded&dquot;, i.e. disregard case when sorting), Size, Date, Extension, or Don't Sort to display directory entries as they are read from disk. <tag>Strip <your home directory></tag> If this is selected, and the current path of the browser is somewhere under your home directory, the leftmost file listbox will contain your home directory rather than the root directory. This speeds up the display of directory hierarchies under your home directory. <tag>Overwrite Always</tag> If this option is selected, TkDesk won't ask if the destination file already exists when copying or moving files. <tag>Really Delete</tag> Relates to the deletion of files. If this option is selected, the &dquot;Delete Files&dquot; dialog box will always have the &dquot;Delete permanently&dquot; checkbutton selected by default. <tag>Quick Drag'n'Drop</tag> Normally when you drop files onto a file list, the copy dialog appears. If this option is selected, dropped files will be moved to the destination directory without further questions. If <tt>Control</tt> is pressed during dropping, the files will be copied. If the drop target is the trash can, TkDesk will ask if the files are to be deleted &dquot;really&dquot;. <tag>Sort History</tag> This option determines whether the history menus are to be sorted alphabetically or not. <tag>TkDesk Server</tag> Start or stop the built-in server to remote execute TkDesk commands. See also section <ref id="server" name="Using the TkDesk Server">. <tag>Dialogs at Pointer</tag> If selected, TkDesk will always try to place new windows right under the mouse pointer. <tag>Autoraise AppBar</tag> Whether to raise the AppBar above any obscuring window when it is entered by the mouse pointer. <tag>Use Sound</tag> If you have sound working with TkDesk on your machine, you can temporarily disable sound by selecting this option. Handy when playing Audio CDROMs, for instance. <tag>Number Of Listboxes</tag> This relates to the number of listboxes in the file browser window. Between 1 and 6 can be displayed, though 18 would theoratically not be a problem. <tag>Balloon Help</tag> Whether to display a small window at the mouse pointer if it is placed over part of TkDesk, e.g. an AppBar button. <tag>Use Netscape for Help</tag> Whether to use Netscape rather than the built-in help viewer for displaying TkDesk online help. </descrip> The settings of these options are by default automatically saved to <tt>~/.tkdesk/_options</tt>. This can be disabled by deselecting the &dquot;Options&dquot; entry of the &dquot;TkDesk/Auto Save&dquot; submenu. <!-- ********************************************************************** --> <sect2> Help Menu <p> This menu tries to give you some help with using TkDesk. It also contains entries for displaying the list of FAQs, recently made changes to TkDesk, and the license for using and distributing TkDesk: <descrip> <tag>User's Guide</tag> Displays the TkDesk User's Guide either in the built-in help viewer or using Netscape (see previous section). <tag>Manual Page...</tag> Asks for a command for which to display the system's manual page in an editor window. <tag>Getting Started</tag> Displays the &dquot;Getting Started&dquot; guide. <tag>TkDesk FAQ</tag> Jumps directly to the FAQ section of the TkDesk help volume. <tag>Changes</tag> Displays recent changes to TkDesk. Make sure you invoke this entry after upgrading to a newer version of TkDesk. <tag>License</tag> Restates that TkDesk is distributed under the GNU Public License (GPL). <tag>About TkDesk...</tag> Some info about TkDesk (version, author etc.), plus a link to the TkDesk web page. </descrip> <!-- ********************************************************************** --> <sect1> The Button Bar <p> The button bar that's located right underneath the menu bar allows for fast access to any of the commands of TkDesk. It's also possible to configure each button individually to run customized Tcl scripts, that e.g. work on the currently selected files. By default, the buttons in the button bar provide access to the more often used functions of TkDesk (copy, create, delete etc.). When you place the mouse button over any of these buttons, a small help window will appear telling you what this button does (provided the &dquot;Balloon Help&dquot; option is activated). The contents of this button bar can be defined via the configuration file <tt>ButtonBar</tt>. See section <ref id="config" name="Configuration of TkDesk"> and the configuration file itself for details on how to do this. Note that by not defining the variable <tt>tkdesk(button_bar)</tt> in that file or by not defining <tt>tkdesk(small_button_bar)</tt> the button for file viewer or list windows may be surpressed respectively. <!-- ********************************************************************** --> <sect1> The Path Entry <p> This entry field displays the current path of the file browser or file list. You can also type directly into this field to change the display to another directory. There is a sort of &dquot;auto-completion&dquot; available; if you type only the first part of a directory's name and press <tt>Control-Tab</tt>, its name will be automatically completed as far as there's no ambiguity. If you click the right mouse button over this field a popup menu appears which lets you select any of the current directory's parent directories and their subdirectories. You can also open files directly from this popup menu. The button to the right of the entry field contains a menu of the last 30 (default value this may be changed in the &dquot;System&dquot; configuration file) directories you have visited. If you select one of these, the current path of the browser will change to it. The &dquot;Control-trick&dquot; to open the selected path in a new list window that's been described in section <ref id="dir" name="Directories"> works here as well! <!-- ********************************************************************** --> <sect1> The File Listboxes <p> The main part of the file browser and list windows is taken up by the file listboxes. Directories are by default displayed in blue with a bold font, executable files in red and bold, and regular files in black and a medium font. These settings as well as the color, font and icons used for individual file types may be configured via the <tt>FileTags</tt> configuration file. See section <ref id="config" name="Configuration of TkDesk"> for details on how to do this. Right above each listbox the name of the directory whose contents it shows is displayed, together with the current file mask settings. Clicking on this label reveals a menu with the following entries: <descrip> <tag>Refresh</tag> Refreshes the listbox contents. Although TkDesk refreshes all its file lists every 5 seconds (default), sometimes you may want to have a file listbox instantaneously updated. <tag>Set Mask...</tag> Sets a mask for this listbox's display. For example, you can configure the listbox to only display or select files matching the pattern <tt>*.gif</tt>. Multiple masks separated by spaces may be entered to display files matching any of these. A history menu button for reusing masks previously entered is also provided, as well as a checkbutton for inverting the mask's effect (i.e. only files <bf>not</bf> matching the given mask will be displayed). <tag>No Mask</tag> Removes a previously activated file mask. <tag>Disk Usage</tag> Calculates the directory's disk usage, i.e. the number of kilobytes (default) occupied by that directory and all its subdirectories. A window will appear when the calculation is complete, containing a sorted list of all subdirectories and their individual disk usages. If you double-click on any of its entries, a new file list window will appear displaying the directory's contents. Clicking the right mouse button over any of the directories displays its associated popup menu. <tag>Free Space</tag> Displays the space available in the file system of this listbox's directory, using TkDesk's &dquot;Periodic Execution&dquot; window. <tag>Execute here...</tag> Asks for a command to execute in the directory displayed by this listbox. All the usual TkDesk %-sequences to work with the names of selected files may be used, e.g. <tt>%A</tt> to pass all currently selected files to the command. <tag>Execute as root...</tag> The same as the previous one, except the command will be run under root permission after asking you for the root password (and you entered the correct one...). <tag>Long Listing</tag> Instead of just the file names displays a &dquot;long listing&dquot; i.e. a listing containing all the details such as size, permission bits, date and time of last modification etc. <tag>Show All Files</tag> Whether to display files starting with a dot (&dquot;.&dquot;) or not. <tag>Inverse Order</tag> Whether to invert the current sorting order. <tag>Sort by...</tag> This is identical to the entry of the same name in the menu bar's &dquot;Options&dquot; menu, but affects this single listbox only. <tag>Open List Window</tag> If this listbox is part of a file browser window, open a new list window displaying this listbox's directory. <tag>Open Browser</tag> Else, open a browser with this directory. </descrip> This menubutton can also be used to drag and drop files, as well as to drag the directory displayed in the associated listbox to another target, e.g. the root window by pressing the middle mouse button over the listbox title menu button. <!-- ********************************************************************** --> <sect2> Handling of the Listboxes (Bindings) <label id="bind"><p> The handling of the file browser windows is very similar to that of the NeXT file manager: when you double click on a directory, the listbox to the right of the current one will display this directory's contents. You can open a directory in this listbox, and the listbox to the right of this one will display its contents. This way you can browse through complete directory hierarchies, while having instant access to the contents of the complete directory tree. When the number of opened directories exceeds the number of visible listboxes, you can scroll the listboxes with the horizontal scrollbar which is located right above the file listboxes. Files are selected in the file listboxes using the left mouse button. A single click selects a single file, deselecting all other files. If the Control key is pressed simultaneously, the old selection will be retained. By dragging the mouse pointer over a file list, while at the same time pressing the left mouse button, a set of files can be selected. Shift-doubleclick selects all files in that file list. Holding down the shift button extends the current selection unto that file (a.k.a. &dquot;range-selection&dquot;). Note that if the option &dquot;Single Click (Dirs)&dquot; is set in the &dquot;Options&dquot; menu a single click on a directory will suffice open it. In this case you need to use Control-Click to select a single directory. A double click on any listbox item performs its default action. For directories this is to open it, for executables this is to execute it, and for files it invokes the first entry of the corresponding popup menu as defined in the &dquot;Popups&dquot; configuration file (see below). If the Control key is pressed while double-clicking a directory, a new file list or file browser window (depending on the setting of the &dquot;Always In Browser&dquot; option) is created displaying the contents of that directory. For files, a dialog box will appear asking for a command to execute on that file. Files can be dragged by pressing the middle mouse button over any selected file. If no file is selected, the clicked-over file will be selected. Files can be dropped by releasing the mouse button over any other file listbox and the menubutton above them, over windows of the built-in editor, over the application bar (if the corresponding button has been configured to handle dropped files), over iconified file browsers and file lists and in general over any other application supporting the <tt>file</tt> protocol of the BLT package's drag and drop implementation. Valid drop targets can be identified by looking at the window that is displayed while files are dragged: the window's relief appears raised if over a valid target, flat if not. The right mouse button is used in the file listboxes to access the file-specific popup menu. Every popup menu contains a submenu, labeled with the file's name, which contains entries for the most common file operations. The remaining entries of the menu can be configured via the configuration file <tt>Popups</tt>. See section <ref id="config" name="Configuration of TkDesk"> for details on how to do this. The file listboxes may also be fully controlled using the keyboard: <descrip> <tag>Up-/Down Arrow</tag> Move the selection one file up/down. <tag>Page Up/Down</tag> Move the selection one page up/down. <tag>Home/End</tag> Move the selection to first/last item in the listbox. <tag>A-Z</tag> Select the first file matching the character pressed, cycle through subsequent matches on repeated pressing of the same key. <tag>Return</tag> Open selected file. <tag>Menu (the key left to the right Control key on W95 keyboards)</tag> Display the currently selected file's popup menu, the menu itself may also be operated using Up/Down, Return, and Esc. <tag>Backspace</tag> Change directory to the parent directory. <tag>F11</tag> Open the current file listbox's menu. </descrip> <!-- ********************************************************************** --> <sect1> The Status Bar <p> The status bar is located at the bottom edge of file browser windows. If &dquot;Status in List Windows&dquot; is selected in the &dquot;Options&dquot; menu file list windows will contain a status bar as well. It displays either <itemize> <item> the current state of TkDesk, <item> the amount of free space in the current file system, <item> details about the currently selected file, or <item> number and total size of currently selected files. </itemize> <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> The File List Window <p> The file list window is basically a more compact version of the file browser window. It displays only one listbox, which results in the fact that the contents of opened directories are always displayed in the same listbox, and it doesn't have a status bar unless &dquot;Status in List Windows&dquot; is set in the &dquot;Options&dquot; menu. Its advantage is that it is more quickly created than a file browser window and occupies less space on your screen. My usual setup is that I have one file browser and lots of file lists opened. The menu bar of the file list windows is also more compact, but allows access to all menus of the file browser through submenus of the &dquot;Others&dquot; menu. This menu contains one additional entry to open a file browser window displaying this file list's directory. <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> File Operations <p> <!-- ********************************************************************** --> <sect1> File Information <label id="fileinfo"><p> The &dquot;File Information&dquot; window displays detailed information about a file. It can also be used to change the owner or access-permissions of a file. In addition, an annotation to a file can be added here. The following information is displayed: <descrip> <tag>Path</tag> The path of the file. <tag>Size</tag> The size of the file in bytes. <tag>Modified</tag> When the file was last modified. By clicking on the button displaying the date file can be &dquot;touched&dquot;, ie. their modification timestamp will be set to the current time. <tag>Owner</tag> Displays the owner of the file. By clicking the button displaying the owner, the owner can be changed to another user. <tag>Group</tag> Displays the group ownership of the file. By clicking the button displaying the group's name, the group can be changed. <tag>Mode</tag> Here the access permissions are displayed in &dquot;ls -l&dquot; style. The first three button correspond to the rights of the owner (r: may read, w: may write, x: may execute or open the directory), the second three buttons to the rights of the group, and the last three buttons to every one else's rights. The &dquot;x&dquot; buttons are special in that they cycle through four settings: <tt>x</tt> for executable, <tt>s</tt> for set user/group id <it>and</it> executable, <tt>S</tt> for set id only, and <tt>-</tt> for not executable. Note that using the right mouse button toggles between <tt>-</tt> and <tt>x</tt> only. Clicking the middle mouse button over any of these buttons copies its current setting to the corresponding button in the other two groups. If the settings of any of these buttons is changed, the &dquot;Change Mode&dquot; button at the bottom edge of the window must be clicked to actually change the file's permissions. <tag>Links</tag> Number of (hard) links to this file. <tag>Type</tag> Tries to give some information about the file's contents. TkDesk uses the shell command <tt>file</tt> for this. </descrip> In the &dquot;Annotation&dquot; text field you can enter any remarks about the file. This annotation will be saved when the &dquot;Close&dquot; button is pressed. Files having annotations attached to them will appear underlined in the file listboxes, and their popup menu will contain the first few words of that annotation as an &dquot;informative&dquot; menu entry. If the window displays information about a file an additional button labeled &dquot;Disk Usage&dquot; is provided, which calculates the disk usage of the hierarchy rooted at that directory. The entries of the &dquot;Disk Usage&dquot; window can be double-clicked to open new file list windows. <!-- ********************************************************************** --> <sect1> Copying, Moving and Deleting Files <label id="copydel"><p> These functions can be accessed from the &dquot;File&dquot; menu, among others. There is one dialog for copying, moving and linking files (plus an arbitrary number of user-defined actions such as diff and patch, as configured in the &dquot;Popups&dquot; config file), one dialog for renaming files (which does nothing else than moving the file to its new name), and one dialog for deleting files. The &dquot;Rename&dquot; dialog is very straight-forward and probably does not need further explanation. The &dquot;Copy etc.&dquot; dialog contains the obvious source and destination entry fields, plus a checkbutton labeled &dquot;all selected files&dquot; if more that one file was selected when this dialog was opened. If this checkbutton is selected (default) the operation will be applied on all selected files. If it is not checked, each file will be handled individually. The &dquot;Skip&dquot; button can then be used for skipping individual files. The &dquot;Delete&dquot; dialog also contains this checkbutton, plus a checkbutton labeled &dquot;Delete permanently&dquot;. If this checkbutton is selected, the files will <it>not</it> be moved to the trash can but will be <bf>really and ultimately deleted!!</bf> The default setting of this checkbutton can be set from the &dquot;Options&dquot; menu. <!-- ********************************************************************** --> <sect1> Finding Files <label id="findfiles"><p> The &dquot;File&dquot; menu contains two entries for finding files: &dquot;Find Files...&dquot; and &dquot;Find Annotation...&dquot;. Files can be annotated through their &dquot;Info&dquot; dialog (accessible from the &dquot;File&dquot; menu or from their popup menu), see section <ref id="fileinfo" name="File Information">. &dquot;Find Annotation&dquot; enables you to look for an annotated file whose annotation matches a certain regular expression (which can be as simple as an ordinary string). &dquot;Find Files&dquot; lets you look for files (or rather: let's you instruct TkDesk to look for files) whose names match one or multiple patterns, which are of a certain type (such as directory), which contain a certain string, which are smaller or bigger than a certain number of kilobytes or which are younger/older than a certain date. All fields which are not left blank in this dialog will be combined with a logical AND. This dialog is currently the only one utilizing the balloon help capability of TkDesk, so for now I would like to refer you to this. For instance, if you want to know how to enter the file size you're looking for, place the mouse pointer over the &dquot;Size&dquot; entry field without moving it for a few seconds. Both &dquot;Find&dquot; dialogs display their results in the same file listboxes that are used by the file list and browser windows, so the same bindings described in section <ref id="bind" name="Handling of the Listboxes (Bindings)"> apply here as well! <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> Cascading Directory Popup Menus <p> Ugh, what a title... However, one of the most powerful features of TkDesk are its &dquot;cascading directory popup menus.&dquot; These are menus that start from a given directory, contain submenus for each subdirectories (and possibly menu entries for files), and that dynamically add submenus as you traverse the menu. Hm, kinda hard to explain, but pretty cool. These menus are the ones that are accessible through menu entries &dquot;Directory/ Trees/ Home&dquot; and &dquot;Root&dquot;, by clicking the right mouse button in the file browser and list window's path entry field, and through the popup menus of directories (submenu &dquot;Subdirectories&dquot; and &dquot;... and Files&dquot;). They may also appear in other places, depending on your configuration of TkDesk. There are two special bindings available in these menus: <descrip> <tag>Control-Mousebutton-Release</tag> On a menu entry associated with a directory this opens a new file list window displaying this directory's contents. On a file it asks for a command to run on this file by displaying the &dquot;Open or Execute&dquot; dialog. <tag>Left-and-Right-Mousebutton-Press</tag> If the left and right mousebuttons are pressed simultaneously over a menu entry, the menu disappear, the popup menu for the associated file or directory is displayed. </descrip> Otherwise the directory or file that's selected from the menu is opened using its default command. <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> The Application Bar <p> TkDesk provides you with an application bar for fast access to your favorite applications, commands, directories etc., plus displays for the current date and time, system load, and the status of your mailbox and dialup link. It consists of an arbitrary number of buttons. Each button (also the aforementioned displays) contains a popup menu which can be accessed by pressing the right mouse button over any of them. If you single-click the left mouse button over such a button, the first entry from the corresponding popup menu defining a command to execute will be invoked. The first button (displaying a small desk by default) could be called TkDesk's &dquot;Start Button&dquot;. Its popup menu contains entries for accessing TkDesk's most often used functions, such as executing a command or opening a file list or browser window, plus submenus for your bookmarks, the files you most recently opened, and the directories you've last visited. The next entry is a submenu labeled &dquot;Application Bar&dquot;. Here you can configure all aspects of the application bar, especially its <bf>position and orientation</bf>. See my answer to the MFAQ <ref id="apppos" name="How can I change the position of the application bar?"> for more. The last entry labeled &dquot;Configuration&dquot; contains another submenu which gives you fast access to TkDesk's configuration files. The second button gives you access to this User's Guide, and lets you view manual pages, also making use of a running TkMan (which is a hypertextified manual pager which is to be highly recommended). This button also allows you to drop executables on it to automatically display their manual page, if they have got one. Note that the default value of the &dquot;Manual Page&dquot; dialog is the contents of the current X selection, so this can be used for some sort of &dquot;context sensitive&dquot; help. Keep in mind that all the buttons can be configured by you! See section <ref id="config" name="Configuration of TkDesk"> for details on how to do this. <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> The Built-in Editor <label id="editor"><p> The built-in editor of TkDesk is meant to be a simple ASCII editor for editing files smaller than, say, 500kB. A single editor window can handle an arbitrary number of buffers each of which contains another file. Files can either be loaded through the &dquot;File&dquot; menu of the editor or by simply dropping one or more files on the text field of the editor window from one of the file listboxes. An editor window contains the following menus in its menu bar: <descrip> <tag>File</tag> Contains entries to load, save, and print files, and to close the current buffer, current window, or all windows. <tag>Edit</tag> The first entry provides access to the editor's &dquot;Undo&dquot; functionality. Note that the undo buffer may contain a maximum of 500 events. This menu also contains entries for managing TkDesk's own text clipboard. Also provides entries to search for text (regular expressions), replace text, or find the next occurence of the currently selected text. The entry &dquot;HyperSearch&dquot; is a special form of search: all matching lines are displayed in a file listbox. If one of its entries is clicked on, the editor automatically displays that line at the top of the text field. This can be useful for jumping to headings, function definitions etc. Oh yes, and the regular expressions entered here are also saved with the other histories. <tag>Options</tag> The &dquot;Auto Indent&dquot; option determines whether the cursor is automatically indented after hitting <tt>Return</tt>. If the &dquot;Send to Netscape&dquot; option is set, the current file will be loaded by (maybe a running) Netscape each time it is saved. Useful when editing HTML files. <tag>Buffers</tag> Lists the buffers of the editor window. Buffers can be selected from this menu. <tag>Configuration</tag> This menu is only available when editing one of TkDesk's configuration files. It lets you save the file and reload it into TkDesk by selecting the corresponding menu entry or pressing <tt>F5</tt>, or exactly the same plus closing the buffer by invoking the next entry or pressing <tt>F6</tt>. This way configuration files can be edited and reloaded very quickly into TkDesk. </descrip> The text area provides all the more common Motif- and Emacs-like key-bindings (including <tt>Control-Space</tt> for selecting an area of the text). Maybe it should be mentioned that <tt>Control-C/X/V</tt> do not work on the X selection but on TkDesk's own clipboard. Use the middle mouse button to paste from the X selection into TkDesk or from TkDesk into another application. <!-- ********************************************************************** --> <sect1> Marks <p> Marks can be set with <tt>Control-[1-9]</tt>, and be jumped to with <tt>Alt/Meta-[1-9]</tt>. You can always jump back with <tt>Control-0</tt>. They work across buffer and editor window boundaries, and are currently only valid as long as the buffer in which the mark was set is open. <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> Using the TkDesk Server <label id="server"><p> In order to allow remote control of TkDesk, i.e. to allow programs outside of TkDesk to have TkDesk perform certain tasks, TkDesk implements a TCP/IP server that can be used to send Tcl scripts to TkDesk. Whether this server is &dquot;up&dquot; or not is determined by the option &dquot;TkDesk Server&dquot; that is to be found in the &dquot;Options&dquot; menu. Currently, only programs running on the same machine as TkDesk may connect to the server, but multiple TkDesk servers started from different users may be running at the same time. The server is a much faster way to send command to TkDesk then to use the Tcl/Tk <tt>send</tt> command, as that requires a second Tcl/Tk shell (&dquot;wish&dquot;) to be started to do the send. However, using a TCP/IP server that's basically system-wide accessible to perform arbitrary command under the accout of the user who started TkDesk and thus the server brings with quite a big security risk that TkDesk tries to reduce by keeping the port the TkDesk server uses secret. The port to be used is calculated randomly at server start-up, and is saved into a file that can only be read by the user who started TkDesk. To prevent &dquot;guessing&dquot; of the port number a generated key is also stored in this file that get passed to the server. The client performing the communication with the TkDesk server gets installed with TkDesk; its name is <tt>tkdeskclient</tt>. This command expects exactly one argument which will be directly sent to the server and evaluated there as a Tcl script. E.g. you could do a <tscreen><verb> tkdeskclient "dsk_view id" </verb></tscreen> to find out who you are <tt>:-)</tt>. Along with TkDesk a number of front-end shell scripts for <tt>tkdeskclient</tt> are installed, which comprise of the following: <descrip> <tag>cd-tkdesk <it>?path?</it></tag> Let's the currently active TkDesk file list or browser window (i.e. the one the mouse pointer was over last) change its directory to <it>path</it> if <it>path</it> is given, or returns the current directory of the active window. <tag>ed-tkdesk <it>?+linenum? ?file? ...</it></tag> Without argument opens a new editor window, or loads all files given as arguments into the editor (and into the same window if you're using a multi-buffer capable editor, such as the built-in one). If a file is preceded by <it>+linenum</it>, i.e. something like <it>+20</it>, the built-in editor will position the cursor on the line when displaying the file that's given in the following argument. <tag>od-tkdesk <it>?dir?</it></tag> If no arguments are given opens a file list window for the current directory of the shell/program where the command was issued, or opens a window for the directory specied in <it>dir</it>. <tag>op-tkdesk <it>?file ...?</it></tag> For each <it>file</it> performs its default action (the one defined first in its corresponding popup menu as defined in &dquot;Popups&dquot;), or asks for a command to execute if no files are given. <tag>pop-tkdesk <it>file</it></tag> Displays the popup menu for <it>file</it>, as defined in the &dquot;Popups&dquot; config file. The popup menu may also be controlled by the keyboard: Up, Down, Return, and Esc keys do what you would expect. </descrip> Note that all of these scripts are merely examples of possible usages of <tt>tkdeskclient</tt>. Use your imagination to find new and even more exciting applications! <tt>:-)</tt> <!-- ********************************************************************** --> <!-- ********************************************************************** --> <sect> Configuration of TkDesk <label id="config"><p> Currently, TkDesk can be configured only by editing ASCII files. This is not necessarily a drawback, because this way you can add complex Tcl/Tk procedures to the configuration files. GUI based configuration is planned for one of the next releases. If you don't know Tcl/Tk: Don't despair! For the biggest part of TkDesk's configuration files it is absolutely not necessary for you to know how to program in Tcl/Tk, since you just have to modify values or extend the examples I and others have provided. And to those who want to exploit all of the power available by using Tcl/Tk as TkDesk's configuration language, please have a look at the answer to FAQ <ref id="tcltk" name="Where can I find out more about Tcl/Tk?">. The configuration files can be accessed from the <tt>TkDesk</tt> menu via the <tt>Configuration</tt> submenu, or from the &dquot;Configuration&dquot; submenu of the popup menu of the application bar's first button. The built-in editor will then appear with the selected configuration file(s) already loaded. Each configuration file contains pretty detailed comments on what the individual settings are for, and how they can be modified. Well, at least they contain examples, which can guide your modification attempts. Once you have modified the configuration file, you can save it and reload it into TkDesk by making use of the entries of the editor's &dquot;Configuration&dquot; menu described in section <ref id="editor" name="The Built-in Editor">. Use F5 to save the file and reload it into TkDesk, and F6 to do the same but additionally close the configuration file. As already mentioned, each configuration file contains directions on how to modify its definitions. The general advice is: Simply look at the example definitions, because these make things a great deal clearer than any explanation can. <bf>Tip:</bf> In case your looking for the definition of a specific part of TkDesk, e.g. for the popup menu of <tt>pdf</tt> files, use the &dquot;Find...&dquot; entry in the &dquot;Configuration&dquot; menu. Just enter <tt>pdf</tt> in the &dquot;String:&dquot; field, hit enter, and double-click the found file. <sect1> Configuration Files <p> All of these are &dquot;sourced&dquot; (i.e. processed) by TkDesk during start-up. This happens in the following order: <enum> <item> System <item> ButtonBar <item> Preferences <item> FileTags <item> Commands <item> Directories <item> Popups <item> AppBar <item> Sounds <item> Local (if existent) </enum> <sect2> AppBar <label id="appbar"><p> This configuration file lets you define all aspects of the application bar (apart from its layout, use the entries of the submenu &dquot;Application Bar&dquot; of the desk-button's popup menu for this). This is what you can define here: <itemize> <item> If the application bar should be managed by the window manager just as an ordinary window. This is disabled by default. <item> The maximum number of buttons in a column (vertical layout) or row (horizontal layout). Yes, the application bar can contain several columns or rows! <item> The fonts to use for the time and date display. <item> Period in seconds after which to update the system load and mailbox displays, where to check for new mail, plus several additional mail related options. <item> Icons and commands to use for the dial-up networking button. <item> Icons and fonts to use for the trash button, and whether to display its current disk usage in the appbar. <item> The actual button definitions, ie. which image to display, and the entries of the (possibly cascaded) popup menu. See below for the syntax used. </itemize> <sect2> ButtonBar <p> This file defines the contents and appearance of the Button Bar, which is displayed underneath the menu bar of TkDesk's file browser (<tt>tkdesk(button_bar)</tt>) or file list windows (tkdesk(small_button_bar). Both button bars are configured independently from one another. The third list defined here (<tt>tkdesk(dir_button_bar)</tt>) is for directory-specific button bars which will be dynamically displayed and hidden when the respective directory is entered or left. These will be displayed in addition to the standard button bar. <sect2> Commands <p> In this file you can define entries which will be added to TkDesk's &dquot;Command&dquot; menu, for example to uncompress all selected files, manage RPM package files, or do whatever you can possibly imagine. <sect2> Directories <p> Here you can define entries and cascaded submenus of directories which will be added to TkDesk's &dquot;Directories&dquot; menu. More details are contained in the &dquot;Directories&dquot file itself. You can also define a list of directories whose parent directories are not to be displayed in file browser windows, as is usually the case with your home directory. This can speed up display of directories underneath an AFS directory, for instance. Another list lets you define action that are to be performed when a specific directory is opened. This can be used for example to auto-mount floppies and CDROM's. <sect2> FileTags <p> Contains definitions for color and font of specific file types (as distinguished by their extension) as well as standard values for regular files, executables, and directories, that will be used in TkDesk's file lists. Also the icons displayed when the &dquot;Add Icons&dquot; option is selected are configured here, as well as the icons used for the desk items, i.e. files and directories which have been dropped on the root window. <sect2> Local <p> This file does not exist when TkDesk gets installed, but still appears in the &dquot;Configuration&dquot; menu. This is because if it exists it will be sourced during start-up of TkDesk, so this is the ideal place to put local extensions of TkDesk. <sect2> Popups <p> The popup menus that appear when the right mouse button is clicked over a file or directory are defined here. There are individual definitions for the popup menus of directories, executables, and of other files. To find the correct popup menu for a file TkDesk checks the mask of each entry of a popup list one after the other from the beginning of the list to its end, so the masks should become more general towards the end of the list. An additional popup list (<tt>tkdesk(fileops,popup)</tt>) may be defined here for use in the &dquot;Copy, Move, etc.&dquot; dialog. <sect2> Sounds <p> Here the command to be used for playing sounds can be defined, as well as the sound files which are to be played at certain events. TkDesk comes with a few AU sound files which are located in the <tt>sounds</tt> subdirectory of TkDesk's library directory. You can find out where this is by looking at the fifth line of the <tt>tkdesk</tt> script. <sect2> System <p> All the more &dquot;basic&dquot; features of TkDesk can be configured here. These are: <itemize> <item> Colours and fonts to be used by TkDesk, <item> Default size of file listboxes, <item> Definitions of shell commands which are used by TkDesk to copy, move, delete, etc. files. These should be fine as they are in most cases, but you may want to tweak them for your system, so take a look at these definitions. <item> The default command for printing, <item> Which editor to use, plus settings for the built-in editor, <item> Periods for automatic refreshing of the file lists and saving of TkDesk's configuration, <item> Maximum number of entries in history menus, <item> If TkDesk should ask for confirmation when the user wants to quit TkDesk, <item> If TkDesk should allow the menus to be &dquot;tearoff-able&dquot;, <item> The paths in which TkDesk will look for images and sounds used in the configuration files, <item> Icons to use for file list and browser windows, and for the help window, <item> Whether to use &dquot;focus follows mouse&dquot; default or not, <item> Settings for the desk items, e.g. whether to let them be managed by the window manager, <item> Commands to execute after start-up and before shutdown (of TkDesk). </itemize> <sect1> The TkDesk API <p> Many of the values to be specified for TkDesk's configuration files are Tcl scripts. Also, you can define your own Tcl proc's to use in these scripts. To allow better control of TkDesk, TkDesk externalizes a couple of proc's which could be called the &dquot;TkDesk API&dquot; (although currently they aren't much more then a random set of more or less useful proc's, but anyway). The proc's I currently think are part of the API are the following: <descrip> <tag>dsk_msg_alert <it>msg</it></tag> Display <it>msg</it> in an alert dialog box. <tag>dsk_msg_error <it>msg</it></tag> Display <it>msg</it> in an error dialog box. <tag>dsk_msg_info <it>msg</it></tag> Display <it>msg</it> in an info dialog box. <tag>dsk_confirm <it>msg</it> <it>script</it></tag> Display <it>msg</it> in a dialog box with &dquot;OK&dquot; and &dquot;Cancel&dquot; buttons. Evaluate <it>script</it> if user pressed &dquot;OK&dquot;. <tag>dsk_debug <it>msg</it></tag> If TkDesk runs in development mode (e.g. by setting the <tt>-devel</tt> command line option) print <it>msg</it> to stderr. <tag>dsk_ask_dir</tag> Ask for a directory to open. <tag>dsk_ask_exec</tag> Ask for a command to execute or file to open. <tag>dsk_exec <it>command</it></tag> Execute <it>command</it> in the background. When the command completes its exit code will be displayed in the status bar of all file browser windows. <tag>dsk_exec_as_root <it>command</it></tag> Asks for the root password, and executes <it>command</it> in the backgroud under root permission if it was okay. <tag>dsk_view <it>command</it></tag> Execute <it>command</it> in the background and displays its output (stdout and stderr) in a built-in editor window. <tag>dsk_view_as_root <it>command</it></tag> Same as dsk_view but asks for the root password first. <tag>dsk_path_exec <it>path command</it></tag> Executes <it>command</it> in <it>path</it> and in the background. <tag>dsk_path_view</tag> Executes <it>command</it> in <it>path</it> and in the background, and displays its output in an editor window. <tag>dsk_edit <it>?+linenum? ?file? ...</it></tag> Without arguments asks for a file to open, or opens all <it>files</it> in the same editor window, positioning the cursor at line <it>linenum</it> if given. If <it>file</it> is &dquot;New File&dquot; a black editor window will be opened. <tag>dsk_busy</tag> Displays mouse as &dquot;busy&dquot; and locks all TkDesk window from receiving mouse events. <tag>dsk_lazy</tag> Displays mouse as &dquot;lazy&dquot;, and makes all TkDesk windows responsive again. <tag>dsk_active <it>what</it></tag> Returns information about the currently active file browser or list window: If <it>what</it> is &dquot;dir&dquot; it returns its directory; if it is &dquot;sel&dquot; it returns all selected items as a Tcl list; if it is &dquot;win&dquot; it returns the Tk name of the active window. <tag>dsk_select X ?names?</tag> If <it>names</it> is not given, copies the full path names of the files selected in the currently active file browser or list window to the X selection. Otherwise just their names are copied. <tag>dsk_read_string <it>msg</it> <it>?script?</it> ?dontpaste?</tag> Displays $msg in a dialog box where the user can enter an arbitrary string. If <it>script</it> is not given, the string will be returned; if it is given the variable <tt>dsk_read_string</tt> will be set to the user's input and <it>script</it> will be evaluated. If &dquot;dontpaste&dquot; is passed as a third argument to this command the current X selection will not be pasted into the entry field. <tag>dsk_filesel <it>message</it> <it>path</it></tag> Opens TkDesk file selection dialog using <it>message</it> as the label, and presetting the entry field with <it>path</it>. <it>Path</it> is also used as the filter for the full file selection box. <tag>dsk_refresh ?file ...?</tag> For each <it>file</it> all windows displaying the corresponding directory is refreshed. <it>File</it> may also be a directory. <tag>dsk_sound <it>sound</it></tag> Plays <it>sound</it> if a sound command has been defined and sound is switched on. <it>Sound</it> is the second index in the <tt>tkdesk(sound,xxx)</tt> array as defined in the &dquot;Sounds&dquot; config file (&dquot;xxx&dquot; in this case). <tag>dsk_copy ?source ...? ?dest?</tag> Opens the &dquot;Copy, Move, etc.&dquot; dialog box, filling in &dquot;Source&dquot; and &dquot;Destination&dquot; fields if provided. <tag>dsk_dialup <it>phonenumber</it></tag> Without arguments asks for a phone number, and passes this to the command specified in <tt>tkdesk(appbar,dialup,cmd_up)</tt>, else it does the same without asking. <tag>dsk_find_files ?options?</tag> Opens the &dquot;Find Files&dquot; dialog, presetting its fields from the <it>options.</it> The following options are available: -path <it>path</it>, -mask <it>mask</it>, -string <it>string</it>, -regexp <it>regexp</it>, -extreg <it>extreg</it>, -type <it>&dquot;all|file|dir|sym|socket|pipe&dquot;</it>, -owner <it>owner</it>, -group <it>group</it>, -modified <it>mod</it>, -accessed <it>acc</it>, -size <it>size</it>, -case, -samefs, -followsym. If the option <tt>-doit</tt> is given the search will start as soon as the dialog is displayed, without having to press &dquot;Search&dquot;. <tag>dsk_mail <it>file</it> <it>?string?</it></tag> Asks for an email address where to send <it>file</it>. If <it>file</it> is the empty string, <it>string</it> will be sent instead. <tag>dsk_netscape <it>what</it> <it>?url?</it> <it>?args?</it></tag> This proc is used to communicate with a running netscape. If Netscape is not running yet it will be started first. <it>What</it> may be &dquot;file&dquot;, &dquot;url&dquot;, or &dquot;rcmd&dquot; (Communicator only); <it>url</it> may be a regular URL, a file name, or, if <it>what</it> is &dquot;rcmd&dquot;, it may be &dquot;mail&dquot;, &dquot;news&dquot; or &dquot;edit&dquot;. <it>Args</it> may be &dquot;window&dquot;, or &dquot;raise&dquot;, or both. <tag>dsk_periodic <it>command</it> <it>seconds</it></tag> Opens the &dquot;Periodic Execution&dquot; window and executes &dquot;command&dquot; every &dquot;seconds&dquot; seconds. <tag>dsk_open <it>viewer</it> <it>file</it></tag> Opens <it>file</it> by performing its default action. <it>Viewer</it> should be given as &dquot;&dquot;; I just didn't manage to get rid of this obsolete argument yet. <tag>dsk_open_browser <it>dir</it></tag> Opens <it>dir</it> in a new file browser window. <tag>dsk_open_dir <it>dir</it></tag> Opens <it>dir</it> in a new file list window (unless the option &dquot;Always in Browser&dquot; is set). <tag>dsk_openall <it>?files?</it></tag> Opens all <it>files</it> if given, or all currently selected files by performing their respective default action. </descrip> <sect1> Shortcuts <p> When specifying Tcl scripts in the popup and appbar configuration lists (<tt>tkdesk(popup,...)</tt> and <tt>tkdesk(appbar)</tt>), and in some other places as well (precide, hus?), you can use a number of shortcuts that will be expanded just before evaluation: <descrip> <tag>%s</tag> Will be replaced with the full pathname of the first selected file. <tag>%f</tag> Will be replaced with its filename only. <tag>%b</tag> Will be replaced with its filename without extension (&dquot;basename&dquot;). <!-- This seems to be the same as %b in reality... <tag>%c</tag> Will be replaced with its full pathname without extension. --> <tag>%d</tag> Will be replaced with its directory only. <tag>%A</tag> Will be replaced with a Tcl list of the full pathnames of all currently selected files, or of the files dropped on an appbar button. <tag>%a</tag> Same as %a, but replaces with a list of filenames only. <tag>%B</tag> Same as %A but doesn't complain if no files are selected. Instead it will be replaced with the empty string. <tag>%D</tag> Will be replaced with the directory of the currently active file list or browser window. <tag>%x</tag> Will be replaced with the contents of the X selection. <tag>%X</tag> Same as %x but doesn't complain if the selection is empty. <tag>%S</tag> For the &dquot;Copy, Move, etc.&dquot; popup menu; will be replaced of what's been entered into the &dquot;Source&dquot; text entry field. <tag>%D</tag> For the same popup menu; will be replaced of what's been entered into the &dquot;Destination&dquot; text entry field. </descrip> <sect1> Utilities <p> To make life while configuring TkDesk a little easier as long as there's no GUI configuration available yet, TkDesk provides four little &dquot;helper&dquot; tools that help with selecting colors, fonts, icons, and sounds, by using graphical mouse-based dialog boxes. These are accessed through the &dquot;Configuration&dquot; menu, and are basically all handled in the same way. Each of these dialogs contains three buttons: <descrip> <tag>Insert</tag> Works only correctly if the dialog was invoked from the editor's &dquot;Configuration&dquot; menu. Inserts the current value at the cursor position in that editor window. <tag>Select</tag> Copies the selected value to the X selection, so it can be pasted at arbitrary places using the middle mouse button. <tag>Close</tag> Closes the dialog. </descrip> <!-- appendix --> <sect> Frequently Asked Questions <p> <sect1> How can I change the position of the application bar? <label id="apppos"> <p> The appbar should have a &dquot;handle&dquot; at the upper or left edge that can be used to drag the appbar around the screen by just pressing the left mouse button over it. You can also drag the application bar around simply by holding down the Alt- or Meta-key and simultaneously pressing the left mouse button over the application bar. You can also invoke the &dquot;Move...&dquot; entry from the desk-button's popup menu to do the same without having to press Alt/Meta. The application-bar configuration file, &dquot;AppBar&dquot;, allows you to set a variable named tkdesk(appbar,wm_managed) which can be used to have the apllication bar managed by the window manager, although this is usually not necessary. <sect1> Can I have transparent icons? <p> No, but have a look at the answer to the next question. As far as I know to have transparent icons in X you need to make use of X11's SHAPE extension. Now as raw X programming is something only for the very brave, I didn't look into this any deeper yet. Any takers? <sect1> How can I change the background colour of the icons and desk items? <p> The background colour of icons used when the window manager iconifies a window can be set in the configuration file "System". The variable you are looking for is tkdesk(color,icon_background). By setting this variable to the same colour as your root window you can achieve the effect of transparent icons. You can define the colour either as a normal name (such as "grey", "blue") or in the form #rrggbb. <sect1> How can I have a different set of desk items on each virtual screen? <p> <sect2> FVWM and similar or derived window managers <p> First, you have to set the variable <tt>tkdesk(desk_items,wm_managed)</tt> in the System config file to 1. Then you have to configure fvwm to not decorate windows of class <tt>dsk_DeskItem</tt>. For instance: <tscreen><verb> Style "dsk_DeskItem" NoTitle, NoHandles, WindowListSkip, BorderWidth 0 </verb></tscreen> <sect2> CDE window manager <p> As for FVWM, you first have to set the variable <tt>tkdesk(desk_items,wm_managed)</tt> in the System config file to 1. To tell the CDE window manager (dtwm) to not decorate desk items you have to add the following line to either the file Dtwm or .Xdefaults in your home directory, and then restart dtwm: <tscreen><verb> Dtwm*dsk_DeskItem*clientDecoration: none </verb></tscreen> <sect1> How do I configure FVWM mini-icons for TkDesk? <p> For fvwm95's Style command (like fvwm2 from which it comes) you can use a variety of things to identify a tkdesk window - you don't have to use the name only; you can also use the window's class name or resource string. To find out what the window class and resource string are for a certain type of tkdesk window, use the FvwmIdent module (if you use Debian, this will be under the Window Managers->Modules menu; if you're using the default fvwm95rc that comes with the fvwm95 distribution, this will be under the Programs->Modules menu; I don't know about others). <p> Anyway, FvwmIdent tells me that the window opened by tkDesk's "Open Browser..." menu has a class name of dsk_FileViewer, so I open the FvwmConsole (or FvwmTalk) module, and enter the commands: <tscreen><verb> Style dsk_FileViewer TitleIcon mini-filemgr.xpm Recapture </verb></tscreen> Then the browser window appears with the appropriate icon. To get the window opened by the "Open Directory..." menu, use: Style dsk_FileList TitleIcon mini-filemgr.xpm <p> (The "recapture" command is only necessary to apply the command to windows already open; it isn't necessary in your config. file) (Contributed by Daniel Martin, <tt>dtm12@jhunix.hcf.jhu.edu</tt>) <sect1>Configuring the appbar or popup menus to execute (export foo=bar; program;) doesn't work.<p> Yes, this is a bit tricky. What you need to do is the following: <tscreen><verb> dsk_exec sh -c {export foo=bar; program} </verb></tscreen> <sect1> Composing characters doesn't work in TkDesk's editor. <p> Currently you have to edit the file cb_tools/bindings.tcl in TkDesk's library directory to make this work. Locate the two lines containing the word &dquot;XKB&dquot;; all you need to do is to comment out the following lines by prepending a '#'. <sect1> TkDesk's layout seems to be screwed. I can't get the appbar displayed anymore. <p> TkDesk saves all layout information in the file ~/.tkdesk/_layout, so this is the place to look for bad lines. If in doubt you can safely delete this file and all should be back to normal. In case of the appbar not showing up you can try the following (sent by Jochem Huhmann, joh@unidui.uni-duisburg.de): <enum> <item> Quit tkdesk <item> Open the file ".tkdesk/_layout" within your home directory with your favorite editor <item> Delete the line looking like "Toplevel dsk_appbar 1 66x740+956+0" <item> Write the file back to disk <item> Restart tkdesk. It will place the AppBar according to its defaults now. <item> Place the AppBar as you like (it doesn't like horizontal layout too much) <item> Do a "TkDesk/Save All Now" </enum> <sect1> On my RedHat 5.x system the appbar clock shows the wrong time. <p> This is from Bryan Venable, spif@vousi.com: This may have to do with the location of the zoneinfo directory. For libc5 I believe it's /usr/lib/zoneinfo, whereas for glibc it'd be /usr/share/zoneinfo. Try making a symbolic link from whichever you have to whichever you don't. There is a fix to Red Hat 5.0 which has to do with this, but in that situation the problem is with libc5 programs running on a system "optimized" for glibc. And Raul Quiroga, quiroga@cartan.math.cinvestav.mx, also has got some advice for this: Concerning the problem described below I received several suggestions. Some allowed to get the date in the tkdesk right but an incorrect one with the date command. Finally what I did is set the time with timeconfig to Universal with "Hardware clock set to GMT" checked; after a reboot both the appbar and date reported the correct time. Thanks all for your help. <sect1> TkDesk complains about &dquot;invalid command name wm&dquot; and won't start up<p> There seem to be two solutions to this problem: One is if you're running on a RedHad Linux 5.x system, the libc5 that's packaged and installed in /lib may be too old a version. If <tt>ls /lib/libc.so.5.*</tt> on your system gives something like <tt>/lib/libc.so.5.3.xx</tt> you should upgrade to at least 5.4.20. I think you can get a newer version of libc from sunsite.unc.edu in /pub/Linux/GCC. The other solution that should always work is to do the following (thanks to Ike Hishikawa, ike@hishikawa.f.uunet.de, for this one): <tscreen><verb> Assuming that the tarball was unpacked under /usr/local, open /usr/local/bin/tkdesk with your favorite editor. At the very top of the file it should say: #!/bin/sh #-*- tcl -*- \ PATH=/usr/local/bin:$PATH ;#\ exec tkdesksh "$0" "$@" After the 3rd line, insert two lines pointing to the location of tcl/tk libs, so that you get: #!/bin/sh #-*- tcl -*- \ PATH=/usr/local/bin:$PATH ;#\ export TCL_LIBRARY=/usr/local/lib/TkDesk/tcl_lib ;#\ export TK_LIBRARY=/usr/local/lib/TkDesk/tk_lib ;#\ exec tkdesksh "$0" "$@" This did the trick for me :) Hope this helps, </verb></tscreen> <sect1> I cannot launch other Tcl/Tk applications from within TkDesk <p> Probably your version of TkDesk sets the environment variables TK_LIBRARY and TCL_LIBRARY, which confuses other Tcl/Tk apps. Unset these variables in the config files before the invocation of the problematic commands, eg. replace xterm with <tscreen><verb> sh -c {unset TK_LIBRARY TCL_LIBRARY; xterm} </verb></tscreen> (contributed by Christoph Dalitz, <tt>dalitz@infotech.de</tt>) <sect1> I'd like TkDesk to do this and that. How can I achieve this? <p> The first place to start are the various configuration files of TkDesk. These can be accessed either by the "TkDesk/Configuration" menu of the file browser windows, or by the "Configuration" submenu of the popup menu of the very first button of the application bar of TkDesk. Since TkDesk uses Tcl as the language for its configuration, and these configuration files are simply "source"ed, you could add any sort of Tcl proc for instance to the configuration file "System". This proc would then be available in every other configuration file as well. With the set of commands provided by TkDesk, which are listed e.g. in the configuration file "Popups", TkDesk provides a very powerful platform for the user who knows Tcl. <sect1> Is there a TkDesk mailing list? <p> There two mailing lists dedicated to TkDesk, one for discussing general installation/usage topics (tkdesk-users) and one targeted more for TkDesk-related development topics (tkdesk-code). You can subscribe to the tkdesk-users list here: <tt><url url="https://lists.sourceforge.net/lists/listinfo/tkdesk-users">.</tt> To post questions, etc., send email to <tt><htmlurl url="mailto:tkdesk-users@lists.sourceforge.net" name="tkdesk-users@lists.sourceforge.net">.</tt> It you are interested in contributing to the development of TkDesk, you can subscribe to the tkdesk-code list here: <tt><url url="https://lists.sourceforge.net/lists/listinfo/tkdesk-code">.</tt> Once you have subscribed, you can post using the address <tt><htmlurl url="mailto:tkdesk-code@lists.sourceforge.net" name="tkdesk-code@lists.sourceforge.net">.</tt> <sect1> Where can I find out more about Tcl/Tk? <label id="tcltk"> <p> The official Tcl/Tk homepage is at <tt><url url="http://www.tcl.tk/"></tt>. There is also a newsgroup dedicated to Tcl/Tk: <tt><htmlurl url="news:comp.lang.tcl" name="comp.lang.tcl"></tt>. <sect> Tips and Tricks <p> This section currently contains just one tip on how to combine TkDesk and XEmacs. If <bf>you</bf> have any procs or other stuff in your configuration file which you consider could be useful for others as well, or just think it's generally cool, please send me an email, so that I can add it to this section! Thanks! <sect1> TkDesk and XEmacs <p> If you are using XEmacs 19.12 or later you can couple TkDesk and XEmacs quite closely together by adding the following proc into any of your configuration files (I have it in &dquot;Popups&dquot;): <tscreen><code> proc xemacs_load {what {where same}} { switch $where { "same" { exec gnudoit -q (find-file \"$what\") } "other" { exec gnudoit -q (find-file-other-window \"$what\") } "frame" { exec gnudoit -q (find-file-other-frame \"$what\") } "scratch" { exec gnudoit -q (switch-to-buffer-other-frame \"*scratch*\") } } } </code></tscreen> And now my generic popup menu for files matching <tt>*</tt> reads: <tscreen><code> {{*} { {{Edit} {dsk_edit %s}} {{XEmacs} {xemacs_load %s}} {{Other Window} {xemacs_load %s other}} {{Other Frame} {xemacs_load %s frame}} - {{Print} {dsk_print %s}} }} </code></tscreen> This way you can load files from TkDesk into a running XEmacs! This assumes that you have the command <tt>gnudoit</tt> somewhere in your path, and have started the XEmacs server. This can be done by adding the following line to your <tt>~/.emacs</tt>: <tscreen><verb> (gnuserv-start) </verb></tscreen> </article> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/doc/guide.txt����������������������������������������������������������������������������0100644�0001750�0000764�00000232561�10037101245�013332� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� TkDesk User's Guide Originally written by Christian Bolik, now maintained by J. Chris Coppick, jchris@users.sourceforge.net Version 2.0, 15 April 2004 TkDesk is a graphical, highly configurable and powerful desktop man- ager for UNIX and the X Window System. This document is meant to be a comprehensive guide to the functions, services and configuration pos- sibilities offered by TkDesk. Please also take a look at the CHANGES file for latest news. A list of answers to frequently asked questions is also included. The TkDesk homepage is at <http://tkdesk.sourceforge.net>. ______________________________________________________________________ Table of Contents 1. Introduction 1.1 Acknowledgments 1.2 Using TkDesk's Help System 1.3 Command Line Options 2. The File Browser Window 2.1 The Menu Bar 2.1.1 TkDesk Menu 2.1.2 File Menu 2.1.3 Directory Menu 2.1.4 Commands Menu 2.1.5 Bookmarks Menu 2.1.6 Options Menu 2.1.7 Help Menu 2.2 The Button Bar 2.3 The Path Entry 2.4 The File Listboxes 2.4.1 Handling of the Listboxes (Bindings) 2.5 The Status Bar 3. The File List Window 4. File Operations 4.1 File Information 4.2 Copying, Moving and Deleting Files 4.3 Finding Files 5. Cascading Directory Popup Menus 6. The Application Bar 7. The Built-in Editor 7.1 Marks 8. Using the TkDesk Server 9. Configuration of TkDesk 9.1 Configuration Files 9.1.1 AppBar 9.1.2 ButtonBar 9.1.3 Commands 9.1.4 Directories 9.1.5 FileTags 9.1.6 Local 9.1.7 Popups 9.1.8 Sounds 9.1.9 System 9.2 The TkDesk API 9.3 Shortcuts 9.4 Utilities 10. Frequently Asked Questions 10.1 How can I change the position of the application bar? 10.2 Can I have transparent icons? 10.3 How can I change the background colour of the icons and desk items? 10.4 How can I have a different set of desk items on each virtual screen? 10.4.1 FVWM and similar or derived window managers 10.4.2 CDE window manager 10.5 How do I configure FVWM mini-icons for TkDesk? 10.6 Configuring the appbar or popup menus to execute (export foo=bar; program;) doesn't work. 10.7 Composing characters doesn't work in TkDesk's editor. 10.8 TkDesk's layout seems to be screwed. I can't get the appbar displayed anymore. 10.9 On my RedHat 5.x system the appbar clock shows the wrong time. 10.10 TkDesk complains about "invalid command name wm" and won't start up 10.11 I cannot launch other Tcl/Tk applications from within TkDesk 10.12 I'd like TkDesk to do this and that. How can I achieve this? 10.13 Is there a TkDesk mailing list? 10.14 Where can I find out more about Tcl/Tk? 11. Tips and Tricks 11.1 TkDesk and XEmacs ______________________________________________________________________ 1. Introduction TkDesk is a graphical desktop manager for UNIX (with a slight emphasis on Linux, but it also runs just as well on AIX, Solaris, HP-UX, SGI Irix and other UNIX flavors) and the X Window System. It offers a very complete set of file operations and services, plus gives the user the ability to configure most aspects of TkDesk in a very powerful way. The reason for this is the use of the Tcl scripting language as the configuration and (for the greatest part of TkDesk) implementation language. This makes TkDesk not just configurable but truly programmable. TkDesk has been influenced by various other systems and file managers: NeXT, for laying out the file browser windows, Apple Finder, for the idea of file annotations and, (shock horror), Windows 95, for some other (of course minor and unimportant) inspirations. This is a brief overview of the most prominent features of TkDesk: o Arbitrary number of automatically refreshed file browsers and file list windows, o Configurable file-specific popup-menus, o Drag and drop, o Files and directories may also be dropped onto the root window, a.k.a. desktop, o Configurable application bar, with several displays and cascaded popup menus for each button, files can also be dropped here, o History of visited directories, opened files, executed commands, and others, which is automatically saved to disk, o Find files through their annotation, name, contents, size, age, or other criteria, o A trash can for safe deletion of files and directories, o Calculation of disk usage for directory hierarchies, o All file operations (find, copy, disk usage, etc.) are carried out in the background, o Traversal of directory hierarchies through recursive cascaded menus, o Bookmarks, create menu entries for often used files/directories, o Comprehensive hypertextish online help, o Built-in multi-buffer and undo-capable editor, o Remote control of XEmacs, o Close coupling with Netscape Navigator for displaying HTML files or selected URLs, o Sound support, o Powerful on-the-fly configuration of nearly all aspect of TkDesk using Tcl/Tk, this also allows the Tcl-literate to extend TkDesk in arbitrary ways, o Free of charge! But see the file COPYING, or menu entry Help/License for information on usage and redistribution of TkDesk. 1.1. Acknowledgments Christian Bolik writes: TkDesk uses a number of other freely available packages without which TkDesk would not have been possible. I'd like to say many thanks to the following people: o Chris Sterritt for setting up and managing the TkDesk mailing list (at the old location majordomo@mrj.com o Alan V. Shackelford for seamlessly taking over the list to his machine and responsibility (the list is now at majordomo@shaknet.clark.net), o Ken Hornstein for his wonderful netscape-remote package, o Ioi Kim Lan for making an XPM image reader for Tk available, o George Howlett for his great BLT, of which parts are used by TkDesk, o Michael McLennan for his massively useful [incr tcl], o John Ousterhout for Tcl/Tk, which is definitely the best thing since sliced bread, o Greg Hankins and Matt Welsh for putting together the most wonderful linuxdoc-sgml package, o and of course, Linus Torvalds whose Linux kind of changed my life, really! And a very big thank you to the growing TkDesk user community, which provides me with a constant flow of bug reports (getting less now :-)), suggestions for enhancements of TkDesk, and lots of motivation and encouragement. Special thanks to Chuck Robey for revising a previous version of this guide. 1.2. Using TkDesk's Help System If you check menu entry Options/Use Netscape for Help, TkDesk will use that for displaying this User's Guide on-line. Otherwise, to reduce overhead, TkDesk uses its own help system. It features hypertext links, context sensitivity (which is not yet fully utilised by TkDesk) and full text search. The help window consists of four areas: 1. A listbox listing all the section headings. A section can be selected by pressing the left mouse button, 2. the text display, which contains the actual help text, 3. a text entry field in which a regular expression may be entered (such as [Ff]eatures). After hitting Return, the whole help text is searched for this expression. Pressing Return again continues the search, 4. three buttons: "Print" prints the complete help volume, "Back" jumps back after a hypertext link has been followed (see next paragraph), and "Close" to close the help window. Text that is displayed blue in the help window is a hypertext link. When the left mouse button is clicked over such a link the display will automatically change to the referenced section. You can jump back by pressing the "Back" button described above. The following keys are bound when the mouse pointer is inside the help window: Tab Moves to the next section. Shift-Tab Moves to the previous section. Control-Tab Moves to the first section. Control-Shift-Tab Moves to the last section. Up, Down Scrolls one line up/down. Page up, Page down Scrolls one page up/down. Control-Home Jumps to start of help text. Control-End Jumps to end of help text. Meta/Alt-b Equivalent to pressing the "Back" button. Meta/Alt-c, Escape Equivalent to pressing the "Close" button. 1.3. Command Line Options Usually TkDesk is started simply by executing the command "tkdesk" from the shell prompt or your X initialisation file. However, you may specify the following options to this command: -configdir dir Reads the configuration from directory dir instead of ~/.tkdesk. -default Reads the default configuration of TkDesk instead of the user's one in ~/.tkdesk. -iconic Iconifies all file browser and file list windows created by TkDesk during start-up. -layout file Reads the window layout information from file file instead of the default ~/.tkdesk/_layout. -startdir dir If this option is given, the first file browser will open with directory dir. -twm Don't use icon windows when file browser or list windows are iconified. Some window managers liek twm cannot handle these properly. For example, the command "tkdesk -twm -iconic" tells Tkdesk to not use icon windows and start with all windows iconified. 2. The File Browser Window The file browser window is the largest window when TkDesk is started for the first time. It contains a menu bar, a configurable button bar (for fast access to the most often used file operations and other functions), an entry field (displaying the current path), a horizontal scrollbar, a certain number of file listboxes (three by default), and a status and information bar. 2.1. The Menu Bar The following sections describe the entries of the individual menus of the file browser windows. 2.1.1. TkDesk Menu The "TkDesk" menu contains the following entries: New Browser... Asks for a directory for which to open a new file browser window. If the directory is the empty string, the operation is cancelled. By default the checkbox "In Browser" is checked, but if you uncheck it a file list window will be opened instead. The button to the right of the text entry field gives access to a menu containing previously visited directories. Clone Window Creates a new file browser window with the same directory as the current one (that is, the directory of the file browser from which this menu entry was invoked). Display AppBar/Hide AppBar Opens the application bar if it had been closed before, or hides (closes) it otherwise. This menu entry's label changes based on whether the AppBar is currently displayed or not. Configuration This is a submenu from which individual or all configuration files of TkDesk can be opened to modify them. Auto Save Here you can select which parts of TkDesk are automatically saved periodically and when TkDesk exits. Save All Now Saves all parts of TkDesk's configuration no matter what the settings in the previous submenu. Close Window Closes the file browser window. Quit Quits TkDesk. 2.1.2. File Menu This menu provides all the usual file operations plus entries for finding files. The contained menu entries are: Information Opens a file information window for each currently selected file. This window, which can also be used to add annotations to files, is described in section ``File Information''. New File... Asks for the name of a file which will be created in the current directory of the file browser or list. If "Open in Editor" is checked the new file will automatically be opened in the configured editor. New Directory... Asks for the name of a directory which will be created in the current directory of the file browser or list. If "Open after Creation" is checked the current window will switch to the new directory after it is created. Open Opens the selected files using their default action, which corresponds to the first action defined in their popup menus as configured in the "Popups" configuration file. Print... Asks for a command to print the currently selected files. The names of the selected files will be appended to the command. The default command can be defined in the configuration file "System". Copy, Move, Link... Opens a dialog box for copying, moving, linking, symbolic linking etc. of files. This dialog window is described in more detail in section ``Copying, Moving and Deleting Files''. Rename... For each selected file, TkDesk asks for a new name. Before actually renaming TkDesk checks for an existing file with the same name. Delete... Opens a dialog to delete files. The section ``Copying, Moving and Deleting Files'' gives more details. Find Files... Opens a dialog window which can be used to search for files, using a variety of characteristics. This dialog is described in more detail in section ``Finding Files''. Find Annotation... Lets you search for files with a certain annotation. More details in section ``Finding Files''. Copy To X Selection Copies the complete names of all currently selected files (i.e. including their paths) to the X clipboard. They can then be pasted into any other X application using the middle mouse button. Copy Names Only Same as previous one, but copies only the files' names (i.e. not their paths.) History Displays a submenu containing recently opened files. Close Window Closes the window. 2.1.3. Directory Menu The main purpose of this menu is to select directories which are to be opened. It also manages TkDesk's trash can. The menu contains the following entries: Open... Asks for a directory. A new file list or file browser window will be created, baesed on the setting of the "In Browser" checkbutton. New... Asks for the name of a directory which will be created in the current directory of the file browser or list. Home Directory Changes the directory of the window from which this entry is invoked to the user's home directory. A number of path entries These can be configured in the configuration file Directories. See section ``Configuration of TkDesk'' for details on how to do this. If one of these menu entries is invoked, the path of the file browser will change to that directory. If such an entry is invoked while at the same time pressing the "Control" key, a new file list window will be created (if the option "Always In Browser" is selected a file browser window will be created), displaying the contents of the selected directory. This feature applies to all menus that contain directory names! Trees Contains two cascaded submenus: "Home" and "Root". The contents of these submenus is dynamically generated and corresponds to the directory hierarchy rooted either at the user's home directory or at "/". Selecting an entry from these submenus changes the directory of the window to the selected directory. Pressing Control at the same time opens a new window with this directory. Open Trash Can Opens a file list window displaying the contents of the trash can. When this window is iconified and TkDesk uses icon windows the icon can be used as a Mac-like trash can. Empty Trash Can Empties the trash can after confirmation from the user. This erases the files contained in the trash can for good! History Displays a submenu containing recently opened directories. 2.1.4. Commands Menu This menu provides entries to execute commands either once or periodically, edit files, and provides some very basic form of "job control". Its entries are: Execute... Asks for a command to execute. The button to the right of the command entry contains a history of previously executed commands. Selecting one of these copies its name to the entry widget. Checking the "View Output" button will open an editor window after the command completed displaying its output (stdout and stderr). Execute as root... The same as the previous entry but TkDesk will ask you for the root password before executing the command. The command will then run under root permission. Periodic Execution... Opens a window which can be used to execute a command and watch its output periodically. If the "Don't execute" checkbutton is selected, the execution is paused. Job Control Opens a window which allows to stop, terminate, kill etc. processes which have been started by TkDesk. Environment... Opens the "Edit Environment" dialog which can be used to display and modify TkDesk's environment variables. Programs and commands started from TkDesk after any modification in this dialog has been made will pick up the modified environment. Edit File... Asks for the name of a file to edit, and opens it in the editor you selected in the "System" configuration file (defaults to the built-in editor). The "Browse..." button opens a file selector from which a file may be selected. New File Opens a new blank editor window. Edit Selected Opens all currently selected files in one new editor window. Buffers Displays a submenu of all current editor buffers (both files and command output). A number of custom command entries These can be defined in the configuration file Commands. See section ``Configuration of TkDesk'' for details on how to do this. If one of these menu entries is invoked, the corresponding command will be executed. History Displays a submenu containing commands recently executed. 2.1.5. Bookmarks Menu This menu lets you create bookmarks for often used files and directories. To do this, select at least one file/directory and invoke the "Add Bookmark" menu entry. The name(s) of the selected files and directories will now appear alphabetically sorted in the "Bookmarks" menu. You can remove any bookmark from the menu by selecting its corresponding entry from the "Remove Bookmark" submenu. The bookmarks you add will be automatically saved, if the "Bookmarks" entry of the "Auto Save" submenu contained in the "TkDesk" menu is selected (which is the default). 2.1.6. Options Menu The Options menu lets you configure many aspects of TkDesk "on the fly". The entries are: Long Listing Select this to let the details of all files and directories be displayed in the file listboxes. This slightly decreases performance and for that reason is switched off by default. Show All Files Whether to show files in the file lists whose name start with ".". Add Icons If selected, small icons will be displayed to the left of the file names in the file listboxes. Turn this off for faster scrolling. Folders On Top If selected (default) folders will always appear on top of the file lists. Append Type Char Whether to append a file-type specific character to the file names. This is mainly intended for monochrome displays and is then automatically selected. Single Click (Dirs) Lets you open directories with a single click of the left mouse button. Individual directories can still be selected by pressing Control at the same time. Always In Browser If this option is selected, the "In Browser" checkbutton of the "Open Directory" dialog will always be selected. Control- Doubleclick on a directory will open a file browser instead of a file list window. Status in List Windows Whether the singly columned file list windows should have a status bar as well. Sort by ... Sort all file listboxes by one of these criteria: Name, Name (fold, meaning that upper and lower case characters will be "folded", i.e. disregard case when sorting), Size, Date, Extension, or Don't Sort to display directory entries as they are read from disk. Strip <your home directory> If this is selected, and the current path of the browser is somewhere under your home directory, the leftmost file listbox will contain your home directory rather than the root directory. This speeds up the display of directory hierarchies under your home directory. Overwrite Always If this option is selected, TkDesk won't ask if the destination file already exists when copying or moving files. Really Delete Relates to the deletion of files. If this option is selected, the "Delete Files" dialog box will always have the "Delete permanently" checkbutton selected by default. Quick Drag'n'Drop Normally when you drop files onto a file list, the copy dialog appears. If this option is selected, dropped files will be moved to the destination directory without further questions. If Control is pressed during dropping, the files will be copied. If the drop target is the trash can, TkDesk will ask if the files are to be deleted "really". Sort History This option determines whether the history menus are to be sorted alphabetically or not. TkDesk Server Start or stop the built-in server to remote execute TkDesk commands. See also section ``Using the TkDesk Server''. Dialogs at Pointer If selected, TkDesk will always try to place new windows right under the mouse pointer. Autoraise AppBar Whether to raise the AppBar above any obscuring window when it is entered by the mouse pointer. Use Sound If you have sound working with TkDesk on your machine, you can temporarily disable sound by selecting this option. Handy when playing Audio CDROMs, for instance. Number Of Listboxes This relates to the number of listboxes in the file browser window. Between 1 and 6 can be displayed, though 18 would theoratically not be a problem. Balloon Help Whether to display a small window at the mouse pointer if it is placed over part of TkDesk, e.g. an AppBar button. Use Netscape for Help Whether to use Netscape rather than the built-in help viewer for displaying TkDesk online help. The settings of these options are by default automatically saved to ~/.tkdesk/_options. This can be disabled by deselecting the "Options" entry of the "TkDesk/Auto Save" submenu. 2.1.7. Help Menu This menu tries to give you some help with using TkDesk. It also contains entries for displaying the list of FAQs, recently made changes to TkDesk, and the license for using and distributing TkDesk: User's Guide Displays the TkDesk User's Guide either in the built-in help viewer or using Netscape (see previous section). Manual Page... Asks for a command for which to display the system's manual page in an editor window. Getting Started Displays the "Getting Started" guide. TkDesk FAQ Jumps directly to the FAQ section of the TkDesk help volume. Changes Displays recent changes to TkDesk. Make sure you invoke this entry after upgrading to a newer version of TkDesk. License Restates that TkDesk is distributed under the GNU Public License (GPL). About TkDesk... Some info about TkDesk (version, author etc.), plus a link to the TkDesk web page. 2.2. The Button Bar The button bar that's located right underneath the menu bar allows for fast access to any of the commands of TkDesk. It's also possible to configure each button individually to run customized Tcl scripts, that e.g. work on the currently selected files. By default, the buttons in the button bar provide access to the more often used functions of TkDesk (copy, create, delete etc.). When you place the mouse button over any of these buttons, a small help window will appear telling you what this button does (provided the "Balloon Help" option is activated). The contents of this button bar can be defined via the configuration file ButtonBar. See section ``Configuration of TkDesk'' and the configuration file itself for details on how to do this. Note that by not defining the variable tkdesk(button_bar) in that file or by not defining tkdesk(small_button_bar) the button for file viewer or list windows may be surpressed respectively. 2.3. The Path Entry This entry field displays the current path of the file browser or file list. You can also type directly into this field to change the display to another directory. There is a sort of "auto-completion" available; if you type only the first part of a directory's name and press Control-Tab, its name will be automatically completed as far as there's no ambiguity. If you click the right mouse button over this field a popup menu appears which lets you select any of the current directory's parent directories and their subdirectories. You can also open files directly from this popup menu. The button to the right of the entry field contains a menu of the last 30 (default value this may be changed in the "System" configuration file) directories you have visited. If you select one of these, the current path of the browser will change to it. The "Control-trick" to open the selected path in a new list window that's been described in section ``Directories'' works here as well! 2.4. The File Listboxes The main part of the file browser and list windows is taken up by the file listboxes. Directories are by default displayed in blue with a bold font, executable files in red and bold, and regular files in black and a medium font. These settings as well as the color, font and icons used for individual file types may be configured via the FileTags configuration file. See section ``Configuration of TkDesk'' for details on how to do this. Right above each listbox the name of the directory whose contents it shows is displayed, together with the current file mask settings. Clicking on this label reveals a menu with the following entries: Refresh Refreshes the listbox contents. Although TkDesk refreshes all its file lists every 5 seconds (default), sometimes you may want to have a file listbox instantaneously updated. Set Mask... Sets a mask for this listbox's display. For example, you can configure the listbox to only display or select files matching the pattern *.gif. Multiple masks separated by spaces may be entered to display files matching any of these. A history menu button for reusing masks previously entered is also provided, as well as a checkbutton for inverting the mask's effect (i.e. only files not matching the given mask will be displayed). No Mask Removes a previously activated file mask. Disk Usage Calculates the directory's disk usage, i.e. the number of kilobytes (default) occupied by that directory and all its subdirectories. A window will appear when the calculation is complete, containing a sorted list of all subdirectories and their individual disk usages. If you double-click on any of its entries, a new file list window will appear displaying the directory's contents. Clicking the right mouse button over any of the directories displays its associated popup menu. Free Space Displays the space available in the file system of this listbox's directory, using TkDesk's "Periodic Execution" window. Execute here... Asks for a command to execute in the directory displayed by this listbox. All the usual TkDesk %-sequences to work with the names of selected files may be used, e.g. %A to pass all currently selected files to the command. Execute as root... The same as the previous one, except the command will be run under root permission after asking you for the root password (and you entered the correct one...). Long Listing Instead of just the file names displays a "long listing" i.e. a listing containing all the details such as size, permission bits, date and time of last modification etc. Show All Files Whether to display files starting with a dot (".") or not. Inverse Order Whether to invert the current sorting order. Sort by... This is identical to the entry of the same name in the menu bar's "Options" menu, but affects this single listbox only. Open List Window If this listbox is part of a file browser window, open a new list window displaying this listbox's directory. Open Browser Else, open a browser with this directory. This menubutton can also be used to drag and drop files, as well as to drag the directory displayed in the associated listbox to another target, e.g. the root window by pressing the middle mouse button over the listbox title menu button. 2.4.1. Handling of the Listboxes (Bindings) The handling of the file browser windows is very similar to that of the NeXT file manager: when you double click on a directory, the listbox to the right of the current one will display this directory's contents. You can open a directory in this listbox, and the listbox to the right of this one will display its contents. This way you can browse through complete directory hierarchies, while having instant access to the contents of the complete directory tree. When the number of opened directories exceeds the number of visible listboxes, you can scroll the listboxes with the horizontal scrollbar which is located right above the file listboxes. Files are selected in the file listboxes using the left mouse button. A single click selects a single file, deselecting all other files. If the Control key is pressed simultaneously, the old selection will be retained. By dragging the mouse pointer over a file list, while at the same time pressing the left mouse button, a set of files can be selected. Shift-doubleclick selects all files in that file list. Holding down the shift button extends the current selection unto that file (a.k.a. "range-selection"). Note that if the option "Single Click (Dirs)" is set in the "Options" menu a single click on a directory will suffice open it. In this case you need to use Control-Click to select a single directory. A double click on any listbox item performs its default action. For directories this is to open it, for executables this is to execute it, and for files it invokes the first entry of the corresponding popup menu as defined in the "Popups" configuration file (see below). If the Control key is pressed while double-clicking a directory, a new file list or file browser window (depending on the setting of the "Always In Browser" option) is created displaying the contents of that directory. For files, a dialog box will appear asking for a command to execute on that file. Files can be dragged by pressing the middle mouse button over any selected file. If no file is selected, the clicked-over file will be selected. Files can be dropped by releasing the mouse button over any other file listbox and the menubutton above them, over windows of the built-in editor, over the application bar (if the corresponding button has been configured to handle dropped files), over iconified file browsers and file lists and in general over any other application supporting the file protocol of the BLT package's drag and drop implementation. Valid drop targets can be identified by looking at the window that is displayed while files are dragged: the window's relief appears raised if over a valid target, flat if not. The right mouse button is used in the file listboxes to access the file-specific popup menu. Every popup menu contains a submenu, labeled with the file's name, which contains entries for the most common file operations. The remaining entries of the menu can be configured via the configuration file Popups. See section ``Configuration of TkDesk'' for details on how to do this. The file listboxes may also be fully controlled using the keyboard: Up-/Down Arrow Move the selection one file up/down. Page Up/Down Move the selection one page up/down. Home/End Move the selection to first/last item in the listbox. A-Z Select the first file matching the character pressed, cycle through subsequent matches on repeated pressing of the same key. Return Open selected file. Menu (the key left to the right Control key on W95 keyboards)" Display the currently selected file's popup menu, the menu itself may also be operated using Up/Down, Return, and Esc. Backspace Change directory to the parent directory. F11 Open the current file listbox's menu. 2.5. The Status Bar The status bar is located at the bottom edge of file browser windows. If "Status in List Windows" is selected in the "Options" menu file list windows will contain a status bar as well. It displays either o the current state of TkDesk, o the amount of free space in the current file system, o details about the currently selected file, or o number and total size of currently selected files. 3. The File List Window The file list window is basically a more compact version of the file browser window. It displays only one listbox, which results in the fact that the contents of opened directories are always displayed in the same listbox, and it doesn't have a status bar unless "Status in List Windows" is set in the "Options" menu. Its advantage is that it is more quickly created than a file browser window and occupies less space on your screen. My usual setup is that I have one file browser and lots of file lists opened. The menu bar of the file list windows is also more compact, but allows access to all menus of the file browser through submenus of the "Others" menu. This menu contains one additional entry to open a file browser window displaying this file list's directory. 4. File Operations 4.1. File Information The "File Information" window displays detailed information about a file. It can also be used to change the owner or access-permissions of a file. In addition, an annotation to a file can be added here. The following information is displayed: Path The path of the file. Size The size of the file in bytes. Modified When the file was last modified. By clicking on the button displaying the date file can be "touched", ie. their modification timestamp will be set to the current time. Owner Displays the owner of the file. By clicking the button displaying the owner, the owner can be changed to another user. Group Displays the group ownership of the file. By clicking the button displaying the group's name, the group can be changed. Mode Here the access permissions are displayed in "ls -l" style. The first three button correspond to the rights of the owner (r: may read, w: may write, x: may execute or open the directory), the second three buttons to the rights of the group, and the last three buttons to every one else's rights. The "x" buttons are special in that they cycle through four settings: x for executable, s for set user/group id and executable, S for set id only, and - for not executable. Note that using the right mouse button toggles between - and x only. Clicking the middle mouse button over any of these buttons copies its current setting to the corresponding button in the other two groups. If the settings of any of these buttons is changed, the "Change Mode" button at the bottom edge of the window must be clicked to actually change the file's permissions. Links Number of (hard) links to this file. Type Tries to give some information about the file's contents. TkDesk uses the shell command file for this. In the "Annotation" text field you can enter any remarks about the file. This annotation will be saved when the "Close" button is pressed. Files having annotations attached to them will appear underlined in the file listboxes, and their popup menu will contain the first few words of that annotation as an "informative" menu entry. If the window displays information about a file an additional button labeled "Disk Usage" is provided, which calculates the disk usage of the hierarchy rooted at that directory. The entries of the "Disk Usage" window can be double-clicked to open new file list windows. 4.2. Copying, Moving and Deleting Files These functions can be accessed from the "File" menu, among others. There is one dialog for copying, moving and linking files (plus an arbitrary number of user-defined actions such as diff and patch, as configured in the "Popups" config file), one dialog for renaming files (which does nothing else than moving the file to its new name), and one dialog for deleting files. The "Rename" dialog is very straight-forward and probably does not need further explanation. The "Copy etc." dialog contains the obvious source and destination entry fields, plus a checkbutton labeled "all selected files" if more that one file was selected when this dialog was opened. If this checkbutton is selected (default) the operation will be applied on all selected files. If it is not checked, each file will be handled individually. The "Skip" button can then be used for skipping individual files. The "Delete" dialog also contains this checkbutton, plus a checkbutton labeled "Delete permanently". If this checkbutton is selected, the files will not be moved to the trash can but will be really and ultimately deleted!! The default setting of this checkbutton can be set from the "Options" menu. 4.3. Finding Files The "File" menu contains two entries for finding files: "Find Files..." and "Find Annotation...". Files can be annotated through their "Info" dialog (accessible from the "File" menu or from their popup menu), see section ``File Information''. "Find Annotation" enables you to look for an annotated file whose annotation matches a certain regular expression (which can be as simple as an ordinary string). "Find Files" lets you look for files (or rather: let's you instruct TkDesk to look for files) whose names match one or multiple patterns, which are of a certain type (such as directory), which contain a certain string, which are smaller or bigger than a certain number of kilobytes or which are younger/older than a certain date. All fields which are not left blank in this dialog will be combined with a logical AND. This dialog is currently the only one utilizing the balloon help capability of TkDesk, so for now I would like to refer you to this. For instance, if you want to know how to enter the file size you're looking for, place the mouse pointer over the "Size" entry field without moving it for a few seconds. Both "Find" dialogs display their results in the same file listboxes that are used by the file list and browser windows, so the same bindings described in section ``Handling of the Listboxes (Bindings)'' apply here as well! 5. Cascading Directory Popup Menus Ugh, what a title... However, one of the most powerful features of TkDesk are its "cascading directory popup menus." These are menus that start from a given directory, contain submenus for each subdirectories (and possibly menu entries for files), and that dynamically add submenus as you traverse the menu. Hm, kinda hard to explain, but pretty cool. These menus are the ones that are accessible through menu entries "Directory/ Trees/ Home" and "Root", by clicking the right mouse button in the file browser and list window's path entry field, and through the popup menus of directories (submenu "Subdirectories" and "... and Files"). They may also appear in other places, depending on your configuration of TkDesk. There are two special bindings available in these menus: Control-Mousebutton-Release On a menu entry associated with a directory this opens a new file list window displaying this directory's contents. On a file it asks for a command to run on this file by displaying the "Open or Execute" dialog. Left-and-Right-Mousebutton-Press If the left and right mousebuttons are pressed simultaneously over a menu entry, the menu disappear, the popup menu for the associated file or directory is displayed. Otherwise the directory or file that's selected from the menu is opened using its default command. 6. The Application Bar TkDesk provides you with an application bar for fast access to your favorite applications, commands, directories etc., plus displays for the current date and time, system load, and the status of your mailbox and dialup link. It consists of an arbitrary number of buttons. Each button (also the aforementioned displays) contains a popup menu which can be accessed by pressing the right mouse button over any of them. If you single-click the left mouse button over such a button, the first entry from the corresponding popup menu defining a command to execute will be invoked. The first button (displaying a small desk by default) could be called TkDesk's "Start Button". Its popup menu contains entries for accessing TkDesk's most often used functions, such as executing a command or opening a file list or browser window, plus submenus for your bookmarks, the files you most recently opened, and the directories you've last visited. The next entry is a submenu labeled "Application Bar". Here you can configure all aspects of the application bar, especially its position and orientation. See my answer to the MFAQ ``How can I change the position of the application bar?'' for more. The last entry labeled "Configuration" contains another submenu which gives you fast access to TkDesk's configuration files. The second button gives you access to this User's Guide, and lets you view manual pages, also making use of a running TkMan (which is a hypertextified manual pager which is to be highly recommended). This button also allows you to drop executables on it to automatically display their manual page, if they have got one. Note that the default value of the "Manual Page" dialog is the contents of the current X selection, so this can be used for some sort of "context sensitive" help. Keep in mind that all the buttons can be configured by you! See section ``Configuration of TkDesk'' for details on how to do this. 7. The Built-in Editor The built-in editor of TkDesk is meant to be a simple ASCII editor for editing files smaller than, say, 500kB. A single editor window can handle an arbitrary number of buffers each of which contains another file. Files can either be loaded through the "File" menu of the editor or by simply dropping one or more files on the text field of the editor window from one of the file listboxes. An editor window contains the following menus in its menu bar: File Contains entries to load, save, and print files, and to close the current buffer, current window, or all windows. Edit The first entry provides access to the editor's "Undo" functionality. Note that the undo buffer may contain a maximum of 500 events. This menu also contains entries for managing TkDesk's own text clipboard. Also provides entries to search for text (regular expressions), replace text, or find the next occurence of the currently selected text. The entry "HyperSearch" is a special form of search: all matching lines are displayed in a file listbox. If one of its entries is clicked on, the editor automatically displays that line at the top of the text field. This can be useful for jumping to headings, function definitions etc. Oh yes, and the regular expressions entered here are also saved with the other histories. Options The "Auto Indent" option determines whether the cursor is automatically indented after hitting Return. If the "Send to Netscape" option is set, the current file will be loaded by (maybe a running) Netscape each time it is saved. Useful when editing HTML files. Buffers Lists the buffers of the editor window. Buffers can be selected from this menu. Configuration This menu is only available when editing one of TkDesk's configuration files. It lets you save the file and reload it into TkDesk by selecting the corresponding menu entry or pressing F5, or exactly the same plus closing the buffer by invoking the next entry or pressing F6. This way configuration files can be edited and reloaded very quickly into TkDesk. The text area provides all the more common Motif- and Emacs-like key- bindings (including Control-Space for selecting an area of the text). Maybe it should be mentioned that Control-C/X/V do not work on the X selection but on TkDesk's own clipboard. Use the middle mouse button to paste from the X selection into TkDesk or from TkDesk into another application. 7.1. Marks Marks can be set with Control-[1-9], and be jumped to with Alt/Meta-[1-9]. You can always jump back with Control-0. They work across buffer and editor window boundaries, and are currently only valid as long as the buffer in which the mark was set is open. 8. Using the TkDesk Server In order to allow remote control of TkDesk, i.e. to allow programs outside of TkDesk to have TkDesk perform certain tasks, TkDesk implements a TCP/IP server that can be used to send Tcl scripts to TkDesk. Whether this server is "up" or not is determined by the option "TkDesk Server" that is to be found in the "Options" menu. Currently, only programs running on the same machine as TkDesk may connect to the server, but multiple TkDesk servers started from different users may be running at the same time. The server is a much faster way to send command to TkDesk then to use the Tcl/Tk send command, as that requires a second Tcl/Tk shell ("wish") to be started to do the send. However, using a TCP/IP server that's basically system-wide accessible to perform arbitrary command under the accout of the user who started TkDesk and thus the server brings with quite a big security risk that TkDesk tries to reduce by keeping the port the TkDesk server uses secret. The port to be used is calculated randomly at server start-up, and is saved into a file that can only be read by the user who started TkDesk. To prevent "guessing" of the port number a generated key is also stored in this file that get passed to the server. The client performing the communication with the TkDesk server gets installed with TkDesk; its name is tkdeskclient. This command expects exactly one argument which will be directly sent to the server and evaluated there as a Tcl script. E.g. you could do a tkdeskclient "dsk_view id" to find out who you are :-). Along with TkDesk a number of front-end shell scripts for tkdeskclient are installed, which comprise of the following: cd-tkdesk ?path? Let's the currently active TkDesk file list or browser window (i.e. the one the mouse pointer was over last) change its directory to path if path is given, or returns the current directory of the active window. ed-tkdesk ?+linenum? ?file? ... Without argument opens a new editor window, or loads all files given as arguments into the editor (and into the same window if you're using a multi-buffer capable editor, such as the built-in one). If a file is preceded by +linenum, i.e. something like +20, the built-in editor will position the cursor on the line when displaying the file that's given in the following argument. od-tkdesk ?dir? If no arguments are given opens a file list window for the current directory of the shell/program where the command was issued, or opens a window for the directory specied in dir. op-tkdesk ?file ...? For each file performs its default action (the one defined first in its corresponding popup menu as defined in "Popups"), or asks for a command to execute if no files are given. pop-tkdesk file Displays the popup menu for file, as defined in the "Popups" config file. The popup menu may also be controlled by the keyboard: Up, Down, Return, and Esc keys do what you would expect. Note that all of these scripts are merely examples of possible usages of tkdeskclient. Use your imagination to find new and even more exciting applications! :-) 9. Configuration of TkDesk Currently, TkDesk can be configured only by editing ASCII files. This is not necessarily a drawback, because this way you can add complex Tcl/Tk procedures to the configuration files. GUI based configuration is planned for one of the next releases. If you don't know Tcl/Tk: Don't despair! For the biggest part of TkDesk's configuration files it is absolutely not necessary for you to know how to program in Tcl/Tk, since you just have to modify values or extend the examples I and others have provided. And to those who want to exploit all of the power available by using Tcl/Tk as TkDesk's configuration language, please have a look at the answer to FAQ ``Where can I find out more about Tcl/Tk?''. The configuration files can be accessed from the TkDesk menu via the Configuration submenu, or from the "Configuration" submenu of the popup menu of the application bar's first button. The built-in editor will then appear with the selected configuration file(s) already loaded. Each configuration file contains pretty detailed comments on what the individual settings are for, and how they can be modified. Well, at least they contain examples, which can guide your modification attempts. Once you have modified the configuration file, you can save it and reload it into TkDesk by making use of the entries of the editor's "Configuration" menu described in section ``The Built- in Editor''. Use F5 to save the file and reload it into TkDesk, and F6 to do the same but additionally close the configuration file. As already mentioned, each configuration file contains directions on how to modify its definitions. The general advice is: Simply look at the example definitions, because these make things a great deal clearer than any explanation can. Tip: In case your looking for the definition of a specific part of TkDesk, e.g. for the popup menu of pdf files, use the "Find..." entry in the "Configuration" menu. Just enter pdf in the "String:" field, hit enter, and double-click the found file. 9.1. Configuration Files All of these are "sourced" (i.e. processed) by TkDesk during start-up. This happens in the following order: 1. System 2. ButtonBar 3. Preferences 4. FileTags 5. Commands 6. Directories 7. Popups 8. AppBar 9. Sounds 10. Local (if existent) 9.1.1. AppBar This configuration file lets you define all aspects of the application bar (apart from its layout, use the entries of the submenu "Application Bar" of the desk-button's popup menu for this). This is what you can define here: o If the application bar should be managed by the window manager just as an ordinary window. This is disabled by default. o The maximum number of buttons in a column (vertical layout) or row (horizontal layout). Yes, the application bar can contain several columns or rows! o The fonts to use for the time and date display. o Period in seconds after which to update the system load and mailbox displays, where to check for new mail, plus several additional mail related options. o Icons and commands to use for the dial-up networking button. o Icons and fonts to use for the trash button, and whether to display its current disk usage in the appbar. o The actual button definitions, ie. which image to display, and the entries of the (possibly cascaded) popup menu. See below for the syntax used. 9.1.2. ButtonBar This file defines the contents and appearance of the Button Bar, which is displayed underneath the menu bar of TkDesk's file browser (tkdesk(button_bar)) or file list windows (tkdesk(small_button_bar). Both button bars are configured independently from one another. The third list defined here (tkdesk(dir_button_bar)) is for directory- specific button bars which will be dynamically displayed and hidden when the respective directory is entered or left. These will be displayed in addition to the standard button bar. 9.1.3. Commands In this file you can define entries which will be added to TkDesk's "Command" menu, for example to uncompress all selected files, manage RPM package files, or do whatever you can possibly imagine. 9.1.4. Directories Here you can define entries and cascaded submenus of directories which will be added to TkDesk's "Directories" menu. More details are contained in the "Directories" file itself. You can also define a list of directories whose parent directories are not to be displayed in file browser windows, as is usually the case with your home directory. This can speed up display of directories underneath an AFS directory, for instance. Another list lets you define action that are to be performed when a specific directory is opened. This can be used for example to auto- mount floppies and CDROM's. 9.1.5. FileTags Contains definitions for color and font of specific file types (as distinguished by their extension) as well as standard values for regular files, executables, and directories, that will be used in TkDesk's file lists. Also the icons displayed when the "Add Icons" option is selected are configured here, as well as the icons used for the desk items, i.e. files and directories which have been dropped on the root window. 9.1.6. Local This file does not exist when TkDesk gets installed, but still appears in the "Configuration" menu. This is because if it exists it will be sourced during start-up of TkDesk, so this is the ideal place to put local extensions of TkDesk. 9.1.7. Popups The popup menus that appear when the right mouse button is clicked over a file or directory are defined here. There are individual definitions for the popup menus of directories, executables, and of other files. To find the correct popup menu for a file TkDesk checks the mask of each entry of a popup list one after the other from the beginning of the list to its end, so the masks should become more general towards the end of the list. An additional popup list (tkdesk(fileops,popup)) may be defined here for use in the "Copy, Move, etc." dialog. 9.1.8. Sounds Here the command to be used for playing sounds can be defined, as well as the sound files which are to be played at certain events. TkDesk comes with a few AU sound files which are located in the sounds subdirectory of TkDesk's library directory. You can find out where this is by looking at the fifth line of the tkdesk script. 9.1.9. System All the more "basic" features of TkDesk can be configured here. These are: o Colours and fonts to be used by TkDesk, o Default size of file listboxes, o Definitions of shell commands which are used by TkDesk to copy, move, delete, etc. files. These should be fine as they are in most cases, but you may want to tweak them for your system, so take a look at these definitions. o The default command for printing, o Which editor to use, plus settings for the built-in editor, o Periods for automatic refreshing of the file lists and saving of TkDesk's configuration, o Maximum number of entries in history menus, o If TkDesk should ask for confirmation when the user wants to quit TkDesk, o If TkDesk should allow the menus to be "tearoff-able", o The paths in which TkDesk will look for images and sounds used in the configuration files, o Icons to use for file list and browser windows, and for the help window, o Whether to use "focus follows mouse" default or not, o Settings for the desk items, e.g. whether to let them be managed by the window manager, o Commands to execute after start-up and before shutdown (of TkDesk). 9.2. The TkDesk API Many of the values to be specified for TkDesk's configuration files are Tcl scripts. Also, you can define your own Tcl proc's to use in these scripts. To allow better control of TkDesk, TkDesk externalizes a couple of proc's which could be called the "TkDesk API" (although currently they aren't much more then a random set of more or less useful proc's, but anyway). The proc's I currently think are part of the API are the following: dsk_msg_alert msg Display msg in an alert dialog box. dsk_msg_error msg Display msg in an error dialog box. dsk_msg_info msg Display msg in an info dialog box. dsk_confirm msg script Display msg in a dialog box with "OK" and "Cancel" buttons. Evaluate script if user pressed "OK". dsk_debug msg If TkDesk runs in development mode (e.g. by setting the -devel command line option) print msg to stderr. dsk_ask_dir Ask for a directory to open. dsk_ask_exec Ask for a command to execute or file to open. dsk_exec command Execute command in the background. When the command completes its exit code will be displayed in the status bar of all file browser windows. dsk_exec_as_root command Asks for the root password, and executes command in the backgroud under root permission if it was okay. dsk_view command Execute command in the background and displays its output (stdout and stderr) in a built-in editor window. dsk_view_as_root command Same as dsk_view but asks for the root password first. dsk_path_exec path command Executes command in path and in the background. dsk_path_view Executes command in path and in the background, and displays its output in an editor window. dsk_edit ?+linenum? ?file? ... Without arguments asks for a file to open, or opens all files in the same editor window, positioning the cursor at line linenum if given. If file is "New File" a black editor window will be opened. dsk_busy Displays mouse as "busy" and locks all TkDesk window from receiving mouse events. dsk_lazy Displays mouse as "lazy", and makes all TkDesk windows responsive again. dsk_active what Returns information about the currently active file browser or list window: If what is "dir" it returns its directory; if it is "sel" it returns all selected items as a Tcl list; if it is "win" it returns the Tk name of the active window. dsk_select X ?names? If names is not given, copies the full path names of the files selected in the currently active file browser or list window to the X selection. Otherwise just their names are copied. dsk_read_string msg ?script? ?dontpaste? Displays $msg in a dialog box where the user can enter an arbitrary string. If script is not given, the string will be returned; if it is given the variable dsk_read_string will be set to the user's input and script will be evaluated. If "dontpaste" is passed as a third argument to this command the current X selection will not be pasted into the entry field. dsk_filesel message path Opens TkDesk file selection dialog using message as the label, and presetting the entry field with path. Path is also used as the filter for the full file selection box. dsk_refresh ?file ...? For each file all windows displaying the corresponding directory is refreshed. File may also be a directory. dsk_sound sound Plays sound if a sound command has been defined and sound is switched on. Sound is the second index in the tkdesk(sound,xxx) array as defined in the "Sounds" config file ("xxx" in this case). dsk_copy ?source ...? ?dest? Opens the "Copy, Move, etc." dialog box, filling in "Source" and "Destination" fields if provided. dsk_dialup phonenumber Without arguments asks for a phone number, and passes this to the command specified in tkdesk(appbar,dialup,cmd_up), else it does the same without asking. dsk_find_files ?options? Opens the "Find Files" dialog, presetting its fields from the options. The following options are available: -path path, -mask mask, -string string, -regexp regexp, -extreg extreg, -type "all|file|dir|sym|socket|pipe", -owner owner, -group group, -modified mod, -accessed acc, -size size, -case, -samefs, -followsym. If the option -doit is given the search will start as soon as the dialog is displayed, without having to press "Search". dsk_mail file ?string? Asks for an email address where to send file. If file is the empty string, string will be sent instead. dsk_netscape what ?url? ?args? This proc is used to communicate with a running netscape. If Netscape is not running yet it will be started first. What may be "file", "url", or "rcmd" (Communicator only); url may be a regular URL, a file name, or, if what is "rcmd", it may be "mail", "news" or "edit". Args may be "window", or "raise", or both. dsk_periodic command seconds Opens the "Periodic Execution" window and executes "command" every "seconds" seconds. dsk_open viewer file Opens file by performing its default action. Viewer should be given as ""; I just didn't manage to get rid of this obsolete argument yet. dsk_open_browser dir Opens dir in a new file browser window. dsk_open_dir dir Opens dir in a new file list window (unless the option "Always in Browser" is set). dsk_openall ?files? Opens all files if given, or all currently selected files by performing their respective default action. 9.3. Shortcuts When specifying Tcl scripts in the popup and appbar configuration lists (tkdesk(popup,...) and tkdesk(appbar)), and in some other places as well (precide, hus?), you can use a number of shortcuts that will be expanded just before evaluation: %s Will be replaced with the full pathname of the first selected file. %f Will be replaced with its filename only. %b Will be replaced with its filename without extension ("basename"). %d Will be replaced with its directory only. %A Will be replaced with a Tcl list of the full pathnames of all currently selected files, or of the files dropped on an appbar button. %a Same as %a, but replaces with a list of filenames only. %B Same as %A but doesn't complain if no files are selected. Instead it will be replaced with the empty string. %D Will be replaced with the directory of the currently active file list or browser window. %x Will be replaced with the contents of the X selection. %X Same as %x but doesn't complain if the selection is empty. %S For the "Copy, Move, etc." popup menu; will be replaced of what's been entered into the "Source" text entry field. %D For the same popup menu; will be replaced of what's been entered into the "Destination" text entry field. 9.4. Utilities To make life while configuring TkDesk a little easier as long as there's no GUI configuration available yet, TkDesk provides four little "helper" tools that help with selecting colors, fonts, icons, and sounds, by using graphical mouse-based dialog boxes. These are accessed through the "Configuration" menu, and are basically all handled in the same way. Each of these dialogs contains three buttons: Insert Works only correctly if the dialog was invoked from the editor's "Configuration" menu. Inserts the current value at the cursor position in that editor window. Select Copies the selected value to the X selection, so it can be pasted at arbitrary places using the middle mouse button. Close Closes the dialog. 10. Frequently Asked Questions 10.1. How can I change the position of the application bar? The appbar should have a "handle" at the upper or left edge that can be used to drag the appbar around the screen by just pressing the left mouse button over it. You can also drag the application bar around simply by holding down the Alt- or Meta-key and simultaneously pressing the left mouse button over the application bar. You can also invoke the "Move..." entry from the desk-button's popup menu to do the same without having to press Alt/Meta. The application-bar configuration file, "AppBar", allows you to set a variable named tkdesk(appbar,wm_managed) which can be used to have the apllication bar managed by the window manager, although this is usually not necessary. 10.2. Can I have transparent icons? No, but have a look at the answer to the next question. As far as I know to have transparent icons in X you need to make use of X11's SHAPE extension. Now as raw X programming is something only for the very brave, I didn't look into this any deeper yet. Any takers? 10.3. How can I change the background colour of the icons and desk items? The background colour of icons used when the window manager iconifies a window can be set in the configuration file "System". The variable you are looking for is tkdesk(color,icon_background). By setting this variable to the same colour as your root window you can achieve the effect of transparent icons. You can define the colour either as a normal name (such as "grey", "blue") or in the form #rrggbb. 10.4. How can I have a different set of desk items on each virtual screen? 10.4.1. FVWM and similar or derived window managers First, you have to set the variable tkdesk(desk_items,wm_managed) in the System config file to 1. Then you have to configure fvwm to not decorate windows of class dsk_DeskItem. For instance: Style "dsk_DeskItem" NoTitle, NoHandles, WindowListSkip, BorderWidth 0 10.4.2. CDE window manager As for FVWM, you first have to set the variable tkdesk(desk_items,wm_managed) in the System config file to 1. To tell the CDE window manager (dtwm) to not decorate desk items you have to add the following line to either the file Dtwm or .Xdefaults in your home directory, and then restart dtwm: Dtwm*dsk_DeskItem*clientDecoration: none 10.5. How do I configure FVWM mini-icons for TkDesk? For fvwm95's Style command (like fvwm2 from which it comes) you can use a variety of things to identify a tkdesk window - you don't have to use the name only; you can also use the window's class name or resource string. To find out what the window class and resource string are for a certain type of tkdesk window, use the FvwmIdent module (if you use Debian, this will be under the Window Managers->Modules menu; if you're using the default fvwm95rc that comes with the fvwm95 distribution, this will be under the Programs->Modules menu; I don't know about others). Anyway, FvwmIdent tells me that the window opened by tkDesk's "Open Browser..." menu has a class name of dsk_FileViewer, so I open the FvwmConsole (or FvwmTalk) module, and enter the commands: Style dsk_FileViewer TitleIcon mini-filemgr.xpm Recapture Then the browser window appears with the appropriate icon. To get the window opened by the "Open Directory..." menu, use: Style dsk_FileList TitleIcon mini-filemgr.xpm (The "recapture" command is only necessary to apply the command to windows already open; it isn't necessary in your config. file) (Contributed by Daniel Martin, dtm12@jhunix.hcf.jhu.edu) 10.6. Configuring the appbar or popup menus to execute (export foo=bar; program;) doesn't work. Yes, this is a bit tricky. What you need to do is the following: dsk_exec sh -c {export foo=bar; program} 10.7. Composing characters doesn't work in TkDesk's editor. Currently you have to edit the file cb_tools/bindings.tcl in TkDesk's library directory to make this work. Locate the two lines containing the word "XKB"; all you need to do is to comment out the following lines by prepending a '#'. 10.8. TkDesk's layout seems to be screwed. I can't get the appbar displayed anymore. TkDesk saves all layout information in the file /.tkdesk/_layout, so this is the place to look for bad lines. If in doubt you can safely delete this file and all should be back to normal. In case of the appbar not showing up you can try the following (sent by Jochem Huhmann, joh@unidui.uni-duisburg.de): 1. Quit tkdesk 2. Open the file ".tkdesk/_layout" within your home directory with your favorite editor 3. Delete the line looking like "Toplevel dsk_appbar 1 66x740+956+0" 4. Write the file back to disk 5. Restart tkdesk. It will place the AppBar according to its defaults now. 6. Place the AppBar as you like (it doesn't like horizontal layout too much) 7. Do a "TkDesk/Save All Now" 10.9. On my RedHat 5.x system the appbar clock shows the wrong time. This is from Bryan Venable, spif@vousi.com: This may have to do with the location of the zoneinfo directory. For libc5 I believe it's /usr/lib/zoneinfo, whereas for glibc it'd be /usr/share/zoneinfo. Try making a symbolic link from whichever you have to whichever you don't. There is a fix to Red Hat 5.0 which has to do with this, but in that situation the problem is with libc5 programs running on a system "optimized" for glibc. And Raul Quiroga, quiroga@cartan.math.cinvestav.mx, also has got some advice for this: Concerning the problem described below I received several suggestions. Some allowed to get the date in the tkdesk right but an incorrect one with the date command. Finally what I did is set the time with timeconfig to Universal with "Hardware clock set to GMT" checked; after a reboot both the appbar and date reported the correct time. Thanks all for your help. 10.10. TkDesk complains about "invalid command name wm" and won't start up There seem to be two solutions to this problem: One is if you're running on a RedHad Linux 5.x system, the libc5 that's packaged and installed in /lib may be too old a version. If ls /lib/libc.so.5.* on your system gives something like /lib/libc.so.5.3.xx you should upgrade to at least 5.4.20. I think you can get a newer version of libc from sunsite.unc.edu in /pub/Linux/GCC. The other solution that should always work is to do the following (thanks to Ike Hishikawa, ike@hishikawa.f.uunet.de, for this one): Assuming that the tarball was unpacked under /usr/local, open /usr/local/bin/tkdesk with your favorite editor. At the very top of the file it should say: #!/bin/sh #-*- tcl -*- \ PATH=/usr/local/bin:$PATH ;#\ exec tkdesksh "$0" "$@" After the 3rd line, insert two lines pointing to the location of tcl/tk libs, so that you get: #!/bin/sh #-*- tcl -*- \ PATH=/usr/local/bin:$PATH ;#\ export TCL_LIBRARY=/usr/local/lib/TkDesk/tcl_lib ;#\ export TK_LIBRARY=/usr/local/lib/TkDesk/tk_lib ;#\ exec tkdesksh "$0" "$@" This did the trick for me :) Hope this helps, 10.11. I cannot launch other Tcl/Tk applications from within TkDesk Probably your version of TkDesk sets the environment variables TK_LIBRARY and TCL_LIBRARY, which confuses other Tcl/Tk apps. Unset these variables in the config files before the invocation of the problematic commands, eg. replace xterm with sh -c {unset TK_LIBRARY TCL_LIBRARY; xterm} (contributed by Christoph Dalitz, dalitz@infotech.de) 10.12. I'd like TkDesk to do this and that. How can I achieve this? The first place to start are the various configuration files of TkDesk. These can be accessed either by the "TkDesk/Configuration" menu of the file browser windows, or by the "Configuration" submenu of the popup menu of the very first button of the application bar of TkDesk. Since TkDesk uses Tcl as the language for its configuration, and these configuration files are simply "source"ed, you could add any sort of Tcl proc for instance to the configuration file "System". This proc would then be available in every other configuration file as well. With the set of commands provided by TkDesk, which are listed e.g. in the configuration file "Popups", TkDesk provides a very powerful platform for the user who knows Tcl. 10.13. Is there a TkDesk mailing list? There two mailing lists dedicated to TkDesk, one for discussing general installation/usage topics (tkdesk-users) and one targeted more for TkDesk-related development topics (tkdesk-code). You can subscribe to the tkdesk-users list here: <https://lists.sourceforge.net/lists/listinfo/tkdesk-users>. To post questions, etc., send email to tkdesk-users@lists.sourceforge.net. It you are interested in contributing to the development of TkDesk, you can subscribe to the tkdesk-code list here: <https://lists.sourceforge.net/lists/listinfo/tkdesk-code>. Once you have subscribed, you can post using the address tkdesk- code@lists.sourceforge.net. 10.14. Where can I find out more about Tcl/Tk? The official Tcl/Tk homepage is at <http://www.tcl.tk/>. There is also a newsgroup dedicated to Tcl/Tk: comp.lang.tcl. 11. Tips and Tricks This section currently contains just one tip on how to combine TkDesk and XEmacs. If you have any procs or other stuff in your configuration file which you consider could be useful for others as well, or just think it's generally cool, please send me an email, so that I can add it to this section! Thanks! 11.1. TkDesk and XEmacs If you are using XEmacs 19.12 or later you can couple TkDesk and XEmacs quite closely together by adding the following proc into any of your configuration files (I have it in "Popups"): ______________________________________________________________________ proc xemacs_load {what {where same}} { switch $where { "same" { exec gnudoit -q (find-file \"$what\") } "other" { exec gnudoit -q (find-file-other-window \"$what\") } "frame" { exec gnudoit -q (find-file-other-frame \"$what\") } "scratch" { exec gnudoit -q (switch-to-buffer-other-frame \"*scratch*\") } } } ______________________________________________________________________ And now my generic popup menu for files matching * reads: ______________________________________________________________________ {{*} { {{Edit} {dsk_edit %s}} {{XEmacs} {xemacs_load %s}} {{Other Window} {xemacs_load %s other}} {{Other Frame} {xemacs_load %s frame}} - {{Print} {dsk_print %s}} }} ______________________________________________________________________ This way you can load files from TkDesk into a running XEmacs! This assumes that you have the command gnudoit somewhere in your path, and have started the XEmacs server. This can be done by adding the following line to your ~/.emacs: (gnuserv-start) �����������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/doc/od-tkdesk.1��������������������������������������������������������������������������0100644�0001750�0000764�00000002146�10037077413�013446� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" .\" Copyright 1996 by Christian Bolik (Christian.Bolik@mainz.netsurf.de) .\" .TH TKDESK 1 "TkDesk 2.0, 04/15/2004" "" "" .UC 4 .SH NAME od-tkdesk \- remotely open a directory using TkDesk .SH SYNOPSIS .B od-tkdesk [ \fIdirectory ] .br .SH DESCRIPTION If invoked with no argument, od-tkdesk will open a new TkDesk file browser or list window displaying the contents of the current working directory. Whether a browser or list window will be opened is determined by the menu entry "Always In Browser" in TkDesk's "Options" menu. .PP If .I directory is given to od-tkdesk, its contents will be displayed in a new TkDesk window. .PP Od-tkdesk communicates with TkDesk through a TCP/IP server that needs to be started from within TkDesk using the menu entry "TkDesk Server" from TkDesk's "Options" menu. .SH OPTIONS .IP \fIdirectory The directory whose contents is to be displayed. .SH "FILES" .IP tkdeskclient Usually in "/usr/local/bin", this program is used to perform the actual communication with TkDesk. .SH "SEE ALSO" tkdesk(1), cd-tkdesk(1), ed-tkdesk(1) .SH "AUTHOR" Christian Bolik (Christian.Bolik@mainz.netsurf.de) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/doc/tkdesk.1�����������������������������������������������������������������������������0100644�0001750�0000764�00000017430�10037077060�013046� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" .\" Copyright 1996 by Christian Bolik (Christian.Bolik@mainz.netsurf.de) .\" .TH TKDESK 1 "TkDesk 2.0, 04/15/2004" "" "" .UC 4 .SH NAME TkDesk \- a Graphical File and Desktop Manager for the X Window System .SH SYNOPSIS .B tkdesk .RB [ \-configdir\ \fIdir ] .RB [ \-debug ] .RB [ \-default ] .RB [ \-develop ] .RB [ \-iconic ] .RB [ \-layout\ \fIfile ] .RB [ \-startdir\ \fIdir ] .RB [ \-twm ] .RB [ \-? | \-\-help ] .br .SH DESCRIPTION TkDesk provides a rich set of functions to help you manage your files and efficiently navigate your file systems. It also provides several desktop and system managing capabilities such as accessing the external devices of your workstation, starting programs, monitoring the system load and mailbox etc. .PP The main parts of TkDesk are the following: .PP 1. One or more .I file browser windows (or just "browsers"). These display the contents of the currently selected directory in the rightmost listbox and the contents of a configurable number of parent directories in the listboxes left to it. In addition this window contains a menu bar, a button bar, and a status bar. .PP 2. One or more .I file list windows. These display the contents of just the currently selected directory. These windows also contain a menu bar, a button bar, and may also contain a status bar. .PP 3. An .I application bar (or "appbar"). This is a set of buttons displaying icons or other graphical output that can be configured to execute certain commands. All of the buttons provide a possibly cascaded popup menu that is accessed by pressing the right mouse button over any appbar button. Files may be dragged from any file browser or list window and dropped onto appbar buttons that have been configured to be drag'n'drop targets. The contents of the appbar is configured in the "AppBar" configuration file (located in "~/.tkdesk"). .PP 4. The .I built-in editor (or just "editor"). The editor provides multiple buffers in the same window, virtually unlimited "undo", marks, search and replace, etc. Files may be dropped onto any editor window from TkDesk's file listboxes or the desktop. .SH BASIC USAGE The listboxes contained in the file browser and list windows display the contents of their respective directory. They provide the following mouse button bindings: .IP Single-1 Select the file under the mouse pointer, deselect all others. .IP Control-Single-1 Select or deselect the file under the mouse pointer, without deselecting any other file. .IP Shift-Single-1 Select all files between and including the one under the mouse pointer and the one that was last selected by Single-1 or Control-Single-1. Deselect all others. .IP Double-1 Select and open the file under the mouse pointer, deselect all others. The command that will be used to open the file is the first entry of the file's popup menu (see next item). .IP Control-Double-1 Select the file under the mouse pointer, and ask for a command to be executed on that file. .IP Press-2 Used to initiate a drag and drop operation. Valid drop targets are all other file listboxes, appbar buttons that have been configured to be drag'n'drop targets, editor windows, and the root window (a.k.a. desktop). .IP Press-3 Display the file's or directory's popup menu. The contents of these menus are configured in the "Popups" configuration file (located in "~/.tkdesk"). .PP The display options of any file listbox can be configured through the menubutton right above the listbox, displaying the name of the displayed directory, and the file mask currently set. It can also be used as a drag and drop source (by pressing mouse button 2) and target. .PP TkDesk makes heavy use of cascading popup menus. Basically in every text entry field that's used for entering paths of file names pressing the right mouse button brings up a popup menu of parent directories. Next to most test entry field there is a menu button displaying a turned-over triangle that gives access to a menu containing a history of entered strings. .SH OPTIONS .IP \-configdir\ \fIdir By default TkDesk reads its configuration either from the files contained in the directory "~/.tkdesk" (see section .I FILES below), or if it does not exist, from TkDesk's library directory (usually "/usr/local/lib/TkDesk"). This options lets TkDesk load its configuration files from directory .I dir. .IP \-debug Switch on "debug mode". This generates some output on stderr that may be used for debugging purposes. .IP \-default Let TkDesk read its configuration from its library directory. This means that TkDesk will start using the default configuration. .IP \-develop Switch on "development mode". This adds a "Development" submenu to the "TkDesk" menu. .IP \-iconic Open all file browser and list windows in iconic state when TkDesk starts up. .IP \-layout\ \fIfile Load and save TkDesk's window layout from and to .I file. If .I file doesn't exist the default layout is used. .IP \-startdir\ \fIdir Display .I dir in the first file browser window that's opened. .IP \-twm Some window managers, namely .I twm cannot handle icon windows correctly. TkDesk uses these for colored icons. This options switches to monochrome icons. .IP "\-?, \-\-help" Displays the command line options available. .SH "FILES" All of the following configuration files are accessible directly inside the "Configuration" submenu of the "TkDesk" menu. The default application bar contains the same menu in the first button (the one with the comet). .IP ~/.tkdesk/AppBar Definition of the application bar. .IP ~/.tkdesk/ButtonBar Definition of the button bars used in file browser and file list windows, and of directory-specific button bars. .IP ~/.tkdesk/Commands Contains a list of menu entries that will be added to the "Commands" menu. .IP ~/.tkdesk/Directories Definition of the contents of the "Directory" menu and of actions performed when specific directories are opened. .IP ~/.tkdesk/FileTags Defines how files and directories are displayed, both by default and on a per-object basis (using shell-like "glob" patterns). .IP ~/.tkdesk/Local Initially, this file does not exist. If it does though, it is sourced (evaluated) when TkDesk starts up. This is the right place to put any local extensions. .IP ~/.tkdesk/Popups Definition of the file- and directory-specific popup menus. Also contains the definition of the popup menu used in the "Copy, Move, ..." dialog. .IP ~/.tkdesk/Sounds Defines which command should be used for playing sounds, if any, plus defines which sounds should be played at which TkDesk event. .IP ~/.tkdesk/System Contains definitions of colours and fonts to be used, which commands should be used for copying, deleting etc. Also contains options that affect the built-in editor and some other configuration variables. .SH "ENVIRONMENT" The static version of TkDesk is affected just by one environment variable: .IP TKDESK_LIBRARY This determines where TkDesk will look for its library (by default usually "/usr/local/lib/TkDesk"). .PP The dynamically linked version is affected by the following variables: .IP TCL_LIBRARY Location of the Tcl library files. .IP TK_LIBRARY Location of the Tk library files. .PP Note that other more general variables like PATH may also affect TkDesk. .SH "SEE ALSO" cd-tkdesk(1), ed-tkdesk(1), od-tkdesk(1) .SH "AUTHOR" Christian Bolik (Christian.Bolik@mainz.netsurf.de) .SH "BUGS" TkDesk may still have problems with files containing spaces, brackets, and braces. This is due to the fact that the largest part of TkDesk is written using Tcl, the Tool Command Language created by John Ousterhout, for which these characters are "special" in some respects. .sp If you can't get the application bar back on the screen, delete the line starting with "Toplevel dsk_appbar" from the file ~/.tkdesk/_layout and restart TkDesk. .sp Some Sun machines seem to have problems with Drag&Drop. It seems these problems go away when a more recent X server is used. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/libdesk/���������������������������������������������������������������������������������0040755�0001750�0000764�00000000000�10037657327�012357� 5����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/libdesk/Makefile.in����������������������������������������������������������������������0100644�0001750�0000764�00000002626�10020457430�014410� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This is the Makefile for the C library of TkDesk. # It is called from the main Makefile in .., which passes the CC_OPTS. #---------------------------------------------------------------- # Things you can change to personalize the Makefile for your own # site (you can make these changes in either Makefile.in or # Makefile, but changes to Makefile will get lost if you re-run # the configuration script). #---------------------------------------------------------------- # Location of Tcl header files: TCL_INCLUDE_DIR = @TCL_INCLUDE_PATH@ # Location of Tk header files: TK_INCLUDE_DIR = @TK_INCLUDE_PATH@ # Location of X11 header files: X_INCLUDE_FLAG = @TK_XINCLUDES@ # Configuration compiler flags: AC_FLAGS = @DEFS@ # Miscellaneous settings: RANLIB = @RANLIB@ CC = @CC@ AR = ar rc RM = rm -f #---------------------------------------------------------------- # The information below should be usable as is. The configure # script won't modify it and you shouldn't need to modify it # either. #---------------------------------------------------------------- CFLAGS = ${CC_OPTS} ${AC_FLAGS} -I.. -I${TCL_INCLUDE_DIR} -I${TK_INCLUDE_DIR} ${X_INCLUDE_FLAG} libname = libdesk.a OBJS = init.o dsk_ls.o misc.o util.o ot.o o_FileListbox.o \ tixImgXpm.o default: cd ..; make -k shell lib: $(libname) $(libname): $(OBJS) $(RM) $@ $(AR) $@ $(OBJS) $(RANLIB) $@ clean: $(RM) $(OBJS) $(libname) *\~ "#"* ����������������������������������������������������������������������������������������������������������tkdesk-2.0/libdesk/dsk_ls.c�������������������������������������������������������������������������0100644�0001750�0000764�00000113756�10037117721�014001� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: dsk_ls.c * Project: TkDesk * Started: 24.06.94 * Changed: 27.04.97 * * Description: ls-replacement for tkdesk. Does sorting, masking and all the * other stuff that tkdesk requires. Output format is a simple * Tcl-list or a Tcl-list of Tcl-lists. * * Copyright (C) 1996, 1997 Christian Bolik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * See the file "COPYING" in the base directory of this distribution * for more. * * ---------------------------------------------------------------------------- * * Functions: * * linked_file * perm_string * type_char * write_element * write_list * write_file_lists * qsort_by_name * qsort_by_size * qsort_by_date * qsort_by_ext * sort_by_type * process_file_list * process_file_lists * new_file_entry * new_file_list * read_list * read_file_lists * process_cmdline * dsk_ls_init * main * * ========================================================================= */ #include "libdesk.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #ifdef HAVE_DIRENT_H #include <dirent.h> #endif #include <time.h> /* for ctime */ #ifdef HAVE_UNISTD_H #include <unistd.h> /* for readlink */ #endif #include <pwd.h> /* for getpwuid */ #include <grp.h> /* for getgrgid */ #include <sys/types.h> #include <sys/stat.h> /* === Defines ============================================================= */ #if defined(S_IFLNK) && !defined(S_ISLNK) #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif #define MAXPATHLEN 1024 #define MAXFILELEN 256 #define BY_NAME 0 #define BY_SIZE 1 #define BY_DATE 2 #define BY_EXT 3 #define BY_NTH 4 #define BY_NAME_FOLD 5 #define TYPE_REG 0 #define TYPE_EXEC 1 #define TYPE_DIR 2 #define TYPE_LINK 3 #define TYPE_SOCK 4 #define TYPE_UNKN 5 #define TYPE_LDIR 6 #define TC_REG '_' #define TC_EXEC '*' #define TC_DIR '/' #define TC_LREG '@' #define TC_LEXEC '+' #define TC_LDIR '-' #define TC_SOCK '=' #define TC_UNKN '_' #define USAGE "Usage: dsk_ls [options] <path>" #ifdef MEM_DEBUG #define dsk_malloc(a, b) my_malloc((a), (b)) #define dsk_free(a, b) my_free((a), (b)) #else #define dsk_malloc(a, b) malloc((a)) #define dsk_free(a, b) free((a)) #endif /* === Typedefs ============================================================ */ typedef struct _file_entry { struct _file_entry *next; char *name; /* file name without path */ char *path; /* file name with path */ struct stat sb; /* stat buffer */ int type; /* one of TYPE_* */ char tc[2]; /* type character */ } FILE_ENTRY; typedef struct _file_list { struct _file_list *next; struct _file_entry *first; int num_files; struct _file_entry **fe_array; /* for qsorting */ } FILE_LIST; void dsk_ls_init (); int process_cmdline _ANSI_ARGS_((int argc, char *argv[])); int read_file_lists (); int process_file_lists (); int free_file_lists (); void write_element _ANSI_ARGS_((FILE_ENTRY *fe, char *xbuf)); /* === Global Variables ==================================================== */ /* these are set throught the cmd-line */ /* They are reset on every call of dsk_ls by calling dsk_ls_init! */ int par_every_dir = 0, /* read files for every dir in path? */ par_sort_type = BY_NAME, /* how to sort the file-lists */ par_sort_by_type = 0, /* sort by type? (only distincts dir/other) */ par_show_all = 0, /* list all files? (starting with .) */ par_invert = 0, /* invert the file-lists? */ par_add_date = 0, /* add date to every file? */ par_add_size = 0, /* add size to every file? */ par_long_listing = 0, /* long listing? (implies date and size) */ par_no_points = 0, /* hide . and .. entries? */ par_append_char = 0, /* append *,@,/,=," " ? */ par_print_num = 0, /* print # files as first entry? */ par_one_file = 0, /* treat a directory like other files? */ par_full_path = 0, /* prepend full path to entries? */ par_only_directories = 0; /* list only directories? */ char par_path[MAXPATHLEN] = ""; /* path for which to list files */ char par_mask[64] = ""; /* others */ FILE_LIST root_of_lists = {NULL, NULL, 0, NULL}; int just_one_file = 0; int num_mallocs, num_frees; /* variables used when called from dsk_ls_and_tag_Cmd */ int o_iargc, o_start, o_end, o_current, o_maskc; int o_maskinv = 0, o_dotreg = 0; char **o_iargv, **o_maskv, *o_maskv_ptr; /* ========================================================================= */ /* Functions */ /* ========================================================================= */ void *my_malloc (size_t size, char *caller) { void *ptr; fprintf (stderr, "malloc %d bytes", size); ptr = malloc (size); fprintf (stderr, " --> %p (%s)\n", ptr, caller); num_mallocs++; return ptr; } void my_free (void *ptr, char *caller) { fprintf (stderr, "free %p (%s)\n", ptr, caller); num_frees++; free (ptr); } /* ============================================================================ * Name: dsk_ls_init_and_parse_options * Desc: Called from dsk_ls_and_tag_Cmd, initializes internal variables, * and parses the command line. * In : argc, argv, path * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int dsk_ls_init_and_parse_options (argc, argv, path) int argc; char **argv; char *path; { dsk_ls_init (); #ifdef MEM_DEBUG { int i = 0; fprintf (stderr, "dsk_ls_init_... "); while (i < argc) fprintf (stderr, "%s ", argv[i++]); fprintf (stderr, "%s\n", path); } #endif strncpy (par_path, path, MAXPATHLEN); par_path[MAXPATHLEN] = (char) NULL; if (process_cmdline (argc, argv) != TCL_OK) return (TCL_ERROR); return TCL_OK; } /* dsk_ls_init_and_parse_options */ /* ============================================================================ * Name: dsk_ls_read_and_sort * Desc: Called from dsk_ls_and_tag_Cmd, reads and sorts the file list. * In : iargc, iargv (ignore masks), mask * Out : number of matching files read * ------------------------------------------------------------------------- */ int dsk_ls_read_and_sort (iargc, iargv, maskc, maskv, mask_invert, dot_reg) int iargc; char **iargv; int maskc; char **maskv; int mask_invert; int dot_reg; { o_iargc = iargc; o_iargv = iargv; o_maskc = maskc; o_maskv = maskv; o_maskinv = mask_invert; o_dotreg = dot_reg; read_file_lists(); process_file_lists(); if (par_invert) { o_start = root_of_lists.num_files - 1; o_end = -1; } else { o_start = 0; o_end = root_of_lists.num_files; } o_current = o_start; return (root_of_lists.num_files); } /* ============================================================================ * Name: dsk_ls_next_file * Desc: Called from dsk_ls_and_tag_Cmd, returns next file * In : iargc, iargv (ignore masks), mask * Out : 1: more files to read, 0: done * ------------------------------------------------------------------------- */ int dsk_ls_next_file (elembuf, nametcbuf) char *elembuf; char *nametcbuf; { FILE_ENTRY *fe; if (o_current == o_end) return 0; fe = root_of_lists.fe_array[o_current]; write_element (fe, elembuf); strcpy (nametcbuf, fe->name); strcat (nametcbuf, fe->tc); if (par_invert) { o_current--; } else { o_current++; } return 1; } /* ============================================================================ * Name: dsk_ls_cleanup * Desc: Called from dsk_ls_and_tag_Cmd, frees allocated memory * In : - * Out : - * ------------------------------------------------------------------------- */ void dsk_ls_cleanup () { free_file_lists(); #ifdef MEM_DEBUG fprintf (stderr, "num_mallocs: %d, num_frees: %d %s\n", num_mallocs, num_frees, num_mallocs != num_frees ? "*unequal*" : ""); #endif } /* ============================================================================ * Name: ignore_file * Desc: Called from new_file_entry, checks if the file is to be ignored. * In : fe - pointer to current file entry * Out : 1: ignore; 0: don't * ------------------------------------------------------------------------- */ int ignore_file (fe) FILE_ENTRY *fe; { int match = 0; if (o_iargv == NULL && o_maskv == NULL) return 0; if (o_maskv != NULL) { int k; for (k = 0; k < o_maskc; k++) { /*printf ("%d: %s\n", k, o_maskv[k]);*/ if (Tcl_StringMatch (fe->name, o_maskv[k])) { match = 1; break; } } if (!match && !o_maskinv) return 1; } if (o_iargv != NULL) { int k; for (k = 0; k < o_iargc; k++) { if (Tcl_StringMatch (fe->name, o_iargv[k])) { return 1; } } } return (0 || (o_maskinv && match)); } /* ============================================================================ * Name: linked_file * Desc: if fe->name is a symlink, it returns the name of the linked file * In : fe - adress of file entry * Out : "" or name of linked file * ------------------------------------------------------------------------- */ char *linked_file (fe) FILE_ENTRY *fe; { static char linkname[MAXPATHLEN]; int len; if (S_ISLNK(fe->sb.st_mode)) { strcpy (linkname, " -> "); len = readlink (fe->path, linkname + 4, MAXPATHLEN - 4); *(linkname + 4 + len) = (char) NULL; return (linkname); } else { return (strcpy (linkname, "")); } } /* linked_file */ /* ============================================================================ * Name: perm_string * Desc: Builds a perm-string from the st_mode-field. Almost completely * copied from the O'Reilly-Book "Using C ...", Ex. 4.3. * In : fe - adress of file-entry * Out : perm-string * ------------------------------------------------------------------------- */ char *perm_string (fe) FILE_ENTRY *fe; { static char permstr[12]; static char *modes[] = { "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx" }; int i,j; mode_t mb; mb = fe->sb.st_mode; /* * Get the file type. For convenience (and to * make this example universal), we ignore the * other types which are version-dependent. */ switch (mb & S_IFMT) { case S_IFREG: permstr[0] = '-'; break; case S_IFDIR: permstr[0] = 'd'; break; case S_IFCHR: permstr[0] = 'c'; break; case S_IFBLK: permstr[0] = 'b'; break; case S_IFLNK: permstr[0] = 'l'; break; /* symbolic link */ case S_IFSOCK: permstr[0] = 's'; break; default: permstr[0] = '?'; break; } /* * Get each of the three groups of permissions * (owner, group, world). Since they're just * bits, we can count in binary and use this * as a subscript (see the modes array, above). */ permstr[1] = 0; for (i = 2; i >= 0; i--) { /* * Since we're subscripting, we don't * need the constants. Just get a * value between 0 and 7. */ j = (mb >> (i*3)) & 07; /* * Get the perm bits. */ strcat(permstr, modes[j]); } /* * Handle special bits which replace the 'x' * in places. */ if ((mb & S_ISUID) != 0) { if (mb & S_IXUSR) permstr[3] = 's'; else permstr[3] = 'S'; } if ((mb & S_ISGID) != 0) { if (mb & S_IXGRP) permstr[6] = 's'; else permstr[6] = 'S'; } if ((mb & S_ISVTX) != 0) permstr[9] = 't'; return (permstr); } /* perm_string */ /* ============================================================================ * Name: type_char * Desc: return a character that represents the type of file fe * In : fe - adress of file entry * Out : see Desc * ------------------------------------------------------------------------- */ char type_char (fe) FILE_ENTRY *fe; { struct stat rs; if (!par_append_char) return (' '); switch (fe->type) { case TYPE_REG: return '_'; case TYPE_EXEC: return '*'; case TYPE_DIR: return '/'; case TYPE_LDIR: return '-'; case TYPE_LINK: /* determine type of linked-to file */ stat(fe->path, &rs); switch (rs.st_mode & S_IFMT) { case S_IFREG: if (rs.st_mode & S_IEXEC) return '+'; else return '@'; case S_IFDIR: return '-'; default: return '@'; } break; case TYPE_SOCK: return '='; case TYPE_UNKN: return '_'; default: return '!'; } return '\0'; } /* type_char */ /* ============================================================================ * Name: get_user * Desc: Returns the login name for a given uid. * In : uid - user id * Out : pointer to static string: login name * ------------------------------------------------------------------------- */ char *get_user (uid) uid_t uid; { static char name[32]; struct passwd *pw; pw = getpwuid (uid); if (pw) { strcpy (name, pw->pw_name); } else { strcpy (name, itoa ((int) uid)); } return (name); } /* get_user */ /* ============================================================================ * Name: get_group * Desc: Returns the group name for a given gid. * In : gid - group id * Out : pointer to static string: group name * ------------------------------------------------------------------------- */ char *get_group (gid) gid_t gid; { static char name[32]; struct group *gr; gr = getgrgid (gid); if (gr) { strcpy (name, gr->gr_name); } else { strcpy (name, itoa((int) gid)); } return (name); } /* get_group */ /* ============================================================================ * Name: file_time * Desc: Returns the formatted modification time. * In : mtime * Out : pointer to static string: "Jan 31 12:00" or "Jan 31 1990" * (2nd format if file is older than half a year (182 days)) * ------------------------------------------------------------------------- */ char *file_time (filetime) time_t filetime; { time_t curtime; struct tm *filetmp, *curtmp, filetm, curtm; static char timestr[32]; time (&curtime); filetmp = gmtime (&filetime); filetm.tm_year = filetmp->tm_year; filetm.tm_yday = filetmp->tm_yday; curtmp = gmtime (&curtime); curtm.tm_year = curtmp->tm_year; curtm.tm_yday = curtmp->tm_yday; strncpy (timestr, ctime (&filetime) + 4, 12); if ((filetm.tm_year == curtm.tm_year && (curtm.tm_yday - filetm.tm_yday) < 182) || (filetm.tm_year == curtm.tm_year - 1 && filetm.tm_yday > (182 + curtm.tm_yday))) timestr[12] = '\0'; else { timestr[6] = '\0'; strcat (timestr, " "); strcat (timestr, itoa (filetm.tm_year + 1900)); } return timestr; } /* file_time */ /* ============================================================================ * Name: write_element * Desc: writes info on fe to stdout as a tcl-list-element * In : fe - address of file entry * Out : * ------------------------------------------------------------------------- */ void write_element (fe, xbuf) FILE_ENTRY *fe; char *xbuf; { char lbuf[512], *np, *buf; if (fe == NULL) return; buf = (xbuf == NULL) ? lbuf : xbuf; np = (par_full_path) ? fe->path : fe->name; if (!par_long_listing && !par_add_date && !par_add_size) { strcpy (buf, np); if (par_append_char && fe->tc[0] != '_') strcat (buf, fe->tc); else strcat (buf, " "); } else if (par_long_listing) { strcpy (buf, np); if (par_append_char && fe->tc[0] != '_') strcat (buf, fe->tc); else strcat (buf, " "); strcat (buf, "\t"); strcat (buf, itoa ((int) fe->sb.st_size)); strcat (buf, "\t"); strcat (buf, file_time (fe->sb.st_mtime)); strcat (buf, "\t"); strcat (buf, get_user(fe->sb.st_uid)); strcat (buf, "\t"); strcat (buf, get_group(fe->sb.st_gid)); strcat (buf, "\t"); strcat (buf, perm_string (fe)); strcat (buf, "\t"); strcat (buf, itoa (fe->sb.st_nlink)); strcat (buf, "\t"); strcat (buf, linked_file(fe)); } Tcl_AppendResult (dsk_interp, "{", buf, "} ", NULL); } /* write_element */ /* ============================================================================ * Name: write_list * Desc: writes a list of fl's files to stdout * In : fl - adress of file list * Out : * ------------------------------------------------------------------------- */ void write_list (fl) FILE_LIST *fl; { FILE_ENTRY *fe; int i; fe = fl->first; if (par_print_num) { Tcl_AppendResult (dsk_interp, "{", itoa(fl->num_files), "} ", NULL); } if (!par_invert) for (i = 0; i < fl->num_files; i++) write_element (fl->fe_array[i], NULL); else for (i = fl->num_files - 1; i >= 0; i--) write_element (fl->fe_array[i], NULL); /* one blank entry for drag'n'drop (to satisfie Tk) ONLY FOR LISTBOXES! */ /* Tcl_AppendResult (dsk_interp, "{}", NULL); */ } /* write_list */ /* ============================================================================ * Name: write_file_lists * Desc: calls write_list, multiple times if par_every_dir * In : * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int write_file_lists () { FILE_LIST *fl; fl = &root_of_lists; if (!fl->next) write_list (fl); else { while (fl) { Tcl_AppendResult (dsk_interp, " {", NULL); write_list (fl); Tcl_AppendResult (dsk_interp, "} ", NULL); fl = fl->next; } } return (TCL_OK); } /* write_file_lists */ /* ============================================================================ * Name: free_file_lists * Desc: Frees all the malloc'ed memory associated with each file list. * In : * Out : TCL_OK * ------------------------------------------------------------------------- */ int free_file_lists () { FILE_LIST *fl, *nfl; FILE_ENTRY *fe, *nfe; fl = &root_of_lists; while (fl) { nfl = fl->next; dsk_free (fl->fe_array, "free_file_lists"); fe = fl->first; while (fe) { nfe = fe->next; dsk_free (fe->path, "free_file_lists 2"); dsk_free (fe, "free_file_lists 3"); fe = nfe; } if (fl != &root_of_lists) dsk_free (fl, "free_file_lists 4"); fl = nfl; } return (TCL_OK); } /* write_file_lists */ /* ============================================================================ * Name: qsort_by_name * Desc: compare-function for qsort if par_sort_type == BY_NAME * In : pointers to two file-entries * Out : -1, 0, 1 (see strcmp(3) and qsort(3)) * ------------------------------------------------------------------------- */ int qsort_by_name (fe1, fe2) FILE_ENTRY **fe1; FILE_ENTRY **fe2; { return (strcmp ((*fe1)->name, (*fe2)->name)); } /* qsort_by_name */ /* ============================================================================ * Name: qsort_by_name_fold * Desc: compare-function for qsort if par_sort_type == BY_NAME * In : pointers to two file-entries * Out : -1, 0, 1 (see strcmp(3) and qsort(3)) * ------------------------------------------------------------------------- */ int qsort_by_name_fold (fe1, fe2) FILE_ENTRY **fe1; FILE_ENTRY **fe2; { return (strcasecmp ((*fe1)->name, (*fe2)->name)); } /* qsort_by_name */ /* ============================================================================ * Name: qsort_by_size * Desc: compare-function for qsort if par_sort_type == BY_SIZE * In : pointers to two file-entries * Out : -1, 0, 1 (see strcmp(3) and qsort(3)) * ------------------------------------------------------------------------- */ int qsort_by_size (fe1, fe2) FILE_ENTRY **fe1; FILE_ENTRY **fe2; { if ((*fe1)->name[0] == '.') { if (strcmp ((*fe1)->name, ".") == 0) return -1; if (strcmp ((*fe1)->name, "..") == 0) { if (strcmp ((*fe2)->name, ".") == 0) return 1; else return -1; } } if ((*fe1)->sb.st_size > (*fe2)->sb.st_size) return (-1); else if ((*fe1)->sb.st_size < (*fe2)->sb.st_size) return (1); else return (strcmp ((*fe1)->name, (*fe2)->name)); } /* qsort_by_size */ /* ============================================================================ * Name: qsort_by_date * Desc: compare-function for qsort if par_sort_type == BY_DATE * In : pointers to two file-entries * Out : -1, 0, 1 (see strcmp(3) and qsort(3)) * ------------------------------------------------------------------------- */ int qsort_by_date (fe1, fe2) FILE_ENTRY **fe1; FILE_ENTRY **fe2; { if ((*fe1)->name[0] == '.') { if (strcmp ((*fe1)->name, ".") == 0) return -1; if (strcmp ((*fe1)->name, "..") == 0) { if (strcmp ((*fe2)->name, ".") == 0) return 1; else return -1; } } if ((*fe1)->sb.st_mtime > (*fe2)->sb.st_mtime) return (-1); else if ((*fe1)->sb.st_mtime < (*fe2)->sb.st_mtime) return (1); else return (strcmp ((*fe1)->name, (*fe2)->name)); } /* qsort_by_date */ /* ============================================================================ * Name: qsort_by_ext * Desc: compare-function for qsort if par_sort_type == BY_EXT * In : pointers to two file-entries * Out : -1, 0, 1 (see strcmp(3) and qsort(3)) * ------------------------------------------------------------------------- */ int qsort_by_ext (fe1, fe2) FILE_ENTRY **fe1; FILE_ENTRY **fe2; { char *lp1, *lp2; int r; if ((*fe1)->name[0] == '.') { if (strcmp ((*fe1)->name, ".") == 0) return -1; if (strcmp ((*fe1)->name, "..") == 0) { if (strcmp ((*fe2)->name, ".") == 0) return 1; else return -1; } } lp1 = strrchr ((*fe1)->name, '.'); lp2 = strrchr ((*fe2)->name, '.'); if (!lp1 && !lp2) return (strcmp ((*fe1)->name, (*fe2)->name)); else if (!lp1 && lp2) return (-1); else if (lp1 && !lp2) return (1); else { r = strcmp (lp1 + 1, lp2 + 1); if (r) return (r); else return (strcmp ((*fe1)->name, (*fe2)->name)); } } /* qsort_by_ext */ /* ============================================================================ * Name: sort_by_type * Desc: sorts the file list by type (currently just dir or not dir) * In : fl - adress of file list * Out : * ------------------------------------------------------------------------- */ void sort_by_type (fl) FILE_LIST *fl; { FILE_ENTRY **fea1, **fea2; int i, num_dir = 0, num_oth = 0; /* fea1 will hold directories */ fea1 = (FILE_ENTRY **) dsk_malloc ( fl->num_files * (sizeof (FILE_ENTRY *)), "sort_by_type"); if (!fea1) { perror ("sort_by_type (fea1)"); exit (1); } /* fea2 will hold everything but directories */ fea2 = (FILE_ENTRY **) dsk_malloc ( fl->num_files * (sizeof (FILE_ENTRY *)), "sort_by_type 2"); if (!fea2) { perror ("sort_by_type (fea2)"); exit (1); } for (i = 0; i < fl->num_files; i++) { if (fl->fe_array[i]->type == TYPE_DIR || fl->fe_array[i]->type == TYPE_LDIR) { fea1[num_dir] = fl->fe_array[i]; num_dir++; } else { fea2[num_oth] = fl->fe_array[i]; num_oth++; } } /* ensure that directories are always on top */ if (!par_invert) { memcpy ((char *)fl->fe_array, fea1, num_dir * sizeof (FILE_ENTRY *)); memcpy ((char *)fl->fe_array + num_dir * sizeof (FILE_ENTRY *), fea2, num_oth * sizeof (FILE_ENTRY *)); } else { memcpy ((char *)fl->fe_array, fea2, num_oth * sizeof (FILE_ENTRY *)); memcpy ((char *)fl->fe_array + num_oth * sizeof (FILE_ENTRY *), fea1, num_dir * sizeof (FILE_ENTRY *)); } dsk_free (fea1, "sort_by_type"); dsk_free (fea2, "sort_by_type 2"); } /* sort_by_type */ /* ============================================================================ * Name: process_file_list * Desc: sorts the list using qsort on an array of pointers to file entries * In : fl - adress of file list * Out : * ------------------------------------------------------------------------- */ void process_file_list (fl) FILE_LIST *fl; { FILE_ENTRY *fe; int i = 0; /* bug-fix by Zsolt Koppany <zkoppany@multix.de> */ if (fl->num_files == 0) return; fl->fe_array = (FILE_ENTRY **) dsk_malloc (fl->num_files * (sizeof (FILE_ENTRY *)), "process_file_list"); if (!fl->fe_array) { perror ("process_file_list"); exit (1); } if (fl->num_files < 2) { fl->fe_array[0] = fl->first; return; } /* fill the array of pointers to file entries */ fe = fl->first; while (fe) { fl->fe_array[i++] = fe; fe = fe->next; } switch (par_sort_type) { case BY_NAME: qsort (fl->fe_array, fl->num_files, sizeof (FILE_ENTRY *), qsort_by_name); break; case BY_NAME_FOLD: qsort (fl->fe_array, fl->num_files, sizeof (FILE_ENTRY *), qsort_by_name_fold); break; case BY_SIZE: qsort (fl->fe_array, fl->num_files, sizeof (FILE_ENTRY *), qsort_by_size); break; case BY_DATE: qsort (fl->fe_array, fl->num_files, sizeof (FILE_ENTRY *), qsort_by_date); break; case BY_EXT: qsort (fl->fe_array, fl->num_files, sizeof (FILE_ENTRY *), qsort_by_ext); break; } if (par_sort_by_type) sort_by_type (fl); } /* process_file_list */ /* ============================================================================ * Name: process_file_lists * Desc: calls write_list, multiple times if par_every_dir * In : * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int process_file_lists () { FILE_LIST *fl; fl = &root_of_lists; if (!fl->next) process_file_list (fl); else { while (fl) { process_file_list (fl); fl = fl->next; } } return TCL_OK; } /* process_file_lists */ /* ============================================================================ * Name: new_file_entry * Desc: allocates and fills a new FILE_ENTRY * In : name - complete name of file * Out : address of new FILE_ENTRY * ------------------------------------------------------------------------- */ FILE_ENTRY *new_file_entry (name) char *name; { FILE_ENTRY *nfe, *fe; FILE_LIST *fl; char *np, *ls, buf[BUFSIZ]; struct stat sb; int ftype; nfe = (FILE_ENTRY *) dsk_malloc (sizeof (FILE_ENTRY), "new_file_entry"); if (!nfe) { perror ("new_file_entry (nfe)"); exit (1); } /* alloc length * 2 to have space for escaping special characters */ np = (char *) dsk_malloc (strlen(name) * 2, "new_file_entry 2"); if (!np) { perror ("new_file_entry (np)"); exit (1); } nfe->next = NULL; nfe->path = np; escape_chars (name, "\"[]{}$;", buf); strcpy (nfe->path, buf); ls = strrchr (nfe->path, '/'); if (!ls) nfe->name = nfe->path; else nfe->name = ls+1; if (lstat (name, &(nfe->sb)) == 0) ftype = (int) (nfe->sb.st_mode & S_IFMT); else ftype = -1; nfe->tc[1] = '\0'; switch (ftype) { case S_IFREG: if (nfe->sb.st_mode & S_IEXEC) { char *c; int have_dot = 0; if (o_dotreg) for (c = nfe->name; *c && !have_dot; c++) if (*c == '.') have_dot = 1; if (!have_dot) { nfe->type = TYPE_EXEC; nfe->tc[0] = TC_EXEC; } else { nfe->type = TYPE_REG; nfe->tc[0] = TC_REG; } } else { nfe->type = TYPE_REG; nfe->tc[0] = TC_REG; } break; case S_IFDIR: nfe->type = TYPE_DIR; nfe->tc[0] = TC_DIR; break; case S_IFLNK: if (stat (name, &sb) == 0) { if (S_ISDIR(sb.st_mode)) { nfe->type = TYPE_LDIR; nfe->tc[0] = TC_LDIR; } else { nfe->type = TYPE_LINK; if (sb.st_mode & S_IEXEC) nfe->tc[0] = TC_LEXEC; else nfe->tc[0] = TC_LREG; } } else { nfe->type = TYPE_UNKN; nfe->tc[0] = TC_UNKN; } break; case S_IFSOCK: nfe->type = TYPE_SOCK; nfe->tc[0] = TC_SOCK; break; default: nfe->type = TYPE_UNKN; nfe->tc[0] = TC_UNKN; break; } if (nfe->type != TYPE_DIR && nfe->type != TYPE_LDIR) { if (ignore_file (nfe)) { dsk_free (nfe->path, "new_file_entry"); dsk_free (nfe, "new_file_entry 2"); return NULL; } } /* append new entry to appropriate list */ fl = &root_of_lists; while (fl->next) { fl = fl->next; } fl->num_files++; if (!fl->first) { fl->first = nfe; } else { fe = fl->first; while (fe->next) { fe = fe->next; } fe->next = nfe; } return (nfe); } /* new_file_entry */ /* ============================================================================ * Name: new_file_list * Desc: allocates and fills a new FILE_LIST * In : * Out : address of new FILE_LIST * ------------------------------------------------------------------------- */ FILE_LIST *new_file_list () { FILE_LIST *nfl, *fl; nfl = (FILE_LIST *) dsk_malloc (sizeof (FILE_LIST), "new_file_list"); if (!nfl) { perror ("new_file_list"); exit (1); } nfl->next = NULL; nfl->first = NULL; nfl->num_files = 0; nfl->fe_array = NULL; /* append the new file_list */ fl = &root_of_lists ; while (fl->next) fl = fl->next; fl->next = nfl; return (nfl); } /* new_file_list */ /* ============================================================================ * Name: read_list * Desc: reads the files of directory path (last char: /) * In : path * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int read_list (path) char *path; { DIR *dp; struct dirent *dir; char buffer[MAXPATHLEN]; struct stat sb; #ifdef LSDEBUG fprintf (stderr, "Opening %s ...\n", path); #endif if ((dp = opendir (path)) == NULL) { sprintf (dsk_interp->result, "no such file or dir: %s", path); return (TCL_ERROR); } while ((dir = readdir (dp))) { /* skip removed files */ if (dir->d_ino == 0) continue; #ifdef LSDEBUG fprintf (stderr, "Found file: %s\n", dir->d_name); #endif if (dir->d_name[0] == '.') { if ( (dir->d_name[1] == '\0') || (dir->d_name[1] == '.' && dir->d_name[2] == '\0')) { if (!par_no_points) { strcpy (buffer, path); strcat (buffer, dir->d_name); new_file_entry (buffer); } } else if (par_show_all) { strcpy (buffer, path); strcat (buffer, dir->d_name); new_file_entry (buffer); } } else { strcpy (buffer, path); strcat (buffer, dir->d_name); if (!par_only_directories) { new_file_entry (buffer); } else { stat (buffer, &sb); if ((sb.st_mode & S_IFMT) == S_IFDIR) { /* directories are never ignored */ new_file_entry (buffer); } } } } closedir (dp); return (TCL_OK); } /* read_list */ /* ============================================================================ * Name: read_file_lists * Desc: prepares par_path, calls read_list * In : * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int read_file_lists () { struct stat sb; char bc, *slash; if (par_path[strlen (par_path) - 1] == '/' && !par_one_file) { /* par_path is the name of a directory */ if (stat (par_path, &sb) < 0) { sprintf (dsk_interp->result, "no such file or dir: %s", par_path); return (TCL_ERROR); } } else { if (stat (par_path, &sb) < 0) { sprintf (dsk_interp->result, "no such file or dir: %s", par_path); return (TCL_ERROR); } if (!S_ISDIR(sb.st_mode) || par_one_file) { FILE_ENTRY *fe; fe = new_file_entry (par_path); if (fe != NULL) { write_element (fe, NULL); dsk_free (fe->path, "read_file_lists"); dsk_free (fe, "read_file_lists 2"); just_one_file = 1; return (TCL_OK); } else return (TCL_ERROR); } else strcat (par_path, "/"); } if (!par_every_dir) { read_list (par_path); } else { /* read root directory */ bc = par_path[1]; par_path[1] = 0; read_list (par_path); par_path[1] = bc; /* and now one by one */ slash = strchr (par_path + 1, '/'); while (slash++) { new_file_list (); bc = *slash; *slash = 0; read_list (par_path); *slash = bc; slash = strchr (slash, '/'); } } return TCL_OK; } /* read_file_lists */ /* ============================================================================ * Name: process_cmdline * Desc: sets global var. (par_*) accordingly to the command line parameters * In : argc, argv as in main * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int process_cmdline (argc, argv) int argc; char *argv[]; { int i; i = 1; while (argc > 1) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'a': par_show_all = 1; break; case 'd': par_only_directories = 1; break; case 'D': par_add_date = 1; break; case 'e': par_every_dir = 1; break; case 'f': par_sort_by_type = 1; break; case 'i': par_invert = 1; break; case 'l': par_long_listing = 1; break; case 'M': if (argc == 2) { strcpy (dsk_interp->result, USAGE); return (TCL_ERROR); } i++; argc--; strcpy (par_mask, argv[i]); o_maskc = 1; o_maskv_ptr = par_mask; o_maskv = &o_maskv_ptr; break; case 'n': par_print_num = 1; break; case 'o': par_one_file = 1; break; case 'p': par_no_points = 1; break; case 'P': par_full_path = 1; break; case 's': if (argc == 2) { strcpy (dsk_interp->result, USAGE); return (TCL_ERROR); } i++; argc--; if (!strcmp ("size", argv[i])) par_sort_type = BY_SIZE; else if (!strcmp ("name", argv[i])) par_sort_type = BY_NAME; else if (!strcmp ("fold", argv[i])) par_sort_type = BY_NAME_FOLD; else if (!strcmp ("date", argv[i])) par_sort_type = BY_DATE; else if (!strcmp ("ext", argv[i])) par_sort_type = BY_EXT; else if (!strcmp ("not", argv[i])) par_sort_type = BY_NTH; else { sprintf (dsk_interp->result, "Don't know how to sort by %s.\n%s", argv[i], USAGE); return (TCL_ERROR); } break; case 'S': par_add_size = 1; break; case 't': par_append_char = 1; break; default: sprintf (dsk_interp->result, "Dont understand: -%c\n%s", argv[i][1], USAGE); return (TCL_ERROR); } } else { if (*par_path) { strcpy (dsk_interp->result, USAGE); return (TCL_ERROR); } strcpy (par_path, argv[i]); } i++; argc--; } if (!*par_path) { strcpy (dsk_interp->result, USAGE); return (TCL_ERROR); } #ifdef LSDEBUG fprintf (stderr, "par_every_dir: %d\npar_sort_type: %d\npar_sort_by_type: %d\npar_show_all: %d\npar_invert: %d\npar_add_date: %d\npar_add_size: %d\npar_long_listing: %d\npar_no_points: %d\npar_append_char: %d\npar_path: %s\n", par_every_dir, par_sort_type, par_sort_by_type, par_show_all, par_invert, par_add_date, par_add_size, par_long_listing, par_no_points, par_append_char, par_path); #endif return (TCL_OK); } /* process_cmdline */ /* ============================================================================ * Name: dsk_ls_init * Desc: reinitializes global variables * In : * Out : * ------------------------------------------------------------------------- */ void dsk_ls_init () { par_every_dir = 0; /* read files for every dir in path? */ par_sort_type = BY_NAME; /* how to sort the file-lists */ par_sort_by_type = 0; /* sort by type? (only distincts dir/other) */ par_show_all = 0; /* list all files? (starting with .) */ par_invert = 0; /* invert the file-lists? */ par_add_date = 0; /* add date to every file? */ par_add_size = 0; /* add size to every file? */ par_long_listing = 0; /* long listing? (implies date and size) */ par_no_points = 0; /* hide . and .. entries? */ par_append_char = 0; /* append *,@,/,=," " ? */ par_print_num = 0; /* print # files as first entry? */ par_one_file = 0; par_full_path = 0; par_only_directories = 0; par_path[0] = 0; par_mask[0] = 0; root_of_lists.next = NULL; root_of_lists.first = NULL; root_of_lists.num_files = 0; root_of_lists.fe_array = NULL; just_one_file = 0; o_iargc = 0; o_iargv = NULL; o_maskc = 0; o_maskv = NULL; num_mallocs = 0; num_frees = 0; } /* dsk_ls_init */ /* ============================================================================ * Name: dsk_ls_Cmd * Desc: entry point, control * In : argc, argv * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int dsk_ls_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { #ifdef MEM_DEBUG { int i = 0; fprintf (stderr, "dsk_ls "); while (i < argc) fprintf (stderr, "%s ", argv[i++]); fprintf (stderr, "\n"); } #endif dsk_ls_init (); if (process_cmdline (argc, argv) != TCL_OK) return (TCL_ERROR); #ifdef LSDEBUG fprintf( stderr, "\nEntering read_file_lists ...\n"); #endif if (read_file_lists () != TCL_OK) return (TCL_ERROR); if (just_one_file) { #ifdef MEM_DEBUG fprintf (stderr, "num_mallocs: %d, num_frees: %d %s\n", num_mallocs, num_frees, num_mallocs != num_frees ? "*unequal*" : ""); #endif return (TCL_OK); } #ifdef LSDEBUG fprintf( stderr, "\nEntering process_file_lists ...\n"); #endif if (process_file_lists () != TCL_OK) return (TCL_ERROR); #ifdef LSDEBUG fprintf( stderr, "\nEntering write_file_lists ...\n"); #endif if (write_file_lists (0) != TCL_OK) return (TCL_ERROR); free_file_lists(); #ifdef MEM_DEBUG fprintf (stderr, "num_mallocs: %d, num_frees: %d %s\n", num_mallocs, num_frees, num_mallocs != num_frees ? "*unequal*" : ""); #endif return (TCL_OK); } /* dsk_ls_Cmd */ ������������������tkdesk-2.0/libdesk/init.c���������������������������������������������������������������������������0100644�0001750�0000764�00000007206�10037120032�013442� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: init.c * Project: TkDesk * Started: 07.10.94 * Changed: 20.04.96 * * Description: Initializes libdesk's new commands when starting up. * * Copyright (C) 1996 Christian Bolik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * See the file "COPYING" in the base directory of this distribution * for more. * * ---------------------------------------------------------------------------- * * Functions: * Dsk_Init (Tcl_Interp *interp) * * ========================================================================= */ #include <stdlib.h> #include <stdio.h> #include <string.h> #ifdef CATCH_SIGNALS #include <signal.h> #include <unistd.h> #endif #include "libdesk.h" #if (TCL_MAJOR_VERSION != 8) #error "tcl.h is not from version 8.x" #endif #ifdef CATCH_SIGNALS void signal_handler (int signum); #endif #define TKDESK_VERSION "2.0b" Tcl_Interp *dsk_interp; /* ============================================================================ * Name: Dsk_Init * Desc: Initializes libdesk when called from tkAppInit. * In : interp - Adress of tcl interpreter * Out : TCL_OK or TCL_ERROR * ------------------------------------------------------------------------- */ int Dsk_Init (interp) Tcl_Interp *interp; { dsk_interp = interp; Tcl_CreateCommand (interp, "dskC_ls", dsk_ls_Cmd, NULL, NULL); Tcl_CreateCommand (interp, "dskC_striptc", dsk_striptc_Cmd, NULL, NULL); Tcl_CreateCommand (interp, "dskC_esc", dsk_esc_Cmd, NULL, NULL); Tcl_CreateCommand (interp, "dskC_unesc", dsk_unesc_Cmd, NULL, NULL); Tcl_CreateCommand (interp, "dskC_localtime", dsk_localtime_Cmd, NULL, NULL); Tcl_CreateCommand (interp, "dskC_statfs", dsk_statfs_Cmd, NULL, NULL); /* these commands are for tcl optimization: */ Tcl_CreateCommand (interp, "dskC_ls_and_tag", dsk_ls_and_tag_Cmd, NULL, NULL); Tcl_CreateCommand (interp, "dskC_init_ftags", dsk_init_ftags_Cmd, NULL, NULL); Tcl_SetVar (interp, "dskC_version", TKDESK_VERSION, TCL_GLOBAL_ONLY); #ifdef CATCH_SIGNALS /* signal handling */ /* Unfortunately, there's not enough time for saving TkDesk's layout when a SIGHP is caught (eg. when X exits). */ /*signal (SIGHUP, signal_handler);*/ #endif return (TCL_OK); } /* Dsk_Init */ #ifdef CATCH_SIGNALS /* --------------------------------------------------------------------------- signal_handler: A very simple and naive signal handler. Tries to invoke a Tcl command to handle the received signal. */ void signal_handler (signum) int signum; { signal (signum, SIG_IGN); /*fprintf (stderr, "Signal: %d\n", signum);*/ switch (signum) { case SIGHUP: Tcl_GlobalEval (dsk_interp, "eval $tkdesk(signal_handler,SIGHUP)"); break; case SIGCHLD: Tcl_GlobalEval (dsk_interp, "eval $tkdesk(signal_handler,SIGCHLD)"); break; } /* ignore Tcl errors for now: */ dsk_interp->result = ""; signal (signum, signal_handler); } /* signal_handler */ #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/libdesk/libdesk.h������������������������������������������������������������������������0100644�0001750�0000764�00000004304�10037120207�014121� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: libdesk.h * Project: TkDesk * Started: 07.10.94 * Changed: 12.07.96 * * Copyright (C) 1996 Christian Bolik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * See the file "COPYING" in the base directory of this distribution * for more. * * ========================================================================= */ #ifndef _LIBDESK_H #define _LIBDESK_H #include "config.h" #include "ot.h" #include <tcl.h> extern Tcl_Interp *dsk_interp; /* ===== Tcl commands created by libtv: */ extern Tcl_CmdProc dsk_ls_Cmd; extern Tcl_CmdProc dsk_striptc_Cmd; extern Tcl_CmdProc dsk_ppflist_Cmd; extern Tcl_CmdProc dsk_ls_and_tag_Cmd; extern Tcl_CmdProc dsk_init_ftags_Cmd; extern Tcl_CmdProc dsk_esc_Cmd; extern Tcl_CmdProc dsk_unesc_Cmd; extern Tcl_CmdProc dsk_localtime_Cmd; extern Tcl_CmdProc dsk_statfs_Cmd; /* ===== Exported C functions: */ char *escape_chars _ANSI_ARGS_((char *str, char *chars, char *buf)); char *unescape_chars _ANSI_ARGS_((char *str, char *buf)); char *itoa _ANSI_ARGS_((long val)); int dsk_ls_init_and_parse_options _ANSI_ARGS_((int argc, char **argv, char *path)); int dsk_ls_read_and_sort _ANSI_ARGS_((int iargc, char **iargv, int maskc, char **maskv, int mask_invert, int dot_reg)); int dsk_ls_next_file _ANSI_ARGS_((char *elembuf, char *nametcbuf)); void dsk_ls_cleanup _ANSI_ARGS_(()); #endif /* _LIBDESK_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/libdesk/misc.c���������������������������������������������������������������������������0100644�0001750�0000764�00000017721�10020457430�013444� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: misc.c * Project: TkDesk * Started: 05.01.94 * Changed: 31.03.96 * * Description: Implements several utility Tcl commands for TkDesk. * * Copyright (C) 1996 Christian Bolik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * See the file "COPYING" in the base directory of this distribution * for more. * * ---------------------------------------------------------------------------- * * Functions: * * * ========================================================================= */ #include "libdesk.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <time.h> #ifdef HAVE_SYS_VFS_H #include <sys/vfs.h> #endif #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> #endif #ifdef HAVE_SYS_MOUNT_H #include <sys/mount.h> #endif #ifdef HAVE_STATVFS_H #include <sys/types.h> #include <statvfs.h> #endif #ifdef HAVE_SYS_STATVFS_H #include <sys/types.h> #include <sys/statvfs.h> #endif /* ============================================================================ * Name : dsk_stripc_Cmd * In : ... * Out : ... * Desc : Syntax: dsk_stripc ?-keep? string * This strips the file type character (one of "/@*+-_") from the * string string. If the -keep option is given, only one of "+-_" * will be removed. * Side-FX: none * ------------------------------------------------------------------------- */ int dsk_striptc_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { int keep = 0, i; char tc; char instr[TCL_RESULT_SIZE], outstr[TCL_RESULT_SIZE]; if (argc < 2 || argc > 3) { sprintf (interp->result, "usage: dsk_striptc ?-keep? string"); return (TCL_ERROR); } if (argc == 3) { if (strcmp ("-keep", argv[1]) == 0) { keep = 1; strcpy(instr, argv[2]); } else { sprintf (interp->result, "usage: dsk_striptc ?-keep? string"); return (TCL_ERROR); } } else { strcpy(instr, argv[1]); } if (instr[0] == 0) { *interp->result = 0; return (TCL_OK); } strcpy (outstr, instr); i = 0; while (outstr[i+1]) { if (outstr[i+1] == '\\' && (outstr[i+2] == 't')) break; i++; } /* outstr[i] now contains the type character */ tc = outstr[i]; if (!keep) { if (tc == '@' || tc == '*' || tc == '/' || tc == '+' || tc == '-' || tc == '=' || tc == '_') { if (outstr[i+1]) outstr[i] = ' '; else outstr[i] = '\0'; } } else { if (tc == '_') { if (outstr[i+1]) outstr[i] = ' '; else outstr[i] = '\0'; } } strcpy (interp->result, outstr); return (TCL_OK); } /* dsk_striptc_Cmd */ /* ---------------------------------------------------------------------------- * dsk_esc_Cmd: * Tcl command: dsk_esc string chars * Precedes each char in $string if it is in $chars. Returns the result. */ int dsk_esc_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { char *buf; if (argc != 3) { sprintf (interp->result, "usage: dsk_esc string chars"); return (TCL_ERROR); } /* assume every char needs to be backslashed, for safety reasons */ buf = (char *) malloc (strlen (argv[1]) * 2); if (buf == NULL) { fprintf (stderr, "out of memory in dsk_esc_Cmd\n"); exit (1); } escape_chars (argv[1], argv[2], buf); Tcl_SetResult (interp, buf, TCL_VOLATILE); free (buf); return TCL_OK; } /* dsk_esc_Cmd */ /* ---------------------------------------------------------------------------- * dsk_unesc_Cmd: * Tcl command: dsk_unesc string * Removes all the backslashes in $string except before }. */ int dsk_unesc_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { char *buf; if (argc != 2) { strcpy (interp->result, "usage: dsk_unesc string"); return (TCL_ERROR); } /* assume every char needs to be backslashed, for safety reasons */ buf = (char *) malloc (strlen (argv[1]) * 2); if (buf == NULL) { fprintf (stderr, "out of memory in dsk_esc_Cmd\n"); exit (1); } unescape_chars (argv[1], buf); Tcl_SetResult (interp, buf, TCL_VOLATILE); free (buf); return TCL_OK; } /* dsk_esc_Cmd */ /* ---------------------------------------------------------------------------- * dsk_localtime_Cmd: * Tcl command: dsk_localtime_Cmd * Returns the current time in "array set" format. The elements correspond * to the entries of "struct tm". */ int dsk_localtime_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { time_t t; struct tm *ts; char buf[10]; t = time (NULL); ts = localtime (&t); sprintf (buf, "%02d ", ts->tm_sec); Tcl_AppendResult (interp, "sec ", buf, NULL); sprintf (buf, "%02d ", ts->tm_min); Tcl_AppendResult (interp, "min ", buf, NULL); sprintf (buf, "%02d ", ts->tm_hour); Tcl_AppendResult (interp, "hour ", buf, NULL); sprintf (buf, "%d ", ts->tm_mday); Tcl_AppendResult (interp, "mday ", buf, NULL); sprintf (buf, "%d ", ts->tm_mon); Tcl_AppendResult (interp, "mon ", buf, NULL); sprintf (buf, "%d ", ts->tm_year % 100); Tcl_AppendResult (interp, "year ", buf, NULL); sprintf (buf, "%d ", ts->tm_wday); Tcl_AppendResult (interp, "wday ", buf, NULL); sprintf (buf, "%d ", ts->tm_yday); Tcl_AppendResult (interp, "yday ", buf, NULL); sprintf (buf, "%d ", ts->tm_isdst); Tcl_AppendResult (interp, "isdst ", buf, NULL); return (TCL_OK); } /* dsk_localtime_Cmd */ /* ---------------------------------------------------------------------------- * dsk_statfs_Cmd: * Tcl command: dsk_statfs path?/file? * Returns info about the file system $path resides on, as a * list with three elements: {totalBlocks freeBlocks percentFree} */ int dsk_statfs_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { #if defined(HAVE_STATFS) || defined(HAVE_STATVFS) # ifdef HAVE_STATVFS struct statvfs fs; # else struct statfs fs; # endif char buf[64]; if (argc != 2) { strcpy (interp->result, "usage: dskC_statfs path?/file?"); return TCL_ERROR; } # ifdef HAVE_STATVFS if (statvfs (argv[1], &fs) == 0) { # else if (statfs (argv[1], &fs) == 0) { # endif if (fs.f_bsize == 0) fs.f_bsize = 1024; if (fs.f_blocks == 0) fs.f_blocks = fs.f_bavail; # ifdef HAVE_STATVFS strcpy (buf, itoa((long)((double)fs.f_blocks/(1024./fs.f_frsize)))); # else strcpy (buf, itoa((long)((double)fs.f_blocks/(1024./fs.f_bsize)))); # endif strcat (buf, " "); # ifdef HAVE_STATVFS strcat (buf, itoa((long)((double)fs.f_bavail/(1024./fs.f_frsize)))); # else strcat (buf, itoa((long)((double)fs.f_bavail/(1024./fs.f_bsize)))); # endif strcat (buf, " "); if (fs.f_blocks > 0) strcat (buf, itoa((long)((double)fs.f_bavail / (double)fs.f_blocks * 100))); else strcat (buf, "0"); Tcl_AppendResult (interp, buf, NULL); return TCL_OK; } else { sprintf (interp->result, "Error %d", errno); return TCL_ERROR; } #else strcpy (interp->result, ""); return TCL_OK; #endif } /*dsk_statfs_Cmd */ �����������������������������������������������tkdesk-2.0/libdesk/o_FileListbox.c������������������������������������������������������������������0100644�0001750�0000764�00000026344�10020457430�015254� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: o_FileListbox.c * Project: TkDesk * Started: 14.02.95 * Changed: 14.02.95 * * Description: Contains optimizations of tcl code from the file * ../tcldesk/FileListbox.tcl. These are implemented as * tcl commands. * * Copyright (C) 1996 Christian Bolik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * See the file "COPYING" in the base directory of this distribution * for more. * * ---------------------------------------------------------------------------- * * Functions: * * * ========================================================================= */ #include <string.h> #include "libdesk.h" /* the ignore list */ static int iargc; static char **iargv = NULL; /* the tag list */ static int targc; static char **targv = NULL; /* the pattern list */ static int pargc; static char **pargv = NULL; /* ============================================================================ * Name : dsk_init_ftags_Cmd (tcl: dskC_init_ftags) * In : ... (opt: argv[1]: dsk_FileListbox::taglist, * argv[2]: dsk_FileListbox::patlist, * argv[3]: dsk_FileListbox::ignorelist) * Out : ... * Desc : Initializes internal file tags structures. * ------------------------------------------------------------------------- */ int dsk_init_ftags_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { char *taglist, *ilist, *plist; /* get and split the taglist */ if (targv != NULL) free ((char *)targv); if (argc > 1) { if (Tcl_SplitList (interp, argv[1], &targc, &targv) == TCL_ERROR) return TCL_ERROR; } else { ot_getvar ("taglist", taglist); if (Tcl_SplitList (interp, taglist, &targc, &targv) == TCL_ERROR) return TCL_ERROR; } /* get and split tag masks */ if (pargv != NULL) free ((char *)pargv); if (argc > 2) { if (Tcl_SplitList (interp, argv[2], &pargc, &pargv) == TCL_ERROR) return TCL_ERROR; } else { ot_getvar ("patlist", plist); if (Tcl_SplitList (interp, plist, &pargc, &pargv) == TCL_ERROR) return TCL_ERROR; } /* get and split ignore masks */ if (iargv != NULL) free ((char *)iargv); if (argc > 3) { if (Tcl_SplitList (interp, argv[3], &iargc, &iargv) == TCL_ERROR) return TCL_ERROR; } else { ot_getvar ("ignorelist", ilist); if (Tcl_SplitList (interp, ilist, &iargc, &iargv) == TCL_ERROR) return TCL_ERROR; } return TCL_OK; } /* dsk_init_ftags_Cmd */ /* ============================================================================ * Name : dsk_ls_and_tag_Cmd (tcl: dskC_ls_and_tag) * In : argv[1]: directory to read and tag * Out : ... * Desc : Reads file list by calling functions from dsk_ls.c, plus does * additional processing like tagging and file masking. * Side-FX: none * ------------------------------------------------------------------------- */ int dsk_ls_and_tag_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { int i = 0, fargc, k, typechar, largc = 0, dotreg, mtags = 0, mtargc, ntd, add_images, notmatch, notmatch_set = 0, showall, mask_matches_all, lsargc, maskc = 0, invert_mask; char fnametc[256], **maskv, *mask, maskbuf[64], buf[256], *thisP, **lsargv, **mtargv, *bufp, filebuf[1024], *lscmd, escbuf[1024], annobuf[1024], *frame, this[256]; Tcl_DString dbigcmd; #if TCL_MAJOR_VERSION >= 8 Tcl_Obj *notmatch_obj = Tcl_NewStringObj("mt_notmatch", -1); Tcl_Obj *anno_obj = Tcl_NewStringObj("tkdesk_anno", -1); Tcl_Obj *annotag_obj = Tcl_NewStringObj("annotag", -1); Tcl_Obj *mt_obj = Tcl_NewStringObj("mt", -1); Tcl_Obj *obj; #endif if (argc != 2) { strcpy (interp->result, "usage: dskC_ls_and_tag path"); return TCL_ERROR; } /* * get values of required class variables */ ot_getvar ("this", thisP); strcpy (this, thisP); /* need to save this for Tcl 8.x */ ot_getvar ("frame", frame); ot_getboolean ("invert_mask", NULL, &invert_mask); ot_getvar ("mask", mask); if (strcmp (mask, "*") == 0 && !invert_mask) mask_matches_all = 1; else { mask_matches_all = 0; if (Tcl_SplitList (interp, mask, &maskc, &maskv) == TCL_ERROR) return TCL_ERROR; /*printf ("%s: %d elements\n", mask, maskc);*/ } ot_getboolean ("typechar", NULL, &typechar); ot_getboolean ("notrivialdirs", NULL, &ntd); ot_getboolean ("add_icons", NULL, &add_images); ot_getboolean ("showall", NULL, &showall); ot_getboolean ("dotregular", NULL, &dotreg); /* intialize dsk_ls */ ot_getvar ("ls_cmd", lscmd); if (Tcl_SplitList (interp, lscmd, &lsargc, &lsargv) == TCL_ERROR) return TCL_ERROR; dsk_ls_init_and_parse_options (lsargc, lsargv, argv[1]); free (lsargv); /* now read and sort the file list */ fargc = dsk_ls_read_and_sort (iargc, (showall ? NULL : iargv), maskc, (mask_matches_all ? NULL : maskv), invert_mask, dotreg); /* prepare for filling the list box with the filelist */ Tcl_DStringInit (&dbigcmd); Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".dlb config -list {", -1); /* * Loop through the file list. * Append the index of each file to the tag list it matches. */ i = 0; strcpy (annobuf, argv[1]); while (dsk_ls_next_file (filebuf, fnametc)) { /* loop through $taglist until a matching tag is found */ notmatch = 1; strcpy (buf, itoa(i)); for (k = 0; k < targc; k++) { /* if the filename matches this tag append its index */ if (Tcl_StringMatch (fnametc, pargv[k])) { #if TCL_MAJOR_VERSION < 8 if (!(Tcl_SetVar2 (interp, "mt", targv[k], buf, TCL_LIST_ELEMENT | TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG))) return TCL_ERROR; #else if (!(Tcl_ObjSetVar2 (interp, mt_obj, Tcl_NewStringObj(targv[k], -1), Tcl_NewStringObj(buf, -1), TCL_LIST_ELEMENT | TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG))) return TCL_ERROR; #endif mtags = 1; notmatch = 0; break; } } /* add_images: need also line-nr.s of "normal" files */ if (notmatch && add_images) { notmatch_set = 1; #if TCL_MAJOR_VERSION < 8 ot_setvar_with_flags ("mt_notmatch", buf, TCL_LIST_ELEMENT | TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG); #else if (Tcl_ObjSetVar2 (interp, notmatch_obj, NULL, Tcl_NewStringObj (buf, -1), TCL_LIST_ELEMENT | TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR; #endif } /* check whether there's an annotation for this file */ strcpy (annobuf+strlen(argv[1]), fnametc); annobuf[strlen(annobuf) - 1] = '\0'; #if TCL_MAJOR_VERSION < 8 if (Ot_GetVar2 (interp, "tkdesk_anno", annobuf, TCL_GLOBAL_ONLY) != NULL) { ot_setvar2_with_flags ("mt", "annotag", buf, TCL_LIST_ELEMENT | TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG); mtags = 1; } #else if (Tcl_ObjGetVar2 (interp, anno_obj, Tcl_NewStringObj (annobuf, -1), TCL_GLOBAL_ONLY) != NULL) { if (Tcl_ObjSetVar2 (interp, mt_obj, annotag_obj, Tcl_NewStringObj (buf, -1), TCL_LIST_ELEMENT | TCL_APPEND_VALUE | TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR; mtags = 1; } #endif /* append the file entry to the file list */ unescape_chars (filebuf, escbuf); Tcl_DStringAppendElement (&dbigcmd, escbuf); i++; if (i % 10 == 0) while (Tcl_DoOneEvent (TCL_ALL_EVENTS | TCL_DONT_WAIT) != 0); } Tcl_DStringAppend (&dbigcmd, "} ; ", -1); /* set the label of the menu button of this FileListbox */ Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".mb config -state normal -text \"[", -1); Tcl_DStringAppend (&dbigcmd, this, -1); Tcl_DStringAppend (&dbigcmd, " _mb_label]\" ; ", -1); /* repack the menu button (it may not be mapped) */ Tcl_DStringAppend (&dbigcmd, "pack ", -1); Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".mb -in ", -1); Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".fMb -before ", -1); Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".lFiles -fill x -side left -expand yes ; ", -1); /* update the listbox label */ largc = (!ntd && mask_matches_all) ? fargc - 2 : fargc; if (largc == 1) { Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".lFiles config -text \" 1 Item\" ;", -1); } else { Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".lFiles config -text \" ", -1); Tcl_DStringAppend (&dbigcmd, itoa(largc), -1); Tcl_DStringAppend (&dbigcmd, " Items\" ; ", -1); } if (!mtags) { if (Tcl_Eval (interp, dbigcmd.string) == TCL_ERROR) { return TCL_ERROR; } Tcl_DStringFree (&dbigcmd); } /* free memory allocated by dsk_ls */ dsk_ls_cleanup(); if (!mask_matches_all) free (maskv); /* * And now the tagging: */ if (mtags) { ot_invoke ("array names mt"); strcpy (buf, interp->result); if (Tcl_SplitList (interp, buf, &mtargc, &mtargv) == TCL_ERROR) return TCL_ERROR; /*Tcl_DStringInit (&dbigcmd);*/ for (k = 0; k < mtargc; k++) { ot_getvar2 ("mt", mtargv[k], bufp); Tcl_DStringAppend (&dbigcmd, frame, -1); Tcl_DStringAppend (&dbigcmd, ".dlb tag add ", -1); Tcl_DStringAppend (&dbigcmd, mtargv[k], -1); Tcl_DStringAppend (&dbigcmd, " {", -1); Tcl_DStringAppend (&dbigcmd, bufp, -1); Tcl_DStringAppend (&dbigcmd, "} ; ", -1); if (mtargv[k][0] == 'a') if (strcmp (mtargv[k], "annotag") == 0) continue; if (add_images) { Tcl_DStringAppend (&dbigcmd, this, -1); Tcl_DStringAppend (&dbigcmd, " imginsert ", -1); Tcl_DStringAppend (&dbigcmd, mtargv[k], -1); Tcl_DStringAppend (&dbigcmd, " {", -1); Tcl_DStringAppend (&dbigcmd, bufp, -1); Tcl_DStringAppend (&dbigcmd, "} ; ", -1); } if (k % 10 == 0) while (Tcl_DoOneEvent (TCL_ALL_EVENTS | TCL_DONT_WAIT) != 0); } if (Tcl_Eval (interp, dbigcmd.string) == TCL_ERROR) { return TCL_ERROR; } Tcl_DStringFree (&dbigcmd); free (mtargv); } if (add_images && notmatch_set) { ot_getvar ("mt_notmatch", bufp); Tcl_DStringInit (&dbigcmd); Tcl_DStringAppend (&dbigcmd, this, -1); Tcl_DStringAppend (&dbigcmd, " imginsert {} {", -1); Tcl_DStringAppend (&dbigcmd, bufp, -1); Tcl_DStringAppend (&dbigcmd, "}", -1); ot_invoke (dbigcmd.string); Tcl_DStringFree (&dbigcmd); } Tcl_ResetResult (interp); return TCL_OK; } /* dsk_ls_and_tag_Cmd */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/libdesk/ot.c�����������������������������������������������������������������������������0100644�0001750�0000764�00000032776�10037121475�013147� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: ot.c * Project: ot - Optimizing Tcl * Started: 12.02.94 * Changed: 12.02.94 * * Description: This file creates a few new tcl commands for optimizing * tcl code and implements a few utility functions for using * the Tcl/Tk C libraries. * * ---------------------------------------------------------------------------- * * Sections: * int Ot_Init * ===== Tcl Commands * int ot_timer_Cmd * ===== Utility Functions * int Ot_TclInvoke * int Ot_GetInt * int Ot_GetDouble * int Ot_GetBoolean * * ========================================================================= */ #include "ot.h" #ifdef OTTIMER /* the following three includes are for gettimeofday() */ #include <sys/types.h> #include <sys/time.h> #include <unistd.h> #endif /* ============================================================================ * Name: Ot_Init * Desc: Initializes ot and creates the new Tcl-commands. * In : interp - Adress of tcl interpreter * Out : TCL_OK * SFX : * ------------------------------------------------------------------------- */ int Ot_Init (interp) Tcl_Interp *interp; { #ifdef OTTIMER Tcl_CreateCommand (interp, "ot_timer", ot_timer_Cmd, NULL, NULL); #endif Tcl_CreateCommand (interp, "ot_maplist", ot_maplist_Cmd, NULL, NULL); #ifndef NO_TK Tcl_CreateCommand (interp, "ot_warp_pointer", ot_warp_pointer_Cmd, NULL, NULL); #endif return TCL_OK; } /* ===== Tcl Commands ====================================================== */ #ifdef OTTIMER /* ============================================================================ * Name : ot_timer_Cmd * In : ... * Out : ... * Desc : Implements the tcl command ot_timer <"set"|"get"> <varname>, * that helps to measure the time between a call to "ot_timer set" * and a call to "ot_timer get". $varname is the name of a variable * where to store the timestamp ("set"). "ot_timer get <varname>" * returns the time bw. this call and "ot_timer set <varname>" in * seconds. * Side-FX: none * ------------------------------------------------------------------------- */ int ot_timer_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { #ifdef __hpux extern struct timeval { unsigned long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ }; #endif struct timeval tv; double curtm, lasttm; char buf[32], *vp; if (argc != 3) { interp->result = "usage: ot_timer <\"set\"|\"get\"> <varname>"; return TCL_ERROR; } gettimeofday (&tv, NULL); /* convert time to seconds (float): */ curtm = tv.tv_sec + tv.tv_usec / 1000000.; if (strcmp (argv[1], "set") == 0) { sprintf (buf, "%f", curtm); if (Tcl_SetVar (interp, argv[2], buf, TCL_LEAVE_ERR_MSG) == NULL) { return TCL_ERROR; } else { strcpy (interp->result, buf); return TCL_OK; } } else if (strcmp (argv[1], "get") == 0) { if ((vp = Tcl_GetVar (interp, argv[2], TCL_LEAVE_ERR_MSG)) == NULL) { return TCL_ERROR; } else { if (Tcl_GetDouble (interp, vp, &lasttm) != TCL_OK) { return TCL_ERROR; } sprintf (interp->result, "%f", curtm - lasttm); return TCL_OK; } } else { interp->result = "usage: ot_timer <\"set\"|\"get\"> <varname>"; return TCL_ERROR; } } /* ot_time_Cmd */ #endif /* ============================================================================ * Name : ot_maplist_Cmd * In : ... (argv[1]: a tcl list, argv[2...n]: names of tcl variables) * Out : ... ("") * Desc : Implements the tcl command ot_maplist list ?var var ...? * list is a tcl list, var are tcl variables. Each of the named * tcl variable will be set to the respective element of list, i.e. * the first variable will be set to the first element, the second * to the second and so on. * Side-FX: none * ------------------------------------------------------------------------- */ int ot_maplist_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { int largc, i, j; char **largv; if (argc < 3) { interp->result = "usage: ot_maplist list var ?var ...?"; return TCL_ERROR; } if (Tcl_SplitList (interp, argv[1], &largc, &largv) == TCL_ERROR) { return TCL_ERROR; } for (i = 0; i < largc; i++) { if (!Tcl_SetVar (interp, argv[i + 2], largv[i], TCL_LEAVE_ERR_MSG)) { free (largv); return TCL_ERROR; } if (i == argc - 3) { /* last variable */ for (j = i + 1; j < largc; j++) { if (!Tcl_SetVar (interp, argv[i + 2], largv[j], TCL_LEAVE_ERR_MSG|TCL_LIST_ELEMENT|TCL_APPEND_VALUE)) { free (largv); return TCL_ERROR; } } break; } } free (largv); return TCL_OK; } /* ot_maplist_Cmd */ /* ============================================================================ * Name : ot_warp_pointer_Cmd * In : ... (argv[1]: a tk window or x coordinate, * argv[2]: (opt.) y coordinate) * Out : ... ("") * Desc : Implements the tcl command ot_warp_pointer win|x ?y? * The mouse pointer will be centered over $win or be put at $x/$y. * Side-FX: Generates mouse motion events. * ------------------------------------------------------------------------- */ #ifndef NO_TK int ot_warp_pointer_Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { Tk_Window tkwin; Window wid; int x, y; if (argc != 2 && argc != 3) { interp->result = "usage: ot_warp_pointer window / ot_warp_pointer x y"; return TCL_ERROR; } if (argc == 2) { tkwin = Tk_NameToWindow (interp, argv[1], Tk_MainWindow (interp)); if (!tkwin) return TCL_ERROR; x = Tk_Width (tkwin) / 2; y = Tk_Height (tkwin) / 2; wid = Tk_WindowId (tkwin); } else { tkwin = Tk_MainWindow (interp); tcl_getint (argv[1], &x); tcl_getint (argv[2], &y); wid = RootWindow (Tk_Display (tkwin), 0); } XWarpPointer (Tk_Display (tkwin), None, wid, 0, 0, 0, 0, x, y); return TCL_OK; } /* ot_warp_pointer_Cmd */ #endif /* ===== Utility Functions ================================================= */ /* ============================================================================ * Name : Ot_TclInvoke * In : interp - Tcl interpreter * cmd - command to invoke * Out : return value of the invoked command * Desc : This invokes the tcl command specified by argc and argv directly * without performing any kind of substitution. That's why it should * be considerably faster than Tcl_Eval. * Side-FX: none * ------------------------------------------------------------------------- */ int Ot_TclInvoke (interp, cmd) Tcl_Interp *interp; char *cmd; { Tcl_CmdInfo cmdInfo; int argc, retval; char **argv; #ifdef ODEBUG fprintf (stderr, "(Ot_TclInvoke) %s\n", cmd); #endif if (Tcl_SplitList (interp, cmd, &argc, &argv) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetCommandInfo (interp, argv[0], &cmdInfo) == 0) { sprintf (interp->result, "invalid command name \"%s\"", argv[0]); free (argv); return TCL_ERROR; } Tcl_ResetResult (interp); retval = (*cmdInfo.proc) (cmdInfo.clientData, interp, argc, argv); free (argv); return retval; } /* Ot_TclInvoke */ /* ============================================================================ * Name : Ot_GetInt * In : interp - tcl interpreter * var - name of tcl variable * index - NULL or name of index if var is an array variable * val - pointer of type int (value of $var will be stored here) * Out : TCL_OK or TCL_ERROR * Desc : Stores the value of the given variable at *val as an integer. * Side-FX: none * ------------------------------------------------------------------------- */ int Ot_GetInt (interp, var, index, val) Tcl_Interp *interp; char *var; char *index; int *val; { char *v_value; if (!index) { if (!(v_value = (char *) Tcl_GetVar (interp, var, TCL_LEAVE_ERR_MSG))) { return TCL_ERROR; } } else { if (!(v_value = (char *) Tcl_GetVar2 (interp, var, index, TCL_LEAVE_ERR_MSG))) { return TCL_ERROR; } #ifdef ODEBUG fprintf (stderr, "(Ot_GetInt) %s(%s) : %s\n", var, index, v_value); #endif } return Tcl_GetInt (interp, v_value, val); } /* Ot_GetInt */ /* ============================================================================ * Name : Ot_GetDouble * In : interp - tcl interpreter * var - name of tcl variable * index - NULL or name of index if var is an array variable * val - pointer of type double (value of $var will be stored here) * Out : TCL_OK or TCL_ERROR * Desc : Stores the value of the given variable at *val as a double. * Side-FX: none * ------------------------------------------------------------------------- */ int Ot_GetDouble (interp, var, index, val) Tcl_Interp *interp; char *var; char *index; double *val; { char *v_value; if (!index) { if (!(v_value = (char *) Tcl_GetVar (interp, var, TCL_LEAVE_ERR_MSG))) { return TCL_ERROR; } } else { if (!(v_value = (char *) Tcl_GetVar2 (interp, var, index, TCL_LEAVE_ERR_MSG))) { return TCL_ERROR; } } return Tcl_GetDouble (interp, v_value, val); } /* Ot_GetDouble */ /* ============================================================================ * Name : Ot_GetBoolean * In : interp - tcl interpreter * var - name of tcl variable * index - NULL or name of index if var is an array variable * val - pointer of type int (value of $var will be stored here) * Out : TCL_OK or TCL_ERROR * Desc : Stores the value of the given variable at *val as an integer. * Side-FX: none * ------------------------------------------------------------------------- */ int Ot_GetBoolean (interp, var, index, val) Tcl_Interp *interp; char *var; char *index; int *val; { char *v_value; if (!index) { if (!(v_value = (char *) Tcl_GetVar (interp, var, TCL_LEAVE_ERR_MSG))) { return TCL_ERROR; } } else { if (!(v_value = (char *) Tcl_GetVar2 (interp, var, index, TCL_LEAVE_ERR_MSG))) { return TCL_ERROR; } } return Tcl_GetBoolean (interp, v_value, val); } /* Ot_GetBoolean */ /* ============================================================================ * Name : Ot_GetVar * In : interp - tcl interpreter * var - name of tcl variable * flags - OR'ed Tcl flags * Out : Pointer to var's value or NULL * ------------------------------------------------------------------------- */ char *Ot_GetVar (interp, var, flags) Tcl_Interp *interp; char *var; int flags; { #if TCL_MAJOR_VERSION < 8 return Tcl_GetVar (interp, var, flags); #else Tcl_Obj *obj; obj = Tcl_ObjGetVar2 (interp, Tcl_NewStringObj (var, -1), NULL, flags); if (obj != NULL) return Tcl_GetStringFromObj (obj, NULL); else return NULL; #endif } /* ============================================================================ * Name : Ot_GetVar2 * In : interp - tcl interpreter * var - name of tcl variable * ind - "index" of array var * flags - OR'ed Tcl flags * Out : Pointer to var's value or NULL * ------------------------------------------------------------------------- */ char *Ot_GetVar2 (interp, var, ind, flags) Tcl_Interp *interp; char *var; char *ind; int flags; { #if TCL_MAJOR_VERSION < 8 return Tcl_GetVar2 (interp, var, ind, flags); #else Tcl_Obj *obj; obj = Tcl_ObjGetVar2 (interp, Tcl_NewStringObj (var, -1), Tcl_NewStringObj (ind, -1), flags); if (obj != NULL) return Tcl_GetStringFromObj (obj, NULL); else return NULL; #endif } /* ============================================================================ * Name : Ot_SetVar * In : interp - tcl interpreter * var - name of tcl variable * newval - new value of var * flags - OR'ed Tcl flags * Out : Pointer to var's value or NULL * ------------------------------------------------------------------------- */ char *Ot_SetVar (interp, var, newval, flags) Tcl_Interp *interp; char *var; char *newval; int flags; { #if TCL_MAJOR_VERSION < 8 return Tcl_SetVar (interp, var, newval, flags); #else Tcl_Obj *obj; obj = Tcl_ObjSetVar2 (interp, Tcl_NewStringObj (var, -1), NULL, Tcl_NewStringObj (newval, -1), flags); if (obj != NULL) return Tcl_GetStringFromObj (obj, NULL); else return NULL; #endif } /* ============================================================================ * Name : Ot_SetVar2 * In : interp - tcl interpreter * var - name of tcl variable * newval - new value of var * flags - OR'ed Tcl flags * Out : Pointer to var's value or NULL * ------------------------------------------------------------------------- */ char *Ot_SetVar2 (interp, var, ind, newval, flags) Tcl_Interp *interp; char *var; char *ind; char *newval; int flags; { #if TCL_MAJOR_VERSION < 8 return Tcl_SetVar2 (interp, var, ind, newval, flags); #else Tcl_Obj *obj; obj = Tcl_ObjSetVar2 (interp, Tcl_NewStringObj (var, -1), Tcl_NewStringObj (ind, -1), Tcl_NewStringObj (newval, -1), flags); if (obj != NULL) return Tcl_GetStringFromObj (obj, NULL); else return NULL; #endif } ��tkdesk-2.0/libdesk/ot.h�����������������������������������������������������������������������������0100644�0001750�0000764�00000006676�10037120740�013146� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: ot.h * Project: ot - Optimizing Tcl * Started: 12.02.94 * Changed: 12.02.94 * * Description: This header file contains a few macro function definitions * for simplifying the use of the ot and Tcl/Tk C libraries. * And of course it contains prototypes for functions and * tcl commands implemented by ot. * * ========================================================================= */ #ifndef _OT_H #define _OT_H #include <stdlib.h> #include <tcl.h> #include <tk.h> /* ===== Defines */ #define round(x) (int)(((x) >= 0) ? (double)(x) + 0.5 : (double)(x) - 0.5) /* Macros for tcl optimization (these assume the variable interp to point to the Tcl interpreter): */ #define ot_invoke(cmd) \ if (Ot_TclInvoke (interp, (cmd)) == TCL_ERROR) return TCL_ERROR; #define ot_getvar(var, x) \ if (!((x) = Ot_GetVar (interp, (var), TCL_LEAVE_ERR_MSG))) \ return TCL_ERROR; #define ot_getvar2(var, ind, x) \ if (!((x) = Ot_GetVar2 (interp, (var), (ind), TCL_LEAVE_ERR_MSG))) \ return TCL_ERROR; #define ot_setvar(var, newval) \ if (!(Ot_SetVar (interp, (var), (newval), TCL_LEAVE_ERR_MSG))) \ return TCL_ERROR; #define ot_setvar2(var, ind, newval) \ if (!(Ot_SetVar2 (interp, (var), (ind), (newval), TCL_LEAVE_ERR_MSG))) \ return TCL_ERROR; #define ot_setvar_with_flags(var, newval, flags) \ if (!(Ot_SetVar (interp, (var), (newval), (flags)))) \ return TCL_ERROR; #define ot_setvar2_with_flags(var, ind, newval, flags) \ if (!(Ot_SetVar2 (interp, (var), (ind), (newval), (flags)))) \ return TCL_ERROR; #define tcl_getint(str, x) \ if (Tcl_GetInt (interp, (str), (int *)(x)) == TCL_ERROR) \ return TCL_ERROR; #define tcl_getdouble(str, x) \ if (Tcl_GetDouble (interp, (str), (double *)(x)) == TCL_ERROR) \ return TCL_ERROR; #define tcl_getboolean(str, x) \ if (Tcl_GetBoolean (interp, (str), (int *)(x)) == TCL_ERROR) \ return TCL_ERROR; #define ot_getint(var, ind, x) \ if (Ot_GetInt (interp, (var), (ind), (int *)(x)) == TCL_ERROR) \ return TCL_ERROR; #define ot_getdouble(var, ind, x) \ if (Ot_GetDouble (interp, (var), (ind), (double *)(x)) == TCL_ERROR) \ return TCL_ERROR; #define ot_getboolean(var, ind, x) \ if (Ot_GetBoolean (interp, (var), (ind), (int *)(x)) == TCL_ERROR) \ return TCL_ERROR; /* ===== Prototypes of Utility Functions */ EXTERN int Ot_TclInvoke _ANSI_ARGS_((Tcl_Interp *interp, char *cmd)); EXTERN char *Ot_GetVar _ANSI_ARGS_((Tcl_Interp *interp, char *var, int flags)); EXTERN char *Ot_GetVar2 _ANSI_ARGS_((Tcl_Interp *interp, char *var, char *ind, int flags)); EXTERN char *Ot_SetVar _ANSI_ARGS_((Tcl_Interp *interp, char *var, char *newval, int flags)); EXTERN char *Ot_SetVar2 _ANSI_ARGS_((Tcl_Interp *interp, char *var, char *ind, char *newval, int flags)); EXTERN int Ot_GetInt _ANSI_ARGS_((Tcl_Interp *interp, char *var, char *index, int *val)); EXTERN int Ot_GetDouble _ANSI_ARGS_((Tcl_Interp *interp, char *var, char *index, double *val)); EXTERN int Ot_GetBoolean _ANSI_ARGS_((Tcl_Interp *interp, char *var, char *index, int *val)); /* ===== Prototypes of Tcl Commands */ EXTERN int Ot_Init _ANSI_ARGS_((Tcl_Interp *interp)); EXTERN Tcl_CmdProc ot_timer_Cmd; EXTERN Tcl_CmdProc ot_maplist_Cmd; #ifndef NO_TK EXTERN Tcl_CmdProc ot_warp_pointer_Cmd; #endif #endif /* _OT_H */ ������������������������������������������������������������������tkdesk-2.0/libdesk/tixImgXpm.c����������������������������������������������������������������������0100644�0001750�0000764�00000117271�10037121264�014441� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * tixImgXpm.c -- * * This file implements images of type "pixmap" for Tix. * ______________________________________________________________________ * * Copyright statement for tixImgXpm.c * Copyright 1996, Expert Interface Technologies * * The following terms apply only to this file and no other parts of the * Tix library. * * Permission is hereby granted, without written agreement and * without license or royalty fees, to use, copy, modify, and * distribute this file, for any purpose, provided that existing * copyright notices are retained in all copies and that this * notice is included verbatim in any distributions. * * DISCLAIMER OF ALL WARRANTIES * * IN NO EVENT SHALL THE AUTHOR OF THIS SOFTWARE BE LIABLE TO ANY * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE * AND ITS DOCUMENTATION, EVEN IF THE AUTHOR OF THIS SOFTWARE HAS * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * THE AUTHOR OF THIS SOFTWARE SPECIFICALLY DISCLAIMS ANY * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" * BASIS, AND THE AUTHOR OF THIS SOFTWARE HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR * MODIFICATIONS. * ___________________________________________________________________ * * This file is adapted from the Tk 4.0 source file tkImgBmap.c * Original tkImgBmap.c copyright information: * * Copyright (c) 1994 The Regents of the University of California. * Copyright (c) 1994-1995 Sun Microsystems, Inc. * * See the file "license.terms.tcltk" for information on usage * and redistribution of the original tkImgBmap.c file, and for a * DISCLAIMER OF ALL WARRANTIES from the authors of tkImgBmap.c. */ #include "config.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #ifdef HAVE_TCL8_0_H #include <tcl8.0.h> #include <tk8.0.h> #else #include <tk.h> #endif #include <X11/Xutil.h> /*#include "tkInt.h" #include "tkPort.h"*/ /*#include <tix.h>*/ EXTERN void panic(); /* constants used only in this file */ #define XPM_MONO 1 #define XPM_GRAY_4 2 #define XPM_GRAY 3 #define XPM_COLOR 4 #define XPM_SYMBOLIC 5 #define XPM_UNKNOWN 6 /* * The following data structure represents the master for a pixmap * image: */ typedef struct PixmapMaster { Tk_ImageMaster tkMaster; /* Tk's token for image master. NULL means * the image is being deleted. */ Tcl_Interp *interp; /* Interpreter for application that is * using image. */ Tcl_Command imageCmd; /* Token for image command (used to delete * it when the image goes away). NULL means * the image command has already been * deleted. */ char *fileString; /* Value of -file option (malloc'ed). * valid only if the -file option is specified */ char *dataString; /* Value of -data option (malloc'ed). * valid only if the -data option is specified */ /* First in list of all instances associated * with this master. */ Tk_Uid id; /* ID's for XPM data already compiled * into the tixwish binary */ int size[2]; /* width and height */ int ncolors; /* number of colors */ int cpp; /* characters per pixel */ char ** data; /* The data that defines this pixmap * image (array of strings). It is * converted into an X Pixmap when this * image is instanciated */ int isDataAlloced; /* False iff the data is got from * the -id switch */ struct PixmapInstance *instancePtr; } PixmapMaster; /* Make this more portable */ typedef struct ColorStruct { char c; /* This is used if CPP is one */ char * cstring; /* This is used if CPP is bigger than one */ XColor * colorPtr; } ColorStruct; /* * The following data structure represents all of the instances of an * image that lie within a particular window: * * %% ToDo * Currently one instance is created for each window that uses this pixmap. * This is usually OK because pixmaps are usually not shared or only shared by * a small number of windows. To improve resource allocation, we can * create an instance for each (Display x Visual x Depth) combo. This will * usually reduce the number of instances to one. */ typedef struct PixmapInstance { int refCount; /* Number of instances that share this * data structure. */ PixmapMaster *masterPtr; /* Pointer to master for image. */ Tk_Window tkwin; /* Window in which the instances will be * displayed. */ Pixmap pixmap; /* The pixmap to display. */ Pixmap mask; /* Mask: only display pixmap pixels where * there are 1's here. */ GC gc; /* Graphics context for displaying pixmap. * None means there was an error while * setting up the instance, so it cannot * be displayed. */ struct PixmapInstance *nextPtr; /* Next in list of all instance structures * associated with masterPtr (NULL means * end of list). */ ColorStruct * colors; } PixmapInstance; /* * The type record for pixmap images: */ static int ImgXpmCreate _ANSI_ARGS_((Tcl_Interp *interp, char *name, int argc, char **argv, Tk_ImageType *typePtr, Tk_ImageMaster master, ClientData *clientDataPtr)); static ClientData ImgXpmGet _ANSI_ARGS_((Tk_Window tkwin, ClientData clientData)); static void ImgXpmDisplay _ANSI_ARGS_((ClientData clientData, Display *display, Drawable drawable, int imageX, int imageY, int width, int height, int drawableX, int drawableY)); static void ImgXpmFree _ANSI_ARGS_((ClientData clientData, Display *display)); static void ImgXpmDelete _ANSI_ARGS_((ClientData clientData)); Tk_ImageType tixPixmapImageType = { "pixmap", /* name */ ImgXpmCreate, /* createProc */ ImgXpmGet, /* getProc */ ImgXpmDisplay, /* displayProc */ ImgXpmFree, /* freeProc */ ImgXpmDelete, /* deleteProc */ (Tk_ImagePostscriptProc *) NULL, /* postscriptProc ?? */ (struct Tk_ImageType *) NULL /* nextPtr */ }; /* * Information used for parsing configuration specs: */ static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_STRING, "-data", (char *) NULL, (char *) NULL, (char *) NULL, Tk_Offset(PixmapMaster, dataString), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-file", (char *) NULL, (char *) NULL, (char *) NULL, Tk_Offset(PixmapMaster, fileString), TK_CONFIG_NULL_OK}, {TK_CONFIG_UID, "-id", (char *) NULL, (char *) NULL, (char *) NULL, Tk_Offset(PixmapMaster, id), TK_CONFIG_NULL_OK}, {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0} }; /* * Prototypes for procedures used only locally in this file: */ static int ImgXpmCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc, char **argv)); static void ImgXpmCmdDeletedProc _ANSI_ARGS_(( ClientData clientData)); static void ImgXpmConfigureInstance _ANSI_ARGS_(( PixmapInstance *instancePtr)); static int ImgXpmConfigureMaster _ANSI_ARGS_(( PixmapMaster *masterPtr, int argc, char **argv, int flags)); static int ImgXpmGetData _ANSI_ARGS_((Tcl_Interp *interp, PixmapMaster *masterPtr)); static char ** ImgXpmGetDataFromFile _ANSI_ARGS_((Tcl_Interp * interp, char * string, int * numLines_return)); static char ** ImgXpmGetDataFromId _ANSI_ARGS_((Tcl_Interp * interp, char * id)); static char ** ImgXpmGetDataFromString _ANSI_ARGS_((Tcl_Interp*interp, char * string, int * numLines_return)); static int ImgXpmGetPixmapFromData _ANSI_ARGS_(( Tcl_Interp * interp, PixmapMaster *masterPtr, PixmapInstance *instancePtr)); /* Local data, used only in this file */ static Tcl_HashTable xpmTable; static int xpmTableInited = 0; /* *---------------------------------------------------------------------- * * ImgXpmCreate -- * * This procedure is called by the Tk image code to create "pixmap" * images. * * Results: * A standard Tcl result. * * Side effects: * The data structure for a new image is allocated. * *---------------------------------------------------------------------- */ static int ImgXpmCreate(interp, name, argc, argv, typePtr, master, clientDataPtr) Tcl_Interp *interp; /* Interpreter for application containing * image. */ char *name; /* Name to use for image. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings for options (doesn't * include image name or type). */ Tk_ImageType *typePtr; /* Pointer to our type record (not used). */ Tk_ImageMaster master; /* Token for image, to be used by us in * later callbacks. */ ClientData *clientDataPtr; /* Store manager's token for image here; * it will be returned in later callbacks. */ { PixmapMaster *masterPtr; masterPtr = (PixmapMaster *) ckalloc(sizeof(PixmapMaster)); masterPtr->tkMaster = master; masterPtr->interp = interp; masterPtr->imageCmd = Tcl_CreateCommand(interp, name, ImgXpmCmd, (ClientData) masterPtr, ImgXpmCmdDeletedProc); masterPtr->fileString = NULL; masterPtr->dataString = NULL; masterPtr->id = NULL; masterPtr->data = NULL; masterPtr->isDataAlloced = 0; masterPtr->instancePtr = NULL; if (ImgXpmConfigureMaster(masterPtr, argc, argv, 0) != TCL_OK) { ImgXpmDelete((ClientData) masterPtr); return TCL_ERROR; } *clientDataPtr = (ClientData) masterPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * * ImgXpmConfigureMaster -- * * This procedure is called when a pixmap image is created or * reconfigured. It process configuration options and resets * any instances of the image. * * Results: * A standard Tcl return value. If TCL_ERROR is returned then * an error message is left in masterPtr->interp->result. * * Side effects: * Existing instances of the image will be redisplayed to match * the new configuration options. * * If any error occurs, the state of *masterPtr is restored to * previous state. * *---------------------------------------------------------------------- */ static int ImgXpmConfigureMaster(masterPtr, argc, argv, flags) PixmapMaster *masterPtr; /* Pointer to data structure describing * overall pixmap image to (reconfigure). */ int argc; /* Number of entries in argv. */ char **argv; /* Pairs of configuration options for image. */ int flags; /* Flags to pass to Tk_ConfigureWidget, * such as TK_CONFIG_ARGV_ONLY. */ { PixmapInstance *instancePtr; char * oldData, * oldFile; Tk_Uid oldId; oldData = masterPtr->dataString; oldFile = masterPtr->fileString; oldId = masterPtr->id; if (Tk_ConfigureWidget(masterPtr->interp, Tk_MainWindow(masterPtr->interp), configSpecs, argc, argv, (char *) masterPtr, flags) != TCL_OK) { return TCL_ERROR; } if (masterPtr->id != NULL || masterPtr->dataString != NULL || masterPtr->fileString != NULL) { if (ImgXpmGetData(masterPtr->interp, masterPtr) != TCL_OK) { goto error; } } else { Tcl_AppendResult(masterPtr->interp, "must specify one of -data, -file or -id", NULL); goto error; } /* * Cycle through all of the instances of this image, regenerating * the information for each instance. Then force the image to be * redisplayed everywhere that it is used. */ for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { ImgXpmConfigureInstance(instancePtr); } if (masterPtr->data) { Tk_ImageChanged(masterPtr->tkMaster, 0, 0, masterPtr->size[0], masterPtr->size[1], masterPtr->size[0], masterPtr->size[1]); } else { Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, 0, 0); } /* done:*/ return TCL_OK; error: /* Restore it to the original (possible valid) mode */ masterPtr->dataString = oldData; masterPtr->fileString = oldFile; masterPtr->id = oldId; return TCL_ERROR; } /* *---------------------------------------------------------------------- * * ImgXpmGetData -- * * Given a file name or ASCII string, this procedure parses the * file or string contents to produce binary data for a pixmap. * * Results: * If the pixmap description was parsed successfully then the data * is read into an array of strings. This array will later be used * to create X Pixmaps for each instance. * * Side effects: * The masterPtr->data array is allocated when successful. Contents of * *masterPtr is changed only when successful. *---------------------------------------------------------------------- */ static int ImgXpmGetData(interp, masterPtr) Tcl_Interp *interp; /* For reporting errors. */ PixmapMaster *masterPtr; { char ** data = NULL; int isAllocated = 0; /* do we need to free "data"? */ int listArgc; char ** listArgv = NULL; int numLines; int size[2]; int cpp; int ncolors; if (masterPtr->id != NULL) { data = ImgXpmGetDataFromId(interp, (char *) (masterPtr->id)); isAllocated = 0; } else if (masterPtr->fileString != NULL) { data = ImgXpmGetDataFromFile(interp, masterPtr->fileString, &numLines); isAllocated = 1; } else if (masterPtr->dataString != NULL) { data = ImgXpmGetDataFromString(interp,masterPtr->dataString,&numLines); isAllocated = 1; } else { /* Should have been enforced by ImgXpmConfigureMaster() */ panic("ImgXpmGetData(): -data, -file and -id are all NULL"); } if (data == NULL) { return TCL_ERROR; } /* Parse the first line of the data and get info about this pixmap */ if (Tcl_SplitList(interp, data[0], &listArgc, &listArgv) != TCL_OK) { goto error; } if (listArgc < 4) { Tcl_AppendResult(interp, "File format error", NULL); goto error; } if (Tcl_GetInt(interp, listArgv[0], &size[0]) != TCL_OK) { goto error; } if (Tcl_GetInt(interp, listArgv[1], &size[1]) != TCL_OK) { goto error; } if (Tcl_GetInt(interp, listArgv[2], &ncolors) != TCL_OK) { goto error; } if (Tcl_GetInt(interp, listArgv[3], &cpp) != TCL_OK) { goto error; } if (listArgv) { ckfree((char*)listArgv); } if (isAllocated) { if (numLines != size[1] + ncolors + 1) { /* the number of lines read from the file/data * is not the same as specified in the data */ goto error; } } /*done:*/ if (masterPtr->isDataAlloced && masterPtr->data) { ckfree((char*)masterPtr->data); } masterPtr->isDataAlloced = isAllocated; masterPtr->data = data; masterPtr->size[0] = size[0]; masterPtr->size[1] = size[1]; masterPtr->cpp = cpp; masterPtr->ncolors = ncolors; return TCL_OK; error: Tcl_ResetResult(interp); Tcl_AppendResult(interp, "File format error", NULL); if (isAllocated && data) { ckfree((char*)data); } return TCL_ERROR; } static char ** ImgXpmGetDataFromId(interp, id) Tcl_Interp * interp; char * id; { Tcl_HashEntry * hashPtr; if (xpmTableInited == 0) { hashPtr = NULL; } else { hashPtr = Tcl_FindHashEntry(&xpmTable, id); } if (hashPtr == NULL) { Tcl_AppendResult(interp, "unknown pixmap ID \"", id, "\"", NULL); return (char**)NULL; } else { return (char**)Tcl_GetHashValue(hashPtr); } } static char ** ImgXpmGetDataFromString(interp, string, numLines_return) Tcl_Interp * interp; char * string; int * numLines_return; { int quoted; char * p, * list; int numLines; char ** data; /* skip the leading blanks (leading blanks are not defined in the * the XPM definition, but skipping them shouldn't hurt. Also, the ability * to skip the leading blanks is good for using in-line XPM data in TCL * scripts */ while (isspace(*string)) { ++ string; } /* parse the header */ if (strncmp("/* XPM", string, 6) != 0) { goto error; } /* strip the comments */ for (quoted = 0, p=string; *p;) { if (!quoted) { if (*p == '"') { quoted = 1; ++ p; continue; } if (*p == '/' && *(p+1) == '*') { *p++ = ' '; *p++ = ' '; while (1) { if (*p == 0) { break; } if (*p == '*' && *(p+1) == '/') { *p++ = ' '; *p++ = ' '; break; } *p++ = ' '; } continue; } ++ p; } else { if (*p == '"') { quoted = 0; } ++ p; } } /* Search for the opening brace */ for (p=string; *p;) { if (*p != '{') { ++ p; } else { ++p; break; } } /* Change the buffer in to a proper TCL list */ quoted = 0; list = p; while (*p) { if (!quoted) { if (*p == '"') { quoted = 1; ++ p; continue; } if (isspace(*p)) { *p = ' '; } else if (*p == ',') { *p = ' '; } else if (*p == '}') { *p = 0; break; } ++p; } else { if (*p == '"') { quoted = 0; } ++ p; } } /* The following code depends on the fact that Tcl_SplitList * strips away double quoates inside a list: ie: * if string == "\"1\" \"2\"" then * list[0] = "1" * list[1] = "2" * and NOT * * list[0] = "\"1\"" * list[1] = "\"2\"" */ if (Tcl_SplitList(interp, list, &numLines, &data) != TCL_OK) { goto error; } else { if (numLines == 0) { /* error: empty data? */ if (data != NULL) { ckfree((char*)data); goto error; } } * numLines_return = numLines; return data; } error: Tcl_AppendResult(interp, "File format error", NULL); return (char**) NULL; } static char ** ImgXpmGetDataFromFile(interp, fileName, numLines_return) Tcl_Interp * interp; char * fileName; int * numLines_return; { int fileId, size; char ** data; struct stat statBuf; char *cmdBuffer = NULL; Tcl_DString buffer; /* initialized by Tcl_TildeSubst */ fileName = Tcl_TildeSubst(interp, fileName, &buffer); if (fileName == NULL) { goto error; } fileId = open(fileName, O_RDONLY, 0); if (fileId < 0) { Tcl_AppendResult(interp, "couldn't read file \"", fileName, "\": ", Tcl_PosixError(interp), (char *) NULL); goto error; } if (fstat(fileId, &statBuf) == -1) { Tcl_AppendResult(interp, "couldn't stat file \"", fileName, "\": ", Tcl_PosixError(interp), (char *) NULL); close(fileId); goto error; } cmdBuffer = (char *) ckalloc((unsigned) statBuf.st_size+1); size = read(fileId, cmdBuffer, (size_t) statBuf.st_size); if (size < 0) { Tcl_AppendResult(interp, "error in reading file \"", fileName, "\": ", Tcl_PosixError(interp), (char *) NULL); close(fileId); goto error; } if (close(fileId) != 0) { Tcl_AppendResult(interp, "error closing file \"", fileName, "\": ", Tcl_PosixError(interp), (char *) NULL); goto error; } cmdBuffer[size] = 0; /*done:*/ data = ImgXpmGetDataFromString(interp, cmdBuffer, numLines_return); ckfree(cmdBuffer); Tcl_DStringFree(&buffer); return data; error: if (cmdBuffer != NULL) { ckfree(cmdBuffer); } Tcl_DStringFree(&buffer); return (char**)NULL; } static char * GetType(colorDefn, type_ret) char * colorDefn; int * type_ret; { char * p = colorDefn; /* skip white spaces */ while (*p && isspace(*p)) { p ++; } /* parse the type */ if (p[0] != '\0' && p[0] == 'm' && p[1] != '\0' && isspace(p[1])) { *type_ret = XPM_MONO; p += 2; } else if (p[0] != '\0' && p[0] == 'g' && p[1] != '\0' && p[1] == '4' && p[2] != '\0' && isspace(p[2])) { *type_ret = XPM_GRAY_4; p += 3; } else if (p[0] != '\0' && p[0] == 'g' && p[1] != '\0' && isspace(p[1])) { *type_ret = XPM_GRAY; p += 2; } else if (p[0] != '\0' && p[0] == 'c' && p[1] != '\0' && isspace(p[1])) { *type_ret = XPM_COLOR; p += 2; } else if (p[0] != '\0' && p[0] == 's' && p[1] != '\0' && isspace(p[1])) { *type_ret = XPM_SYMBOLIC; p += 2; } else { *type_ret = XPM_UNKNOWN; return NULL; } return p; } /* colorName is guaranteed to be big enough */ static char * GetColor(colorDefn, colorName, type_ret) char * colorDefn; char * colorName; /* if found, name is copied to this array */ int * type_ret; { /*int n, type;*/ int type; char * p; if (!colorDefn) { return NULL; } if ((colorDefn = GetType(colorDefn, &type)) == NULL) { /* unknown type */ return NULL; } else { *type_ret = type; } /* skip white spaces */ while (*colorDefn && isspace(*colorDefn)) { colorDefn ++; } p = colorName; while (1) { int dummy; while (*colorDefn && !isspace(*colorDefn)) { *p++ = *colorDefn++; } if (!*colorDefn) { break; } if (GetType(colorDefn, &dummy) == NULL) { /* the next string should also be considered as a part of a color * name */ while (*colorDefn && isspace(*colorDefn)) { *p++ = *colorDefn++; } } else { break; } if (!*colorDefn) { break; } } /* Mark the end of the colorName */ *p = '\0'; return colorDefn; } /*---------------------------------------------------------------------- * ImgXpmGetPixmapFromData -- * * Creates a pixmap for an image instance. *---------------------------------------------------------------------- */ static int ImgXpmGetPixmapFromData(interp, masterPtr, instancePtr) Tcl_Interp * interp; PixmapMaster *masterPtr; PixmapInstance *instancePtr; { XImage * image = NULL, * mask = NULL; /*int pad, depth, i, j, k, n, lOffset, isTransp = 0, isMono;*/ int pad, depth, i, j, k, lOffset, isTransp = 0, isMono; ColorStruct * colors; GC gc; Display *display = Tk_Display(instancePtr->tkwin); depth = Tk_Depth(instancePtr->tkwin); if (depth > 16) { pad = 32; } else if (depth > 8) { pad = 16; } else { pad = 8; } switch ((Tk_Visual(instancePtr->tkwin))->class) { case StaticGray: case GrayScale: isMono = 1; break; default: isMono = 0; } /* * Create the XImage structures to store the temporary image */ image = XCreateImage(display, Tk_Visual(instancePtr->tkwin), depth, ZPixmap, 0, 0, masterPtr->size[0], masterPtr->size[1], pad, 0); image->data = (char *)ckalloc(image->bytes_per_line * masterPtr->size[1]); mask = XCreateImage(display, Tk_Visual(instancePtr->tkwin), 1, ZPixmap, 0, 0, masterPtr->size[0], masterPtr->size[1], pad, 0); mask->data = (char *)ckalloc(mask->bytes_per_line * masterPtr->size[1]); /* * Parse the colors */ lOffset = 1; colors = (ColorStruct*)ckalloc(sizeof(ColorStruct)*masterPtr->ncolors); /* Initialize the color structures */ for (i=0; i<masterPtr->ncolors; i++) { colors[i].colorPtr = NULL; if (masterPtr->cpp == 1) { colors[i].c = 0; } else { colors[i].cstring = (char*)ckalloc(masterPtr->cpp); colors[i].cstring[0] = 0; } } for (i=0; i<masterPtr->ncolors; i++) { char * colorDefn; /* the color definition line */ char * colorName; /* temp place to hold the color name * defined for one type of visual */ char * useName; /* the color name used for this * color. If there are many names * defined, choose the name that is * "best" for the target visual */ int found; colorDefn = masterPtr->data[i+lOffset]+masterPtr->cpp; colorName = (char*)ckalloc(strlen(colorDefn)); useName = (char*)ckalloc(strlen(colorDefn)); found = 0; while (colorDefn && *colorDefn) { int type; if ((colorDefn=GetColor(colorDefn, colorName, &type)) == NULL) { break; } if (colorName[0] == '\0') { continue; } switch (type) { case XPM_MONO: if (isMono && depth == 1) { strcpy(useName, colorName); found = 1; goto gotcolor; } break; case XPM_GRAY_4: if (isMono && depth == 4) { strcpy(useName, colorName); found = 1; goto gotcolor; } break; case XPM_GRAY: if (isMono && depth > 4) { strcpy(useName, colorName); found = 1; goto gotcolor; } break; case XPM_COLOR: if (!isMono) { strcpy(useName, colorName); found = 1; goto gotcolor; } break; } if (type != XPM_SYMBOLIC && type != XPM_UNKNOWN) { if (!found) { /* use this color as default */ strcpy(useName, colorName); found = 1; } } } gotcolor: if (masterPtr->cpp == 1) { colors[i].c = masterPtr->data[i+lOffset][0]; } else { strncpy(colors[i].cstring, masterPtr->data[i+lOffset], (size_t)masterPtr->cpp); } if (found) { if (strcasecmp(useName, "none") != 0) { colors[i].colorPtr = Tk_GetColor(interp, instancePtr->tkwin, Tk_GetUid(useName)); if (colors[i].colorPtr == NULL) { colors[i].colorPtr = Tk_GetColor(interp, instancePtr->tkwin, Tk_GetUid("black")); } } } else { colors[i].colorPtr = Tk_GetColor(interp, instancePtr->tkwin, Tk_GetUid("black")); } ckfree(colorName); ckfree(useName); } lOffset += masterPtr->ncolors; /* * Parse the main body of the image */ for (i=0; i<masterPtr->size[1]; i++) { char * p = masterPtr->data[i+lOffset]; for (j=0; j<masterPtr->size[0]; j++) { if (masterPtr->cpp == 1) { for (k=0; k<masterPtr->ncolors; k++) { if (*p == colors[k].c) { if (colors[k].colorPtr != NULL) { XPutPixel(image, j, i, colors[k].colorPtr->pixel); XPutPixel(mask, j, i, 1); } else { XPutPixel(mask, j, i, 0); isTransp = 1; } break; } } if (*p) { p++; } } else { for (k=0; k<masterPtr->ncolors; k++) { if (strncmp(p, colors[k].cstring, (size_t)masterPtr->cpp) == 0) { if (colors[k].colorPtr != NULL) { XPutPixel(image, j, i, colors[k].colorPtr->pixel); XPutPixel(mask, j, i, 1); } else { XPutPixel(mask, j, i, 0); isTransp = 1; } break; } } for (k=0; *p && k<masterPtr->cpp; k++) { p++; } } } } /* * Create the pixmap(s) from the XImage structure. The mask is created * only if needed (i.e., there is at least one transparent pixel) */ instancePtr->colors = colors; /* main image */ instancePtr->pixmap = Tk_GetPixmap(display, Tk_WindowId(instancePtr->tkwin), masterPtr->size[0], masterPtr->size[1], depth); gc = Tk_GetGC(instancePtr->tkwin, 0, NULL); /* TkPutImage(NULL, 0, display, instancePtr->pixmap, gc, image, 0, 0, 0, 0, masterPtr->size[0], masterPtr->size[1]); */ XPutImage(display, instancePtr->pixmap, gc, image, 0, 0, 0, 0, masterPtr->size[0], masterPtr->size[1]); Tk_FreeGC(display, gc); /* mask, if necessary */ if (isTransp) { instancePtr->mask = Tk_GetPixmap(display, Tk_WindowId(instancePtr->tkwin), masterPtr->size[0], masterPtr->size[1], 1); gc = XCreateGC(display, instancePtr->mask, 0, NULL); /*TkPutImage(NULL, 0, display, instancePtr->mask, gc, mask, 0, 0, 0, 0, masterPtr->size[0], masterPtr->size[1]);*/ XPutImage(display, instancePtr->mask, gc, mask, 0, 0, 0, 0, masterPtr->size[0], masterPtr->size[1]); XFreeGC(display, gc); } else { instancePtr->mask = None; } /* Done */ if (image) { ckfree((char*)image->data); image->data = NULL; XDestroyImage(image); } if (mask) { ckfree((char*)mask->data); mask->data = NULL; XDestroyImage(mask); } return 0; } /* *---------------------------------------------------------------------- * * ImgXpmConfigureInstance -- * * This procedure is called to create displaying information for * a pixmap image instance based on the configuration information * in the master. It is invoked both when new instances are * created and when the master is reconfigured. * * Results: * None. * * Side effects: * Generates errors via Tk_BackgroundError if there are problems * in setting up the instance. * *---------------------------------------------------------------------- */ static void ImgXpmConfigureInstance(instancePtr) PixmapInstance *instancePtr; /* Instance to reconfigure. */ { PixmapMaster *masterPtr = instancePtr->masterPtr; XGCValues gcValues; GC gc; unsigned int gcMask; if (instancePtr->pixmap != None) { XFreePixmap(Tk_Display(instancePtr->tkwin), instancePtr->pixmap); } if (instancePtr->mask != None) { XFreePixmap(Tk_Display(instancePtr->tkwin), instancePtr->mask); } if (instancePtr->colors != NULL) { int i; for (i=0; i<masterPtr->ncolors; i++) { if (instancePtr->colors[i].colorPtr != NULL) { Tk_FreeColor(instancePtr->colors[i].colorPtr); } if (masterPtr->cpp != 1) { ckfree(instancePtr->colors[i].cstring); } } ckfree((char*)instancePtr->colors); } if (Tk_WindowId(instancePtr->tkwin) == None) { Tk_MakeWindowExist(instancePtr->tkwin); } /* Assumption: masterPtr->data is always non NULL (enfored by * ImgXpmConfigureMaster()). Also, the data must be in a valid * format (partially enforced by ImgXpmConfigureMaster(), see comments * inside that function). */ ImgXpmGetPixmapFromData(masterPtr->interp, masterPtr, instancePtr); /* Allocate a GC for drawing this instance (mask is not used if there * is no transparent pixels inside the image).*/ if (instancePtr->mask != None) { gcMask = GCGraphicsExposures|GCClipMask; } else { gcMask = GCGraphicsExposures; } gcValues.graphics_exposures = False; gcValues.clip_mask = instancePtr->mask; gc = Tk_GetGC(instancePtr->tkwin, gcMask, &gcValues); if (instancePtr->gc != None) { Tk_FreeGC(Tk_Display(instancePtr->tkwin), instancePtr->gc); } instancePtr->gc = gc; return; } /* *-------------------------------------------------------------- * * ImgXpmCmd -- * * This procedure is invoked to process the Tcl command * that corresponds to an image managed by this module. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */ static int ImgXpmCmd(clientData, interp, argc, argv) ClientData clientData; /* Information about button widget. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { PixmapMaster *masterPtr = (PixmapMaster *) clientData; int c, code; size_t length; if (argc < 2) { sprintf(interp->result, "wrong # args: should be \"%.50s option ?arg arg ...?\"", argv[0]); return TCL_ERROR; } c = argv[1][0]; length = strlen(argv[1]); if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0) && (length >= 2)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " cget option\"", (char *) NULL); return TCL_ERROR; } return Tk_ConfigureValue(interp, Tk_MainWindow(interp), configSpecs, (char *) masterPtr, argv[2], 0); } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0) && (length >= 2)) { if (argc == 2) { code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp), configSpecs, (char *) masterPtr, (char *) NULL, 0); } else if (argc == 3) { code = Tk_ConfigureInfo(interp, Tk_MainWindow(interp), configSpecs, (char *) masterPtr, argv[2], 0); } else { code = ImgXpmConfigureMaster(masterPtr, argc-2, argv+2, TK_CONFIG_ARGV_ONLY); } return code; } /*error:*/ Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be cget or configure", (char *) NULL); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * ImgXpmGet -- * * This procedure is called for each use of a pixmap image in a * widget. * * Results: * The return value is a token for the instance, which is passed * back to us in calls to ImgXpmDisplay and ImgXpmFre. * * Side effects: * A data structure is set up for the instance (or, an existing * instance is re-used for the new one). * *---------------------------------------------------------------------- */ static ClientData ImgXpmGet(tkwin, masterData) Tk_Window tkwin; /* Window in which the instance will be * used. */ ClientData masterData; /* Pointer to our master structure for the * image. */ { PixmapMaster *masterPtr = (PixmapMaster *) masterData; PixmapInstance *instancePtr; /* * See if there is already an instance for this window. If so * then just re-use it. */ for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { if (instancePtr->tkwin == tkwin) { instancePtr->refCount++; return (ClientData) instancePtr; } } /* * The image isn't already in use in this window. Make a new * instance of the image. */ instancePtr = (PixmapInstance *) ckalloc(sizeof(PixmapInstance)); instancePtr->refCount = 1; instancePtr->masterPtr = masterPtr; instancePtr->tkwin = tkwin; instancePtr->pixmap = None; instancePtr->mask = None; instancePtr->gc = None; instancePtr->nextPtr = masterPtr->instancePtr; instancePtr->colors = NULL; masterPtr->instancePtr = instancePtr; ImgXpmConfigureInstance(instancePtr); /* * If this is the first instance, must set the size of the image. */ if (instancePtr->nextPtr == NULL) { if (masterPtr->data) { Tk_ImageChanged(masterPtr->tkMaster, 0, 0, masterPtr->size[0], masterPtr->size[1], masterPtr->size[0], masterPtr->size[1]); } else { Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, 0, 0); } } return (ClientData) instancePtr; } /* *---------------------------------------------------------------------- * * ImgXpmDisplay -- * * This procedure is invoked to draw a pixmap image. * * Results: * None. * * Side effects: * A portion of the image gets rendered in a pixmap or window. * *---------------------------------------------------------------------- */ static void ImgXpmDisplay(clientData, display, drawable, imageX, imageY, width, height, drawableX, drawableY) ClientData clientData; /* Pointer to PixmapInstance structure for * for instance to be displayed. */ Display *display; /* Display on which to draw image. */ Drawable drawable; /* Pixmap or window in which to draw image. */ int imageX, imageY; /* Upper-left corner of region within image * to draw. */ int width, height; /* Dimensions of region within image to draw.*/ int drawableX, drawableY; /* Coordinates within drawable that * correspond to imageX and imageY. */ { PixmapInstance *instancePtr = (PixmapInstance *) clientData; /* * If there's no graphics context, it means that an error occurred * while creating the image instance so it can't be displayed. */ if (instancePtr->gc == None) { return; } /* * We always use masking: modify the mask origin within * the graphics context to line up with the image's origin. * Then draw the image and reset the clip origin, if there's * a mask. */ XSetClipOrigin(display, instancePtr->gc, drawableX - imageX, drawableY - imageY); XCopyArea(display, instancePtr->pixmap, drawable, instancePtr->gc, imageX, imageY, (unsigned) width, (unsigned) height, drawableX, drawableY); XSetClipOrigin(display, instancePtr->gc, 0, 0); } /* *---------------------------------------------------------------------- * * ImgXpmFree -- * * This procedure is called when a widget ceases to use a * particular instance of an image. * * Results: * None. * * Side effects: * Internal data structures get cleaned up. * *---------------------------------------------------------------------- */ static void ImgXpmFree(clientData, display) ClientData clientData; /* Pointer to PixmapInstance structure for * for instance to be displayed. */ Display *display; /* Display containing window that used image.*/ { PixmapInstance *instancePtr = (PixmapInstance *) clientData; PixmapInstance *prevPtr; instancePtr->refCount--; if (instancePtr->refCount > 0) { return; } /* * There are no more uses of the image within this widget. Free * the instance structure. */ if (instancePtr->pixmap != None) { XFreePixmap(display, instancePtr->pixmap); } if (instancePtr->mask != None) { XFreePixmap(display, instancePtr->mask); } if (instancePtr->gc != None) { Tk_FreeGC(display, instancePtr->gc); } if (instancePtr->colors != NULL) { int i; for (i=0; i<instancePtr->masterPtr->ncolors; i++) { if (instancePtr->colors[i].colorPtr != NULL) { Tk_FreeColor(instancePtr->colors[i].colorPtr); } if (instancePtr->masterPtr->cpp != 1) { ckfree(instancePtr->colors[i].cstring); } } ckfree((char*)instancePtr->colors); } if (instancePtr->masterPtr->instancePtr == instancePtr) { instancePtr->masterPtr->instancePtr = instancePtr->nextPtr; } else { for (prevPtr = instancePtr->masterPtr->instancePtr; prevPtr->nextPtr != instancePtr; prevPtr = prevPtr->nextPtr) { /* Empty loop body */ } prevPtr->nextPtr = instancePtr->nextPtr; } ckfree((char *) instancePtr); } /* *---------------------------------------------------------------------- * * ImgXpmDelete -- * * This procedure is called by the image code to delete the * master structure for an image. * * Results: * None. * * Side effects: * Resources associated with the image get freed. * *---------------------------------------------------------------------- */ static void ImgXpmDelete(masterData) ClientData masterData; /* Pointer to PixmapMaster structure for * image. Must not have any more instances. */ { PixmapMaster *masterPtr = (PixmapMaster *) masterData; if (masterPtr->instancePtr != NULL) { panic("tried to delete pixmap image when instances still exist"); } masterPtr->tkMaster = NULL; if (masterPtr->imageCmd != NULL) { Tcl_DeleteCommand(masterPtr->interp, Tcl_GetCommandName(masterPtr->interp, masterPtr->imageCmd)); } if (masterPtr->isDataAlloced && masterPtr->data != NULL) { ckfree((char*)masterPtr->data); } Tk_FreeOptions(configSpecs, (char *) masterPtr, (Display *) NULL, 0); ckfree((char *) masterPtr); } /* *---------------------------------------------------------------------- * * ImgXpmCmdDeletedProc -- * * This procedure is invoked when the image command for an image * is deleted. It deletes the image. * * Results: * None. * * Side effects: * The image is deleted. * *---------------------------------------------------------------------- */ static void ImgXpmCmdDeletedProc(clientData) ClientData clientData; /* Pointer to PixmapMaster structure for * image. */ { PixmapMaster *masterPtr = (PixmapMaster *) clientData; masterPtr->imageCmd = NULL; if (masterPtr->tkMaster != NULL) { Tk_DeleteImage(masterPtr->interp, Tk_NameOfImage(masterPtr->tkMaster)); } } /* *---------------------------------------------------------------------- * * Tix_DefinePixmap * * Define an XPM data structure with an unique name, so that you can * later refer to this pixmap using the -id switch in [image create * pixmap]. * * Results: * None. * * Side effects: * The data is stored in a HashTable. *---------------------------------------------------------------------- */ int Tix_DefinePixmap(interp, name, data) Tcl_Interp * interp; Tk_Uid name; /* Name to use for bitmap. Must not already * be defined as a bitmap. */ char **data; { int new; Tcl_HashEntry *hshPtr; if (!xpmTableInited) { xpmTableInited = 1; Tcl_InitHashTable(&xpmTable, TCL_ONE_WORD_KEYS); } hshPtr = Tcl_CreateHashEntry(&xpmTable, name, &new); if (!new) { Tcl_AppendResult(interp, "pixmap \"", name, "\" is already defined", (char *) NULL); return TCL_ERROR; } Tcl_SetHashValue(hshPtr, (char*)data); return TCL_OK; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/libdesk/util.c���������������������������������������������������������������������������0100644�0001750�0000764�00000006214�10020457430�013461� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ============================================================================ * * File: util.c * Project: TkDesk * Started: 12.07.96 * Changed: 12.07.96 * * Provides several utility C subroutines. * * Copyright (C) 1996 Christian Bolik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * See the file "COPYING" in the base directory of this distribution * for more. * * ========================================================================= */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "libdesk.h" /* --------------------------------------------------------------------------- * escape_chars: * Precedes each char of chars in str with a backslash. Puts the result into * buf, which must be big enough. */ char *escape_chars (str, chars, buf) char *str; char *chars; char *buf; { char *bp; bp = buf; while (*str) { if (strchr (chars, *str)) { *bp++ = '\\'; } *bp++ = *str++; } *bp = '\0'; return buf; } /* escape_chars */ /* --------------------------------------------------------------------------- * unescape_chars: * Removes all backslashes from str, except before }. \t is replaced with * a real tab. Puts result at buf. */ char *unescape_chars (str, buf) char *str; char *buf; { char *bp; bp = buf; while (*str) { if (*str != '\\') { *bp++ = *str++; } else { switch (*(str + 1)) { case 't': str += 2; /* points just past t now */ *bp++ = '\t'; break; #ifdef UNDEFINED case '}': *bp++ = *str++; /* copy backslash in this case */ break; #endif default: str++; } } } *bp = '\0'; return buf; } /* unescape_chars */ /* ============================================================================ * Name: itoa * Desc: Converts an unsigned long integer to a string. * In : val - integer * Out : pointer to static string * ------------------------------------------------------------------------- */ char *itoa(val) long val; { static char str[16]; int i = 0, r; #define I2CHAR(a) \ if (val >= (a)) { \ r = val / (a); \ str[i++] = '0' + r; \ val -= r * (a); \ } else if (i > 0) \ str[i++] = '0'; I2CHAR(1000000000l); I2CHAR(100000000l); I2CHAR(10000000l); I2CHAR(1000000l); I2CHAR(100000l); I2CHAR(10000l); I2CHAR(1000l); I2CHAR(100l); I2CHAR(10l); str[i++] = '0' + (val % 10); str[i] = '\0'; return str; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/netscape-remote/�������������������������������������������������������������������������0040755�0001750�0000764�00000000000�10037657327�014035� 5����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/netscape-remote/COPYRIGHT����������������������������������������������������������������0100644�0001750�0000764�00000003142�10020457430�015306� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������COPYRIGHT,v 1.1.1.1 2004/02/29 21:52:24 jcc Exp Copyright (c) 1996 Ken Hornstein All rights reserved. Redistribution and use in source and binary forms, with or without modificatin, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Ken Hornstein. 4. The name of Ken Hornstein may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY KEN HORNSTEIN ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEN HORNSTEIN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/netscape-remote/ClientWin.c��������������������������������������������������������������0100644�0001750�0000764�00000005351�10020457430�016057� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $XConsortium: ClientWin.c,v 1.4 94/04/17 20:15:50 rws Exp $ */ /* Copyright (c) 1989 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. */ #include <X11/Xlib.h> #include <X11/Xatom.h> static Window TryChildren(); /* Find a window with WM_STATE, else return win itself, as per ICCCM */ Window XmuClientWindow (dpy, win) Display *dpy; Window win; { Atom WM_STATE; Atom type = None; int format; unsigned long nitems, after; unsigned char *data; Window inf; WM_STATE = XInternAtom(dpy, "WM_STATE", True); if (!WM_STATE) return win; XGetWindowProperty(dpy, win, WM_STATE, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data); if (type) return win; inf = TryChildren(dpy, win, WM_STATE); if (!inf) inf = win; return inf; } static Window TryChildren (dpy, win, WM_STATE) Display *dpy; Window win; Atom WM_STATE; { Window root, parent; Window *children; unsigned int nchildren; unsigned int i; Atom type = None; int format; unsigned long nitems, after; unsigned char *data; Window inf = 0; if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) return 0; for (i = 0; !inf && (i < nchildren); i++) { XGetWindowProperty(dpy, children[i], WM_STATE, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data); if (type) inf = children[i]; } for (i = 0; !inf && (i < nchildren); i++) inf = TryChildren(dpy, children[i], WM_STATE); if (children) XFree((char *)children); return inf; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/netscape-remote/Makefile.in��������������������������������������������������������������0100644�0001750�0000764�00000002542�10020457430�016063� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This is the Makefile for the netscape-remote needed by TkDesk. # It is called from the main Makefile in .., which passes the CC_OPTS. #---------------------------------------------------------------- # Things you can change to personalize the Makefile for your own # site (you can make these changes in either Makefile.in or # Makefile, but changes to Makefile will get lost if you re-run # the configuration script). #---------------------------------------------------------------- # Location of Tcl header files: TCL_INCLUDE_DIR = @TCL_INCLUDE_PATH@ # Location of Tk header files: TK_INCLUDE_DIR = @TK_INCLUDE_PATH@ # Location of X11 header files: X_INCLUDE_FLAG = @TK_XINCLUDES@ # Configuration compiler flags: AC_FLAGS = @DEFS@ # Miscellaneous settings: RANLIB = @RANLIB@ CC = @CC@ AR = ar rc RM = rm -f #---------------------------------------------------------------- # The information below should be usable as is. The configure # script won't modify it and you shouldn't need to modify it # either. #---------------------------------------------------------------- CFLAGS = ${CC_OPTS} ${AC_FLAGS} -I.. -I${TCL_INCLUDE_DIR} -I${TK_INCLUDE_DIR} ${X_INCLUDE_FLAG} libname = libnetscape.a OBJS = netscape_remote.o ClientWin.o lib: $(libname) $(libname): $(OBJS) $(RM) $@ $(AR) $@ $(OBJS) $(RANLIB) $@ clean: $(RM) $(OBJS) $(libname) *\~ "#"* ��������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/netscape-remote/README�������������������������������������������������������������������0100644�0001750�0000764�00000016106�10020457430�014677� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������README,v 1.1.1.1 2004/02/29 21:52:24 jcc Exp Netscape-Remote - A Tcl/Tk extension to communicate with Netscape Version 1.2 By Ken Hornstein <kenh@cmf.nrl.navy.mil> CHANGES FROM PREVIOUS VERSION ============================= - netscape-remote has been renamed to netscape_remote, so as to better work with the "load" command. - Support for being loaded as a "package". INTRODUCTION ============ On my Unix desktop, I almost always have a Netscape running. But I quickly discovered that if I came across a URL somewhere on my desktop, it was a pain in the ass to go to my Netscape window, open the "Go To URL" dialog box, paste in the string, delete the old string, etc etc. The good people at Netscape, however, already thought up a solution to this problem. Unix Netscape supports the -remote option on the command line. This uses X Windows properties to send a command to a currently-running Netscape, which means you can easily send it a command from another program. So, this sounds great. However, I quickly discovered this also has it's problems: - It requires you loading in most of the Netscape executable. Many times I don't run Netscape on my desktop machine (because it will really swap like hell if Netscape is loaded), but instead on another machine. But if I want to control it from a process on my desktop, I have to load most of Netscape, and since I'm not running it on my desktop, it's not in my buffer cache, and that takes forever. Since the size of Netscape increases with every release, this problem is only going to get worse. - For every command, it requires one to do an XQueryTree call and iterate over all windows on your display and checking to see which one is Netscape. On a Unix domain socket this isn't so bad, but over the net this takes a noticable amount of time. The net result is that using netscape -remote takes a lot more resources and time than I'd like. Since I use a lot of Tk applications (and in fact I use a small Tcl/Tk app to yank URL's out of the cut buffer and send them to Netscape), I decided it would be a lot more efficient if Tk could just talk to Netscape directly. Thus, Netscape-Remote was born. Netscape-Remote is a dyanmically loadable extension to for Tcl 7.5/Tk 4.1 and Tcl 7.6/Tk 4.2 that uses the same protocol that is used by the -remote flag. This protocol is documented by the following URL: http://home.netscape.com/newsref/std/x-remote-proto.html Essentially, it uses X Windows properties to communicate with the remote version of Netscape. This has the following advantages over using netscape -remote: - It's less resource-intensive. Since it's a Tcl extension, all you need is the Tcl/Tk runtime loaded instead of all of Netscape. And if you're controlling Netscape from a Tcl/Tk application, there is practically no overhead at all. - It's faster. Since Netscape-Remote caches the window Id from the XQueryTree call, it doesn't need to look at every window on your display to see if it's a Netscape window. INSTALLATION ============ To install this extension, you must have Tcl 7.5/Tk 4.1 or higher (it has been tested with Tcl 7.6/Tk 4.2) with dyanmically loadable extension support. I imagine it would be possible to compile this extension into wish statically, but I have not done it. (You can probably just invoke Netscape_remote_Init() from tkAppInit.c). Edit the supplied Makefile and fill in the defaults for your system. Included are sample configurations for SunOS 4.x, SunOS 5.x, NetBSD, and IRIX 5.x systems. If you don't have one of these systems, you'll have to figure out what to use for PICFLAG, SHAREDLIBEXTENSION, and SHAREDLIBFLAGS. You can probably gain some hints for this from your "tclConfig.sh" script, which will be located somewhere in your lib directory. Once that's done, "make" should compile netscape_remote.so (or whatever is your shared library extension). You should then probably install this shared library whereever you place loadable extensions. A pkgIndex.tcl script has been provided, but you can generate your own if you prefer by using pkg_mkIndex. Once this has been installed properly, you should be able to load the extension by doing: % package require netscape_remote Alternatively, you can load netscape_remote directly with "load", as exampled below. % load ./netscape_remote.so USE === This extension creates a new command in your interpreter called "send-netscape". This command accepts only one argument: the command string to send to netscape. For example: % send-netscape openURL(http://www.nrl.navy.mil/CCS/people/kenh/carp/) Will tell Netscape to go to the above URL. The function names come from the Xt action arguments that can be found in the Netscape.ad file. For a partial list of possible actions and arguments to them, see the following URL: http://home.netscape.com/newsref/std/x-remote.html If you wish to emulate the "-noraise" flag that the command-line netscape has, add it as an extra option to the Xt action command. For example: % send-netscape {openURL(http://www.nrl.navy.mil/CCS/people/kenh/carp/, noraise)} % send-netscape openURL(noraise) The return value from send-netscape is the response string from Netscape. This is a 3-digit response code and an optional text string. See the above documentation for more info on this code (a quick summary - if the code begins with "2", it's a succesfull command. If it begins with "5", it's a failed command). send-netscape supports some additional options. They are: -id windowId - Send the command to the window specified by "windowId", rather than searching through all the windows on your display. "windowId" is a hexadecimal string indicating the X identifier for the window. -idvar var - Store the X identifier for the window found in the variable "var", suitable for passing to the "-id" flag. -timeout ms - Set the protocol timeout for "ms" milliseconds. The default is 10000 (10 seconds). Note that a timeout of 0 is a really short timeout, not an infinite timout! New in 1.1 is the "info-netscape" command. Valid arguments to info-netscape are: list - Return a list of X identifiers for all Netscape windows on your display. version windowID - Return the version of Netscape that is running on window "windowID". url windowID - Returns the current URL for the Netscape that is running on window "windowID". BUGS ==== Send-netscape doesn't check the Netscape version number for validity, even now that there is a "info-netscape version" command. I'm not sure what I would do with a version number if I did check it. Errors during the protocol unlock phase are not checked. There is no way to specify a unlimited timeout. MISC ==== The "ClientWin.c" file distributed with this package is from the X Consortium X11R6 distribution. This implements XmuClientWindow, which is used by Netscape-Remote. This source code is Copyright 1989 by the X Consortium. Please see ClientWin.c for the complete copyright notice. Netscape-Remote is Copyright (c) 1996 by Ken Hornstein. Please see the COPYRIGHT file included with this distribution for a complete copyright notice. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/netscape-remote/netscape_remote.c��������������������������������������������������������0100644�0001750�0000764�00000062724�10020457430�017347� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * netscape_remote.c,v 1.1.1.1 2004/02/29 21:52:24 jcc Exp * * netscape_remote - a Tcl/Tk extension to talk the remote command protocol * that Netscape uses * * This extension speaks the remote protocol that is used by the Netscape * web browser. This lets us control netscape remotely without having to * start up a new netscape process (despite what the people at Netscape * say, starting up a whole new copy of Netscape takes too long for my * tastes). * * We also cache the window id used for Netscape so we don't have to call * XQueryTree for every command. * * Documentation on the protocol netscape uses can be found at the following * URL: http://home.netscape.com/newsref/std/x-remote-proto.html * * By Ken Hornstein <kenh@cmf.nrl.navy.mil> * */ /* 29.07.96 - Added #ifdef's to let it work with Tk 4.0. CB */ /* 05.08.96 - Removed newline inside a string. CB */ #ifndef LINT static char rcsid[]= "netscape_remote.c,v 1.1.1.1 2004/02/29 21:52:24 jcc Exp"; #endif #include "config.h" #include <sys/types.h> #include <unistd.h> #ifdef HAVE_TCL8_0_H #include <tcl8.0.h> #include <tk8.0.h> #else #include <tk.h> #endif #include <X11/Xatom.h> #include <X11/Xproto.h> /* * Just include a prototype for XmuClientWindow here, since we bring * along a copy of ClientWin.c with this distribution */ extern Window XmuClientWindow _ANSI_ARGS_((Display *, Window)); /* * Names of some of the Netscape internal properties, and variables to * store them in. */ #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION" #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK" #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND" #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE" #define MOZILLA_URL_PROP "_MOZILLA_URL" static Atom XA_MOZILLA_VERSION = 0; static Atom XA_MOZILLA_LOCK = 0; static Atom XA_MOZILLA_COMMAND = 0; static Atom XA_MOZILLA_RESPONSE = 0; static Atom XA_MOZILLA_URL = 0; /* * This is a structure that contains all the info about pending things * happening on Netscape windows. We use this to communicate between * NetscapeEventProc and what's happening now */ typedef struct PendingCommand { int state; /* Type of sub-event we're waiting for */ Window win; /* Window we're waiting for */ Atom atom; /* Atom for PropertyChange/Delete */ int response; /* Did we get a response? */ } PendingCmd; #define PENDING_OK 1 #define PENDING_TIMEOUT 2 #define PENDING_DESTROY 3 /* * Prototypes for internal functions */ static int Netscape_Remote_Cmd _ANSI_ARGS_((ClientData, Tcl_Interp *, int, char *[])); static Window GetWindow _ANSI_ARGS_((Tcl_Interp *, Tk_Window)); static int ListWindows _ANSI_ARGS_((Tcl_Interp *, Display *)); static int CheckForNetscape _ANSI_ARGS_((Display *, Window)); static int SendCommand _ANSI_ARGS_((Tcl_Interp *, Tk_Window, Window, char *, PendingCmd *, int)); static int NetscapeEventHandler _ANSI_ARGS_((ClientData, XEvent *)); static int GetLock _ANSI_ARGS_((Tcl_Interp *, Display *, Window, PendingCmd *, int)); #if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) \ || (TK_MAJOR_VERSION >= 8) static Tk_RestrictAction NetscapeRestrict _ANSI_ARGS_((ClientData, XEvent *)); #else static Tk_RestrictProc NetscapeRestrict; #endif static void LockTimeout _ANSI_ARGS_((ClientData)); static void ReleaseLock _ANSI_ARGS_((Display *, Window)); static int Netscape_Info_Cmd _ANSI_ARGS_((ClientData, Tcl_Interp *, int, char*[])); static Window CachedWindow = None; #define DEFAULT_TIMEOUT 10000 /* * Our package init routine. Set things up for our new interpreter commands. */ int Netscape_remote_Init(interp) Tcl_Interp *interp; { Tk_Window main; if ((main = Tk_MainWindow(interp)) == NULL) { Tcl_AppendResult(interp, "No main window associated with ", "this interpreter!", (char *) NULL); return TCL_ERROR; } /* * Get the Atoms corresponding to these property names; we use * them later. */ if (! XA_MOZILLA_VERSION) XA_MOZILLA_VERSION = Tk_InternAtom(main, MOZILLA_VERSION_PROP); if (! XA_MOZILLA_LOCK) XA_MOZILLA_LOCK = Tk_InternAtom(main, MOZILLA_LOCK_PROP); if (! XA_MOZILLA_COMMAND) XA_MOZILLA_COMMAND = Tk_InternAtom(main, MOZILLA_COMMAND_PROP); if (! XA_MOZILLA_RESPONSE) XA_MOZILLA_RESPONSE = Tk_InternAtom(main, MOZILLA_RESPONSE_PROP); if (! XA_MOZILLA_URL) XA_MOZILLA_URL = Tk_InternAtom(main, MOZILLA_URL_PROP); /* * Create our "send-netscape" and "info-netscape" interpreter * commands */ Tcl_CreateCommand(interp, "send-netscape", Netscape_Remote_Cmd, (ClientData) main, (void (*)()) NULL); Tcl_CreateCommand(interp, "info-netscape", Netscape_Info_Cmd, (ClientData) main, (void (*)()) NULL); /* * Tell the Tcl package interface that we exist */ if (Tcl_PkgProvide(interp, "netscape_remote", "1.2") != TCL_OK) return TCL_ERROR; return TCL_OK; } /* * This is the Tcl glue routine to the routines that do the real work * (in this case, SendCommand) */ static int Netscape_Remote_Cmd(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { Tk_Window main = (Tk_Window) clientData; Tk_ErrorHandler error; Window w = None; PendingCmd pending; int timeout = DEFAULT_TIMEOUT; char *idVar = NULL; char *arg = NULL; int i; if (argc < 2 || argc > 8) { goto usage; } /* * Parse our command-line arguments */ for (i = 1; i < argc; i++) { arg = argv[i]; if (arg[0] == '-') { /* * Process the -id (specify the window id) option */ if (strcmp(arg, "-id") == 0) { i++; if (i >= argc) { Tcl_AppendResult(interp, "\"-id\" must", " be followed by a window id", (char *) NULL); return TCL_ERROR; } if (Tcl_GetInt(interp, argv[i], (int *) &w) != TCL_OK) { return TCL_ERROR; } } /* * Process -idvar (variable for window id) option */ else if (strcmp(argv[i], "-idvar") == 0) { i++; if (i >= argc) { Tcl_AppendResult(interp, "\"-idvar\" " "must be followed ", "by a variable name", (char *) NULL); return TCL_ERROR; } idVar = argv[i]; } /* * Process the -timeout (for various protocol timeouts) * option */ else if (strcmp(argv[i], "-timeout") == 0) { i++; if (i >= argc) { Tcl_AppendResult(interp, "\"-timeout\" must ", "be followed by an ", "integer", (char *) NULL); return TCL_ERROR; } if (Tcl_GetInt(interp, argv[i], &timeout) != TCL_OK) { return TCL_ERROR; } if (timeout <= 0) { Tcl_AppendResult(interp, "\"timeout\" " "must be a positive " "interger.", (char *) NULL); return TCL_ERROR; } } else { break; } } else { break; } } if (i != argc - 1) { usage: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?-id id? ?-idvar idvar? ?-timeout timeout", " netscapeCommand\"", (char *) NULL); return TCL_ERROR; } /* * Figure out which window to use. Check to see if we have * a cached window - if so, use that one, rather than iterating * through all of the windows on our display. * * We need to setup an error handler here, otherwise we will * exit if the window doesn't exist. */ error = Tk_CreateErrorHandler(Tk_Display(main), BadWindow, X_GetProperty, -1, NULL, NULL); if (w != None) { if (! CheckForNetscape(Tk_Display(main), w)) { Tcl_AppendResult(interp, "Invalid window Id, or " "window is not a Netscape window", (char *) NULL); Tk_DeleteErrorHandler(error); return TCL_ERROR; } } if (CachedWindow != None && w == None) { if (CheckForNetscape(Tk_Display(main), CachedWindow)) { w = CachedWindow; } else { CachedWindow = None; } } if (w == None) { if ((w = GetWindow(interp, main)) == None) { Tk_DeleteErrorHandler(error); return TCL_ERROR; } CachedWindow = w; } Tk_DeleteErrorHandler(error); if (idVar) { char value[256]; sprintf(value, "0x%08x", (int) w); if (Tcl_SetVar(interp, idVar, value, TCL_LEAVE_ERR_MSG) == NULL) { return TCL_ERROR; } } return SendCommand(interp, main, w, argv[i], &pending, timeout); } /* * This is the Tcl glue code for the "info-netscape" command */ static int Netscape_Info_Cmd(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; { Tk_Window main = (Tk_Window) clientData; Window w; Tk_ErrorHandler error; Atom type; int format, status; unsigned long nitems, bytesafter; unsigned char *data; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args, should be \"", argv[0], " option ?arg arg ...?\"", (char *) NULL); return TCL_ERROR; } /* * Check which option we were given */ if (strcmp(argv[1], "list") == 0) { return ListWindows(interp, Tk_Display(main)); } else if (strcmp(argv[1], "version") == 0 || strcmp(argv[1], "url") == 0) { /* * Handle the "version" or the "url" command. This code * is nearly identical, except that a different property * is fetched at the last part. */ if (argc != 3) { Tcl_AppendResult(interp, "Wrong # args, must be: \"", argv[0], " ", argv[1], " windowId\"", (char *) NULL); return TCL_ERROR; } if (Tcl_GetInt(interp, argv[2], (int *) &w) != TCL_OK) { return TCL_ERROR; } error = Tk_CreateErrorHandler(Tk_Display(main), BadWindow, X_GetProperty, -1, NULL, NULL); if (! CheckForNetscape(Tk_Display(main), w)) { Tcl_AppendResult(interp, "Window is either nonexistant" " or is not a valid Netscape window", (char *) NULL); return TCL_ERROR; } status = XGetWindowProperty(Tk_Display(main), w, argv[1][0] == 'v' ? XA_MOZILLA_VERSION : XA_MOZILLA_URL, 0, 65536 / sizeof(long), False, XA_STRING, &type, &format, &nitems, &bytesafter, &data); Tk_DeleteErrorHandler(error); if (status != Success) { Tcl_AppendResult(interp, "Error while reading " " Netscape ", argv[1], (char *) NULL); if (data) XFree(data); return TCL_ERROR; } Tcl_SetResult(interp, data, TCL_VOLATILE); XFree(data); return TCL_OK; } else { Tcl_AppendResult(interp, "Invalid option: \"", argv[1], "\"; must be one of: list version url", (char *) NULL); return TCL_ERROR; } return TCL_OK; } /* * Find the window to use on the remote display. Most of this code is * taken from Netscape reference implementation. We don't do any version * checking right now. */ static Window GetWindow(interp, main) Tcl_Interp *interp; Tk_Window main; { int i; Window root = RootWindowOfScreen(Tk_Screen(main)); Window root2, parent, *kids; unsigned int nkids; Window result = None; /* (CB) Sync display to overcome timing problems: */ XSync(Tk_Display(main), True); if (! XQueryTree(Tk_Display(main), root, &root2, &parent, &kids, &nkids)) { Tcl_AppendResult(interp, "XQueryTree failed", (char *) NULL); return None; } if (root != root2) { Tcl_AppendResult(interp, "Root windows didn't match!", (char *) NULL); return None; } if (parent != None) { Tcl_AppendResult(interp, "We got a valid parent window, but", " we shouldn't have!", (char *) NULL); return None; } if (! (kids && nkids)) { Tcl_AppendResult(interp, "No children found!", (char *) NULL); return None; } for (i = 0; i < nkids; i++) { Window w = XmuClientWindow(Tk_Display(main), kids[i]); if (CheckForNetscape(Tk_Display(main), w)) { result = w; break; } } if (result == None) { Tcl_AppendResult(interp, "Couldn't find a netscape window", (char *) NULL); } return result; } /* * Return all Netscape windows on a given display. */ static int ListWindows(interp, d) Tcl_Interp *interp; Display *d; { Window root = RootWindowOfScreen(DefaultScreenOfDisplay(d)); Window root2, parent, *kids; unsigned int nkids; int i; char value[256]; Tcl_DString dstr; Tcl_DStringInit(&dstr); /* * Much of the work is the same as in GetWindow, but we're going * to return all valid Netscape windows */ if (! XQueryTree(d, root, &root2, &parent, &kids, &nkids)) { Tcl_AppendResult(interp, "XQueryTree failed", (char *) NULL); return TCL_ERROR; } if (root != root2) { Tcl_AppendResult(interp, "Root windows didn't match!", (char *) NULL); return TCL_ERROR; } if (parent != None) { Tcl_AppendResult(interp, "We got a valid parent window, but", " we shouldn't have!", (char *) NULL); return TCL_ERROR; } if (! (kids && nkids)) { Tcl_AppendResult(interp, "No children found!", (char *) NULL); return TCL_ERROR; } for (i = 0; i < nkids; i++) { Window w = XmuClientWindow(d, kids[i]); if (CheckForNetscape(d, w)) { sprintf(value, "0x%08x", (int) w); Tcl_DStringAppendElement(&dstr, value); } } Tcl_DStringResult(interp, &dstr); return TCL_OK; } /* * See if the given window is a Netscape window by looking for the * XA_MOZILLA_VERSION property */ static int CheckForNetscape(d, w) Display *d; Window w; { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *version = NULL; int status = XGetWindowProperty(d, w, XA_MOZILLA_VERSION, 0, 65536 / sizeof(long), False, XA_STRING, &type, &format, &nitems, &bytesafter, &version); if (status != Success || !version) { if (version) XFree(version); return 0; } /* * We don't do anything with the version right now */ XFree(version); return 1; } /* * Send a command to the Netscape window we found previously */ static int SendCommand(interp, mainwin, win, command, pending, timeout) Tcl_Interp *interp; Tk_Window mainwin; Window win; char *command; PendingCmd *pending; int timeout; { Tk_RestrictProc *prevRestrict; #if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) \ || (TK_MAJOR_VERSION >= 8) ClientData prevArgs; #if (TK_MAJOR_VERSION >= 8) Tcl_TimerToken token; #endif #else char *prevArgs; Tk_TimerToken token; #endif int result; Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *data; Tk_ErrorHandler error; /* * Select for PropertyChange events on the Netscape window */ XSelectInput(Tk_Display(mainwin), win, (PropertyChangeMask | StructureNotifyMask)); /* * Create a generic event handler to get events on that window */ pending->state = 0; pending->win = None; pending->response = 0; Tk_CreateGenericHandler(NetscapeEventHandler, (ClientData) pending); if (GetLock(interp, Tk_Display(mainwin), win, pending, timeout) == 0) { Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); XSelectInput(Tk_Display(mainwin), win, 0); return TCL_ERROR; } /* * We've got a successful lock, so send the command to Netscape */ XChangeProperty(Tk_Display(mainwin), win, XA_MOZILLA_COMMAND, XA_STRING, 8, PropModeReplace, (unsigned char *) command, strlen(command)); /* * Netscape should delete the property containing the command * Wait for this to happen. */ #if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) \ || (TK_MAJOR_VERSION >= 8) prevRestrict = Tk_RestrictEvents(NetscapeRestrict, (ClientData) pending, &prevArgs); #else prevRestrict = Tk_RestrictEvents(NetscapeRestrict, (char *) pending, &prevArgs); #endif pending->win = win; pending->state = PropertyDelete; pending->atom = XA_MOZILLA_COMMAND; pending->response = 0; #if (TK_MAJOR_VERSION >= 8) token = Tcl_CreateTimerHandler(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tcl_DoOneEvent(TCL_ALL_EVENTS); } Tcl_DeleteTimerHandler(token); #else #if (TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0) Tcl_CreateModalTimeout(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tcl_DoOneEvent(TCL_WINDOW_EVENTS); } Tcl_DeleteModalTimeout(LockTimeout, (ClientData) pending); #else token = Tk_CreateTimerHandler(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tk_DoOneEvent(TK_X_EVENTS); } Tk_DeleteTimerHandler(token); #endif #endif Tk_RestrictEvents(prevRestrict, prevArgs, &prevArgs); if (pending->response == PENDING_TIMEOUT) { Tcl_AppendResult(interp, "Timeout waiting for Netscape to ", "acknowledge command", (char *) NULL); ReleaseLock(Tk_Display(mainwin), win); Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); XSelectInput(Tk_Display(mainwin), win, 0); return TCL_ERROR; } else if (pending->response == PENDING_DESTROY) { Tcl_AppendResult(interp, "Window was destroyed while waiting ", "for acknowledgement", (char *) NULL); Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); return TCL_ERROR; } /* * Wait for a response. Netscape will write it's response code * in the XA_MOZILLA_RESPONSE property -- check that for the * response code */ #if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) \ || (TK_MAJOR_VERSION >= 8) prevRestrict = Tk_RestrictEvents(NetscapeRestrict, (ClientData) pending, &prevArgs); #else prevRestrict = Tk_RestrictEvents(NetscapeRestrict, (char *) pending, &prevArgs); #endif pending->win = win; pending->state = PropertyNewValue; pending->atom = XA_MOZILLA_RESPONSE; pending->response = 0; #if (TK_MAJOR_VERSION >= 8) token = Tcl_CreateTimerHandler(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tcl_DoOneEvent(TCL_ALL_EVENTS); } Tcl_DeleteTimerHandler(token); #else #if (TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0) Tcl_CreateModalTimeout(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tcl_DoOneEvent(TCL_WINDOW_EVENTS); } Tcl_DeleteModalTimeout(LockTimeout, (ClientData) pending); #else token = Tk_CreateTimerHandler(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tk_DoOneEvent(TK_X_EVENTS); } Tk_DeleteTimerHandler(token); #endif #endif Tk_RestrictEvents(prevRestrict, prevArgs, &prevArgs); if (pending->response == PENDING_TIMEOUT) { Tcl_AppendResult(interp, "Timeout waiting for a response from", " Netscape", (char *) NULL); ReleaseLock(Tk_Display(mainwin), win); Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); XSelectInput(Tk_Display(mainwin), win, 0); return TCL_ERROR; } else if (pending->response == PENDING_DESTROY) { Tcl_AppendResult(interp, "Window was destroyed while waiting ", "for a response", (char *) NULL); Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); return TCL_ERROR; } /* * Get the response string from Netscape */ result = XGetWindowProperty(Tk_Display(mainwin), win, XA_MOZILLA_RESPONSE, 0, 65536 / sizeof(long), True /* delete */, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (result != Success) { Tcl_AppendResult(interp, "Failed to read response from " "Netscape", (char *) NULL); ReleaseLock(Tk_Display(mainwin), win); Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); XSelectInput(Tk_Display(mainwin), win, 0); return TCL_ERROR; } if (! data) { Tcl_AppendResult(interp, "No data returned from Netscape", (char *) NULL); ReleaseLock(Tk_Display(mainwin), win); Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); XSelectInput(Tk_Display(mainwin), win, 0); return TCL_ERROR; } Tcl_AppendResult(interp, data, (char *) NULL); XFree(data); /* * Remove the lock on Netscape. Note that first we install an * error handler for BadWindow errors. We do this because if we * send Netscape a command such as delete() or exit(), Netscape * will destroy that window before we can clean up completely. * The error handler prevents our Tk application from exiting. */ error = Tk_CreateErrorHandler(Tk_Display(mainwin), BadWindow, X_ChangeWindowAttributes, -1, NULL, NULL); ReleaseLock(Tk_Display(mainwin), win); /* * Delete the generic event handler (otherwise we would be getting * _all_ X events, which would be wasteful) */ Tk_DeleteGenericHandler(NetscapeEventHandler, (ClientData) pending); /* * Don't select these events anymore. */ XSelectInput(Tk_Display(mainwin), win, 0); Tk_DeleteErrorHandler(error); return TCL_OK; } static int NetscapeEventHandler(clientData, event) ClientData clientData; XEvent *event; { PendingCmd *pending = (PendingCmd *) clientData; if (pending->win == None) return 0; if (event->type == PropertyNotify && event->xproperty.window == pending->win && event->xproperty.state == pending->state && event->xproperty.atom == pending->atom) { pending->response = PENDING_OK; } else if (event->type == DestroyNotify && event->xdestroywindow.window == pending->win) { pending->response = PENDING_DESTROY; } return 0; } /* * Participate in the Netscape locking protocol so our commands don't * collide */ static int GetLock(interp, d, win, pending, timeout) Tcl_Interp *interp; Display *d; Window win; PendingCmd *pending; int timeout; { char lock_data[255]; Bool locked = False; Tk_RestrictProc *prevRestrict; #if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) \ || (TK_MAJOR_VERSION >= 8) ClientData prevArgs; #if (TK_MAJOR_VERSION >= 8) Tcl_TimerToken token; #endif #else char *prevArgs; Tk_TimerToken token; #endif sprintf(lock_data, "TkApp-pid%d@", getpid()); if (gethostname(lock_data + strlen(lock_data), 100) == -1) { Tcl_AppendResult(interp, "gethostname() returned an error", (char *) NULL); return 0; } do { int result; Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *data = NULL; /* * Grab the server so nobody else can do anything */ XGrabServer(d); /* * See if it's locked */ result = XGetWindowProperty(d, win, XA_MOZILLA_LOCK, 0, (65536 / sizeof(long)), False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (result != Success || actual_type == None) { /* * It's not locked now, lock it! */ XChangeProperty(d, win, XA_MOZILLA_LOCK, XA_STRING, 8, PropModeReplace, (unsigned char *) lock_data, strlen(lock_data)); locked = True; } /* * Release the server grab */ XUngrabServer(d); XSync(d, False); if (! locked) { /* * There was already a lock in place. Wait for * a PropertyDelete event. Use a RestrictProc * to make sure we're synchronous */ #if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) \ || (TK_MAJOR_VERSION >= 8) prevRestrict = Tk_RestrictEvents(NetscapeRestrict, (ClientData) pending, &prevArgs); #else prevRestrict = Tk_RestrictEvents(NetscapeRestrict, (char *) pending, &prevArgs); #endif pending->win = win; pending->state = PropertyDelete; pending->atom = XA_MOZILLA_LOCK; pending->response = 0; #if (TK_MAJOR_VERSION >= 8) token = Tcl_CreateTimerHandler(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tcl_DoOneEvent(TCL_ALL_EVENTS); } Tcl_DeleteTimerHandler(token); #else #if (TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0) Tcl_CreateModalTimeout(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tcl_DoOneEvent(TCL_WINDOW_EVENTS); } Tcl_DeleteModalTimeout(LockTimeout, (ClientData) pending); #else token = Tk_CreateTimerHandler(timeout, LockTimeout, (ClientData) pending); while (!pending->response) { Tk_DoOneEvent(TK_X_EVENTS); } Tk_DeleteTimerHandler(token); #endif #endif Tk_RestrictEvents(prevRestrict, prevArgs, &prevArgs); if (pending->response == PENDING_TIMEOUT) { Tcl_AppendResult(interp, "Timeout waiting for " "locked to be released", (char *) NULL); if (data) { Tcl_AppendResult(interp, " by ", data, (char *) NULL); XFree(data); } break; } else if (pending->response == PENDING_DESTROY) { Tcl_AppendResult(interp, "Window was destoyed " "while trying to get lock", (char *) NULL); if (data) XFree(data); break; } } if (data) XFree(data); } while (! locked); return locked == True ? 1 : 0; } /* * Unlock our lock with Netscape. We should check for errors, but this * routine doesn't */ static void ReleaseLock(d, win) Display *d; Window win; { Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *data; XGetWindowProperty(d, win, XA_MOZILLA_LOCK, 0, 65536 / sizeof(long), True /* delete */, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (data) XFree(data); } #if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION > 0)) \ || (TK_MAJOR_VERSION >= 8) static Tk_RestrictAction NetscapeRestrict(clientData, event) ClientData clientData; XEvent *event; { PendingCmd *pending = (PendingCmd *) clientData; if ((event->type != PropertyNotify || event->xproperty.window != pending->win || event->xproperty.atom != pending->atom) && (event->type != DestroyNotify || event->xdestroywindow.window != pending->win)) { return TK_DEFER_EVENT; } return TK_PROCESS_EVENT; } #else static Bool NetscapeRestrict(display, event, arg) Display *display; XEvent *event; char *arg; { PendingCmd *pending = (PendingCmd *) arg; if ((event->type != PropertyNotify || event->xproperty.window != pending->win || event->xproperty.atom != pending->atom) && (event->type != DestroyNotify || event->xdestroywindow.window != pending->win)) { return False; } return True; } #endif static void LockTimeout(clientData) ClientData clientData; { PendingCmd *pending = (PendingCmd *) clientData; pending->response = PENDING_TIMEOUT; } ��������������������������������������������tkdesk-2.0/tcldesk/���������������������������������������������������������������������������������0040755�0001750�0000764�00000000000�10037657327�012373� 5����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/tcldesk/doc/�����������������������������������������������������������������������������0040755�0001750�0000764�00000000000�10037657327�013140� 5����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/tcldesk/doc/Guide������������������������������������������������������������������������0100644�0001750�0000764�00000232561�10037101554�014110� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� TkDesk User's Guide Originally written by Christian Bolik, now maintained by J. Chris Coppick, jchris@users.sourceforge.net Version 2.0, 15 April 2004 TkDesk is a graphical, highly configurable and powerful desktop man- ager for UNIX and the X Window System. This document is meant to be a comprehensive guide to the functions, services and configuration pos- sibilities offered by TkDesk. Please also take a look at the CHANGES file for latest news. A list of answers to frequently asked questions is also included. The TkDesk homepage is at <http://tkdesk.sourceforge.net>. ______________________________________________________________________ Table of Contents 1. Introduction 1.1 Acknowledgments 1.2 Using TkDesk's Help System 1.3 Command Line Options 2. The File Browser Window 2.1 The Menu Bar 2.1.1 TkDesk Menu 2.1.2 File Menu 2.1.3 Directory Menu 2.1.4 Commands Menu 2.1.5 Bookmarks Menu 2.1.6 Options Menu 2.1.7 Help Menu 2.2 The Button Bar 2.3 The Path Entry 2.4 The File Listboxes 2.4.1 Handling of the Listboxes (Bindings) 2.5 The Status Bar 3. The File List Window 4. File Operations 4.1 File Information 4.2 Copying, Moving and Deleting Files 4.3 Finding Files 5. Cascading Directory Popup Menus 6. The Application Bar 7. The Built-in Editor 7.1 Marks 8. Using the TkDesk Server 9. Configuration of TkDesk 9.1 Configuration Files 9.1.1 AppBar 9.1.2 ButtonBar 9.1.3 Commands 9.1.4 Directories 9.1.5 FileTags 9.1.6 Local 9.1.7 Popups 9.1.8 Sounds 9.1.9 System 9.2 The TkDesk API 9.3 Shortcuts 9.4 Utilities 10. Frequently Asked Questions 10.1 How can I change the position of the application bar? 10.2 Can I have transparent icons? 10.3 How can I change the background colour of the icons and desk items? 10.4 How can I have a different set of desk items on each virtual screen? 10.4.1 FVWM and similar or derived window managers 10.4.2 CDE window manager 10.5 How do I configure FVWM mini-icons for TkDesk? 10.6 Configuring the appbar or popup menus to execute (export foo=bar; program;) doesn't work. 10.7 Composing characters doesn't work in TkDesk's editor. 10.8 TkDesk's layout seems to be screwed. I can't get the appbar displayed anymore. 10.9 On my RedHat 5.x system the appbar clock shows the wrong time. 10.10 TkDesk complains about "invalid command name wm" and won't start up 10.11 I cannot launch other Tcl/Tk applications from within TkDesk 10.12 I'd like TkDesk to do this and that. How can I achieve this? 10.13 Is there a TkDesk mailing list? 10.14 Where can I find out more about Tcl/Tk? 11. Tips and Tricks 11.1 TkDesk and XEmacs ______________________________________________________________________ 1. Introduction TkDesk is a graphical desktop manager for UNIX (with a slight emphasis on Linux, but it also runs just as well on AIX, Solaris, HP-UX, SGI Irix and other UNIX flavors) and the X Window System. It offers a very complete set of file operations and services, plus gives the user the ability to configure most aspects of TkDesk in a very powerful way. The reason for this is the use of the Tcl scripting language as the configuration and (for the greatest part of TkDesk) implementation language. This makes TkDesk not just configurable but truly programmable. TkDesk has been influenced by various other systems and file managers: NeXT, for laying out the file browser windows, Apple Finder, for the idea of file annotations and, (shock horror), Windows 95, for some other (of course minor and unimportant) inspirations. This is a brief overview of the most prominent features of TkDesk: o Arbitrary number of automatically refreshed file browsers and file list windows, o Configurable file-specific popup-menus, o Drag and drop, o Files and directories may also be dropped onto the root window, a.k.a. desktop, o Configurable application bar, with several displays and cascaded popup menus for each button, files can also be dropped here, o History of visited directories, opened files, executed commands, and others, which is automatically saved to disk, o Find files through their annotation, name, contents, size, age, or other criteria, o A trash can for safe deletion of files and directories, o Calculation of disk usage for directory hierarchies, o All file operations (find, copy, disk usage, etc.) are carried out in the background, o Traversal of directory hierarchies through recursive cascaded menus, o Bookmarks, create menu entries for often used files/directories, o Comprehensive hypertextish online help, o Built-in multi-buffer and undo-capable editor, o Remote control of XEmacs, o Close coupling with Netscape Navigator for displaying HTML files or selected URLs, o Sound support, o Powerful on-the-fly configuration of nearly all aspect of TkDesk using Tcl/Tk, this also allows the Tcl-literate to extend TkDesk in arbitrary ways, o Free of charge! But see the file COPYING, or menu entry Help/License for information on usage and redistribution of TkDesk. 1.1. Acknowledgments Christian Bolik writes: TkDesk uses a number of other freely available packages without which TkDesk would not have been possible. I'd like to say many thanks to the following people: o Chris Sterritt for setting up and managing the TkDesk mailing list (at the old location majordomo@mrj.com o Alan V. Shackelford for seamlessly taking over the list to his machine and responsibility (the list is now at majordomo@shaknet.clark.net), o Ken Hornstein for his wonderful netscape-remote package, o Ioi Kim Lan for making an XPM image reader for Tk available, o George Howlett for his great BLT, of which parts are used by TkDesk, o Michael McLennan for his massively useful [incr tcl], o John Ousterhout for Tcl/Tk, which is definitely the best thing since sliced bread, o Greg Hankins and Matt Welsh for putting together the most wonderful linuxdoc-sgml package, o and of course, Linus Torvalds whose Linux kind of changed my life, really! And a very big thank you to the growing TkDesk user community, which provides me with a constant flow of bug reports (getting less now :-)), suggestions for enhancements of TkDesk, and lots of motivation and encouragement. Special thanks to Chuck Robey for revising a previous version of this guide. 1.2. Using TkDesk's Help System If you check menu entry Options/Use Netscape for Help, TkDesk will use that for displaying this User's Guide on-line. Otherwise, to reduce overhead, TkDesk uses its own help system. It features hypertext links, context sensitivity (which is not yet fully utilised by TkDesk) and full text search. The help window consists of four areas: 1. A listbox listing all the section headings. A section can be selected by pressing the left mouse button, 2. the text display, which contains the actual help text, 3. a text entry field in which a regular expression may be entered (such as [Ff]eatures). After hitting Return, the whole help text is searched for this expression. Pressing Return again continues the search, 4. three buttons: "Print" prints the complete help volume, "Back" jumps back after a hypertext link has been followed (see next paragraph), and "Close" to close the help window. Text that is displayed blue in the help window is a hypertext link. When the left mouse button is clicked over such a link the display will automatically change to the referenced section. You can jump back by pressing the "Back" button described above. The following keys are bound when the mouse pointer is inside the help window: Tab Moves to the next section. Shift-Tab Moves to the previous section. Control-Tab Moves to the first section. Control-Shift-Tab Moves to the last section. Up, Down Scrolls one line up/down. Page up, Page down Scrolls one page up/down. Control-Home Jumps to start of help text. Control-End Jumps to end of help text. Meta/Alt-b Equivalent to pressing the "Back" button. Meta/Alt-c, Escape Equivalent to pressing the "Close" button. 1.3. Command Line Options Usually TkDesk is started simply by executing the command "tkdesk" from the shell prompt or your X initialisation file. However, you may specify the following options to this command: -configdir dir Reads the configuration from directory dir instead of ~/.tkdesk. -default Reads the default configuration of TkDesk instead of the user's one in ~/.tkdesk. -iconic Iconifies all file browser and file list windows created by TkDesk during start-up. -layout file Reads the window layout information from file file instead of the default ~/.tkdesk/_layout. -startdir dir If this option is given, the first file browser will open with directory dir. -twm Don't use icon windows when file browser or list windows are iconified. Some window managers liek twm cannot handle these properly. For example, the command "tkdesk -twm -iconic" tells Tkdesk to not use icon windows and start with all windows iconified. 2. The File Browser Window The file browser window is the largest window when TkDesk is started for the first time. It contains a menu bar, a configurable button bar (for fast access to the most often used file operations and other functions), an entry field (displaying the current path), a horizontal scrollbar, a certain number of file listboxes (three by default), and a status and information bar. 2.1. The Menu Bar The following sections describe the entries of the individual menus of the file browser windows. 2.1.1. TkDesk Menu The "TkDesk" menu contains the following entries: New Browser... Asks for a directory for which to open a new file browser window. If the directory is the empty string, the operation is cancelled. By default the checkbox "In Browser" is checked, but if you uncheck it a file list window will be opened instead. The button to the right of the text entry field gives access to a menu containing previously visited directories. Clone Window Creates a new file browser window with the same directory as the current one (that is, the directory of the file browser from which this menu entry was invoked). Display AppBar/Hide AppBar Opens the application bar if it had been closed before, or hides (closes) it otherwise. This menu entry's label changes based on whether the AppBar is currently displayed or not. Configuration This is a submenu from which individual or all configuration files of TkDesk can be opened to modify them. Auto Save Here you can select which parts of TkDesk are automatically saved periodically and when TkDesk exits. Save All Now Saves all parts of TkDesk's configuration no matter what the settings in the previous submenu. Close Window Closes the file browser window. Quit Quits TkDesk. 2.1.2. File Menu This menu provides all the usual file operations plus entries for finding files. The contained menu entries are: Information Opens a file information window for each currently selected file. This window, which can also be used to add annotations to files, is described in section ``File Information''. New File... Asks for the name of a file which will be created in the current directory of the file browser or list. If "Open in Editor" is checked the new file will automatically be opened in the configured editor. New Directory... Asks for the name of a directory which will be created in the current directory of the file browser or list. If "Open after Creation" is checked the current window will switch to the new directory after it is created. Open Opens the selected files using their default action, which corresponds to the first action defined in their popup menus as configured in the "Popups" configuration file. Print... Asks for a command to print the currently selected files. The names of the selected files will be appended to the command. The default command can be defined in the configuration file "System". Copy, Move, Link... Opens a dialog box for copying, moving, linking, symbolic linking etc. of files. This dialog window is described in more detail in section ``Copying, Moving and Deleting Files''. Rename... For each selected file, TkDesk asks for a new name. Before actually renaming TkDesk checks for an existing file with the same name. Delete... Opens a dialog to delete files. The section ``Copying, Moving and Deleting Files'' gives more details. Find Files... Opens a dialog window which can be used to search for files, using a variety of characteristics. This dialog is described in more detail in section ``Finding Files''. Find Annotation... Lets you search for files with a certain annotation. More details in section ``Finding Files''. Copy To X Selection Copies the complete names of all currently selected files (i.e. including their paths) to the X clipboard. They can then be pasted into any other X application using the middle mouse button. Copy Names Only Same as previous one, but copies only the files' names (i.e. not their paths.) History Displays a submenu containing recently opened files. Close Window Closes the window. 2.1.3. Directory Menu The main purpose of this menu is to select directories which are to be opened. It also manages TkDesk's trash can. The menu contains the following entries: Open... Asks for a directory. A new file list or file browser window will be created, baesed on the setting of the "In Browser" checkbutton. New... Asks for the name of a directory which will be created in the current directory of the file browser or list. Home Directory Changes the directory of the window from which this entry is invoked to the user's home directory. A number of path entries These can be configured in the configuration file Directories. See section ``Configuration of TkDesk'' for details on how to do this. If one of these menu entries is invoked, the path of the file browser will change to that directory. If such an entry is invoked while at the same time pressing the "Control" key, a new file list window will be created (if the option "Always In Browser" is selected a file browser window will be created), displaying the contents of the selected directory. This feature applies to all menus that contain directory names! Trees Contains two cascaded submenus: "Home" and "Root". The contents of these submenus is dynamically generated and corresponds to the directory hierarchy rooted either at the user's home directory or at "/". Selecting an entry from these submenus changes the directory of the window to the selected directory. Pressing Control at the same time opens a new window with this directory. Open Trash Can Opens a file list window displaying the contents of the trash can. When this window is iconified and TkDesk uses icon windows the icon can be used as a Mac-like trash can. Empty Trash Can Empties the trash can after confirmation from the user. This erases the files contained in the trash can for good! History Displays a submenu containing recently opened directories. 2.1.4. Commands Menu This menu provides entries to execute commands either once or periodically, edit files, and provides some very basic form of "job control". Its entries are: Execute... Asks for a command to execute. The button to the right of the command entry contains a history of previously executed commands. Selecting one of these copies its name to the entry widget. Checking the "View Output" button will open an editor window after the command completed displaying its output (stdout and stderr). Execute as root... The same as the previous entry but TkDesk will ask you for the root password before executing the command. The command will then run under root permission. Periodic Execution... Opens a window which can be used to execute a command and watch its output periodically. If the "Don't execute" checkbutton is selected, the execution is paused. Job Control Opens a window which allows to stop, terminate, kill etc. processes which have been started by TkDesk. Environment... Opens the "Edit Environment" dialog which can be used to display and modify TkDesk's environment variables. Programs and commands started from TkDesk after any modification in this dialog has been made will pick up the modified environment. Edit File... Asks for the name of a file to edit, and opens it in the editor you selected in the "System" configuration file (defaults to the built-in editor). The "Browse..." button opens a file selector from which a file may be selected. New File Opens a new blank editor window. Edit Selected Opens all currently selected files in one new editor window. Buffers Displays a submenu of all current editor buffers (both files and command output). A number of custom command entries These can be defined in the configuration file Commands. See section ``Configuration of TkDesk'' for details on how to do this. If one of these menu entries is invoked, the corresponding command will be executed. History Displays a submenu containing commands recently executed. 2.1.5. Bookmarks Menu This menu lets you create bookmarks for often used files and directories. To do this, select at least one file/directory and invoke the "Add Bookmark" menu entry. The name(s) of the selected files and directories will now appear alphabetically sorted in the "Bookmarks" menu. You can remove any bookmark from the menu by selecting its corresponding entry from the "Remove Bookmark" submenu. The bookmarks you add will be automatically saved, if the "Bookmarks" entry of the "Auto Save" submenu contained in the "TkDesk" menu is selected (which is the default). 2.1.6. Options Menu The Options menu lets you configure many aspects of TkDesk "on the fly". The entries are: Long Listing Select this to let the details of all files and directories be displayed in the file listboxes. This slightly decreases performance and for that reason is switched off by default. Show All Files Whether to show files in the file lists whose name start with ".". Add Icons If selected, small icons will be displayed to the left of the file names in the file listboxes. Turn this off for faster scrolling. Folders On Top If selected (default) folders will always appear on top of the file lists. Append Type Char Whether to append a file-type specific character to the file names. This is mainly intended for monochrome displays and is then automatically selected. Single Click (Dirs) Lets you open directories with a single click of the left mouse button. Individual directories can still be selected by pressing Control at the same time. Always In Browser If this option is selected, the "In Browser" checkbutton of the "Open Directory" dialog will always be selected. Control- Doubleclick on a directory will open a file browser instead of a file list window. Status in List Windows Whether the singly columned file list windows should have a status bar as well. Sort by ... Sort all file listboxes by one of these criteria: Name, Name (fold, meaning that upper and lower case characters will be "folded", i.e. disregard case when sorting), Size, Date, Extension, or Don't Sort to display directory entries as they are read from disk. Strip <your home directory> If this is selected, and the current path of the browser is somewhere under your home directory, the leftmost file listbox will contain your home directory rather than the root directory. This speeds up the display of directory hierarchies under your home directory. Overwrite Always If this option is selected, TkDesk won't ask if the destination file already exists when copying or moving files. Really Delete Relates to the deletion of files. If this option is selected, the "Delete Files" dialog box will always have the "Delete permanently" checkbutton selected by default. Quick Drag'n'Drop Normally when you drop files onto a file list, the copy dialog appears. If this option is selected, dropped files will be moved to the destination directory without further questions. If Control is pressed during dropping, the files will be copied. If the drop target is the trash can, TkDesk will ask if the files are to be deleted "really". Sort History This option determines whether the history menus are to be sorted alphabetically or not. TkDesk Server Start or stop the built-in server to remote execute TkDesk commands. See also section ``Using the TkDesk Server''. Dialogs at Pointer If selected, TkDesk will always try to place new windows right under the mouse pointer. Autoraise AppBar Whether to raise the AppBar above any obscuring window when it is entered by the mouse pointer. Use Sound If you have sound working with TkDesk on your machine, you can temporarily disable sound by selecting this option. Handy when playing Audio CDROMs, for instance. Number Of Listboxes This relates to the number of listboxes in the file browser window. Between 1 and 6 can be displayed, though 18 would theoratically not be a problem. Balloon Help Whether to display a small window at the mouse pointer if it is placed over part of TkDesk, e.g. an AppBar button. Use Netscape for Help Whether to use Netscape rather than the built-in help viewer for displaying TkDesk online help. The settings of these options are by default automatically saved to ~/.tkdesk/_options. This can be disabled by deselecting the "Options" entry of the "TkDesk/Auto Save" submenu. 2.1.7. Help Menu This menu tries to give you some help with using TkDesk. It also contains entries for displaying the list of FAQs, recently made changes to TkDesk, and the license for using and distributing TkDesk: User's Guide Displays the TkDesk User's Guide either in the built-in help viewer or using Netscape (see previous section). Manual Page... Asks for a command for which to display the system's manual page in an editor window. Getting Started Displays the "Getting Started" guide. TkDesk FAQ Jumps directly to the FAQ section of the TkDesk help volume. Changes Displays recent changes to TkDesk. Make sure you invoke this entry after upgrading to a newer version of TkDesk. License Restates that TkDesk is distributed under the GNU Public License (GPL). About TkDesk... Some info about TkDesk (version, author etc.), plus a link to the TkDesk web page. 2.2. The Button Bar The button bar that's located right underneath the menu bar allows for fast access to any of the commands of TkDesk. It's also possible to configure each button individually to run customized Tcl scripts, that e.g. work on the currently selected files. By default, the buttons in the button bar provide access to the more often used functions of TkDesk (copy, create, delete etc.). When you place the mouse button over any of these buttons, a small help window will appear telling you what this button does (provided the "Balloon Help" option is activated). The contents of this button bar can be defined via the configuration file ButtonBar. See section ``Configuration of TkDesk'' and the configuration file itself for details on how to do this. Note that by not defining the variable tkdesk(button_bar) in that file or by not defining tkdesk(small_button_bar) the button for file viewer or list windows may be surpressed respectively. 2.3. The Path Entry This entry field displays the current path of the file browser or file list. You can also type directly into this field to change the display to another directory. There is a sort of "auto-completion" available; if you type only the first part of a directory's name and press Control-Tab, its name will be automatically completed as far as there's no ambiguity. If you click the right mouse button over this field a popup menu appears which lets you select any of the current directory's parent directories and their subdirectories. You can also open files directly from this popup menu. The button to the right of the entry field contains a menu of the last 30 (default value this may be changed in the "System" configuration file) directories you have visited. If you select one of these, the current path of the browser will change to it. The "Control-trick" to open the selected path in a new list window that's been described in section ``Directories'' works here as well! 2.4. The File Listboxes The main part of the file browser and list windows is taken up by the file listboxes. Directories are by default displayed in blue with a bold font, executable files in red and bold, and regular files in black and a medium font. These settings as well as the color, font and icons used for individual file types may be configured via the FileTags configuration file. See section ``Configuration of TkDesk'' for details on how to do this. Right above each listbox the name of the directory whose contents it shows is displayed, together with the current file mask settings. Clicking on this label reveals a menu with the following entries: Refresh Refreshes the listbox contents. Although TkDesk refreshes all its file lists every 5 seconds (default), sometimes you may want to have a file listbox instantaneously updated. Set Mask... Sets a mask for this listbox's display. For example, you can configure the listbox to only display or select files matching the pattern *.gif. Multiple masks separated by spaces may be entered to display files matching any of these. A history menu button for reusing masks previously entered is also provided, as well as a checkbutton for inverting the mask's effect (i.e. only files not matching the given mask will be displayed). No Mask Removes a previously activated file mask. Disk Usage Calculates the directory's disk usage, i.e. the number of kilobytes (default) occupied by that directory and all its subdirectories. A window will appear when the calculation is complete, containing a sorted list of all subdirectories and their individual disk usages. If you double-click on any of its entries, a new file list window will appear displaying the directory's contents. Clicking the right mouse button over any of the directories displays its associated popup menu. Free Space Displays the space available in the file system of this listbox's directory, using TkDesk's "Periodic Execution" window. Execute here... Asks for a command to execute in the directory displayed by this listbox. All the usual TkDesk %-sequences to work with the names of selected files may be used, e.g. %A to pass all currently selected files to the command. Execute as root... The same as the previous one, except the command will be run under root permission after asking you for the root password (and you entered the correct one...). Long Listing Instead of just the file names displays a "long listing" i.e. a listing containing all the details such as size, permission bits, date and time of last modification etc. Show All Files Whether to display files starting with a dot (".") or not. Inverse Order Whether to invert the current sorting order. Sort by... This is identical to the entry of the same name in the menu bar's "Options" menu, but affects this single listbox only. Open List Window If this listbox is part of a file browser window, open a new list window displaying this listbox's directory. Open Browser Else, open a browser with this directory. This menubutton can also be used to drag and drop files, as well as to drag the directory displayed in the associated listbox to another target, e.g. the root window by pressing the middle mouse button over the listbox title menu button. 2.4.1. Handling of the Listboxes (Bindings) The handling of the file browser windows is very similar to that of the NeXT file manager: when you double click on a directory, the listbox to the right of the current one will display this directory's contents. You can open a directory in this listbox, and the listbox to the right of this one will display its contents. This way you can browse through complete directory hierarchies, while having instant access to the contents of the complete directory tree. When the number of opened directories exceeds the number of visible listboxes, you can scroll the listboxes with the horizontal scrollbar which is located right above the file listboxes. Files are selected in the file listboxes using the left mouse button. A single click selects a single file, deselecting all other files. If the Control key is pressed simultaneously, the old selection will be retained. By dragging the mouse pointer over a file list, while at the same time pressing the left mouse button, a set of files can be selected. Shift-doubleclick selects all files in that file list. Holding down the shift button extends the current selection unto that file (a.k.a. "range-selection"). Note that if the option "Single Click (Dirs)" is set in the "Options" menu a single click on a directory will suffice open it. In this case you need to use Control-Click to select a single directory. A double click on any listbox item performs its default action. For directories this is to open it, for executables this is to execute it, and for files it invokes the first entry of the corresponding popup menu as defined in the "Popups" configuration file (see below). If the Control key is pressed while double-clicking a directory, a new file list or file browser window (depending on the setting of the "Always In Browser" option) is created displaying the contents of that directory. For files, a dialog box will appear asking for a command to execute on that file. Files can be dragged by pressing the middle mouse button over any selected file. If no file is selected, the clicked-over file will be selected. Files can be dropped by releasing the mouse button over any other file listbox and the menubutton above them, over windows of the built-in editor, over the application bar (if the corresponding button has been configured to handle dropped files), over iconified file browsers and file lists and in general over any other application supporting the file protocol of the BLT package's drag and drop implementation. Valid drop targets can be identified by looking at the window that is displayed while files are dragged: the window's relief appears raised if over a valid target, flat if not. The right mouse button is used in the file listboxes to access the file-specific popup menu. Every popup menu contains a submenu, labeled with the file's name, which contains entries for the most common file operations. The remaining entries of the menu can be configured via the configuration file Popups. See section ``Configuration of TkDesk'' for details on how to do this. The file listboxes may also be fully controlled using the keyboard: Up-/Down Arrow Move the selection one file up/down. Page Up/Down Move the selection one page up/down. Home/End Move the selection to first/last item in the listbox. A-Z Select the first file matching the character pressed, cycle through subsequent matches on repeated pressing of the same key. Return Open selected file. Menu (the key left to the right Control key on W95 keyboards)" Display the currently selected file's popup menu, the menu itself may also be operated using Up/Down, Return, and Esc. Backspace Change directory to the parent directory. F11 Open the current file listbox's menu. 2.5. The Status Bar The status bar is located at the bottom edge of file browser windows. If "Status in List Windows" is selected in the "Options" menu file list windows will contain a status bar as well. It displays either o the current state of TkDesk, o the amount of free space in the current file system, o details about the currently selected file, or o number and total size of currently selected files. 3. The File List Window The file list window is basically a more compact version of the file browser window. It displays only one listbox, which results in the fact that the contents of opened directories are always displayed in the same listbox, and it doesn't have a status bar unless "Status in List Windows" is set in the "Options" menu. Its advantage is that it is more quickly created than a file browser window and occupies less space on your screen. My usual setup is that I have one file browser and lots of file lists opened. The menu bar of the file list windows is also more compact, but allows access to all menus of the file browser through submenus of the "Others" menu. This menu contains one additional entry to open a file browser window displaying this file list's directory. 4. File Operations 4.1. File Information The "File Information" window displays detailed information about a file. It can also be used to change the owner or access-permissions of a file. In addition, an annotation to a file can be added here. The following information is displayed: Path The path of the file. Size The size of the file in bytes. Modified When the file was last modified. By clicking on the button displaying the date file can be "touched", ie. their modification timestamp will be set to the current time. Owner Displays the owner of the file. By clicking the button displaying the owner, the owner can be changed to another user. Group Displays the group ownership of the file. By clicking the button displaying the group's name, the group can be changed. Mode Here the access permissions are displayed in "ls -l" style. The first three button correspond to the rights of the owner (r: may read, w: may write, x: may execute or open the directory), the second three buttons to the rights of the group, and the last three buttons to every one else's rights. The "x" buttons are special in that they cycle through four settings: x for executable, s for set user/group id and executable, S for set id only, and - for not executable. Note that using the right mouse button toggles between - and x only. Clicking the middle mouse button over any of these buttons copies its current setting to the corresponding button in the other two groups. If the settings of any of these buttons is changed, the "Change Mode" button at the bottom edge of the window must be clicked to actually change the file's permissions. Links Number of (hard) links to this file. Type Tries to give some information about the file's contents. TkDesk uses the shell command file for this. In the "Annotation" text field you can enter any remarks about the file. This annotation will be saved when the "Close" button is pressed. Files having annotations attached to them will appear underlined in the file listboxes, and their popup menu will contain the first few words of that annotation as an "informative" menu entry. If the window displays information about a file an additional button labeled "Disk Usage" is provided, which calculates the disk usage of the hierarchy rooted at that directory. The entries of the "Disk Usage" window can be double-clicked to open new file list windows. 4.2. Copying, Moving and Deleting Files These functions can be accessed from the "File" menu, among others. There is one dialog for copying, moving and linking files (plus an arbitrary number of user-defined actions such as diff and patch, as configured in the "Popups" config file), one dialog for renaming files (which does nothing else than moving the file to its new name), and one dialog for deleting files. The "Rename" dialog is very straight-forward and probably does not need further explanation. The "Copy etc." dialog contains the obvious source and destination entry fields, plus a checkbutton labeled "all selected files" if more that one file was selected when this dialog was opened. If this checkbutton is selected (default) the operation will be applied on all selected files. If it is not checked, each file will be handled individually. The "Skip" button can then be used for skipping individual files. The "Delete" dialog also contains this checkbutton, plus a checkbutton labeled "Delete permanently". If this checkbutton is selected, the files will not be moved to the trash can but will be really and ultimately deleted!! The default setting of this checkbutton can be set from the "Options" menu. 4.3. Finding Files The "File" menu contains two entries for finding files: "Find Files..." and "Find Annotation...". Files can be annotated through their "Info" dialog (accessible from the "File" menu or from their popup menu), see section ``File Information''. "Find Annotation" enables you to look for an annotated file whose annotation matches a certain regular expression (which can be as simple as an ordinary string). "Find Files" lets you look for files (or rather: let's you instruct TkDesk to look for files) whose names match one or multiple patterns, which are of a certain type (such as directory), which contain a certain string, which are smaller or bigger than a certain number of kilobytes or which are younger/older than a certain date. All fields which are not left blank in this dialog will be combined with a logical AND. This dialog is currently the only one utilizing the balloon help capability of TkDesk, so for now I would like to refer you to this. For instance, if you want to know how to enter the file size you're looking for, place the mouse pointer over the "Size" entry field without moving it for a few seconds. Both "Find" dialogs display their results in the same file listboxes that are used by the file list and browser windows, so the same bindings described in section ``Handling of the Listboxes (Bindings)'' apply here as well! 5. Cascading Directory Popup Menus Ugh, what a title... However, one of the most powerful features of TkDesk are its "cascading directory popup menus." These are menus that start from a given directory, contain submenus for each subdirectories (and possibly menu entries for files), and that dynamically add submenus as you traverse the menu. Hm, kinda hard to explain, but pretty cool. These menus are the ones that are accessible through menu entries "Directory/ Trees/ Home" and "Root", by clicking the right mouse button in the file browser and list window's path entry field, and through the popup menus of directories (submenu "Subdirectories" and "... and Files"). They may also appear in other places, depending on your configuration of TkDesk. There are two special bindings available in these menus: Control-Mousebutton-Release On a menu entry associated with a directory this opens a new file list window displaying this directory's contents. On a file it asks for a command to run on this file by displaying the "Open or Execute" dialog. Left-and-Right-Mousebutton-Press If the left and right mousebuttons are pressed simultaneously over a menu entry, the menu disappear, the popup menu for the associated file or directory is displayed. Otherwise the directory or file that's selected from the menu is opened using its default command. 6. The Application Bar TkDesk provides you with an application bar for fast access to your favorite applications, commands, directories etc., plus displays for the current date and time, system load, and the status of your mailbox and dialup link. It consists of an arbitrary number of buttons. Each button (also the aforementioned displays) contains a popup menu which can be accessed by pressing the right mouse button over any of them. If you single-click the left mouse button over such a button, the first entry from the corresponding popup menu defining a command to execute will be invoked. The first button (displaying a small desk by default) could be called TkDesk's "Start Button". Its popup menu contains entries for accessing TkDesk's most often used functions, such as executing a command or opening a file list or browser window, plus submenus for your bookmarks, the files you most recently opened, and the directories you've last visited. The next entry is a submenu labeled "Application Bar". Here you can configure all aspects of the application bar, especially its position and orientation. See my answer to the MFAQ ``How can I change the position of the application bar?'' for more. The last entry labeled "Configuration" contains another submenu which gives you fast access to TkDesk's configuration files. The second button gives you access to this User's Guide, and lets you view manual pages, also making use of a running TkMan (which is a hypertextified manual pager which is to be highly recommended). This button also allows you to drop executables on it to automatically display their manual page, if they have got one. Note that the default value of the "Manual Page" dialog is the contents of the current X selection, so this can be used for some sort of "context sensitive" help. Keep in mind that all the buttons can be configured by you! See section ``Configuration of TkDesk'' for details on how to do this. 7. The Built-in Editor The built-in editor of TkDesk is meant to be a simple ASCII editor for editing files smaller than, say, 500kB. A single editor window can handle an arbitrary number of buffers each of which contains another file. Files can either be loaded through the "File" menu of the editor or by simply dropping one or more files on the text field of the editor window from one of the file listboxes. An editor window contains the following menus in its menu bar: File Contains entries to load, save, and print files, and to close the current buffer, current window, or all windows. Edit The first entry provides access to the editor's "Undo" functionality. Note that the undo buffer may contain a maximum of 500 events. This menu also contains entries for managing TkDesk's own text clipboard. Also provides entries to search for text (regular expressions), replace text, or find the next occurence of the currently selected text. The entry "HyperSearch" is a special form of search: all matching lines are displayed in a file listbox. If one of its entries is clicked on, the editor automatically displays that line at the top of the text field. This can be useful for jumping to headings, function definitions etc. Oh yes, and the regular expressions entered here are also saved with the other histories. Options The "Auto Indent" option determines whether the cursor is automatically indented after hitting Return. If the "Send to Netscape" option is set, the current file will be loaded by (maybe a running) Netscape each time it is saved. Useful when editing HTML files. Buffers Lists the buffers of the editor window. Buffers can be selected from this menu. Configuration This menu is only available when editing one of TkDesk's configuration files. It lets you save the file and reload it into TkDesk by selecting the corresponding menu entry or pressing F5, or exactly the same plus closing the buffer by invoking the next entry or pressing F6. This way configuration files can be edited and reloaded very quickly into TkDesk. The text area provides all the more common Motif- and Emacs-like key- bindings (including Control-Space for selecting an area of the text). Maybe it should be mentioned that Control-C/X/V do not work on the X selection but on TkDesk's own clipboard. Use the middle mouse button to paste from the X selection into TkDesk or from TkDesk into another application. 7.1. Marks Marks can be set with Control-[1-9], and be jumped to with Alt/Meta-[1-9]. You can always jump back with Control-0. They work across buffer and editor window boundaries, and are currently only valid as long as the buffer in which the mark was set is open. 8. Using the TkDesk Server In order to allow remote control of TkDesk, i.e. to allow programs outside of TkDesk to have TkDesk perform certain tasks, TkDesk implements a TCP/IP server that can be used to send Tcl scripts to TkDesk. Whether this server is "up" or not is determined by the option "TkDesk Server" that is to be found in the "Options" menu. Currently, only programs running on the same machine as TkDesk may connect to the server, but multiple TkDesk servers started from different users may be running at the same time. The server is a much faster way to send command to TkDesk then to use the Tcl/Tk send command, as that requires a second Tcl/Tk shell ("wish") to be started to do the send. However, using a TCP/IP server that's basically system-wide accessible to perform arbitrary command under the accout of the user who started TkDesk and thus the server brings with quite a big security risk that TkDesk tries to reduce by keeping the port the TkDesk server uses secret. The port to be used is calculated randomly at server start-up, and is saved into a file that can only be read by the user who started TkDesk. To prevent "guessing" of the port number a generated key is also stored in this file that get passed to the server. The client performing the communication with the TkDesk server gets installed with TkDesk; its name is tkdeskclient. This command expects exactly one argument which will be directly sent to the server and evaluated there as a Tcl script. E.g. you could do a tkdeskclient "dsk_view id" to find out who you are :-). Along with TkDesk a number of front-end shell scripts for tkdeskclient are installed, which comprise of the following: cd-tkdesk ?path? Let's the currently active TkDesk file list or browser window (i.e. the one the mouse pointer was over last) change its directory to path if path is given, or returns the current directory of the active window. ed-tkdesk ?+linenum? ?file? ... Without argument opens a new editor window, or loads all files given as arguments into the editor (and into the same window if you're using a multi-buffer capable editor, such as the built-in one). If a file is preceded by +linenum, i.e. something like +20, the built-in editor will position the cursor on the line when displaying the file that's given in the following argument. od-tkdesk ?dir? If no arguments are given opens a file list window for the current directory of the shell/program where the command was issued, or opens a window for the directory specied in dir. op-tkdesk ?file ...? For each file performs its default action (the one defined first in its corresponding popup menu as defined in "Popups"), or asks for a command to execute if no files are given. pop-tkdesk file Displays the popup menu for file, as defined in the "Popups" config file. The popup menu may also be controlled by the keyboard: Up, Down, Return, and Esc keys do what you would expect. Note that all of these scripts are merely examples of possible usages of tkdeskclient. Use your imagination to find new and even more exciting applications! :-) 9. Configuration of TkDesk Currently, TkDesk can be configured only by editing ASCII files. This is not necessarily a drawback, because this way you can add complex Tcl/Tk procedures to the configuration files. GUI based configuration is planned for one of the next releases. If you don't know Tcl/Tk: Don't despair! For the biggest part of TkDesk's configuration files it is absolutely not necessary for you to know how to program in Tcl/Tk, since you just have to modify values or extend the examples I and others have provided. And to those who want to exploit all of the power available by using Tcl/Tk as TkDesk's configuration language, please have a look at the answer to FAQ ``Where can I find out more about Tcl/Tk?''. The configuration files can be accessed from the TkDesk menu via the Configuration submenu, or from the "Configuration" submenu of the popup menu of the application bar's first button. The built-in editor will then appear with the selected configuration file(s) already loaded. Each configuration file contains pretty detailed comments on what the individual settings are for, and how they can be modified. Well, at least they contain examples, which can guide your modification attempts. Once you have modified the configuration file, you can save it and reload it into TkDesk by making use of the entries of the editor's "Configuration" menu described in section ``The Built- in Editor''. Use F5 to save the file and reload it into TkDesk, and F6 to do the same but additionally close the configuration file. As already mentioned, each configuration file contains directions on how to modify its definitions. The general advice is: Simply look at the example definitions, because these make things a great deal clearer than any explanation can. Tip: In case your looking for the definition of a specific part of TkDesk, e.g. for the popup menu of pdf files, use the "Find..." entry in the "Configuration" menu. Just enter pdf in the "String:" field, hit enter, and double-click the found file. 9.1. Configuration Files All of these are "sourced" (i.e. processed) by TkDesk during start-up. This happens in the following order: 1. System 2. ButtonBar 3. Preferences 4. FileTags 5. Commands 6. Directories 7. Popups 8. AppBar 9. Sounds 10. Local (if existent) 9.1.1. AppBar This configuration file lets you define all aspects of the application bar (apart from its layout, use the entries of the submenu "Application Bar" of the desk-button's popup menu for this). This is what you can define here: o If the application bar should be managed by the window manager just as an ordinary window. This is disabled by default. o The maximum number of buttons in a column (vertical layout) or row (horizontal layout). Yes, the application bar can contain several columns or rows! o The fonts to use for the time and date display. o Period in seconds after which to update the system load and mailbox displays, where to check for new mail, plus several additional mail related options. o Icons and commands to use for the dial-up networking button. o Icons and fonts to use for the trash button, and whether to display its current disk usage in the appbar. o The actual button definitions, ie. which image to display, and the entries of the (possibly cascaded) popup menu. See below for the syntax used. 9.1.2. ButtonBar This file defines the contents and appearance of the Button Bar, which is displayed underneath the menu bar of TkDesk's file browser (tkdesk(button_bar)) or file list windows (tkdesk(small_button_bar). Both button bars are configured independently from one another. The third list defined here (tkdesk(dir_button_bar)) is for directory- specific button bars which will be dynamically displayed and hidden when the respective directory is entered or left. These will be displayed in addition to the standard button bar. 9.1.3. Commands In this file you can define entries which will be added to TkDesk's "Command" menu, for example to uncompress all selected files, manage RPM package files, or do whatever you can possibly imagine. 9.1.4. Directories Here you can define entries and cascaded submenus of directories which will be added to TkDesk's "Directories" menu. More details are contained in the "Directories" file itself. You can also define a list of directories whose parent directories are not to be displayed in file browser windows, as is usually the case with your home directory. This can speed up display of directories underneath an AFS directory, for instance. Another list lets you define action that are to be performed when a specific directory is opened. This can be used for example to auto- mount floppies and CDROM's. 9.1.5. FileTags Contains definitions for color and font of specific file types (as distinguished by their extension) as well as standard values for regular files, executables, and directories, that will be used in TkDesk's file lists. Also the icons displayed when the "Add Icons" option is selected are configured here, as well as the icons used for the desk items, i.e. files and directories which have been dropped on the root window. 9.1.6. Local This file does not exist when TkDesk gets installed, but still appears in the "Configuration" menu. This is because if it exists it will be sourced during start-up of TkDesk, so this is the ideal place to put local extensions of TkDesk. 9.1.7. Popups The popup menus that appear when the right mouse button is clicked over a file or directory are defined here. There are individual definitions for the popup menus of directories, executables, and of other files. To find the correct popup menu for a file TkDesk checks the mask of each entry of a popup list one after the other from the beginning of the list to its end, so the masks should become more general towards the end of the list. An additional popup list (tkdesk(fileops,popup)) may be defined here for use in the "Copy, Move, etc." dialog. 9.1.8. Sounds Here the command to be used for playing sounds can be defined, as well as the sound files which are to be played at certain events. TkDesk comes with a few AU sound files which are located in the sounds subdirectory of TkDesk's library directory. You can find out where this is by looking at the fifth line of the tkdesk script. 9.1.9. System All the more "basic" features of TkDesk can be configured here. These are: o Colours and fonts to be used by TkDesk, o Default size of file listboxes, o Definitions of shell commands which are used by TkDesk to copy, move, delete, etc. files. These should be fine as they are in most cases, but you may want to tweak them for your system, so take a look at these definitions. o The default command for printing, o Which editor to use, plus settings for the built-in editor, o Periods for automatic refreshing of the file lists and saving of TkDesk's configuration, o Maximum number of entries in history menus, o If TkDesk should ask for confirmation when the user wants to quit TkDesk, o If TkDesk should allow the menus to be "tearoff-able", o The paths in which TkDesk will look for images and sounds used in the configuration files, o Icons to use for file list and browser windows, and for the help window, o Whether to use "focus follows mouse" default or not, o Settings for the desk items, e.g. whether to let them be managed by the window manager, o Commands to execute after start-up and before shutdown (of TkDesk). 9.2. The TkDesk API Many of the values to be specified for TkDesk's configuration files are Tcl scripts. Also, you can define your own Tcl proc's to use in these scripts. To allow better control of TkDesk, TkDesk externalizes a couple of proc's which could be called the "TkDesk API" (although currently they aren't much more then a random set of more or less useful proc's, but anyway). The proc's I currently think are part of the API are the following: dsk_msg_alert msg Display msg in an alert dialog box. dsk_msg_error msg Display msg in an error dialog box. dsk_msg_info msg Display msg in an info dialog box. dsk_confirm msg script Display msg in a dialog box with "OK" and "Cancel" buttons. Evaluate script if user pressed "OK". dsk_debug msg If TkDesk runs in development mode (e.g. by setting the -devel command line option) print msg to stderr. dsk_ask_dir Ask for a directory to open. dsk_ask_exec Ask for a command to execute or file to open. dsk_exec command Execute command in the background. When the command completes its exit code will be displayed in the status bar of all file browser windows. dsk_exec_as_root command Asks for the root password, and executes command in the backgroud under root permission if it was okay. dsk_view command Execute command in the background and displays its output (stdout and stderr) in a built-in editor window. dsk_view_as_root command Same as dsk_view but asks for the root password first. dsk_path_exec path command Executes command in path and in the background. dsk_path_view Executes command in path and in the background, and displays its output in an editor window. dsk_edit ?+linenum? ?file? ... Without arguments asks for a file to open, or opens all files in the same editor window, positioning the cursor at line linenum if given. If file is "New File" a black editor window will be opened. dsk_busy Displays mouse as "busy" and locks all TkDesk window from receiving mouse events. dsk_lazy Displays mouse as "lazy", and makes all TkDesk windows responsive again. dsk_active what Returns information about the currently active file browser or list window: If what is "dir" it returns its directory; if it is "sel" it returns all selected items as a Tcl list; if it is "win" it returns the Tk name of the active window. dsk_select X ?names? If names is not given, copies the full path names of the files selected in the currently active file browser or list window to the X selection. Otherwise just their names are copied. dsk_read_string msg ?script? ?dontpaste? Displays $msg in a dialog box where the user can enter an arbitrary string. If script is not given, the string will be returned; if it is given the variable dsk_read_string will be set to the user's input and script will be evaluated. If "dontpaste" is passed as a third argument to this command the current X selection will not be pasted into the entry field. dsk_filesel message path Opens TkDesk file selection dialog using message as the label, and presetting the entry field with path. Path is also used as the filter for the full file selection box. dsk_refresh ?file ...? For each file all windows displaying the corresponding directory is refreshed. File may also be a directory. dsk_sound sound Plays sound if a sound command has been defined and sound is switched on. Sound is the second index in the tkdesk(sound,xxx) array as defined in the "Sounds" config file ("xxx" in this case). dsk_copy ?source ...? ?dest? Opens the "Copy, Move, etc." dialog box, filling in "Source" and "Destination" fields if provided. dsk_dialup phonenumber Without arguments asks for a phone number, and passes this to the command specified in tkdesk(appbar,dialup,cmd_up), else it does the same without asking. dsk_find_files ?options? Opens the "Find Files" dialog, presetting its fields from the options. The following options are available: -path path, -mask mask, -string string, -regexp regexp, -extreg extreg, -type "all|file|dir|sym|socket|pipe", -owner owner, -group group, -modified mod, -accessed acc, -size size, -case, -samefs, -followsym. If the option -doit is given the search will start as soon as the dialog is displayed, without having to press "Search". dsk_mail file ?string? Asks for an email address where to send file. If file is the empty string, string will be sent instead. dsk_netscape what ?url? ?args? This proc is used to communicate with a running netscape. If Netscape is not running yet it will be started first. What may be "file", "url", or "rcmd" (Communicator only); url may be a regular URL, a file name, or, if what is "rcmd", it may be "mail", "news" or "edit". Args may be "window", or "raise", or both. dsk_periodic command seconds Opens the "Periodic Execution" window and executes "command" every "seconds" seconds. dsk_open viewer file Opens file by performing its default action. Viewer should be given as ""; I just didn't manage to get rid of this obsolete argument yet. dsk_open_browser dir Opens dir in a new file browser window. dsk_open_dir dir Opens dir in a new file list window (unless the option "Always in Browser" is set). dsk_openall ?files? Opens all files if given, or all currently selected files by performing their respective default action. 9.3. Shortcuts When specifying Tcl scripts in the popup and appbar configuration lists (tkdesk(popup,...) and tkdesk(appbar)), and in some other places as well (precide, hus?), you can use a number of shortcuts that will be expanded just before evaluation: %s Will be replaced with the full pathname of the first selected file. %f Will be replaced with its filename only. %b Will be replaced with its filename without extension ("basename"). %d Will be replaced with its directory only. %A Will be replaced with a Tcl list of the full pathnames of all currently selected files, or of the files dropped on an appbar button. %a Same as %a, but replaces with a list of filenames only. %B Same as %A but doesn't complain if no files are selected. Instead it will be replaced with the empty string. %D Will be replaced with the directory of the currently active file list or browser window. %x Will be replaced with the contents of the X selection. %X Same as %x but doesn't complain if the selection is empty. %S For the "Copy, Move, etc." popup menu; will be replaced of what's been entered into the "Source" text entry field. %D For the same popup menu; will be replaced of what's been entered into the "Destination" text entry field. 9.4. Utilities To make life while configuring TkDesk a little easier as long as there's no GUI configuration available yet, TkDesk provides four little "helper" tools that help with selecting colors, fonts, icons, and sounds, by using graphical mouse-based dialog boxes. These are accessed through the "Configuration" menu, and are basically all handled in the same way. Each of these dialogs contains three buttons: Insert Works only correctly if the dialog was invoked from the editor's "Configuration" menu. Inserts the current value at the cursor position in that editor window. Select Copies the selected value to the X selection, so it can be pasted at arbitrary places using the middle mouse button. Close Closes the dialog. 10. Frequently Asked Questions 10.1. How can I change the position of the application bar? The appbar should have a "handle" at the upper or left edge that can be used to drag the appbar around the screen by just pressing the left mouse button over it. You can also drag the application bar around simply by holding down the Alt- or Meta-key and simultaneously pressing the left mouse button over the application bar. You can also invoke the "Move..." entry from the desk-button's popup menu to do the same without having to press Alt/Meta. The application-bar configuration file, "AppBar", allows you to set a variable named tkdesk(appbar,wm_managed) which can be used to have the apllication bar managed by the window manager, although this is usually not necessary. 10.2. Can I have transparent icons? No, but have a look at the answer to the next question. As far as I know to have transparent icons in X you need to make use of X11's SHAPE extension. Now as raw X programming is something only for the very brave, I didn't look into this any deeper yet. Any takers? 10.3. How can I change the background colour of the icons and desk items? The background colour of icons used when the window manager iconifies a window can be set in the configuration file "System". The variable you are looking for is tkdesk(color,icon_background). By setting this variable to the same colour as your root window you can achieve the effect of transparent icons. You can define the colour either as a normal name (such as "grey", "blue") or in the form #rrggbb. 10.4. How can I have a different set of desk items on each virtual screen? 10.4.1. FVWM and similar or derived window managers First, you have to set the variable tkdesk(desk_items,wm_managed) in the System config file to 1. Then you have to configure fvwm to not decorate windows of class dsk_DeskItem. For instance: Style "dsk_DeskItem" NoTitle, NoHandles, WindowListSkip, BorderWidth 0 10.4.2. CDE window manager As for FVWM, you first have to set the variable tkdesk(desk_items,wm_managed) in the System config file to 1. To tell the CDE window manager (dtwm) to not decorate desk items you have to add the following line to either the file Dtwm or .Xdefaults in your home directory, and then restart dtwm: Dtwm*dsk_DeskItem*clientDecoration: none 10.5. How do I configure FVWM mini-icons for TkDesk? For fvwm95's Style command (like fvwm2 from which it comes) you can use a variety of things to identify a tkdesk window - you don't have to use the name only; you can also use the window's class name or resource string. To find out what the window class and resource string are for a certain type of tkdesk window, use the FvwmIdent module (if you use Debian, this will be under the Window Managers->Modules menu; if you're using the default fvwm95rc that comes with the fvwm95 distribution, this will be under the Programs->Modules menu; I don't know about others). Anyway, FvwmIdent tells me that the window opened by tkDesk's "Open Browser..." menu has a class name of dsk_FileViewer, so I open the FvwmConsole (or FvwmTalk) module, and enter the commands: Style dsk_FileViewer TitleIcon mini-filemgr.xpm Recapture Then the browser window appears with the appropriate icon. To get the window opened by the "Open Directory..." menu, use: Style dsk_FileList TitleIcon mini-filemgr.xpm (The "recapture" command is only necessary to apply the command to windows already open; it isn't necessary in your config. file) (Contributed by Daniel Martin, dtm12@jhunix.hcf.jhu.edu) 10.6. Configuring the appbar or popup menus to execute (export foo=bar; program;) doesn't work. Yes, this is a bit tricky. What you need to do is the following: dsk_exec sh -c {export foo=bar; program} 10.7. Composing characters doesn't work in TkDesk's editor. Currently you have to edit the file cb_tools/bindings.tcl in TkDesk's library directory to make this work. Locate the two lines containing the word "XKB"; all you need to do is to comment out the following lines by prepending a '#'. 10.8. TkDesk's layout seems to be screwed. I can't get the appbar displayed anymore. TkDesk saves all layout information in the file /.tkdesk/_layout, so this is the place to look for bad lines. If in doubt you can safely delete this file and all should be back to normal. In case of the appbar not showing up you can try the following (sent by Jochem Huhmann, joh@unidui.uni-duisburg.de): 1. Quit tkdesk 2. Open the file ".tkdesk/_layout" within your home directory with your favorite editor 3. Delete the line looking like "Toplevel dsk_appbar 1 66x740+956+0" 4. Write the file back to disk 5. Restart tkdesk. It will place the AppBar according to its defaults now. 6. Place the AppBar as you like (it doesn't like horizontal layout too much) 7. Do a "TkDesk/Save All Now" 10.9. On my RedHat 5.x system the appbar clock shows the wrong time. This is from Bryan Venable, spif@vousi.com: This may have to do with the location of the zoneinfo directory. For libc5 I believe it's /usr/lib/zoneinfo, whereas for glibc it'd be /usr/share/zoneinfo. Try making a symbolic link from whichever you have to whichever you don't. There is a fix to Red Hat 5.0 which has to do with this, but in that situation the problem is with libc5 programs running on a system "optimized" for glibc. And Raul Quiroga, quiroga@cartan.math.cinvestav.mx, also has got some advice for this: Concerning the problem described below I received several suggestions. Some allowed to get the date in the tkdesk right but an incorrect one with the date command. Finally what I did is set the time with timeconfig to Universal with "Hardware clock set to GMT" checked; after a reboot both the appbar and date reported the correct time. Thanks all for your help. 10.10. TkDesk complains about "invalid command name wm" and won't start up There seem to be two solutions to this problem: One is if you're running on a RedHad Linux 5.x system, the libc5 that's packaged and installed in /lib may be too old a version. If ls /lib/libc.so.5.* on your system gives something like /lib/libc.so.5.3.xx you should upgrade to at least 5.4.20. I think you can get a newer version of libc from sunsite.unc.edu in /pub/Linux/GCC. The other solution that should always work is to do the following (thanks to Ike Hishikawa, ike@hishikawa.f.uunet.de, for this one): Assuming that the tarball was unpacked under /usr/local, open /usr/local/bin/tkdesk with your favorite editor. At the very top of the file it should say: #!/bin/sh #-*- tcl -*- \ PATH=/usr/local/bin:$PATH ;#\ exec tkdesksh "$0" "$@" After the 3rd line, insert two lines pointing to the location of tcl/tk libs, so that you get: #!/bin/sh #-*- tcl -*- \ PATH=/usr/local/bin:$PATH ;#\ export TCL_LIBRARY=/usr/local/lib/TkDesk/tcl_lib ;#\ export TK_LIBRARY=/usr/local/lib/TkDesk/tk_lib ;#\ exec tkdesksh "$0" "$@" This did the trick for me :) Hope this helps, 10.11. I cannot launch other Tcl/Tk applications from within TkDesk Probably your version of TkDesk sets the environment variables TK_LIBRARY and TCL_LIBRARY, which confuses other Tcl/Tk apps. Unset these variables in the config files before the invocation of the problematic commands, eg. replace xterm with sh -c {unset TK_LIBRARY TCL_LIBRARY; xterm} (contributed by Christoph Dalitz, dalitz@infotech.de) 10.12. I'd like TkDesk to do this and that. How can I achieve this? The first place to start are the various configuration files of TkDesk. These can be accessed either by the "TkDesk/Configuration" menu of the file browser windows, or by the "Configuration" submenu of the popup menu of the very first button of the application bar of TkDesk. Since TkDesk uses Tcl as the language for its configuration, and these configuration files are simply "source"ed, you could add any sort of Tcl proc for instance to the configuration file "System". This proc would then be available in every other configuration file as well. With the set of commands provided by TkDesk, which are listed e.g. in the configuration file "Popups", TkDesk provides a very powerful platform for the user who knows Tcl. 10.13. Is there a TkDesk mailing list? There two mailing lists dedicated to TkDesk, one for discussing general installation/usage topics (tkdesk-users) and one targeted more for TkDesk-related development topics (tkdesk-code). You can subscribe to the tkdesk-users list here: <https://lists.sourceforge.net/lists/listinfo/tkdesk-users>. To post questions, etc., send email to tkdesk-users@lists.sourceforge.net. It you are interested in contributing to the development of TkDesk, you can subscribe to the tkdesk-code list here: <https://lists.sourceforge.net/lists/listinfo/tkdesk-code>. Once you have subscribed, you can post using the address tkdesk- code@lists.sourceforge.net. 10.14. Where can I find out more about Tcl/Tk? The official Tcl/Tk homepage is at <http://www.tcl.tk/>. There is also a newsgroup dedicated to Tcl/Tk: comp.lang.tcl. 11. Tips and Tricks This section currently contains just one tip on how to combine TkDesk and XEmacs. If you have any procs or other stuff in your configuration file which you consider could be useful for others as well, or just think it's generally cool, please send me an email, so that I can add it to this section! Thanks! 11.1. TkDesk and XEmacs If you are using XEmacs 19.12 or later you can couple TkDesk and XEmacs quite closely together by adding the following proc into any of your configuration files (I have it in "Popups"): ______________________________________________________________________ proc xemacs_load {what {where same}} { switch $where { "same" { exec gnudoit -q (find-file \"$what\") } "other" { exec gnudoit -q (find-file-other-window \"$what\") } "frame" { exec gnudoit -q (find-file-other-frame \"$what\") } "scratch" { exec gnudoit -q (switch-to-buffer-other-frame \"*scratch*\") } } } ______________________________________________________________________ And now my generic popup menu for files matching * reads: ______________________________________________________________________ {{*} { {{Edit} {dsk_edit %s}} {{XEmacs} {xemacs_load %s}} {{Other Window} {xemacs_load %s other}} {{Other Frame} {xemacs_load %s frame}} - {{Print} {dsk_print %s}} }} ______________________________________________________________________ This way you can load files from TkDesk into a running XEmacs! This assumes that you have the command gnudoit somewhere in your path, and have started the XEmacs server. This can be done by adding the following line to your ~/.emacs: (gnuserv-start) �����������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/tcldesk/doc/License����������������������������������������������������������������������0100644�0001750�0000764�00000044352�10037101366�014435� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� TkDesk - A Desktop and File Manager for Unix and the X Window System Copyright (C) 1996 Christian Bolik This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License appended below for more details. -------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) 19yy <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tkdesk-2.0/tcldesk/doc/QuickStart�������������������������������������������������������������������0100644�0001750�0000764�00000011671�10020457430�015141� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� How To Get Started With TkDesk Really Quickly (V1.2) ============================================================================= What's On Desk ----------------------------------------------------------------------------- When you start TkDesk for the first time, it comes up with its default window layout. This is: o The Application Bar (or appbar for short) -- that's the vertical row of buttons on the left hand side of the screen, and o A File Browser Window (or browser) containing three file listboxes. This is the "main" window of TkDesk used for file operations like copy, move etc. (in fact, you can have several of such browsers). There's also a smaller variant of the browser window: the File List Window. This is a compact version of the browser with just one file listbox. The Application Bar ----------------------------------------------------------------------------- Each button provides a pop-up menu that can be accessed by pressing the right mouse button over any of the appbar's buttons. Clicking the first mouse button over an appbar button invokes the first entry of the pop-up menu. The appbar can be moved around by pressing the Meta- or Alt-key and the first mouse-button at the same time over the appbar. Summary of mouse button bindings (1: left, 2: middle, 3: right button): 1: Invokes first entry of associated pop-up menu. 3: Displays associated pop-up menu. Meta/Alt-1: Move the application bar around. Meta/Alt-2: Shortcut to TkDesk's configuration files. The first appbar button gives access to some more often used functions of TkDesk, and to most history lists managed by TkDesk. It also contains an entry to change the layout of the appbar to horizontal, if that's what you prefer. The File Browser Window ----------------------------------------------------------------------------- The main part of the file browser windows are the file listboxes. They are laid out such that the left listbox always displays the contents of the parent directory of the file listbox to the right. As you traverse a directory tree, listboxes "move off" the visible area to the left. The large horizontal scrollbar on top of the listboxes can be used to make them visible again. Summary of mouse button bindings (1: left, 2: middle, 3: right button): 1: Select directory or file. Control-1: Single-select directory or file. Shift-1: Range-select directories or files. Drag-1: Select an area. Shift-Double-1: Select all items. Double-1: Open directory or file (actually invokes the first entry of the associated pop-up menu). Ctrl-Double-1: Directory: Open file list window with this directory. File: Open file interactively. 2-Move: Drag directory or file. Can be dropped on other file listboxes, some appbar buttons, or the desktop (root window). 3: Display associated pop-up menu. The button right above each file listbox displaying the name of the directory whose contents is displayed in the listbox is a menu button. Pressing the first mouse button over it displays a menu of operations applicable to the contents of the listbox. The File List Window ----------------------------------------------------------------------------- File list windows can be opened by opening a directory while pressing the Control key, or by entries from the browser's "Directory" menu. The default directory popup menu also contains an entry for this purpose. The main difference of the file list window compared with a browser window is that the contents of directories you open are always displayed in the same file listbox. For the list of mouse bindings please refer to the previous section. Get going! ----------------------------------------------------------------------------- Hopefully, this is enough to get you started with TkDesk. Note that there is a "Help" menu button in the menu bar of the file browser window. It gives access to the TkDesk User's Guide, which documents every tiny feature of TkDesk (not yet though). Also note that TkDesk has a "balloon help" feature: just place the mouse pointer over any button of the application bar or button bar in the browser window. A very useful place to look for help is the searchable archive of the TkDesk mailing list at http://www.findmail.com/list/tkdesk. And now it's your turn: Start exploring TkDesk! Look into every menu, click on every button, and take a look at the User's Guide! Please also take a look at "Help/Changes", even if you're a first-time TkDesk user. It explains many of the less obvious TkDesk features. When you've become a bit more familiar with TkDesk you will want to take a look at TkDesk's configuration files (Menu "TkDesk/Configuration"). Virtually every aspect of TkDesk can be configured! Just experiment with the default configuration. Enjoy TkDesk!! �����������������������������������������������������������������������tkdesk-2.0/tcldesk/doc/guide-1.html�����������������������������������������������������������������0100644�0001750�0000764�00000020124�10037101554�015237� 0����������������������������������������������������������������������������������������������������ustar �jcc�����������������������������jcc��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <HTML> <HEAD> <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.20"> <TITLE>TkDesk User's Guide: Introduction Next Previous Contents

1. Introduction

TkDesk is a graphical desktop manager for UNIX (with a slight emphasis on Linux, but it also runs just as well on AIX, Solaris, HP-UX, SGI Irix and other UNIX flavors) and the X Window System. It offers a very complete set of file operations and services, plus gives the user the ability to configure most aspects of TkDesk in a very powerful way. The reason for this is the use of the Tcl scripting language as the configuration and (for the greatest part of TkDesk) implementation language. This makes TkDesk not just configurable but truly programmable. TkDesk has been influenced by various other systems and file managers: NeXT, for laying out the file browser windows, Apple Finder, for the idea of file annotations and, (shock horror), Windows 95, for some other (of course minor and unimportant) inspirations.

This is a brief overview of the most prominent features of TkDesk:

  • Arbitrary number of automatically refreshed file browsers and file list windows,
  • Configurable file-specific popup-menus,
  • Drag and drop,
  • Files and directories may also be dropped onto the root window, a.k.a. desktop,
  • Configurable application bar, with several displays and cascaded popup menus for each button, files can also be dropped here,
  • History of visited directories, opened files, executed commands, and others, which is automatically saved to disk,
  • Find files through their annotation, name, contents, size, age, or other criteria,
  • A trash can for safe deletion of files and directories,
  • Calculation of disk usage for directory hierarchies,
  • All file operations (find, copy, disk usage, etc.) are carried out in the background,
  • Traversal of directory hierarchies through recursive cascaded menus,
  • Bookmarks, create menu entries for often used files/directories,
  • Comprehensive hypertextish online help,
  • Built-in multi-buffer and undo-capable editor,
  • Remote control of XEmacs,
  • Close coupling with Netscape Navigator for displaying HTML files or selected URLs,
  • Sound support,
  • Powerful on-the-fly configuration of nearly all aspect of TkDesk using Tcl/Tk, this also allows the Tcl-literate to extend TkDesk in arbitrary ways,
  • Free of charge! But see the file COPYING, or menu entry Help/License for information on usage and redistribution of TkDesk.

1.1 Acknowledgments

Christian Bolik writes:

TkDesk uses a number of other freely available packages without which TkDesk would not have been possible. I'd like to say many thanks to the following people:

  • Chris Sterritt for setting up and managing the TkDesk mailing list (at the old location majordomo@mrj.com
  • Alan V. Shackelford for seamlessly taking over the list to his machine and responsibility (the list is now at majordomo@shaknet.clark.net),
  • Ken Hornstein for his wonderful netscape-remote package,
  • Ioi Kim Lan for making an XPM image reader for Tk available,
  • George Howlett for his great BLT, of which parts are used by TkDesk,
  • Michael McLennan for his massively useful [incr tcl],
  • John Ousterhout for Tcl/Tk, which is definitely the best thing since sliced bread,
  • Greg Hankins and Matt Welsh for putting together the most wonderful linuxdoc-sgml package,
  • and of course, Linus Torvalds whose Linux kind of changed my life, really!

And a very big thank you to the growing TkDesk user community, which provides me with a constant flow of bug reports (getting less now :-)), suggestions for enhancements of TkDesk, and lots of motivation and encouragement.

Special thanks to Chuck Robey for revising a previous version of this guide.

1.2 Using TkDesk's Help System

If you check menu entry Options/Use Netscape for Help, TkDesk will use that for displaying this User's Guide on-line. Otherwise, to reduce overhead, TkDesk uses its own help system. It features hypertext links, context sensitivity (which is not yet fully utilised by TkDesk) and full text search.

The help window consists of four areas:

  1. A listbox listing all the section headings. A section can be selected by pressing the left mouse button,
  2. the text display, which contains the actual help text,
  3. a text entry field in which a regular expression may be entered (such as [Ff]eatures). After hitting Return, the whole help text is searched for this expression. Pressing Return again continues the search,
  4. three buttons: "Print" prints the complete help volume, "Back" jumps back after a hypertext link has been followed (see next paragraph), and "Close" to close the help window.

Text that is displayed blue in the help window is a hypertext link. When the left mouse button is clicked over such a link the display will automatically change to the referenced section. You can jump back by pressing the "Back" button described above.

The following keys are bound when the mouse pointer is inside the help window:

Tab

Moves to the next section.

Shift-Tab

Moves to the previous section.

Control-Tab

Moves to the first section.

Control-Shift-Tab

Moves to the last section.

Up, Down

Scrolls one line up/down.

Page up, Page down

Scrolls one page up/down.

Control-Home

Jumps to start of help text.

Control-End

Jumps to end of help text.

Meta/Alt-b

Equivalent to pressing the "Back" button.

Meta/Alt-c, Escape

Equivalent to pressing the "Close" button.

1.3 Command Line Options

Usually TkDesk is started simply by executing the command "tkdesk" from the shell prompt or your X initialisation file. However, you may specify the following options to this command:

-configdir dir

Reads the configuration from directory dir instead of ~/.tkdesk.

-default

Reads the default configuration of TkDesk instead of the user's one in ~/.tkdesk.

-iconic

Iconifies all file browser and file list windows created by TkDesk during start-up.

-layout file

Reads the window layout information from file file instead of the default ~/.tkdesk/_layout.

-startdir dir

If this option is given, the first file browser will open with directory dir.

-twm

Don't use icon windows when file browser or list windows are iconified. Some window managers liek twm cannot handle these properly.

For example, the command "tkdesk -twm -iconic" tells Tkdesk to not use icon windows and start with all windows iconified.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-10.html0100644000175000007640000002701310037101554015323 0ustar jccjcc TkDesk User's Guide: Frequently Asked Questions Next Previous Contents

10. Frequently Asked Questions

10.1 How can I change the position of the application bar?

The appbar should have a "handle" at the upper or left edge that can be used to drag the appbar around the screen by just pressing the left mouse button over it.

You can also drag the application bar around simply by holding down the Alt- or Meta-key and simultaneously pressing the left mouse button over the application bar. You can also invoke the "Move..." entry from the desk-button's popup menu to do the same without having to press Alt/Meta.

The application-bar configuration file, "AppBar", allows you to set a variable named tkdesk(appbar,wm_managed) which can be used to have the apllication bar managed by the window manager, although this is usually not necessary.

10.2 Can I have transparent icons?

No, but have a look at the answer to the next question. As far as I know to have transparent icons in X you need to make use of X11's SHAPE extension. Now as raw X programming is something only for the very brave, I didn't look into this any deeper yet. Any takers?

10.3 How can I change the background colour of the icons and desk items?

The background colour of icons used when the window manager iconifies a window can be set in the configuration file "System". The variable you are looking for is tkdesk(color,icon_background). By setting this variable to the same colour as your root window you can achieve the effect of transparent icons. You can define the colour either as a normal name (such as "grey", "blue") or in the form #rrggbb.

10.4 How can I have a different set of desk items on each virtual screen?

FVWM and similar or derived window managers

First, you have to set the variable tkdesk(desk_items,wm_managed) in the System config file to 1. Then you have to configure fvwm to not decorate windows of class dsk_DeskItem. For instance:

Style "dsk_DeskItem"    NoTitle, NoHandles, WindowListSkip, BorderWidth 0

CDE window manager

As for FVWM, you first have to set the variable tkdesk(desk_items,wm_managed) in the System config file to 1. To tell the CDE window manager (dtwm) to not decorate desk items you have to add the following line to either the file Dtwm or .Xdefaults in your home directory, and then restart dtwm:

Dtwm*dsk_DeskItem*clientDecoration:  none

10.5 How do I configure FVWM mini-icons for TkDesk?

For fvwm95's Style command (like fvwm2 from which it comes) you can use a variety of things to identify a tkdesk window - you don't have to use the name only; you can also use the window's class name or resource string. To find out what the window class and resource string are for a certain type of tkdesk window, use the FvwmIdent module (if you use Debian, this will be under the Window Managers->Modules menu; if you're using the default fvwm95rc that comes with the fvwm95 distribution, this will be under the Programs->Modules menu; I don't know about others).

Anyway, FvwmIdent tells me that the window opened by tkDesk's "Open Browser..." menu has a class name of dsk_FileViewer, so I open the FvwmConsole (or FvwmTalk) module, and enter the commands:

Style dsk_FileViewer TitleIcon mini-filemgr.xpm
Recapture

Then the browser window appears with the appropriate icon. To get the window opened by the "Open Directory..." menu, use: Style dsk_FileList TitleIcon mini-filemgr.xpm

(The "recapture" command is only necessary to apply the command to windows already open; it isn't necessary in your config. file) (Contributed by Daniel Martin, dtm12@jhunix.hcf.jhu.edu)

10.6 Configuring the appbar or popup menus to execute (export foo=bar; program;) doesn't work.

Yes, this is a bit tricky. What you need to do is the following:

dsk_exec sh -c {export foo=bar; program}

10.7 Composing characters doesn't work in TkDesk's editor.

Currently you have to edit the file cb_tools/bindings.tcl in TkDesk's library directory to make this work. Locate the two lines containing the word "XKB"; all you need to do is to comment out the following lines by prepending a '#'.

10.8 TkDesk's layout seems to be screwed. I can't get the appbar displayed anymore.

TkDesk saves all layout information in the file  /.tkdesk/_layout, so this is the place to look for bad lines. If in doubt you can safely delete this file and all should be back to normal.

In case of the appbar not showing up you can try the following (sent by Jochem Huhmann, joh@unidui.uni-duisburg.de):

  1. Quit tkdesk
  2. Open the file ".tkdesk/_layout" within your home directory with your favorite editor
  3. Delete the line looking like "Toplevel dsk_appbar 1 66x740+956+0"
  4. Write the file back to disk
  5. Restart tkdesk. It will place the AppBar according to its defaults now.
  6. Place the AppBar as you like (it doesn't like horizontal layout too much)
  7. Do a "TkDesk/Save All Now"

10.9 On my RedHat 5.x system the appbar clock shows the wrong time.

This is from Bryan Venable, spif@vousi.com:

This may have to do with the location of the zoneinfo directory. For libc5 I believe it's /usr/lib/zoneinfo, whereas for glibc it'd be /usr/share/zoneinfo. Try making a symbolic link from whichever you have to whichever you don't. There is a fix to Red Hat 5.0 which has to do with this, but in that situation the problem is with libc5 programs running on a system "optimized" for glibc.

And Raul Quiroga, quiroga@cartan.math.cinvestav.mx, also has got some advice for this:

Concerning the problem described below I received several suggestions. Some allowed to get the date in the tkdesk right but an incorrect one with the date command. Finally what I did is set the time with timeconfig to Universal with "Hardware clock set to GMT" checked; after a reboot both the appbar and date reported the correct time. Thanks all for your help.

10.10 TkDesk complains about "invalid command name wm" and won't start up

There seem to be two solutions to this problem: One is if you're running on a RedHad Linux 5.x system, the libc5 that's packaged and installed in /lib may be too old a version. If ls /lib/libc.so.5.* on your system gives something like /lib/libc.so.5.3.xx you should upgrade to at least 5.4.20. I think you can get a newer version of libc from sunsite.unc.edu in /pub/Linux/GCC.

The other solution that should always work is to do the following (thanks to Ike Hishikawa, ike@hishikawa.f.uunet.de, for this one):

Assuming that the tarball was unpacked under /usr/local,
open /usr/local/bin/tkdesk with your favorite editor.
At the very top of the file it should say:

  #!/bin/sh
  #-*- tcl -*- \
  PATH=/usr/local/bin:$PATH ;#\
  exec tkdesksh "$0" "$@"

After the 3rd line, insert two lines pointing to the location of
tcl/tk libs,  so that you get:

  #!/bin/sh
  #-*- tcl -*- \
  PATH=/usr/local/bin:$PATH ;#\
  export TCL_LIBRARY=/usr/local/lib/TkDesk/tcl_lib ;#\
  export TK_LIBRARY=/usr/local/lib/TkDesk/tk_lib ;#\
  exec tkdesksh "$0" "$@"

This did the trick for me :)
Hope this helps,

10.11 I cannot launch other Tcl/Tk applications from within TkDesk

Probably your version of TkDesk sets the environment variables TK_LIBRARY and TCL_LIBRARY, which confuses other Tcl/Tk apps. Unset these variables in the config files before the invocation of the problematic commands, eg. replace xterm with

        sh -c {unset TK_LIBRARY TCL_LIBRARY; xterm}

(contributed by Christoph Dalitz, dalitz@infotech.de)

10.12 I'd like TkDesk to do this and that. How can I achieve this?

The first place to start are the various configuration files of TkDesk. These can be accessed either by the "TkDesk/Configuration" menu of the file browser windows, or by the "Configuration" submenu of the popup menu of the very first button of the application bar of TkDesk.

Since TkDesk uses Tcl as the language for its configuration, and these configuration files are simply "source"ed, you could add any sort of Tcl proc for instance to the configuration file "System". This proc would then be available in every other configuration file as well. With the set of commands provided by TkDesk, which are listed e.g. in the configuration file "Popups", TkDesk provides a very powerful platform for the user who knows Tcl.

10.13 Is there a TkDesk mailing list?

There two mailing lists dedicated to TkDesk, one for discussing general installation/usage topics (tkdesk-users) and one targeted more for TkDesk-related development topics (tkdesk-code). You can subscribe to the tkdesk-users list here: https://lists.sourceforge.net/lists/listinfo/tkdesk-users. To post questions, etc., send email to tkdesk-users@lists.sourceforge.net.

It you are interested in contributing to the development of TkDesk, you can subscribe to the tkdesk-code list here: https://lists.sourceforge.net/lists/listinfo/tkdesk-code. Once you have subscribed, you can post using the address tkdesk-code@lists.sourceforge.net.

10.14 Where can I find out more about Tcl/Tk?

The official Tcl/Tk homepage is at http://www.tcl.tk/.

There is also a newsgroup dedicated to Tcl/Tk: comp.lang.tcl.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-11.html0100644000175000007640000000452510037101554015327 0ustar jccjcc TkDesk User's Guide: Tips and Tricks Next Previous Contents

11. Tips and Tricks

This section currently contains just one tip on how to combine TkDesk and XEmacs. If you have any procs or other stuff in your configuration file which you consider could be useful for others as well, or just think it's generally cool, please send me an email, so that I can add it to this section! Thanks!

11.1 TkDesk and XEmacs

If you are using XEmacs 19.12 or later you can couple TkDesk and XEmacs quite closely together by adding the following proc into any of your configuration files (I have it in "Popups"):


proc xemacs_load {what {where same}} {
    switch $where {
        "same" {
            exec gnudoit -q (find-file \"$what\")
        }
        "other" {
            exec gnudoit -q (find-file-other-window \"$what\")
        }
        "frame" {
            exec gnudoit -q (find-file-other-frame \"$what\")
        }
        "scratch" {
            exec gnudoit -q (switch-to-buffer-other-frame \"*scratch*\")
        }
    }
}

And now my generic popup menu for files matching * reads:


    {{*} {
        {{Edit} {dsk_edit %s}}
        {{XEmacs} {xemacs_load %s}}
        {{Other Window} {xemacs_load %s other}}
        {{Other Frame} {xemacs_load %s frame}}
        -
        {{Print} {dsk_print %s}}
    }}

This way you can load files from TkDesk into a running XEmacs! This assumes that you have the command gnudoit somewhere in your path, and have started the XEmacs server. This can be done by adding the following line to your ~/.emacs:

(gnuserv-start)


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-2.html0100644000175000007640000006502110037101554015245 0ustar jccjcc TkDesk User's Guide: The File Browser Window Next Previous Contents

2. The File Browser Window

The file browser window is the largest window when TkDesk is started for the first time. It contains a menu bar, a configurable button bar (for fast access to the most often used file operations and other functions), an entry field (displaying the current path), a horizontal scrollbar, a certain number of file listboxes (three by default), and a status and information bar.

2.1 The Menu Bar

The following sections describe the entries of the individual menus of the file browser windows.

TkDesk Menu

The "TkDesk" menu contains the following entries:

New Browser...

Asks for a directory for which to open a new file browser window. If the directory is the empty string, the operation is cancelled. By default the checkbox "In Browser" is checked, but if you uncheck it a file list window will be opened instead. The button to the right of the text entry field gives access to a menu containing previously visited directories.

Clone Window

Creates a new file browser window with the same directory as the current one (that is, the directory of the file browser from which this menu entry was invoked).

Display AppBar/Hide AppBar

Opens the application bar if it had been closed before, or hides (closes) it otherwise. This menu entry's label changes based on whether the AppBar is currently displayed or not.

Configuration

This is a submenu from which individual or all configuration files of TkDesk can be opened to modify them.

Auto Save

Here you can select which parts of TkDesk are automatically saved periodically and when TkDesk exits.

Save All Now

Saves all parts of TkDesk's configuration no matter what the settings in the previous submenu.

Close Window

Closes the file browser window.

Quit

Quits TkDesk.

File Menu

This menu provides all the usual file operations plus entries for finding files. The contained menu entries are:

Information

Opens a file information window for each currently selected file. This window, which can also be used to add annotations to files, is described in section File Information.

New File...

Asks for the name of a file which will be created in the current directory of the file browser or list. If "Open in Editor" is checked the new file will automatically be opened in the configured editor.

New Directory...

Asks for the name of a directory which will be created in the current directory of the file browser or list. If "Open after Creation" is checked the current window will switch to the new directory after it is created.

Open

Opens the selected files using their default action, which corresponds to the first action defined in their popup menus as configured in the "Popups" configuration file.

Print...

Asks for a command to print the currently selected files. The names of the selected files will be appended to the command. The default command can be defined in the configuration file "System".

Copy, Move, Link...

Opens a dialog box for copying, moving, linking, symbolic linking etc. of files. This dialog window is described in more detail in section Copying, Moving and Deleting Files.

Rename...

For each selected file, TkDesk asks for a new name. Before actually renaming TkDesk checks for an existing file with the same name.

Delete...

Opens a dialog to delete files. The section Copying, Moving and Deleting Files gives more details.

Find Files...

Opens a dialog window which can be used to search for files, using a variety of characteristics. This dialog is described in more detail in section Finding Files.

Find Annotation...

Lets you search for files with a certain annotation. More details in section Finding Files.

Copy To X Selection

Copies the complete names of all currently selected files (i.e. including their paths) to the X clipboard. They can then be pasted into any other X application using the middle mouse button.

Copy Names Only

Same as previous one, but copies only the files' names (i.e. not their paths.)

History

Displays a submenu containing recently opened files.

Close Window

Closes the window.

Directory Menu

The main purpose of this menu is to select directories which are to be opened. It also manages TkDesk's trash can. The menu contains the following entries:

Open...

Asks for a directory. A new file list or file browser window will be created, baesed on the setting of the "In Browser" checkbutton.

New...

Asks for the name of a directory which will be created in the current directory of the file browser or list.

Home Directory

Changes the directory of the window from which this entry is invoked to the user's home directory.

A number of path entries

These can be configured in the configuration file Directories. See section Configuration of TkDesk for details on how to do this. If one of these menu entries is invoked, the path of the file browser will change to that directory. If such an entry is invoked while at the same time pressing the "Control" key, a new file list window will be created (if the option "Always In Browser" is selected a file browser window will be created), displaying the contents of the selected directory. This feature applies to all menus that contain directory names!

Trees

Contains two cascaded submenus: "Home" and "Root". The contents of these submenus is dynamically generated and corresponds to the directory hierarchy rooted either at the user's home directory or at "/". Selecting an entry from these submenus changes the directory of the window to the selected directory. Pressing Control at the same time opens a new window with this directory.

Open Trash Can

Opens a file list window displaying the contents of the trash can. When this window is iconified and TkDesk uses icon windows the icon can be used as a Mac-like trash can.

Empty Trash Can

Empties the trash can after confirmation from the user. This erases the files contained in the trash can for good!

History

Displays a submenu containing recently opened directories.

Commands Menu

This menu provides entries to execute commands either once or periodically, edit files, and provides some very basic form of "job control". Its entries are:

Execute...

Asks for a command to execute. The button to the right of the command entry contains a history of previously executed commands. Selecting one of these copies its name to the entry widget. Checking the "View Output" button will open an editor window after the command completed displaying its output (stdout and stderr).

Execute as root...

The same as the previous entry but TkDesk will ask you for the root password before executing the command. The command will then run under root permission.

Periodic Execution...

Opens a window which can be used to execute a command and watch its output periodically. If the "Don't execute" checkbutton is selected, the execution is paused.

Job Control

Opens a window which allows to stop, terminate, kill etc. processes which have been started by TkDesk.

Environment...

Opens the "Edit Environment" dialog which can be used to display and modify TkDesk's environment variables. Programs and commands started from TkDesk after any modification in this dialog has been made will pick up the modified environment.

Edit File...

Asks for the name of a file to edit, and opens it in the editor you selected in the "System" configuration file (defaults to the built-in editor). The "Browse..." button opens a file selector from which a file may be selected.

New File

Opens a new blank editor window.

Edit Selected

Opens all currently selected files in one new editor window.

Buffers

Displays a submenu of all current editor buffers (both files and command output).

A number of custom command entries

These can be defined in the configuration file Commands. See section Configuration of TkDesk for details on how to do this. If one of these menu entries is invoked, the corresponding command will be executed.

History

Displays a submenu containing commands recently executed.

Bookmarks Menu

This menu lets you create bookmarks for often used files and directories. To do this, select at least one file/directory and invoke the "Add Bookmark" menu entry. The name(s) of the selected files and directories will now appear alphabetically sorted in the "Bookmarks" menu. You can remove any bookmark from the menu by selecting its corresponding entry from the "Remove Bookmark" submenu.

The bookmarks you add will be automatically saved, if the "Bookmarks" entry of the "Auto Save" submenu contained in the "TkDesk" menu is selected (which is the default).

Options Menu

The Options menu lets you configure many aspects of TkDesk "on the fly". The entries are:

Long Listing

Select this to let the details of all files and directories be displayed in the file listboxes. This slightly decreases performance and for that reason is switched off by default.

Show All Files

Whether to show files in the file lists whose name start with ".".

Add Icons

If selected, small icons will be displayed to the left of the file names in the file listboxes. Turn this off for faster scrolling.

Folders On Top

If selected (default) folders will always appear on top of the file lists.

Append Type Char

Whether to append a file-type specific character to the file names. This is mainly intended for monochrome displays and is then automatically selected.

Single Click (Dirs)

Lets you open directories with a single click of the left mouse button. Individual directories can still be selected by pressing Control at the same time.

Always In Browser

If this option is selected, the "In Browser" checkbutton of the "Open Directory" dialog will always be selected. Control-Doubleclick on a directory will open a file browser instead of a file list window.

Status in List Windows

Whether the singly columned file list windows should have a status bar as well.

Sort by ...

Sort all file listboxes by one of these criteria: Name, Name (fold, meaning that upper and lower case characters will be "folded", i.e. disregard case when sorting), Size, Date, Extension, or Don't Sort to display directory entries as they are read from disk.

Strip <your home directory>

If this is selected, and the current path of the browser is somewhere under your home directory, the leftmost file listbox will contain your home directory rather than the root directory. This speeds up the display of directory hierarchies under your home directory.

Overwrite Always

If this option is selected, TkDesk won't ask if the destination file already exists when copying or moving files.

Really Delete

Relates to the deletion of files. If this option is selected, the "Delete Files" dialog box will always have the "Delete permanently" checkbutton selected by default.

Quick Drag'n'Drop

Normally when you drop files onto a file list, the copy dialog appears. If this option is selected, dropped files will be moved to the destination directory without further questions. If Control is pressed during dropping, the files will be copied. If the drop target is the trash can, TkDesk will ask if the files are to be deleted "really".

Sort History

This option determines whether the history menus are to be sorted alphabetically or not.

TkDesk Server

Start or stop the built-in server to remote execute TkDesk commands. See also section Using the TkDesk Server.

Dialogs at Pointer

If selected, TkDesk will always try to place new windows right under the mouse pointer.

Autoraise AppBar

Whether to raise the AppBar above any obscuring window when it is entered by the mouse pointer.

Use Sound

If you have sound working with TkDesk on your machine, you can temporarily disable sound by selecting this option. Handy when playing Audio CDROMs, for instance.

Number Of Listboxes

This relates to the number of listboxes in the file browser window. Between 1 and 6 can be displayed, though 18 would theoratically not be a problem.

Balloon Help

Whether to display a small window at the mouse pointer if it is placed over part of TkDesk, e.g. an AppBar button.

Use Netscape for Help

Whether to use Netscape rather than the built-in help viewer for displaying TkDesk online help.

The settings of these options are by default automatically saved to ~/.tkdesk/_options. This can be disabled by deselecting the "Options" entry of the "TkDesk/Auto Save" submenu.

Help Menu

This menu tries to give you some help with using TkDesk. It also contains entries for displaying the list of FAQs, recently made changes to TkDesk, and the license for using and distributing TkDesk:

User's Guide

Displays the TkDesk User's Guide either in the built-in help viewer or using Netscape (see previous section).

Manual Page...

Asks for a command for which to display the system's manual page in an editor window.

Getting Started

Displays the "Getting Started" guide.

TkDesk FAQ

Jumps directly to the FAQ section of the TkDesk help volume.

Changes

Displays recent changes to TkDesk. Make sure you invoke this entry after upgrading to a newer version of TkDesk.

License

Restates that TkDesk is distributed under the GNU Public License (GPL).

About TkDesk...

Some info about TkDesk (version, author etc.), plus a link to the TkDesk web page.

2.2 The Button Bar

The button bar that's located right underneath the menu bar allows for fast access to any of the commands of TkDesk. It's also possible to configure each button individually to run customized Tcl scripts, that e.g. work on the currently selected files. By default, the buttons in the button bar provide access to the more often used functions of TkDesk (copy, create, delete etc.). When you place the mouse button over any of these buttons, a small help window will appear telling you what this button does (provided the "Balloon Help" option is activated).

The contents of this button bar can be defined via the configuration file ButtonBar. See section Configuration of TkDesk and the configuration file itself for details on how to do this. Note that by not defining the variable tkdesk(button_bar) in that file or by not defining tkdesk(small_button_bar) the button for file viewer or list windows may be surpressed respectively.

2.3 The Path Entry

This entry field displays the current path of the file browser or file list. You can also type directly into this field to change the display to another directory. There is a sort of "auto-completion" available; if you type only the first part of a directory's name and press Control-Tab, its name will be automatically completed as far as there's no ambiguity.

If you click the right mouse button over this field a popup menu appears which lets you select any of the current directory's parent directories and their subdirectories. You can also open files directly from this popup menu.

The button to the right of the entry field contains a menu of the last 30 (default value this may be changed in the "System" configuration file) directories you have visited. If you select one of these, the current path of the browser will change to it. The "Control-trick" to open the selected path in a new list window that's been described in section Directories works here as well!

2.4 The File Listboxes

The main part of the file browser and list windows is taken up by the file listboxes. Directories are by default displayed in blue with a bold font, executable files in red and bold, and regular files in black and a medium font. These settings as well as the color, font and icons used for individual file types may be configured via the FileTags configuration file. See section Configuration of TkDesk for details on how to do this.

Right above each listbox the name of the directory whose contents it shows is displayed, together with the current file mask settings. Clicking on this label reveals a menu with the following entries:

Refresh

Refreshes the listbox contents. Although TkDesk refreshes all its file lists every 5 seconds (default), sometimes you may want to have a file listbox instantaneously updated.

Set Mask...

Sets a mask for this listbox's display. For example, you can configure the listbox to only display or select files matching the pattern *.gif. Multiple masks separated by spaces may be entered to display files matching any of these. A history menu button for reusing masks previously entered is also provided, as well as a checkbutton for inverting the mask's effect (i.e. only files not matching the given mask will be displayed).

No Mask

Removes a previously activated file mask.

Disk Usage

Calculates the directory's disk usage, i.e. the number of kilobytes (default) occupied by that directory and all its subdirectories. A window will appear when the calculation is complete, containing a sorted list of all subdirectories and their individual disk usages. If you double-click on any of its entries, a new file list window will appear displaying the directory's contents. Clicking the right mouse button over any of the directories displays its associated popup menu.

Free Space

Displays the space available in the file system of this listbox's directory, using TkDesk's "Periodic Execution" window.

Execute here...

Asks for a command to execute in the directory displayed by this listbox. All the usual TkDesk %-sequences to work with the names of selected files may be used, e.g. %A to pass all currently selected files to the command.

Execute as root...

The same as the previous one, except the command will be run under root permission after asking you for the root password (and you entered the correct one...).

Long Listing

Instead of just the file names displays a "long listing" i.e. a listing containing all the details such as size, permission bits, date and time of last modification etc.

Show All Files

Whether to display files starting with a dot (".") or not.

Inverse Order

Whether to invert the current sorting order.

Sort by...

This is identical to the entry of the same name in the menu bar's "Options" menu, but affects this single listbox only.

Open List Window

If this listbox is part of a file browser window, open a new list window displaying this listbox's directory.

Open Browser

Else, open a browser with this directory.

This menubutton can also be used to drag and drop files, as well as to drag the directory displayed in the associated listbox to another target, e.g. the root window by pressing the middle mouse button over the listbox title menu button.

Handling of the Listboxes (Bindings)

The handling of the file browser windows is very similar to that of the NeXT file manager: when you double click on a directory, the listbox to the right of the current one will display this directory's contents. You can open a directory in this listbox, and the listbox to the right of this one will display its contents. This way you can browse through complete directory hierarchies, while having instant access to the contents of the complete directory tree. When the number of opened directories exceeds the number of visible listboxes, you can scroll the listboxes with the horizontal scrollbar which is located right above the file listboxes.

Files are selected in the file listboxes using the left mouse button. A single click selects a single file, deselecting all other files. If the Control key is pressed simultaneously, the old selection will be retained. By dragging the mouse pointer over a file list, while at the same time pressing the left mouse button, a set of files can be selected. Shift-doubleclick selects all files in that file list. Holding down the shift button extends the current selection unto that file (a.k.a. "range-selection").

Note that if the option "Single Click (Dirs)" is set in the "Options" menu a single click on a directory will suffice open it. In this case you need to use Control-Click to select a single directory.

A double click on any listbox item performs its default action. For directories this is to open it, for executables this is to execute it, and for files it invokes the first entry of the corresponding popup menu as defined in the "Popups" configuration file (see below). If the Control key is pressed while double-clicking a directory, a new file list or file browser window (depending on the setting of the "Always In Browser" option) is created displaying the contents of that directory. For files, a dialog box will appear asking for a command to execute on that file.

Files can be dragged by pressing the middle mouse button over any selected file. If no file is selected, the clicked-over file will be selected. Files can be dropped by releasing the mouse button over any other file listbox and the menubutton above them, over windows of the built-in editor, over the application bar (if the corresponding button has been configured to handle dropped files), over iconified file browsers and file lists and in general over any other application supporting the file protocol of the BLT package's drag and drop implementation. Valid drop targets can be identified by looking at the window that is displayed while files are dragged: the window's relief appears raised if over a valid target, flat if not.

The right mouse button is used in the file listboxes to access the file-specific popup menu. Every popup menu contains a submenu, labeled with the file's name, which contains entries for the most common file operations. The remaining entries of the menu can be configured via the configuration file Popups. See section Configuration of TkDesk for details on how to do this.

The file listboxes may also be fully controlled using the keyboard:

Up-/Down Arrow

Move the selection one file up/down.

Page Up/Down

Move the selection one page up/down.

Home/End

Move the selection to first/last item in the listbox.

A-Z

Select the first file matching the character pressed, cycle through subsequent matches on repeated pressing of the same key.

Return

Open selected file.

Menu (the key left to the right Control key on W95 keyboards)

Display the currently selected file's popup menu, the menu itself may also be operated using Up/Down, Return, and Esc.

Backspace

Change directory to the parent directory.

F11

Open the current file listbox's menu.

2.5 The Status Bar

The status bar is located at the bottom edge of file browser windows. If "Status in List Windows" is selected in the "Options" menu file list windows will contain a status bar as well.

It displays either

  • the current state of TkDesk,
  • the amount of free space in the current file system,
  • details about the currently selected file, or
  • number and total size of currently selected files.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-3.html0100644000175000007640000000267410037101554015253 0ustar jccjcc TkDesk User's Guide: The File List Window Next Previous Contents

3. The File List Window

The file list window is basically a more compact version of the file browser window. It displays only one listbox, which results in the fact that the contents of opened directories are always displayed in the same listbox, and it doesn't have a status bar unless "Status in List Windows" is set in the "Options" menu. Its advantage is that it is more quickly created than a file browser window and occupies less space on your screen. My usual setup is that I have one file browser and lots of file lists opened.

The menu bar of the file list windows is also more compact, but allows access to all menus of the file browser through submenus of the "Others" menu. This menu contains one additional entry to open a file browser window displaying this file list's directory.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-4.html0100644000175000007640000001466510037101554015257 0ustar jccjcc TkDesk User's Guide: File Operations Next Previous Contents

4. File Operations

4.1 File Information

The "File Information" window displays detailed information about a file. It can also be used to change the owner or access-permissions of a file. In addition, an annotation to a file can be added here.

The following information is displayed:

Path

The path of the file.

Size

The size of the file in bytes.

Modified

When the file was last modified. By clicking on the button displaying the date file can be "touched", ie. their modification timestamp will be set to the current time.

Owner

Displays the owner of the file. By clicking the button displaying the owner, the owner can be changed to another user.

Group

Displays the group ownership of the file. By clicking the button displaying the group's name, the group can be changed.

Mode

Here the access permissions are displayed in "ls -l" style. The first three button correspond to the rights of the owner (r: may read, w: may write, x: may execute or open the directory), the second three buttons to the rights of the group, and the last three buttons to every one else's rights. The "x" buttons are special in that they cycle through four settings: x for executable, s for set user/group id and executable, S for set id only, and - for not executable. Note that using the right mouse button toggles between - and x only. Clicking the middle mouse button over any of these buttons copies its current setting to the corresponding button in the other two groups. If the settings of any of these buttons is changed, the "Change Mode" button at the bottom edge of the window must be clicked to actually change the file's permissions.

Links

Number of (hard) links to this file.

Type

Tries to give some information about the file's contents. TkDesk uses the shell command file for this.

In the "Annotation" text field you can enter any remarks about the file. This annotation will be saved when the "Close" button is pressed. Files having annotations attached to them will appear underlined in the file listboxes, and their popup menu will contain the first few words of that annotation as an "informative" menu entry.

If the window displays information about a file an additional button labeled "Disk Usage" is provided, which calculates the disk usage of the hierarchy rooted at that directory. The entries of the "Disk Usage" window can be double-clicked to open new file list windows.

4.2 Copying, Moving and Deleting Files

These functions can be accessed from the "File" menu, among others. There is one dialog for copying, moving and linking files (plus an arbitrary number of user-defined actions such as diff and patch, as configured in the "Popups" config file), one dialog for renaming files (which does nothing else than moving the file to its new name), and one dialog for deleting files.

The "Rename" dialog is very straight-forward and probably does not need further explanation. The "Copy etc." dialog contains the obvious source and destination entry fields, plus a checkbutton labeled "all selected files" if more that one file was selected when this dialog was opened. If this checkbutton is selected (default) the operation will be applied on all selected files. If it is not checked, each file will be handled individually. The "Skip" button can then be used for skipping individual files.

The "Delete" dialog also contains this checkbutton, plus a checkbutton labeled "Delete permanently". If this checkbutton is selected, the files will not be moved to the trash can but will be really and ultimately deleted!! The default setting of this checkbutton can be set from the "Options" menu.

4.3 Finding Files

The "File" menu contains two entries for finding files: "Find Files..." and "Find Annotation...". Files can be annotated through their "Info" dialog (accessible from the "File" menu or from their popup menu), see section File Information.

"Find Annotation" enables you to look for an annotated file whose annotation matches a certain regular expression (which can be as simple as an ordinary string).

"Find Files" lets you look for files (or rather: let's you instruct TkDesk to look for files) whose names match one or multiple patterns, which are of a certain type (such as directory), which contain a certain string, which are smaller or bigger than a certain number of kilobytes or which are younger/older than a certain date. All fields which are not left blank in this dialog will be combined with a logical AND. This dialog is currently the only one utilizing the balloon help capability of TkDesk, so for now I would like to refer you to this. For instance, if you want to know how to enter the file size you're looking for, place the mouse pointer over the "Size" entry field without moving it for a few seconds.

Both "Find" dialogs display their results in the same file listboxes that are used by the file list and browser windows, so the same bindings described in section Handling of the Listboxes (Bindings) apply here as well!


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-5.html0100644000175000007640000000412610037101554015247 0ustar jccjcc TkDesk User's Guide: Cascading Directory Popup Menus Next Previous Contents

5. Cascading Directory Popup Menus

Ugh, what a title... However, one of the most powerful features of TkDesk are its "cascading directory popup menus." These are menus that start from a given directory, contain submenus for each subdirectories (and possibly menu entries for files), and that dynamically add submenus as you traverse the menu. Hm, kinda hard to explain, but pretty cool. These menus are the ones that are accessible through menu entries "Directory/ Trees/ Home" and "Root", by clicking the right mouse button in the file browser and list window's path entry field, and through the popup menus of directories (submenu "Subdirectories" and "... and Files"). They may also appear in other places, depending on your configuration of TkDesk.

There are two special bindings available in these menus:

Control-Mousebutton-Release

On a menu entry associated with a directory this opens a new file list window displaying this directory's contents. On a file it asks for a command to run on this file by displaying the "Open or Execute" dialog.

Left-and-Right-Mousebutton-Press

If the left and right mousebuttons are pressed simultaneously over a menu entry, the menu disappear, the popup menu for the associated file or directory is displayed.

Otherwise the directory or file that's selected from the menu is opened using its default command.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-6.html0100644000175000007640000000523510037101554015252 0ustar jccjcc TkDesk User's Guide: The Application Bar Next Previous Contents

6. The Application Bar

TkDesk provides you with an application bar for fast access to your favorite applications, commands, directories etc., plus displays for the current date and time, system load, and the status of your mailbox and dialup link. It consists of an arbitrary number of buttons. Each button (also the aforementioned displays) contains a popup menu which can be accessed by pressing the right mouse button over any of them. If you single-click the left mouse button over such a button, the first entry from the corresponding popup menu defining a command to execute will be invoked.

The first button (displaying a small desk by default) could be called TkDesk's "Start Button". Its popup menu contains entries for accessing TkDesk's most often used functions, such as executing a command or opening a file list or browser window, plus submenus for your bookmarks, the files you most recently opened, and the directories you've last visited. The next entry is a submenu labeled "Application Bar". Here you can configure all aspects of the application bar, especially its position and orientation. See my answer to the MFAQ How can I change the position of the application bar? for more. The last entry labeled "Configuration" contains another submenu which gives you fast access to TkDesk's configuration files.

The second button gives you access to this User's Guide, and lets you view manual pages, also making use of a running TkMan (which is a hypertextified manual pager which is to be highly recommended). This button also allows you to drop executables on it to automatically display their manual page, if they have got one. Note that the default value of the "Manual Page" dialog is the contents of the current X selection, so this can be used for some sort of "context sensitive" help.

Keep in mind that all the buttons can be configured by you! See section Configuration of TkDesk for details on how to do this.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-7.html0100644000175000007640000000715710037101554015260 0ustar jccjcc TkDesk User's Guide: The Built-in Editor Next Previous Contents

7. The Built-in Editor

The built-in editor of TkDesk is meant to be a simple ASCII editor for editing files smaller than, say, 500kB. A single editor window can handle an arbitrary number of buffers each of which contains another file. Files can either be loaded through the "File" menu of the editor or by simply dropping one or more files on the text field of the editor window from one of the file listboxes.

An editor window contains the following menus in its menu bar:

File

Contains entries to load, save, and print files, and to close the current buffer, current window, or all windows.

Edit

The first entry provides access to the editor's "Undo" functionality. Note that the undo buffer may contain a maximum of 500 events. This menu also contains entries for managing TkDesk's own text clipboard. Also provides entries to search for text (regular expressions), replace text, or find the next occurence of the currently selected text. The entry "HyperSearch" is a special form of search: all matching lines are displayed in a file listbox. If one of its entries is clicked on, the editor automatically displays that line at the top of the text field. This can be useful for jumping to headings, function definitions etc. Oh yes, and the regular expressions entered here are also saved with the other histories.

Options

The "Auto Indent" option determines whether the cursor is automatically indented after hitting Return. If the "Send to Netscape" option is set, the current file will be loaded by (maybe a running) Netscape each time it is saved. Useful when editing HTML files.

Buffers

Lists the buffers of the editor window. Buffers can be selected from this menu.

Configuration

This menu is only available when editing one of TkDesk's configuration files. It lets you save the file and reload it into TkDesk by selecting the corresponding menu entry or pressing F5, or exactly the same plus closing the buffer by invoking the next entry or pressing F6. This way configuration files can be edited and reloaded very quickly into TkDesk.

The text area provides all the more common Motif- and Emacs-like key-bindings (including Control-Space for selecting an area of the text). Maybe it should be mentioned that Control-C/X/V do not work on the X selection but on TkDesk's own clipboard. Use the middle mouse button to paste from the X selection into TkDesk or from TkDesk into another application.

7.1 Marks

Marks can be set with Control-[1-9], and be jumped to with Alt/Meta-[1-9]. You can always jump back with Control-0. They work across buffer and editor window boundaries, and are currently only valid as long as the buffer in which the mark was set is open.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-8.html0100644000175000007640000001014710037101554015252 0ustar jccjcc TkDesk User's Guide: Using the TkDesk Server Next Previous Contents

8. Using the TkDesk Server

In order to allow remote control of TkDesk, i.e. to allow programs outside of TkDesk to have TkDesk perform certain tasks, TkDesk implements a TCP/IP server that can be used to send Tcl scripts to TkDesk. Whether this server is "up" or not is determined by the option "TkDesk Server" that is to be found in the "Options" menu. Currently, only programs running on the same machine as TkDesk may connect to the server, but multiple TkDesk servers started from different users may be running at the same time.

The server is a much faster way to send command to TkDesk then to use the Tcl/Tk send command, as that requires a second Tcl/Tk shell ("wish") to be started to do the send. However, using a TCP/IP server that's basically system-wide accessible to perform arbitrary command under the accout of the user who started TkDesk and thus the server brings with quite a big security risk that TkDesk tries to reduce by keeping the port the TkDesk server uses secret. The port to be used is calculated randomly at server start-up, and is saved into a file that can only be read by the user who started TkDesk. To prevent "guessing" of the port number a generated key is also stored in this file that get passed to the server.

The client performing the communication with the TkDesk server gets installed with TkDesk; its name is tkdeskclient. This command expects exactly one argument which will be directly sent to the server and evaluated there as a Tcl script. E.g. you could do a

        tkdeskclient "dsk_view id"
to find out who you are :-).

Along with TkDesk a number of front-end shell scripts for tkdeskclient are installed, which comprise of the following:

cd-tkdesk ?path?

Let's the currently active TkDesk file list or browser window (i.e. the one the mouse pointer was over last) change its directory to path if path is given, or returns the current directory of the active window.

ed-tkdesk ?+linenum? ?file? ...

Without argument opens a new editor window, or loads all files given as arguments into the editor (and into the same window if you're using a multi-buffer capable editor, such as the built-in one). If a file is preceded by +linenum, i.e. something like +20, the built-in editor will position the cursor on the line when displaying the file that's given in the following argument.

od-tkdesk ?dir?

If no arguments are given opens a file list window for the current directory of the shell/program where the command was issued, or opens a window for the directory specied in dir.

op-tkdesk ?file ...?

For each file performs its default action (the one defined first in its corresponding popup menu as defined in "Popups"), or asks for a command to execute if no files are given.

pop-tkdesk file

Displays the popup menu for file, as defined in the "Popups" config file. The popup menu may also be controlled by the keyboard: Up, Down, Return, and Esc keys do what you would expect.

Note that all of these scripts are merely examples of possible usages of tkdeskclient. Use your imagination to find new and even more exciting applications! :-)


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide-9.html0100644000175000007640000004420210037101554015252 0ustar jccjcc TkDesk User's Guide: Configuration of TkDesk Next Previous Contents

9. Configuration of TkDesk

Currently, TkDesk can be configured only by editing ASCII files. This is not necessarily a drawback, because this way you can add complex Tcl/Tk procedures to the configuration files. GUI based configuration is planned for one of the next releases.

If you don't know Tcl/Tk: Don't despair! For the biggest part of TkDesk's configuration files it is absolutely not necessary for you to know how to program in Tcl/Tk, since you just have to modify values or extend the examples I and others have provided. And to those who want to exploit all of the power available by using Tcl/Tk as TkDesk's configuration language, please have a look at the answer to FAQ Where can I find out more about Tcl/Tk?.

The configuration files can be accessed from the TkDesk menu via the Configuration submenu, or from the "Configuration" submenu of the popup menu of the application bar's first button. The built-in editor will then appear with the selected configuration file(s) already loaded. Each configuration file contains pretty detailed comments on what the individual settings are for, and how they can be modified. Well, at least they contain examples, which can guide your modification attempts. Once you have modified the configuration file, you can save it and reload it into TkDesk by making use of the entries of the editor's "Configuration" menu described in section The Built-in Editor. Use F5 to save the file and reload it into TkDesk, and F6 to do the same but additionally close the configuration file.

As already mentioned, each configuration file contains directions on how to modify its definitions. The general advice is: Simply look at the example definitions, because these make things a great deal clearer than any explanation can.

Tip: In case your looking for the definition of a specific part of TkDesk, e.g. for the popup menu of pdf files, use the "Find..." entry in the "Configuration" menu. Just enter pdf in the "String:" field, hit enter, and double-click the found file.

9.1 Configuration Files

All of these are "sourced" (i.e. processed) by TkDesk during start-up. This happens in the following order:

  1. System
  2. ButtonBar
  3. Preferences
  4. FileTags
  5. Commands
  6. Directories
  7. Popups
  8. AppBar
  9. Sounds
  10. Local (if existent)

AppBar

This configuration file lets you define all aspects of the application bar (apart from its layout, use the entries of the submenu "Application Bar" of the desk-button's popup menu for this). This is what you can define here:

  • If the application bar should be managed by the window manager just as an ordinary window. This is disabled by default.
  • The maximum number of buttons in a column (vertical layout) or row (horizontal layout). Yes, the application bar can contain several columns or rows!
  • The fonts to use for the time and date display.
  • Period in seconds after which to update the system load and mailbox displays, where to check for new mail, plus several additional mail related options.
  • Icons and commands to use for the dial-up networking button.
  • Icons and fonts to use for the trash button, and whether to display its current disk usage in the appbar.
  • The actual button definitions, ie. which image to display, and the entries of the (possibly cascaded) popup menu. See below for the syntax used.

ButtonBar

This file defines the contents and appearance of the Button Bar, which is displayed underneath the menu bar of TkDesk's file browser (tkdesk(button_bar)) or file list windows (tkdesk(small_button_bar). Both button bars are configured independently from one another.

The third list defined here (tkdesk(dir_button_bar)) is for directory-specific button bars which will be dynamically displayed and hidden when the respective directory is entered or left. These will be displayed in addition to the standard button bar.

Commands

In this file you can define entries which will be added to TkDesk's "Command" menu, for example to uncompress all selected files, manage RPM package files, or do whatever you can possibly imagine.

Directories

Here you can define entries and cascaded submenus of directories which will be added to TkDesk's "Directories" menu. More details are contained in the "Directories" file itself.

You can also define a list of directories whose parent directories are not to be displayed in file browser windows, as is usually the case with your home directory. This can speed up display of directories underneath an AFS directory, for instance.

Another list lets you define action that are to be performed when a specific directory is opened. This can be used for example to auto-mount floppies and CDROM's.

FileTags

Contains definitions for color and font of specific file types (as distinguished by their extension) as well as standard values for regular files, executables, and directories, that will be used in TkDesk's file lists. Also the icons displayed when the "Add Icons" option is selected are configured here, as well as the icons used for the desk items, i.e. files and directories which have been dropped on the root window.

Local

This file does not exist when TkDesk gets installed, but still appears in the "Configuration" menu. This is because if it exists it will be sourced during start-up of TkDesk, so this is the ideal place to put local extensions of TkDesk.

Popups

The popup menus that appear when the right mouse button is clicked over a file or directory are defined here. There are individual definitions for the popup menus of directories, executables, and of other files. To find the correct popup menu for a file TkDesk checks the mask of each entry of a popup list one after the other from the beginning of the list to its end, so the masks should become more general towards the end of the list.

An additional popup list (tkdesk(fileops,popup)) may be defined here for use in the "Copy, Move, etc." dialog.

Sounds

Here the command to be used for playing sounds can be defined, as well as the sound files which are to be played at certain events. TkDesk comes with a few AU sound files which are located in the sounds subdirectory of TkDesk's library directory. You can find out where this is by looking at the fifth line of the tkdesk script.

System

All the more "basic" features of TkDesk can be configured here. These are:

  • Colours and fonts to be used by TkDesk,
  • Default size of file listboxes,
  • Definitions of shell commands which are used by TkDesk to copy, move, delete, etc. files. These should be fine as they are in most cases, but you may want to tweak them for your system, so take a look at these definitions.
  • The default command for printing,
  • Which editor to use, plus settings for the built-in editor,
  • Periods for automatic refreshing of the file lists and saving of TkDesk's configuration,
  • Maximum number of entries in history menus,
  • If TkDesk should ask for confirmation when the user wants to quit TkDesk,
  • If TkDesk should allow the menus to be "tearoff-able",
  • The paths in which TkDesk will look for images and sounds used in the configuration files,
  • Icons to use for file list and browser windows, and for the help window,
  • Whether to use "focus follows mouse" default or not,
  • Settings for the desk items, e.g. whether to let them be managed by the window manager,
  • Commands to execute after start-up and before shutdown (of TkDesk).

9.2 The TkDesk API

Many of the values to be specified for TkDesk's configuration files are Tcl scripts. Also, you can define your own Tcl proc's to use in these scripts. To allow better control of TkDesk, TkDesk externalizes a couple of proc's which could be called the "TkDesk API" (although currently they aren't much more then a random set of more or less useful proc's, but anyway).

The proc's I currently think are part of the API are the following:

dsk_msg_alert msg

Display msg in an alert dialog box.

dsk_msg_error msg

Display msg in an error dialog box.

dsk_msg_info msg

Display msg in an info dialog box.

dsk_confirm msg script

Display msg in a dialog box with "OK" and "Cancel" buttons. Evaluate script if user pressed "OK".

dsk_debug msg

If TkDesk runs in development mode (e.g. by setting the -devel command line option) print msg to stderr.

dsk_ask_dir

Ask for a directory to open.

dsk_ask_exec

Ask for a command to execute or file to open.

dsk_exec command

Execute command in the background. When the command completes its exit code will be displayed in the status bar of all file browser windows.

dsk_exec_as_root command

Asks for the root password, and executes command in the backgroud under root permission if it was okay.

dsk_view command

Execute command in the background and displays its output (stdout and stderr) in a built-in editor window.

dsk_view_as_root command

Same as dsk_view but asks for the root password first.

dsk_path_exec path command

Executes command in path and in the background.

dsk_path_view

Executes command in path and in the background, and displays its output in an editor window.

dsk_edit ?+linenum? ?file? ...

Without arguments asks for a file to open, or opens all files in the same editor window, positioning the cursor at line linenum if given. If file is "New File" a black editor window will be opened.

dsk_busy

Displays mouse as "busy" and locks all TkDesk window from receiving mouse events.

dsk_lazy

Displays mouse as "lazy", and makes all TkDesk windows responsive again.

dsk_active what

Returns information about the currently active file browser or list window: If what is "dir" it returns its directory; if it is "sel" it returns all selected items as a Tcl list; if it is "win" it returns the Tk name of the active window.

dsk_select X ?names?

If names is not given, copies the full path names of the files selected in the currently active file browser or list window to the X selection. Otherwise just their names are copied.

dsk_read_string msg ?script? ?dontpaste?

Displays $msg in a dialog box where the user can enter an arbitrary string. If script is not given, the string will be returned; if it is given the variable dsk_read_string will be set to the user's input and script will be evaluated. If "dontpaste" is passed as a third argument to this command the current X selection will not be pasted into the entry field.

dsk_filesel message path

Opens TkDesk file selection dialog using message as the label, and presetting the entry field with path. Path is also used as the filter for the full file selection box.

dsk_refresh ?file ...?

For each file all windows displaying the corresponding directory is refreshed. File may also be a directory.

dsk_sound sound

Plays sound if a sound command has been defined and sound is switched on. Sound is the second index in the tkdesk(sound,xxx) array as defined in the "Sounds" config file ("xxx" in this case).

dsk_copy ?source ...? ?dest?

Opens the "Copy, Move, etc." dialog box, filling in "Source" and "Destination" fields if provided.

dsk_dialup phonenumber

Without arguments asks for a phone number, and passes this to the command specified in tkdesk(appbar,dialup,cmd_up), else it does the same without asking.

dsk_find_files ?options?

Opens the "Find Files" dialog, presetting its fields from the options. The following options are available: -path path, -mask mask, -string string, -regexp regexp, -extreg extreg, -type "all|file|dir|sym|socket|pipe", -owner owner, -group group, -modified mod, -accessed acc, -size size, -case, -samefs, -followsym. If the option -doit is given the search will start as soon as the dialog is displayed, without having to press "Search".

dsk_mail file ?string?

Asks for an email address where to send file. If file is the empty string, string will be sent instead.

dsk_netscape what ?url? ?args?

This proc is used to communicate with a running netscape. If Netscape is not running yet it will be started first. What may be "file", "url", or "rcmd" (Communicator only); url may be a regular URL, a file name, or, if what is "rcmd", it may be "mail", "news" or "edit". Args may be "window", or "raise", or both.

dsk_periodic command seconds

Opens the "Periodic Execution" window and executes "command" every "seconds" seconds.

dsk_open viewer file

Opens file by performing its default action. Viewer should be given as ""; I just didn't manage to get rid of this obsolete argument yet.

dsk_open_browser dir

Opens dir in a new file browser window.

dsk_open_dir dir

Opens dir in a new file list window (unless the option "Always in Browser" is set).

dsk_openall ?files?

Opens all files if given, or all currently selected files by performing their respective default action.

9.3 Shortcuts

When specifying Tcl scripts in the popup and appbar configuration lists (tkdesk(popup,...) and tkdesk(appbar)), and in some other places as well (precide, hus?), you can use a number of shortcuts that will be expanded just before evaluation:

%s

Will be replaced with the full pathname of the first selected file.

%f

Will be replaced with its filename only.

%b

Will be replaced with its filename without extension ("basename").

%d

Will be replaced with its directory only.

%A

Will be replaced with a Tcl list of the full pathnames of all currently selected files, or of the files dropped on an appbar button.

%a

Same as %a, but replaces with a list of filenames only.

%B

Same as %A but doesn't complain if no files are selected. Instead it will be replaced with the empty string.

%D

Will be replaced with the directory of the currently active file list or browser window.

%x

Will be replaced with the contents of the X selection.

%X

Same as %x but doesn't complain if the selection is empty.

%S

For the "Copy, Move, etc." popup menu; will be replaced of what's been entered into the "Source" text entry field.

%D

For the same popup menu; will be replaced of what's been entered into the "Destination" text entry field.

9.4 Utilities

To make life while configuring TkDesk a little easier as long as there's no GUI configuration available yet, TkDesk provides four little "helper" tools that help with selecting colors, fonts, icons, and sounds, by using graphical mouse-based dialog boxes. These are accessed through the "Configuration" menu, and are basically all handled in the same way.

Each of these dialogs contains three buttons:

Insert

Works only correctly if the dialog was invoked from the editor's "Configuration" menu. Inserts the current value at the cursor position in that editor window.

Select

Copies the selected value to the X selection, so it can be pasted at arbitrary places using the middle mouse button.

Close

Closes the dialog.


Next Previous Contents tkdesk-2.0/tcldesk/doc/guide.html0100644000175000007640000001207510037101554015107 0ustar jccjcc TkDesk User's Guide Next Previous Contents

TkDesk User's Guide

Originally written by Christian Bolik, now maintained by J. Chris Coppick, jchris@users.sourceforge.net

Version 2.0, 15 April 2004
TkDesk is a graphical, highly configurable and powerful desktop manager for UNIX and the X Window System. This document is meant to be a comprehensive guide to the functions, services and configuration possibilities offered by TkDesk. Please also take a look at the CHANGES file for latest news. A list of answers to frequently asked questions is also included. The TkDesk homepage is at http://tkdesk.sourceforge.net.

1. Introduction

2. The File Browser Window

3. The File List Window

4. File Operations

5. Cascading Directory Popup Menus

6. The Application Bar

7. The Built-in Editor

8. Using the TkDesk Server

9. Configuration of TkDesk

10. Frequently Asked Questions

11. Tips and Tricks


Next Previous Contents tkdesk-2.0/tcldesk/Common.tcl0100644000175000007640000007365510020457430014325 0ustar jccjcc# ============================================================================= # # File: dsk_Common.tcl # Project: TkDesk # # Started: 17.09.96 # Changed: 17.09.96 # Author: cb # # Description: Defines mega-widgets common to both file browser and # file list windows (i.e. menu bar, button bar). # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_Common #s method _create_button_bar {frame {bbdesc ""}} #s method _create_dir_button_bar {desc} #s method _create_menubar {frame} #s method _expand_title {title} #s method _path_popup {x y} #s method _remove_dir_button_bar {} #s method _retrieve_X {offset maxBytes} #s method cget {pubvar} #s method config {config} {} #s method configure {config} {} #s method set_title {} #s method textview {file} # # ============================================================================= # ----------------------------------------------------------------------------- # CLASS: dsk_CommonMenuBar: # Implements the menu bar of file browser/list windows. # itcl_class dsk_Common { constructor {config} { set topl [virtual getToplevel] } destructor { } # # ----- Methods and Procs ------------------------------------------------- # method config {config} {} method configure {config} {} method cget {pubvar} { return [virtual set [string trimleft $pubvar -]] } method status {str {do_update 1}} { pack propagate $topl 0 if ![winfo exists $topl.fStatus.l] return if {[set nl [string first "\n" $str]] >= 0} { #set str [string range $str 0 [expr $nl -1]]... set str [string_replace $str "\n" " "] } $topl.fStatus.l config -text $str if $do_update { update ;# idletasks } } method status_ready {{do_update 1}} { pack propagate $topl 0 if ![winfo exists $topl.fStatus.l] return $topl.fStatus.l config -text \ "Ready. [dsk_fs_status [_make_path_valid [virtual cget -directory]]]" if $do_update { update ;# idletasks } } method selstatus {} { global tkdesk if ![winfo exists $topl.fStatus.l] return set sfl [virtual select get] dsk_debug "selstatus: sfl $sfl" set l [llength $sfl] if {$l == 0} { $this status "Ready. [dsk_fs_status [virtual cget -directory]]" #$this status "Ready." } elseif {$l == 1} { set fi "" catch {set fi [dskC_ls -l -o [lindex $sfl 0]]} if {$fi != ""} { set fi [split [lindex $fi 0] \t] if {[lindex $fi 7] == ""} { ot_maplist $fi n s d o g p l set n [dskC_unesc [string trimright $n " "]] $this status "$n: $s Bytes, $d, $o/$g ($p)" } else { # it's a symbolic link set n [dskC_unesc [string trimright [lindex $fi 0] " "]] set lf [lindex [lindex $fi 7] 1] $this status "$n: symbolic link to $lf" } } else { $this status "[file tail $sfl]: broken symbolic link" } } else { set s 0 # sum up file sizes - another candidate for C coding foreach f $sfl { catch {set s [expr $s + [file size $f]]} } $this status "$l Items selected ($s Bytes)." } } method _create_menubar {frame} { global [set this] tkdesk env cb_tools # ---- TkDesk Menu if [$this isa dsk_FileViewer] { menubutton $frame.mbTkDesk -text "TkDesk" -underline 0 \ -menu $frame.mbTkDesk.menu pack $frame.mbTkDesk -side left menu [set m $frame.mbTkDesk.menu] } else { menubutton $frame.mbOthers -text "Others" -underline 0 \ -menu $frame.mbOthers.menu menu $frame.mbOthers.menu menu [set m $frame.mbOthers.menu.tkdesk] } set tkdesk_menu $m if $tkdesk(in_development) { $m add cascade -label "Development" \ -menu [set dm $m.dmenu] $m add separator menu $dm -tearoff 1 $dm add command -label "Eval Expression..." \ -command {dsk_read_string "Eval Tcl Expression:" \ {eval $dsk_read_string}} $dm add command -label "Eval XSelection " \ -command {eval [selection get]} \ -accelerator "Meta-x" $dm add command -label "Restart" -underline 2 \ -command "dsk_restart" -accelerator "Meta-r" $dm add separator $dm add checkbutton -label "Debug Mode" \ -variable tkdesk(debug) $dm add command -label "Debug tkdesksh" -command " exec rxvt -e gdb $env(HOME)/tcl/TkDesk/tkdesksh [pid] & " $dm add command -label "Force Error" -command "tkerror xxx" bind $topl {eval [selection get]; bell} bind $topl {dsk_restart} } $m add command -label "New Browser..." -underline 0 \ -command "dsk_ask_dir browser" if [$this isa dsk_FileViewer] { $m add command -label "Clone Window" -underline 2 \ -command "dsk_FileViewer .fv\[dsk_FileViewer :: id\] \ -dir \[$this curdir\] -num_lbs \[$this cget num_lbs\]" } else { $m add command -label "Clone Window" -underline 2 \ -command "dsk_FileList .fl\[dsk_FileList :: id\] \ -dir \[$this curdir\]" } if [winfo exists .dsk_appbar] { $m add command -label "Hide AppBar" -underline 0 \ -command "dsk_appbar" } else { $m add command -label "Display AppBar" -underline 0 \ -command "dsk_appbar" } $m add separator $m add cascade -label "Configuration " -underline 0 \ -menu $m.edmenu $m add cascade -label "Auto Save" -underline 1 \ -menu $m.aumenu $m add command -label "Save All Now" -underline 0 \ -command {dsk_save_config 1} $m add separator $m add command -label "Close Window" -underline 0 \ -command "$this close" if !$tkdesk(xmaster) { $m add command -label "Quit" -underline 0 -command "dsk_exit" } else { $m add command -label "Quit X Windows" -underline 0 \ -command "dsk_exit" } menu [set em $m.edmenu] -tearoff 1 foreach cf $tkdesk(configfiles) { set cfs [format "%-12s" $cf] $em add command \ -label "$cfs ($tkdesk(configxpl,$cf))" \ -command "dsk_edit_configs $cf" } $em add separator $em add cascade -label "Reload" -menu [set rm $em.remenu] menu $rm -tearoff 1 foreach cf $tkdesk(configfiles) { $rm add command \ -label "$cf" \ -command "dsk_reread_config $cf" } $rm add separator $rm add checkbutton -label " Reload Automatically" \ -variable tkdesk(auto_reload_conf) $em add command -label "Find in Config..." \ -command "dsk_find_files -path $tkdesk(configdir) -name [list $tkdesk(configfiles)] -type file" $em add command -label "Colors..." \ -command "dsk_config_panel colors" $em add command -label "Fonts..." \ -command "dsk_config_panel fonts" $em add command -label "Icons..." \ -command "dsk_config_panel icons" $em add command -label "Sounds..." \ -command "dsk_config_panel sounds" menu [set am $m.aumenu] $am add checkbutton -label " Annotations" \ -variable tkdesk(autosave,annotations) $am add checkbutton -label " Bookmarks" \ -variable tkdesk(autosave,bookmarks) $am add checkbutton -label " Histories" \ -variable tkdesk(autosave,history) $am add checkbutton -label " Options" \ -variable tkdesk(autosave,options) $am add checkbutton -label " Window Layout" \ -variable tkdesk(autosave,layout) # ---- File Menu menubutton $frame.mbFile -text "File" -underline 0 \ -menu $frame.mbFile.menu pack $frame.mbFile -side left menu [set m $frame.mbFile.menu] $m add command -label "Information" -underline 0 \ -command "dsk_fileinfo" -accelerator "Ctrl-i" $m add command -label "New File..." -underline 0 \ -command "dsk_create file" -accelerator "Ctrl-n" $m add command -label "New Directory..." -underline 4 \ -command "dsk_create directory" -accelerator "Ctrl-d" $m add separator $m add command -label "Open (Using Default)" -underline 0 \ -command "dsk_openall" $m add command -label "Open With..." -underline 0 \ -command "dsk_openwith" -accelerator "Ctrl-w" $m add command -label "Print..." -underline 0 \ -command "dsk_print" -accelerator "Ctrl-p" $m add command -label "Copy, Move, Link... " -underline 0 \ -command "dsk_copy" -accelerator "Ctrl-c" $m add command -label "Rename... " -underline 0 \ -command "dsk_rename" -accelerator "Ctrl-r" $m add command -label "Delete..." -underline 1 \ -command "dsk_delete" -accelerator "Del" $m add separator $m add command -label "Find Files..." -underline 0 \ -command "dsk_find_files" -accelerator "Ctrl-f" $m add command -label "Find Annotation..." -underline 5 \ -command "dsk_find_annotation" $m add command -label "Copy To X Selection" -underline 8 \ -command "dsk_select X" $m add command -label "Copy Names Only " -underline 6 \ -command "dsk_select X names" $m add separator $m add cascade -label "History" -menu $m.mhf menu $m.mhf -postcommand \ "file_history buildmenu $m.mhf; update" # add dummy entry to work around bug in pre Tk 4.0p2: $m.mhf add command -label "dummy" file_history changed bind $m.mhf " set tkdesk(file_lb,control) 0 [bind Menu ]" bind $m.mhf " set tkdesk(file_lb,control) 1 [bind Menu ]" $m add command -label "Close Window" -command "$this delete" # ---- Directories Menu menubutton $frame.mbDirs -text "Directory" -underline 0 \ -menu $frame.mbDirs.menu pack $frame.mbDirs -side left menu [set m $frame.mbDirs.menu] $m add command -label "Open..." -underline 0 \ -command "dsk_ask_dir" -accelerator "Ctrl-o" $m add command -label "New..." -underline 0 \ -command "dsk_create directory" -accelerator "Ctrl-d" $m add separator $m add command -label "Home" -underline 0 \ -command {dsk_cd ~} \ -accelerator "Ctrl-Home" $m add command -label "Previous" -underline 0 \ -command {dsk_cd [list [dir_history back]]} \ -accelerator "Ctrl-Left" $m add command -label "Next" -underline 0 \ -command {dsk_cd [list [dir_history forward]]} \ -accelerator "Ctrl-Right" $m add separator $m add command -label "Open Trash Can" -underline 0 \ -command "dsk_FileList .dfl\[dsk_FileList :: id\] \ -directory $tkdesk(trashdir)" $m add command -label "Empty Trash Can" -underline 0 \ -command "dsk_empty_trash" if {[info exists tkdesk(deskdir)]} { $m add command -label "Open Desktop" -underline 0 \ -command "dsk_FileList .dfl\[dsk_FileList :: id\] \ -directory $tkdesk(deskdir)" } $m add separator if [info exists tkdesk(directories)] { foreach mdir $tkdesk(directories) { if {$mdir == "-"} { $m add separator } else { _add_dir_to_menu $this $m $mdir } } } $m add separator $m add cascade -label "Trees" -menu ${m}.fs menu ${m}.fs menu ${m}.fs.home -postcommand "dsk_casdirs $env(HOME) ${m}.fs.home 1" ${m}.fs add cascade -label "Home " -menu ${m}.fs.home menu ${m}.fs.root -postcommand "dsk_casdirs / ${m}.fs.root 1" ${m}.fs add cascade -label "Root " -menu ${m}.fs.root $m add cascade -label "History" -menu $m.mhd menu $m.mhd -postcommand \ "dir_history buildmenu $m.mhd open; update" # add dummy entry to work around bug in pre Tk 4.0p2: $m.mhd add command -label "dummy" dir_history changed bind $m " set tkdesk(menu,control) 0 [bind Menu ]" bind $m " set tkdesk(menu,control) 1 [bind Menu ]" # ---- Others Menu (FileList) if [$this isa dsk_FileList] { pack $frame.mbOthers -side left set m $frame.mbOthers.menu #$m add command -label "Open Browser View" -underline 0 \ -command "dsk_FileViewer .fv\[dsk_FileViewer :: id\] \ -dir \[$this curdir\] -num_lbs \$tkdesk(num_lbs)" $m add cascade -label "TkDesk" -menu $frame.mbOthers.menu.tkdesk $m add cascade -label "Commands" -menu $frame.mbOthers.menu.cmd $m add cascade -label "Bookmarks" -menu $frame.mbOthers.menu.book $m add cascade -label "Options" -menu $frame.mbOthers.menu.opts $m add cascade -label "Help" -menu $frame.mbOthers.menu.help $m add separator $m add command -label "Open Browser " \ -command "dsk_FileViewer .fv\[dsk_FileViewer :: id\] \ -directory \[$this info public directory -value\]" } # ---- Commands Menu if [$this isa dsk_FileViewer] { menubutton $frame.mbCmds -text "Commands" -underline 0 \ -menu $frame.mbCmds.menu pack $frame.mbCmds -side left menu [set m $frame.mbCmds.menu] } else { menu [set m $topl.fMenu.mbOthers.menu.cmd] } $m add command -label "Execute..." -underline 0 \ -command "dsk_ask_exec" -accelerator "Ctrl-x" $m add command -label "Execute as root..." -underline 11 \ -command "$this exec_as_root" $m add command -label "Periodic Execution..." -underline 0 \ -command "dsk_periodic" $m add command -label "Job Control" -underline 0 -command "dsk_jobs" $m add command -label "Environment..." -underline 2 \ -command "dsk_env_edit" $m add separator $m add command -label "Edit File..." -underline 0 \ -command {dsk_edit} -accelerator "Ctrl-e" $m add command -label "New File" -underline 0 \ -command {dsk_edit "New File"} $m add command -label "Edit Selected" -underline 0 \ -command {eval [_expand_pc {dsk_edit %B}]} $m add cascade -label "Buffers" -menu $m.bufs menu [set tm $m.bufs] -postcommand \ "dsk_Editor :: bufferMenu $tm" $m add separator if [info exists tkdesk(commands)] { foreach cmd $tkdesk(commands) { if {[llength $cmd] > 1} { _add_cmd_to_menu $m $cmd } else { $m add separator } } } $m add separator $m add cascade -label "History" -menu $m.mhe menu $m.mhe -postcommand \ "exec_history buildmenu $m.mhe; update" # add dummy entry to work around bug in pre Tk 4.0p2: $m.mhe add command -label "dummy" exec_history changed bind $m.mhe " set tkdesk(file_lb,control) 0 [bind Menu ]" bind $m.mhe " set tkdesk(file_lb,control) 1 [bind Menu ]" # ---- Bookmarks Menu if [$this isa dsk_FileViewer] { menubutton $frame.mbBook -text "Bookmarks" -underline 0 \ -menu $frame.mbBook.menu pack $frame.mbBook -side left menu [set m $frame.mbBook.menu] \ -postcommand "dsk_bookmark menu $m" } else { menu [set m $topl.fMenu.mbOthers.menu.book] \ -postcommand "dsk_bookmark menu $m" } # add dummy entry to work around bug in pre Tk 4.0p2: $m add command -label "dummy" bind $m " set tkdesk(file_lb,control) 0 [bind Menu ]" bind $m " set tkdesk(file_lb,control) 1 [bind Menu ]" # ---- Options Menu if [$this isa dsk_FileViewer] { menubutton $frame.mbOpts -text "Options" -underline 0 \ -menu $frame.mbOpts.menu pack $frame.mbOpts -side left menu [set m $frame.mbOpts.menu] } else { menu [set m $topl.fMenu.mbOthers.menu.opts] } set optm $m # "File Lists" menu $optm add cascade -label "File Lists" -menu [set m $optm.mfl] menu $m $m add checkbutton -label " Long Listing " -underline 1 \ -variable tkdesk(long_listing) \ -command "dsk_FileListbox :: longlist \$tkdesk(long_listing)" $m add checkbutton -label " Show All Files " -underline 1 \ -variable tkdesk(show_all_files) \ -command "dsk_FileListbox :: showall \$tkdesk(show_all_files)" $m add checkbutton -label " Add Icons " -underline 1 \ -variable tkdesk(add_icons) \ -command "dsk_FileListbox :: addicons \$tkdesk(add_icons)" $m add checkbutton -label " Folders On Top " -underline 1 \ -variable tkdesk(folders_on_top) \ -command "dsk_FileListbox :: topfolders \$tkdesk(folders_on_top)" $m add checkbutton -label " Append Type Char " -underline 2 \ -variable tkdesk(append_type_char) \ -command "dsk_FileListbox :: typechar \$tkdesk(append_type_char)" $m add checkbutton -label " Dot: Execs Regular " -underline 1 \ -variable tkdesk(dot_regular) \ -command "dsk_FileListbox :: dotregular \$tkdesk(dot_regular)" $m add separator $m add cascade -label "Sort by ..." -menu [set m $m.smenu] menu $m $m add radiobutton -label " Name " \ -variable tkdesk(default_sort) -value name \ -command "dsk_FileListbox :: sort \$tkdesk(default_sort)" $m add radiobutton -label " Name (fold) " \ -variable tkdesk(default_sort) -value fold \ -command "dsk_FileListbox :: sort \$tkdesk(default_sort)" $m add radiobutton -label " Size " \ -variable tkdesk(default_sort) -value size \ -command "dsk_FileListbox :: sort \$tkdesk(default_sort)" $m add radiobutton -label " Date " \ -variable tkdesk(default_sort) -value date \ -command "dsk_FileListbox :: sort \$tkdesk(default_sort)" $m add radiobutton -label " Extension " \ -variable tkdesk(default_sort) -value ext \ -command "dsk_FileListbox :: sort \$tkdesk(default_sort)" $m add radiobutton -label " Don't sort " \ -variable tkdesk(default_sort) -value not \ -command "dsk_FileListbox :: sort \$tkdesk(default_sort)" # "Appearance" menu $optm add cascade -label "Appearance" -menu [set m $optm.map] menu $m $m add checkbutton -label " Strip $env(HOME) " -underline 2 \ -variable tkdesk(strip_home) \ -command "$this refresh all" $m add checkbutton -label " Sort History " -underline 6 \ -variable tkdesk(sort_history) $m add checkbutton -label " Dialogs At Pointer " -underline 1 \ -variable tkdesk(at_pointer) $m add checkbutton -label " Status in List Windows " -underline 5 \ -variable tkdesk(list_statbar) \ -command "dsk_FileList :: status_bar \$tkdesk(list_statbar)" $m add checkbutton -label " Expand Dir Bookmarks " -underline 1 \ -variable tkdesk(expand_dir_bookmarks) $m add checkbutton -label " Tear-off Menus " -underline 3 \ -variable tkdesk(tearoff-menus) \ -command {option add *Menu.tearOff $tkdesk(tearoff-menus)} $m add checkbutton -label " Strictly Motif " -underline 3 \ -variable tkdesk(strict_motif) \ -command {set tk_strictMotif $tkdesk(strict_motif)} if [$this isa dsk_FileViewer] { $m add separator $m add cascade -label "Number Of Listboxes" \ -menu [set m $m.numlbs] menu $m set num_lbs [virtual cget -num_lbs] set [set this](num_lbs) $num_lbs $m add radiobutton -label " 1 Listbox" \ -variable [set this](num_lbs) \ -value 1 -command "$this _resize" $m add radiobutton -label " 2 Listboxes" \ -variable [set this](num_lbs) \ -value 2 -command "$this _resize" $m add radiobutton -label " 3 Listboxes" \ -variable [set this](num_lbs) \ -value 3 -command "$this _resize" $m add radiobutton -label " 4 Listboxes" \ -variable [set this](num_lbs) \ -value 4 -command "$this _resize" $m add radiobutton -label " 5 Listboxes" \ -variable [set this](num_lbs) \ -value 5 -command "$this _resize" $m add radiobutton -label " 6 Listboxes" \ -variable [set this](num_lbs) \ -value 6 -command "$this _resize" } # "Behavior" Menu $optm add cascade -label "Behavior" -menu [set m $optm.mbe] menu $m $m add checkbutton -label " Single Click (Dirs) " -underline 4 \ -variable tkdesk(single_click) $m add checkbutton -label " Always In Browser " -underline 11 \ -variable tkdesk(in_browser) $m add checkbutton -label " Quick Drag'n'Drop " -underline 1 \ -variable tkdesk(quick_dragndrop) #$m add checkbutton -label " Dot: Execs Regular " -underline 1 \ # -variable tkdesk(dot_regular) $m add checkbutton -label " Focus Follows Mouse " -underline 1 \ -variable tkdesk(focus_follows_mouse) \ -command {if $tkdesk(focus_follows_mouse) \ {tk_focusFollowsMouse} else \ {dsk_msg_info "You need to restart TkDesk to make this change take effect."}} $m add checkbutton -label " Autoraise AppBar " \ -variable tkdesk(appbar,autoraise) # "Confirmation" Menu $optm add cascade -label "Confirmation" -menu [set m $optm.mco] menu $m $m add checkbutton -label " Overwrite Always " -underline 1 \ -variable tkdesk(overwrite_always) $m add checkbutton -label " Really Delete " -underline 1 \ -variable tkdesk(really_delete) $m add checkbutton -label " Ask On Delete " -underline 3 \ -variable tkdesk(ask_on_delete) $m add checkbutton -label " Start Netscape " -underline 7 \ -variable tkdesk(confirm_netscape) $m add checkbutton -label " Ask On Exit " -underline 8 \ -variable tkdesk(ask_on_exit) # "Help" Menu $optm add cascade -label "Help Options" -menu [set m $optm.mhe] menu $m $m add checkbutton -label "Balloon Help" \ -variable cb_tools(balloon_help) $m add checkbutton -label "Use Netscape for Help" \ -variable tkdesk(netscape_help) # Other entries set m $optm $m add separator $m add checkbutton -label " TkDesk Server " -underline 4 \ -command "dsk_setup_server" -variable tkdesk(tkdesk_server) $m add checkbutton -label " Use Sound " -underline 8 \ -variable tkdesk(use_sound) # ---- Help Menu if [$this isa dsk_FileViewer] { menubutton $frame.mbHelp -text "Help" -underline 0 \ -menu $frame.mbHelp.menu pack $frame.mbHelp -side right menu [set m $frame.mbHelp.menu] } else { menu [set m $topl.fMenu.mbOthers.menu.help] } $m add command -label "User's Guide " \ -command "dsk_help guide" \ -accelerator "F1" $m add command -label "Manual Page... " \ -command "dsk_man" $m add separator $m add command -label "Getting Started" \ -command "dsk_help quick" $m add command -label "TkDesk FAQ" \ -command "dsk_help faq" $m add command -label "Changes" \ -command "dsk_help changes" $m add command -label "License" \ -command "dsk_help license" $m add separator $m add command -label "About TkDesk..." \ -command dsk_about if $tkdesk(in_development) { $m add command -label "About TkDesk (Web)..." \ -command dsk_about_web } } method _create_button_bar {frame {bbdesc ""}} { global tkdesk if {$bbdesc == ""} { if [$this isa dsk_FileViewer] { set bbdesc $tkdesk(button_bar) } else { set bbdesc $tkdesk(small_button_bar) } } catch {destroy $frame.fBB} frame $frame.fBB pack $frame.fBB -fill x set bcnt 0 set fcnt 0 frame $frame.fBB.f$fcnt pack $frame.fBB.f$fcnt -fill x -padx $tkdesk(pad) -pady $tkdesk(pad) foreach but $bbdesc { if {[llength $but] == 1} { if {$but == "break" || $but == "\\n"} { incr fcnt frame $frame.fBB.f$fcnt pack $frame.fBB.f$fcnt -fill x \ -padx $tkdesk(pad) -pady $tkdesk(pad) } else { frame $frame.fBB.f$fcnt.f$bcnt -width $tkdesk(pad) pack $frame.fBB.f$fcnt.f$bcnt -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) incr bcnt } continue } set bitmap [lindex $but 0] set bgcolor [cb_col $tkdesk(color,background)] set fgcolor [cb_col $tkdesk(color,foreground)] if {[llength $bitmap] > 1} { if {[lindex $bitmap 1] != ""} { set fgcolor [lindex $bitmap 1] } if {[llength $bitmap] > 2} { if {[lindex $bitmap 2] != ""} { set bgcolor [lindex $bitmap 2] } } set bitmap [lindex $bitmap 0] } #set action [string_replace [lindex $but 1] \" \\\"] set action [lindex $but 1] set desc "" if {[llength $action] > 1} { set desc [lindex $action 1] set action [lindex $action 0] } dsk_debug "BB$bcnt action: $action" button $frame.fBB.f$fcnt.b$bcnt \ -image [dsk_image $bitmap -background $bgcolor \ -foreground $fgcolor] \ -takefocus 0 -highlightthickness 0 \ -command "catch \{cd \[dsk_active dir\]\} ;\ set tkdesk(error_source) {ButtonBar} ;\ eval \[_expand_pc [list [dskC_esc $action \[\]\$\"]]\];\ set tkdesk(error_source) {} ;\ cd ~" pack $frame.fBB.f$fcnt.b$bcnt -side left \ -padx 0 -pady 0 \ -ipadx 2 -ipady 2 if {$desc != ""} { cb_balloonHelp $frame.fBB.f$fcnt.b$bcnt "$desc" } incr bcnt } } method _create_dir_button_bar {desc} { pack propagate $topl 0 if ![winfo exists $topl.fDirBB] { frame $topl.fDirBB -bd 1 -relief raised } else { pack propagate $topl.fDirBB 0 destroy $topl.fDirBB.fBB } if [winfo exists $topl.fButtonBar] { pack $topl.fDirBB -after $topl.fButtonBar -fill x } else { pack $topl.fDirBB -after $topl.fMenu -fill x } _create_button_bar $topl.fDirBB $desc #pack propagate $topl.fDirBB 1 } method _remove_dir_button_bar {} { catch {destroy $topl.fDirBB} } method _retrieve_X {offset maxBytes} { # return "offset: $offset, maxBytes: $maxBytes" if {$offset != 0} { # this should never happen, so return return "" } set sfl [$this select get] set rl "" if {$sfl != ""} { if {[llength $sfl] > 1} { foreach file $sfl { if {[llength $file] > 1} { append rl "\"$file\" " } else { append rl "$file " } } } else { set rl $sfl } } if {[string length $rl] > $maxBytes} { return [string range $rl 0 [expr $maxBytes - 1]] } else { return $rl } } method _retrieve_X_names {offset maxBytes} { # return "offset: $offset, maxBytes: $maxBytes" if {$offset != 0} { # this should never happen, so return return "" } set sfl [$this select get] set rl "" if {$sfl != ""} { if {[llength $sfl] > 1} { foreach file $sfl { set file [file tail $file] if {[llength $file] > 1} { append rl "\"$file\" " } else { append rl "$file " } } } else { set rl [file tail $sfl] } } if {[string length $rl] > $maxBytes} { return [string range $rl 0 [expr $maxBytes - 1]] } else { return $rl } } method set_title {} { global tkdesk if [$this isa dsk_FileViewer] { set title $tkdesk(title,browser) } else { set title $tkdesk(title,list) } wm title $topl [_expand_title $title] set title $tkdesk(title,icon) wm iconname $topl [_expand_title $title] } method exec_as_root {} { global tkdesk set asr $tkdesk(exec_as_root) ::set tkdesk(exec_as_root) 1 ::dsk_ask_exec ::set tkdesk(exec_as_root) $asr } method _expand_title {title} { global tkdesk if {[string first "%d" $title] > -1} { regsub "%d" $title \ [file tail [string trimright \ [virtual cget -directory] "/"]]/ title } if {[string first "%h" $title] > -1} { regsub "%h" $title [dsk_hostname] title } if {[string first "%p" $title] > -1} { regsub "%p" $title [cb_tilde [virtual cget -directory] collapse] \ title } if {[string first "%u" $title] > -1} { regsub "%u" $title [dsk_logname] title } if {[string first "%v" $title] > -1} { regsub "%v" $title $tkdesk(version) title } return $title } method _path_popup {x y} { global tkdesk set m $topl-ppop catch {destroy $m} menu $m bind $m " set tkdesk(menu,control) 0 [bind Menu ]" bind $m " set tkdesk(menu,control) 1 [bind Menu ]" set p [cb_tilde [string trimright \ [virtual cget -directory] "/"] collapse] if {$p != ""} { set op $p while {1} { set p [file dirname $p] $m add command -label $p \ -command "$this config -dir [list $p]" \ -font [cb_font $tkdesk(font,file_lbs)] if {$p == "/"} break } $m add separator set sm $m.sub $m add cascade -label "Subdirectories " -menu $sm menu $sm set p $op set i 0 while {1} { set p [file dirname $p] set cm $sm.cas$i $sm add cascade -label $p -menu $cm \ -font [cb_font $tkdesk(font,file_lbs)] catch {destroy $cm} menu $cm -postcommand "dsk_casdirs [list $p] $cm 1" incr i if {$p == "/"} break } set sm $m.files $m add cascade -label "... and Files " -menu $sm menu $sm set p $op set i 0 while {1} { set p [file dirname $p] set cm $sm.cas$i $sm add cascade -label $p -menu $cm \ -font [cb_font $tkdesk(font,file_lbs)] catch {destroy $cm} menu $cm -postcommand "dsk_casdirs [list $p] $cm 1 {} 1" incr i if {$p == "/"} break } update tk_popup $m $x $y } } method textview {file} { set textviewer [dsk_editor fileview $textviewer $file] } method _adjust_menu {match label} { catch {$tkdesk_menu entryconfig $match -label $label} } proc setSoundEntryState {} { global tkdesk if ![info exists tkdesk(soundcmd)] { foreach obj [itcl_info objects -class dsk_FileList] { set m [$obj getToplevel].fMenu.mbOthers.menu.opts $m entryconfig [$m index *Sound*] -state disabled } foreach obj [itcl_info objects -class dsk_FileViewer] { set m [$obj getToplevel].fMenu.mbOpts.menu $m entryconfig [$m index *Sound*] -state disabled } } else { foreach obj [itcl_info objects -class dsk_FileList] { set m [$obj getToplevel].fMenu.mbOthers.menu.opts $m entryconfig [$m index *Sound*] -state normal } foreach obj [itcl_info objects -class dsk_FileViewer] { set m [$obj getToplevel].fMenu.mbOpts.menu $m entryconfig [$m index *Sound*] -state normal } } } proc adjustAppBarMenuEntry {displaying} { if $displaying { set l "Hide AppBar" set m "Display AppBar" } else { set l "Display AppBar" set m "Hide AppBar" } foreach fv [itcl_info objects -class dsk_FileViewer] { if [winfo exists [$fv getToplevel]] { $fv _adjust_menu $m $l } } foreach fl [itcl_info objects -class dsk_FileList] { if [winfo exists [$fl getToplevel]] { $fl _adjust_menu $m $l } } } # # ----- Variables --------------------------------------------------------- # protected textviewer "" protected tkdesk_menu protected topl } tkdesk-2.0/tcldesk/Desktop.tcl0100644000175000007640000003625310020457430014477 0ustar jccjcc# ============================================================================= # # File: Desktop.tcl # Project: TkDesk # # Started: 17.12.95 # Changed: 17.09.96 # Author: cb # # Description: Manages the TkDesk "desktop", i.e. items on the root window. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_DeskItem #s method config {config} {} #s method configure {config} {} #s method cget {pubvar} #s method drag {cmd args} #s method open {} #s method popup {x y} #s method _dd_pkgcmd {token} #s method _dd_dragcmd {} #s method _dd_drop {mytype} #s method refresh {} #s proc dsk_desktop_drop {x y {flb ""}} #s proc id {{cmd ""}} #s proc set_width {val} #s proc defimg {type img} #s proc move {ofile nfile} #s proc remove {rfile} # # ============================================================================= # ============================================================================ # # Proc : dsk_desktop_drop # In : x, y - root location # Out : - # Desc : Checks if a file has been dropped onto the root window (desktop). # Side FX : none # # ---------------------------------------------------------------------------- set tkdesk(drop_on_item) 0 proc dsk_desktop_drop {x y {flb ""}} { global tkdesk if $tkdesk(drop_on_item) { set tkdesk(drop_on_item) 0 return } set w [winfo containing $x $y] if {$w != ""} { #puts $w set o 0 if {[winfo class $w] != "DragDrop"} { set err [catch {set o [wm overrideredirect $w]} errmsg] if {($err || !$o) && ([winfo class $w] != "Icon") \ && ([winfo class $w] != "Menu")} { return 0 } if {$o && [winfo class $w] == "dsk_DeskItem"} { return 0 } set err [catch {set o [wm state $w]} errmsg] if {($err || $o == "normal") && ([winfo class $w] != "Menu")} { return 0 } } } # Ok, a file has been dropped outside any of TkDesk's windows -> icon set xoff 0 set yoff 0 if {$flb == ""} { set flist [dsk_active sel] } elseif {[string index [string trimleft $flb :] 0] == "."} { set flist [string trimright [$flb cget -directory] /] } else { set flist $flb } foreach file $flist { set di .di[dsk_DeskItem :: id] dsk_DeskItem $di -file $file -dontmap 1 set di [$di getToplevel] update idletasks set w [winfo reqwidth $di] set h [winfo reqheight $di] set x [expr [winfo pointerx $di] + $xoff] set y [expr [winfo pointery $di] + $yoff] set sw [winfo screenwidth .] set sh [winfo screenheight .] if {$x + $w > $sw} {set x [expr $sw - $w]} if {$y + $h > $sh} {set y [expr $sh - $h]} wm geometry $di +$x+$y wm deiconify $di incr xoff 16 incr yoff 16 } return 1 } proc dsk_desktop_update {} { global tkdesk set dilist [itcl_info objects -class dsk_DeskItem] set xoff 0 set yoff 0 foreach file [lsort [glob -nocomplain $tkdesk(deskdir)/*]] { set have_already 0 foreach diobj $dilist { if {[$diobj cget -file] == $file} { set have_already 1 break } } if {$have_already} { continue } set di .di[dsk_DeskItem :: id] dsk_DeskItem $di -file $file -dontmap 1 set di [$di getToplevel] update idletasks set w [winfo reqwidth $di] set h [winfo reqheight $di] set x [expr [winfo pointerx $di] + $xoff] set y [expr [winfo pointery $di] + $yoff] set sw [winfo screenwidth .] set sh [winfo screenheight .] if {$x + $w > $sw} {set x [expr $sw - $w]} if {$y + $h > $sh} {set y [expr $sh - $h]} wm geometry $di +$x+$y wm deiconify $di incr xoff 16 incr yoff 16 } } # ============================================================================ # # Class : dsk_DeskItem # Desc : A class for desktop items, i.e. transient windows consisting # of a bitmap and a label. # Methods : # Procs : # Publics : # # ---------------------------------------------------------------------------- itcl_class dsk_DeskItem { inherit Toplevel constructor {args} { global tkdesk if {$tkdesk(tcl_version) < 8.0} { Toplevel::constructor } wm withdraw $top $top config -bg [cb_col $tkdesk(color,icon)] \ -cursor top_left_arrow label $top.i -image [dsk_image ficons32/file.xpm] \ -bd 0 -bg [cb_col $tkdesk(color,icon)] \ -cursor top_left_arrow pack $top.i -ipadx 2 -ipady 2 label $top.l -text "no name" -font [cb_font $tkdesk(font,file_lbs)] \ -bd 1 -bg [cb_col $tkdesk(color,filelb)] \ -cursor top_left_arrow -relief raised pack $top.l -ipadx 1 -ipady 1 bind $top.i "$this drag start %X %Y; break" bind $top.i "$this drag to %X %Y; break" bind $top.i \ "set tkdesk(file_lb,control) 0; \ $this open; \ break" bind $top.i \ "set tkdesk(file_lb,control) 1; \ $this open; \ break" bind $top.i <3> \ "$this popup %X %Y ;\ break" if {[winfo depth .] != 1} { set cc [cb_col $tkdesk(color,drag)] } else { set cc white } blt_drag&drop source $top.i \ -button 2 -packagecmd "$this _dd_pkgcmd %t" -rejectfg red \ -selftarget 0 -send file -tokenanchor nw \ -tokencursor "@$tkdesk(library)/images/xbm/hand.xbm \ $tkdesk(library)/images/xbm/hand.mask.xbm \ [cb_col $tkdesk(color,foreground)] $cc" blt_drag&drop source $top.l \ -button 2 -packagecmd "$this _dd_pkgcmd %t" -rejectfg red \ -selftarget 0 -send file -tokenanchor nw \ -tokencursor "@$tkdesk(library)/images/xbm/hand.xbm \ $tkdesk(library)/images/xbm/hand.mask.xbm \ [cb_col $tkdesk(color,foreground)] $cc" blt_drag&drop source $top.i handler file blt_drag&drop source $top.l handler file if $tkdesk(desk_items,wm_managed) { wm transient $top . wm title $top "DeskItem" } else { wm overrideredirect $top 1 } wm geometry $top +[winfo pointerx $top]+[winfo pointery $top] eval config $args if [file isdirectory $file] { blt_drag&drop target $top.i handler file "$this _dd_drop dir %v" blt_drag&drop target $top.l handler file "$this _dd_drop dir %v" blt_drag&drop target $top handler file "$this _dd_drop dir %v" } elseif [file_executable $file] { blt_drag&drop target $top.i handler file "$this _dd_drop exec %v" blt_drag&drop target $top.l handler file "$this _dd_drop exec %v" blt_drag&drop target $top handler file "$this _dd_drop exec %v" } else { set type [file type $file] if {$type == "characterSpecial" || $type == "blockSpecial"} { blt_drag&drop target $top.i handler file "$this _dd_drop dir %v" blt_drag&drop target $top.l handler file "$this _dd_drop dir %v" blt_drag&drop target $top handler file "$this _dd_drop dir %v" } } bind $top.i \ "set tkdesk(file_lb,control) 1;\ $this _dd_dragcmd" bind $top.i \ "set tkdesk(file_lb,control) 0;\ $this _dd_dragcmd" # Copy bindings to other widgets of the desk item: # (note: "blt_drag&drop target" doesn't add bindings) foreach b [bind $top.i] { bind $top.l $b [bind $top.i $b] bind $top $b [bind $top.i $b] } if !$dontmap { wm deiconify $top tkwait visibility $top catch "lower $top .dsk_welcome" update } } destructor { } # ---- Methods ----------------------------------------------------------- method config {config} {} method configure {config} {} method cget {pubvar} { return [virtual set [string trimleft $pubvar -]] } method drag {cmd args} { global tkdesk switch $cmd { "start" { set x [lindex $args 0] set y [lindex $args 1] set move_x [expr $x - [winfo rootx $top]] set move_y [expr $y - [winfo rooty $top]] $top.l config -cursor [$top.i cget -cursor] raise $top } "to" { set x [lindex $args 0] set y [lindex $args 1] wm geometry $top +[expr $x - $move_x]+[expr $y - $move_y] } } } method discard {} { global tkdesk if {[info exists tkdesk(deskdir)] && [file exists $file]} { if {[string first $tkdesk(deskdir) $file] == 0} { dsk_delete $file return } } delete } method open {} { if [file exists $file] { if [file isdirectory $file] { dsk_open_dir $file } else { dsk_open [dsk_active viewer] $file } } else { dsk_bell if {[cb_yesno "The associated file/directory has been deleted from outside TkDesk. Remove desk item?"] == 0} { $this delete } } } method popup {x y} { dsk_popup "" $file $x $y "deskitem $this" } method _dd_pkgcmd {token} { global tkdesk set tkdesk(dd_token_window) $token catch "destroy $token.label" catch "destroy $token.lFiles" catch "destroy $token.lDirs" if $tkdesk(quick_dragndrop) { set f [cb_font $tkdesk(font,labels)] catch { set f [join [lreplace [split $f -] 7 7 10] -] set f [join [lreplace [split $f -] 3 3 medium] -] } label $token.label -text "Moving:" \ -font [cb_font $f] pack $token.label } label $token.lFiles -text " [eval file tail [_make_fname_safe $file]]" pack $token.lFiles -anchor w catch "wm deiconify $token" focus $token return [list [_make_fname_safe $file]] } method _dd_dragcmd {} { global tkdesk set token $tkdesk(dd_token_window) if $tkdesk(quick_dragndrop) { if $tkdesk(file_lb,control) { $token.label config -text "Copying:" } else { $token.label config -text "Moving:" } update idletasks } } method _dd_drop {mytype flist} { global tkdesk set tkdesk(drop_on_item) 1 catch "wm withdraw $tkdesk(dd_token_window)" update if {$mytype == "dir"} { if ![file writable $file] { dsk_errbell cb_error "You don't have write permission for this directory!" return } if {[string first "$tkdesk(trashdir)/" $file] == -1} { dsk_ddcopy $flist $file } else { if !$tkdesk(quick_dragndrop) { dsk_delete $flist } else { if {!$tkdesk(file_lb,control) && !$tkdesk(really_delete)} { dsk_ddcopy $flist $file } else { if {[cb_yesno "Really deleting! Are you sure that this is what you want?"] == 0} { dsk_sound dsk_really_deleting dsk_bgexec "$tkdesk(cmd,rm) $flist" \ "Deleting [llength $flist] File(s)..." dsk_refresh "$flist $dest" } } } } } elseif {$mytype == "exec"} { if $tkdesk(quick_dragndrop) { dsk_exec $file $flist } else { dsk_ask_exec "$file $flist" } } } method refresh {} { # re-set the image: config -file $file } method _break_name {fname} { set len [string length $fname] if {$len <= $width} { return $fname } # find a good place to insert a newline to break the filename set j 0 set bname "" set breaki -1 set breakc "" for {set i 0} {$i < $len} {incr i} { set c [string index $fname $i] if {[string first $c " .:_-"] > -1} { if {$breaki > -1} { append bname [string range $fname \ $breaki [expr $i - 1]] } set breaki $i set breakc $c } elseif {$breaki < 0} { append bname $c } incr j if {($j >= $width && $breaki >= 0) || $j > $width + 4} { switch -- $breakc { " " { # replace with newline append bname "\n" append bname [string range $fname [expr $breaki +1] $i] } "." { # insert newline before breakchar append bname "\n" append bname [string range $fname $breaki $i] } ":" - "_" - "-" { # insert newline after breakchar append bname $breakc append bname "\n" append bname [string range $fname [expr $breaki + 1] $i] } default { append bname "\n" } } set breaki -1 set breakc "" set j 0 } } if {$breaki > -1} { append bname [string range $fname $breaki [expr $i - 1]] } return $bname } # ---- Procs ------------------------------------------------------------- proc id {{cmd ""}} { if {$cmd == ""} { set i $id_counter incr id_counter return $i } elseif {$cmd == "reset"} { set id_counter 0 } } proc set_width {val} { set width $val } proc defimg {type img} { set defimg_$type $img } proc move {ofile nfile} { # $rfile has been moved -> adjust associated icons foreach di [itcl_info objects -class dsk_DeskItem] { if ![winfo exists [$di getToplevel]] continue set file [$di cget -file] if {$file == $ofile} { $di config -file $nfile } elseif {[string first $ofile/ $file] == 0} { $di config -file $nfile/[string range $file \ [string length $nfile/] 1000] } } } proc remove {rfile} { # $rfile has been deleted -> remove associated icons foreach di [itcl_info objects -class dsk_DeskItem] { if ![winfo exists [$di getToplevel]] continue set file [$di cget -file] if {$file == $rfile} { $di delete } elseif {[string first $rfile/ $file] == 0} { $di delete } } } # ---- Variables --------------------------------------------------------- public file "no name" { global tkdesk set havematch 0 set fname [file tail $file] if [file isdirectory $file] { set dicon $defimg_dir if [info exists tkdesk(file_tags,directories)] { foreach tf $tkdesk(file_tags,directories) { if {[llength $tf] > 4} { ot_maplist $tf pats col font licon tdicon foreach pat $pats { if [string match $pat $fname] { set dicon $tdicon set havematch 1 break } } } if $havematch break } } $top.i config -image [dsk_image $dicon] } elseif [file_executable $file] { set dicon $defimg_exec if [info exists tkdesk(file_tags,executables)] { foreach tf $tkdesk(file_tags,executables) { if {[llength $tf] > 4} { ot_maplist $tf pats col font licon tdicon foreach pat $pats { if [string match $pat $fname] { set dicon $tdicon set havematch 1 break } } } if $havematch break } } $top.i config -image [dsk_image $dicon] } else { set dicon $defimg_file if [info exists tkdesk(file_tags)] { foreach tf $tkdesk(file_tags) { if {[llength $tf] > 4} { ot_maplist $tf pats col font licon tdicon foreach pat $pats { if [string match $pat $fname] { set dicon $tdicon set havematch 1 break } } } if $havematch break } } $top.i config -image [dsk_image $dicon] } $top.l config -text [_break_name [file tail $file]] } public dontmap 0 protected move_x protected move_y common id_counter 0 common width 12 common defimg_file ficons32/file.xpm common defimg_dir ficons32/dir.xpm common defimg_exec ficons32/exec.xpm } tkdesk-2.0/tcldesk/DiskUsage.tcl0100644000175000007640000001144210020457430014736 0ustar jccjcc# ============================================================================= # # File: DiskUsage.tcl # Project: TkDesk # # Started: 22.10.94 # Changed: 22.10.94 # Author: cb # # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_DiskUsage #s method config {config} #s method refresh {} #s proc id {} #s proc dsk_du {dir} # # ----------------------------------------------------------------------------- # # ============================================================================= # # Class: dsk_DiskUsage # Desc: Creates a window for displaying the disk usage # of a directory. # # Methods: # Procs: # Publics: directory name of directory # itcl_class dsk_DiskUsage { inherit Toplevel constructor {args} { global tkdesk if {$tkdesk(tcl_version) < 8.0} { Toplevel::constructor } wm withdraw $top frame $top.fl -bd 1 -relief raised pack $top.fl -fill x label $top.lDir -text "" pack $top.lDir -in $top.fl -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) cb_listbox $top.flb -vscroll 1 -hscroll 1 -lborder 1 -uborder 1 \ -width 5 -height 1 -font [cb_font $tkdesk(font,mono)] \ -selectmode single $top.flb config -bd 1 -relief raised pack $top.flb -fill both -expand yes bind $top.flb.lbox { dsk_open_dir [lindex [%W get [%W nearest %y]] 1] } bind $top.flb.lbox <3> { dsk_popup {} [lindex [%W get [%W nearest %y]] 1] %X %Y } frame $top.fb -bd 1 -relief raised pack $top.fb -fill x button $top.bClose -text " Close " -command "$this delete" button $top.bRefresh -text " Refresh " -command "$this refresh" pack $top.bClose $top.bRefresh -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 2 wm minsize $top 40 16 #wm geometry $top 40x15 wm title $top "Disk Usage" wm protocol $top WM_DELETE_WINDOW "$this delete" eval config $args } destructor { } # # ----- Methods and Procs ------------------------------------------------- # method config {config} { } method refresh {} { $this config -directory $directory } proc id {} { set i $id incr id return $i } # # ----- Variables --------------------------------------------------------- # public dir "/" { $this config -directory $dir } public directory "/" { global tkdesk #dsk_busy dsk_status "Determining disk usage..." if {$directory != "/"} { set directory [cb_tilde [string trimright $directory "/"] expand] } $top.lDir config -text "Disk Usage of [cb_tilde $directory collapse]:" # automatically read linked directories set err [catch {set d [file readlink $directory]}] if !$err { if [file exists $d] { set directory $d } } $top.bClose config -state disabled $top.bRefresh config -state disabled set du_list \ [dsk_bgexec "$tkdesk(cmd,du) [list $directory] | $tkdesk(cmd,sort) 2>@stderr" \ "Determining disk usage..."] $top.bClose config -state normal $top.bRefresh config -state normal if {$du_list != "break"} { set du_list [split $du_list \n] $top.flb.lbox delete 0 end foreach d $du_list { $top.flb.lbox insert end [string trimright [format "%6s %s" \ [lindex $d 0] [cb_tilde [lrange $d 1 1000] collapse]] "\n"] } $top.flb.lbox config -width 40 \ -height [cb_min 15 [cb_max 2 [llength $du_list]]] update idletasks if {[wm state $top] == "withdrawn"} { dsk_place_window $top diskusage "" wm deiconify $top } } else { if {[wm state $top] == "withdrawn"} { after 200 $this delete } } dsk_status "Ready." #dsk_lazy } common id 0 } # # ----------------------------------------------------------------------------- # # Proc: dsk_du # Args: dir name of directory # Returns: "" # Desc: Creates an object of class dsk_DiskUsage on $dir. # Side-FX: # proc dsk_du {dir} { if ![file isdirectory $dir] { set dir [file dirname $dir] } if {$dir != ""} { dsk_DiskUsage .du[dsk_DiskUsage :: id] -directory $dir } } tkdesk-2.0/tcldesk/Editor.tcl0100644000175000007640000021132210024002754014303 0ustar jccjcc# ============================================================================= # # File: Editor.tcl # Project: TkDesk # # Started: 21.11.94 # Changed: 28.03.96 # Author: cb # # Description: Implements a class for multi-buffer editor windows plus # supporting procs. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_Editor #s method config {config} #s method load {} #s method insertfile {} #s method save {{as ""} {auto 0} {id ""}} #s method print {} #s method buffer {cmd args} #s method _buffer_new {file} #s method _buffer_create {file {insert 0} {asknew 1}} #s method _load_default_cfg {} #s method _read_file {file} #s method _tkdesk {cmd} #s method _buffer_display {id} #s method _buffer_delete {id {dontdel 0}} #s method _buffer_reload {} #s method _changing {type data} #s method close_win {} #s method undo {} #s method copy {} #s method cut {} #s method paste {} #s method gotoline {} #s method search {} #s method _do_search {{exp ""}} #s method _do_replace {{mode "single"}} #s method textWidget {} #s method hypersearch {} #s method _hsearch_callback {line_nr} #s method _do_hsearch {} #s method findsel {} #s method setfont {{what ""}} #s method set_auto_indent {} #s method set_word_wrap {} #s method set_tab_string {{ask 1}} #s method _tabstr {} #s method _dd_drophandler {} #s method set_qmark {nr} #s method goto_qmark {nr} #s method popup {x y} #s proc id {{cmd ""}} #s proc bufferMenu {menu {win ""}} #s proc save_all {} #s proc dsk_editor {cmd args} #s proc dsk_editor_hsearch_cb {t exp} # # ============================================================================= # # ============================================================================= # # Class: dsk_Editor # Desc: Implements a class for multi-buffer editor windows. # # Methods: buffer create - create a new buffer for $file # buffer delete - delete buffer $name # buffer display - switch to buffer $name # Procs: id - used internally to name objects of this class # Publics: # itcl_class dsk_Editor { inherit Toplevel constructor {args} { global tkdesk [set this] if {$tkdesk(editor,bindings) == "nedit"} { set accel(file,new) {Ctrl-N } set accel(file,open) {Ctrl-O } set accel(file,insert) {Ctrl-I } set accel(file,reload) {Alt-R } set accel(file,save) {Ctrl-S } set accel(file,print) {Ctrl-P } set accel(file,close) {Ctrl-W } set accel(edit,undo) {Ctrl-Z } set accel(edit,cut) {Ctrl-X } set accel(edit,copy) {Ctrl-C } set accel(edit,paste) {Ctrl-V } set accel(edit,goto) {Ctrl-L } set accel(edit,selall) {Ctrl-A } set accel(edit,search) {Ctrl-F } set accel(edit,replace) {Ctrl-R } set accel(edit,searchsel) {Ctrl-H } set accel(edit,hyper) {Ctrl-Alt-H } set accel(edit,ff) {Ctrl-Alt-L } set accel(edit,time) {Ctrl-Alt-T } set accel(sel,left) {Ctrl-, } set accel(sel,right) {Ctrl-. } set accel(help,guide) {F1 } } else { set accel(file,new) {Ctrl-N } set accel(file,open) {Ctrl-O } set accel(file,insert) {Ctrl-I } set accel(file,reload) {Alt-R } set accel(file,save) {Alt-S } set accel(file,print) {Ctrl-P } set accel(file,close) {Alt-C } set accel(edit,undo) {Ctrl-Z } set accel(edit,cut) {Ctrl-X } set accel(edit,copy) {Ctrl-C } set accel(edit,paste) {Ctrl-V } set accel(edit,goto) {Ctrl-g } set accel(edit,selall) {Ctrl-A } set accel(edit,search) {Ctrl-S } set accel(edit,replace) {Ctrl-R } set accel(edit,hyper) {Ctrl-H } set accel(edit,searchsel) {Alt-Spc } set accel(edit,ff) {Ctrl-Alt-L } set accel(edit,time) {Ctrl-Alt-T } set accel(sel,left) {Ctrl-, } set accel(sel,right) {Ctrl-. } set accel(help,guide) {F1 } } if {$tkdesk(tcl_version) < 8.0} { Toplevel::constructor } wm withdraw $top frame $top.fMenu -bd 2 -relief raised pack $top.fMenu -fill x # ---- File Menu menubutton $top.fMenu.mbFile -text "File" -underline 0 \ -menu $top.fMenu.mbFile.menu pack $top.fMenu.mbFile -side left menu [set m $top.fMenu.mbFile.menu] $m add command -label "New " -underline 0 \ -command "$this buffer new {}" \ -accelerator [lindex $accel(file,new) 0] $m add command -label "Open... " -underline 0 \ -command "$this load" \ -accelerator [lindex $accel(file,open) 0] $m add cascade -label "Same Dir..." -menu $m.odm menu $m.odm -postcommand "$this _openFromSameDir $m.odm" $m add separator $m add command -label "Insert... " -underline 0 \ -command "$this insertfile" \ -accelerator [lindex $accel(file,insert) 0] $m add command -label "Find... " -underline 0 \ -command "$this findfile" $m add command -label "Reload " -underline 0 \ -command "$this buffer reload" \ -accelerator [lindex $accel(file,reload) 0] $m add separator $m add command -label "Save " -underline 0 \ -command "$this save" \ -accelerator [lindex $accel(file,save) 0] $m add command -label "Save as... " -underline 5 \ -command "$this save as" $m add command -label "Save all" -underline 2 \ -command "dsk_Editor :: save_all" $m add command -label "Print... " -underline 0 \ -command "$this print" \ -accelerator [lindex $accel(file,print) 0] $m add command -label "Mail to... " -underline 0 \ -command "$this mail" $m add separator $m add command -label "Close Buffer " -underline 0 \ -command "$this buffer delete *current*" \ -accelerator [lindex $accel(file,close) 0] $m add command -label "Close Window " -underline 6 \ -command "$this close_win" $m add command -label "Close All " -underline 2 \ -command "dsk_editor delall" # ---- Edit Menu menubutton $top.fMenu.mbEdit -text "Edit" -underline 0 \ -menu $top.fMenu.mbEdit.menu pack $top.fMenu.mbEdit -side left menu [set m $top.fMenu.mbEdit.menu] $m add command -label "Undo" -underline 0 -command "$this undo" \ -accelerator [lindex $accel(edit,undo) 0] -state disabled $m add separator $m add command -label "Cut" -underline 0 -command "$this cut" \ -accelerator [lindex $accel(edit,cut) 0] $m add command -label "Copy" -underline 1 -command "$this copy" \ -accelerator [lindex $accel(edit,copy) 0] $m add command -label "Paste" -underline 0 -command "$this paste" \ -accelerator [lindex $accel(edit,paste) 0] $m add command -label "Select all" -underline 7 \ -command "$top.ft.text tag add sel 1.0 end catch {$this update_status}" \ -accelerator [lindex $accel(edit,selall) 0] $m add separator $m add command -label "Search/Replace... " -underline 0 \ -command "$this search" \ -accelerator [lindex $accel(edit,search) 0] $m add command -label "HyperSearch... " -underline 0 \ -command "$this hypersearch" \ -accelerator [lindex $accel(edit,hyper) 0] $m add command -label "Find Selection" -underline 0 \ -command "$this findsel" \ -accelerator [lindex $accel(edit,searchsel) 0] $m add command -label "Goto Line... " -underline 0 \ -command "$this gotoline" \ -accelerator [lindex $accel(edit,goto) 0] $m add separator $m add command -label "Insert Form Feed " -underline 0 \ -command "$this insert_ff" \ -accelerator [lindex $accel(edit,ff) 0] $m add command -label "Insert Time Stamp " -underline 0 \ -command "$this insert_timestamp" \ -accelerator [lindex $accel(edit,time) 0] # ---- Selection Menu menubutton $top.fMenu.mbSel -text "Selection" -underline 2 \ -menu $top.fMenu.mbSel.menu pack $top.fMenu.mbSel -side left menu [set m $top.fMenu.mbSel.menu] $m add command -label "Shift Left" -underline 6 \ -command "$this sel_op shift_left" \ -accelerator [lindex $accel(sel,left) 0] $m add command -label "Shift Right" -underline 6 \ -command "$this sel_op shift_right" \ -accelerator [lindex $accel(sel,right) 0] $m add separator $m add cascade -label "Pipe..." -menu $m.pm menu $m.pm $m.pm add command -label "Replace..." \ -command "$this sel_op pipe replace" $m.pm add command -label "Insert..." \ -command "$this sel_op pipe insert" $m.pm add command -label "View..." \ -command "$this sel_op pipe view" $m.pm add command -label "Execute..." \ -command "$this sel_op pipe exec" # ---- Options Menu menubutton $top.fMenu.mbOptions -text "Options" -underline 0 \ -menu $top.fMenu.mbOptions.menu pack $top.fMenu.mbOptions -side left menu [set m $top.fMenu.mbOptions.menu] ::set [set this](auto_indent) $tkdesk(editor,auto_indent) $m add checkbutton -label " Auto Indent" -underline 1 \ -variable [set this](auto_indent) \ -command "$this set_auto_indent" ::set [set this](brace_indent) $tkdesk(editor,brace_indent) $m add checkbutton -label " Indent after \"\{\"" -underline 1 \ -variable [set this](brace_indent) $m add separator ::set [set this](quick_load) 0 $m add checkbutton -label " Quick Load" -underline 1 \ -variable [set this](quick_load) \ -command "$this set_quick_load" $m add checkbutton -label " Load Once" -underline 1 \ -variable tkdesk(editor,load_once) ::set [set this](do_backups) $tkdesk(editor,do_backups) $m add checkbutton -label " Create Backups" -underline 1 \ -variable [set this](do_backups) ::set [set this](send_netscape) 0 $m add checkbutton -label " Send to Netscape" -underline 9 \ -variable [set this](send_netscape) ::set [set this](statbar) $tkdesk(editor,statbar) $m add checkbutton -label " Status Bar" -underline 1 \ -variable [set this](statbar) \ -command "$this set_statbar" $m add separator #$m add command -label "Font..." \ # -underline 0 \ # -command "$this setfont" #$m add command -label "Default Font " \ # -underline 0 \ # -command "$this setfont default" $m add cascade -label "Tab Stops" -menu $m.mtab \ -underline 0 menu [set m $m.mtab] ::set [set this](realtabs) $tkdesk(editor,real_tabs) $m add radiobutton -label "Real Tabs" \ -underline 0 \ -variable [set this](realtabs) \ -value 1 \ -command "$this set_tab_string" $m add radiobutton -label "Tab Width..." \ -underline 0 \ -variable [set this](realtabs) \ -value 0 \ -command "$this set_tab_string" set m $top.fMenu.mbOptions.menu $m add cascade -label "Line Wrap" -menu $m.mwrap \ -underline 0 menu [set m $m.mwrap] ::set [set this](wrap) $tkdesk(editor,wrap) $m add radiobutton -label "None" \ -underline 0 \ -variable [set this](wrap) \ -value none \ -command "$this set_line_wrap" $m add radiobutton -label "Character" \ -underline 0 \ -variable [set this](wrap) \ -value char \ -command "$this set_line_wrap" $m add radiobutton -label "Word" \ -underline 0 \ -variable [set this](wrap) \ -value word \ -command "$this set_line_wrap" # ---- Buffer Menu menubutton $top.fMenu.mbBuffer -text "Buffers" -underline 0 \ -menu $top.fMenu.mbBuffer.menu pack $top.fMenu.mbBuffer -side left menu $top.fMenu.mbBuffer.menu \ -postcommand "dsk_Editor :: bufferMenu $top.fMenu.mbBuffer.menu $this" # add dummy entry to work around bug in pre Tk 4.0p2: $top.fMenu.mbBuffer.menu add command -label "dummy" tk_menuBar $top.fMenu $top.fMenu.mbFile $top.fMenu.mbEdit \ $top.fMenu.mbOptions $top.fMenu.mbBuffer tk_bindForTraversal $top # ---- Help Menu menubutton $top.fMenu.mbHelp -text "Help" -underline 0 \ -menu $top.fMenu.mbHelp.menu pack $top.fMenu.mbHelp -side right menu [set m $top.fMenu.mbHelp.menu] $m add command -label "User's Guide " \ -command "dsk_help guide" \ -accelerator [lindex $accel(help,guide) 0] $m add command -label "Manual Page... " \ -command "dsk_man" $m add separator $m add command -label "Getting Started" \ -command "dsk_help quick" $m add command -label "TkDesk FAQ" \ -command "dsk_help faq" $m add command -label "Changes" \ -command "dsk_help changes" $m add command -label "License" \ -command "dsk_help license" $m add separator $m add command -label "About TkDesk..." \ -command dsk_about if $tkdesk(in_development) { $m add command -label "About TkDesk (Web)..." \ -command dsk_about_web } # ---- Text Widget frame $top.f1 -bd 1 -relief raised pack $top.f1 -fill both -expand yes cb_text $top.ft -vscroll $tkdesk(side_of_scrollbars) -hscroll 0 \ -pad $tkdesk(pad) -height 5 \ -bd 2 -relief sunken -lborder 1 -setgrid 1 \ -wrap char -font [cb_font $tkdesk(editor,font)] \ -exportselection 1 -bg [cb_col $tkdesk(color,text)] \ -insertbackground [cb_col $tkdesk(color,insert)] pack $top.ft -in $top.f1 -fill both -expand yes -pady $tkdesk(pad) blt_drag&drop target $top.ft.text handler \ file "$this _dd_drophandler %v" # ---- Status Bar frame [set f $top.fs] -bd 1 -relief raised if {$tkdesk(editor,statbar)} { pack $f -fill x } label $f.l -font [cb_font $tkdesk(font,status)] pack $f.l -fill x -padx [expr $tkdesk(pad) / 2] \ -pady [expr $tkdesk(pad) / 2] set stat_label $f.l # # Bindings # bind $top { if $tkdesk(focus_follows_mouse) { if {[grab current] == ""} { focus [winfo toplevel %W].ft.text } } } # Common bindings bind $top.ft.text "$this popup %X %Y; break" bind $top.ft.text <3> "$this popup %X %Y; break" # Create binding tag for status bar: bind status-$this "catch {$this update_status}" bind status-$this "catch {$this update_status}" bindtags $top.ft.text "$top.ft.text Text status-$this all" # Customized bindings bind $top.ft.text [lindex $accel(file,new) 1] \ "$this buffer new {}; break" bind $top.ft.text [lindex $accel(file,open) 1] \ "$this load; break" bind $top.ft.text [lindex $accel(file,insert) 1] \ "$this insertfile; break" bind $top.ft.text [lindex $accel(file,reload) 1] \ "$this buffer reload; break" bind $top.ft.text [lindex $accel(file,save) 1] \ "$this save; break" bind $top.ft.text [lindex $accel(file,print) 1] \ "$this print; break" bind $top.ft.text [lindex $accel(file,close) 1] \ "$this buffer delete *current*; break" bind $top.ft.text [lindex $accel(edit,undo) 1] \ "$this undo; break" bind $top.ft.text [lindex $accel(edit,cut) 1] \ "$this cut; break" bind $top.ft.text [lindex $accel(edit,copy) 1] \ "$this copy; break" bind $top.ft.text [lindex $accel(edit,paste) 1] \ "$this paste; break" bind $top.ft.text [lindex $accel(edit,selall) 1] \ "$top.ft.text tag add sel 1.0 end; catch {$this update_status} break" bind $top.ft.text [lindex $accel(edit,goto) 1] \ "$this gotoline; break" bind $top.ft.text [lindex $accel(edit,search) 1] \ "$this search; break" bind $top.ft.text [lindex $accel(edit,replace) 1] \ "$this search; break" bind $top.ft.text [lindex $accel(edit,hyper) 1] \ "$this hypersearch; break" bind $top.ft.text [lindex $accel(edit,searchsel) 1] \ "$this findsel; break" bind $top.ft.text [lindex $accel(edit,ff) 1] \ "$this insert_ff; break" bind $top.ft.text [lindex $accel(edit,time) 1] \ "$this insert_timestamp; break" bind $top.ft.text [lindex $accel(sel,left) 1] \ "$this sel_op shift_left; break" bind $top.ft.text [lindex $accel(sel,right) 1] \ "$this sel_op shift_right; break" bind $top.ft.text [lindex $accel(help,guide) 1] \ "dsk_help guide; break" cb_manage_secondary $top $top-search $top-hsearch for {set i 0} {$i < 10} {incr i} { bind $top.ft.text "$this set_qmark $i" bind $top.ft.text "$this goto_qmark $i" bind $top.ft.text "$this goto_qmark $i" } global cb_Text set cb_Text(change_callback,$top.ft.text) "$this _changing" set_auto_indent set_line_wrap set_tab_string 0 # # Window manager settings # regsub {[1-90][1-90]*x[1-90][1-90]*} \ $tkdesk(editor,default_geometry) "" xy if {$xy != ""} { wm geometry $top $tkdesk(editor,default_geometry) } else { set w [lindex [split $tkdesk(editor,default_geometry) x+-] 0] set h [lindex [split $tkdesk(editor,default_geometry) x+-] 1] dsk_place_window $top editor $tkdesk(editor,default_geometry) 1 } wm minsize $top 20 5 wm title $top "no file" wm protocol $top WM_DELETE_WINDOW "$this close_win" if $tkdesk(fvwm) { # create the icon window toplevel $top-icon -bg [cb_col $tkdesk(color,icon)] \ -class Icon wm withdraw $top-icon label $top-icon.label \ -image [dsk_image $tkdesk(icon,editor)] \ -bd 0 -bg [cb_col $tkdesk(color,icon)] pack $top-icon.label -ipadx 2 -ipady 2 blt_drag&drop target $top-icon.label handler \ file "$this _dd_drophandler %v" update idletasks wm geometry $top-icon \ [winfo reqwidth $top-icon]x[winfo reqheight $top-icon] wm protocol $top-icon WM_DELETE_WINDOW "$this delete" wm iconwindow $top $top-icon update idletasks } else { wm iconbitmap $top @$tkdesk(library)/images/xbm/pencil3.xbm } # Other inits set current(buffer) "" ::set [set this](case) 0 ::set [set this](regexp) 0 ::set [set this](back) 0 # Populate HyperSearch history if still empty if {[hsearch_history get] == {}} { # Linux HOWTO: hsearch_history add {^ [1-9]\.} # UNIX man pages: hsearch_history add {^[A-Z][-A-Z1-90 ]*$} # Tcl/itcl code: hsearch_history add {^[ ]*proc|^[ ]*method|^[ ]*class|^[ ]*itcl_class|^[ ]*constructor|^[ ]*destructor} # C/C++ functions: hsearch_history add {^[a-zA-Z][a-zA-Z _1-90]*\(.*[\),]$} } if {$args != {}} { eval config $args } if {!$files_set} { cb_deiconify $top } update } destructor { global [set this] catch {::unset [set this]} catch {destroy $top-icon} catch {destroy $top-search} catch {destroy $top-hsearch} } # # ----- Methods and Procs ------------------------------------------------- # method config {config} { } method cget {var} { return [set [string trimleft $var -]] } method load {} { global tkdesk set curfile $buffer($currentid,file) if {[string match "New *" $curfile] || \ [string first " " $curfile] > -1} { set filter [dsk_active dir]* } else { if [file readable [file dirname $curfile]] { set filter [string trimright [file dirname $curfile] "/"]/* } else { set filter [dsk_active dir]* } } while {1} { set file [dsk_filesel "Select a file to edit:" $filter showall] if {$file != ""} { if [file isdirectory $file] { dsk_errbell cb_error "$file is a directory. Please choose a file." } else { $this buffer create $file break } } else { break } } } method insertfile {} { set curfile $buffer($currentid,file) if {[string match "New *" $curfile] || \ [string first " " $curfile] > -1} { set filter [dsk_active dir]* } else { if [file readable [file dirname $curfile]] { set filter [string trimright [file dirname $curfile] "/"]/* } else { set filter [dsk_active dir]* } } #set file [cb_fileSelector -filter $filter \ #-label "Select file to insert:" -showall 1] while {1} { set file [dsk_filesel "Select a file to insert:" $filter showall] if {$file != ""} { if [file isdirectory $file] { dsk_errbell cb_error "$file is a directory. Please choose a file." } else { $this buffer create $file 1 break } } else { break } } } method findfile {} { global tkdesk set curfile [cb_tilde $buffer($currentid,file) expand] dsk_find_files -path [file dirname $curfile] \ -mask *[file extension $curfile] } method _openFromSameDir {m} { set curfile $buffer($currentid,file) if [file exists $curfile] { set mdir [file dirname $curfile] return [dsk_casdirs $mdir $m 1 "$this buffer create %d" 1] } else { return "" } } method save {{as ""} {auto 0} {id ""}} { global tkdesk [set this] if {$id == ""} { set id $currentid } set curfile $buffer($id,file) if {[string match "New *" $curfile] || \ [string match "* (Output)" $curfile]} { set filter [dsk_active dir]* } else { if [file readable [file dirname $curfile]] { set filter [string trimright [file dirname $curfile] "/"]/* } else { set filter [dsk_active dir]* } } set file $buffer($id,file) set old_fname $buffer($id,file) set old_auto_fname [file dirname $file]/\#[file tail $file]\# if {$as != "" \ || [string match "New *" $buffer($id,file)] \ || [string match "* (Output)" $curfile] \ || $buffer($id,readonly)} { if $auto { return "ok" } #set fname [cb_fileSelector -filter $filter \ #-label "Save file as:" -showall 1] set fname [dsk_filesel "Save file as:" $filter showall] if {$fname == ""} { return "cancel" } else { set buffer($id,file) $fname set file $fname } } if $auto { if [set [set this](do_backups)] { set file [file dirname $file]/\#[file tail $file]\# } } else { # make a backup copy of the file if {[info exists file] && [set [set this](do_backups)]} { catch {exec cp $file $file~} } } set ext [file extension $file] if {$ext == ".gz" || $ext == ".z"} { set err [catch {set fd [open "|gzip >\"$file\"" w]}] } elseif {$ext == ".Z"} { set err [catch {set fd [open "|compress >\"$file\"" w]}] } else { set err [catch {set fd [open "$file" w]}] } if $err { dsk_errbell cb_error "Couldn't open $file for writing!" set buffer($id,file) $old_fname return "cancel" } dsk_busy dsk_catch { if {$id == $currentid} { puts -nonewline $fd [$top.ft.text get 1.0 "end - 1 chars"] } else { puts -nonewline $fd $buffer($id,text) } close $fd } if !$auto { set changed($id) 0 set buffer($id,readonly) 0 set buffer($id,newfile) 0 set file [subst -nocommands $file] if {$id == $currentid} { wm title $top "[cb_tilde $file collapse]" wm iconname $top "[file tail $file]" } # remove a previous auto-save copy: if [file exists $old_auto_fname] { catch {exec rm $old_auto_fname} } catch { if [set [set this](send_netscape)] { dsk_netscape file [cb_tilde $file expand] } } } $top.ft.text config -state normal dsk_lazy return "ok" } method print {} { global tkdesk tmppcmd dsk_print_string [$top.ft.text get 1.0 end] } method mail {} { global tkdesk tmppcmd dsk_mail "" [$top.ft.text get 1.0 end] } method buffer {cmd args} { switch -- $cmd { new {return [eval _buffer_new $args]} create {return [eval _buffer_create $args]} delete {return [eval _buffer_delete $args]} display {return [eval _buffer_display $args]} reload {return [eval _buffer_reload $args]} } } method _buffer_new {file} { if {$file == ""} { set file "New File" } set id [incr bufcount] set buffer($id,file) $file set buffer($id,text) "" set buffer($id,vpos) 0 set buffer($id,cursor) 1.0 set buffer($id,win) $this set buffer($id,newfile) 1 if {[file writable [file dirname $file]] \ || [string match "New *" $buffer($id,file)] \ || [string match "* (Output)" $buffer($id,file)]} { set buffer($id,readonly) 0 } else { set buffer($id,readonly) 1 } set changed($id) 0 set undo_enabled($id) 0 set undo_list($id) "" set undo_pointer($id) -1 $this buffer display $id return $id } method _buffer_create {file {insert 0} {asknew 1}} { global tkdesk [set this] set id "" set linenum 0 if ![file exists $file] { if !$insert { set le [lindex $file 0] if [regexp {^\+[1-90]+$} $le] { set linenum [string trimleft $le "+"] set file [lrange $file 1 [llength $file]] } else { set id [$this buffer new $file] return $id } } else { cb_error "$file does not exist." return } } if [file isdirectory $file] { dsk_errbell cb_error "Please use TkDesk's file windows to edit directories! ;-)" #catch "$this delete" return -1 } # see if this file is already loaded if {$tkdesk(editor,load_once)} { for {set id 1} {$id <= $bufcount} {incr id} { if ![info exists buffer($id,file)] continue if {$buffer($id,file) == $file} { dsk_debug "displaying id: $id" if {$linenum > 0} { set buffer($id,linenum) $linenum } $buffer($id,win) buffer display $id return -1 } } } if {[file size $file] > 500000} { if {[cb_okcancel "You're about to load a very large file into the editor. This will probably take ages and require a ridiculous amount of memory. Continue anyway?"] == 1} { #if !$insert { # catch "$this delete" #} return -1 } } set ext [file extension $file] if {$ext == ".gz" || $ext == ".z"} { set err [catch {set fd [open "|gzip -cd \"[cb_tilde $file expand]\""]}] } elseif {$ext == ".Z"} { set err [catch {set fd [open "|zcat \"[cb_tilde $file expand]\""]}] } else { set err [catch {set fd [open $file]}] } if $err { dsk_errbell catch "cb_error \"Error: Couldn't open $file for reading!\"" #if !$insert { # catch {$this delete} #} return -1 } set err [dsk_catch { dsk_busy if !$insert { set id [incr bufcount] set buffer($id,file) $file set buffer($id,text) [read $fd] set buffer($id,vpos) 0 set buffer($id,cursor) 1.0 set buffer($id,win) $this set buffer($id,newfile) 0 set changed($id) 0 set undo_enabled($id) 0 set undo_list($id) "" set undo_pointer($id) -1 if ![file writable $file] { #cb_alert "Opening file read-only." set buffer($id,readonly) 1 } else { set buffer($id,readonly) 0 } } else { $top.ft.text insert insert [read $fd] cb_Text_change_callback $top.ft.text } close $fd dsk_lazy }] if $err { #if !$insert { # catch "$this delete" #} return -1 } if $insert { return 0 } if {$linenum > 0} { set buffer($id,linenum) $linenum } $this buffer display $id if {[string first $tkdesk(configdir) $file] == 0} { if ![winfo exists $top.fMenu.mbTkDesk] { menubutton $top.fMenu.mbTkDesk -text "Configuration" \ -menu [set m $top.fMenu.mbTkDesk.m] \ -underline 0 pack propagate $top.fMenu 0 pack $top.fMenu.mbTkDesk -side left menu $m $m add cascade -label "Load..." \ -menu $m.cfg menu $m.cfg foreach cf $tkdesk(configfiles) { set cfs [format "%-12s" $cf] $m.cfg add command \ -label "$cfs ($tkdesk(configxpl,$cf))" \ -command "$this buffer create $tkdesk(configdir)/$cf" } $m add command -label "Find in Config..." \ -command "dsk_find_files -path $tkdesk(configdir) -name [list $tkdesk(configfiles)] -type file" $m add separator $m add command -label "Save and Reload into TkDesk " \ -command "$this _tkdesk reload" \ -accelerator F5 $m add command -label "Save, Reload and Close" \ -command "$this _tkdesk reload_and_close" \ -accelerator F6 $m add command -label "Evaluate Selection" \ -command {dsk_catch {eval [selection get]}} \ -accelerator F7 $m add command -label "Load Default Version" \ -command "$this _load_default_cfg" \ -accelerator F8 $m add separator $m add command -label "Colors..." \ -command "dsk_config_panel colors $top.ft.text" \ -accelerator F9 $m add command -label "Fonts..." \ -command "dsk_config_panel fonts $top.ft.text" \ -accelerator F10 $m add command -label "Icons..." \ -command "dsk_config_panel icons $top.ft.text" \ -accelerator F11 $m add command -label "Sounds..." \ -command "dsk_config_panel sounds $top.ft.text" \ -accelerator F12 bind $top.ft.text "$this _tkdesk reload" bind $top.ft.text "$this _tkdesk reload_and_close" bind $top.ft.text {dsk_catch {eval [selection get]}} bind $top.ft.text "$this _load_default_cfg" bind $top.ft.text \ "dsk_config_panel colors $top.ft.text" bind $top.ft.text \ "dsk_config_panel fonts $top.ft.text" bind $top.ft.text \ "dsk_config_panel icons $top.ft.text" bind $top.ft.text \ "dsk_config_panel sounds $top.ft.text" } } return $id } method _load_default_cfg {} { global tkdesk set f $tkdesk(library)/configs/[file tail $buffer($currentid,file)] buffer create $f } method _read_file {file} { } method _tkdesk {cmd} { global tkdesk set id $currentid set curfile [cb_tilde $buffer($currentid,file) expand] if {[string first [cb_tilde $tkdesk(configdir) expand] $curfile] < 0} { dsk_errbell cb_error "This buffer does currently not contain one of TkDesk's configuration files." return } switch -- $cmd { "reload" { save dsk_reread_config [file tail $curfile] } "reload_and_close" { save buffer delete *current* dsk_reread_config [file tail $curfile] } } } method _buffer_display {id} { global tkdesk [set this] if $dont_display return if {$currentid != -1} { set buffer($currentid,vpos) [lindex \ [cb_old_sb_get $top.ft.vscroll] 2] set buffer($currentid,cursor) [$top.ft.text index insert] set buffer($currentid,text) [$top.ft.text get 1.0 "end - 1 chars"] } $top.ft.text config -state normal $top.ft.text delete 1.0 end $top.ft.text insert end $buffer($id,text) $top.ft.text yview $buffer($id,vpos) $top.ft.text mark set insert $buffer($id,cursor) #if $buffer($id,readonly) { # $top.ft.text config -bg [cb_col $tkdesk(color,background)] #} else { # $top.ft.text config -bg [cb_col $tkdesk(color,text)] #} if [info exists buffer($id,linenum)] { $top.ft.text mark set insert $buffer($id,linenum).0 $top.ft.text see insert unset buffer($id,linenum) } if {[wm state $top] != "normal"} { cb_deiconify $top } else { raise $top } set currentid $id set name $buffer($id,file) if $changed($id) { set t "* [cb_tilde $name collapse]" if {$buffer($id,newfile) && $name != "New File"} { append t " (new)" } if {$buffer($id,readonly)} { append t " (read-only)" } wm title $top $t wm iconname $top "*[file tail $name]" } else { set t [cb_tilde $name collapse] if {$buffer($id,newfile) && $name != "New File"} { append t " (new)" } if {$buffer($id,readonly)} { append t " (read-only)" } wm title $top $t wm iconname $top [file tail $name] } if [info exists tkdesk(editor,findexp)] { if {$tkdesk(editor,findexp) != ""} { set [set this](regexp) $tkdesk(editor,findisreg) _do_search $tkdesk(editor,findexp) } } if [winfo exists $top-hsearch] { _do_hsearch } update_status } method _buffer_delete {id {dontdel 0}} { if {$id == "*current*"} { set id $currentid } if ![info exists changed($id)] return if $changed($id) { if {$currentid != $id} { set dd $dont_display set dont_display 0 $this buffer display $id set dont_display $dd } cb_raise $top catch {destroy $top.tqmod} set ans [cb_dialog $top.tqmod "File modified" \ "[file tail $buffer($id,file)]:\nThis file has been modified. Save it?" \ questhead 0 "Yes" "No" "Cancel"] if {$ans == 0} { set ret [save] if {$ret == "cancel"} { return "cancel" } } elseif {$ans == 2} { return "cancel" } } dsk_busy # remove auto save copy: if ![info exists buffer($id,file)] { dsk_lazy return } set file $buffer($id,file) set file [file dirname $file]/\#[file tail $file]\# if [file exists $file] { catch {exec rm -f $file} } if !$dont_display { $top.ft.text delete 1.0 end } set currentid -1 unset buffer($id,file) unset buffer($id,text) unset buffer($id,vpos) unset buffer($id,cursor) unset buffer($id,win) unset changed($id) unset undo_enabled($id) unset undo_list($id) unset undo_pointer($id) # delete quickmarks associated with this buffer for {set nr 0} {$nr < 10} {incr nr} { if {[lindex $qmark($nr) 0] == $id} { set qmark($nr) "" } } set id -1 for {set i 1} {$i <= $bufcount} {incr i} { if [info exists buffer($i,file)] { if [info exists buffer($i,win)] { if {$buffer($i,win) == $this} { set id $i break } } else { unset buffer($i,file) } } } dsk_lazy if {$id != -1} { $this buffer display $id return "" } else { if !$dontdel { $this delete } } } method _buffer_reload {} { set id $currentid if $changed($id) { if {$currentid != $id} { $this buffer display $id } cb_raise $top catch {destroy $top.tqmod} set ans [cb_dialog $top.tqmod "File modified" \ "This file has been modified. Save it?" \ questhead 0 "Yes" "No" "Cancel"] if {$ans == 0} { save } elseif {$ans == 2} { return "cancel" } } set file $buffer($id,file) set ext [file extension $file] if {$ext == ".gz" || $ext == ".z"} { set err [catch {set fd [open "|gzip -cd \"$file\""]}] } elseif {$ext == ".Z"} { set err [catch {set fd [open "|zcat \"$file\""]}] } else { set err [catch {set fd [open $file]}] } if $err { dsk_errbell cb_error "Error: Couldn't open $file for reading!" return } dsk_busy dsk_catch { set buffer($id,text) [read $fd] set changed($id) 0 close $fd } wm title $top [cb_tilde $file collapse] wm iconname $top "[file tail $file]" dsk_lazy set buffer($id,vpos) [lindex [cb_old_sb_get $top.ft.vscroll] 2] set buffer($id,cursor) [$top.ft.text index insert] $top.ft.text delete 1.0 end $top.ft.text insert end $buffer($id,text) $top.ft.text yview $buffer($id,vpos) $top.ft.text mark set insert $buffer($id,cursor) update_status } method _changing {type data} { if {$currentid < 0} return #puts "(_changing) type: $type, data: $data" if !$changed($currentid) { set changed($currentid) 1 set file $buffer($currentid,file) set t "* [cb_tilde $file collapse]" if {$buffer($currentid,newfile) && $file != "New File"} { append t " (new)" } if {$buffer($currentid,readonly)} { append t " (read-only)" } wm title $top $t wm iconname $top "*[file tail $file]" } incr autosavecount if {$autosavecount >= $autosaveperiod} { set autosavecount 0 save "" 1 } # prepare for undo if !$undo_enabled($currentid) { set undo_enabled($currentid) 1 $top.fMenu.mbEdit.menu entryconfigure "Undo" -state normal } set ip [$top.ft.text index insert] set selr [$top.ft.text tag ranges sel] if {$selr != ""} { set ip [lindex $selr 0] } set ule "" switch -- $type { insert { if {$selr != ""} { set selb [eval $top.ft.text get $selr] set utype replace } else { set selb {} set utype add } set ule [list $ip $utype $data $selb] } paste { set x [lindex $data 0] set y [lindex $data 1] set ip [$top.ft.text index @$x,$y] set data [lindex $data 2] set selb {} set utype add set ule [list $ip $utype $data $selb] } replace { # called from _do_replace ot_maplist $data start end text set ule [list $start replace $text \ [$top.ft.text get $start $end]] } delete { switch -- $data { backchar - char { if {$selr != ""} { set selb [eval $top.ft.text get $selr] set data {} } else { if {$data == "char"} { set selb {} set data [$top.ft.text get insert] } else { set selb {} set data [$top.ft.text get "insert - 1 chars"] set ip [$top.ft.text index "$ip - 1 chars"] } } set ule [list $ip remove $data $selb] } word { set ule [list $ip remove \ [$top.ft.text get insert "insert wordend"] {}] } lineend { set ule [list $ip remove \ [$top.ft.text get insert "insert lineend"] {}] } } } } if {$ule != ""} { incr undo_pointer($currentid) catch { set undo_list($currentid) \ [lreplace $undo_list($currentid) \ $undo_pointer($currentid) 10000] } if {$undo_pointer($currentid) > $undo_size} { # prevent the undo list from growing infinitely set undo_list($currentid) \ [lreplace $undo_list($currentid) 0 \ [expr $undo_pointer($currentid) \ - $undo_size]] } lappend undo_list($currentid) $ule } #set undo_pointer [expr [llength $undo_list($currentid)] - 1] } method close_win {} { set ret "" set dont_display 1 for {set id 1} {$id <= $bufcount} {incr id} { if [info exists buffer($id,file)] { catch { if {$buffer($id,win) == $this} { if {[$this buffer delete $id] == "cancel"} { set ret cancel } } } } if {$ret == "cancel"} break } set dont_display 0 return $ret } method undo {} { if $undo_enabled($currentid) { if {$undo_pointer($currentid) < 0} { dsk_bell return } if {$undo_pointer($currentid) \ >= [llength $undo_list($currentid)]} { # just to be safe... set undo_pointer($currentid) \ [expr [llength $undo_list($currentid)] - 1] } set ul [lindex $undo_list($currentid) $undo_pointer($currentid)] #puts $ul incr undo_pointer($currentid) -1 if {$undo_pointer($currentid) < 0} { set undo_enabled($currentid) 0 $top.fMenu.mbEdit.menu entryconfigure "Undo" -state disabled set changed($currentid) 0 set file $buffer($currentid,file) wm title $top "[cb_tilde $file collapse]" wm iconname $top "[file tail $file]" } ot_maplist $ul inspos type data sel switch -- $type { replace - add { switch -- $data { - { $top.ft.text delete $inspos } default { $top.ft.text delete $inspos \ [$top.ft.text index \ "$inspos + [string length $data] chars"] } } if {$type == "replace"} { $top.ft.text insert $inspos $sel $top.ft.text mark set insert \ "$inspos + [string length $sel] chars" } else { $top.ft.text mark set insert $inspos } } remove { if {$data != ""} { $top.ft.text insert $inspos $data $top.ft.text mark set insert \ "$inspos + [string length $data] chars" } else { $top.ft.text insert $inspos $sel $top.ft.text mark set insert \ "$inspos + [string length $sel] chars" } } } $top.ft.text see $inspos update_status if {$undo_pointer($currentid) >= 0 && !$changed($currentid)} { set changed($currentid) 1 set file $buffer($currentid,file) wm title $top "* [cb_tilde $file collapse]" wm iconname $top "*[file tail $file]" incr autosavecount } } else { dsk_bell } } method copy {} { global cb_Text set sel [$top.ft.text tag ranges sel] if {$sel != ""} { set cutbuffer [eval $top.ft.text get $sel] selection clear $top.ft.text catch "unset cb_Text(selstart)" } } method cut {} { global cb_Text set sel [$top.ft.text tag ranges sel] if {$sel != ""} { cb_Text_change_callback $top.ft.text delete char set cutbuffer [eval $top.ft.text get $sel] eval $top.ft.text delete $sel selection clear $top.ft.text catch "unset cb_Text(selstart)" } } method paste {} { global cb_Text if {[string length "$cutbuffer"] > 0} { set t $top.ft.text $t yview -pickplace insert set bb [$t bbox [$t index insert]] $t insert insert $cutbuffer $t yview -pickplace insert if {$bb != ""} { cb_Text_change_callback $top.ft.text paste [list \ [lindex $bb 0] [lindex $bb 1] $cutbuffer] } selection clear $top.ft.text catch "unset cb_Text(selstart)" } else { dsk_bell } } method gotoline {} { global tmplnr set curline [lindex [split [$top.ft.text index insert] "."] 0] set tmplnr "" cb_readString "Goto line (current: $curline):" tmplnr "Goto Line" 10 if {$tmplnr != ""} { # test if $tmplnr contains a number: set err [catch {$top.ft.text mark set insert $tmplnr.0}] if !$err { $top.ft.text yview -pickplace insert update_status } else { dsk_errbell cb_error "Invalid line number!" } } unset tmplnr } method search {} { global tkdesk [set this] set t "$top-search" if [winfo exists $t] { cb_raise $t focus $t.es return } toplevel $t wm withdraw $t frame $t.fs -bd 1 -relief raised pack $t.fs -fill both -expand yes frame $t.fsf pack $t.fsf -in $t.fs -fill both -expand yes \ -padx $tkdesk(pad) -pady $tkdesk(pad) frame $t.fslf pack $t.fslf -in $t.fsf -fill x label $t.ls -text "Search for:" -anchor w pack $t.ls -in $t.fslf -side left checkbutton $t.cbRegexp -text "RegExp" -relief flat \ -variable [set this](regexp) -underline 0 pack $t.cbRegexp -in $t.fslf -side right checkbutton $t.cbCase -text "Case " -relief flat \ -variable [set this](case) -underline 0 pack $t.cbCase -in $t.fslf -side right checkbutton $t.cbBack -text "Backward " -relief flat \ -variable [set this](back) -underline 0 pack $t.cbBack -in $t.fslf -side right frame $t.fsfe entry $t.es -bd 2 -relief sunken -width 40 -exportselection no $t.es insert end $searchexp pack $t.es -in $t.fsfe -fill x -expand yes \ -side left -ipady 2 -pady $tkdesk(pad) menubutton $t.mbSHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $t.mbSHist.menu pack $t.mbSHist -in $t.fsfe -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipadx 2 -ipady 2 menu $t.mbSHist.menu -postcommand "$this _histmenu search_history $t.mbSHist.menu $t.es" # add dummy entry to work around bug in pre Tk 4.0p2: $t.mbSHist.menu add command -label "dummy" search_history changed pack $t.fsfe -in $t.fsf -fill x -expand yes bind $t.es {%W selection range 0 end} bind $t.es <1> \ "focus %W ;\ $t.bReplace config -relief flat ;\ $t.bSearch config -relief sunken" bind $t.es \ "$this _do_search ;\ focus $t.er ;\ $t.bSearch config -relief flat ;\ $t.bReplace config -relief sunken; break" bind $t.es "destroy $t; focus -force $top.ft.text; break" bind $t.es "$this _do_search; break" bind $t.es "$this _do_search; break" #bind $t.es "+$this _do_search" cb_addShortcutBinding $t.es $t.cbRegexp "r" cb_addShortcutBinding $t.es $t.cbCase "c" cb_addShortcutBinding $t.es $t.cbBack "b" frame $t.fr -bd 1 -relief raised pack $t.fr -fill both -expand yes frame $t.frf pack $t.frf -in $t.fr -fill both -expand yes \ -padx $tkdesk(pad) -pady $tkdesk(pad) label $t.lr -text "Replace with:" -anchor w pack $t.lr -in $t.frf -anchor w frame $t.frfr entry $t.er -bd 2 -relief sunken -width 40 -exportselection no pack $t.er -in $t.frfr -side left \ -fill x -expand yes -ipady 2 -pady $tkdesk(pad) menubutton $t.mbRHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $t.mbRHist.menu pack $t.mbRHist -in $t.frfr -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipadx 2 -ipady 2 menu $t.mbRHist.menu -postcommand "$this _histmenu replace_history $t.mbRHist.menu $t.er" # add dummy entry to work around bug in pre Tk 4.0p2: $t.mbRHist.menu add command -label "dummy" replace_history changed pack $t.frfr -in $t.frf -fill x -expand yes bind $t.er {%W selection range 0 end} bind $t.er <1> \ "$this _do_search ;\ focus %W ;\ $t.bSearch config -relief flat ;\ $t.bReplace config -relief sunken" bind $t.er \ "focus $t.es ;\ $t.bReplace config -relief flat ;\ $t.bSearch config -relief sunken" bind $t.er "destroy $t; focus -force $top.ft.text" bind $t.er "$this _do_replace" cb_addShortcutBinding $t.er $t.cbRegexp "r" cb_addShortcutBinding $t.er $t.cbCase "c" cb_addShortcutBinding $t.er $t.cbBack "b" frame $t.fb -bd 1 -relief raised pack $t.fb -fill x cb_button $t.bSearch -text "Search" -default 1 \ -command "$this _do_search" cb_button $t.bReplace -text "Replace" \ -command "$this _do_replace" cb_button $t.bRepAll -text "Replace all" \ -command "$this _do_replace all" cb_button $t.bClose -text " Close " -command "destroy $t; focus -force $top" pack $t.bSearch $t.bReplace $t.bRepAll $t.bClose \ -in $t.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) bind $t \ "if \$tkdesk(focus_follows_mouse) \{ \ if \{\[focus\] != \"$t.er\"\} \{ focus $t.es $t.bReplace config -relief flat $t.bSearch config -relief sunken \} \}" wm title $t "Search/Replace" wm minsize $t 311 170 update idletasks # determine position of search window: set rw [winfo reqwidth $t] set rh [winfo reqheight $t] set ww [winfo width $top] set wh [winfo height $top] set sw [winfo screenwidth $t] set sh [winfo screenheight $t] set wx [winfo x $top] set wy [winfo y $top] set dy 34 ;# offset for window decoration set x $wx if {$x < 0} {set x 0} if {$x + $rw > $sw} {set x [expr $sw - $rw]} set h1 $wy set h2 [expr $sh - $wy - $wh] if {$h1 > $h2} { set y [expr $wy - $rh] if {$y < 0} {set y 0} } else { set y [expr $wy + $wh] if {$y + $rh + $dy > $sh} { set y [expr $sh - $rh - $dy] } } #puts "$wx $wy $ww $wh, $rw $rh, $h1 $h2, $x $y" wm geometry $t +$x+$y cb_deiconify $t focus $t.es } method _histmenu {histobj menu entry} { global tkdesk catch {$menu delete 0 last} if $tkdesk(sort_history) { set l [lsort [$histobj get]] } else { set l [$histobj get] } foreach ent $l { $menu add command -label $ent \ -command "$entry delete 0 end ;\ $entry insert end \[list $ent\]" \ -font [cb_font $tkdesk(font,entries)] } } method _do_search {{exp ""}} { global [set this] if {$exp == ""} { set exp [$top-search.es get] } if {$exp == ""} return set searchexp $exp search_history add [list $exp] set match_range "" set tw $top.ft.text catch "$tw tag remove sel 1.0 end" dsk_busy set stidx "insert" set success 0 set scmd "$tw search -count cnt" if ![set [set this](case)] { append scmd " -nocase" } if [set [set this](regexp)] { append scmd " -regexp" } set scmd1 $scmd if [set [set this](back)] { append scmd " -backwards -- [list $exp] insert 1.0" } else { append scmd " -- [list $exp] insert end" } set err [catch {set mrange [eval $scmd]}] if {$err && [set [set this](regexp)]} { dsk_lazy dsk_errbell cb_error "Error in regular expression." return } if {$mrange != ""} { set success 1 lappend mrange [$tw index "$mrange + $cnt chars"] } dsk_lazy if !$success { if [set [set this](back)] { set restart ![cb_yesno "No more matches. Restart at bottom?"] } else { set restart ![cb_yesno "No more matches. Restart at top?"] } if $restart { dsk_busy set stidx "1.0" set success 0 set scmd $scmd1 if [set [set this](back)] { append scmd " -backwards -- [list $exp] end 1.0" } else { append scmd " -- [list $exp] 1.0 end" } set err [catch {set mrange [eval $scmd]} errmsg] if {$err && [set [set this](regexp)]} { dsk_lazy dsk_errbell cb_error "Error in regular expression." return } if {$mrange != ""} { set success 1 lappend mrange [$tw index "$mrange + $cnt chars"] } dsk_lazy } else { return } } if $success { set mstart [lindex $mrange 0] set mend [lindex $mrange 1] set match_range $mrange $tw tag add sel $mstart $mend $tw yview -pickplace $mstart if [set [set this](back)] { $tw mark set insert $mstart $tw mark set anchor $mstart } else { $tw mark set insert $mend $tw mark set anchor $mend } update_status #$tw mark set insert $mstart set search_regexp $exp } else { dsk_bell } } method _do_replace {{mode "single"}} { global [set this] cb_Text if {$match_range == ""} { $this _do_search } if {$match_range == ""} return set mstart [lindex $match_range 0] if {$mode == "single"} { set mend [lindex $match_range 1] } else { set mend end } set subexp [$top-search.er get] #if {$subexp == ""} return replace_history add [list $subexp] puts "subexp: $subexp" dsk_busy set exp $search_regexp if ![set [set this](regexp)] { set exp [dskC_esc $exp {\\|*+?.^$[]-()}] } set tw $top.ft.text set otext [$tw get $mstart $mend] if ![set [set this](case)] { if {$mode == "single"} { set err [catch {regsub -nocase -- \ $exp $otext $subexp stext} errmsg] } else { set err [catch {regsub -nocase -all -- \ $exp $otext $subexp stext} errmsg] } } else { if {$mode == "single"} { set err [catch {regsub -- \ $exp $otext $subexp stext} errmsg] } else { set err [catch {regsub -all -- \ $exp $otext $subexp stext} errmsg] } } if {$err && [set [set this](regexp)]} { dsk_lazy dsk_errbell cb_error "Error in regular expression." return } cb_Text_change_callback $tw replace "$mstart $mend [list $stext]" $tw delete $mstart $mend $tw insert $mstart $stext update_status selection clear $tw catch "unset cb_Text(selstart)" dsk_lazy if {$mode == "single"} { $this _do_search } } # ---------------------- # textWidget: # Returns name of the embedded text widget. # method textWidget {} { return $top.ft.text } # ---------------------- # hypersearch: # Creates a toplevel to enter a regular expression. Than grep is run on # the current buffer and the listbox is filled with matching lines. # Pressing MB 1 on one listbox entry makes the respective line the first # visible in the buffer. # method hypersearch {} { global tkdesk [set this] set t "$top-hsearch" if [winfo exists $t] { cb_raise $t return } toplevel $t wm withdraw $t frame $t.fs -bd 1 -relief raised pack $t.fs -fill x frame $t.fsf pack $t.fsf -in $t.fs -fill x \ -padx $tkdesk(pad) -pady $tkdesk(pad) frame $t.fslf pack $t.fslf -in $t.fsf -fill x label $t.ls -text "Search for (RegExp):" -anchor w pack $t.ls -in $t.fslf -side left checkbutton $t.cbCase -text "Case Sensitive" -relief flat \ -variable [set this](case) pack $t.cbCase -in $t.fslf -side right checkbutton $t.cbSort -text "Sort " -relief flat \ -variable [set this](sort) pack $t.cbSort -in $t.fslf -side right entry $t.es -bd 2 -relief sunken -width 40 menubutton $t.mbHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $t.mbHist.menu menu $t.mbHist.menu \ -postcommand "hsearch_history buildmenu $t.mbHist.menu" # add dummy entry to work around bug in pre Tk 4.0p2: $t.mbHist.menu add command -label "dummy" hsearch_history changed pack $t.es \ -in $t.fsf -side left -fill x -expand yes \ -ipady 2 -pady $tkdesk(pad) pack $t.mbHist \ -in $t.fsf -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipadx 2 -ipady 2 bind $t.es "destroy $t" bind $t.es " $this _do_hsearch hsearch_history add \[list \[$t.es get\]\] " frame $t.fm -bd 1 -relief raised pack $t.fm -fill both -expand yes frame $t.fmf pack $t.fmf -in $t.fm -fill both -expand yes \ -padx $tkdesk(pad) -pady $tkdesk(pad) label $t.lMatches -text "Matches:" -anchor w pack $t.lMatches -in $t.fmf -fill x cb_listbox $t.flb -vscroll 1 -hscroll 1 -lborder 0 -uborder 1 \ -width 10 -height 4 \ -font [cb_font $tkdesk(editor,font)] -setgrid 1 \ -selectmode single # $t.flb config -bd 1 -relief raised pack $t.flb -in $t.fmf -fill both -expand yes bind $t.flb.lbox <1> " %W selection clear 0 end set tmplbi \[%W nearest %y\] %W selection set \$tmplbi $this _hsearch_callback \$tmplbi unset tmplbi " frame $t.fb -bd 1 -relief raised pack $t.fb -fill x cb_button $t.bSearch -text " Search " \ -command "$this _do_hsearch ; \ hsearch_history add \[list \[$t.es get\]\]" \ -default 1 cb_button $t.bClose -text " Close " -command "destroy $t" cb_button $t.bRemove -text " Remove Entry " \ -command "hsearch_history remove \[$t.es get\]" pack $t.bSearch $t.bClose \ -in $t.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) pack $t.bRemove \ -in $t.fb -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) bind $t "if \$tkdesk(focus_follows_mouse) \{focus $t.es\}" wm title $t "HyperSearch" wm minsize $t 5 2 #wm geometry $t 20x6 dsk_place_window $t hypersearch 20x6 1 #wm group $t $top cb_deiconify $t } method _hsearch_callback {line_nr} { if {[wm state $top] != "normal"} { cb_deiconify $top } else { raise $top } #$top.ft.text yview [expr [lindex $hsearch_lnr $line_nr] - 1] set lnr [lindex $hsearch_lnr $line_nr] $top.ft.text mark set insert $lnr.0 $top.ft.text yview -pickplace insert update_status } method _do_hsearch {} { global [set this] set exp [$top-hsearch.es get] if {$exp == ""} return set t "$top-hsearch" set lb "$t.flb.lbox" $lb delete 0 end dsk_busy if ![set [set this](case)] { set err [catch {set grep_output \ [exec egrep -ni -e $exp << [$top.ft.text get 1.0 end]]}] } else { set err [catch {set grep_output \ [exec egrep -n -e $exp << [$top.ft.text get 1.0 end]]}] } if $err { dsk_lazy dsk_bell return } if [set [set this](sort)] { set grep_output [exec echo $grep_output | sort -t : +1] } set grep_output [split $grep_output \n] set hsearch_lnr "" foreach match $grep_output { set lnr [string range $match 0 [expr [string first : $match] - 1]] lappend hsearch_lnr $lnr $lb insert end [string range $match \ [expr [string first : $match] + 1] end] } dsk_lazy } method findsel {} { global [set this] cb_Text set tw $top.ft.text set selr [$tw tag ranges sel] catch "unset cb_Text(selstart)" if {$selr != ""} { set exp [$tw get [lindex $selr 0] [lindex $selr 1]] dsk_busy if ![set [set this](case)] { set fi [$tw search -exact -nocase -- $exp insert end] } else { set fi [$tw search -exact -- $exp insert end] } dsk_lazy if {$fi != ""} { selection clear $tw $tw tag add sel $fi "$fi + [string length $exp] chars" $tw yview -pickplace $fi $tw mark set insert "$fi + [string length $exp] chars" } else { dsk_errbell set restart ![cb_yesno "No more matches. Restart at top?"] if $restart { $tw mark set insert 1.0 findsel } } update_status } else { dsk_bell } } method setfont {{what ""}} { global tkdesk if {$what == "default"} { $top.ft.text config -font [cb_font $tkdesk(editor,font)] } else { set font [cb_fontSel] if {$font != ""} { if [winfo exists $top.ft.text] { $top.ft.text config -font [cb_font $font] } } } } method set_auto_indent {} { global [set this] set m $top.fMenu.mbOptions.menu set me [$m index " Indent after*"] if [set [set this](auto_indent)] { catch {$m entryconfigure $me -state normal} } else { catch {$m entryconfigure $me -state disabled} } bind $top.ft.text \ "$this _do_auto_indent %W; catch {$this update_status}; break" } method _do_auto_indent {textw} { global [set this] if [set [set this](auto_indent)] { set tmpline [$textw get "insert linestart" "insert"] regexp {^[ ]*} $tmpline tmpmatch set tmpchar [$textw get "insert - 1 chars"] } else { set tmpmatch "" } cb_Text_change_callback $textw insert $textw insert insert "\n$tmpmatch" $textw yview -pickplace insert if [set [set this](auto_indent)] { if [set [set this](brace_indent)] { if {$tmpchar == "\{"} { $this _tabstr } } } } method set_quick_load {} { global tkdesk [set this] if [set [set this](quick_load)] { catch {::set tkdesk(quick_load_editor) $this} } else { catch {::unset tkdesk(quick_load_editor)} } } method set_line_wrap {} { global [set this] tkdesk $top.ft.text config -wrap [set [set this](wrap)] set tkdesk(editor,wrap) [set [set this](wrap)] } method set_statbar {} { global [set this] tkdesk set val [set [set this](statbar)] if {$val} { pack $top.fs -fill x update_status } else { pack forget $top.fs } set tkdesk(editor,statbar) $val } method update_status {{msg ""} {anchor w}} { global [set this] if {![set [set this](statbar)]} { return } if {[string length $msg] == 0} { set tw $top.ft.text set ii [split [$tw index insert] .] set line [lindex $ii 0] set col [expr [lindex $ii 1] + 1] #set len [expr [string length [$top.ft.text get 1.0 end]] - 1] #$stat_label config -text "Line: $line, Col: $col, $len Bytes" set msg "Line: $line, Col: $col" set selr [$tw tag ranges sel] if {$selr != {}} { set selt [eval $tw get $selr] append msg ", Selected [string length $selt] chars" set r [expr [lindex [split [lindex $selr 1] .] 0] \ - [lindex [split [lindex $selr 0] .] 0] \ + ([lindex [split [lindex $selr 1] .] 1] != 0)] append msg " in $r line(s)" } } $stat_label config -text $msg -anchor $anchor } method set_tab_string {{ask 1}} { global [set this] tkdesk if [set [set this](realtabs)] { bind $top.ft.text { cb_Text_change_callback %W insert %W insert insert \t %W yview -pickplace insert break } } else { if $ask { set val [cb_readString "Tab Width (Number of Spaces):"] if [regexp {[1-90][1-90]*} $val] { set tkdesk(editor,tab_width) $val } else { dsk_errbell set tkdesk(editor,tab_width) 8 } } bind $top.ft.text "$this _tabstr; break" } } method _tabstr {} { global [set this] tkdesk set w $top.ft.text #cb_Text_change_callback $w insert if [set [set this](realtabs)] { set tmpstr "\t" } else { set tmpci [lindex [split [$w index insert] .] 1] set tmpval [expr $tkdesk(editor,tab_width) - \ $tmpci % $tkdesk(editor,tab_width)] if !$tmpval {set tmpval $tkdesk(editor,tab_width)} set tmpstr "" for {set i 0} {$i < $tmpval} {incr i} {append tmpstr " "} } cb_Text_change_callback $w insert $tmpstr $w insert insert $tmpstr $w yview -pickplace insert } method _dd_drophandler {args} { global tkdesk catch "wm withdraw $tkdesk(dd_token_window)" update foreach file $args { $this buffer create $file } } method set_qmark {nr} { set qmark($nr) "$currentid [$top.ft.text index insert]" } method goto_qmark {nr} { if {$qmark($nr) == ""} { dsk_bell return } else { set id [lindex $qmark($nr) 0] set idx [lindex $qmark($nr) 1] set qmark(0) "$currentid [$top.ft.text index insert]" $buffer($id,win) buffer display $id set bufw [$buffer($id,win) getToplevel] $bufw.ft.text see $idx $bufw.ft.text mark set insert $idx update_status } } method popup {x y} { if [file exists $buffer($currentid,file)] { dsk_popup "" [cb_tilde $buffer($currentid,file) expand] $x $y } else { # Currently the following doesn't make too much sense except # for tar file listings, but who cares. set curfile [lindex [dsk_active sel] 0] if {[string length $curfile] > 0} { dsk_popup "" [cb_tilde $curfile expand] $x $y } } } method insert_ff {} { cb_Text_change_callback $top.ft.text insert "\f\n" $top.ft.text insert insert "\f\n" } method insert_timestamp {} { set ts "failed" catch {set ts [exec date]} cb_Text_change_callback $top.ft.text insert $ts $top.ft.text insert insert $ts } method sel_op {cmd args} { set tw $top.ft.text set selr [$tw tag ranges sel] if {$selr == {}} { # selection empty -> select current line $tw tag add sel "[$tw index {insert linestart}]" \ "[$tw index {insert linestart}] + 1 lines" set selr [$tw tag ranges sel] } #puts $selr if {$cmd == "shift_left" || $cmd == "shift_right"} { set firstLine [lindex [split [lindex $selr 0] .] 0] set lastLine [lindex [split [lindex $selr 1] .] 0] for {set l $firstLine} {$l < $lastLine} {incr l} { if {$cmd == "shift_right"} { $tw insert "$l.0 linestart" " " } else { #if {[string first [$tw get "$l.0 linestart"] " \t"] \ # == -1} { # break; #} $tw delete "$l.0 linestart" "$l.0 linestart + 1 char" } } cb_Text_change_callback $tw eval $tw tag add sel $selr } elseif {$cmd == "pipe"} { set subcmd $args set selText [$tw get [lindex $selr 0] [lindex $selr 1]] dsk_debug "selText: $selText" switch $subcmd { replace { set str "Pipe and replace using command:" } insert { set str "Pipe and insert result of command:" } view { set str "Pipe and view result of command:" } exec { set str "Pipe and execute command:" } } set cmd [dsk_read_string "Pipe through command:" {} dontpaste] if {[string length $cmd] == 0} { return } switch $subcmd { replace { set err [catch { set resText [exec $cmd <<$selText] } errmsg] if {$err} { dsk_errbell cb_error $errmsg return } cb_Text_change_callback $tw replace \ "[lindex $selr 0] [lindex $selr 1] [list $resText\n]" $tw delete [lindex $selr 0] [lindex $selr 1] $tw insert [lindex $selr 0] $resText\n } insert { set err [catch { set resText [exec $cmd <<$selText] } errmsg] if {$err} { dsk_errbell cb_error $errmsg return } cb_Text_change_callback $tw insert [list $resText\n] $tw insert insert $resText\n } view { dsk_view echo \"$selText\" | $cmd } exec { dsk_exec echo \"$selText\" | $cmd } } } } proc id {{cmd ""}} { if {$cmd == ""} { set i $object_id incr object_id return $i } elseif {$cmd == "reset"} { set object_id 0 } } proc bufferMenu {menu {win ""}} { global tkdesk # sort the buffer names set l {} for {set id 1} {$id <= $bufcount} {incr id} { if ![info exists buffer($id,file)] continue set fname [file tail $buffer($id,file)] lappend l [list $fname $id] } set l [lsort $l] catch "$menu delete 0 last" set ne 0 foreach b $l { set id [lindex $b 1] if ![info exists buffer($id,file)] continue if ![info exists changed($id)] continue if {$win != ""} { set currentid [$win info protected currentid -value] } else { set currentid -1 } if [file exists $buffer($id,file)] { set fname "[lindex $b 0] ([cb_tilde [file dirname $buffer($id,file)] collapse])" } elseif [file exists [file dirname $buffer($id,file)]] { set fname "[lindex $b 0] ([cb_tilde [file dirname $buffer($id,file)] collapse])" } else { set fname $buffer($id,file) } if {$id == $currentid} { if $changed($id) { set l "@$fname" } else { set l ">$fname" } } else { if $changed($id) { set l "*$fname" } else { set l " $fname" } } incr ne if {$ne > $tkdesk(_max_menu_entries)} { $menu add cascade -label "More..." -menu [set menu $menu.c$ne] catch {destroy $menu} menu $menu set ne 0 } $menu add command -label $l \ -font [cb_font $tkdesk(font,file_lbs)] \ -command "$buffer($id,win) buffer display $id" } } proc save_all {} { for {set id 1} {$id <= $bufcount} {incr id} { if ![info exists buffer($id,file)] continue if $changed($id) { #$buffer($id,win) buffer display $id $buffer($id,win) save "" 0 $id } } } proc load_or_display {w file} { # see if this file is already loaded for {set id 1} {$id <= $bufcount} {incr id} { if ![info exists buffer($id,file)] continue if {$buffer($id,file) == $file} { $buffer($id,win) buffer display $id return } } if {[itcl_info objects $w] != {}} { $w buffer create $file 0 0 } } proc wait_for_close {obj} { tkwait [$obj getToplevel] } # # ----- Variables --------------------------------------------------------- # public files "" { set files_set 1 set dont_display 1 set id -1 foreach f $files { dsk_status "Loading $f..." if {$id == -1} { set id [$this buffer create $f] } else { $this buffer create $f } } set dont_display 0 if {$id != -1} { $this buffer display $id } else { set files "" } dsk_status_ready } public name "" { set buffer($currentid,file) $name wm title $top [cb_tilde $name collapse] wm iconname $top "[file tail $name]" } protected currentid -1 protected match_range "" protected search_regexp "" protected hsearch_lnr "" protected autosavecount 0 protected autosaveperiod 500 protected undo_size 500 protected dont_display 0 protected files_set 0 protected stat_label common buffer common bufcount 0 common changed common object_id 0 common cutbuffer "" common searchexp "" common qmark common undo_enabled common undo_list common undo_pointer for {set i 0} {$i < 10} {incr i} { set qmark($i) "" } } # # ----------------------------------------------------------------------------- # # Proc: dsk_editor # Args: cmd command to invoke # args other arguments # Returns: "" # Desc: Meta function to access the built-in editor. # Side-FX: none # set dsk_editor(cnt) 0 proc dsk_editor {cmd args} { global dsk_editor tkdesk dsk_exec set w "" switch -- $cmd { new { set w .de[dsk_Editor :: id] dsk_Editor $w $w buffer new {} } load { set w .de[dsk_Editor :: id] dsk_Editor $w foreach file $args { $w buffer create $file } } string { set w .de[dsk_Editor :: id] dsk_Editor $w $w buffer new {} eval [$w getToplevel].ft.text insert end $args $w update_status # $w _changing } delall { foreach obj [itcl_info objects -class dsk_Editor] { catch {$obj close_win} } } fileview { set w [lindex $args 0] set file [lindex $args 1] if {[itcl_info objects $w] == {}} { set w .de[dsk_Editor :: id] dsk_Editor $w set dsk_editor($w,viewerid) "" } if {$dsk_editor($w,viewerid) != ""} { $w buffer delete $dsk_editor($w,viewerid) 1 } set ret [$w buffer create $file 0 0] set dsk_editor($w,viewerid) $ret } quickload { set w [lindex $args 0] if {[itcl_info objects $w] == {}} { catch {unset tkdesk(quick_load_editor)} return } set file [lindex $args 1] dsk_Editor :: load_or_display $w $file } cmd { set cnt [incr dsk_editor(cnt)] set cmd $args if [info exists dsk_exec(dir)] { if [file isdirectory $dsk_exec(dir)] { cd $dsk_exec(dir) } else { unset dsk_exec(dir) } } else { cd [dsk_active dir] } # move this block here to handle immediate exit of blt_bgexec dsk_status "Launched: $cmd" # [todo] where does this dsk_lazy come from??? #dsk_lazy if $tkdesk(exec_as_root) { set cmd [string_replace $tkdesk(cmd,su,view) %c $cmd] dsk_debug "SU-View: $cmd" } if {[string first " <" $cmd] < 0} { set devnull " "$this chmod $i; break" bind $top.bm($i) "$this chmod $i Button-2" bind $top.bm($i) "$this chmod $i Button-3" pack $top.bm($i) -in $top.fmod -side left if {$i == 3 || $i == 6} { pack [frame $top.fm($i) -width 8] -in $top.fmod -side left } } pack $top.lrPath $top.lrSize $top.bMod $top.bOwn \ $top.bGrp $top.fmod $top.lrLink $top.mMagic \ -in $top.f2 -side top -anchor w frame $top.fa -bd 1 -relief raised pack $top.fa -fill both -expand yes frame $top.fa1 pack $top.fa1 -in $top.fa -fill both -expand yes -pady $tkdesk(pad) label $top.lComment -text "Annotation:" -anchor w pack $top.lComment -in $top.fa1 -fill x -expand no -anchor w \ -padx $tkdesk(pad) cb_text $top.ft -vscroll 1 -lborder 1 -pad $tkdesk(pad) \ -width 36 -height 6 \ -wrap word -setgrid 1 pack $top.ft -in $top.fa1 -fill both -expand yes frame $top.fb -bd 1 -relief raised pack $top.fb -fill x button $top.bClose -text " Close " -command "$this close" button $top.bChmod -text " Change Mode " -state disabled \ -command "$this chmod set" pack $top.bClose $top.bChmod -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 bind $top "focus $top.ft.text" wm title $top "File Information" wm protocol $top WM_DELETE_WINDOW "$this close" eval config $args } destructor { } # # ----- Methods and Procs ------------------------------------------------- # method close {} { global tkdesk_anno set anno [string trimright [$top.ft.text get 1.0 end] \n] if {$anno != ""} { set tkdesk_anno($file) $anno dsk_refresh $file } elseif [info exists tkdesk_anno($file)] { unset tkdesk_anno($file) dsk_refresh $file } if {[info tclversion] >= 8.0} { $this delete } else { wm withdraw $top dsk_FileInfo::cache add $this } } method config {config} { } method touch {} { if {![file owned $file] && ![dsk_is_superuser]} { dsk_errbell cb_error "Sorry, you're not the owner of [file tail $file]!" return } if {[cb_okcancel "Touch [file tail $file]?"] == 0} { set err [catch {exec touch $file} errmsg] if $err { cb_error $errmsg } else { $this config -file $file } } } method chown {} { global o set as_root 0 if {![file owned $file] && ![dsk_is_superuser]} { set ret [cb_yesno \ "You're not the owner of [file tail $file]. Try as root?"] if {$ret == 0} { set as_root 1 } else { return } } set o $owner cb_readString "Change owner of [file tail $file] to:" o "Change Owner" if {$o != ""} { if {!$as_root} { set err [catch {exec chown $o $file} errmsg] if $err { cb_error $errmsg } else { $this config -file $file } } else { dsk_exec_as_root chown $o $file $this config -file $file } } } method chgrp {} { global g set as_root 0 if {![file owned $file] && ![dsk_is_superuser]} { set ret [cb_yesno \ "You're not the owner of [file tail $file]. Try as root?"] if {$ret == 0} { set as_root 1 } else { return } } set g $group cb_readString "Change group of [file tail $file] to:" g "Change Group" if {$g != ""} { if {!$as_root} { set err [catch {exec chgrp $g $file} errmsg] if $err { cb_error $errmsg } else { $this config -file $file } } else { dsk_exec_as_root chgrp $g $file $this config -file $file } } } method chmod {num {event Button-1}} { # num is: user r/w/x: 1/2/3, group r/w/x: 4/5/6, world r/w/x: 7/8/9, # or "set" # fmode contains the current mode string if {![file owned $file] && ![dsk_is_superuser]} { dsk_errbell cb_error "Sorry, you're not the owner of [file tail $file]!" return } if [dsk_on_rofs $file] { dsk_errbell cb_error "Read-only file system. Can't change permissions, sorry." return } $top.bChmod config -state normal for {set i 0} {$i < 10} {incr i} { set m($i) [string index $fmode $i] } # modified by jdblair to deal with different button events # button 1: normal # button 2: all -- set this state in all ownership classes # button 3: leap -- toggle execute on or off, ignoring s, S, t, T. if {$event == "Button-2"} { switch -- $m($num) { "r" - "w" - "x" - "-" { set cycle [expr $num % 3] if {$cycle == 0} {set cycle 3} for {set i $cycle} {$i < 10} {incr i 3} { set m($i) $m($num) $top.bm($i) config -text $m($num) } } } } else { switch $num { 1 - 4 - 7 { if {$m($num) == "r"} { set m($num) "-" } else { set m($num) "r" } } 2 - 5 - 8 { if {$m($num) == "w"} { set m($num) "-" } else { set m($num) "w" } } 3 - 6 { if {$event == "Button-3"} { if {$m($num) == "x"} { set m($num) "-" } else { set m($num) "x" } } else { if {$m($num) == "x"} { set m($num) "s" } elseif {$m($num) == "s"} { set m($num) "S" } elseif {$m($num) == "S"} { set m($num) "-" } else { set m($num) "x" } } } 9 { if {$event == "Button-3"} { if {$m($num) == "x"} { set m($num) "-" } else { set m($num) "x" } } else { if {$m($num) == "x"} { set m($num) "t" } elseif {$m($num) == "t"} { set m($num) "T" } elseif {$m($num) == "T"} { set m($num) "-" } else { set m($num) "x" } } } } } if {$num != "set"} { $top.bm($num) config -text $m($num) set fmode "" for {set i 0} {$i < 10} {incr i} { append fmode $m($i) } } else { set s 0 ; set o 0 ; set g 0 ; set w 0 if {$m(1) == "r"} {incr o 4} if {$m(2) == "w"} {incr o 2} if {$m(3) == "x"} { incr o 1 } else { if {$m(3) != "-"} { incr s 4 if {$m(3) == "s"} {incr o 1} } } if {$m(4) == "r"} {incr g 4} if {$m(5) == "w"} {incr g 2} if {$m(6) == "x"} { incr g 1 } else { if {$m(6) != "-"} { incr s 2 if {$m(6) == "s"} {incr g 1} } } if {$m(7) == "r"} {incr w 4} if {$m(8) == "w"} {incr w 2} if {$m(9) == "x"} { incr w 1 } else { if {$m(9) != "-"} { incr s 1 if {$m(9) == "t"} {incr w 1} } } set amode "" append amode $s $o $g $w set err [catch {exec chmod $amode $file} errmsg] if $err { dsk_errbell cb_error $errmsg } dsk_refresh $file catch {$top.bChmod config -state disabled} } } method _configFile {} { # backend private proc of the "file" public var global tkdesk tkdesk_anno dsk_busy if [file isdirectory $file] { set file [string trimright $file "/"]/ set file [dsk_canon_path $file] if {$file != "/"} { set file [string trimright $file "/"] } } set n [file tail $file] if [string match $n ""] {set n "/"} $top.label config -text $n set p [file dirname $file] if {[string first $tkdesk(trashdir) $p] == 0} { set p [string range $p \ [string length $tkdesk(trashdir)/] 1000] if {$p == ""} { set p "Trash" } } $top.lrPath config -text [cb_tilde $p collapse] set lsl "" set lsl [lindex [dskC_ls -l -o $file] 0] #regsub -all {\\t} $lsl "\t" lsl set lsl [split $lsl "\t"] dsk_debug "$file: $lsl" $top.lrSize config -text "[lindex $lsl 1] Bytes" $top.lrLink config -text [string trimleft [lindex $lsl 6] " "] $top.bMod config -text "[lindex $lsl 2]" set owner [lindex $lsl 3] $top.bOwn config -text "$owner" set group [lindex $lsl 4] $top.bGrp config -text "$group" set fmode [lindex $lsl 5] for {set i 1} {$i < 10} {incr i} { $top.bm($i) config -text [string index $fmode $i] } set err [catch {set m [exec file $file]} errmsg] if !$err { $top.mMagic config -text [string trimleft [string range $m \ [expr [string first ":" $m] + 1] 10000] " "] } else { $top.mMagic config -text $errmsg } $top.ft.text delete 1.0 end if [info exists tkdesk_anno($file)] { $top.ft.text insert end $tkdesk_anno($file) } if {[file isdirectory $file] && ![winfo exists $top.bDU]} { button $top.bDU -text " Disk Usage " -command "dsk_du \"$file\"" pack $top.bDU -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 2 } else { if [winfo exists $top.bDU] { destroy $top.bDU } } update idletasks wm iconname $top "Info: [file tail $file]" wm sizefrom $top program if {[wm state $top] != "normal"} { dsk_place_window $top fileinfo "36x6" 1 wm deiconify $top } dsk_lazy } proc id {} { set i $id incr id return $i } proc cache {cmd args} { switch $cmd { "get" { if {$objectCache == {}} { return [eval dsk_FileInfo .fi[dsk_FileInfo :: id] $args] } else { #puts "objectCache before: $objectCache" set obj [lindex $objectCache 0] set objectCache [lrange $objectCache 1 end] #puts "objectCache after: $objectCache" eval $obj config $args return $obj } } "add" { lappend objectCache $args } } } # # ----- Variables --------------------------------------------------------- # public file "" { set file [cb_tilde $file expand] dsk_debug "dsk_FileInfo config file: $file" if [file exists $file] { _configFile } } protected owner protected group protected fmode common id 0 common objectCache {} } # # ----------------------------------------------------------------------------- # # Proc: dsk_fileinfo # Args: none! # Returns: "" # Desc: Creates an object of class dsk_FileInfo on each selected file # in the calling file viewer. # Side-FX: # proc dsk_fileinfo {args} { global tkdesk set files $args if {$files == ""} { set files [_make_fnames_safe] } if {$files == ""} { dsk_bell cb_info "Please select one or more files first." return } foreach file $files { set file [subst -nocommands -novariables $file] set file [dskC_striptc $file] if ![file exists $file] { dsk_errbell cb_info "[file tail $file]\nis a broken symbolic link or has been removed." continue } if {$file != ""} { if {[info tclversion] >= 8.0} { # itcl 3.x seems to disallow object creation through # class procs dsk_FileInfo .fi[dsk_FileInfo :: id] -file $file } else { dsk_FileInfo :: cache get -file $file } } } } tkdesk-2.0/tcldesk/FileListbox.tcl0100644000175000007640000013250110020457430015303 0ustar jccjcc# ============================================================================= # # File: FileListbox.tcl # Project: TkDesk # # Started: 09.10.94 # Changed: 09.10.94 # Author: cb # # Description: Implements a class for a configurable file listbox. # Based on dsk_Listbox. Needs the BLT-library for drag&drop. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_FileListbox #s method config {config} #s method cget {var} #s method curdir {} #s method refresh {} #s method clear {} #s method tagpath {{dir ""}} #s method imginsert {tname lines} #s method create_img {l image} #s method set_mask {m} #s method _ask_mask {} #s method command {{cmd ""}} #s method _build_ls_cmd {} #s method _selstatus {} #s method _dd_pkgcmd {token} #s method _dd_pkgcmd_mb {token} #s method _dd_dragcmd {} #s method _dd_drophandler {{win ""}} #s method _dragscroll {mode} #s method _mb_label {} #s method _selmask {} #s method _tag_icons {} #s proc font {fnt} #s proc color {col} #s proc defimage {img} #s proc pathimage {img} #s proc showall {val} #s proc longlist {val} #s proc topfolders {val} #s proc typechar {val} #s proc addicons {val} #s proc sort {val} #s proc tag {cmd args} #s proc initC {} #s proc print_ready {val} #s proc ignore {list} #s proc dsk_FileListbox_fileTags {} # # ============================================================================= # # ============================================================================= # # Class: dsk_FileListbox # Desc: Configurable listbox widget for the displaying of file lists. # Consists of a menubutton on top and a dsk_Listbox. # # Methods: config (see Publics below) # refresh rereads the filelist # clear empties the filelist # # Procs: font set listbox font # showall show all files ? # longlist long listing ? # topfolders put folders on top ? # typechar append a type character ? # tag create ?font? # create a tag for files that match # tag config ?font? # configure the given tag # # Publics: dir/directory directory to display # mask file mask (glob style) # width width of listbox # height height of listbox # pad padding of listbox # invert invert file lists ? # sort <"name"|"size"|"date"|"ext"|"not"> you know what # notrivialdirs show . and .. ? # addsize add size to displayed files ? # adddate add date to displayed files ? # long long listing ? # itcl_class dsk_FileListbox { inherit Frame constructor {args} { global [set this] tkdesk if {$tkdesk(tcl_version) < 8.0} { Frame::constructor } # set config from common vars set showall $_showall set longlist $_longlist set topfolders $_topfolders set typechar $_typechar set dotregular $_dotreg set add_icons $_addicons set sort $_sort frame $frame.fMb pack $frame.fMb -fill x menubutton $frame.mb -text "" -bd 1 -relief raised \ -menu $frame.mb.menu -indicatoron 0 menu $frame.mb.menu $frame.mb.menu add command -label "Refresh " \ -command "$this refresh; $this _selstatus" $frame.mb.menu add command -label "Set Mask... " \ -command "$this _ask_mask" $frame.mb.menu add command -label "No Mask " \ -command "$this no_mask" $frame.mb.menu add separator $frame.mb.menu add command -label "Disk Usage " \ -command "dsk_du \[$this curdir\]" $frame.mb.menu add command -label "Free Space " \ -command "dsk_periodic \[list $tkdesk(cmd,df) \[$this cget -directory\]\] 60" $frame.mb.menu add command -label "Execute here... " \ -command "$this command" $frame.mb.menu add command -label "Execute as root... " \ -command "$this command {} root" $frame.mb.menu add separator $frame.mb.menu add checkbutton -label " Long Listing " \ -variable [set this](longlist) \ -command "$this config -longlist \[set [set this](longlist)\]" ::set [set this](longlist) $longlist $frame.mb.menu add checkbutton -label " Show All Files " \ -variable [set this](showall) \ -command "$this config -showall \[set [set this](showall)\]" ::set [set this](showall) $showall $frame.mb.menu add checkbutton -label " Inverse Order " \ -variable [set this](invert) \ -command "$this config -invert \[set [set this](invert)\]" ::set [set this](invert) $invert $frame.mb.menu add cascade -label "Sort by ... " -menu $frame.mb.menu.smenu $frame.mb.menu add separator $frame.mb.menu add command -label "Open List Window" \ -command "dsk_FileList .dfl\[dsk_FileList :: id\] \ -directory \[$this info public directory -value\]" menu $frame.mb.menu.smenu $frame.mb.menu.smenu add radiobutton -label " Name " \ -variable [set this](sort) -value name \ -command "$this config -sort \[set [set this](sort)\]" $frame.mb.menu.smenu add radiobutton -label " Name (fold)" \ -variable [set this](sort) -value fold \ -command "$this config -sort \[set [set this](sort)\]" $frame.mb.menu.smenu add radiobutton -label " Size " \ -variable [set this](sort) -value size \ -command "$this config -sort \[set [set this](sort)\]" $frame.mb.menu.smenu add radiobutton -label " Date " \ -variable [set this](sort) -value date \ -command "$this config -sort \[set [set this](sort)\]" $frame.mb.menu.smenu add radiobutton -label " Extension " \ -variable [set this](sort) -value ext \ -command "$this config -sort \[set [set this](sort)\]" $frame.mb.menu.smenu add radiobutton -label " Don't sort " \ -variable [set this](sort) -value not \ -command "$this config -sort \[set [set this](sort)\]" ::set [set this](sort) $sort pack $frame.mb -in $frame.fMb -fill x -side left -expand no label $frame.lFiles -text " 1234 Items" -anchor e \ -font [cb_font $tkdesk(font,status)] -bd 3 pack $frame.lFiles -in $frame.fMb -side left -padx $pad if {$tkdesk(tcl_version) >= 8.0} { set lbname ::[namespace tail $frame.dlb] } else { set lbname $frame.dlb } dsk_Listbox $lbname \ -width $width -height $height \ -font [cb_font $lbfont] -has_dots 1 \ -bg [cb_col $tkdesk(color,filelb)] -fg $lbcolor $frame.dlb config -bd 1 -relief raised set framedlb [$frame.dlb getFrame] $framedlb.text config -tabs {190 right 220 left 330 left \ 390 left 450 left 540 left 560 left} if {[winfo depth .] != 1} { foreach t $taglist { $frame.dlb tag config $t \ -foreground $tags($t,color) -font $tags($t,font) } } else { foreach t $taglist { $frame.dlb tag config $t -font $tags($t,font) } } $frame.dlb tag config path -borderwidth 1 -relief raised # $frame.dlb tag config path -underline 1 $frame.dlb tag lower path pack $framedlb -fill both -expand yes # need to know $viewer (parent toplevel) before binding: set vi [lsearch -exact $args {-viewer}] if {$vi > -1} { # just set, don't config set viewer [lindex $args [expr $vi + 1]] } dsk_debug " viewer \"$viewer\"" bind $framedlb.text <1> \ "+$this _tag_icons ;\ catch {destroy .cb_balloon}" bind $framedlb.text \ "+$this _tag_icons ;\ catch {destroy .cb_balloon}" bind $framedlb.text \ "if \$tkdesk(focus_follows_mouse) { focus \[winfo toplevel %W\] } #dsk_refresh \[$this cget -directory\] dsk_update_flbs $this" bind $framedlb.text "$this _show_tip_leave %X %Y" ::set tkdesk(_doubleclick) 0 bind $framedlb.text " set tkdesk(file_lb,control) 0 set tmpsc \$tkdesk(single_click) set tkdesk(single_click) 0 set tkdesk(config_nostat) 1 set tkdesk(_doubleclick) 1 $viewer _dblclick $this \[$frame.dlb select get\] set tkdesk(config_nostat) 0 catch {set tkdesk(single_click) \$tmpsc} catch {unset tmpsc} break " bind $framedlb.text \ "+if \$tkdesk(single_click) \{ set tkdesk(file_lb,control) 0 $viewer _dblclick $this \[$frame.dlb select get\] \} if !\$tkdesk(_doubleclick) \{ after 100 \{$this _quick_load \[$frame.dlb select get\] $this _quick_info \[$frame.dlb select get\]\} \} " bind $framedlb.text \ "+if \$tkdesk(single_click) \{ set tkdesk(file_lb,control) 1 #$viewer _dblclick $this \[$frame.dlb select get\] \} $this _tag_icons $this _selstatus " bind $framedlb.text " $this _tag_icons $this _selstatus " bind $framedlb.text " $frame.dlb select clear $frame.dlb select @%x,%y $frame.dlb _sel_first @%x,%y set tkdesk(file_lb,control) 1 set tmpsc \$tkdesk(single_click) set tkdesk(single_click) 0 set tkdesk(config_nostat) 1 $viewer _dblclick $this \[$frame.dlb select get\] set tkdesk(config_nostat) 0 catch {set tkdesk(single_click) \$tmpsc} catch {unset tmpsc} break " bind $framedlb.text " [bind $framedlb.text <1>] $this _selmask " bind $framedlb.text <3> " [bind $framedlb.text <1>] update idletasks $this _selstatus $viewer _popup $this \[$frame.dlb select get\] %X %Y" bind $framedlb.text {# nothing} bind $framedlb.text "+$this _tag_icons if !\$tkdesk(_doubleclick) \{ $this _selstatus \} set tkdesk(_doubleclick) 0 " if {[winfo depth .] != 1} { set cc [cb_col $tkdesk(color,drag)] } else { set cc white } blt_drag&drop source $framedlb.text \ -button 2 -packagecmd "$this _dd_pkgcmd %t" -rejectfg red \ -selftarget 1 -send {file text} \ -tokenanchor nw \ -tokencursor "@$tkdesk(library)/images/xbm/hand.xbm \ $tkdesk(library)/images/xbm/hand.mask.xbm \ [cb_col $tkdesk(color,foreground)] $cc" blt_drag&drop source $frame.mb \ -button 2 -packagecmd "$this _dd_pkgcmd_mb %t" -rejectfg red \ -selftarget 1 -send {file string} \ -tokenanchor nw \ -tokencursor "@$tkdesk(library)/images/xbm/hand.xbm \ $tkdesk(library)/images/xbm/hand.mask.xbm \ [cb_col $tkdesk(color,foreground)] $cc" blt_drag&drop source $framedlb.text handler file blt_drag&drop source $frame.mb handler file blt_drag&drop target $framedlb.text handler \ file "$this _dd_drophandler $framedlb.text %v" blt_drag&drop target $frame.mb handler \ file "$this _dd_drophandler $frame.mb %v" #bind $framedlb.text "[bind Text ]" #bind $framedlb.text "[bind Text ]" bind $framedlb.text " $frame.dlb _sel_for_dd @%x,%y $frame.dlb _dd_start %X %Y $this _tag_icons $this _selstatus blt_drag&drop source %W -send {file text} blt_drag&drop drag %W %X %Y focus %W " bind $frame.mb " blt_drag&drop drag %W %X %Y focus $framedlb.text " bind $framedlb.text " blt_drag&drop source %W -send {file text} blt_drag&drop drag %W %X %Y " bind $framedlb.text \ "set tkdesk(file_lb,control) 1 $this _dd_dragcmd" bind $framedlb.text \ "set tkdesk(file_lb,control) 1 ;\ blt_drag&drop drop %W %X %Y ;\ blt_drag&drop source %W -send {} ;\ dsk_desktop_drop %X %Y ;\ focus -force ." bind $framedlb.text \ "set tkdesk(file_lb,control) 0 ;\ blt_drag&drop drop %W %X %Y ;\ blt_drag&drop source %W -send {} ;\ dsk_desktop_drop %X %Y ;\ focus -force ." bind $frame.mb \ "set tkdesk(file_lb,control) 1 ;\ blt_drag&drop drop %W %X %Y ;\ dsk_desktop_drop %X %Y $this ;\ focus -force ." bind $frame.mb \ "set tkdesk(file_lb,control) 0 ;\ blt_drag&drop drop %W %X %Y ;\ dsk_desktop_drop %X %Y $this ;\ focus -force ." bind $framedlb.text \ "set tkdesk(file_lb,control) 0;\ $this _dd_dragcmd" bind $frame.mb \ "set tkdesk(file_lb,control) 1 ;\ $this _dd_dragcmd" bind $frame.mb \ "set tkdesk(file_lb,control) 0;\ $this _dd_dragcmd" bind $framedlb.text \ "set tkdesk(file_lb,control) 0; \ $this _dd_dragcmd" bind $framedlb.text \ "set tkdesk(file_lb,control) 1; \ $this _dd_dragcmd" bind $framedlb.text \ "catch \{after cancel \$tkdesk(_show_tip_id); \ after cancel \$cb_balloonHelp(id,timeout) ;\ destroy .cb_balloon\} ;\ if \{%s == 0\} \{ set tkdesk(_show_tip_id) \ \[after 500 $this _show_tip %X %Y %x %y\] \}" # define binding tag for the list icons: bind listimg-$frame <1> " $viewer _selecting $this focus [winfo toplevel $frame] $frame.dlb select clear $frame.dlb select \[lindex \[split %W :\] 1\] $frame.dlb _sel_first \[lindex \[split %W :\] 1\] $this _tag_icons " bind listimg-$frame " $viewer _selecting $this $frame.dlb _sel_toggle \[lindex \[split %W :\] 1\] $frame.dlb _sel_first \[lindex \[split %W :\] 1\] $this _tag_icons " bind listimg-$frame \ [bind $framedlb.text ] bind listimg-$frame \ [bind $framedlb.text ] bind listimg-$frame " $frame.dlb _sel_for_dd \[lindex \[split %W :\] 1\] $frame.dlb _dd_start %X %Y $this _tag_icons $this _selstatus drag&drop drag $framedlb.text %X %Y focus $framedlb.text " bind listimg-$frame \ "[bind listimg-$frame ]" bind listimg-$frame \ "set tkdesk(file_lb,control) 1 ;\ drag&drop drag $framedlb.text %X %Y ;\ $this _dd_dragcmd" bind listimg-$frame \ "set tkdesk(file_lb,control) 0 ;\ drag&drop drag $framedlb.text %X %Y ;\ $this _dd_dragcmd" bind listimg-$frame \ "set tkdesk(file_lb,control) 1 ;\ drag&drop drop $framedlb.text %X %Y ;\ dsk_desktop_drop %X %Y ;\ focus -force ." bind listimg-$frame \ "set tkdesk(file_lb,control) 0 ;\ drag&drop drop $framedlb.text %X %Y ;\ dsk_desktop_drop %X %Y ;\ focus -force ." bind listimg-$frame <3> " [bind listimg-$frame <1>] update idletasks $this _selstatus $viewer _popup $this \ \[$frame.dlb select get\] %X %Y " bind listimg-$frame {# nothing} $this _build_ls_cmd eval config $args $frame.dlb show_hsb $longlist } destructor { global [set this] $frame.dlb delete ::unset [set this] } # # ----- Methods and Procs ------------------------------------------------- # method config {config} { } method cget {var} { return [set [string trimleft $var -]] } method getLbObj {} { return $frame.dlb } method curdir {} { return $directory } method refresh {} { if {$directory == ""} { return } if {$dont_refresh} { return } dsk_busy set ptag [$frame.dlb tag ranges path] if {$ptag != ""} { set pname [$frame.dlb get $ptag] } set ypos [lindex [cb_old_sb_get $framedlb.sb] 2] set sel [$frame.dlb select getnames] set refreshing 1 config -directory $directory set refreshing 0 $frame.dlb select name $sel $frame.dlb _yview $ypos if {$ptag != ""} { $frame.dlb tag add path [lsearch [$frame.dlb get] $pname] } dsk_lazy } method clear {} { global tkdesk $frame.mb config -text "" -state disabled catch "pack unpack $frame.mb" $frame.lFiles config -text "" $frame.dlb config -list {} $frame.dlb config -bg [cb_col $tkdesk(color,background)] set directory "" } method tagpath {{dir ""}} { global tkdesk # display old "open" directory in "closed" state set index [$frame.dlb tag ranges path] if {$index != ""} { $frame.dlb tag remove path $index if $add_icons { if {$tkdesk(tcl_version) < 8.0} { catch {label $framedlb.text.l:$index} #incr index if {$closed_image != ""} { $framedlb.text.l:$index config \ -image $closed_image -bd 0 } else { $framedlb.text.l:$index config \ -image [dsk_image $tags(dir,image)] -bd 0 } } else { set index [expr $index + 1].0 if {$closed_image != ""} { [$frame.dlb getText] image configure $index \ -image $closed_image } else { [$frame.dlb getText] image configure $index \ -image [dsk_image $tags(dir,image)] } } } } if {$dir == ""} { return } # display new "open" directory in "opened" state set fname [file tail [string trimright $dir "/"]] if $typechar { set fname $fname/ } set list [$frame.dlb get] set index [lsearch $list $fname] if {$index < 0} { set index [lsearch -glob $list "$fname *"] if {$index < 0} { # This happens if one listbox displays the contents # of a "hidden" directory. return } } $frame.dlb tag add path $index #incr index if $add_icons { if {$tkdesk(tcl_version) < 8.0} { catch {label $framedlb.text.l:$index} set closed_image [$framedlb.text.l:$index cget -image] $framedlb.text.l:$index config \ -image [dsk_image $path_image] -bd 0 } else { set index [expr $index + 1].0 set closed_image \ [[$frame.dlb getText] image cget $index -image] [$frame.dlb getText] image configure $index \ -image [dsk_image $path_image] } } # $framedlb.text yview -pickplace $index $frame.dlb select clear _tag_icons } method imginsert {tname lines} { global tkdesk if {$lines == ""} return if {$tname != ""} { set image $tags($tname,image) } else { set image $default_image } set nlines [llength $lines] if {$tkdesk(tcl_version) < 8.0 || $nlines > 500} { foreach l $lines { $framedlb.text window create [expr $l + 1].0 \ -create "$this create_img $l $image" -padx 2 } } else { foreach l $lines { $framedlb.text image create [expr $l + 1].0 \ -image [dsk_image $image] -padx 2 } } } method create_img {l image} { catch {label $framedlb.text.l:$l} $framedlb.text.l:$l config -image [dsk_image $image] -bd 0 bindtags $framedlb.text.l:$l listimg-$frame return $framedlb.text.l:$l } method set_mask {m} { set mask $m } method no_mask {} { set invert_mask 0 config -mask {*} } method _ask_mask {} { dsk_HistEntry .he[dsk_HistEntry :: id] \ -title " Set Mask " \ -label "Mask for files to show or select:" \ -history mask_history \ -entrydefault $_last_mask \ -callback "$this _ask_mask_ok" \ -addbutton "Select" \ -addcallback "$this _ask_mask_select" \ -checklabel "Invert Mask" \ -checkvalue $invert_mask } method _ask_mask_ok {entry val} { if {$entry != ""} { mask_history add $entry set invert_mask $val set _last_mask $entry $this config -mask $entry } } method _ask_mask_select {entry val} { if {$entry != ""} { mask_history add $entry set invert_mask $val set _last_mask $entry $frame.dlb select name $entry $invert_mask } } method command {{cmd ""} {root ""}} { global tkdesk if {$root != ""} { set asr $tkdesk(exec_as_root) ::set tkdesk(exec_as_root) 1 } ::dsk_exec_here $cmd $directory if {$root != ""} { ::set tkdesk(exec_as_root) $asr } } method _build_ls_cmd {} { # assembles the appropriate options for dskC_ls #set options {-t} ;# this is always needed if $showall { lappend options -a } if $longlist { lappend options -l } if $invert { lappend options -i } if $notrivialdirs { lappend options -p } if $topfolders { lappend options -f } if $typechar { lappend options -t } switch $sort { name {lappend options -s name} fold {lappend options -s fold} size {lappend options -s size} date {lappend options -s date} ext {lappend options -s ext} not {lappend options -s not} } set ls_cmd "dskC_ls $options" } method _selstatus {} { global tkdesk if {$viewer != ""} { $viewer selstatus } } method _dd_pkgcmd {token} { global tkdesk set tkdesk(dd_token_window) $token set flist [$viewer select get] if {$flist == ""} { return } catch "destroy $token.label" catch "destroy $token.lFiles" catch "destroy $token.lDirs" if $tkdesk(quick_dragndrop) { set f [cb_font $tkdesk(font,labels)] catch { set f [join [lreplace [split $f -] 7 7 10] -] set f [join [lreplace [split $f -] 3 3 medium] -] } label $token.label -text "Moving:" \ -font [cb_font $f] pack $token.label -fill x } set ll [llength $flist] if {$ll == 1} { label $token.lFiles -text " [eval file tail $flist]" } else { label $token.lFiles -text " $ll Items" } pack $token.lFiles -anchor w -ipadx 6 cb_deiconify $token #focus $token dsk_debug "flist $flist" return [list $flist] } method _dd_pkgcmd_mb {token} { global tkdesk set tkdesk(dd_token_window) $token if {$directory != "/"} { set flist [string trimright $directory /] } else { set flist $directory } catch "destroy $token.label" catch "destroy $token.lFiles" catch "destroy $token.lDirs" if $tkdesk(quick_dragndrop) { set f [cb_font $tkdesk(font,labels)] catch { set f [join [lreplace [split $f -] 7 7 10] -] set f [join [lreplace [split $f -] 3 3 medium] -] } label $token.label -text "Moving:" \ -font [cb_font $f] pack $token.label } label $token.lFiles -text " [eval file tail $flist]" pack $token.lFiles -anchor w -ipadx 6 cb_deiconify $token #focus $token return [list $flist] } method _dd_dragcmd {} { global tkdesk set err [catch {set token $tkdesk(dd_token_window)}] if $err return if {$tkdesk(quick_dragndrop) && [winfo exists $token.label]} { if $tkdesk(file_lb,control) { $token.label config -text "Copying:" } else { $token.label config -text "Moving:" } update idletasks } } method _dd_drophandler {win args} { global tkdesk dsk_debug "_dd_drophandler" set flist $args set tkdesk(drop_on_item) 1 if ![$frame.dlb _dd_end [winfo pointerx .] [winfo pointery .] 6] { return } if {$win == ""} { set win $framedlb.text } if {$win == "$framedlb.text"} { set xy [blt_drag&drop location] set x [expr [lindex $xy 0]-[winfo rootx $framedlb.text]] set y [expr [lindex $xy 1]-[winfo rooty $framedlb.text]] catch "wm withdraw $tkdesk(dd_token_window)" update set f [$framedlb.text get "@$x,$y linestart" \ "@$x,$y + 1 lines linestart"] set file [string trimright [lindex [split $f \t] 0]] #puts $file if $tkdesk(append_type_char) { set file [dskC_striptc $file] } set dest $directory if {$file == ".."} { } if {$file != ""} { #puts $dest$file set isdir 0 catch {set isdir [file isdirectory $dest$file]} if $isdir { #set di [$framedlb.text dlineinfo "end - 1 lines"] #set maxy [expr [lindex $di 1] + [lindex $di 3]] #if {$y < $maxy} { set dest $dest$file/ #} # else drop underneath last entry -> retain dest } } } else { # drop on menubutton above text widget set dest $directory } if {$dest == ""} { dsk_errbell cb_error "This listbox is not a valid target (since it's empty)." return } #if ![file writable $dest] { # dsk_errbell # if {$dest != ""} { # cb_error "You don't have write permission for this directory!" # } else { # cb_error "This listbox is not a valid target (since it's empty)." # } # return #} dsk_debug "Rec.: $flist" dsk_debug "dest: $dest" if {[string first "$tkdesk(trashdir)/" $dest] == -1} { dsk_ddcopy $flist $dest } else { if !$tkdesk(quick_dragndrop) { dsk_delete $flist } else { if {!$tkdesk(file_lb,control) && !$tkdesk(really_delete)} { dsk_ddcopy $flist $dest } else { if {[cb_yesno "Really deleting! Are you sure that this is what you want?"] == 0} { dsk_sound dsk_really_deleting dsk_bgexec "$tkdesk(cmd,rm) $flist" \ "Deleting [llength $flist] File(s)..." dsk_refresh "$flist $dest" } } } } } method _dragscroll {mode} { if {$mode == "press"} { eval [bind Text ] } else { eval [bind Text ] } } method _mb_label {} { global tkdesk set dn [file tail [string trimright $directory /] ] set l $dn/$mask set sl [string length $dn] # assume 12 to be the average char width... set mbw [winfo width $frame.mb] if {$mbw > 1} { set maxw [expr $mbw / 12] } else { set maxw [expr $tkdesk(file_lb,width) - 10] } if {$sl > $maxw} { # leave space for "..." set l "[string range $dn 0 [expr $maxw -1]].../$mask" bind $frame.mb {} cb_balloonHelp $frame.mb "$dn/$mask" } else { bind $frame.mb {} cb_balloonHelp $frame.mb "This is a menu button!" } return $l } method _selmask {} { $frame.dlb select name *[file extension [lindex [lindex [$frame.dlb get] [$frame.dlb select get]] 0]] $this _selstatus } method _tag_icons {} { global tkdesk if $add_icons { if {$old_selection != ""} { foreach i $old_selection { catch {$framedlb.text.l:$i config \ -bg [cb_col $tkdesk(color,background)]} } } set old_selection [$frame.dlb select get] foreach i $old_selection { catch {$framedlb.text.l:$i config \ -bg [cb_col $tkdesk(color,listsel)]} } } } method _quick_load {sel} { global tkdesk if ![info exists tkdesk(quick_load_editor)] return if {$sel == ""} return set file [string trimright [lindex [split [lindex [$frame.dlb get] \ [lindex $sel 0]] \t] 0] " "] if $tkdesk(append_type_char) { set file [dskC_striptc $file] } if {[string first "/" $file] == -1} { set file "$directory$file" } set isdir 0 catch {set isdir [file isdirectory $file]} if !$isdir { dsk_editor quickload $tkdesk(quick_load_editor) "$file" } } method _quick_info {sel} { global tkdesk if {$sel == ""} return set file [string trimright [lindex [split [lindex [$frame.dlb get] \ [lindex $sel 0]] \t] 0] " "] if $tkdesk(append_type_char) { set file [dskC_striptc $file] } if {[string first "/" $file] == -1} { set file "$directory$file" } dsk_debug "_quick_info file: $file" set o [lindex [itcl_info objects -class dsk_FileInfo] 0] if {$o != {}} { set w [$o getToplevel] if [winfo exists $w] { if {[wm state $w] == "normal"} { $o config -file $file } } } } method _show_tip {rx ry x y} { global tkdesk tkdesk_anno cb_tools if {!$cb_tools(balloon_help)} return set shown 0 set url "" set tw [$frame.dlb getFrame].text set fname [$frame.dlb get_entry_at $x $y] if {[string length $fname] == 0} { return } set absfname [string trimright $directory "/"]/$fname if {![file exists $absfname]} { return } if [regexp {\.url$} $absfname] { catch { set fd [open $absfname "r"] set url [gets $fd] close $fd } } if {[file type $absfname] == "link"} { if {$url != ""} { set url "$url,\nSymbolic link to [file readlink $absfname]." } else { set url "Symbolic link to [file readlink $absfname]" } } if [info exists tkdesk_anno($absfname)] { if {[string length $url] > 0} { set tipmsg "$fname:\n$url\n\n$tkdesk_anno($absfname)" } else { set tipmsg "$fname:\n$tkdesk_anno($absfname)" } } else { if {[string length $url] > 0} { set tipmsg "$fname:\n$url" } else { set tipmsg "$fname" } } set line [expr int([$tw index @$x,$y])] if $add_icons { set bb [$tw bbox $line.[string length $fname]] } else { set bb [$tw bbox $line.[expr [string length $fname] - 1]] } dsk_debug "_show_tip: $x $y ($line): $fname ($absfname), bb: \"$bb\"" if {[string length $bb] == 0} { _cb_balloonDisplay "" $tipmsg [expr $rx + 0] [expr $ry + 16] set shown 1 } else { set ww [expr [winfo width $tw] - (2 * [$tw cget -bd])] ot_maplist $bb x y w h if {$x + $w >= $ww} { _cb_balloonDisplay "" $tipmsg [expr $rx + 0] [expr $ry + 16] set shown 1 } else { set bb [$tw bbox $line.$add_icons] if {[string length $bb] == 0} { _cb_balloonDisplay "" $tipmsg \ [expr $rx + 0] [expr $ry + 16] set shown 1 } } } if !$shown { if {[info exists tkdesk_anno($absfname)] \ || [string length $url] > 0} { _cb_balloonDisplay "" $tipmsg [expr $rx + 0] [expr $ry + 16] } } } method _show_tip_leave {rx ry} { global tkdesk if ![winfo exists .cb_balloon] { catch {after cancel $tkdesk(_show_tip_id)} return } set win [winfo containing $rx $ry] if {[lsearch [list .cb_balloon [winfo children .cb_balloon]] $win] \ == -1} { catch { after cancel $tkdesk(_show_tip_id) destroy .cb_balloon } } } method _add_binding {seq script} { bind $framedlb.text $seq +$script } method _set_binding {seq script} { bind $framedlb.text $seq $script } method _configDirectory {} { # backend private proc of public var "directory" global tkdesk # Look if there's an action to execute when this directory is opened: if {[info exists tkdesk(action_on_open)] && !$refreshing} { set newd [string trimright $directory /] set dmatch 0 foreach d $tkdesk(action_on_open) { set dp [lindex $d 0] ;# list of patterns set da [lindex $d 1] ;# Tcl script to execute foreach p $dp { if {$p != "/"} { set p [string trimright [cb_tilde $p expand] /] } if {[string match $p $newd] || \ [string match [string trimright $p /*] $newd]} { set err [catch {eval [_expand_pc $da]} errmsg] if $err { global errorCode errorInfo dsk_errbell cb_error "Error while executing action from \"Directories\":\n$errmsg" if {$tkdesk(in_development)} { puts stderr $errmsg puts stderr $errorCode puts stderr $errorInfo } } set dmatch 1 break } } if $dmatch break } } # Look if there's button bar specifically for this directory: if {[info exists tkdesk(dir_button_bar)] && !$refreshing} { set newd [string trimright $directory /] set dmatch 0 foreach d $tkdesk(dir_button_bar) { set dp [lindex $d 0] ;# list of patterns set db [lindex $d 1] ;# Button Bar description foreach p $dp { if {$p != "/"} { set p [string trimright [cb_tilde $p expand] /] } if {[string match $p $newd] || \ [string match [string trimright $p /*] $newd]} { $viewer _create_dir_button_bar $db set dmatch 1 break } } if $dmatch break } if !$dmatch { $viewer _remove_dir_button_bar } } # read the filelist and modification time set err [catch {set mtime [file mtime $directory]}] if {$err} { return } dsk_debug "Reading dir: $directory (with $ls_cmd)" dsk_status "Reading $directory ..." set list "" catch {unset mt} ;# array for matching tags dskC_ls_and_tag $directory $frame.dlb config -bg [cb_col $tkdesk(color,filelb)] dsk_debug "Done." if $print_ready { dsk_status_ready 0 } } proc font {fnt} { foreach obj [itcl_info objects -class dsk_FileListbox] { if [winfo exists $obj.dlb.text] { $obj.dlb.text config -font [cb_font $fnt] } } set lbfont $fnt } proc color {col} { foreach obj [itcl_info objects -class dsk_FileListbox] { if [winfo exists $obj.dlb.text] { $obj.dlb.text config -foreground $col } } set lbcolor $col } proc defimage {img} { set default_image $img } proc pathimage {img} { set path_image $img } proc showall {val} { set _showall $val foreach object [itcl_info objects -class dsk_FileListbox] { if {[$object cget -showall] != $val} { $object config -showall $val } } } proc longlist {val} { set _longlist $val foreach object [itcl_info objects -class dsk_FileListbox] { #if {[$object cget -longlist] != $val} { $object config -longlist $val #} } } proc topfolders {val} { set _topfolders $val foreach object [itcl_info objects -class dsk_FileListbox] { $object config -topfolders $val } } proc typechar {val} { set _typechar $val foreach object [itcl_info objects -class dsk_FileListbox] { $object config -typechar $val } } proc dotregular {val} { set _dotreg $val foreach object [itcl_info objects -class dsk_FileListbox] { $object config -dotregular $val } } proc addicons {val} { set _addicons $val foreach object [itcl_info objects -class dsk_FileListbox] { if {[$object cget -add_icons] != $val} { $object config -add_icons $val } } } proc sort {val} { set _sort $val foreach object [itcl_info objects -class dsk_FileListbox] { $object config -sort $val } } # syntax: tag create ?font? returns tag name # tag config ?font? # tag reset proc tag {cmd args} { global tkdesk switch -glob -- $cmd { create { set tname filetag[incr numfiletags] set pat [lindex $args 0] if {[llength $pat] > 1} { if {[lindex $pat 0] == "dir"} { set tags($tname,match) "[lindex $pat 1]/" } else { set tags($tname,match) "[lindex $pat 1]\\*" } } else { set tags($tname,match) "${pat}_" } if {[winfo depth .] != 1} { set tags($tname,color) [lindex $args 1] } else { set tags($tname,color) "black" } if {[llength $args] > 2} { set f [lindex $args 2] if {$f != ""} { set tags($tname,font) $f } else { set tags($tname,font) $lbfont } } else { set tags($tname,font) $lbfont } if {[llength $args] > 3} { set f [lindex $args 3] if {$f != ""} { set tags($tname,image) [lindex $args 3] } else { set tags($tname,image) $default_image } } elseif ![info exists tags($tname,image)] { set tags($tname,image) $default_image } #eval lappend taglist $tname # Comment the previous line and uncomment the following two # to let file_tags overrule type tags. set di [lsearch $taglist dir] set taglist [linsert $taglist $di $tname] foreach this \ [itcl_info objects -class dsk_FileListbox] { [$this getLbObj] tag config $tname \ -foreground $tags($tname,color) \ -font $tags($tname,font) } return $tname } config* { set tname [lindex $args 0] if {[winfo depth .] != 1} { set tags($tname,color) [lindex $args 1] } else { set tags($tname,color) "black" } if {[llength $args] > 2} { set f [lindex $args 2] if {$f != ""} { set tags($tname,font) $f } else { set tags($tname,font) $lbfont } } else { set tags($tname,font) $lbfont } if {[llength $args] > 3} { set tags($tname,image) [lindex $args 3] } elseif ![info exists tags($tname,image)] { set tags($tname,image) $default_image } foreach this \ [itcl_info objects -class dsk_FileListbox] { [$this getLbObj] tag config $tname \ -foreground $tags($tname,color) \ -font $tags($tname,font) } if {$tname == "dir"} { set tkdesk(color,directories) $tags($tname,color) set tkdesk(font,directories) $tags($tname,font) } elseif {$tname == "symdir"} { set tkdesk(color,symdirectories) $tags($tname,color) set tkdesk(font,symdirectories) $tags($tname,font) } elseif {$tname == "exec"} { set tkdesk(color,executables) $tags($tname,color) set tkdesk(font,executables) $tags($tname,font) } elseif {$tname == "symexec"} { set tkdesk(color,symexecutables) $tags($tname,color) set tkdesk(font,symexecutables) $tags($tname,font) } elseif {$tname == "sym"} { set tkdesk(color,symlinks) $tags($tname,color) set tkdesk(font,symlinks) $tags($tname,font) } } reset { set numfiletags 0 set taglist "dir exec sym symdir symexec" } default { error "unknown tag cmd: $cmd" } get { set ti {} foreach t $taglist { lappend ti [list $t $tags($t,match) \ $tags($t,color) $tags($t,font) $tags($t,image)] } return $ti } } } proc initC {} { set patlist "" foreach t $taglist { lappend patlist $tags($t,match) } dskC_init_ftags } proc print_ready {val} { set print_ready $val } proc ignore {list} { set ignorelist $list } # # ----- Variables --------------------------------------------------------- # # # Options for dskC_ls: # protected ls_cmd public invert {0} { $this _build_ls_cmd $this refresh ::set [set this](invert) $invert } public sort {name} { global [set this] $this _build_ls_cmd $this refresh ::set [set this](sort) $sort } public notrivialdirs {0} { $this _build_ls_cmd $this refresh } public showall {0} { global [set this] $this _build_ls_cmd $this refresh ::set [set this](showall) $showall } public longlist {0} { global [set this] $this _build_ls_cmd $this refresh ::set [set this](longlist) $longlist $frame.dlb show_hsb $longlist } public topfolders {0} { $this _build_ls_cmd $this refresh } public typechar {0} { $this _build_ls_cmd $this refresh } public dotregular {0} { #$this _build_ls_cmd $this refresh } public add_icons {1} { $this refresh ::set [set this](add_icons) $add_icons } public dont_refresh 0 common _showall 0 common _longlist 0 common _topfolders 0 common _typechar 0 common _dotreg 0 common _addicons 0 common _sort name # # Other # public toplevel "" { # a hack for the file list windows global [set this] if {$toplevel != ""} { if [$frame.mb.menu cget -tearoff] { set laste 15 } else { set laste 14 } $frame.mb.menu delete [expr $laste -1] ;# delete separator $frame.mb.menu delete $laste ;# delete " Open Window " $frame.mb.menu insert [expr $laste -2] checkbutton \ -label " Add Icons " \ -variable [set this](add_icons) \ -command "$this config -add_icons \[set [set this](add_icons)\]" ::set [set this](add_icons) $add_icons $frame.mb.menu insert [expr $laste -2] checkbutton \ -label " Folders On Top " \ -variable [set this](topfolders) \ -command "$this config -topfolders \[set [set this](topfolders)\]" ::set [set this](topfolders) $topfolders $frame.mb.menu add separator $frame.mb.menu add command -label "Open Browser " \ -command "dsk_FileViewer .fv\[dsk_FileViewer :: id\] \ -directory \[$this info public directory -value\]" } } public dir {} { $this config -directory $dir } public directory {} { global tkdesk if {[string index $directory 0] == "~"} { catch {set directory [glob $directory]} } if {[string length $directory] > 0} { dsk_busy _configDirectory dsk_lazy } } public mask {*} { $this config -directory $directory if {$toplevel == ""} { $viewer status "Ready." } } public width {10} { $frame.dlb config -width $width } public height {10} { $frame.dlb config -height $height } public pad {4} { $frame.dlb config -pad $pad } public viewer {} { bind $framedlb.text <1> " $viewer _selecting $this [bind $framedlb.text <1>] bind $framedlb.text \"$this _selstatus\"" bind $framedlb.text " $viewer _selecting $this [bind $framedlb.text ] bind $framedlb.text \"$this _selstatus\"" bind $framedlb.text " $viewer _selecting $this [bind $framedlb.text ] bind $framedlb.text \"$this _selstatus\"" bind $framedlb.text <2> "$viewer _selecting $this [bind $framedlb.text <2>]" bind $framedlb.text <3> "$viewer _selecting $this [bind $framedlb.text <3>]" } protected _last_mask {*} protected _last_size_stat "" protected mtime 0 protected refreshing 0 protected old_selection "" protected closed_image "" protected invert_mask 0 protected framedlb common default_image ficons16/file.xpm common path_image ficons16/diropen.xpm common lbfont fixed common lbcolor black common print_ready 1 common ignorelist {} common taglist {} common patlist {} lappend taglist dir exec sym symdir symexec common numfiletags 0 common tags set tags(dir,match) {*/} set tags(exec,match) {*\*} set tags(sym,match) {*@} set tags(symdir,match) {*-} set tags(symexec,match) {*+} set tags(dir,color) black set tags(exec,color) black set tags(sym,color) black set tags(symdir,color) black set tags(symexec,color) black set tags(dir,font) fixed set tags(exec,font) fixed set tags(sym,font) fixed set tags(symdir,font) fixed set tags(symexec,font) fixed set tags(dir,image) ficons16/dir.xpm set tags(exec,image) ficons16/exec.xpm set tags(sym,image) ficons16/sym.xpm set tags(symdir,image) ficons16/symdir.xpm set tags(symexec,image) ficons16/symexec.xpm } proc dsk_FileListbox_fileTags {} { global tkdesk if [info exists tkdesk(file_tags,directories)] { foreach tag $tkdesk(file_tags,directories) { set font ""; set licon ""; set dicon ""; ot_maplist $tag pats col font licon dicon catch {set col [subst $col]} catch {set font [subst $font]} if {$font != ""} {set font [cb_font $font]} catch {set licon [subst $licon]} catch {set dicon [subst $dicon]} if {$pats == "!default"} { set tkdesk(color,directories) $col set tkdesk(font,directories) $font dsk_FileListbox :: tag config "dir" [cb_col $col] $font $licon dsk_DeskItem :: defimg dir $dicon } elseif {$pats == "!symlink"} { set tkdesk(color,symdirectories) $col set tkdesk(font,symdirectories) $font dsk_FileListbox :: tag config "symdir" [cb_col $col] $font $licon } elseif {$pats == "!opened"} { dsk_FileListbox :: pathimage $licon } else { foreach pat $pats { dsk_FileListbox :: tag create "dir $pat" [cb_col $col] $font $licon } } } } if [info exists tkdesk(file_tags,executables)] { foreach tag $tkdesk(file_tags,executables) { set font ""; set licon ""; set dicon ""; ot_maplist $tag pats col font licon dicon catch {set col [subst $col]} catch {set font [subst $font]} if {$font != ""} {set font [cb_font $font]} catch {set licon [subst $licon]} catch {set dicon [subst $dicon]} if {$pats == "!default"} { set tkdesk(color,executables) $col set tkdesk(font,executables) $font dsk_FileListbox :: tag config "exec" [cb_col $col] $font $licon dsk_DeskItem :: defimg exec $dicon } elseif {$pats == "!symlink"} { set tkdesk(color,symexecutables) $col set tkdesk(font,symexecutables) $font dsk_FileListbox :: tag config "symexec" [cb_col $col] $font $licon } else { foreach pat $pats { dsk_FileListbox :: tag create "exec $pat" [cb_col $col] $font $licon } } } } # this must be the last so that the other lists get precedence if [info exists tkdesk(file_tags)] { foreach tag $tkdesk(file_tags) { set font ""; set licon ""; set dicon ""; ot_maplist $tag pats col font licon dicon catch {set col [subst $col]} catch {set font [subst $font]} if {$font != ""} {set font [cb_font $font]} catch {set licon [subst $licon]} catch {set dicon [subst $dicon]} if {$pats == "!default"} { dsk_FileListbox :: font $font dsk_FileListbox :: color [cb_col $col] dsk_FileListbox :: defimage $licon dsk_DeskItem :: defimg file $dicon } elseif {$pats == "!symlink"} { set tkdesk(color,symlinks) $col set tkdesk(font,symlinks) $font dsk_FileListbox :: tag config "sym" [cb_col $col] $font $licon } else { foreach pat $pats { dsk_FileListbox :: tag create $pat [cb_col $col] $font $licon } } } } if [info exists tkdesk(file_tags,ignore)] { dsk_FileListbox :: ignore $tkdesk(file_tags,ignore) } dsk_FileListbox :: initC } # "System" Settings dsk_FileListbox :: font [cb_font $tkdesk(font,file_lbs)] dsk_FileListbox :: tag config dir \ [cb_col $tkdesk(color,directories)] [cb_font $tkdesk(font,directories)] dsk_FileListbox :: tag config exec \ [cb_col $tkdesk(color,executables)] [cb_font $tkdesk(font,executables)] dsk_FileListbox :: tag config sym \ [cb_col $tkdesk(color,symlinks)] [cb_font $tkdesk(font,symlinks)] dsk_FileListbox :: tag config symdir \ [cb_col $tkdesk(color,symdirectories)] [cb_font $tkdesk(font,symdirectories)] dsk_FileListbox :: tag config symexec \ [cb_col $tkdesk(color,symexecutables)] [cb_font $tkdesk(font,symexecutables)] # "Preferences" Settings dsk_FileListbox :: showall $tkdesk(show_all_files) dsk_FileListbox :: longlist $tkdesk(long_listing) dsk_FileListbox :: topfolders $tkdesk(folders_on_top) dsk_FileListbox :: typechar $tkdesk(append_type_char) dsk_FileListbox :: dotregular $tkdesk(dot_regular) dsk_FileListbox :: addicons $tkdesk(add_icons) dsk_FileListbox :: sort $tkdesk(default_sort) # "FileTags" Settings dsk_FileListbox_fileTags tkdesk-2.0/tcldesk/Frame.tcl0100644000175000007640000000517210020457430014114 0ustar jccjcc# ============================================================================= # # File: Frame.tcl # Project: TkDesk # # Started: 04.11.98 # Changed: 04.11.98 # Author: cb # # Description: Implements a generic Frame class which instantiates # a Tk frame widget to be used be subclasses implementing # megawidgets. # # Copyright (C) 1998 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ============================================================================= # ----------------------------------------------------------------------------- # class Frame # Instantiates a Tk frame widget. Subclasses can access this widget # through protected variable frame. An external interface is provided # via the getFrame method. # itcl_class Frame { constructor {config} { #puts "Frame $this" #puts "class [::$this info class]" if {[info tclversion] >= 8.0} { set frame [::frame [namespace tail $this]-frame \ -class [namespace tail [$this info class]]] } else { set frame [::frame [string trimleft $this-frame ":"] \ -class [::$this info class]] } #puts "frame: $frame" } destructor { catch {destroy $frame} } # ----- Methods and Procs ------------------------------------------------- method config {config} { } method cget {var} { return [set [string trimleft $var -]] } method getFrame {} { return $frame } # ----- Class Variables -------------------------------------------------- protected frame "" public bd {} { config -borderwidth $bd } public borderwidth {} { if [winfo exists $frame] { $frame config -bd $borderwidth } } public bg {} { config -background $bg } public background {} { if [winfo exists $frame] { $frame config -bg $background } } public relief {} { if [winfo exists $frame] { $frame config -relief $relief } } } tkdesk-2.0/tcldesk/HistEntry.tcl0100644000175000007640000001710710020457430015014 0ustar jccjcc# ============================================================================= # # File: HistEntry.tcl # Project: TkDesk # # Started: 28.01.97 # Changed: 28.01.97 # Author: cb # # # Copyright (C) 1997 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_HistEntry #s method config {config} #s method ok_pressed {} #s method add_pressed {} #s method histmenu {} #s proc id {} # # ----------------------------------------------------------------------------- # # ============================================================================= # # Class: dsk_HistEntry # Desc: Creates a dialog window with a text entry field plus # attached history. # # Methods: # Procs: # Publics: # itcl_class dsk_HistEntry { inherit Toplevel constructor {config} { global tkdesk [set this] if {$tkdesk(tcl_version) < 8.0} { Toplevel::constructor } wm withdraw $top frame $top.f -bd 1 -relief raised pack $top.f -fill both -expand yes frame $top.fl pack $top.fl -in $top.f -fill x label $top.label -text $label pack $top.label -in $top.fl -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) set [set this](cbvar) $checkvalue if {$checklabel != ""} { checkbutton $top.cbBrowser -text $checklabel \ -variable [set this](cbvar) pack $top.cbBrowser -in $top.fl -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) } frame $top.fe pack $top.fe -in $top.f -fill both -expand yes entry $top.entry -width 40 -bd 2 -relief sunken \ -exportselection no set sel "" if {$entrydefault != ""} { $top.entry insert end $entrydefault $top.entry icursor end $top.entry xview end } elseif {$history != "" && $sel == ""} { $top.entry insert end [$history last] $top.entry icursor end $top.entry xview end } pack $top.entry -in $top.fe -fill x -expand yes -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 2 bind $top.entry {%W selection range 0 end} bind $top.entry "$top.bOK.button invoke" bind $top.entry "$top.bCancel.button invoke" bind $top.entry "$top.bCancel.button invoke; break" bind $top.entry "$this _entry_popup" bind $top.entry <3> "$this _entry_popup" cb_bindForCompletion $top.entry blt_drag&drop target $top.entry handler file \ "dd_handle_text $top.entry %v 1" if {$history != ""} { menubutton $top.mbHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $top.mbHist.menu pack $top.mbHist -in $top.fe -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipadx 2 -ipady 2 menu $top.mbHist.menu -postcommand "$this histmenu" # add dummy entry to work around bug in pre Tk 4.0p2: $top.mbHist.menu add command -label "dummy" $history changed } frame $top.fb -bd 1 -relief raised pack $top.fb -fill x cb_button $top.bOK -text " OK " \ -underline 3 -default 1 -command " set tkdesk(geometry,hist_entry) \[wm geometry $top\] set [set this](entry) \[$top.entry get\] destroy $top update idletasks $this ok_pressed " cb_button $top.bApply -text " Apply " \ -underline 1 -command " set [set this](entry) \[$top.entry get\] $this ok_pressed 0 " if $noapply { $top.bApply.button config -state disabled } if {$addbutton != ""} { set i 0 while {[string index $addbutton $i] == " "} {incr i} set sc [string index $addbutton $i] cb_button $top.bAdd -text $addbutton \ -underline $i -command " set tkdesk(geometry,hist_entry) \[wm geometry $top\] set [set this](entry) \[$top.entry get\] destroy $top update idletasks $this add_pressed " cb_addShortcutBinding $top.entry $top.bAdd.button "$sc" } cb_button $top.bCancel -text " Cancel " \ -underline 1 -command " set tkdesk(geometry,hist_entry) \[wm geometry $top\] $this delete " pack $top.bOK $top.bApply -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) if {$addbutton != ""} { pack $top.bAdd -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) } pack $top.bCancel -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) cb_addShortcutBinding $top.entry $top.bOK.button "o" cb_addShortcutBinding $top.entry $top.bCancel.button "c" if !$noapply { cb_addShortcutBinding $top.entry $top.bApply.button "a" } bind $top "focus $top.entry" wm minsize $top 348 117 if {$title == ""} { set title $label } wm title $top $title wm protocol $top WM_DELETE_WINDOW "$top.bCancel.button invoke" if ![info exists tkdesk(geometry,hist_menu)] { set tkdesk(geometry,hist_menu) "" } dsk_place_window $top hist_entry "" 0 1 wm deiconify $top if !$nograb { grab $top set old_focus [focus] focus -force $top.entry tkwait window $top catch {focus -force $old_focus} } } destructor { #after 10 rename $this-top {} ;# delete this name #catch {destroy $this} ;# destroy associated window } # # ----- Methods and Procs ------------------------------------------------- # method config {config} { } method ok_pressed {{delete 1}} { global [set this] if {$callback != ""} { eval $callback \ [list [set [set this](entry)]] [set [set this](cbvar)] } if $delete { delete } } method add_pressed {} { global [set this] if {$addcallback != ""} { eval $addcallback \ [list [set [set this](entry)]] [set [set this](cbvar)] } delete } method histmenu {} { global tkdesk catch "$top.mbHist.menu delete 0 last" if $tkdesk(sort_history) { set l [lsort [$history get]] } else { set l [$history get] } set ne 0 set menu $top.mbHist.menu foreach ent $l { $menu add command -label $ent \ -command "$top.entry delete 0 end ;\ $top.entry insert end [list $ent]" \ -font [cb_font $tkdesk(font,entries)] incr ne if {$ne > $tkdesk(_max_menu_entries)} { set om $menu $menu add cascade -label "More..." \ -menu [set menu $menu.c$ne] catch {destroy $menu}; menu $menu; set ne 0 foreach b [bind $om] {bind $menu $b [bind $om $b]} } } } method _entry_popup {} { if {$entrypopupproc != ""} { $entrypopupproc $top.entry } } proc id {} { set i $id incr id return $i } # # ----- Variables --------------------------------------------------------- # common id 0 public label "dummy" public title "" public checklabel "" public checkvalue 0 public entrydefault "" public entrypopupproc "" public history "" public callback "" public addbutton "" public addcallback "" public nograb 0 public noapply 0 } tkdesk-2.0/tcldesk/List.tcl0100644000175000007640000005050710024014636014000 0ustar jccjcc# ============================================================================= # # File: dsk_FileList.tcl # Project: TkDesk # # Started: 09.10.94 # Changed: 09.10.94 # Author: cb # # Description: Implements a class that opens a file-list toplevel. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_FileList #s method config {config} #s method cget {var} #s method close {} #s method curdir {} #s method refresh {{mode ""}} #s method refreshdir {dir} #s method select {cmd} #s method _dblclick {lb sel} #s method _popup {lb sel mx my} #s method _selecting {lb} #s method _dd_drophandler {} #s method _button_bar {} #s proc id {{cmd ""}} # # ============================================================================= # # ============================================================================= # # Class: dsk_FileList # Desc: Creates file-list toplevels. # # Methods: # Procs: # Publics: # itcl_class dsk_FileList { inherit dsk_Common Toplevel constructor {args} { global tkdesk env wm withdraw $top dsk_busy frame $top.fMenu -bd 2 -relief raised pack $top.fMenu -fill x _create_menubar $top.fMenu # ---- Button Bar if {[llength $tkdesk(small_button_bar)] > 0} { frame $top.fButtonBar -bd 1 -relief raised pack $top.fButtonBar -after $top.fMenu -fill x _create_button_bar $top.fButtonBar } # ---- Path Entry frame $top.fEntry -bd 1 -relief raised pack $top.fEntry -fill x entry $top.entry -bd 2 -relief sunken -width 5 bindtags $top.entry "$top.entry Entry All" bind $top.entry "[bind Entry ]; break" bind $top.entry { if $tkdesk(focus_follows_mouse) {focus %W} } bind $top.entry " $this config -dir \[%W get\] " bind $top.entry "focus $top" bind $top.entry <3> "update idletasks; $this _path_popup %X %Y" cb_bindForCompletion $top.entry cb_balloonHelp $top.entry {The path displayed in the current window. Change to visit another directory. The right mouse button gives a popup menu of parent directories.} blt_drag&drop target $top.entry \ handler file "dd_handle_text $top.entry %v 1" pack $top.entry -in $top.fEntry -side left -fill x \ -expand yes -padx $pad -pady $pad -ipady 2 frame $top.fEntry.dis -width $tkdesk(pad) pack $top.fEntry.dis -side right menubutton $top.mbHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $top.mbHist.menu pack $top.mbHist -in $top.fEntry -side right -ipadx 2 -ipady 2 menu $top.mbHist.menu \ -postcommand "dir_history buildmenu $top.mbHist.menu ; update" # add dummy entry to work around bug in pre Tk 4.0p2: $top.mbHist.menu add command -label "dummy" dir_history changed bind $top.mbHist.menu " set tkdesk(menu,control) 0 [bind Menu ]" bind $top.mbHist.menu " set tkdesk(menu,control) 1 [bind Menu ]" # --- File Listbox set lbname ::[namespace tail $top.flb] dsk_FileListbox $lbname -viewer $this \ -width $tkdesk(file_lb,minwidth) \ -height $tkdesk(file_lb,minheight) \ -pad $tkdesk(pad) -toplevel $top -notrivialdirs 0 pack [$lbname getFrame] -fill both -expand yes # ---- create status bar if $statbar { frame $top.fStatus -bd [expr $tkdesk(pad) > 0] -relief raised pack [set f $top.fStatus] -fill x label $f.l -anchor w -font [cb_font $tkdesk(font,status)] pack $f.l -fill x -padx [expr $tkdesk(pad) / 2] \ -pady [expr $tkdesk(pad) / 2] } # ---- bindings bind $top \ "set tkdesk(active_viewer) $this; break" bind $top "focus $top.entry; break" bind $top "dsk_fileinfo; break" bind $top "dsk_find_files; break" bind $top "dsk_create file; break" bind $top "dsk_create directory; break" bind $top "dsk_copy; break" bind $top "dsk_rename; break" bind $top "dsk_delete; break" bind $top "dsk_ask_exec; break" bind $top "dsk_edit; break" bind $top "dsk_ask_dir; break" bind $top "dsk_print; break" bind $top "dsk_openwith; break" bind $top {dsk_cd ~; break} bind $top {dsk_cd [list [dir_history back]]; break} bind $top {dsk_cd [list [dir_history forward]]; break} bind $top "dsk_openall; break" bind $top "dsk_cbhelp $tkdesk(library)/doc/Guide howto" bind $top {dsk_cd ..} bind $top "$this keynav up" bind $top "$this keynav down" bind $top "$this keynav pgup" bind $top "$this keynav pgdown" bind $top "$this keynav first" bind $top "$this keynav last" bind $top "$this keynav menu" bind $top "$this keynav lbmenu" foreach l {a b c d e f g h i j k l m n o p q r s t u v w x y z} { bind $top <$l> "$this keynav $l" bind $top "$this keynav $l reverse" } foreach l {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} { bind $top "$this keynav [string tolower $l] reverse" #bind $top "$this keynav $l" } update idletasks wm title $top "File List" wm minsize $top $tkdesk(file_lb,minwidth) $tkdesk(file_lb,minheight) set w [cb_max $tkdesk(file_lb,minwidth) \ [expr $tkdesk(file_lb,width) - 10]] dsk_place_window $top flist ${w}x$tkdesk(file_lb,height) 1 wm protocol $top WM_DELETE_WINDOW "$this close" if $tkdesk(fvwm) { # create the icon window # (this code is based upon the code posted by: # kennykb@dssv01.crd.ge.com (Kevin B. Kenny)) toplevel $top-icon -bg [cb_col $tkdesk(color,icon)] \ -class Icon wm withdraw $top-icon label $top-icon.label \ -image [dsk_image $tkdesk(icon,filelist)] -bd 0 \ -bg [cb_col $tkdesk(color,icon)] pack $top-icon.label -ipadx 2 -ipady 2 blt_drag&drop target $top-icon.label handler \ file "$this _dd_drophandler %v" update idletasks wm geometry $top-icon \ [winfo reqwidth $top-icon]x[winfo reqheight $top-icon] wm protocol $top-icon WM_DELETE_WINDOW "$this delete" wm iconwindow $top $top-icon } else { wm iconbitmap $top @$tkdesk(library)/images/xbm/filing_open.xbm } eval config $args dsk_sound dsk_new_filelist if {$isFileSel} { # # We're a file selector # if $statbar { destroy $top.fStatus } frame $top.fFileSel -bd [expr $tkdesk(pad) > 0] -relief raised pack [set f $top.fFileSel] -fill x label $f.lSel -text $fileSelLabel -anchor w pack $f.lSel -fill x -padx $tkdesk(pad) entry $f.eSel -bd 2 -relief sunken pack $f.eSel -fill x -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 2 bindtags $f.eSel "$f.eSel Entry all" bind $f.eSel { if $tkdesk(focus_follows_mouse) {focus %W} } frame $f.fButs pack $f.fButs -fill x button $f.bOK \ -command "$this _fs_ok; $this delete" \ -text " OK " -width 7 button $f.bCancel \ -command "$this _fs_cancel; $this delete" \ -text "Cancel" -width 7 pack $f.bOK $f.bCancel -in $f.fButs -side left -expand y \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 $lbname _add_binding "$this _fs_select" $lbname _set_binding " catch \{after cancel \$tkdesk(_show_tip_id)\} set tkdesk(file_lb,control) 0 set tmpsc \$tkdesk(single_click) set tkdesk(single_click) 0 set tkdesk(config_nostat) 1 set tkdesk(_doubleclick) 1 $this _fs_ok; $this delete; set tkdesk(config_nostat) 0 catch {set tkdesk(single_click) \$tmpsc} catch {unset tmpsc} break " wm protocol $top WM_DELETE_WINDOW \ "$this _fs_cancel; $this delete" wm title $top $fileSelTitle catch {wm transient $top [[dsk_active win] getToplevel]} raise $top } if {!$dontmap} { wm deiconify $top tkwait visibility $top catch "lower $top .dsk_welcome" update } $top.entry icursor end $top.entry xview end pack propagate $top 0 update dsk_lazy } destructor { global env tkdesk # add current directory to the path history: if {$last_directory != ""} { if {[string first $env(HOME) $last_directory] == 0} { dir_history add [string_replace $last_directory $env(HOME) ~] } else { dir_history add $last_directory } } $top.flb delete catch {destroy $top-icon} set tkdesk(active_viewer) "" foreach fv "[itcl_info objects -class dsk_FileViewer] \ [itcl_info objects -class dsk_FileList]" { if {"$fv" != "$this"} { set tkdesk(active_viewer) $fv } } } # # ----- Methods and Procs ------------------------------------------------- # method config {config} { } method cget {var} { return [set [string trimleft $var -]] } method close {} { global tkdesk env # add current directory to history before closing window: if {[string first $env(HOME) $directory] == 0} { dir_history add [string_replace $directory $env(HOME) ~] } else { dir_history add $directory } if [winfo exists .dsk_appbar] { $this delete } elseif {[dsk_active viewer] == 1} { # about to close last window dsk_exit 1 } else { $this delete } } method keynav {what {opt ""}} { set lb ::[$top.flb getLbObj] switch $what { up { $lb select up } down { $lb select down } pgup { $lb select pgup } pgdown { $lb select pgdown } first { $lb select first } last { $lb select last } menu { set file [dsk_active sel] if {$file != ""} { dsk_popup "" $file [winfo pointerx .] [winfo pointery .] } } lbmenu { set lbf [$lbname getFrame] set mb $lbf.mb $mb.menu post [winfo rootx $mb] [expr [winfo rooty $mb] + [winfo height $mb]] focus $mb.menu $mb.menu activate 0 } default { if {$opt == "reverse"} { $lb select letter $what 1 } else { $lb select letter $what } } } $top.flb _tag_icons } method display_statbar {display} { global tkdesk catch {destroy $top.fStatus} if $display { frame $top.fStatus -bd [expr $tkdesk(pad) > 0] -relief raised pack [set f $top.fStatus] -fill x label $f.l -anchor w -font [cb_font $tkdesk(font,status)] pack $f.l -fill x -padx [expr $tkdesk(pad) / 2] \ -pady [expr $tkdesk(pad) / 2] } } method curdir {} { return $directory } method refresh {{mode ""}} { if {$mode == "all"} { foreach fv [itcl_info objects -class dsk_FileViewer] { $fv refresh } } } method refreshdir {dir} { global tkdesk if {$dir == $directory} { # $top.flb config -directory $directory $top.flb refresh if {$directory == "$tkdesk(trashdir)/"} { if $tkdesk(fvwm) { if {[llength [[$top.flb getLbObj] get]] == 0} { $top-icon.label config \ -image [dsk_image $tkdesk(icon,trash-empty)] } else { $top-icon.label config \ -image [dsk_image $tkdesk(icon,trash-full)] } } else { if {[llength [[$top.flb getLbObj] get]] == 0} { wm iconbitmap $top \ @$tkdesk(library)/images/xbm/trashcan.xbm } else { wm iconbitmap $top \ @$tkdesk(library)/images/xbm/trashcan_full.xbm } } } } } method select {cmd args} { global tkdesk switch -glob -- $cmd { get {# return a list of all selected files set sfl "" set sl [[$top.flb getLbObj] select get] if {$sl != ""} { set fl [[$top.flb getLbObj] get] foreach s $sl { set file [lindex [split [lindex $fl $s] \t] 0] set file [string trimright $file " "] #if {$file == "." || $file == ".."} continue if $tkdesk(append_type_char) { set file [dskC_striptc $file] } lappend sfl "$directory$file" } } return $sfl } clear {# clear selection in listbox [$top.flb getLbObj] select clear } X {# copy selected filenames to X selection set sfl [$this select get] if {$sfl != ""} { if {$args == "names"} { selection handle $top "$this _retrieve_X_names" } else { selection handle $top "$this _retrieve_X" } selection own $top } else { cb_info "Please select one or more files first." } } default { error "$this select: unknown option $cmd" } } } method _dblclick {lb sel} { global tkdesk if {$sel == "" || $lb == ""} { return } if {$tkdesk(single_click) && [llength $sel] > 1} { return } set dir [string trimright [$lb info public directory -value] "/"] #set file [lindex [lindex [$lb.dlb get] [lindex $sel 0]] 0] set file [string trimright [lindex [split \ [lindex [[$lb getLbObj] get] \ [lindex $sel 0]] \t] 0] " "] if $tkdesk(append_type_char) { set file [dskC_striptc $file] } if {[string first "/" $file] == -1} { set file "$dir/$file" } if {!$tkdesk(single_click) || \ ($tkdesk(single_click) && [file isdirectory $file])} { ::dsk_open $this "$file" } } method _popup {lb sel mx my} { if {$sel == "" || $lb == ""} { return } set dir [string trimright [$lb info public directory -value] "/"] #set file [lindex [lindex [$lb.dlb get] [lindex $sel 0]] 0] set file [string trimright [lindex [split \ [lindex [[$lb getLbObj] get] \ [lindex $sel 0]] \t] 0] " "] ::dsk_popup $lb "$dir/$file" $mx $my } method _selecting {lb} { # do nothing } method _dd_drophandler {args} { global tkdesk catch "wm withdraw $tkdesk(dd_token_window)" update set dest $directory set flist $args if ![file writable $dest] { dsk_errbell if {$dest != ""} { cb_error "You don't have write permission for this directory!" } else { cb_error "This listbox is not a valid target (since it's empty)." } } #dsk_debug "Rec.: $flist" #dsk_debug "dest: $dest" if {[string first "$tkdesk(trashdir)/" $dest] == -1} { dsk_ddcopy $flist $dest } else { if !$tkdesk(quick_dragndrop) { dsk_delete $flist } else { if {!$tkdesk(file_lb,control) && !$tkdesk(really_delete)} { dsk_ddcopy $flist $dest } else { if {[cb_yesno "Really deleting! Are you sure that this is what you want?"] == 0} { dsk_sound dsk_really_deleting dsk_bgexec "$tkdesk(cmd,rm) $flist" \ "Deleting [llength $flist] File(s)..." dsk_refresh "$flist $dest" } } } } } method _button_bar {} { _create_button_bar $top.fButtonBar } method _fs_select {} { set sel [file tail [lindex [dsk_active sel] 0]] if {[string length $sel] > 0} { set e $top.fFileSel.eSel $e delete 0 end $e insert end $sel $e icursor end $e xview end } } method _fs_ok {} { set e $top.fFileSel.eSel set f [$e get] if {[string length $f] > 0} { set fsSelFile $directory[$e get] } else { set fsSelFile "" } } method _fs_cancel {} { set fsSelFile "" } method _configDir {} { # backend private proc of public var "directory" global tkdesk set err [catch {set isdir [file isdirectory $dir]}] if !$err { if !$isdir { catch {set dir [_make_path_valid $dir]} if ![winfo exists .dsk_welcome] { # don't want this during startup catch {dsk_bell} cb_alert "The path you specified is not completely valid." } } elseif ![file readable $dir] { dsk_errbell cb_error "[file tail $dir]: Permission denied." return } } else { cb_error "Path (or user?) does not exist. Opening home directory." set dir ~ } if $tkdesk(menu,control) { dsk_FileList .dfl[dsk_FileList :: id] -directory $dir set tkdesk(menu,control) 0 } else { $this config -directory $dir } } proc id {{cmd ""}} { if {$cmd == ""} { set i $id incr id return $i } elseif {$cmd == "reset"} { set id 0 } } proc status_bar {display} { global tkdesk foreach fl [itcl_info objects -class dsk_FileList] { $fl display_statbar $display } if $display { dsk_status_ready } set statbar $display } proc fileSelector {args} { set fs ::.flFileSel catch {$fs delete} eval dsk_FileList $fs -isFileSel 1 $args set w [$fs getToplevel] grab $w set tkdesk(active_viewer) $fs tkwait window $w return $fsSelFile } # # ----- Variables --------------------------------------------------------- # public dontmap 0 public dir {} { if [winfo exists $top] { _configDir } } public directory {} { global env tkdesk if ![string match {[~/]*} $directory] { set directory [dsk_active dir]$directory } #set directory "[string trimright [glob $directory] "/"]/" set directory "[string trimright [cb_tilde $directory expand] "/"]/" set directory [dsk_canon_path $directory] dsk_debug "Directory $directory" $top.entry delete 0 end $top.entry insert end [cb_tilde $directory collapse] # right-justify the text in the path entry: if {[wm state $top] != "withdrawn"} { $top.entry icursor end $top.entry xview end } # save position of scrollbar: if {$last_directory != ""} { set ypos($last_directory) \ [lindex [cb_old_sb_get \ [::[$top.flb getLbObj] getFrame].sb] 2] } if {[string first "$tkdesk(trashdir)/" $directory] > -1} { $top.flb config -dont_refresh 1 if {$directory == "$tkdesk(trashdir)/"} { $top.flb config -notrivialdirs 1 -showall 1 } else { $top.flb config -notrivialdirs 0 -showall 1 } $top.flb config -dont_refresh 0 } $top.flb config -directory $directory if [info exists ypos($directory)] { [$top.flb getLbObj] _yview $ypos($directory) } # add this directory to the path history: if {$last_directory != ""} { if {[string first $env(HOME) $last_directory] == 0} { dir_history add [string_replace $last_directory $env(HOME) ~] } else { dir_history add $last_directory } } set last_directory $directory if {[string first "$tkdesk(trashdir)/" $directory] > -1} { pack forget $top.fEntry wm title $top Trash wm iconname $top Trash if $tkdesk(fvwm) { if {[llength [[$top.flb getLbObj] get]] == 0} { $top-icon.label config \ -image [dsk_image $tkdesk(icon,trash-empty)] } else { $top-icon.label config \ -image [dsk_image $tkdesk(icon,trash-full)] } } else { if {[llength [[$top.flb getLbObj] get]] == 0} { wm iconbitmap $top @$tkdesk(library)/images/xbm/trashcan.xbm } else { wm iconbitmap $top \ @$tkdesk(library)/images/xbm/trashcan_full.xbm } } } else { if {[wm title $top] == "Trash"} { $top.flb config -notrivialdirs 0 pack $top.fEntry -fill x -after $top.fButtonBar } #set wt [file tail [string trimright $directory "/"]]/ #wm title $top $wt #wm iconname $top $wt set_title if $tkdesk(fvwm) { $top-icon.label config \ -image [dsk_image $tkdesk(icon,filelist)] } else { wm iconbitmap $top @$tkdesk(library)/images/xbm/filing_open.xbm } } if !$height_set { set height_set 1 if {$isFileSel} { set off 9 } else { set off 1 } set h [cb_max \ [cb_min [expr [llength [[$top.flb getLbObj] get]] + $off] \ $tkdesk(file_lb,height)] \ $tkdesk(file_lb,minheight)] set w [cb_max $tkdesk(file_lb,minwidth) \ [expr $tkdesk(file_lb,width) - 10]] wm geometry $top ${w}x$h } } public pad {4} { } public isFileSel 0 public fileSelTitle "Select File" public fileSelLabel "Selection:" protected height_set 0 protected last_directory "" protected ypos protected lbname common id 0 common statbar 0 common fsSelFile "" } tkdesk-2.0/tcldesk/Periodic.tcl0100644000175000007640000001557710020457430014632 0ustar jccjcc# ============================================================================= # # File: periodic.tcl # Project: TkDesk # # Started: 14.10.94 # Changed: 17.10.94 # Author: cb # # Description: Implements procs for opening & executing files and for popups. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_Periodic #s method config {config} #s method execute {} #s method update {} #s proc id {} #s proc dsk_periodic {{cmd ""} {period 10}} #s proc dsk_periodic_cb {t cmd} # # ============================================================================= # if ![info exists tkdesk(geometry,periodic)] { set tkdesk(geometry,periodic) "" } # ============================================================================= # # Class: dsk_Periodic # Desc: Implements a window that periodically executes a shell # command and collects its output in a text widget. # # Methods: # Procs: # Publics: # itcl_class dsk_Periodic { inherit Toplevel constructor {args} { global tkdesk [set this] if {$tkdesk(tcl_version) < 8.0} { Toplevel::constructor } wm withdraw $top frame $top.fe -bd 1 -relief raised pack $top.fe -fill x label $top.lCmd -text "Command:" entry $top.eCmd -bd 2 -relief sunken -width 10 bind $top.eCmd "$top.bExec.button invoke" bind $top.eCmd "focus $top.eSec" menubutton $top.mbHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $top.mbHist.menu menu $top.mbHist.menu \ -postcommand "pcmd_history buildmenu $top.mbHist.menu" # add dummy entry to work around bug in pre Tk 4.0p2: $top.mbHist.menu add command -label "dummy" pcmd_history changed frame $top.fSep -width 16 label $top.lu1 -text "Exec every" entry $top.eSec -bd 2 -relief sunken -width 4 bind $top.eSec "$top.bExec.button invoke" bind $top.eSec "focus $top.eCmd" $top.eSec insert end $period label $top.lu2 -text "seconds" pack $top.lCmd $top.eCmd $top.mbHist $top.fSep $top.lu1 \ $top.eSec $top.lu2 \ -in $top.fe -side left -ipady 2 \ -padx $tkdesk(pad) -pady $tkdesk(pad) pack configure $top.eCmd -fill x -expand yes pack configure $top.mbHist -ipadx 2 -ipady 2 frame $top.ft -bd 1 -relief raised pack $top.ft -fill both -expand yes cb_text $top.ftext -vscroll 1 -hscroll 1 -lborder 1 -wrap none \ -pad $tkdesk(pad) -width 20 -height 2 -state disabled pack $top.ftext -in $top.ft -fill both -expand yes \ -pady $tkdesk(pad) frame $top.fb -bd 1 -relief raised pack $top.fb -fill x cb_button $top.bExec -text " Exec " -default 1 \ -command "$this config -period \[$top.eSec get\] $this config -command \[$top.eCmd get\] focus $top pcmd_history add \[$top.eCmd get\]" cb_button $top.bClose -text " Close " -command "$this close" pack $top.bExec $top.bClose -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) set [set this](dont_exec) 0 checkbutton $top.cbExec -text "Don't execute" \ -variable [set this](dont_exec) \ -command "if !\[set [set this](dont_exec)\] \ \{$top.bExec.button invoke\}" pack $top.cbExec -in $top.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) bind $top "+focus $top" bind $top "focus $top.eCmd" wm minsize $top 354 124 #wm geometry $top 592x278 dsk_place_window $top periodic 592x278 wm title $top "Periodic Execution" wm protocol $top WM_DELETE_WINDOW "$this close" eval config $args wm deiconify $top } destructor { } # # ----- Methods and Procs ------------------------------------------------- # method config {config} { } method close {} { global tkdesk set tkdesk(geometry,periodic) [wm geometry $top] delete } method execute {} { global [set this] tkdesk if [set [set this](dont_exec)] return if ![winfo exists $top] return if {[grab current] == ""} { dsk_busy ;# dsk_Periodic set err [catch {eval blt_bgexec [set this](var) \ -output [set this](result) \ -error [set this](error) \ $command = 8.0} { set top [::toplevel [namespace tail $this]-top \ -class [namespace tail [$this info class]]] } else { set top [::toplevel [string trimleft $this-top ":"] \ -class [::$this info class]] } #puts "top: $top" } destructor { catch {destroy $top} } # ----- Methods and Procs ------------------------------------------------- method config {config} { } method cget {var} { return [set [string trimleft $var -]] } method getToplevel {} { return $top } # ----- Class Variables -------------------------------------------------- protected top "x" } tkdesk-2.0/tcldesk/Viewer.tcl0100644000175000007640000005702410024014636014327 0ustar jccjcc# ============================================================================= # # File: dsk_FileViewer.tcl # Project: TkDesk # # Started: 11.10.94 # Changed: 21.12.96 # # Description: Implements a class for the main file viewer window. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_FileViewer #s method config {config} #s method cget {var} #s method close {} #s method curdir {} #s method refresh {{mode ""}} #s method refreshdir {dir} #s method status {str {do_update 1}} #s method status_ready {{do_update 1}} #s method selstatus {} #s method select {cmd} #s method _set_first_lb {num} #s method _dblclick {lb sel} #s method _popup {lb sel mx my} #s method _resize {} #s method _selecting {lb} #s method _dd_drophandler {} #s method _button_bar {} #s proc id {{cmd ""}} # # ============================================================================= # this var prevents double printing of status when opening a dir: set tkdesk(config_nostat) 0 # # ============================================================================= # # Class: dsk_FileViewer # Desc: This creates a toplevel that is the (or a) main window # for the file viewer (menu, entries, listboxes etc.). # # Methods: # Procs: # Publics: # itcl_class dsk_FileViewer { inherit dsk_Common Toplevel constructor {config} { global [set this] tkdesk env cb_tools if {$tkdesk(tcl_version) < 8.0} { Toplevel::constructor dsk_Common::constructor } wm withdraw $top dsk_busy # # Create menubar # frame $top.fMenu -bd 2 -relief raised pack $top.fMenu -fill x _create_menubar $top.fMenu # ---- create button bar if {[llength $tkdesk(button_bar)] > 0} { if {$tkdesk(pad) > 0} { frame $top.fButtonBar -bd 1 -relief raised } else { frame $top.fButtonBar -bd 0 -relief raised } pack $top.fButtonBar -after $top.fMenu -fill x _create_button_bar $top.fButtonBar } # ---- create path entry if {$tkdesk(pad) > 0} { frame $top.fPE -bd 1 -relief raised } else { frame $top.fPE -bd 0 -relief raised } pack $top.fPE -fill x entry $top.ePE -bd 2 -relief sunken bindtags $top.ePE "$top.ePE Entry All" pack $top.ePE -in $top.fPE -side left -fill x -expand yes \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 2 bind $top.ePE "[bind Entry ]; break" bind $top.ePE { if $tkdesk(focus_follows_mouse) {focus %W} } bind $top.ePE "$this config -dir \[$top.ePE get\]" bind $top.ePE "set tkdesk(menu,control) 1 $this config -dir \[$top.ePE get\] %W delete 0 end %W insert end \[$this curdir\]" bind $top.ePE "focus $top; break" bind $top.ePE <3> "update idletasks; $this _path_popup %X %Y" cb_bindForCompletion $top.ePE cb_balloonHelp $top.ePE {The path displayed in the current window. Change to visit another directory. The right mouse button gives a popup menu of parent directories.} blt_drag&drop target $top.ePE \ handler file "dd_handle_text $top.ePE %v 1" frame $top.fPE.dis -width $tkdesk(pad) pack $top.fPE.dis -side right menubutton $top.mbHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $top.mbHist.menu pack $top.mbHist -in $top.fPE -side right -ipadx 2 -ipady 2 menu $top.mbHist.menu \ -postcommand "dir_history buildmenu $top.mbHist.menu ; update" # add dummy entry to work around bug in pre Tk 4.0p2: $top.mbHist.menu add command -label "dummy" dir_history changed bind $top.mbHist.menu " set tkdesk(menu,control) 0 [bind Menu ]" bind $top.mbHist.menu " set tkdesk(menu,control) 1 [bind Menu ]" # ---- create horizontal scrollbar if {$tkdesk(pad) > 0} { frame $top.fsb -bd 1 -relief raised } else { frame $top.fsb -bd 0 -relief raised } pack $top.fsb -fill x scrollbar $top.hsb -orient horizontal -relief sunken \ -command "$this _set_first_lb" $top.hsb set 1 3 0 2 pack $top.hsb -in $top.fsb -fill x \ -padx $tkdesk(pad) -pady $tkdesk(pad) # # ---- Create listboxes # frame $top.fLBs pack $top.fLBs -fill both -expand yes set j 0 for {set i 0} {$i < $num_lbs} {incr i} { if {$tkdesk(tcl_version) >= 8.0} { set lbname ::[namespace tail $top.lb$j] } else { set lbname $top.lb$j } incr j dsk_FileListbox $lbname -viewer $this \ -width $tkdesk(file_lb,minwidth) \ -height $tkdesk(file_lb,minheight) -pad $tkdesk(pad) update idletasks set lbname [$lbname getFrame] pack $lbname -in $top.fLBs -side left -fill both -expand yes #update pack propagate $lbname 0 } set num_lbs_created $j # ---- create status bar frame $top.fStatus -bd [expr $tkdesk(pad) > 0] -relief raised pack [set f $top.fStatus] -fill x label $f.l -anchor w -font [cb_font $tkdesk(font,status)] # -bd [expr $tkdesk(pad) > 0] -relief sunken #pack $f.l -fill x -padx $tkdesk(pad) -pady $tkdesk(pad) pack $f.l -fill x -padx [expr $tkdesk(pad) / 2] \ -pady [expr $tkdesk(pad) / 2] # # Bindings # bind $top \ "set tkdesk(active_viewer) $this; break" bind $top "focus $top.ePE; break" bind $top "dsk_fileinfo; break" bind $top "dsk_find_files; break" bind $top "dsk_create file; break" bind $top "dsk_create directory; break" bind $top "dsk_copy; break" bind $top "dsk_rename; break" bind $top "dsk_delete; break" bind $top "dsk_ask_exec; break" bind $top "dsk_edit; break" bind $top "dsk_ask_dir; break" bind $top "dsk_bookmark add; break" bind $top "dsk_print; break" bind $top "dsk_openwith; break" bind $top {dsk_cd ~; break} bind $top {dsk_cd [list [dir_history back]]; break} bind $top {dsk_cd [list [dir_history forward]]; break} bind $top "dsk_openall; break" bind $top "dsk_cbhelp $tkdesk(library)/doc/Guide howto" #bind $top dsk_about bind $top {dsk_cd ..} bind $top "$this keynav up" bind $top "$this keynav down" bind $top "$this keynav pgup" bind $top "$this keynav pgdown" bind $top "$this keynav first" bind $top "$this keynav last" bind $top "$this keynav menu" bind $top "$this keynav lbmenu" foreach l {a b c d e f g h i j k l m n o p q r s t u v w x y z} { bind $top "$this keynav $l" bind $top "$this keynav $l reverse" } foreach l {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} { bind $top "$this keynav [string tolower $l] reverse" #bind $top "$this keynav $l" } # # Window manager settings # update idletasks wm title $top "TkDesk Version $tkdesk(version)" wm minsize $top $tkdesk(file_lb,minwidth) $tkdesk(file_lb,minheight) #wm geometry $top $tkdesk(file_lb,width)x$tkdesk(file_lb,height) dsk_place_window $top fbrowser \ $tkdesk(file_lb,width)x$tkdesk(file_lb,height) 1 wm protocol $top WM_DELETE_WINDOW "$this close" if $tkdesk(fvwm) { # create the icon window # (this code is based on the code posted by: # kennykb@dssv01.crd.ge.com (Kevin B. Kenny)) toplevel $top-icon -bg [cb_col $tkdesk(color,icon)] \ -class Icon wm withdraw $top-icon label $top-icon.label \ -image [dsk_image $tkdesk(icon,filebrowser)] -bd 0 \ -bg [cb_col $tkdesk(color,icon)] pack $top-icon.label -ipadx 2 -ipady 2 blt_drag&drop target $top-icon.label handler \ file "$this _dd_drophandler %v" update idletasks wm geometry $top-icon \ [winfo reqwidth $top-icon]x[winfo reqheight $top-icon] wm protocol $top-icon WM_DELETE_WINDOW "$this delete" wm iconwindow $top $top-icon } else { wm iconbitmap $top @$tkdesk(library)/images/xbm/bigfiling.xbm } incr count ;# incr the counter of opened fv windows set tkdesk(menu,control) 0 set tkdesk(file_lb,control) 0 # # Explicitly handle config's that may have been ignored earlier # foreach attr $config { config -$attr [set $attr] } dsk_sound dsk_new_filebrowser if !$dontmap { wm deiconify $top tkwait visibility $top catch "lower $top .dsk_welcome" update } # the status line may become quite large #pack propagate $top.fStatus 0 pack propagate $top 0 update dsk_lazy } destructor { global tkdesk for {set i 0} {$i < $num_lbs_created} {incr i} { catch "$top.lb$i delete" } incr count -1 catch {destroy $top-icon} set tkdesk(active_viewer) "" foreach fv "[itcl_info objects -class dsk_FileViewer] \ [itcl_info objects -class dsk_FileList]" { if {"$fv" != "$this"} { set tkdesk(active_viewer) $fv } } } # # ----- Methods and Procs ------------------------------------------------- # method config {config} { } method cget {var} { return [set [string trimleft $var -]] } method close {} { global tkdesk env # add current directory to history before closing window: if {[string first $env(HOME) $directory] == 0} { dir_history add [string_replace $directory $env(HOME) ~] } else { dir_history add $directory } if [winfo exists .dsk_appbar] { $this delete } elseif {[dsk_active viewer] == 1} { # about to close last window dsk_exit 1 } else { $this delete } } method curdir {} { return $directory } method refresh {{mode ""}} { if {$mode == "all"} { foreach fv [itcl_info objects -class dsk_FileViewer] { $fv refresh } } else { for {set i 0} {$i < $num_lbs_created} {incr i} { set lbdirs($i) "" } $this config -directory $directory } } method refreshdir {dir} { #puts "refreshdir: $dir" for {set i 0} {$i < $dir_depth} {incr i} { if {$lbdirs($i) == $dir} { $top.lb$i refresh #$this status "Ready." break } } } method keynav {what {opt ""}} { dsk_debug "keynav $what $opt" # scroll listboxes all to the right: _set_first_lb 1000 for {set i 0} {$i < $dir_depth - 1} {incr i} { ::[$top.lb$i getLbObj] select clear ::$top.lb$i _tag_icons } set lb ::[$top.lb$i getLbObj] switch $what { up { $lb select up } down { $lb select down } pgup { $lb select pgup } pgdown { $lb select pgdown } first { $lb select first } last { $lb select last } menu { set file [dsk_active sel] if {$file != ""} { dsk_popup "" $file [winfo pointerx .] [winfo pointery .] } } lbmenu { set lbf [$top.lb$i getFrame] set mb $lbf.mb $mb.menu post [winfo rootx $mb] [expr [winfo rooty $mb] + [winfo height $mb]] focus $mb.menu $mb.menu activate 0 } default { if {$opt == "reverse"} { $lb select letter $what 1 } else { $lb select letter $what } } } ::$top.lb[expr $dir_depth -1] _tag_icons } method select {cmd args} { global tkdesk switch -glob -- $cmd { get {# return a list of all selected files set sfl "" for {set i 0} {$i < $dir_depth} {incr i} { set err [catch { if ![winfo exists [[$top.lb$i getLbObj] getFrame]] { continue } } errmsg] if {$err} { continue } set sl [::[$top.lb$i getLbObj] select get] if {$sl != ""} { set fl [::[$top.lb$i getLbObj] get] foreach s $sl { set file [lindex [split [lindex $fl $s] \t] 0] set file [string trimright $file " "] if $tkdesk(append_type_char) { set file [dskC_striptc $file] } lappend sfl "$lbdirs($i)$file" } } } return $sfl } clear {# clear selection in all listboxes for {set i 0} {$i < $num_lbs_created} {incr i} { ::[$top.lb$i getLbObj] select clear $top.lb$i _tag_icons } $this selstatus } X {# copy selected filenames to X selection set sfl [$this select get] if {$sfl != ""} { if {$args == "names"} { selection handle $top "$this _retrieve_X_names" } else { selection handle $top "$this _retrieve_X" } selection own $top } else { cb_info "Please select one or more files first." } } default { error "$this select: unknown option $cmd" } } } method _set_first_lb {num} { global tkdesk # lb$num is to become the leftmost listbox #puts "$num, $first_lb, $dir_depth; [$top.hsb get]" if $tkdesk(dynamic_scrollbars) { if {$dir_depth > $num_lbs} { pack $top.fsb -before $top.fLBs -fill x } else { pack forget $top.fsb } } if {$first_lb == $num && \ $dir_depth == [lindex [$top.hsb get] 0]} return if {$num < 0} { set num 0 } set max_flb [cb_max 0 [expr $dir_depth - $num_lbs]] if {$num > $max_flb} { set num $max_flb } if {$first_lb == 0 && $num == 0} { $top.hsb set $dir_depth $num_lbs $first_lb \ [expr $first_lb + $num_lbs - 1] return } if {$first_lb == $max_flb && $num == $max_flb} { $top.hsb set $dir_depth $num_lbs $first_lb \ [expr $first_lb + $num_lbs - 1] return } set diff [expr $num - $first_lb] #puts "diff: $diff" switch -- $diff { 1 { pack forget [$top.lb$first_lb getFrame] if {$num_lbs > 1} { set i [expr $num + $num_lbs - 1] pack [$top.lb$i getFrame] \ -after [$top.lb[expr $num + $num_lbs-2] getFrame] \ -in $top.fLBs -side left -fill both \ -expand yes } else { set i [expr $num + $num_lbs - 1] pack [$top.lb$i getFrame] \ -in $top.fLBs -side left -fill both \ -expand yes } #update idletasks #update pack propagate [$top.lb$i getFrame] 0 } -1 { pack forget [$top.lb[expr $first_lb + $num_lbs - 1] getFrame] if {$num_lbs > 1} { pack [$top.lb$num getFrame] \ -before [$top.lb$first_lb getFrame] \ -side left -fill both \ -expand yes } else { pack [$top.lb$num getFrame] \ -in $top.fLBs -side left -fill both \ -expand yes } #update idletasks #update pack propagate [$top.lb$num getFrame] 0 } default { for {set i $first_lb} \ {$i < [expr $first_lb + $num_lbs]} {incr i} { pack forget [$top.lb$i getFrame] } for {set i $num} \ {$i < [expr $num + $num_lbs]} {incr i} { pack [$top.lb$i getFrame] \ -in $top.fLBs -side left -fill both \ -expand yes #update idletasks #update pack propagate [$top.lb$i getFrame] 0 } } } set first_lb $num $top.hsb set $dir_depth $num_lbs $first_lb \ [expr $first_lb + $num_lbs - 1] } method _dblclick {lb sel} { global tkdesk dsk_debug "Viewer $this, lb $lb, sel $sel" if {$sel == "" || $lb == ""} { return } if {$tkdesk(single_click) && [llength $sel] > 1} { return } set dir [string trimright [$lb info public directory -value] "/"] #set file [lindex [lindex [$lb.dlb get] [lindex $sel 0]] 0] set file [string trimright [lindex [split \ [lindex [::[$lb getLbObj] get] \ [lindex $sel 0]] \t] 0] " "] if $tkdesk(append_type_char) { set file [dskC_striptc $file] } if {[string first "/" $file] == -1} { set file "$dir/$file" } dsk_debug "file $file" if {!$tkdesk(single_click) || \ ($tkdesk(single_click) && [file isdirectory $file])} { ::dsk_open $this "$file" } if [file isdirectory $file] { select clear } } method _popup {lb sel mx my} { if {$sel == "" || $lb == ""} { return } set dir [string trimright [$lb info public directory -value] "/"] #set file [lindex [lindex [$lb.dlb get] [lindex $sel 0]] 0] set file [string trimright [lindex [split \ [lindex [::[$lb getLbObj] get] \ [lindex $sel 0]] \t] 0] " "] ::dsk_popup $lb "$dir/$file" $mx $my #$this selstatus } method _resize {} { global tkdesk [set this] if {$num_lbs != [set [set this](num_lbs)]} { dsk_busy pack propagate $top 1 if {$num_lbs_created < [set [set this](num_lbs)]} { for {set j $num_lbs_created} \ {$j < [set [set this](num_lbs)]} {incr j} { if {$tkdesk(tcl_version) >= 8.0} { set lbname ::$top.lb$j } else { set lbname $top.lb$j } dsk_FileListbox $lbname -viewer $this \ -width $tkdesk(file_lb,minwidth) -pad $tkdesk(pad) update set lbname [::$lbname getFrame] pack $lbname -in $top.fLBs -side left \ -fill both -expand yes pack propagate $lbname 0 } set num_lbs_created [set [set this](num_lbs)] } else { $this _set_first_lb 0 if {$num_lbs < [set [set this](num_lbs)]} { for {set j $num_lbs} \ {$j < [set [set this](num_lbs)]} {incr j} { pack [$top.lb$j getFrame] -in $top.fLBs -side left \ -fill both -expand yes update pack propagate [$top.lb$j getFrame] 0 } } else { for {set j [set [set this](num_lbs)]} \ {$j < $num_lbs} {incr j} { pack forget [$top.lb$j getFrame] } } } set num_lbs [set [set this](num_lbs)] update idletasks pack propagate $top 0 $this config -directory $directory dsk_lazy } } method _selecting {lb} { global tkdesk dsk_debug "_selecting: lb $lb, num_lbs_created $num_lbs_created" if {$tkdesk(free_selection)} { return } for {set i 0} {$i < $num_lbs_created} {incr i} { if {[string trimleft $top.lb$i :] != [string trimleft $lb :]} { ::[$top.lb$i getLbObj] select clear ::$top.lb$i _tag_icons } } } method _dd_drophandler {args} { global tkdesk catch "wm withdraw $tkdesk(dd_token_window)" update set dest $directory set flist $args if ![file writable $dest] { dsk_errbell if {$dest != ""} { cb_error "You don't have write permission for this directory!" } else { cb_error "This listbox is not a valid target (since it's empty)." } return } #dsk_debug "Rec.: $flist" #dsk_debug "dest: $dest" dsk_ddcopy $flist $dest } method _button_bar {} { _create_button_bar $top.fButtonBar } method _configDirectory {} { # backend private proc of public var "directory" global tkdesk env #set directory "[string trimright [glob $directory] "/"]/" set directory "[string trimright [cb_tilde $directory expand] "/"]/" set directory [dsk_canon_path $directory] dsk_debug "Directory $directory" set strip_i 0 if $tkdesk(strip_home) { if [string match "$env(HOME)/*" $directory] { set strip_i [string length "$env(HOME)"] } } if [info exists tkdesk(strip_parents)] { foreach d $tkdesk(strip_parents) { set d [string trimright $d /] if [string match "$d/*" $directory] { set strip_i [string length $d] break } } } # determine depth of directory set dir_depth 0 set first_i 0 set cmask "" set l [string length $directory] for {set i $strip_i} {$i < $l} {incr i} { if {[string index $directory $i] == "/"} { set ndir [string range $directory 0 $i] if ![info exists lbdirs($dir_depth)] { set lbdirs($dir_depth) $ndir } elseif {$ndir != $lbdirs($dir_depth)} { set lbdirs($dir_depth) $ndir } else { catch {set cmask [$top.lb$first_i cget -mask]} incr first_i } incr dir_depth } } #puts $cmask if {$first_i == $dir_depth && $first_i} { set first_i [expr $dir_depth - 1] } for {set i $dir_depth} {$i < $num_lbs_created} {incr i} { set lbdirs($i) "" } # # fill list boxes # dsk_FileListbox :: print_ready 0 for {set i $first_i} {$i < $dir_depth} {incr i} { if {$i >= $num_lbs_created && ![winfo exists $top.lb$i]} { if {$tkdesk(tcl_version) >= 8.0} { set lbname ::[namespace tail $top.lb$i] } else { set lbname $top.lb$i } dsk_FileListbox $lbname -viewer $this \ -width $tkdesk(file_lb,minwidth) \ -height $tkdesk(file_lb,minheight) -pad $tkdesk(pad) update idletasks if {$cmask != ""} { $top.lb$i set_mask $cmask } pack propagate [$lbname getFrame] 0 } $top.lb$i config -directory $lbdirs($i) if {$i > 0} { $top.lb[expr $i - 1] tagpath $lbdirs($i) } } $top.lb[expr $dir_depth - 1] tagpath if {$i > $num_lbs_created} { set num_lbs_created $i } else { while {$i < $num_lbs_created} { $top.lb$i clear incr i } } set flb [cb_max 0 [expr $dir_depth - $num_lbs]] #puts "dd: $dir_depth" $this _set_first_lb $flb # add last directory to the path history: if {$last_directory != ""} { if {[string first $env(HOME) $last_directory] == 0} { dir_history add [string_replace $last_directory $env(HOME) ~] } else { dir_history add $last_directory } } set last_directory $directory # update the path entry: $top.ePE delete 0 end $top.ePE insert end [cb_tilde $directory collapse] #wm title $top [cb_tilde $directory collapse] #wm iconname $top [file tail [string trimright $directory "/"]]/ set_title if !$tkdesk(config_nostat) { $this status "Ready. [dsk_fs_status $directory]" #$this status "Ready." } dsk_FileListbox :: print_ready 1 dsk_status_ready 0 } proc id {{cmd ""}} { if {$cmd == ""} { set i $id incr id return $i } elseif {$cmd == "reset"} { set id 0 set count 0 } } # # ----- Variables --------------------------------------------------------- # public num_lbs 3 public dontmap 0 public dir {} { global tkdesk set access_ok 1 set err [catch {$top config}] if !$err { set err [catch {set isdir [file isdirectory $dir]}] if !$err { if !$isdir { if [regexp {\.tar\.gz$|\.tgz$} $dir] { # it's a compressed tar file #set ls_proc {dsk_ls_tar} puts "tar: $dir" } else { catch {set dir [_make_path_valid $dir]} if ![winfo exists .dsk_welcome] { # don't want this during startup catch {dsk_bell} cb_alert "The path you specified is not completely valid." } } } elseif ![file readable $dir] { dsk_errbell cb_error "[file tail $dir]: Permission denied." set access_ok 0 } } else { cb_error "Path (or user?) does not exist. Opening home directory." set dir ~ } if {$access_ok} { if $tkdesk(menu,control) { if {$tkdesk(tcl_version) >= 8.0} { set lname ::.dfl[dsk_FileList :: id] } else { set lname .dfl[dsk_FileList :: id] } dsk_FileList $lname -directory $dir set tkdesk(menu,control) 0 } else { $this config -directory $dir } } } } public directory "/" { global tkdesk env if {[winfo exists $top]} { dsk_busy _configDirectory dsk_lazy } } protected dir_depth 0 ;# depth of the current directory protected first_lb 0 ;# number of leftmost listbox protected num_lbs_created 0 ;# number of created listboxes (>= num_lbs) protected lbdirs ;# array of each lb's directory protected sb_state "packed" protected last_directory "" protected ls_proc {} common count 0 common id 0 } tkdesk-2.0/tcldesk/action.tcl0100644000175000007640000012621710020457430014343 0ustar jccjcc# ============================================================================= # # File: action.tcl # Project: TkDesk # # Started: 14.10.94 # Changed: 17.10.94 # Author: cb # # Description: Implements procs for opening & executing files and for popups. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_open {viewer file} #s proc dsk_default_action {popuplist file {matchedvar ""}} #s proc dsk_history_file {file} #s proc dsk_openall {args} #s proc dsk_exec {args} #s proc dsk_exec_trace {arr idx op} #s proc dsk_path_exec {path args} #s proc dsk_ask_exec {{cmd ""} {file ""}} #s proc dsk_ask_exec_ok {entry val} #s proc dsk_ask_dir {{browser ""}} #s proc dsk_ask_dir_ok {entry val} #s proc dsk_ask_dir {{browser ""}} #s proc dsk_ask_dir_ok {entry val} #s proc dsk_filesel {label filter args} #s proc dsk_filesel_ok {entry val} #s proc dsk_filesel_browse {entry val} #s proc dsk_cd {{dir ""}} #s proc dsk_open_dir {dir} #s proc dsk_open_browser {dir} #s proc _expand_pc {cmd {file ""} {file1 ""} {file2 ""}} #s proc _make_fnames_safe {{flist ""}} #s proc _make_fname_safe {{file ""}} #s proc dsk_edit {args} #s proc dsk_view {args} #s proc dsk_confirm {msg script} #s proc dsk_read_string {msg script args} #s proc dsk_read_string_ok {entry val} #s proc dsk_read_string-old {msg script args} #s proc dsk_cbhelp {file {regexp ""}} #s proc dsk_print {args} #s proc dsk_netscape {type {loc ""} {args ""}} #s proc dsk_textview {file} # # ============================================================================= # # ----------------------------------------------------------------------------- # # Proc: dsk_open # Args: viewer name of calling file viewer window # file name of file # Returns: # Desc: Performs the default action for $file. Gets called after # a double click in a file listbox. Or from the Bookmarks # menu. # Side-FX: none # set dsk_open(lastcmd) "" set tkdesk(dont_add_cmd_to_history) 0 proc dsk_open {viewer file} { global tkdesk cmd dsk_open env dsk_debug "dsk_open: viewer $viewer, file $file" if [catch {$viewer getToplevel}] { set viewer "" } if ![file exists $file] { if {$viewer != ""} { $viewer config -dir [$viewer curdir] } cb_error "No such file or directory:\n$file" return } if ![file readable $file] { dsk_errbell cb_error "Permission denied." return } if [file isdirectory $file] { # DIRECTORY if ![file executable $file] { dsk_errbell cb_error "Permission denied (directory not executable)." return } set file [subst -nocommands -novariables [_make_fname_safe $file]] set dname [file tail $file] if [string match $dname "."] { set file [file dirname $file] } elseif [string match $dname ".."] { set file [file dirname $file] set file [file dirname $file] } dsk_debug "tkdesk(file_lb,control) $tkdesk(file_lb,control), viewer $viewer" if $tkdesk(file_lb,control) { if $tkdesk(in_browser) { dsk_FileViewer .fv[dsk_FileViewer :: id] \ -dir "$file" -num_lbs $tkdesk(num_lbs) } else { dsk_FileList .dfl[dsk_FileList :: id] -dir "$file" } set tkdesk(file_lb,control) 0 } elseif {$viewer != ""} { #puts "$viewer config -dir \"$file\"" set action [dsk_default_action directories $file] if {$action != ""} { dsk_debug "action $action" eval [_expand_pc $action $file] } } } elseif [file_executable $file] { # EXECUTABLE if !$tkdesk(file_lb,control) { dsk_busy if {$tkdesk(dot_regular)} { if {[file extension $file] == ""} { set action [dsk_default_action executables $file] } else { set action [dsk_default_action regulars $file] } } else { set action [dsk_default_action executables $file] } set isf 0 if {$action != ""} { if {$isf} { dsk_history_file $file } cd [dsk_active dir] eval [_expand_pc $action $file] cd ~ } dsk_lazy } else { set cmd [file tail $file] dsk_ask_exec $cmd set tkdesk(file_lb,control) 0 } } else { # REGULAR FILE if {$tkdesk(file_lb,control) == 1 || $tkdesk(menu,control) == 1} { #set ft [file tail $file] set ft [_make_fname_safe $file] if {[llength $ft] == 1} { set cmd "$dsk_open(lastcmd) $ft" } else { set cmd "$dsk_open(lastcmd) \"$ft\"" } set cmd [dsk_ask_exec $cmd] set tkdesk(file_lb,control) 0 set tkdesk(menu,control) 0 } elseif {$tkdesk(file_lb,control) == 2} { # called from one of the appbar special menus with Shift pressed dsk_DeskItem .di[dsk_DeskItem :: id] \ -file [cb_tilde $file expand] set tkdesk(file_lb,control) 0 } else { dsk_busy update idletasks set action [dsk_default_action regulars $file] if {$action != "" && $action != "-"} { # add file to file history dsk_history_file $file cd [dsk_active dir] # make TkDesk "lazy" before background execution dsk_lazy eval [_expand_pc $action $file] cd ~ } else { dsk_lazy } } } } # ---------------------------------------------------------------------------- # dsk_default_action popuplist file ?matchedvar? # Returns the default action for $file in list $tkdesk(popup,$popuplist). # If matchedvar is specified it's the name of a variable that will be # set to the matching pattern. # proc dsk_default_action {popuplist file {matchedvar ""}} { global tkdesk dsk_debug "dsk_default_action: popuplist $popuplist, file $file, matchedvar $matchedvar" if ![info exists tkdesk(popup,$popuplist)] return if {$matchedvar != ""} { upvar 1 $matchedvar mpat } set action_found 0 set action "" set fname [file tail $file] foreach entry $tkdesk(popup,$popuplist) { set patlist [lindex $entry 0] foreach pat $patlist { if [string match $pat $fname] { set elist [lindex $entry 1] set action "set tkdesk(error_source) Popups ;\ [lindex [lindex $elist 0] 1] ;\ set tkdesk(error_source) {}" set action_found 1 set mpat $pat break } } if $action_found break } return $action } proc dsk_history_file {file} { global tkdesk env set tkdesk(dont_add_cmd_to_history) 1 if {[string first $env(HOME) $file] == 0} { file_history add [string_replace $file $env(HOME) ~] } else { file_history add $file } set tkdesk(dont_add_cmd_to_history) 0 } # # ----------------------------------------------------------------------------- # # Proc: dsk_openall # Args: files (opt.) list of files # Returns: "" # Desc: Opens all selected files (or $files). "Open" means it # performs the default action for each file. # Side-FX: # proc dsk_openall {args} { global tkdesk set files $args if {$files == ""} { set files [dsk_active sel] } if {$files == ""} { dsk_bell cb_info "Please select one or more files first." return } dsk_busy foreach file $files { dsk_open [dsk_active window] $file } dsk_lazy return } # ---------------------------------------------------------------------------- # dsk_openwith files: # Asks for command to open $files with. proc dsk_openwith {{files ""}} { global tkdesk dsk_open if {$files == ""} { set files [dsk_active sel] } if {$files == ""} { dsk_bell cb_info "Please select one or more files first." return } set ft {} foreach file $files { lappend ft [_make_fname_safe $file] } set cmd "$dsk_open(lastcmd) $ft" set cmd [dsk_ask_exec $cmd] set tkdesk(file_lb,control) 0 set tkdesk(menu,control) 0 return } # # ----------------------------------------------------------------------------- # # Proc: dsk_exec # Args: args command line to execute # Returns: pid or "" # Desc: Executes command $args in the background. # If $args matches dsk_*, $args is evaluated. # Side-FX: none # set tkdesk(dsk_exec,pids) "" set tkdesk(dsk_exec,cmds) "" global dsk_exec if ![info exists dsk_exec(bgcnt)] {set dsk_exec(bgcnt) 0} if ![info exists dsk_exec(shell)] {set dsk_exec(shell) 0} proc dsk_exec {args} { global tkdesk env dsk_exec if {$args == ""} { return } set cmd $args set p [lindex $cmd 0] if [string match dsk_* $p] { #if {[info procs $p] != ""} { set err [catch {eval $cmd} errmsg] if $err { dsk_errbell cb_error "Malformed internal command.\n($errmsg)" } return #} else { # dsk_errbell # cb_alert "Not an internal command: $p" # return #} } else { if ![dsk_auto_execok $p] { dsk_errbell cb_alert "Can't execute: $p" return } } dsk_busy if !$tkdesk(dont_add_cmd_to_history) { if {[string first $env(HOME) $cmd] == 0} { exec_history add [string_replace $cmd $env(HOME) ~] } else { exec_history add $cmd } } set cnt [incr dsk_exec(bgcnt)] dsk_sound dsk_exec_launch if [file_executable $cmd] { # then dsk_exec is called from dsk_open cd [file dirname $cmd] if $tkdesk(exec_as_root) { set cmd [string_replace $tkdesk(cmd,su,exec) %c $cmd] dsk_debug "SU: $cmd" } set err [catch {set pid \ [eval blt_bgexec dsk_exec(bgvar,$cnt) \ $cmd >@stdout *} $token] { lappend shell_args $token } elseif [string match {2>*} $token] { lappend shell_args $token } elseif [string match {&} $token] { continue } else { lappend cmd $token } } if !$inredir { lappend shell_args " -1} { set cmd [string_replace $cmd " ~" " $env(HOME)"] } if [info exists dsk_exec(dir)] { if [file isdirectory $dsk_exec(dir)] { set dsk_exec(bgdir,$cnt) $dsk_exec(dir) cd $dsk_exec(dir) } unset dsk_exec(dir) } else { set dsk_exec(bgdir,$cnt) [dsk_active dir] cd $dsk_exec(bgdir,$cnt) } dsk_debug "Executing: sh -c \"exec $cmd\" $shell_args &" set err 0 if $tkdesk(exec_as_root) { set cmd [string_replace $tkdesk(cmd,su,exec) %c $cmd] dsk_debug "SU: $cmd" } if $dsk_exec(shell) { set dsk_exec(shell) 0 set err [catch {set pid [eval blt_bgexec dsk_exec(bgvar,$cnt) \ sh -c [list "exec $cmd"] >@stdout $shell_args &]} errmsg] } else { if !$tkdesk(in_development) { set err [catch {set pid [eval blt_bgexec dsk_exec(bgvar,$cnt) \ $cmd >@stdout $shell_args &]} errmsg] } else { set pid [eval blt_bgexec dsk_exec(bgvar,$cnt) \ $cmd >@stdout $shell_args &] } } cd ~ if {$err && !$tkdesk(in_development)} { dsk_errbell dsk_lazy cb_error $errmsg return } #set pid [exec sh -c \"exec $cmd\" $shell_args &] } set dsk_exec(bgcmd,$cnt) $cmd trace variable dsk_exec(bgvar,$cnt) w dsk_exec_trace regsub -all "\n" $cmd "; " cmd2 regsub -all "\[\t \]\[\t \]*" $cmd2 " " cmd2 dsk_status "Launched: $cmd2" dsk_lazy lappend tkdesk(dsk_exec,pids) $pid if {[llength $args] > 1} { lappend tkdesk(dsk_exec,cmds) "$args" } else { lappend tkdesk(dsk_exec,cmds) $args } if [winfo exists .dsk_jobs] { dsk_jobs_fill } return $pid } # this proc will be invoked when a dsk_exec'ed command exits: proc dsk_exec_trace {arr idx op} { global dsk_exec tkdesk set num [lindex [split $idx ,] 1] set cmd $dsk_exec(bgcmd,$num) set exit_code [lindex $dsk_exec(bgvar,$num) 2] unset dsk_exec(bgvar,$num) unset dsk_exec(bgcmd,$num) dsk_sound dsk_exec_exit regsub -all "\n" $cmd "; " cmd2 regsub -all "\[\t \]\[\t \]*" $cmd2 " " cmd2 dsk_status "Exit ($exit_code): $cmd2" if [info exists dsk_exec(bgdir,$num)] { set tkdesk(inhibit_status) 1 dsk_refresh $dsk_exec(bgdir,$num) set tkdesk(inhibit_status) 0 unset dsk_exec(bgdir,$num) } } # ----------------------------------------------------------------------------- # dsk_path_exec path args: # Execute $args in $path. # proc dsk_path_exec {path args} { global dsk_exec set dsk_exec(dir) $path eval dsk_exec $args } # ----------------------------------------------------------------------------- # dsk_path_view path args: # Execute $args in $path. # proc dsk_path_view {path args} { global dsk_exec set dsk_exec(dir) $path eval dsk_view $args } # ----------------------------------------------------------------------------- # dsk_raise_or_exec interp cmd raisewin # Checks whether $interp is already running. If it does sends it the command # to raise $raisewin (default "."). If it doesn't executes $cmd. # proc dsk_raise_or_exec {interp cmd {raisewin .}} { foreach int [winfo interps] { if {$int == $interp} { send -async $interp "wm deiconify $raisewin; raise $raisewin" dsk_status "Raised already running $interp" return 1 } } return [dsk_exec $cmd] } # ----------------------------------------------------------------------------- # dsk_exec_as_root args: # Execute $args in $path. # proc dsk_exec_as_root {args} { global tkdesk set ov $tkdesk(exec_as_root) set tkdesk(exec_as_root) 1 eval dsk_exec $args set tkdesk(exec_as_root) $ov } # ----------------------------------------------------------------------------- # dsk_view_as_root args: # Execute $args in $path. # proc dsk_view_as_root {args} { global tkdesk set ov $tkdesk(exec_as_root) set tkdesk(exec_as_root) 1 eval dsk_view $args set tkdesk(exec_as_root) $ov } # # ----------------------------------------------------------------------------- # # Proc: dsk_ask_exec # Args: none # Returns: pid # Desc: Asks the user which command to execute. # Side-FX: none # if ![info exists tkdesk(cmd_history)] { set tkdesk(cmd_history) "" } global dsk_ask_exec set dsk_ask_exec(cnt) 0 proc dsk_ask_exec {{cmd ""} {file ""}} { global tkdesk dsk_ask_exec set t .he_ask_exec if [winfo exists $t] { wm deiconify $t raise $t return } # cope with a bug in itcl 1.5 if {[itcl_info objects $t] != {}} { incr dsk_ask_exec(cnt) set t "$t$dsk_ask_exec(cnt)" } if $tkdesk(exec_as_root) { set lab "Open or Execute as root:" set dsk_ask_exec(as_root) 1 } else { set lab "Open or Execute:" set dsk_ask_exec(as_root) 0 } set dsk_asc_exec(cmd) "" dsk_HistEntry $t \ -title "Execute" \ -label $lab \ -checklabel "View Output" \ -checkvalue 0 \ -entrydefault $cmd \ -history cmd_history \ -callback dsk_ask_exec_ok \ -nograb 1 } proc dsk_ask_exec_ok {entry val} { global tkdesk dsk_open dsk_ask_exec set tmpcmd $entry if {$tmpcmd == ""} { dsk_bell return } set asr $tkdesk(exec_as_root) set tkdesk(exec_as_root) $dsk_ask_exec(as_root) cmd_history add $tmpcmd cd [dsk_active dir] if [file isdirectory $tmpcmd] { dsk_open_dir $tmpcmd } elseif {[file_executable $tmpcmd] || \ [dsk_auto_execok [lindex $tmpcmd 0]]} { set tmpcmd [_expand_pc $tmpcmd] if $val { set err [catch {eval dsk_view $tmpcmd} errmsg] } else { set err [catch {eval dsk_exec $tmpcmd} errmsg] } if $err { dsk_errbell cb_error $errmsg } set cmd $tmpcmd if {$cmd != ""} { set dsk_open(lastcmd) [string range $cmd 0 \ [expr [string first " " $cmd] - 1]] if {[set og [string first \" $dsk_open(lastcmd)]] > -1} { set dsk_open(lastcmd) [string range $dsk_open(lastcmd) \ 0 [expr $og - 2]] } } } elseif [file readable $tmpcmd] { dsk_open "" $tmpcmd } else { dsk_errbell cb_error "Couldn't open/execute $tmpcmd." } set tkdesk(exec_as_root) $asr cd ~ } # # ----------------------------------------------------------------------------- # # Proc: dsk_ask_dir # Args: none # Returns: "" # Desc: Asks the user which directory to open. # Side-FX: none # proc dsk_ask_dir {{browser ""}} { global tkdesk dsk_HistEntry .he[dsk_HistEntry :: id] \ -title "Open Directory" \ -label "Directory to open:" \ -checklabel "In Browser" \ -checkvalue [expr [string match "browser" $browser] || \ $tkdesk(in_browser)] \ -history dir_history \ -callback dsk_ask_dir_ok } proc dsk_ask_dir_ok {entry val} { global tkdesk set tmpdir $entry if {$tmpdir != ""} { if ![file exists $tmpdir] { dsk_bell cb_alert "The path you specified is not completely valid." set tmpdir [_make_path_valid $tmpdir] } if [file readable $tmpdir] { dsk_busy if $val { dsk_FileViewer .fv[dsk_FileViewer :: id] \ -dir $tmpdir -num_lbs $tkdesk(num_lbs) } else { dsk_FileList .dfl[dsk_FileList :: id] -dir $tmpdir } dsk_lazy } else { dsk_errbell cb_error "Permission denied." } } } # # ----------------------------------------------------------------------------- # # Proc: dsk_ask_dir # Args: none # Returns: "" # Desc: Asks the user which directory to open. # Side-FX: none # proc dsk_ask_dir {{browser ""}} { global tkdesk dsk_HistEntry .he[dsk_HistEntry :: id] \ -title "Open Directory" \ -label "Directory to open:" \ -checklabel "In Browser" \ -checkvalue [expr [string match "browser" $browser] || \ $tkdesk(in_browser)] \ -history dir_history \ -callback dsk_ask_dir_ok } proc dsk_ask_dir_ok {entry val} { global tkdesk set tmpdir $entry if {$tmpdir != ""} { if ![file exists $tmpdir] { dsk_bell cb_alert "The path you specified is not completely valid." set tmpdir [_make_path_valid $tmpdir] } if [file readable $tmpdir] { dsk_busy if $val { dsk_FileViewer .fv[dsk_FileViewer :: id] \ -dir $tmpdir -num_lbs $tkdesk(num_lbs) } else { dsk_FileList .dfl[dsk_FileList :: id] -dir $tmpdir } dsk_lazy } else { dsk_errbell cb_error "Permission denied." } } } # # ----------------------------------------------------------------------------- # # Proc: dsk_filesel # Args: none # Returns: "" # Desc: Asks the user which directory to open. # Side-FX: none # proc dsk_filesel {label filter args} { global tkdesk dsk_filesel set dsk_filesel(file) "" set dsk_filesel(filter) $filter set dsk_filesel(label) $label set dsk_filesel(args) $args set filter [cb_tilde $filter collapse] if [string match {*[Ss]ave*} $label] { set dsk_filesel(use_old_box) 1 set dsk_filesel(saving) 1 } else { set dsk_filesel(use_old_box) 1 set dsk_filesel(saving) 0 } dsk_HistEntry .he[dsk_HistEntry :: id] \ -title "Select A File" \ -entrydefault [string trimright [file dirname $filter] /]/ \ -entrypopupproc dsk_filesel_file_menu \ -label $label \ -history file_history \ -callback dsk_filesel_ok \ -addbutton "Browse..." \ -addcallback "dsk_filesel_browse" if {$dsk_filesel(file) != ""} { file_history add [cb_tilde $dsk_filesel(file) collapse] } return $dsk_filesel(file) } proc dsk_filesel_ok {entry val} { global tkdesk dsk_filesel return [set dsk_filesel(file) $entry] } proc dsk_filesel_file_menu {entry} { set path [$entry get] if {![file isdirectory $path]} { set path [_make_path_valid $path /] } dsk_debug "path: $path" if [file isdirectory $path] { set m [winfo toplevel $entry].m catch {destroy $m} menu $m -postcommand "dsk_casdirs [_make_fname_safe $path] $m 1 \"dsk_filesel_menu_cb $entry %d\" 1" update tk_popup $m [winfo pointerx $m] [winfo pointery $m] focus -force $m tkwait window $m update idletasks } } proc dsk_filesel_menu_cb {entry sel} { $entry delete 0 end $entry insert end $sel } proc dsk_filesel_browse {entry val} { global tkdesk dsk_filesel set showall 0 foreach arg $dsk_filesel(args) { switch $arg { "showall" { set showall 1 } } } set dir [_make_path_valid $entry] set file "" set isdir 0 set err 0 catch {set isdir [file isdirectory $entry]} if $isdir { if {$entry != "/"} { set entry [string trimright $entry /] } if !$dsk_filesel(use_old_box) { set tkdesk(_ed_browser) [dsk_open_dir $entry] } else { if {[info command tk_getSaveFile] == {}} { set file [cb_fileSelector \ -filter $entry/[file tail $dsk_filesel(filter)] \ -label $dsk_filesel(label) -showall $showall] } else { global tk_strictMotif if {$tkdesk(file_selector_type) != "default"} { set osm $tk_strictMotif if {$tkdesk(file_selector_type) == "tk"} { set tk_strictMotif 0 } else { set tk_strictMotif 1 } } if $dsk_filesel(saving) { set err [catch {set file [tk_getSaveFile \ -title $dsk_filesel(label) \ -initialdir $entry]} errmsg] } else { set err [catch {set file [tk_getOpenFile \ -title $dsk_filesel(label) \ -initialdir $entry]} errmsg] } if {$tkdesk(file_selector_type) != "default"} { set tk_strictMotif $osm } } } } else { if !$dsk_filesel(use_old_box) { set tkdesk(_ed_browser) [dsk_open_dir [file dirname $entry]] } else { if {[info command tk_getSaveFile] == {}} { set file [cb_fileSelector -filter $dir/$dsk_filesel(filter) \ -label $dsk_filesel(label) -showall $showall] } else { global tk_strictMotif #set osm $tk_strictMotif #set tk_strictMotif 0 if $dsk_filesel(saving) { set err [catch {set file [tk_getSaveFile \ -title $dsk_filesel(label) \ -initialdir $dir]} errmsg] } else { set err [catch {set file [tk_getOpenFile \ -title $dsk_filesel(label) \ -initialdir $dir]} errmsg] } #set tk_strictMotif $osm } } } if !$dsk_filesel(use_old_box) { return "" } else { if {$err} { dsk_errbell cb_error "Error: $errmsg" } return [set dsk_filesel(file) $file] } } # # ----------------------------------------------------------------------------- # # Proc: dsk_cd # Args: dir name of directory # Returns: "" # Desc: Display directory $dir in the active file viewer. # Side-FX: none # proc dsk_cd {{dir ""} {openNew 0}} { global tkdesk env if {$dir == ""} return if {$dir == "\{\}"} {dsk_errbell; return} set special "" if $tkdesk(menu,control) { set openNew 1 set tkdesk(menu,control) 0 } if {$dir == ".."} { set ad [dsk_active dir] if {$ad == "/"} { dsk_bell return } set dir [file dirname [string trimright $ad /]] } elseif ![file isdirectory $dir] { if [regexp {\.tar\.gz$|\.tgz$} $dir] { # it's a compressed tar file set special "tar" } elseif !$openNew { set dir [file dirname $dir] } else { dsk_open {} $dir return } } if {$dir != "" && \ [string length [itcl_info objects $tkdesk(active_viewer)]] > 0} { if {![file isdirectory $dir] && $special == ""} { set dir [_make_path_valid $dir] catch {dsk_bell} cb_alert "The path you specified does not exist." } if {[info command dsk_cd_callback] != ""} { dsk_cd_callback $dir } if !$openNew { $tkdesk(active_viewer) config -directory $dir } else { dsk_open_dir $dir } } } # # ----------------------------------------------------------------------------- # # Proc: dsk_open_dir # Args: dir directory to open # Returns: "" # Desc: Opens a window for directory $dir # Side-FX: none # proc dsk_open_dir {dir} { global tkdesk set f "" if [file readable $dir] { if $tkdesk(in_browser) { set f [dsk_FileViewer .fv[dsk_FileViewer :: id] \ -directory "$dir" -num_lbs $tkdesk(num_lbs)] } else { set f [dsk_FileList .dfl[dsk_FileList :: id] -directory "$dir"] } } else { dsk_errbell cb_error "Permission denied." } return $f } # # ----------------------------------------------------------------------------- # # Proc: dsk_open_browser # Args: dir directory to open # Returns: "" # Desc: Opens a browser window for directory $dir # Side-FX: none # proc dsk_open_browser {dir} { global tkdesk if [file readable $dir] { dsk_FileViewer .fv[dsk_FileViewer :: id] -dir $dir \ -num_lbs $tkdesk(num_lbs) } else { dsk_errbell cb_error "Permission denied." } } # # ----------------------------------------------------------------------------- # # Proc: _expand_pc # Args: cmd command line with %? # file (opt.) name of selected file (with path) # Returns: cmd with %? expanded # Desc: Expands all %? shortcuts in the given command line. # Does also some preprocessing of the command. # Side-FX: none # proc _expand_pc {cmd {file ""} {file1 ""} {file2 ""}} { global tkdesk dsk_debug -nonewline "Expanding \"$cmd\" to " if {$file == ""} { set files [_make_fnames_safe] set file [lindex $files 0] } else { set file [_make_fname_safe $file] set files $file } if {$file == ""} { if {[string first "%A" $cmd] > -1 || \ [string first "%a" $cmd] > -1} { dsk_bell cb_info "Please select one or more files first." return "" } elseif {[string first "%" $cmd] > -1} { if {[string first "%B" $cmd] == -1 && \ [string first "%D" $cmd] == -1 && \ [string first "%X" $cmd] == -1 && \ [string first "%x" $cmd] == -1} { cb_info "Please select a file first." return "" } } } set xsel "" if {[string first "%x" $cmd] > -1} { set err [catch {set xsel [selection get]}] if $err { cb_info "The X-selection is empty." return "" } } elseif {[string first "%X" $cmd] > -1} { catch {set xsel [selection get]} } if {[string first % $file] > -1} { # temporarily replace % in the filename: set file [string_replace $file "%" "_!percent!_"] set percent_in_file 1 } else { set percent_in_file 0 } if {[string first % $files] > -1} { # temporarily replace % in the filenames: set files [string_replace $files "%" "_!percent!_"] set percent_in_files 1 } else { set percent_in_files 0 } set dir [dsk_active dir] if {$dir != "/"} { set dir [string trimright $dir /] } set ocmd $cmd set pcmd "" foreach cmd [split $ocmd \n] { if {[string first "%s" $cmd] > -1} { set cmd [string_replace $cmd %s [list $file]] } if {[string first "%S" $cmd] > -1} { set cmd [string_replace $cmd %S $file1] } if {[string first "%T" $cmd] > -1} { set cmd [string_replace $cmd %T [list $file2]] } if {[string first "%d" $cmd] > -1} { set cmd [string_replace $cmd %d [list [file dirname $file]]] } if {[string first "%f" $cmd] > -1} { set cmd [string_replace $cmd %f [list [file tail $file]]] } if {[string first "%b" $cmd] > -1} { set ft [file tail $file] set cmd [string_replace $cmd %b [list [string range \ $ft 0 [expr [string last "." $ft] - 1 ]]]] } if {[string first "%c" $cmd] > -1} { set cmd [string_replace $cmd %c [list [file tail [list [string \ range $file 0 [expr [string last "." $file] - 1 ]]]]]] } if {[string first "%A" $cmd] > -1} { set cmd [string_replace $cmd %A $files] } if {[string first "%a" $cmd] > -1} { # currently, files of a specific browser may only be # selected in the same directory set result {} foreach fname $files { lappend result [file tail $fname] } set cmd [string_replace $cmd %a $result] } if {[string first "%B" $cmd] > -1} { if {$files != ""} { set cmd [string_replace $cmd %B $files] } else { set cmd [string_replace $cmd %B ""] } } if {[string first "%D" $cmd] > -1} { set cmd [string_replace $cmd %D [list $dir]] } if {[string first "%x" $cmd] > -1} { set cmd [string_replace $cmd %x [string trimright $xsel \;]] } if {[string first "%X" $cmd] > -1} { set cmd [string_replace $cmd %X [string trimright $xsel \;]] } if {$percent_in_file || $percent_in_files} { set cmd [string_replace $cmd "_!percent!_" "%"] } append pcmd "$cmd\n" } dsk_debug "\"$pcmd\"" return [subst -nocommands -novariables $pcmd] } # # ----------------------------------------------------------------------------- # # Proc: _make_fnames_safe # Args: none # Returns: Names of currently selected files with all Tcl-special chars # ([ etc.) backslashed. Calls _make_fname_safe. # Desc: ... # Side-FX: none # proc _make_fnames_safe {{flist ""}} { global tkdesk if {$flist == ""} { set flist [dsk_active sel] } if {$flist == ""} {return ""} foreach file $flist { #if {[string first "\\" $file] > -1} continue set nfile [_make_fname_safe $file] if {$nfile != ""} { lappend rlist $nfile } } #puts $rlist #if {[llength $rlist] == 1} { # return [lindex $rlist 0] #} else { # return $rlist #} return $rlist } # ----------------------------------------------------------------------------- # # Proc: _make_fname_safe # Args: file - filename # Returns: filename with all Tcl-special chars ([ etc.) backslashed # Desc: ... # Side-FX: none # proc _make_fname_safe {{file ""}} { if {$file == ""} {return ""} set nfile [dskC_esc $file { \"[]{}$;}] return $nfile } # # ----------------------------------------------------------------------------- # # Proc: dsk_edit # Args: args a list of filenames to edit # Returns: "" # Desc: Calls the in "System" specified editor on the given files. # The editor will run in the background. # Side-FX: none # proc dsk_edit {args} { global tkdesk set files "" set linenum "" set e "" foreach f $args { if [regexp {^\+[1-90]+$} $f] { set linenum $f } elseif ![file isdirectory $f] { if {$linenum != ""} { lappend files [list $linenum $f] set linenum "" } else { lappend files $f } } } if {$files == "{New File}"} { if {$tkdesk(editor,cmd) == "builtin"} { set files "" } else { set files "new_file" } } elseif {$files == ""} { #set files [cb_fileSelector \ # -filter [string trimright [dsk_active dir] /]/* \ # -label "File to edit:" -showall 1] while {1} { set files [dsk_filesel "File to edit:" \ [string trimright [dsk_active dir] /]/* showall] set isdir 0 catch {set isdir [file isdirectory $files]} if {$files == ""} { return } elseif $isdir { dsk_errbell cb_error "$files is a directory. Please choose a file." } else { break } } } if $tkdesk(editor,mfiles) { if {$tkdesk(editor,cmd) != "builtin"} { eval dsk_exec $tkdesk(editor,cmd) $files } else { if {$files != ""} { dsk_Editor [set e .de[dsk_Editor :: id]] -files $files if {[string length [$e cget -files]] == 0} {$e delete} #dsk_editor load $files } else { set e [dsk_editor new] } } } else { foreach file $files { if {$tkdesk(editor,cmd) != "builtin"} { dsk_exec $tkdesk(editor,cmd) $file } else { dsk_Editor [set e .de[dsk_Editor :: id]] -files $file if {[string length [$e cget -files]] == 0} {$e delete} #dsk_editor load $file } } } return $e } proc dsk_edit_wait {args} { set e [eval dsk_edit $args] if {$e != ""} { set ew "" catch {set ew [$e getToplevel]} if {[winfo exists $ew]} { tkwait window $ew } else { puts stderr "no such win: $ew" } } } # # ----------------------------------------------------------------------------- # # Proc: dsk_view # Args: args - shell command # Returns: "" # Desc: Displays the standard output of command $args in the builtin # editor. # Side-FX: none # proc dsk_view {args} { set p [lindex $args 0] if ![dsk_auto_execok $p] { dsk_errbell cb_alert "Can't execute: $p" return } eval dsk_editor cmd $args } # # ----------------------------------------------------------------------------- # # Proc: dsk_confirm # Args: msg - message to display in the dialog box # args - tcl script # Returns: "" # Desc: Evaluates $args after a positive confirmation. # Side-FX: none # proc dsk_confirm {msg script} { if ![cb_okcancel $msg] { eval $script } } proc dsk_msg_alert {msg} { cb_alert $msg } proc dsk_msg_info {msg} { cb_info $msg } proc dsk_msg_error {msg} { cb_error $msg } # # ----------------------------------------------------------------------------- # # Proc: dsk_read_string # Args: msg - message to display in the dialog box # args - tcl script # Returns: "" # Desc: Evaluates $args if the entered string is != "". # Side-FX: none # proc dsk_read_string {msg {script {}} args} { global dsk_read_string dsk_read_string_script set dontpaste 0 foreach a $args { switch $a { "dontpaste" { set dontpaste 1 } } } set tmpvar "" if !$dontpaste { catch {set tmpvar [selection get]} } #if {[string first " " $tmpvar] > -1} { # set tmpvar "" #} set tmpvar [string trimleft $tmpvar " "] set dsk_read_string_script $script set dsk_read_string {} if {[string length $script] == 0} { set noapply 1 } else { set noapply 0 } dsk_HistEntry .he[dsk_HistEntry :: id] \ -title "Enter String" \ -label $msg \ -entrydefault $tmpvar \ -history string_history \ -noapply $noapply \ -callback dsk_read_string_ok return $dsk_read_string } proc dsk_read_string_ok {entry val} { global dsk_read_string dsk_read_string_script tkdesk if {$entry != ""} { update string_history add $entry set dsk_read_string $entry if {$dsk_read_string_script != {}} { eval $dsk_read_string_script } } } proc dsk_read_string-old {msg script args} { global dsk_read_string tmpvar set dontpaste 0 foreach a $args { switch $a { "dontpaste" { set dontpaste 1 } } } set tmpvar "" if !$dontpaste { catch {set tmpvar [selection get]} } if {[string first " " $tmpvar] > -1} { set tmpvar "" } set dsk_read_string [cb_readString $msg tmpvar] update if {$dsk_read_string != ""} { eval $script } } # # ----------------------------------------------------------------------------- # # Proc: dsk_cbhelp # Args: file, (opt.) mode/regexp # Returns: "" # Desc: Invokes the cb_Help::show class procedure. # Side-FX: none # proc dsk_cbhelp {file {regexp ""}} { global tkdesk dsk_busy if {[string first "|" $file] > -1} { dsk_status "Launched: [string trimleft $file {| }]" } cb_help show $file $regexp if {[string first "|" $file] > -1} { dsk_status "Exit: [string trimleft $file {| }]" } dsk_lazy } # --------------------------------------------------------------------------- # dsk_print: # Asks for the command to use when printing files. # proc dsk_print {args} { global tkdesk dsk_print set files $args if {$files == ""} { set files [_make_fnames_safe] } if {$files == ""} { dsk_bell cb_info "Please select one or more files first." return } set dsk_print(files) $files set dsk_print(string) "" set cmd [printer_history last] if {$cmd == ""} { if [info exists tkdesk(cmd,print)] { set cmd $tkdesk(cmd,print) } else { set cmd "lpr" } } dsk_HistEntry .he[dsk_HistEntry :: id] \ -title "Print" \ -label "Print command (file names will be appended):" \ -entrydefault $cmd \ -history printer_history \ -callback dsk_print_ok } # --------------------------------------------------------------------------- # dsk_print_string: # Asks for the command to use when printing strings. # proc dsk_print_string {string} { global tkdesk dsk_print set dsk_print(string) $string set dsk_print(files) "" if [info exists tkdesk(cmd,print)] { set cmd $tkdesk(cmd,print) } else { set cmd "lpr" } dsk_HistEntry .he[dsk_HistEntry :: id] \ -title "Print" \ -label "Print command (pipe):" \ -entrydefault $cmd \ -history printer_history \ -callback dsk_print_ok } proc dsk_print_ok {entry val} { global tkdesk dsk_print set cmd $entry if {$cmd == ""} { dsk_bell return } printer_history add $cmd set tkdesk(cmd,print) $cmd dsk_busy if {$dsk_print(files) != ""} { set fs $dsk_print(files) set fs [string_replace $fs \{ \"] set fs [string_replace $fs \} \"] set fs [dskC_esc $fs \[\]] dsk_debug "dsk_print: $cmd $fs" set err [catch {eval exec $cmd $fs &} errmsg] } else { set err [catch {set fd [open "|$cmd" "w"]} errmsg] if !$err { catch { puts -nonewline $fd $dsk_print(string) close $fd } } } dsk_lazy if $err { dsk_errbell cb_error $errmsg } } # --------------------------------------------------------------------------- # dsk_netscape: # Makes use of Ken Hornstein's netscape-remote extension to communicate # with Netscape. If Netscape has not been started yet, TkDesk starts it. # Extension by Paul P.H. Wilson to handle various Netscape v4.03 modes # (06.10.97) # proc dsk_netscape {type {loc ""} {args ""}} { global tkdesk if {[lsearch "window" $args] > -1} {set win 1} {set win 0} if {[lsearch "raise" $args] > -1} {set raise "raise"} {set raise "noraise"} set url "" if {$type == "file"} { set url "file:$loc" } elseif {$type == "url"} { set url "$loc" } elseif {$type == "rcmd"} { if {$loc == "messenger" || $loc == "mail"} { set cmd "xfeDoCommand(openInbox)" } elseif {$loc == "discussions" || $loc == "news"} { set cmd "xfeDoCommand(openNewsgroups)" } elseif {$loc == "composer" || $loc == "edit"} { set cmd "xfeDoCommand(openEditor)" } elseif {$loc == "send"} { set cmd "xfeDoCommand(composeMessage)" set loc "mail" } else { error "dsk_netscape: unkown remote command $loc" } } else { error "dsk_netscape: unknown type $type" } if {$url != ""} { if $win { set cmd "openURL($url,new-window,$raise)" } else { set cmd "openURL($url,$raise)" } } #puts $cmd if [info exists tkdesk(netscapeWinId)] { set err [catch {send-netscape -id $tkdesk(netscapeWinId) $cmd}] if $err { unset tkdesk(netscapeWinId) set err [catch {send-netscape -idvar tkdesk(netscapeWinId) $cmd}] } } else { set err [catch {send-netscape -idvar tkdesk(netscapeWinId) $cmd}] } if $err { set start_netscape 0 if {$tkdesk(confirm_netscape)} { if {[cb_yesno "Netscape doesn't seem to be running yet on your display. Start it now?"] == 0} { set start_netscape 1 } } else { if {![file exists ~/.netscape/lock]} { set start_netscape 1 } else { puts stderr "tkdesk: Netscape already running" } } if {$start_netscape} { # start new netscape if {$type == "rcmd"} { eval dsk_exec $tkdesk(cmd,netscape) -$loc } else { if {$url != ""} { eval dsk_exec $tkdesk(cmd,netscape) $url } else { eval dsk_exec $tkdesk(cmd,netscape) } } } } } # ---------------------------------------------------------------------------- # dsk_textview file: # Displays $file in the built-in editor. The created window and buffer # is bound to the browser/list window that was active when this proc # was invoked. # proc dsk_textview {file} { set w [dsk_active window] if {$w != ""} { set dir [file dirname $file] if {$dir == "."} { set dir [dsk_active dir] if {$dir != "/"} { set dir [string trimright $dir /] } } $w textview $dir/[file tail $file] } else { cb_error "No active viewer!" } } # ---------------------------------------------------------------------------- # dsk_exec_here cmd dir: # set dsk_exec(here,view) 0 proc dsk_exec_here {cmd dir} { global dsk_exec tkdesk set dsk_exec(here,dir) $dir if $tkdesk(exec_as_root) { set lab "Execute as root (%A: all selected):" } else { set lab "Enter Command (%A: all selected):" } dsk_HistEntry .dsk_exec_here \ -title "Execute here" \ -label $lab \ -checklabel "View Output" \ -checkvalue $dsk_exec(here,view) \ -entrydefault $cmd \ -history xhere_history \ -callback "dsk_exec_here_do" \ -nograb 0 } proc dsk_exec_here_do {cmd val} { global tkdesk dsk_exec set dsk_exec(here,view) $val if {$cmd != ""} { xhere_history add $cmd if {![string match "dsk_*" [lindex $cmd 0]]} { if $val { set cmd "dsk_view $cmd" } else { set cmd "dsk_exec $cmd" } } update dsk_busy set dsk_exec(dir) $dsk_exec(here,dir) set dsk_exec(shell) 1 set cmd [_expand_pc $cmd] set err [catch {eval $cmd} errmsg] dsk_lazy if $err { cb_error $errmsg } } else { dsk_bell return } } # ---------------------------------------------------------------------------- # dsk_mail file ?string?: # Send $file via email. If $file is the empty string, $string is piped # to the mail command $tkdesk(cmd,mail). # proc dsk_mail {file {string ""}} { global tkdesk dsk_mail if {$file != ""} { set dsk_mail(file) $file set dsk_mail(string) "" dsk_HistEntry .dsk_mail \ -title "Send File" \ -label "Send file to:" \ -history mail_history \ -callback dsk_mail_ok } else { set dsk_mail(file) "" set dsk_mail(string) $string dsk_HistEntry .dsk_mail \ -title "Send Buffer" \ -label "Send buffer to:" \ -history mail_history \ -callback dsk_mail_ok } } proc dsk_mail_ok {entry val} { global tkdesk dsk_mail set address $entry if {$address == ""} { dsk_bell return } mail_history add $address set cmd $tkdesk(cmd,mail) if {$dsk_mail(file) != ""} { set cmd [string_replace $cmd %s [file tail $dsk_mail(file)]] set cmd [string_replace $cmd %a $address] dsk_busy set err [catch {eval exec cat $dsk_mail(file) | $cmd} errmsg] dsk_lazy if $err { dsk_errbell cb_error $errmsg } } else { set cmd [string_replace $cmd %s TkDesk-Buffer] set cmd [string_replace $cmd %a $address] dsk_busy set err [catch {eval exec echo [list $dsk_mail(string)] | $cmd} errmsg] dsk_lazy if $err { dsk_errbell cb_error $errmsg } } } # --------------------------------------------------------------------------- # dsk_man names: # Lookup manual pages for each of $names. # Example for $names: "col socket(2) malloc(3)" proc dsk_man {{names ""}} { if {$names == ""} { set names [dsk_read_string "Show manual page for: (e.g. col(1))"] } foreach name $names { set l [split $name ()] if {[llength $l] == 1} { dsk_view man $l | col -b #dsk_cbhelp "| man $l | col -b" man } else { dsk_view man [lindex $l 1] [lindex $l 0] | col -b #dsk_cbhelp "| man [lindex $l 1] [lindex $l 0] | col -b" man } } } tkdesk-2.0/tcldesk/annotations.tcl0100644000175000007640000001266110020457430015420 0ustar jccjcc# ============================================================================= # # File: annotation.tcl # Project: TkDesk # # Started: 12.12.94 # Changed: 29.03.96 # Author: cb # # Description: Implements procs for searching files and annotations. # Also does the bookmarks stuff. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_find_annotation {} #s proc dsk_anno_find {} #s proc dsk_anno_browse {} # # ============================================================================= # # ----------------------------------------------------------------------------- # # Proc: dsk_find_annotation # Args: none # Returns: "" # Desc: Creates a window for searching an annotation. # Side-FX: none # if ![info exists tkdesk(geometry,dsk_find_annotation)] { set tkdesk(geometry,dsk_find_annotation) "" } set dsk_anno(case_sensitive) 0 proc dsk_find_annotation {} { global tkdesk tkdesk_anno set t .dsk_find_annotation if [winfo exists $t] { cb_raise $t return } toplevel $t wm withdraw $t frame $t.fe -bd 1 -relief raised pack $t.fe -fill x frame $t.f1 pack $t.f1 -in $t.fe -fill x \ -padx $tkdesk(pad) -pady $tkdesk(pad) frame $t.fh1 pack $t.fh1 -in $t.f1 -fill x label $t.le -text "Annotation to seach for (regexp):" -anchor w pack $t.le -in $t.fh1 -side left checkbutton $t.cbCase -text "Case sensitive" -relief flat \ -variable dsk_anno(case_sensitive) pack $t.cbCase -in $t.fh1 -side right entry $t.eAnno -bd 2 -relief sunken pack $t.eAnno -in $t.f1 -fill x -ipady 2 \ -padx $tkdesk(pad) -pady $tkdesk(pad) bind $t.eAnno "$t.bSearch.button invoke" frame $t.fb pack $t.fb -in $t.f1 -fill x cb_button $t.bSearch -text " Search " -default 1 -command dsk_anno_find pack $t.bSearch -in $t.fb -side left -padx $tkdesk(pad) -pady $tkdesk(pad) button $t.bBrowse -text " Browse " -command "dsk_anno_browse" button $t.bClose -text " Close " -command \ "set tkdesk(geometry,dsk_find_annotation) \[wm geometry $t\] ;\ destroy $t" pack $t.bBrowse $t.bClose -in $t.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 frame $t.flb -bd 1 -relief raised pack $t.flb -fill both -expand yes frame $t.f2 pack $t.f2 -in $t.flb -fill both -expand yes -pady $tkdesk(pad) label $t.llb -text "Matching files:" -anchor w pack $t.llb -in $t.f2 -anchor w -padx $tkdesk(pad) pack [_dsk_find_lb $t.dlb] -in $t.f2 -fill both -expand yes bind $t "focus $t.eAnno" wm title $t "Find Annotation" wm minsize $t 10 1 dsk_place_window $t dsk_find_annotation 24x5 1 wm protocol $t WM_DELETE_WINDOW "$t.bClose invoke" wm deiconify $t } # # ----------------------------------------------------------------------------- # # Proc: dsk_anno_find # Args: none # Returns: "" # Desc: Searches in all annotated files for the annotation in the # entry widget. # Side-FX: none # proc dsk_anno_find {} { global tkdesk_anno dsk_anno if ![info exists tkdesk_anno] { dsk_bell cb_info "No file has been annotated yet." return } dsk_busy set t .dsk_find_annotation set re [$t.eAnno get] if {$re != ""} { set mfiles "" foreach afile [array names tkdesk_anno] { if ![file exists $afile] { unset tkdesk_anno($afile) continue } if !$dsk_anno(case_sensitive) { if [regexp -nocase $re $tkdesk_anno($afile)] { lappend mfiles $afile } } else { if [regexp $re $tkdesk_anno($afile)] { lappend mfiles $afile } } } #$t.lbFiles.lbox delete 0 end #eval $t.lbFiles.lbox insert end $mfiles if {$mfiles != ""} { set lbl "" foreach file $mfiles { lappend lbl [file dirname $file]/[subst \ [lindex [dskC_ls -l -o $file] 0]] } $t.dlb config -list $lbl } else { dsk_bell cb_info "No match." } } dsk_lazy } # # ----------------------------------------------------------------------------- # # Proc: dsk_anno_browse # Args: none # Returns: "" # Desc: Fills a text window with all file annotations. # Side-FX: none # proc dsk_anno_browse {} { global tkdesk_anno if ![info exists tkdesk_anno] { dsk_bell cb_info "No file has been annotated yet." return } set afiles "" foreach afile [array names tkdesk_anno] { if ![file exists $afile] { unset tkdesk_anno($afile) continue } append afiles "$afile:\n" append afiles "$tkdesk_anno($afile)\n\n" } if {$afiles != ""} { dsk_editor string $afiles } else { dsk_bell cb_info "No file has been annotated yet." } } tkdesk-2.0/tcldesk/appbar-date.tcl0100644000175000007640000001417610020457430015246 0ustar jccjcc# ============================================================================= # # File: appbar-date.tcl # Project: TkDesk # # Started: 13.11.94 # Changed: 13.11.94 # Author: cb # # Description: Implements the "date" application bar special. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc _appbar_date {frame side} #s proc _appbar_bind_date {count} #s proc _appbar_get_date {} # # ============================================================================= # # ----------------------------------------------------------------------------- # # Proc: _appbar_date # Args: none # Returns: "" # Desc: Displays the time and date in the application bar. # Side-FX: none # proc _appbar_date {frame side} { global tkdesk dsk_appbar if {$frame != ""} { set dsk_appbar(date,frame) $frame } else { set frame $dsk_appbar(date,frame) } set ft .dsk_appbar.fDate if ![winfo exists $ft] { frame $ft -class "AppDate" -cursor top_left_arrow pack $ft -fill both -expand yes -side $side -in $frame } set f .dsk_appbar.fDate.f catch {destroy $f} frame $f -bd 2 -relief raised -cursor top_left_arrow pack $f -fill both -expand yes -in $ft label $f.lTime -font [cb_font $tkdesk(appbar,font,time)] \ -pady 0 -cursor top_left_arrow \ -bg $tkdesk(appbar,time,background) \ -fg $tkdesk(appbar,time,foreground) \ -bd $tkdesk(appbar,time,borderwidth) \ -relief $tkdesk(appbar,time,relief) label $f.lWeekday -font [cb_font $tkdesk(appbar,font,weekday)] \ -pady 0 -cursor top_left_arrow label $f.lDay -font [cb_font $tkdesk(appbar,font,day)] \ -pady 0 -cursor top_left_arrow label $f.lMonth -font [cb_font $tkdesk(appbar,font,month)] \ -pady 0 -cursor top_left_arrow set cw [_appbar_get_button_width] set cw4 [expr $cw + ($tkdesk(appbar,ipad) * 2)] set cw42 [expr $cw4 / 2] if {$dsk_appbar(layout) == "vertical"} { canvas $f.cDate -bd 0 -relief flat -width $cw4 -height 76 \ -cursor top_left_arrow -highlightthickness 0 $f.cDate create window $cw42 2 -window $f.lTime -anchor n $f.cDate create window $cw42 20 -window $f.lWeekday -anchor n $f.cDate create window $cw42 34 -window $f.lDay -anchor n $f.cDate create window $cw42 58 -window $f.lMonth -anchor n if {$cw >= 48} { # nasty hack... $f.lTime config -width 6 } } else { canvas $f.cDate -bd 0 -relief flat -width 76 -height $cw4 \ -cursor top_left_arrow -highlightthickness 0 $f.cDate create window 18 2 -window $f.lTime -anchor n $f.cDate create window 18 [expr $cw42 - 1] -window $f.lMonth -anchor n $f.cDate create window 58 4 -window $f.lDay -anchor n -height 20 $f.cDate create window 58 [expr $cw42 + 6] \ -window $f.lWeekday -anchor n -height 10 } raise $f.lTime raise $f.lWeekday raise $f.lDay raise $f.lMonth pack $f.cDate -in $f -fill none -expand yes _appbar_get_date return $f } proc _appbar_bind_date {count} { global dsk_appbar set f .dsk_appbar.fDate.f _appbar_bind_special \ [list $f.lTime $f.lWeekday $f.lDay $f.lMonth $f.cDate] \ $count $f } set _ab_date(wday,0) "Sun"; set _ab_date(wday,1) "Mon"; set _ab_date(wday,2) "Tue"; set _ab_date(wday,3) "Wed"; set _ab_date(wday,4) "Thu"; set _ab_date(wday,5) "Fri"; set _ab_date(wday,6) "Sat"; set _ab_date(mon,0) "Jan"; set _ab_date(mon,1) "Feb"; set _ab_date(mon,2) "Mar"; set _ab_date(mon,3) "Apr"; set _ab_date(mon,4) "May"; set _ab_date(mon,5) "Jun"; set _ab_date(mon,6) "Jul"; set _ab_date(mon,7) "Aug"; set _ab_date(mon,8) "Sep"; set _ab_date(mon,9) "Oct"; set _ab_date(mon,10) "Nov"; set _ab_date(mon,11) "Dec"; proc _appbar_get_date {} { global dsk_appbar tkdesk _ab_date set f .dsk_appbar.fDate.f if ![winfo exists $f.lTime] { return } if {[info command clock] == {}} { # Tcl < 7.5 doesn't have clock array set time [dskC_localtime] if !$tkdesk(appbar,12hour) { $f.lTime config -text "$time(hour):$time(min)" } else { # fix contributed by jcc@snm.com (J. Chris Coppick) if {$time(hour) > 12} { scan $time(hour) {%d} hour $f.lTime config -text \ [format "%02d:%02s" [expr $hour % 12] $time(min)] } elseif {$time(hour) == 0} { $f.lTime config -text "12:$time(min)" } else { $f.lTime config -text "$time(hour):$time(min)" } } if {$time(wday) == 0} { # it's a Sunday set col red } else { set col [cb_col $tkdesk(color,foreground)] } $f.lWeekday config -text $_ab_date(wday,$time(wday)) -fg $col $f.lDay config -text $time(mday) $f.lMonth config -text $_ab_date(mon,$time(mon)) } else { if !$tkdesk(appbar,12hour) { ot_maplist [clock format [clock seconds] \ -format "%M %H %d %b %a"] \ time(min) time(hour) time(mday) mon wday } else { ot_maplist [clock format [clock seconds] \ -format "%M %I %d %b %a"] \ time(min) time(hour) time(mday) mon wday } $f.lTime config -text "$time(hour):$time(min)" if {$wday == "Sun"} { # it's a Sunday set col red } else { set col [cb_col $tkdesk(color,foreground)] } $f.lWeekday config -text $wday -fg $col $f.lDay config -text [string trimleft $time(mday) "0"] $f.lMonth config -text $mon } if [info exists dsk_appbar(date,afterid)] { catch {after cancel $dsk_appbar(date,afterid)} } set dsk_appbar(date,afterid) [after 60000 _appbar_get_date] } tkdesk-2.0/tcldesk/appbar-dialup.tcl0100644000175000007640000001470010020457430015600 0ustar jccjcc# ============================================================================= # # File: appbar-dialup.tcl # Project: TkDesk # # Started: 13.11.94 # Changed: 13.11.94 # Author: cb # # Description: Implements the "dial-up" application bar special. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: # # ----------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # _appbar_dialup but: # Creates a button that displays the current status of the # dial-up link. # set dsk_appbar(dialup,button) "" set dsk_appbar(dialup,currnr) "" proc _appbar_dialup {but count} { global tkdesk dsk_appbar # set images to use: foreach img {up down} { set bitmap $tkdesk(appbar,dialup,$img) set dsk_appbar(dialup,img,$img) [dsk_image $bitmap] } # create button: set dsk_appbar(dialup,button) $but button $but -activebackground $dsk_appbar(bgcolor) \ -activeforeground $dsk_appbar(fgcolor) \ -cursor top_left_arrow \ -command $dsk_appbar(defaction) \ -padx 0 -pady 0 -highlightthickness 0 \ -image $dsk_appbar(dialup,img,down) bind $but dsk_dialup_do_update set dsk_appbar(dialup,count) $count set dsk_appbar(dialup,state) "down" set dsk_appbar(dialup,offhook) 0 catch {after cancel $dsk_appbar(dialup,after)} dsk_dialup_update } proc dsk_dialup {{phonenr {}}} { global tkdesk dsk_appbar if {$dsk_appbar(dialup,state) == "down" \ && !$dsk_appbar(dialup,offhook)} { if {$phonenr == {}} { dsk_HistEntry .he[dsk_HistEntry :: id] \ -title "Connect to ISP" \ -label "Phone number to dial:" \ -history dialup_history \ -callback dsk_dialup_ok } else { dsk_dialup_ok $phonenr 0 } } else { # bring the link down dsk_busy set cmd $tkdesk(appbar,dialup,cmd_down) dsk_debug "cmd_down: $cmd" catch {eval exec $cmd >@stdout 2>@stderr @stdout 2>@stderr "$but config -relief sunken" bind $lab "$but invoke; $but config -relief raised" bind $lab <3> "_appbar_show_menu $dsk_appbar(dialup,count) %X %Y" bind $lab "break" bindtags $lab "appbar $lab all" update idletasks set x 0 set y [expr [winfo reqheight $but] - [winfo reqheight $lab]] place $lab -x $x -y $y set dsk_appbar(dialup,secsup) 0 catch {after cancel $dsk_appbar(dialup,after,secs)} after 1000 dsk_dialup_update_secs } elseif {$currstate == "down" && $prevstate == "up"} { catch {destroy $dsk_appbar(dialup,label)} } } proc dsk_dialup_update_secs {} { global dsk_appbar if ![winfo exists $dsk_appbar(dialup,label)] return if {$dsk_appbar(dialup,state) == "down"} { catch {destroy $dsk_appbar(dialup,label)} return } incr dsk_appbar(dialup,secsup) set s [expr $dsk_appbar(dialup,secsup) % 60] set m [expr $dsk_appbar(dialup,secsup) / 60] $dsk_appbar(dialup,label) config -text [format "%02d:%02d" $m $s] set dsk_appbar(dialup,after,secs) [after 1000 dsk_dialup_update_secs] } proc dsk_dialup_state {} { global tkdesk dsk_appbar if {[string match $tkdesk(systype) "Linux"] \ || [string match $tkdesk(systype) "linux"]} { set fd [open /proc/net/route] set r [read $fd] close $fd if {[regexp "\nppp0" $r] || [regexp "\nsl0" $r]} { set state "up" } else { set state "down" } } else { set r {} catch {set r [exec ifconfig | egrep "^sl|^ppp"]} if {$r == {}} { set state "down" } else { set state "up" } } set dsk_appbar(dialup,state) $state return $state } proc dsk_dialup_is_off_hook {} { global tkdesk dsk_appbar if {$dsk_appbar(dialup,state) == "up"} { return 1 } else { if {[string match $tkdesk(systype) "Linux"] \ || [string match $tkdesk(systype) "linux"]} { if [file exists /var/lock/LCK..modem] { return 1 } elseif [file exists /var/lock/LCK..ttyS1] { return 1 } else { return 0 } } else { return 0 } } } tkdesk-2.0/tcldesk/appbar-load.tcl0100644000175000007640000001241110020457430015236 0ustar jccjcc# ============================================================================= # # File: appbar-load.tcl # Project: TkDesk # # Started: 13.11.94 # Changed: 13.11.94 # Author: cb # # Description: Implements the "load" application bar special. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc _appbar_load {frame side} #s proc _appbar_bind_load {count} #s proc _appbar_get_load {} # # ----------------------------------------------------------------------------- # # ----------------------------------------------------------------------------- # # Proc: _appbar_load # Args: none # Returns: "" # Desc: Displays the system load in the appbar. # Side-FX: none # proc _appbar_load {frame side} { global tkdesk dsk_appbar if {$frame != ""} { set dsk_appbar(load,frame) $frame } else { set frame $dsk_appbar(load,frame) } set ft .dsk_appbar.fLoad if ![winfo exists $ft] { frame $ft -class "AppLoad" -cursor top_left_arrow pack $ft -fill both -expand yes -side $side -in $frame } set f .dsk_appbar.fLoad.f catch {destroy $f} frame $f -bd 2 -relief raised -cursor top_left_arrow pack $f -fill both -expand yes -in $ft set dsk_appbar(load,size) [_appbar_get_button_width] set cw $dsk_appbar(load,size) set ipad2 [expr $tkdesk(appbar,ipad) * 2] canvas $f.cLoad -bd 0 -relief flat \ -width [expr $cw + $ipad2] -height [expr $cw + $ipad2] \ -cursor top_left_arrow -highlightthickness 0 set adj [expr $tkdesk(appbar,ipad) / 2] $f.cLoad create rectangle $adj $adj [expr $cw + $adj] [expr $cw + $adj] \ -fill white -outline black set dsk_appbar(load,scale) 1.0 set dsk_appbar(load,lastx) 0 set dsk_appbar(load,shrinkcnt) 0 set dsk_appbar(load,uptime_ok) 0 pack $f.cLoad -in $f _appbar_get_load return $f } proc _appbar_bind_load {count} { global dsk_appbar set f .dsk_appbar.fLoad.f _appbar_bind_special [list $f.cLoad] $count $f } proc _appbar_get_load {} { global dsk_appbar tkdesk set f .dsk_appbar.fLoad.f if ![winfo exists $f.cLoad] { return } if [file readable /proc/loadavg] { set uptime 0 dsk_catch { set fd [open /proc/loadavg] set uptime [gets $fd] close $fd } set load1 [lindex $uptime 0] } else { set err [catch {set uptime [exec uptime]} errmsg] if $err { dsk_errbell cb_alert "Executing uptime gave an error. Disabling load display." return } set li [llength $uptime] set load1 [string trimright [lindex $uptime [incr li -3]] ,\;] regsub "," $load1 "." load1 if !$dsk_appbar(load,uptime_ok) { if ![regexp {[a-zA-Z:]} $load1] { set dsk_appbar(load,uptime_ok) 1 } else { cb_alert "Unknown uptime output. Please mail the output of the uptime command and your system type to Christian.Bolik@mainz.netsurf.de. Disabling load display." return } } } set cw $dsk_appbar(load,size) set cw1 [expr $dsk_appbar(load,size) + 1] set oldscale $dsk_appbar(load,scale) if {$load1 >= $dsk_appbar(load,scale)} { set dsk_appbar(load,shrinkcnt) 0 set dsk_appbar(load,scale) [expr ceil($load1)] } else { if {$load1 > $dsk_appbar(load,scale) - 1.0} { set dsk_appbar(load,shrinkcnt) 0 } else { incr dsk_appbar(load,shrinkcnt) if {$dsk_appbar(load,shrinkcnt) >= $cw && $oldscale > 1.0} { set dsk_appbar(load,shrinkcnt) 0 set dsk_appbar(load,scale) [expr $oldscale - 1.0] } } } if {$oldscale != $dsk_appbar(load,scale)} { set scale $dsk_appbar(load,scale) catch {$f.cLoad delete scale} $f.cLoad scale load 1 $cw1 1 [expr $oldscale/$scale] if {$scale > 1.0} { for {set y [expr $cw1 - round($cw/$scale)]} {$y > 1} { \ set y [expr $y - round($cw/$scale)]} { $f.cLoad create line 2 $y $cw1 $y -fill red -tags scale } } } set y [expr $cw1 - round($load1 / $dsk_appbar(load,scale) * $cw.)] if {$dsk_appbar(load,lastx) < $cw} { set x [incr dsk_appbar(load,lastx)] } else { set x $cw $f.cLoad move load -1 0 #set d [$f.cLoad find closest 1 $cw] #if {[$f.cLoad gettags $d] == "load"} { # $f.cLoad delete $d #} eval $f.cLoad delete [$f.cLoad find enclosed -100 0 1 [expr $cw +2]] } $f.cLoad create line $x $cw1 $x $y -tags load catch {$f.cLoad raise scale load} if [info exists dsk_appbar(load,afterid)] { catch {after cancel $dsk_appbar(load,afterid)} } set dsk_appbar(load,afterid) \ [after [expr $tkdesk(appbar,load,delay) * 1000] _appbar_get_load] } tkdesk-2.0/tcldesk/appbar-mail.tcl0100644000175000007640000002010210020457430015235 0ustar jccjcc# ============================================================================= # # File: appbar-mail.tcl # Project: TkDesk # # Started: 13.11.94 # Changed: 13.11.94 # Author: cb # # Description: Implements the "mail" application bar special. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc _appbar_mail {but} #s proc _appbar_check_mail {} # # ----------------------------------------------------------------------------- # # ----------------------------------------------------------------------------- # # Proc: _appbar_mail # Args: none # Returns: "" # Desc: The appbar's xbiff replacement. # Side-FX: none # proc _appbar_mail {but} { global tkdesk dsk_appbar env # sort out location of mail folder: if ![info exists tkdesk(appbar,mail,folders)] { if [info exists env(MAIL)] { set dsk_appbar(mail,folder) $env(MAIL) } else { foreach dir {/usr/mail /var/mail /usr/spool/mail /var/spool/mail} { if [file isdirectory $dir] { set dsk_appbar(mail,folder) $dir catch {set dsk_appbar(mail,folder) \ $dir/[exec $tkdesk(cmd,whoami)]} break } } } } else { set dsk_appbar(mail,folder) $tkdesk(appbar,mail,folders) } if ![info exists dsk_appbar(mail,folder)] { cb_alert "Couldn't locate your incoming mail folder." set dsk_appbar(mail,folder) "" } set dsk_appbar(mail,laststat) "" # set images to use: foreach img {nomail oldmail newmail} { set bitmap $tkdesk(appbar,mail,$img) set dsk_appbar(mail,img,$img) [dsk_image $bitmap] } # create button: set dsk_appbar(mail,button) $but button $but -image $dsk_appbar(mail,img,nomail) \ -activebackground $dsk_appbar(bgcolor) \ -activeforeground $dsk_appbar(fgcolor) \ -cursor top_left_arrow \ -command $dsk_appbar(defaction) \ -padx 0 -pady 0 -highlightthickness 0 _appbar_check_mail } # Mods: # # - Folder list entries that begin with '|' (horizontal bar) will # by treated as commands. Non-null command output implies unseen # mail. # J. Chris Coppick, 1998 # proc _appbar_check_mail {} { global dsk_appbar tkdesk set b $dsk_appbar(mail,button) if ![winfo exists $b] return set ns nomail set nf "" set unseen "" foreach f $dsk_appbar(mail,folder) { if [regsub {^\|} $f "" f] { catch "set unseen \[exec $f]" if {$unseen == ""} { set ns nomail catch {destroy .dsk_mail_headers} } else { set ns newmail } } else { catch { set f [cb_tilde $f expand] if [file exists $f] { file stat $f stat if {$stat(size) > 0} { if {$stat(mtime) > $stat(atime)} { set ns newmail set nf $f } else { set ns oldmail } } else { catch {destroy .dsk_mail_headers} } } } } if {$ns == "newmail"} break } if {$ns != $dsk_appbar(mail,laststat)} { if {$ns != "nomail"} { dsk_mail_headers } set dsk_appbar(mail,laststat) $ns $b config -image $dsk_appbar(mail,img,$ns) if {$ns == "newmail"} { if {$tkdesk(appbar,mail,newbg) != ""} { $b config -bg $tkdesk(appbar,mail,newbg) } dsk_sound dsk_new_mail beep dsk_show_mail_headers if {$tkdesk(appbar,mail,notifier) == "1"} { cb_alert "New mail arrived in\n[cb_tilde $nf collapse]!" } elseif {$tkdesk(appbar,mail,notifier) == "bell"} { bell } } else { $b config -bg [cb_col $tkdesk(color,background)] } } if [info exists dsk_appbar(mail,afterid)] { catch {after cancel $dsk_appbar(mail,afterid)} } set dsk_appbar(mail,afterid) \ [after [expr $tkdesk(appbar,mail,delay) * 1000] _appbar_check_mail] } # mail.tcl (patched) # # Author: J. Chris Coppick, 1997 # Description: Implements a mail-header flasher for TkDesk # # Mods: # # - now treats tkdesk(appbar,mail,headers,cmd) as a list of commands # - removes empty lines from the display # - handles dynamic updates better # - only outputs "retrieval failure" message if in debug mode # J. Chris Coppick, 1998 # # background color for headers window set tkdesk(appbar,mail,headers,bg) black # background color for headers window set tkdesk(appbar,mail,headers,fg) yellow # font for headers window set tkdesk(appbar,mail,headers,font) 9x15bold # command for summarizing mail contents # (there's no default for this as leaving it unset disables the # headers flasher) #set tkdesk(appbar,mail,headers,cmd) "/usr/bin/mailx -H" ### Number of seconds to auto-display mail headers when new mail arrives. ### Set to zero to disable auto-display of mail headers. set tkdesk(appbar,mail,headers,autotime) 5 proc dsk_mail_headers {} { global tkdesk dsk_appbar if {![info exists tkdesk(appbar,mail,headers,cmd)] \ && ![info exists tkdesk(appbar,mail,headers,cmds)]} return if {$tkdesk(appbar,mail,headers,cmd) == ""} return set t .dsk_mail_headers if ![winfo exists $t] { toplevel $t wm withdraw $t frame $t.f -bd 1 -relief raised pack $t.f -fill x text $t.lb -width 60 -height 10 \ -font [cb_font $tkdesk(appbar,mail,headers,font)] \ -bg [cb_col $tkdesk(appbar,mail,headers,bg)] \ -fg [cb_col $tkdesk(appbar,mail,headers,fg)] pack $t.lb -in $t.f -fill both -expand yes } $t.lb configure -state normal $t.lb delete 1.0 end set ml 0 set tl 0 if {[info exists tkdesk(appbar,mail,headers,cmd)] \ && ![info exists tkdesk(appbar,mail,headers,cmds)]} { lappend tkdesk(appbar,mail,headers,cmds) \ $tkdesk(appbar,mail,headers,cmd) } foreach cmd $tkdesk(appbar,mail,headers,cmds) { set cmd [string_replace $cmd \[ \\\[] set cmd [string_replace $cmd \] \\\]] append cmd " 2>/dev/null" if $tkdesk(debug) { catch {puts stderr "$cmd"} } if [catch {set headers [eval exec $cmd]} errmsg] { if $tkdesk(debug) { catch { puts stderr "Mail headers retrieval failed:" puts stderr "$errmsg\n" } } } else { set hl [split $headers \n] if [set ll [llength $hl]] { foreach h $hl { if {$h == ""} continue incr tl set h [string trim $h { }] set hlen [string length $h] if {$hlen > $ml} { set ml $hlen } $t.lb insert end "$h\n" } } } } $t.lb configure -height $tl -width $ml -state disabled cb_centerToplevel $t if [winfo exists $dsk_appbar(mail,button)] { wm transient $t $dsk_appbar(mail,button) wm overrideredirect $t 1 bind $dsk_appbar(mail,button) \ "catch {wm deiconify .dsk_mail_headers; raise .dsk_mail_headers}" bind $dsk_appbar(mail,button) \ "catch {wm withdraw .dsk_mail_headers}" } update idletasks } proc dsk_show_mail_headers {} { global tkdesk if ![info exists tkdesk(appbar,mail,headers,autotime)] return if {$tkdesk(appbar,mail,headers,autotime) == 0 || \ $tkdesk(appbar,mail,headers,autotime) == ""} { return } if [winfo exists .dsk_mail_headers] { wm deiconify .dsk_mail_headers raise .dsk_mail_headers update idletasks after [expr 1000 * $tkdesk(appbar,mail,headers,autotime)] { catch {wm withdraw .dsk_mail_headers} } } } tkdesk-2.0/tcldesk/appbar-trash.tcl0100644000175000007640000000754310037400117015450 0ustar jccjcc# ============================================================================= # # File: appbar-trash.tcl # Project: TkDesk # # Started: 13.11.94 # Changed: 13.11.94 # Author: cb # # Description: Implements the "trash" application bar special. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc _appbar_trash {but} #s proc _appbar_trash_refresh {{state unknown}} # # ----------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # _appbar_trash but: # Creates a trash button that's displays the current fill state of the # trash can. # set dsk_appbar(trash,button) "" proc _appbar_trash {but} { global tkdesk dsk_appbar set no_refresh 0 # is there a trash dir? if ![file exists $tkdesk(trashdir)] { if [cb_yesno "Create trash directory $tkdesk(trashdir)?"] { set no_refresh 1 } else { if [dsk_catch {file mkdir $tkdesk(trashdir)}] { return } } } # set images to use: foreach img {empty full} { set bitmap $tkdesk(appbar,trash,$img) set dsk_appbar(trash,img,$img) [dsk_image $bitmap] } # create button: set dsk_appbar(trash,button) $but button $but -activebackground $dsk_appbar(bgcolor) \ -activeforeground $dsk_appbar(fgcolor) \ -cursor top_left_arrow \ -command $dsk_appbar(defaction) \ -padx 0 -pady 0 -highlightthickness 0 # create label: if $tkdesk(appbar,trash,label) { set dsk_appbar(trash,label) [winfo parent $but].lDu catch {destroy $dsk_appbar(trash,label)} label $dsk_appbar(trash,label) \ -font [cb_font $tkdesk(appbar,trash,font)] \ -cursor top_left_arrow } else { catch {unset dsk_appbar(trash,label)} } if $no_refresh { $but config -image $dsk_appbar(trash,img,empty) return } # initialize display: _appbar_trash_refresh } proc _appbar_trash_refresh {{state unknown}} { global tkdesk dsk_appbar if ![winfo exists $dsk_appbar(trash,button)] return set but $dsk_appbar(trash,button) if ![file isdirectory $tkdesk(trashdir)] { cb_error "File $tkdesk(trashdir) exists, but isn't a directory!" return } switch $state { "empty" { $but config -image $dsk_appbar(trash,img,empty) } "full" { $but config -image $dsk_appbar(trash,img,full) } unknown { set tlen [llength [dskC_ls -a $tkdesk(trashdir)]] if {$tlen > 2} { $but config -image $dsk_appbar(trash,img,full) } else { $but config -image $dsk_appbar(trash,img,empty) } } } if [info exists dsk_appbar(trash,label)] { set du -1 catch {set du [lindex [eval exec \ $tkdesk(cmd,du) -s $tkdesk(trashdir)] 0]} if {$du == -1} { set dus "?" } elseif {$du == 0 || $tlen <= 2} { set dus "Empty" } elseif {$du < 1000} { set dus "${du}kB" } elseif {$du < 1000000} { set dus "[expr round($du./1000)]MB" } else { set dus "[expr round($du./1000000)]GB" } $dsk_appbar(trash,label) config -text $dus } } tkdesk-2.0/tcldesk/appbar.tcl0100644000175000007640000007252310023731205014331 0ustar jccjcc# ============================================================================= # # File: appbar.tcl # Project: TkDesk # # Started: 13.11.94 # Changed: 13.11.94 # Author: cb # # Description: Implements an application bar. This features popup menus # and drag and drop targets. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_appbar {args} #s proc _appbar_create {} #s proc _appbar_mcheck {appmenu nr menu} #s proc _appbar_handle_special_entry {m me} #s proc _appbar_add_to_menu {menu cmd} #s proc _appbar_unpost_parents {menu} #s proc _appbar_dd_action {cmd} #s proc _appbar_show_menu {butnum rootx rooty {win ""}} #s proc _appbar_motion {lastnum x y {win ""}} #s proc _appbar_close {} #s proc _appbar_raise {} #s proc _appbar_lower {} #s proc _appbar_layout {orient} #s proc _appbar_move {} #s proc _appbar_bind_special {widgets count mw} #s proc _appbar_bind_global {} #s proc _appbar_get_button_width {} # # ============================================================================= # # ----------------------------------------------------------------------------- # # Proc: dsk_appbar # Args: layout change the layout # move move the application bar around # Returns: "" # Desc: Meta proc for all things that concern the appbar. # Side-FX: none # proc dsk_appbar {args} { global tkdesk dsk_progress "Creating the application bar..." if ![info exists tkdesk(appbar)] { dsk_errbell cb_error "Couldn't read config file AppBar. Sorry, no application bar available." return } if {$args == ""} { _appbar_create } else { set cmd [lindex $args 0] set opts [lrange $args 1 [llength $args]] switch $cmd { layout {eval _appbar_layout $opts} move {eval _appbar_move $opts} raise {eval _appbar_raise} lower {eval _appbar_lower} close {eval _appbar_close} } } } # # ----------------------------------------------------------------------------- # # Proc: _appbar_create # Args: none # Returns: "" # Desc: Builds and displays the application bar. # Side-FX: none # if ![info exists tkdesk(geometry,dsk_appbar)] { set tkdesk(geometry,dsk_appbar) "" } set tkdesk(toggle,appbar) 0 set dsk_appbar(is_visible) 0 proc _appbar_create {} { global tkdesk dsk_appbar cb_tools set t .dsk_appbar if [winfo exists $t] { #cb_raise $t set tkdesk(toggle,appbar) 0 _appbar_close return } if ![info exists tkdesk(appbar,max)] { set tkdesk(appbar,max) 100 } elseif {$tkdesk(appbar,max) <= 0} { return } set tkdesk(toggle,appbar) 1 dsk_busy toplevel $t wm withdraw $t set ipad $tkdesk(appbar,ipad) set side top set fside left set dsk_appbar(layout) vertical if {$tkdesk(geometry,dsk_appbar) != ""} { set glist [split $tkdesk(geometry,dsk_appbar) x+] if {[lindex $glist 0] > [lindex $glist 1]} { set side left set fside top set dsk_appbar(layout) horizontal } } set dsk_appbar(tmp_bh) $cb_tools(balloon_help) # Setup Quick Menus menu [set m .dsk_appbar.qmConfig] foreach cf $tkdesk(configfiles) { $m add command -label "$cf" \ -command "dsk_edit_configs $cf" } menu [set m .dsk_appbar.qmAppbar] $m add command -label {Edit AppBar} -command {dsk_edit_configs AppBar} $m add command -label {Reload AppBar} -command {dsk_reread_config AppBar} $m add separator $m add command -label {Vertical} -command {dsk_appbar layout vertical} $m add command -label {Horizontal } -command {dsk_appbar layout horizontal} $m add command -label {Raise} -command {dsk_appbar raise} $m add command -label {Lower} -command {dsk_appbar lower} $m add command -label {Hide AppBar} -command {dsk_appbar close} $m add separator $m add command -label {Move Top Left} -command {dsk_appbar move topleft} $m add command -label {Move Bottom Left} \ -command {dsk_appbar move bottomleft} $m add command -label {Move Top Right} -command {dsk_appbar move topright} $m add command -label {Move Bottom Right} \ -command {dsk_appbar move bottomright} if {[llength [array names tkdesk appbar-*]] > 0} { # There's more than one AppBar defined -> create menu menubutton $t.mbBarSel -font fixed -text "Main" \ -menu [set m $t.mbBarSel.m] \ -bd 2 -relief flat -padx 0 -pady 0 menu $m pack $t.mbBarSel -fill x } # # Create Drag/Toggle Area # if {$tkdesk(appbar,dragger)} { button $t.bDrag -bitmap gray12 \ -height 8 -width 8 -cursor top_left_arrow \ -padx 0 -pady 0 -highlightthickness 0 \ -command _appbar_toggle_vis if {$dsk_appbar(layout) == "vertical"} { pack $t.bDrag -padx 0 -pady 0 -fill x -side $side } else { pack $t.bDrag -padx 0 -pady 0 -fill y -side $side } set cc white if {[winfo depth .] != 1} { set cc [cb_col $tkdesk(color,drag)] } bindtags $t.bDrag "appbar $t.bDrag Button all" bind $t.bDrag { set dsk_appbar(oldx) [winfo rootx .dsk_appbar] _appbar_bind_global_b1 %W %X %Y #break } } set count 0 set fcount 0 foreach but $tkdesk(appbar) { if {[expr $count % $tkdesk(appbar,max)] == 0} { incr fcount frame $t.f$fcount -bg [cb_col $tkdesk(color,icon)] pack $t.f$fcount -side $fside -fill both } if {[llength $but] > 1} { set bitmap [lindex $but 0] set dsk_appbar(bgcolor) [cb_col $tkdesk(color,background)] set dsk_appbar(fgcolor) [cb_col $tkdesk(color,foreground)] if {[llength $bitmap] > 1} { if {[lindex $bitmap 1] != ""} { set dsk_appbar(fgcolor) [lindex $bitmap 1] } if {[llength $bitmap] > 2} { if {[lindex $bitmap 2] != ""} { set dsk_appbar(bgcolor) [lindex $bitmap 2] } } set bitmap [lindex $bitmap 0] } set appmenu [lindex $but 1] menu [set m $t.m$count] -disabledforeground blue2 bind $m { if {[winfo containing %X %Y] != $dsk_appbar(lastbut)} { event generate %W } break } set dsk_appbar(num_cas) 0 set dsk_appbar(defaction) "" set dsk_appbar(deflabel) "" set dsk_appbar(ddaction) "" foreach me $appmenu { if {[llength $me] == 1} { _appbar_handle_special_entry $m $me } else { _appbar_add_to_menu $m $me } } if {[string first "special:" $bitmap] == 0} { switch $bitmap { "special:date" { set mw [_appbar_date $t.f$fcount $side] _appbar_bind_date $count set dsk_appbar(date,countval) $count } "special:load" { if {[dsk_auto_execok uptime] || \ $tkdesk(systype) == "Linux"} { set mw [_appbar_load $t.f$fcount $side] _appbar_bind_load $count set dsk_appbar(load,countval) $count } else { dsk_errbell cb_error "Disabling load display as I don't know how to get the load on your system. If you do, please send an email to Christian.Bolik@Mainz.netsurf.de. Thank you." #incr count -1 } } "special:mail" { _appbar_mail $t.b$count pack $t.b$count -in $t.f$fcount -side $side \ -fill both -ipadx $ipad -ipady $ipad bind $t.b$count <3> \ "_appbar_show_menu $count %X %Y" bind $t.m$count \ "_appbar_motion $count %X %Y" cb_balloonHelp $t.b$count $dsk_appbar(deflabel) bindtags $t.b$count "appbar $t.b$count Button all" if {$dsk_appbar(ddaction) != ""} { blt_drag&drop target $t.b$count handler file \ "_appbar_dd_action [list $dsk_appbar(ddaction)] %v" } } "special:dialup" { _appbar_dialup $t.b$count $count pack $t.b$count -in $t.f$fcount -side $side \ -fill both -ipadx $ipad -ipady $ipad bind $t.b$count <3> \ "_appbar_show_menu $count %X %Y" bind $t.m$count \ "_appbar_motion $count %X %Y" cb_balloonHelp $t.b$count $dsk_appbar(deflabel) bindtags $t.b$count "appbar $t.b$count Button all" if {$dsk_appbar(ddaction) != ""} { blt_drag&drop target $t.b$count handler file \ "_appbar_dd_action [list $dsk_appbar(ddaction)] %v" } } "special:trash" { catch {unset dsk_appbar(trash,label)} _appbar_trash $t.b$count pack $t.b$count -in $t.f$fcount -side $side \ -fill both -ipadx $ipad -ipady $ipad if [info exists dsk_appbar(trash,label)] { pack $dsk_appbar(trash,label) \ -in $t.f$fcount -side $side -fill both bindtags $dsk_appbar(trash,label) "appbar all" } bind $t.b$count <3> \ "_appbar_show_menu $count %X %Y" bind $t.m$count \ "_appbar_motion $count %X %Y" cb_balloonHelp $t.b$count $dsk_appbar(deflabel) bindtags $t.b$count "appbar $t.b$count Button all" if {$dsk_appbar(ddaction) != ""} { blt_drag&drop target $t.b$count handler file \ "_appbar_dd_action [list $dsk_appbar(ddaction)] %v" } } } } else { button $t.b$count \ -image [dsk_image $bitmap \ -background $dsk_appbar(bgcolor) \ -foreground $dsk_appbar(fgcolor)] \ -activebackground $dsk_appbar(bgcolor) \ -activeforeground $dsk_appbar(fgcolor) \ -cursor top_left_arrow \ -command $dsk_appbar(defaction) \ -padx 0 -pady 0 -highlightthickness 0 pack $t.b$count -in $t.f$fcount -side $side -fill both \ -ipadx $ipad -ipady $ipad bind $t.b$count <3> "_appbar_show_menu $count %X %Y" bind $t.m$count "_appbar_motion $count %X %Y" cb_balloonHelp $t.b$count $dsk_appbar(deflabel) #_appbar_bind_global $t.b$count bindtags $t.b$count "appbar $t.b$count Button all" if {$dsk_appbar(ddaction) != ""} { blt_drag&drop target $t.b$count handler file \ "_appbar_dd_action [list $dsk_appbar(ddaction)] %v" } } } else { set special [lindex $but 0] switch [lindex $special 0] { date { _appbar_date $t.f$fcount $side # the date occupies 2 buttons: incr count } } } incr count } set dsk_appbar(fcount) $fcount set dsk_appbar(is_visible) 1 bind $t {_appbar_auto_raise} wm title $t "TkDesk Application Bar" if {$tkdesk(geometry,dsk_appbar) == ""} { wm geometry $t +0+0 } else { set pi [string first "+" $tkdesk(geometry,dsk_appbar)] set pi2 [string first "-" $tkdesk(geometry,dsk_appbar)] if {$pi2 > -1 && ($pi2 < $pi || $pi < 0)} {set pi $pi2} wm geometry $t [string range $tkdesk(geometry,dsk_appbar) $pi 1000] } if $tkdesk(appbar,wm_managed) { wm overrideredirect $t 0 } else { wm overrideredirect $t 1 } wm deiconify $t _appbar_bind_global dsk_Common :: adjustAppBarMenuEntry 1 dsk_lazy } proc _appbar_toggle_vis {} { global dsk_appbar if [info exists dsk_appbar(oldx)] { if {$dsk_appbar(oldx) != [winfo rootx .dsk_appbar]} { return } } set t .dsk_appbar if $dsk_appbar(is_visible) { if {[winfo exists $t.bDrag]} { if {$dsk_appbar(layout) == "vertical"} { $t.bDrag config -width [expr [winfo reqwidth $t] -4] } else { $t.bDrag config -height [expr [winfo reqheight $t] -4] } } for {set i 1} {$i <= $dsk_appbar(fcount)} {incr i} { pack forget $t.f$i } set dsk_appbar(is_visible) 0 } else { if {$dsk_appbar(layout) == "vertical"} { set fside left } else { set fside top } for {set i 1} {$i <= $dsk_appbar(fcount)} {incr i} { pack $t.f$i -side $fside -fill both } if {[winfo exists $t.bDrag]} { $t.bDrag config -width 8 -height 8 } set dsk_appbar(is_visible) 1 } } proc _appbar_mcheck {appmenu nr menu} { if {[$appmenu index active] != $nr && \ [string first "tearoff" $menu] == -1} { $menu unpost } } set _appbar_special(cas) 0 proc _appbar_handle_special_entry {m me} { global tkdesk _appbar_special if [$m cget -tearoff] { set inr 1 } else { set inr 0 } if {$me == "-"} { $m add separator } elseif {$me == "."} { $m config -tearoff 1 } elseif {$me == "history:dirs"} { $m add cascade -label "Directories" -menu $m.mhd menu $m.mhd -postcommand \ "dir_history buildmenu $m.mhd open; update" # add dummy entry to work around bug in pre Tk 4.0p2: $m.mhd add command -label "dummy" dir_history changed bind $m.mhd "_appbar_mcheck $m \ [expr [$m index last] + $inr] %W" } elseif {$me == "history:files"} { $m add cascade -label "Files" -menu $m.mhf menu $m.mhf -postcommand \ "file_history buildmenu $m.mhf; update" # add dummy entry to work around bug in pre Tk 4.0p2: $m.mhf add command -label "dummy" file_history changed bind $m.mhf " set tkdesk(file_lb,control) 0 [bind Menu ]" bind $m.mhf " set tkdesk(file_lb,control) 1 [bind Menu ]" bind $m.mhf " set tkdesk(file_lb,control) 2 [bind Menu ]" bind $m.mhf "_appbar_mcheck $m \ [expr [$m index last] + $inr] %W" } elseif {$me == "history:execs"} { $m add cascade -label "Commands" -menu $m.mhe menu $m.mhe -postcommand \ "exec_history buildmenu $m.mhe; update" # add dummy entry to work around bug in pre Tk 4.0p2: $m.mhe add command -label "dummy" exec_history changed bind $m.mhe " set tkdesk(file_lb,control) 0 [bind Menu ]" bind $m.mhe " set tkdesk(file_lb,control) 1 [bind Menu ]" bind $m.mhe " set tkdesk(file_lb,control) 2 [bind Menu ]" bind $m.mhe "_appbar_mcheck $m \ [expr [$m index last] + $inr] %W" } elseif {$me == "bookmarks"} { $m add cascade -label "Bookmarks" -menu $m.book menu $m.book -postcommand "dsk_bookmark menu $m.book" # add dummy entry to work around bug in pre Tk 4.0p2: $m.book add command -label "dummy" bind $m.book " set tkdesk(file_lb,control) 0 [bind Menu ]" bind $m.book " set tkdesk(file_lb,control) 1 [bind Menu ]" bind $m.book " set tkdesk(file_lb,control) 2 [bind Menu ]" bind $m.book "_appbar_mcheck $m \ [expr [$m index last] + $inr] %W" } elseif {$me == "config"} { $m add cascade -label "Configuration" -menu $m.cfg menu [set tm $m.cfg] -tearoff 1 #menu $m.cfg #$m.cfg add cascade -label "Edit Config Files" \ # -menu $m.cfg.edmenu #$m.cfg add cascade -label "Reread Config Files" \ # -menu $m.cfg.rdmenu # #menu [set tm $m.cfg.edmenu] foreach cf $tkdesk(configfiles) { set cfs [format "%-12s" $cf] $tm add command -label "$cfs ($tkdesk(configxpl,$cf))" \ -command "dsk_edit_configs $cf" } $tm add separator $tm add cascade -label "Reload" -menu [set rm $tm.remenu] menu $rm -tearoff 1 foreach cf $tkdesk(configfiles) { $rm add command \ -label "$cf" \ -command "dsk_reread_config $cf" } $rm add separator $rm add checkbutton -label " Reload Automatically" \ -variable tkdesk(auto_reload_conf) $tm add command -label "Find..." \ -command "dsk_find_files -path $tkdesk(configdir) -name [list $tkdesk(configfiles)] -type file" $tm add command -label "Colors..." \ -command "dsk_config_panel colors" $tm add command -label "Fonts..." \ -command "dsk_config_panel fonts" $tm add command -label "Icons..." \ -command "dsk_config_panel icons" $tm add command -label "Sounds..." \ -command "dsk_config_panel sounds" #menu [set tm $m.cfg.rdmenu] #$tm add command -label "All" \ # -command "dsk_reread_config" #$tm add separator #foreach cf $tkdesk(configfiles) { # $tm add command -label $cf \ # -command "dsk_reread_config $cf" #} } elseif {$me == "buffers"} { $m add cascade -label "Buffers" -menu $m.bufs menu [set tm $m.bufs] -postcommand \ "dsk_Editor :: bufferMenu $tm" } elseif [regexp {^\*[^ ]} $me] { set cm $m.cas$_appbar_special(cas) incr _appbar_special(cas) set dir [string trimleft $me *] $m add cascade -label "$dir (*)" -menu $cm menu $cm -postcommand "dsk_casdirs $dir $cm 1" $cm add command -label "dummy" } elseif [regexp {^\&[^ ]} $me] { set cm $m.cas$_appbar_special(cas) incr _appbar_special(cas) set dir [string trimleft $me &] $m add cascade -label "$dir (&)" -menu $cm menu $cm -postcommand "dsk_casdirs $dir $cm 1 {} 1" $cm add command -label "dummy" } else { $m add command -label [subst [lindex $me 0]] \ -state disabled } } proc _appbar_add_to_menu {menu cmd} { global tkdesk dsk_appbar if {[llength $cmd] == 2} { set label [lindex $cmd 0] set command [string_replace [lindex $cmd 1] \" \\\"] if {$label != "dd" && $label != "DD"} { $menu add command -label $label \ -command "$menu unpost ;\ _appbar_unpost_parents $menu ;\ cd \[dsk_active dir\] ;\ set tkdesk(error_source) {AppBar} ;\ eval \[_expand_pc [list $command]\];\ set tkdesk(error_source) {} ;\ cd ~" if {$dsk_appbar(defaction) == ""} { set dsk_appbar(defaction) \ "cd \[dsk_active dir\] ;\ set tkdesk(error_source) {AppBar} ;\ eval \[_expand_pc [list $command]\] ;\ set tkdesk(error_source) {} ;\ cd ~" set dsk_appbar(deflabel) $label } } else { set dsk_appbar(ddaction) $command } } elseif {[llength $cmd] == 1} { _appbar_handle_special_entry $menu $cmd } else { set m ${menu}.mc$dsk_appbar(num_cas) incr dsk_appbar(num_cas) $menu add cascade -label [lindex $cmd 0] -menu $m menu $m set cmd [lreplace $cmd 0 0] foreach c $cmd { _appbar_add_to_menu $m $c } } } proc _appbar_unpost_parents {menu} { set p [winfo parent $menu] while {$p != ""} { if {[winfo class $p] == "Menu"} { catch "$p unpost" } set p [winfo parent $p] } } proc _appbar_dd_action {cmd args} { global tkdesk catch "wm withdraw $tkdesk(dd_token_window)" update set flist $args if {[string first %A $cmd] > -1} { set cmd [string_replace $cmd %A $flist] } else { set cmd [_expand_pc $cmd] } cd [dsk_active dir] eval $cmd cd ~ } proc _appbar_show_menu {butnum rootx rooty {win ""}} { global dsk_appbar set t .dsk_appbar if {$win == ""} { set win $t.b$butnum } set geom [split [wm geometry $t] x+] set tw [lindex $geom 0] set th [lindex $geom 1] # set bx [lindex $geom 2] # set by [lindex $geom 3] set bgeom [split [winfo geometry $win] x+] set bw [lindex $bgeom 0] set bh [lindex $bgeom 1] set bx [winfo rootx $win] set by [winfo rooty $win] set sw [winfo screenwidth $t] set sh [winfo screenheight $t] set mw [winfo reqwidth $t.m$butnum] set mh [winfo reqheight $t.m$butnum] if {$tw > $th} { # horizontal layout set x [winfo rootx $win] if {$by > ($sh >> 1)} { set y [expr $by - $mh] } else { set y [expr $by + $bh] } } else { # vertical layout set y [winfo rooty $win] if {$bx > ($sw >> 1)} { set x [expr $bx - $mw] } else { set x [expr $bx + $bw] } } #cb_MenuPopupAdd $t.b$butnum 3 $t.m$butnum {} {} 1 $x $y 1 update set dsk_appbar(lastbut) $win tk_popup $t.m$butnum $x $y } set dsk_appbar(motion) 0 proc _appbar_motion {lastnum x y {win ""}} { global dsk_appbar if $dsk_appbar(motion) return set dsk_appbar(motion) 1 set t .dsk_appbar set new [winfo containing $x $y] if {$new != $dsk_appbar(lastbut)} { if {[string match $t.b* $new] && [winfo class $new] == "Button"} { scan $new "$t.b%d" num if [info exists num] { $t.m$lastnum unpost _appbar_show_menu $num $x $y } } else { catch {set num $dsk_appbar(num,$new)} if [info exists num] { $t.m$lastnum unpost _appbar_show_menu $num $x $y $win } } } set dsk_appbar(motion) 0 } # # ----------------------------------------------------------------------------- # # Proc: _appbar_close # Args: none # Returns: "" # Desc: Removes the application bar. # Side-FX: none # proc _appbar_close {} { global tkdesk if {[dsk_active viewer] != 0} { if [winfo exists .dsk_appbar] { set tkdesk(geometry,dsk_appbar) [wm geometry .dsk_appbar] destroy .dsk_appbar } } else { cb_info "The application bar cannot be closed because there is no file browser window on screen." } dsk_Common :: adjustAppBarMenuEntry 0 } # # ----------------------------------------------------------------------------- # # Proc: _appbar_raise # Args: none # Returns: "" # Desc: Raises the application bar. # Side-FX: none # proc _appbar_raise {} { global tkdesk if [winfo exists .dsk_appbar] { raise .dsk_appbar } } proc _appbar_auto_raise {} { global tkdesk if [winfo exists .dsk_appbar] { if $tkdesk(appbar,autoraise) { raise .dsk_appbar } } } # # ----------------------------------------------------------------------------- # # Proc: _appbar_lower # Args: none # Returns: "" # Desc: Lowers the application bar. # Side-FX: none # proc _appbar_lower {} { global tkdesk if [winfo exists .dsk_appbar] { lower .dsk_appbar } } # # ----------------------------------------------------------------------------- # # Proc: _appbar_layout # Args: orient orientation: horizontal or vertical # Returns: "" # Desc: Repacks the buttons of the appbar accordingly to $orient. # Side-FX: none # proc _appbar_layout {orient} { global dsk_appbar tkdesk if ![winfo exists .dsk_appbar] return if !$dsk_appbar(is_visible) { _appbar_toggle_vis } if {$orient == "horizontal"} { set side left set fside top set dsk_appbar(layout) horizontal if {[winfo exists .dsk_appbar.bDrag]} { pack config .dsk_appbar.bDrag -padx 0 -pady 0 -fill y -side $side } } else { set side top set fside left set dsk_appbar(layout) vertical if {[winfo exists .dsk_appbar.bDrag]} { pack config .dsk_appbar.bDrag -padx 0 -pady 0 -fill x -side $side } } foreach obj [winfo children .dsk_appbar] { if {[winfo class $obj] == "Button" || \ [winfo class $obj] == "AppDate" || \ [winfo class $obj] == "AppLoad"} { if {[winfo class $obj] == "AppDate"} { _appbar_date "" $side catch {_appbar_bind_date $dsk_appbar(date,countval)} } elseif {[winfo class $obj] == "AppLoad"} { _appbar_load "" $side catch {_appbar_bind_load $dsk_appbar(load,countval)} } pack config $obj -side $side } elseif {[winfo class $obj] == "Frame"} { pack config $obj -side $fside } elseif {[winfo class $obj] == "Label"} { pack config $obj -side $side -fill both } } } # # ----------------------------------------------------------------------------- # # Proc: _appbar_move # Args: none # Returns: "" # Desc: Displays a hand cursor to move the appbar around. # Side-FX: none # proc _appbar_move {{where ""}} { global dsk_appbar tkdesk cb_tools if {$where != ""} { switch $where { topleft { wm geometry .dsk_appbar +0+0 } bottomleft { wm geometry .dsk_appbar +0-0 } topright { wm geometry .dsk_appbar -0+0 } bottomright { wm geometry .dsk_appbar -0-0 } } return } if {[winfo depth .] != 1} { set cc [cb_col $tkdesk(color,drag)] } else { set cc white } catch "unset dsk_appbar(released)" catch {destroy .dsk_appbar._Busy} foreach but [winfo children .dsk_appbar] { if {[winfo class $but] != "Button"} continue $but config -cursor "@$tkdesk(library)/images/xbm/hand.xbm \ $tkdesk(library)/images/xbm/hand.mask.xbm \ [cb_col $tkdesk(color,foreground)] $cc" bind appmove { wm geometry .dsk_appbar +[expr %X - $dsk_appbar(mx)]+[expr %Y - $dsk_appbar(my)]; break} bind appmove {set dsk_appbar(released) 1; break} bind appmove { set dsk_appbar(mx) [expr %X - [winfo rootx .dsk_appbar]] set dsk_appbar(my) [expr %Y - [winfo rooty .dsk_appbar]] break } bindtags $but "appmove" } set cbbh $cb_tools(balloon_help) set cb_tools(balloon_help) 0 set gl [split [wm geometry .dsk_appbar] x+] ot_warp_pointer [expr [lindex $gl 2] + 16] [expr [lindex $gl 3] + 16] grab -global .dsk_appbar tkwait variable dsk_appbar(released) grab release .dsk_appbar set cb_tools(balloon_help) $cbbh catch "unset dsk_appbar(released)" foreach but [winfo children .dsk_appbar] { if {[winfo class $but] != "Button"} continue $but config -cursor top_left_arrow bindtags $but "appbar $but Button all" } } # # ----------------------------------------------------------------------------- # # Proc: _appbar_bind_special # Args: widgets count args # Returns: "" # Desc: Binds the widget of a "special" appbar button. # Side-FX: none # proc _appbar_bind_special {widgets count mw} { global dsk_appbar foreach w $widgets { bind $w "$mw config -relief sunken" bind $w \ "$dsk_appbar(defaction); $mw config -relief raised" bind $w <3> "_appbar_show_menu $count %X %Y $mw" bindtags $w "appbar $w [winfo class $w] all" } foreach w $widgets { cb_balloonHelp $w $dsk_appbar(deflabel) if {$dsk_appbar(ddaction) != ""} { blt_drag&drop target $w handler file \ "_appbar_dd_action \"$dsk_appbar(ddaction)\" %v" } } } # ---------------------------------------------------------------------------- # _appbar_bind_global: # Adds bindings to the widget which apply to all widgets in the appbar # (most of which are accessed by pressing Meta and mousebutton simultaneously. # proc _appbar_bind_global {} { bind appbar { _appbar_bind_global_b1 %W %X %Y break } bind appbar [bind appbar ] bind appbar { if [info exists dsk_appbar(mx)] { wm geometry .dsk_appbar +[expr %X - $dsk_appbar(mx)]+[expr %Y - $dsk_appbar(my)] } break } bind appbar { wm geometry .dsk_appbar +[expr %X - $dsk_appbar(mx)]+[expr %Y - $dsk_appbar(my)] break } bind appbar [bind appbar ] bind appbar { if [info exists dsk_appbar(mx)] { unset dsk_appbar(mx) unset dsk_appbar(my) set cb_tools(balloon_help) $dsk_appbar(tmp_bh) %W config -cursor top_left_arrow #break } } # Quick Menus: bind appbar { tk_popup .dsk_appbar.qmConfig [expr %X + 1] [expr %Y + 1] break } bind appbar [bind appbar ] bind appbar { tk_popup .dsk_appbar.qmAppbar [expr %X + 1] [expr %Y + 1] break } bind appbar [bind appbar ] # work around bug (?) in Tk 4.0/4.1: bind appbar { if {%s == 8} { set dsk_appbar(tmp_bh) $cb_tools(balloon_help) set cb_tools(balloon_help) 0 set dsk_appbar(mx) [expr %X - [winfo rootx .dsk_appbar]] set dsk_appbar(my) [expr %Y - [winfo rooty .dsk_appbar]] %W config -cursor "@$tkdesk(library)/images/xbm/hand.xbm \ $tkdesk(library)/images/xbm/hand.mask.xbm \ [cb_col $tkdesk(color,foreground)] $tkdesk(color,drag)" raise .dsk_appbar break } } bind appbar { if {%s == 8} { tk_popup .dsk_appbar.qmConfig [expr %X + 1] [expr %Y + 1] break } } bind appbar { if {%s == 8} { tk_popup .dsk_appbar.qmAppbar [expr %X + 1] [expr %Y + 1] break } } } proc _appbar_bind_global_b1 {W X Y} { global dsk_appbar cb_tools tkdesk set dsk_appbar(tmp_bh) $cb_tools(balloon_help) set cb_tools(balloon_help) 0 set dsk_appbar(mx) [expr $X - [winfo rootx .dsk_appbar]] set dsk_appbar(my) [expr $Y - [winfo rooty .dsk_appbar]] $W config -cursor "@$tkdesk(library)/images/xbm/hand.xbm \ $tkdesk(library)/images/xbm/hand.mask.xbm \ [cb_col $tkdesk(color,foreground)] $tkdesk(color,drag)" raise .dsk_appbar } # ----------------------------------------------------------------------------- # _appbar_get_button_width: # Returns the width of the first button in the appbar - 4. This should # correspond to the width of the associated pixmap. # proc _appbar_get_button_width {} { global tkdesk set w 32 for {set i 0} {$i < [llength $tkdesk(appbar)]} {incr i} { if [winfo exists .dsk_appbar.b$i] { #update idletasks set w [expr [winfo reqwidth .dsk_appbar.b$i] - 4] break } } return $w } # ----------------------------------------------------------------------------- # dsk_appbar_adjgeom geom: # Swaps width and height info in $geom if appbar is currently minimized. # (Called from config.tcl:save__layout) # proc dsk_appbar_adjgeom {geom} { global dsk_appbar if !$dsk_appbar(is_visible) { set gl [split $geom "x+-"] set pi [string first "+" $geom] set pi2 [string first "-" $geom] if {$pi2 > -1 && ($pi2 < $pi || $pi < 0)} {set pi $pi2} set geom [lindex $gl 1]x[lindex $gl 0][string range $geom $pi 1000] dsk_debug "geom: $geom" } return $geom } tkdesk-2.0/tcldesk/bltDnd.tcl0100644000175000007640000000617210020457430014272 0ustar jccjcc# # bltDnd.tcl # # ---------------------------------------------------------------------- # Bindings for the BLT drag&drop command # ---------------------------------------------------------------------- # AUTHOR: George Howlett # Bell Labs Innovations for Lucent Technologies # gah@bell-labs.com # http://www.tcltk.com/blt # ---------------------------------------------------------------------- # Copyright (c) 1998 Lucent Technologies, Inc. # ====================================================================== # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that the copyright notice and warranty disclaimer appear in # supporting documentation, and that the names of Lucent Technologies # any of their entities not be used in advertising or publicity # pertaining to distribution of the software without specific, written # prior permission. # # Lucent Technologies disclaims all warranties with regard to this # software, including all implied warranties of merchantability and # fitness. In no event shall Lucent be liable for any special, indirect # or consequential damages or any damages whatsoever resulting from loss # of use, data or profits, whether in an action of contract, negligence # or other tortuous action, arising out of or in connection with the use # or performance of this software. # # ====================================================================== if { $tcl_version >= 8.0 } { set cmd blt::dnd } else { set cmd dnd } for { set i 1 } { $i <= 5 } { incr i } { bind BltDndButton$i [list $cmd select %W %X %Y %t] bind BltDndButton$i [list $cmd drag %W %X %Y] bind BltDndButton$i [list $cmd drop %W %X %Y] } # ---------------------------------------------------------------------- # # DndInit -- # # Invoked from C whenever a new drag&drop source is created. # Sets up the default bindings for the drag&drop source. # # Starts the drag operation. # Updates the drag. # Drop the data on the target. # # Arguments: # widget source widget # button Mouse button used to activate drag. # cmd "dragdrop" or "blt::dragdrop" # # ---------------------------------------------------------------------- proc blt::DndInit { widget button } { set tagList {} if { $button > 0 } { lappend tagList BltDndButton$button } foreach tag [bindtags $widget] { if { ![string match BltDndButton* $tag] } { lappend tagList $tag } } bindtags $widget $tagList } proc blt::DndStdDrop { widget args } { array set info $args parray info if { $info(state) & 0x01 } { puts "Shift-Drop" } if { $info(state) & 0x02 } { puts "CapsLock-Drop" } if { $info(state) & 0x04 } { puts "Control-Drop" } if { $info(state) & 0x08 } { puts "Alt-Drop" } if { $info(state) & 0x10 } { puts "NumLock-Drop" } set fmt [lindex $info(formats) 0] dnd pull $widget $fmt return 0 } tkdesk-2.0/tcldesk/bltDragdrop.tcl0100644000175000007640000000535110020457430015325 0ustar jccjcc# # bltDragdrop.tcl # # ---------------------------------------------------------------------- # Bindings for the BLT drag&drop command # ---------------------------------------------------------------------- # AUTHOR: George Howlett # Bell Labs Innovations for Lucent Technologies # gah@bell-labs.com # http://www.tcltk.com/blt # ---------------------------------------------------------------------- # Copyright (c) 1998 Lucent Technologies, Inc. # ====================================================================== # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that the copyright notice and warranty disclaimer appear in # supporting documentation, and that the names of Lucent Technologies # any of their entities not be used in advertising or publicity # pertaining to distribution of the software without specific, written # prior permission. # # Lucent Technologies disclaims all warranties with regard to this # software, including all implied warranties of merchantability and # fitness. In no event shall Lucent be liable for any special, indirect # or consequential damages or any damages whatsoever resulting from loss # of use, data or profits, whether in an action of contract, negligence # or other tortuous action, arising out of or in connection with the use # or performance of this software. # # ====================================================================== if { $tcl_version >= 8.0 } { set cmd blt::drag&drop } else { set cmd drag&drop } for { set i 1 } { $i <= 5 } { incr i } { bind BltDrag&DropButton$i [list $cmd drag %W %X %Y] bind BltDrag&DropButton$i [list $cmd drag %W %X %Y] bind BltDrag&DropButton$i [list $cmd drop %W %X %Y] } # ---------------------------------------------------------------------- # # Drag&DropInit -- # # Invoked from C whenever a new drag&drop source is created. # Sets up the default bindings for the drag&drop source. # # Starts the drag operation. # Updates the drag. # Drop the data on the target. # # Arguments: # widget source widget # button Mouse button used to activate drag. # cmd "dragdrop" or "blt::dragdrop" # # ---------------------------------------------------------------------- proc blt::Drag&DropInit { widget button } { set tagList {} if { $button > 0 } { lappend tagList BltDrag&DropButton$button } foreach tag [bindtags $widget] { if { ![string match BltDrag&DropButton* $tag] } { lappend tagList $tag } } bindtags $widget $tagList } tkdesk-2.0/tcldesk/bookmarks.tcl0100644000175000007640000001075010020457430015050 0ustar jccjcc# ============================================================================= # # File: bookmarks.tcl # Project: TkDesk # # Started: 12.12.94 # Changed: 29.03.96 # Author: cb # # Description: Implements procs for searching files and annotations. # Also does the bookmarks stuff. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_bookmark {cmd args} # # ============================================================================= # =========================================================================== # --------------------------------------------------------------------------- # bookmark cmd ?args ...? # Manages the bookmarks menu and the bookmarks list. # proc dsk_bookmark {cmd args} { global tkdesk env switch $cmd { menu { set m [lindex $args 0] bind $m { #puts "[%W entrycget [%W index active] -label]" set e [%W entrycget [%W index active] -label] set f [string trimright [lindex [split $e "("] 0] " "] set d [cb_tilde [string trim [lindex [split $e "("] 1] ()] \ expand] after 100 dsk_popup {} $d/$f %X %Y break } bind $m break bind $m [bind $m ] bind $m break catch {$m delete 0 last} $m add command -label "Add Bookmark" -underline 0 \ -command "dsk_bookmark add" if {[llength $tkdesk(bookmarks)] > 0} { $m add separator } set l {} foreach bm $tkdesk(bookmarks) { lappend l [list [file tail $bm] [file dirname $bm]] } set bms [lsort $l] set i 0 foreach bm $bms { set f [lindex $bm 0] set d [lindex $bm 1] set l "$f ($d)" set ld "$f/ ($d)" set sf [_make_fname_safe $d/$f] if {[file isdirectory $sf]} { if {$tkdesk(expand_dir_bookmarks)} { set cm $m.cm$i catch {destroy $cm} $m add cascade -label "$l" \ -font [cb_font $tkdesk(font,file_lbs)] \ -menu $cm menu $cm -postcommand "dsk_casdirs \"$sf\" $cm 1 {} 1" $cm add command -label "dummy" incr i } else { $m add command -label "$ld" \ -font [cb_font $tkdesk(font,file_lbs)] \ -command "dsk_open \$tkdesk(active_viewer) \"$sf\"" } } else { $m add command -label "$l" \ -font [cb_font $tkdesk(font,file_lbs)] \ -command "dsk_open \$tkdesk(active_viewer) \"$sf\"" } } $m add separator $m add cascade -label "Remove Bookmark" -menu $m.rb catch {destroy $m.rb} menu [set m $m.rb] foreach bm $bms { set f [lindex $bm 0] set d [lindex $bm 1] set l "$f ($d)" set sf [_make_fname_safe $d/$f] $m add command -label "$l" \ -font [cb_font $tkdesk(font,file_lbs)] \ -command "dsk_bookmark remove \"$sf\"" } } add { if {$args == ""} { # set files to path that's displayed by current browser set files [string trimright [_make_fnames_safe] /] } else { set files $args } if {$files == ""} { set dir [dsk_active dir] if {$dir != ""} { set files [string trimright $dir /] } else { cb_info "Please select one or more files first." return } } foreach file $files { set file [subst $file] if {[string first $env(HOME) $file] == 0} { set file [string_replace $file $env(HOME) ~] } if {[lsearch $tkdesk(bookmarks) $file] == -1} { lappend tkdesk(bookmarks) $file } } } remove { set bm [_make_fname_safe [lindex $args 0]] set nr [lsearch $tkdesk(bookmarks) $bm] if {$nr > -1} { set tkdesk(bookmarks) \ [lreplace $tkdesk(bookmarks) $nr $nr] } else { dsk_debug "not in bookmarks: $bm" dsk_debug "bookmarks:\n$tkdesk(bookmarks)" } } } } tkdesk-2.0/tcldesk/config.tcl0100644000175000007640000013373310037122357014341 0ustar jccjcc# ============================================================================= # # File: config.tcl # Project: TkDesk # # Started: 11.10.94 # Changed: 22.11.94 # Author: cb # # Description: Reads the config files. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_read_config {} #s proc dsk_edit_configs {args} #s proc source_cfg {file} #s proc read_System {} #s proc read_ButtonBar {} #s proc read_FileTags {} #s proc read_Preferences {} #s proc read_Commands {} #s proc read_Directories {} #s proc read_Popups {} #s proc read_AppBar {{fname "AppBar"}} #s proc read_Sounds {} #s proc read_Local {} #s proc read__history {} #s proc read__layout {} #s proc read__annotations {} #s proc read__bookmarks {} #s proc dsk_save_config {{all 0}} #s proc save__history {} #s proc save__layout {} #s proc _shift_geom {top} #s proc save__annotations {} #s proc save__bookmarks {} #s proc save__options {} #s proc dsk_restore_layout {} #s proc dsk_reread_config {{cfgfile ""}} #s proc dsk_persave_config {} #s proc _add_cmd_to_menu {menu cmd} #s proc dsk_rebuild_cmdmenus {} #s proc _add_dir_to_menu {this menu mdir} #s proc dsk_rebuild_dirmenus {} # # ============================================================================= global tkdesk # # ----------------------------------------------------------------------------- # # Proc: dsk_read_config # Args: none # Returns: "" # Desc: Determines the config dir and reads all of the config files. # Side-FX: Exits TkDesk if an error occured. Sets tkdesk(configdir). # proc dsk_read_config {} { global tkdesk env if ![info exists tkdesk(configdir)] { set notice 0 set version 1.0a1 if ![file exists $env(HOME)/.tkdesk] { set tkdesk(first_time_user) 1 exec mkdir $env(HOME)/.tkdesk exec mkdir $env(HOME)/.tkdesk/.trash catch {eval exec cp [glob $tkdesk(library)/configs/*] \ $env(HOME)/.tkdesk} dsk_catch { set fd [open $env(HOME)/.tkdesk/_version w] puts $fd $tkdesk(version) catch {close $fd} } } else { if ![file readable $env(HOME)/.tkdesk/_version] { set notice 1 } else { dsk_catch { set fd [open $env(HOME)/.tkdesk/_version r] set version [gets $fd] catch {close $fd} } if {$version != $tkdesk(version)} { set notice 1 } } } if {$notice} { cb_info "Apparently you've been running a previous version of TkDesk. Please take a look at menu entry \"Help/Changes\" to find out what has changed since version $version." dsk_catch { set fd [open $env(HOME)/.tkdesk/_version w] puts $fd $tkdesk(version) catch {close $fd} } } # # Look for configuration directory in: # 1. home dir, 2. current dir, 3. library dir # if [file isdirectory $env(HOME)/.tkdesk] { if [string match $env(HOME) "/"] { set tkdesk(configdir) /.tkdesk } else { set tkdesk(configdir) $env(HOME)/.tkdesk } } elseif [file isdirectory [pwd]/.tkdesk] { set tkdesk(configdir) [pwd]/.tkdesk } elseif [file isdirectory $tkdesk(library)/configs] { set tkdesk(configdir) $tkdesk(library)/configs } else { exit 1 } } else { # else configdir has been set via command line dsk_debug "TkDesk: Reading configuration from $tkdesk(configdir)" if ![file isdirectory $env(HOME)/.tkdesk] { set tkdesk(first_time_user) 1 } } # Save current mtime for automatic reloading, and desk update set tkdesk(configdir_mtime) [file mtime $tkdesk(configdir)] if ![file writable $tkdesk(configdir)] { cb_alert "Will not be able to auto-save configuration (configuration directory is not writable)." } dsk_debug "Setting tkdesk(configdir) to $tkdesk(configdir)" set tkdesk(configfiles) {AppBar ButtonBar Commands Directories \ FileTags Local Popups Sounds System} set tkdesk(configxpl,AppBar) {Buttons of the Application Bar} set tkdesk(configxpl,ButtonBar) {Defines the Bar underneath the Menu Bar} set tkdesk(configxpl,Commands) {Entries of the "Commands" Menu} set tkdesk(configxpl,Directories) {Entries of the "Directories" Menu} set tkdesk(configxpl,FileTags) {Colors, Fonts, and Icons of Files etc.} set tkdesk(configxpl,Local) {Local Extensions} set tkdesk(configxpl,Popups) {Popup Menus for Files etc.} set tkdesk(configxpl,Sounds) {Sounds to use for TkDesk events} set tkdesk(configxpl,System) {Fonts and Colors used by TkDesk, etc.} #set tkdesk(configxpl,AppBar) {} #set tkdesk(configxpl,ButtonBar) {} #set tkdesk(configxpl,Commands) {} #set tkdesk(configxpl,Directories) {} #set tkdesk(configxpl,FileTags) {} #set tkdesk(configxpl,Local) {} #set tkdesk(configxpl,Popups) {} #set tkdesk(configxpl,Sounds) {} #set tkdesk(configxpl,System) {} read_System read_ButtonBar read_Preferences read_FileTags read_Commands read_Directories read_Popups read_AppBar read_Sounds read_Local read__history read__annotations read__bookmarks read__layout } # # ----------------------------------------------------------------------------- # # Proc: dsk_edit_configs # Args: files opt. list of config files to edit # Returns: "" # Desc: Calls the editor via dsk_edit on all or selected config files. # Side-FX: none # proc dsk_edit_configs {args} { global tkdesk set cdir [string trimright $tkdesk(configdir) "/"] set clist "" set files $args if {$files == ""} { foreach cfile $tkdesk(configfiles) { if {$cfile == "AppBar"} { set cfile $tkdesk(appbar,deffile) } lappend clist $cdir/$cfile } } else { foreach cfile $files { if {$cfile == "AppBar"} { set cfile $tkdesk(appbar,deffile) } lappend clist $cdir/$cfile } } #dsk_Editor .de[dsk_Editor :: id] -files $clist set ed $tkdesk(editor,cmd) if [info exists tkdesk(conf_editor,cmd)] { set tkdesk(editor,cmd) $tkdesk(conf_editor,cmd) } eval dsk_edit $clist set tkdesk(editor,cmd) $ed } # # ----------------------------------------------------------------------------- # # Proc: source_cfg # Args: file - path of cfg file to source # Returns: "" # Desc: Sources the configuration file $file. # Side-FX: none # proc source_cfg {file} { global tkdesk tkdesk_anno tk_strictMotif cb_tools env set err [catch "source $file" errmsg] if $err { # [todo] where does this dsk_lazy come from??? #dsk_lazy dsk_errbell cb_error "Error in file [file tail $file]: $errmsg" } return } # # ----------------------------------------------------------------------------- # # Proc: read_System # Args: none # Returns: "" # Desc: Reads the "System" configuration file. # Sets colors, fonts and other system-wide parameters. # Side-FX: Exits TkDesk if it was not found. # # Defaults: set tkdesk(file_lb,minwidth) 10 ;# this now gets set in read_System below set tkdesk(file_lb,minheight) 3 set tkdesk(color,directories) blue2 set tkdesk(color,executables) red set tkdesk(color,symlinks) black set tkdesk(color,symdirectories) blue2 set tkdesk(color,symexecutables) red set tkdesk(color,drag) wheat set tkdesk(color,entry_bg) white set tkdesk(color,entry_fg) black set tkdesk(color,text_bg) white set tkdesk(color,text_fg) black set tkdesk(color,icon_background) \#185f6a set tkdesk(font,directories) -*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,executables) -*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,symlinks) -*-helvetica-medium-o-*-*-12-*-*-*-*-*-*-* set tkdesk(font,symdirectories) -*-helvetica-bold-o-*-*-12-*-*-*-*-*-*-* set tkdesk(font,symexecutables) -*-helvetica-bold-o-*-*-12-*-*-*-*-*-*-* set tkdesk(font,file_lbs) -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,status) -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,balloon) -*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,dialogs) -*-times-medium-r-*-*-18-*-*-*-*-*-*-* set tkdesk(font,labels) -*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,listboxes) $tkdesk(font,labels) set tkdesk(font,buttons) -*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,menubuttons) $tkdesk(font,labels) set tkdesk(font,menus) $tkdesk(font,menubuttons) set tkdesk(font,entries) -*-lucidatypewriter-medium-r-*-*-12-*-*-*-*-*-*-* set tkdesk(font,text) $tkdesk(font,entries) set tkdesk(font,mono) -*-lucidatypewriter-medium-r-*-*-12-*-*-*-*-*-*-* set tkdesk(num_lbs) 3 set tkdesk(path,images) "$tkdesk(library)/images" set tkdesk(path,sounds) "$tkdesk(library)/sounds" set tkdesk(icon,trash-empty) next/recycle.xpm set tkdesk(icon,trash-full) next/recycle_full.xpm set tkdesk(icon,filebrowser) next/cabinet.xpm set tkdesk(icon,filelist) next/folders.xpm set tkdesk(icon,help) next/BookOpen.xpm set tkdesk(icon,editor) next/Write.xpm set tkdesk(icon,find) next/magnify.xpm set tkdesk(editor,default_geometry) "80x25" set tkdesk(cmd,print) "lpr" set tkdesk(cmd,netscape) "netscape" set tkdesk(cmd,mail) "mail -s %s %a" set tkdesk(cmd,xterm) "xterm" set tkdesk(cmd,df) "df" set tkdesk(cmd,ps) "ps" set tkdesk(cmd,su,exec) {xterm -sb -T "%c" -n "%c" -e su root -c "%c"} set tkdesk(cmd,su,view) {xterm -sb -T "%c" -n "%c" -e pauseme su root -c "%c"} set tkdesk(focus_follows_mouse) 1 set tkdesk(editor,auto_indent) 1 set tkdesk(editor,brace_indent) 1 set tkdesk(editor,wrap) char set tkdesk(editor,real_tabs) 1 set tkdesk(editor,tab_width) 8 set tkdesk(editor,do_backups) 1 set tkdesk(editor,load_once) 1 set tkdesk(editor,cursor) black set tkdesk(editor,statbar) 1 set tkdesk(editor,bindings) "classic" set tkdesk(editor,font) $tkdesk(font,entries) set tkdesk(desk_items,wm_managed) 0 set tkdesk(desk_items,label_width) 12 set tkdesk(dynamic_scrollbars) 1 set tkdesk(use_old_modifiers) 0 set tkdesk(color,annotation) gray55 set tkdesk(side_of_scrollbars) right set tkdesk(focus_width) 1 set tkdesk(scrollbar_width) 11 set tkdesk(file_selector_type) "default" set tkdesk(deskdir) ~/desktop set tkdesk(title,browser) "%p" set tkdesk(title,list) "%d" set tkdesk(title,icon) "%d" proc read_System {} { global tkdesk tk_strictMotif dsk_debug "Reading $tkdesk(configdir)/System" if ![file readable $tkdesk(configdir)/System] { catch {puts stderr "Couldn't read $tkdesk(configdir)/System! Exiting..."} exit 1 } else { source_cfg $tkdesk(configdir)/System } # # Set colors # frame .dummy frame .dummy.f entry .dummy.e text .dummy.t scrollbar .dummy.s listbox .dummy.lb button .dummy.b radiobutton .dummy.rb checkbutton .dummy.cb menubutton .dummy.mb menu .dummy.mb.menu label .dummy.l message .dummy.msg canvas .dummy.cv if ![info exists tkdesk(color,background)] { if [info exists tkdesk(color,basic)] { # for backward compatibility set tkdesk(color,background) [cb_col $tkdesk(color,basic) white] } else { set tkdesk(color,background) [.dummy.e cget -background] } } if ![info exists tkdesk(color,foreground)] { set tkdesk(color,foreground) [.dummy.e cget -foreground] } if ![info exists tkdesk(color,select_bg)] { set tkdesk(color,select_bg) [.dummy.e cget -selectbackground] } if ![info exists tkdesk(color,select_fg)] { set tkdesk(color,select_fg) [.dummy.e cget -selectforeground] } if ![info exists tkdesk(color,check_on)] { set tkdesk(color,check_on) [.dummy.cb cget -selectcolor] } if ![info exists tkdesk(color,focus)] { set tkdesk(color,focus) [.dummy.e cget -highlightcolor] } if ![info exists tkdesk(color,nofocus)] { set tkdesk(color,nofocus) [.dummy.e cget -highlightbackground] } if ![info exists tkdesk(color,focus_width)] { set tkdesk(color,focus_width) [.dummy.e cget -highlightthickness] } if ![info exists tkdesk(color,insert)] { set tkdesk(color,insert) [.dummy.e cget -insertbackground] } if ![info exists tkdesk(color,filelb)] { # for backward compatibility set tkdesk(color,filelb) [cb_col $tkdesk(color,filelb_background)] } if ![info exists tkdesk(color,icon)] { # for backward compatibility set tkdesk(color,icon) [cb_col $tkdesk(color,icon_background)] } if ![info exists tkdesk(color,listsel)] { if {$tkdesk(color,filelb) != "white" \ && [winfo depth .] > 1} { set tkdesk(color,listsel) white } else { set tkdesk(color,listsel) black } } if ![info exists tkdesk(config_done)] { tk_setPalette \ background [cb_col $tkdesk(color,background) white] \ foreground [cb_col $tkdesk(color,foreground) black] \ selectColor [cb_col $tkdesk(color,check_on)] \ selectBackground [cb_col $tkdesk(color,select_bg)] \ selectForeground [cb_col $tkdesk(color,select_fg)] \ highlightBackground [cb_col $tkdesk(color,nofocus)] \ highlightColor [cb_col $tkdesk(color,focus)] \ insertBackground [cb_col $tkdesk(color,insert)] } option add *background [cb_col $tkdesk(color,background) white] userDefault option add *foreground [cb_col $tkdesk(color,foreground) black] userDefault option add *selectColor [cb_col $tkdesk(color,check_on)] userDefault option add *selectBackground [cb_col $tkdesk(color,select_bg)] userDefault option add *selectForeground [cb_col $tkdesk(color,select_fg)] userDefault option add *highlightBackground [cb_col $tkdesk(color,nofocus)] userDefault option add *highlightColor [cb_col $tkdesk(color,focus)] userDefault option add *insertBackground [cb_col $tkdesk(color,insert)] userDefault option add *Entry.background [cb_col $tkdesk(color,entry_bg)] set tkdesk(color,entry) $tkdesk(color,entry_bg) option add *Entry.foreground [cb_col $tkdesk(color,entry_fg)] option add *Text.background [cb_col $tkdesk(color,text_bg)] set tkdesk(color,text) $tkdesk(color,text_bg) option add *Text.foreground [cb_col $tkdesk(color,text_fg)] if {[winfo depth .] > 1} { option add *Listbox.selectBackground [cb_col $tkdesk(color,listsel)] option add *Listbox.selectForeground [cb_col $tkdesk(color,foreground)] } # # Set fonts # option add *Label.font [cb_font $tkdesk(font,labels)] option add *Checkbutton.font [cb_font $tkdesk(font,labels)] option add *Radiobutton.font [cb_font $tkdesk(font,labels)] option add *Entry.font [cb_font $tkdesk(font,entries)] option add *Text.font [cb_font $tkdesk(font,entries)] option add *Button.font [cb_font $tkdesk(font,buttons)] option add *Menubutton.font [cb_font $tkdesk(font,menubuttons)] option add *Menu.font [cb_font $tkdesk(font,menus)] option add *Listbox.font [cb_font $tkdesk(font,listboxes)] option add *Dialog.msg.font [cb_font $tkdesk(font,dialogs)] _cb_help_proc:setfont \ [cb_font $tkdesk(font,entries)] \ [cb_font $tkdesk(font,listboxes)] option add *Menubutton.padX 3 option add *Menubutton.padY 3 option add *Scrollbar.borderWidth 2 option add *Scrollbar.width $tkdesk(scrollbar_width) option add *Entry.highlightThickness $tkdesk(focus_width) userDefault option add *Button.highlightThickness $tkdesk(focus_width) userDefault if $tkdesk(focus_follows_mouse) { tk_focusFollowsMouse } if ![info exists tkdesk(trashdir)] { set tkdesk(trashdir) $tkdesk(configdir)/.trash } else { if ![file isdirectory $tkdesk(trashdir)] { set err [catch {exec mkdir $tkdesk(trashdir)} errmsg] if $err { cb_error "Error: \"$errmsg\". Using default trash directory." set tkdesk(trashdir) $tkdesk(configdir)/.trash } } } set tkdesk(trashdir) [cb_tilde $tkdesk(trashdir) expand] dsk_Listbox :: selcolor [cb_col $tkdesk(color,listsel)] dsk_Listbox :: modifier $tkdesk(use_old_modifiers) dsk_Listbox :: scrollbarside $tkdesk(side_of_scrollbars) #set tkdesk(file_lb,minwidth) $tkdesk(file_lb,width) dsk_DeskItem :: set_width $tkdesk(desk_items,label_width) # Backward compatibility: set tkdesk(strict_motif) $tk_strictMotif # Desktop dir handling: if {[info exists tkdesk(deskdir)]} { if {[string length $tkdesk(deskdir)] > 0} { set tkdesk(deskdir) [cb_tilde $tkdesk(deskdir) expand] if {![file isdirectory $tkdesk(deskdir)]} { exec mkdir $tkdesk(deskdir) } set tkdesk(deskdir_mtime) [file mtime $tkdesk(deskdir)] } } catch {destroy .dummy} return } # # ----------------------------------------------------------------------------- # # Proc: read_ButtonBar # Args: none # Returns: "" # Desc: Reads the "ButtonBar" config file. # Side-FX: sets the tkdesk(directories) list # proc read_ButtonBar {} { global tkdesk # # Read the button bar list # set tkdesk(small_button_bar) {} if [file readable $tkdesk(configdir)/ButtonBar] { dsk_debug "Reading $tkdesk(configdir)/ButtonBar" source_cfg $tkdesk(configdir)/ButtonBar } return } # # ----------------------------------------------------------------------------- # # Proc: read_FileTags # Args: none # Returns: "" # Desc: Reads the "FileTags" config file. # Side-FX: Creates tags for the file listboxes. # set tkdesk(file_tags,ignore) {} proc read_FileTags {} { global tkdesk # # Read file tags # if [file readable $tkdesk(configdir)/FileTags] { dsk_debug "Reading $tkdesk(configdir)/FileTags" source_cfg $tkdesk(configdir)/FileTags if {[info commands dsk_FileListbox_fileTags] != ""} { # exec only if a FileListbox has already been created dsk_FileListbox_fileTags } } return } # # ----------------------------------------------------------------------------- # # Proc: read_Preferences # Args: none # Returns: "" # Desc: Reads the "Preferences" config file. # Side-FX: # # Defaults: set tkdesk(add_icons) 1 ;# add icons to file lists? set tkdesk(show_all_files) 0 ;# show files starting with "." ? set tkdesk(long_listing) 0 ;# do long listings? ? set tkdesk(folders_on_top) 1 ;# put folders always on top ? set tkdesk(append_type_char) 0 ;# append "/" to folders etc. ? set tkdesk(single_click) 0 ;# Let dirs be opened by a single click? set tkdesk(strip_home) 1 ;# use $HOME as root dir if under ~ ? set tkdesk(at_pointer) 1 ;# Place dialogs at mouse pointer? set tkdesk(overwrite_always) 0 ;# Overwrite existing files /wo asking? set tkdesk(really_delete) 0 ;# Delete REALLY by default? set tkdesk(quick_dragndrop) 0 ;# don't ask when dropping files? set tkdesk(dot_regular) 0 ;# treat exec's containing dots as regulars? set tkdesk(sort_history) 1 ;# sort history menus? set tkdesk(use_sound) 0 ;# Use sound if available? (see file "Sounds") set tkdesk(in_browser) 0 ;# open new file windows always as browsers? set tkdesk(default_sort) name ;# default sort style set tkdesk(ask_on_delete) 1 ;# ask even if not really deleting set tkdesk() 1 ;# ask before exiting? set tkdesk(tkdesk_server) 0 ;# start TkDesk server set tkdesk(list_statbar) 0 ;# display status bar in file list windows? set tkdesk(tearoff-menus) 0 ;# 1 if menus should be tear-off-able set tkdesk(focus_follows_mouse) 1 ;# focus model to use set tkdesk(strict_motif) 0 ;# strict Motif look-and-feel? set tkdesk(auto_reload_conf) 1 ;# automatically reload modified conf files? set tkdesk(expand_dir_bookmarks) 1 ;# expand dirs in Bookmarks menu? set tkdesk(confirm_netscape) 1 ;# ask whether to start Netscape? set tkdesk(confvars) { \ long_listing show_all_files add_icons folders_on_top \ append_type_char single_click strip_home at_pointer overwrite_always \ really_delete quick_dragndrop sort_history use_sound default_sort \ ask_on_delete ask_on_exit in_browser netscape_help tkdesk_server \ editor,auto_indent editor,wrap editor,do_backups editor,load_once \ editor,real_tabs editor,tab_width list_statbar appbar,autoraise \ tearoff-menus focus_follows_mouse strict_motif confirm_netscape \ auto_reload_conf editor,statbar expand_dir_bookmarks dot_regular } set tkdesk(autosave,history) 1 set tkdesk(autosave,layout) 1 set tkdesk(autosave,annotations) 1 set tkdesk(autosave,bookmarks) 1 set tkdesk(autosave,options) 1 set cb_tools(balloon_help) 1 ;# display neat/annoying help popups? set tkdesk(netscape_help) 0 ;# display help thru netscape? proc read_Preferences {} { global tkdesk tk_strictMotif # # Read preferences # if [file readable $tkdesk(configdir)/_options] { set pfile "_options" } elseif [file readable $tkdesk(configdir)/Preferences] { set pfile "Preferences" } else { set pfile "no-prefs" ;# fake file name for next test } if [file readable $tkdesk(configdir)/$pfile] { dsk_debug "Reading $tkdesk(configdir)/$pfile" set nlb $tkdesk(num_lbs) ;# for backward compatibilty source_cfg $tkdesk(configdir)/$pfile set tkdesk(num_lbs) $nlb } # always copy, move etc. all selected files by default: set tkdesk(all_files) 1 # we don't want free selection! set tkdesk(free_selection) 0 # disable this at start-up (backward comp.) set tkdesk(exec_as_root) 0 if $tkdesk(tkdesk_server) { dsk_progress "Starting server..." dsk_setup_server } dsk_FileList :: status_bar $tkdesk(list_statbar) dsk_FileListbox :: dotregular $tkdesk(dot_regular) option add *Menu.tearOff $tkdesk(tearoff-menus) set tk_strictMotif $tkdesk(strict_motif) return } # # ----------------------------------------------------------------------------- # # Proc: read_Commands # Args: none # Returns: "" # Desc: Reads the "Commands" config file. # Side-FX: sets the tkdesk(commands) list # proc read_Commands {} { global tkdesk # # Read directories # if [file readable $tkdesk(configdir)/Commands] { dsk_debug "Reading $tkdesk(configdir)/Commands" source_cfg $tkdesk(configdir)/Commands } return } # # ----------------------------------------------------------------------------- # # Proc: read_Directories # Args: none # Returns: "" # Desc: Reads the "Directories" config file. # Side-FX: sets the tkdesk(directories) list # proc read_Directories {} { global tkdesk # # Read directories # if [file readable $tkdesk(configdir)/Directories] { dsk_debug "Reading $tkdesk(configdir)/Directories" source_cfg $tkdesk(configdir)/Directories } return } # # ----------------------------------------------------------------------------- # # Proc: read_Popups # Args: none # Returns: "" # Desc: Reads the "Popups" config file. # Side-FX: sets the tkdesk(popup,...) lists # set tkdesk(fileops,popup) { {{Link (symbolic)} { dsk_exec $tkdesk(cmd,symln) %S %T }} {{Link (hard)} { dsk_exec $tkdesk(cmd,ln) %S %T }} {{Untar} { dsk_bgexec {gzip -cd %S | tar xf - -C %T} {Untaring %f...} }} {{Diff} { dsk_view diff -bc %S %T }} {{Patch} { dsk_path_exec %T xterm -sb -T "patch <%S" -n patch -e pauseme sh -c "patch <%S" }} {{Concatenate} { dsk_exec cat %S >%T }} } proc read_Popups {} { global tkdesk # # Read popups # if [file readable $tkdesk(configdir)/Popups] { dsk_debug "Reading $tkdesk(configdir)/Popups" source_cfg $tkdesk(configdir)/Popups } return } # # ----------------------------------------------------------------------------- # # Proc: read_AppBar # Args: none # Returns: "" # Desc: Reads the "AppBar" config file. # Side-FX: sets the tkdesk(appbar) list # # some defaults: set tkdesk(appbar,wm_managed) 0 set tkdesk(appbar,dragger) 0 set tkdesk(appbar,max) 19 set tkdesk(appbar,font,time) -*-courier-medium-r-*-*-10-*-*-*-*-*-*-* set tkdesk(appbar,font,weekday) -*-helvetica-medium-r-*-*-10-*-*-*-*-*-*-* set tkdesk(appbar,font,day) -*-times-bold-r-*-*-24-*-*-*-*-*-*-* set tkdesk(appbar,font,month) -*-helvetica-medium-o-*-*-12-*-*-*-*-*-*-* set tkdesk(appbar,12hour) 0 set tkdesk(appbar,time,background) black set tkdesk(appbar,time,foreground) green set tkdesk(appbar,time,borderwidth) 2 set tkdesk(appbar,time,relief) raised set tkdesk(appbar,load,delay) 15 set tkdesk(appbar,mail,delay) 30 set tkdesk(appbar,mail,notifier) 0 set tkdesk(appbar,mail,nomail) mailbox_empty.xpm set tkdesk(appbar,mail,oldmail) mailbox_old.xpm set tkdesk(appbar,mail,newmail) mailbox_full.xpm set tkdesk(appbar,mail,newbg) slategrey set tkdesk(appbar,trash,empty) trashcan.xpm set tkdesk(appbar,trash,full) trashcan_full.xpm set tkdesk(appbar,trash,label) 1 set tkdesk(appbar,trash,font) fixed set tkdesk(appbar,dialup,down) next/PhoneTT.xpm set tkdesk(appbar,dialup,up) next/PhoneTTOffhook.xpm set tkdesk(appbar,autoraise) 0 set tkdesk(appbar,deffile) "AppBar" set tkdesk(appbar,ipad) 2 proc read_AppBar {{fname "AppBar"}} { global tkdesk # # Read application bar # if [file readable $tkdesk(configdir)/$fname] { dsk_debug "Reading $tkdesk(configdir)/$fname" set tkdesk(appbar,deffile) $fname source_cfg $tkdesk(configdir)/$fname } return } # ----------------------------------------------------------------------------- # # Proc: read_Sounds # Args: none # Returns: "" # Desc: Reads the "Sound" config file. # Side-FX: sets the tkdesk(sound,...) variables # proc read_Sounds {} { global tkdesk # # Read the sounds file # catch {unset tkdesk(soundcmd)} if [file readable $tkdesk(configdir)/Sounds] { dsk_debug "Reading $tkdesk(configdir)/Sounds" source_cfg $tkdesk(configdir)/Sounds } if ![info exists tkdesk(soundcmd)] { set tkdesk(use_sound) 0 } dsk_Common :: setSoundEntryState return } # ----------------------------------------------------------------------------- # # Proc: read_Local # Args: none # Returns: "" # Desc: Reads the "Local" config file. # Side-FX: depends on user skills... # proc read_Local {} { global tkdesk # # Read the local config file # if [file readable $tkdesk(configdir)/Local] { dsk_debug "Reading $tkdesk(configdir)/Local" source_cfg $tkdesk(configdir)/Local } return } # # ----------------------------------------------------------------------------- # # Proc: read__history # Args: none # Returns: "" # Desc: Reads the _history file if it exists. # Side-FX: Sets the global path history. # proc read__history {} { global tkdesk if [file readable $tkdesk(configdir)/_history] { dsk_debug "Reading $tkdesk(configdir)/_history" set fd [open $tkdesk(configdir)/_history] set tkdesk(history_list) "" while 1 { set h [gets $fd] if {$h == ""} { break } else { lappend tkdesk(history_list) [list $h] } } catch {close $fd} } return } # # ----------------------------------------------------------------------------- # # Proc: read__layout # Args: none # Returns: "" # Desc: Reads the _layout file if it exists. # Side-FX: none # proc read__layout {} { global tkdesk env if [info exists tkdesk(layout_file)] { set lf $tkdesk(layout_file) } else { set lf $tkdesk(configdir)/_layout } if [file readable $lf] { dsk_debug "Reading $lf" set fd [open $lf] set tkdesk(layout) "" while 1 { set l [gets $fd] if {$l == ""} { break } else { lappend tkdesk(layout) $l } } catch {close $fd} if {$tkdesk(layout) == ""} { catch {puts stderr "TkDesk: $f corrupted, skipping"} unset tkdesk(layout) } } else { # start in new user's home directory set tkdesk(initdir) $env(HOME) } return } # # ----------------------------------------------------------------------------- # # Proc: read__annotations # Args: none # Returns: "" # Desc: Reads the _annotations file if it exists. # Side-FX: Sets the file annotations. # proc read__annotations {} { global tkdesk tkdesk_anno if [file readable $tkdesk(configdir)/_annotations] { dsk_debug "Reading $tkdesk(configdir)/_annotations" source_cfg $tkdesk(configdir)/_annotations } return } # ----------------------------------------------------------------------------- # # Proc: read__bookmarks # Args: none # Returns: "" # Desc: Reads the _bookmarks file if it exists. # Side-FX: Sets the bookmarks list. # proc read__bookmarks {} { global tkdesk set tkdesk(bookmarks) {} if [file readable $tkdesk(configdir)/_bookmarks] { dsk_debug "Reading $tkdesk(configdir)/_bookmarks" source_cfg $tkdesk(configdir)/_bookmarks } return } # # ============================================================================= # # ----------------------------------------------------------------------------- # # Proc: dsk_save_config # Args: none # Returns: "" # Desc: Saves the saveable configuration parameters to disk. # Side-FX: none # set dsk_save_config(busy) 0 proc dsk_save_config {{all 0}} { global tkdesk dsk_save_config if $dsk_save_config(busy) return set dsk_save_config(busy) 1 if {$tkdesk(autosave,history) || $all} save__history if {$tkdesk(autosave,layout) || $all} save__layout if {$tkdesk(autosave,annotations) || $all} save__annotations if {$tkdesk(autosave,bookmarks) || $all} save__bookmarks if {$tkdesk(autosave,options) || $all} save__options dsk_status "Ready." set dsk_save_config(busy) 0 return } # # ----------------------------------------------------------------------------- # # Proc: save__history # Args: none # Returns: "" # Desc: Saves the global path history to _history. # Side-FX: none # proc save__history {} { global tkdesk set err [catch {set fd [open $tkdesk(configdir)/_history w]}] if !$err { dsk_catch { dsk_status "Saving History Lists ..." foreach histobj $tkdesk(histlist) { puts $fd "# $histobj" set hist [$histobj get] for {set i [expr [llength $hist] - 1]} {$i > -1} {incr i -1} { puts $fd [lindex $hist $i] } } catch {close $fd} } } else { dsk_debug "Couldn't open $tkdesk(configdir)/_history for writing." } } # # ----------------------------------------------------------------------------- # # Proc: save__layout # Args: none # Returns: "" # Desc: Tries to save the window layout of TkDesk to _layout. # Side-FX: # proc save__layout {} { global tkdesk if [info exists tkdesk(layout_file)] { set lf $tkdesk(layout_file) } else { set lf $tkdesk(configdir)/_layout } set err [catch {set fd [open $lf w]}] if !$err { dsk_catch { dsk_status "Saving Layout ..." foreach class {dsk_FileViewer dsk_FileList dsk_DeskItem \ dsk_HistEntry} { set obj_list [itcl_info objects -class $class] foreach obj $obj_list { set top [$obj getToplevel] if ![winfo exists $top] continue switch $class { dsk_FileViewer - dsk_FileList { set state [wm state $top] if {$state == "normal" || $state == "iconic"} { set geom [_shift_geom $top] puts -nonewline $fd "$class \{[$obj curdir]\} " puts -nonewline $fd "$geom " if {$class == "dsk_FileViewer"} { puts -nonewline $fd "$state " puts $fd "[$obj cget num_lbs]" } else { puts $fd "$state" } } } dsk_DeskItem { puts -nonewline $fd "$class \{[$obj cget -file]\} " set geom [_shift_geom $top] puts $fd $geom } dsk_HistEntry { puts -nonewline $fd "$class " set geom [_shift_geom $top] puts $fd $geom } } } } foreach toplevel {dsk_jobs dsk_copy \ dsk_delete dsk_appbar dsk_find_annotation dsk_bgexec \ dsk_find_files periodic} { set geom "" if [winfo exists .$toplevel] { set geom [_shift_geom .$toplevel] } else { if [info exists tkdesk(geometry,$toplevel)] { set geom $tkdesk(geometry,$toplevel) } } if {$geom != ""} { if {$toplevel == "dsk_appbar"} { set geom [dsk_appbar_adjgeom $geom] puts $fd "Toplevel $toplevel [winfo exists .$toplevel] $geom $tkdesk(appbar,deffile)" } else { puts $fd "Toplevel $toplevel [winfo exists .$toplevel] $geom" } } } catch {close $fd} } } else { dsk_debug "Couldn't open $lf for writing." } } proc _shift_geom {top} { set geom [wm geometry $top] if {[string first "+-" $geom] > -1} { # shift the window into visible area set geom [split $geom x+] set w [lindex $geom 0] set h [lindex $geom 1] set x [lindex $geom 2] set y [lindex $geom 3] if {$x < 0} { if {abs($x) >= [winfo width $top]} { set sw [winfo screenwidth $top] while {$x < 0} {incr x $sw} } } if {$y < 0} { if {abs($y) >= [winfo height $top]} { set sh [winfo screenheight $top] while {$y < 0} {incr y $sh} } } set geom "${w}x${h}+${x}+${y}" } return $geom } # # ----------------------------------------------------------------------------- # # Proc: save__annotations # Args: none # Returns: "" # Desc: Saves the file annotations to _annotations. # Side-FX: none # proc save__annotations {} { global tkdesk tkdesk_anno if ![info exists tkdesk_anno] return set err [catch {set fd [open $tkdesk(configdir)/_annotations w]}] if !$err { dsk_catch { dsk_status "Saving File Annotations ..." foreach name [array names tkdesk_anno] { puts $fd "set tkdesk_anno([_make_fname_safe $name]) \\" puts $fd \{$tkdesk_anno($name)\} } catch {close $fd} } } else { dsk_debug "Couldn't open $tkdesk(configdir)/_annotations for writing." } } # ----------------------------------------------------------------------------- # # Proc: save__bookmarks # Args: none # Returns: "" # Desc: Saves the bookmark list to _bookmarks. # Side-FX: none # proc save__bookmarks {} { global tkdesk set err [catch {set fd [open $tkdesk(configdir)/_bookmarks w]}] if !$err { dsk_catch { dsk_status "Saving Bookmarks ..." puts $fd "set tkdesk(bookmarks) {" foreach bm $tkdesk(bookmarks) { puts $fd "\{$bm\}" } puts $fd "}" catch {close $fd} } } else { dsk_debug "Couldn't open $tkdesk(configdir)/_annotations for writing." } } # ----------------------------------------------------------------------------- # # Proc: save__options # Args: none # Returns: "" # Desc: Saves the settings of the "Options" menu to _options. # Side-FX: none # proc save__options {} { global tkdesk cb_tools set err [catch {set fd [open $tkdesk(configdir)/_options w]}] if !$err { dsk_catch { dsk_status "Saving Options ..." foreach var $tkdesk(confvars) { puts $fd "set tkdesk($var) $tkdesk($var)" } puts $fd "set tkdesk(autosave,history) $tkdesk(autosave,history)" puts $fd "set tkdesk(autosave,layout) $tkdesk(autosave,layout)" puts $fd "set tkdesk(autosave,annotations) $tkdesk(autosave,annotations)" puts $fd "set tkdesk(autosave,bookmarks) $tkdesk(autosave,bookmarks)" puts $fd "set tkdesk(autosave,options) $tkdesk(autosave,options)" puts $fd "set cb_tools(balloon_help) $cb_tools(balloon_help)" catch {close $fd} } } else { dsk_debug "Couldn't open $tkdesk(configdir)/_options for writing." } } # # ============================================================================= # # # ----------------------------------------------------------------------------- # # Proc: dsk_restore_layout # Args: none # Returns: "" # Desc: Tries to restore the layout of the last session. # Side-FX: Creates several windows, depending on the file _layout. # set tkdesk(have_window) 0 proc dsk_restore_layout {} { global tkdesk set tkdesk(restoring_layout) 1 if [info exists tkdesk(layout)] { dsk_debug "Restoring layout..." dsk_debug "tkdesk(ayout): $tkdesk(layout)" foreach l $tkdesk(layout) { set class [lindex $l 0] switch $class { dsk_FileViewer { set tkdesk(have_window) 1 dsk_progress "Creating a file browser..." set win .fv[dsk_FileViewer :: id] set nlb [lindex $l 4] if {$nlb == ""} { set nlb $tkdesk(num_lbs) } if [info exists tkdesk(user,startdir)] { set dir $tkdesk(user,startdir) unset tkdesk(user,startdir) } else { set dir [lindex $l 1] } dsk_FileViewer $win -dir $dir \ -num_lbs $nlb -dontmap 1 set win [$win getToplevel] #wm withdraw $win wm geometry $win [lindex $l 2] if {[lindex $l 3] == "iconic" || $tkdesk(iconic)} { update wm iconify $win } else { wm deiconify $win tkwait visibility $win catch "lower $win .dsk_welcome" update } } dsk_FileList { set tkdesk(have_window) 1 dsk_progress "Creating a file list..." set win .dfl[dsk_FileList :: id] dsk_FileList $win -dir [lindex $l 1] -dontmap 1 set win [$win getToplevel] #wm withdraw $win wm geometry $win [lindex $l 2] if {[lindex $l 3] == "iconic" || $tkdesk(iconic)} { update wm iconify $win } else { wm deiconify $win tkwait visibility $win catch "lower $win .dsk_welcome" update } } dsk_DeskItem { set itemname [lindex $l 1] if {![file exists $itemname]} { continue } if [file isdirectory $itemname] { set tkdesk(have_window) 1 } dsk_progress "Creating a desk item..." set win .di[dsk_DeskItem :: id] dsk_DeskItem $win -file $itemname -dontmap 1 set win [$win getToplevel] #wm withdraw $win set g [split [lindex $l 2] x+] wm geometry $win +[lindex $g 2]+[lindex $g 3] wm deiconify $win tkwait visibility $win catch "lower $win .dsk_welcome" update } dsk_HistEntry { ot_maplist $l type geom set tkdesk(geometry,hist_entry) $geom } Toplevel { set df "" ot_maplist $l type func posted geom df if {$func == "dsk_appbar"} { set tkdesk(have_window) 1 if {$df != ""} { set tkdesk(appbar,deffile) $df if {$df != "AppBar"} { read_AppBar $df } } } set tkdesk(geometry,$func) $geom if $posted { eval $func } } } } } set tkdesk(restoring_layout) 0 return } # # ----------------------------------------------------------------------------- # # Proc: dsk_reread_config # Args: none # Returns: "" # Desc: Rereads the config files. # Side-FX: Does a dsk_save_config. # proc dsk_reread_config {{cfgfile ""}} { global tkdesk if {$cfgfile != ""} { dsk_busy dsk_status "Rereading $cfgfile..." switch $cfgfile { AppBar { if [winfo exists .dsk_appbar] { dsk_appbar close set open_appbar 1 } cb_image !reset read_AppBar if [info exists open_appbar] { dsk_appbar } } ButtonBar { cb_image !reset read_ButtonBar foreach br [itcl_info objects -class dsk_FileViewer] { $br _button_bar } foreach fl [itcl_info objects -class dsk_FileList] { $fl _button_bar } } Commands { read_Commands dsk_rebuild_cmdmenus } Directories { read_Directories dsk_rebuild_dirmenus } FileTags { #dsk_reread_config dsk_FileListbox :: tag reset cb_image !reset read_FileTags foreach fl [itcl_info objects -class dsk_FileListbox] { if [winfo exists [$fl getFrame]] { $fl refresh } } foreach di [itcl_info objects -class dsk_DeskItem] { if [winfo exists [$di getToplevel]] { $di refresh } } } Local { read_Local } Popups { read_Popups } Preferences { read_Preferences } Sounds { read_Sounds } System { read_System } default { # assume unknown config file contains an application bar if [winfo exists .dsk_appbar] { dsk_appbar close set open_appbar 1 } cb_image !reset read_AppBar $cfgfile if [info exists open_appbar] { dsk_appbar } } } dsk_status "Ready." dsk_lazy return } else { foreach cfg $tkdesk(configfiles) { dsk_reread_config $cfg } return } # OBSOLETE - this is no longer executed # # Reread all config files: # # foreach obj [itcl_info objects -class dsk_Editor] { # if {[$obj close_win] == "cancel"} { # return # } # } # # dsk_busy # # dsk_save_config # dsk_status "Rereading configuration..." # dsk_read_config # # dsk_debug "Deleting old windows..." # foreach class {dsk_FileViewer dsk_FileList} { # foreach obj [itcl_info objects -class $class] { # $obj delete # } # $class :: id reset # } # dsk_Editor :: id reset # foreach topname {dsk_ask_exec dsk_ask_dir dsk_jobs dsk_copy \ # dsk_delete dsk_appbar dsk_find_annotation} { # catch "destroy .$topname" # } # # dsk_restore_layout # # dsk_lazy # return } # # ----------------------------------------------------------------------------- # # Proc: dsk_persave_config # Args: none # Returns: "" # Desc: Saves the configuration (i.e. history and layout) periodically. # Side-FX: none # proc dsk_persave_config {} { global tkdesk if {[itcl_info objects -class dsk_FileViewer] != ""} { dsk_save_config after [expr $tkdesk(update,config) * 60000] dsk_persave_config } } # # ----------------------------------------------------------------------------- # # Proc: _add_cmd_to_menu # Args: menu - name of menu widget # cmd - list of commands (initially from the Commands cfgfile) # Returns: "" # Desc: Adds new menu entries to the given menu to invoke commands. # Side-FX: none # set tkdesk(_cmdmenu,cnt) 0 proc _add_cmd_to_menu {menu cmd} { global tkdesk if {[llength $cmd] == 2} { set command [string_replace [lindex $cmd 1] \" \\\"] $menu add command -label [lindex $cmd 0] \ -command "cd \[dsk_active dir\] ;\ set tkdesk(error_source) {Commands} ;\ eval \[_expand_pc [list $command]\];\ set tkdesk(error_source) {} ;\ cd ~" #-font $tkdesk(font,file_lbs) } elseif {[llength $cmd] == 1} { if {$cmd == "."} { $menu config -tearoff 1 } else { $menu add separator } } else { incr tkdesk(_cmdmenu,cnt) set m ${menu}.c$tkdesk(_cmdmenu,cnt) $menu add cascade -label [lindex $cmd 0] -menu $m #-font $tkdesk(font,file_lbs) catch "destroy $m" menu $m set cmd [lreplace $cmd 0 0] foreach c $cmd { _add_cmd_to_menu $m $c } } } # # ----------------------------------------------------------------------------- # # Proc: dsk_rebuild_cmdmenus # Args: none # Returns: "" # Desc: Rebuilds the Commands menus of all Viewers and Lists. # Side-FX: none # proc dsk_rebuild_cmdmenus {} { global tkdesk set tkdesk(_cmdmenu,cnt) 0 if [info exists tkdesk(commands)] { foreach class "dsk_FileViewer dsk_FileList" { foreach t [itcl_info objects -class $class] { if {$class == "dsk_FileViewer"} { set m [$t getToplevel].fMenu.mbCmds.menu } else { set m [$t getToplevel].fMenu.mbOthers.menu.cmd } set fe [expr [$m cget -tearoff] ? 12 : 11] catch "$m delete $fe last" foreach cmd $tkdesk(commands) { if {[llength $cmd] > 1} { _add_cmd_to_menu $m $cmd } else { if {$cmd == "."} { $menu config -tearoff 1 } else { $menu add separator } } } $m add separator $m add cascade -label "History" -menu $m.mhe } } } } # # ----------------------------------------------------------------------------- # # Proc: _add_dir_to_menu # Args: menu - name of menu widget # mdir - list of directories # Returns: "" # Desc: Adds directory menu entries to the given menu. # Side-FX: none # set tkdesk(_dirmenu,cnt) 0 proc _add_dir_to_menu {this menu mdir} { global tkdesk if {[llength $mdir] == 1} { if {[string index $mdir 0] == "*"} { set mdir [string range $mdir 1 end] incr tkdesk(_dirmenu,cnt) set m ${menu}.cas$tkdesk(_dirmenu,cnt) catch {destroy $m} menu $m -postcommand "dsk_casdirs $mdir $m 1" $menu add cascade -label "$mdir (*)" -menu $m \ -font [cb_font $tkdesk(font,file_lbs)] } elseif {[string index $mdir 0] == "&"} { set mdir [string range $mdir 1 end] incr tkdesk(_dirmenu,cnt) set m ${menu}.cas$tkdesk(_dirmenu,cnt) catch {destroy $m} menu $m -postcommand "dsk_casdirs $mdir $m 1 {} 1" $menu add cascade -label "$mdir (&)" -menu $m \ -font [cb_font $tkdesk(font,file_lbs)] } else { $menu add command -label $mdir \ -command "$this config -dir $mdir" \ -font [cb_font $tkdesk(font,file_lbs)] } } else { incr tkdesk(_dirmenu,cnt) set m ${menu}.d$tkdesk(_dirmenu,cnt) $menu add cascade -label [lindex $mdir 0] -menu $m \ -font [cb_font $tkdesk(font,file_lbs)] catch "destroy $m" menu $m bind $m " set tkdesk(menu,control) 0 [bind Menu ]" bind $m " set tkdesk(menu,control) 1 [bind Menu ]" foreach d $mdir { if {$d == "-"} { $m add separator } elseif {$d == "."} { $m config -tearoff 1 } else { _add_dir_to_menu $this $m $d } } } } # # ----------------------------------------------------------------------------- # # Proc: dsk_rebuild_dirmenus # Args: none # Returns: "" # Desc: Rebuilds the Directories menus of all Viewers and Lists. # Side-FX: none # proc dsk_rebuild_dirmenus {} { global tkdesk env set tkdesk(_dirmenu,cnt) 0 if [info exists tkdesk(directories)] { foreach class "dsk_FileViewer dsk_FileList" { foreach t [itcl_info objects -class $class] { set m [$t getToplevel].fMenu.mbDirs.menu set fe [expr [$m cget -tearoff] ? 11 : 10] catch "$m delete $fe last" foreach mdir $tkdesk(directories) { if {$mdir == "-"} { $m add separator } elseif {$mdir == "."} { $m config -tearoff 1 } else { _add_dir_to_menu $t $m $mdir } } $m add separator $m add cascade -label "Trees" -menu ${m}.fs $m add cascade -label "History" -menu $m.mhd } } } } tkdesk-2.0/tcldesk/copy.tcl0100644000175000007640000005113410020457430014033 0ustar jccjcc# ============================================================================= # # File: copy.tcl # Project: TkDesk # # Started: 22.10.94 # Changed: 22.10.94 # Author: cb # # Description: Implements classes and procs for file operations like # copy, move, delete, file info and disk usage (and others). # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_copy {{files ""} {dest ""}} #s proc dsk_copy_dest_popup {entry x y} #s proc dsk_copy_action {cmd} #s proc _dsk_hmenu {menu entry} #s proc _dsk_dmenu {menu entry} #s proc dsk_ddcopy {files dest} # # ----------------------------------------------------------------------------- # # ----------------------------------------------------------------------------- # # Proc: dsk_copy # Args: files (opt.) list of files to copy, move etc. # dest (opt.) destination directory # Returns: "" # Desc: Creates a window for copying, moving and linking the selected # files or $files (opt. to $dest). # Side-FX: Rereads the concerned file listboxes. # if ![info exists tkdesk(geometry,dsk_copy)] { set tkdesk(geometry,dsk_copy) "" } global dsk_copy set dsk_copy(num_cas_dirs) 0 ;# number of cascaded dir menus set dsk_copy(flist) "" ;# initial file list for dsk_copy set dsk_copy(fcnt) 0 ;# counter: that many files have been processed set dsk_copy(fmax) 0 ;# number of files to process - 1 set dsk_copy(ldest) "" ;# last destination global dsk_copy_other set dsk_copy_other "" proc dsk_copy {{files ""} {dest ""}} { global tkdesk dsk_copy dsk_copy_other if {$files == ""} { set files [dsk_active sel] } set dsk_copy(fcnt) 0 set dsk_copy(fmax) [expr [llength $files] - 1] set dsk_copy(flist) $files set dsk_copy(all) $tkdesk(all_files) set t .dsk_copy if [winfo exists $t] { destroy $t } toplevel $t # ---- Source Files frame $t.ff -bd 1 -relief raised pack $t.ff -fill both -expand yes frame $t.fff pack $t.fff -in $t.ff -fill both -expand yes \ -padx $tkdesk(pad) -pady $tkdesk(pad) entry $t.eFile -width 40 -bd 2 -relief sunken pack $t.eFile -in $t.fff -side bottom -fill x -expand yes -ipady 2 $t.eFile insert end [lindex $dsk_copy(flist) 0] $t.eFile icursor end $t.eFile xview end bind $t.eFile "focus $t.eDest" bind $t.eFile {set dummy x ; unset dummy} bind $t.eFile <3> {dsk_popup %W [%W get] %X %Y} cb_bindForCompletion $t.eFile blt_drag&drop target $t.eFile handler file "dd_handle_text $t.eFile %v 1" label $t.lFile if {$files == ""} { $t.lFile config -text "File (no files selected):" } else { $t.lFile config \ -text "File ([expr $dsk_copy(fcnt) + 1] of [expr $dsk_copy(fmax) + 1]):" } pack $t.lFile -in $t.fff -side left if {$dsk_copy(fmax) > 0} { checkbutton $t.cbAll -text "all selected files" \ -padx $tkdesk(pad) -relief flat -variable dsk_copy(all) pack $t.cbAll -in $t.fff -side right } # ---- Destination frame $t.fd -bd 1 -relief raised pack $t.fd -fill both -expand yes frame $t.fdf pack $t.fdf -in $t.fd -fill both -expand yes \ -padx $tkdesk(pad) -pady $tkdesk(pad) entry $t.eDest -width 40 -bd 2 -relief sunken pack $t.eDest -in $t.fdf -side bottom -fill x -expand yes -ipady 2 if {$dest == ""} { $t.eDest insert end $dsk_copy(ldest) } else { $t.eDest insert end $dest } $t.eDest icursor end $t.eDest xview end bind $t.eDest <3> {dsk_copy_dest_popup %W %X %Y} bind $t.eDest "focus $t.eFile" bind $t.eDest "dsk_copy_action copy" cb_bindForCompletion $t.eDest blt_drag&drop target $t.eDest handler file "dd_handle_text $t.eDest %v 1" label $t.lDest -text "Destination:" pack $t.lDest -in $t.fdf -side left frame $t.fdlm pack $t.fdlm -in $t.fdf -side right menubutton $t.mbDir -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/slash.xbm \ -menu $t.mbDir.menu pack $t.mbDir -in $t.fdlm -side left -ipadx 2 -ipady 2 menu [set m $t.mbDir.menu] \ -postcommand "_dsk_dmenu $t.mbDir.menu $t.eDest" # add dummy entry to work around bug in pre Tk 4.0p2: $m add command -label "dummy" menubutton $t.mbHist -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $t.mbHist.menu pack $t.mbHist -in $t.fdlm -side left -ipadx 2 -ipady 2 menu $t.mbHist.menu -postcommand "_dsk_hmenu $t.mbHist.menu $t.eDest" # add dummy entry to work around bug in pre Tk 4.0p2: $t.mbHist.menu add command -label "dummy" # ---- Buttons frame $t.fb -bd 1 -relief raised pack $t.fb -fill x cb_button $t.bCopy -text " Copy " -command "dsk_copy_action copy" \ -default 1 cb_button $t.bMove -text " Move " -command "dsk_copy_action move" #button $t.bLink -text " Link " -command "dsk_copy_action link" #button $t.bSymLink -text " SymLink " -command "dsk_copy_action symlink" cb_button $t.bOther -textvar dsk_copy_other \ -command "dsk_copy_action other" menubutton $t.mbOther -text " Other... " -menu [set m $t.mbOther.m] \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -bd 2 -relief raised menu $m foreach me $tkdesk(fileops,popup) { if {$me == "-"} { $m add separator } else { set l [lindex $me 0] set c [lindex $me 1] $m add command -label $l -command "set dsk_copy_other [list $l]" if {$dsk_copy_other == ""} { set dsk_copy_other $l } } } cb_button $t.bCancel -text " Cancel " -command { set dsk_copy(ldest) [.dsk_copy.eDest get] set tkdesk(geometry,dsk_copy) [wm geometry .dsk_copy] destroy .dsk_copy } pack $t.bCancel -in $t.fb -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 if {$dsk_copy(fmax) > 0} { button $t.bSkip -text " Skip " -command "dsk_copy_action skip" pack $t.bSkip -in $t.fb -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 } #pack $t.bCopy $t.bMove $t.bLink $t.bSymLink pack $t.bCopy $t.bMove $t.bOther \ -in $t.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 pack $t.mbOther -in $t.fb -side left -ipadx 3 -ipady 3 #bind $t "+focus $t.eFile" #focus $t.eFile bind $t.eFile "$t.bCancel.button invoke" bind $t.eDest "$t.bCancel.button invoke" wm minsize $t 486 167 wm title $t "Copy, Move, Link Files" wm protocol $t WM_DELETE_WINDOW {.dsk_copy.bCancel.button invoke} dsk_place_window $t dsk_copy 486x162 focus -force $t.eFile } proc dsk_copy_dest_popup {entry x y} { global tkdesk set dest [$entry get] if {![file isdirectory $dest]} { set dest [_make_path_valid $dest /] } if [file isdirectory $dest] { set this .dsk_copy set m $this-ppop catch {destroy $m} menu $m bind $m " set tkdesk(menu,control) 0 [bind Menu ]" bind $m " set tkdesk(menu,control) 1 [bind Menu ]" set p [cb_tilde [string trimright $dest "/"] collapse] if {$p != ""} { set op $p while {1} { set p [file dirname $p] $m add command -label $p -command \ "$entry delete 0 end; $entry insert end \[list $p\]" \ -font [cb_font $tkdesk(font,file_lbs)] if {$p == "/"} break } $m add separator set p $op set i 0 while {1} { set p [file dirname $p] set cm $m.cas$i $m add cascade -label $p -menu $cm -font [cb_font $tkdesk(font,file_lbs)] catch {destroy $cm} menu $cm -postcommand "dsk_casdirs $p $cm 1 \ \{$entry delete 0 end; $entry insert end \[list %d\]\}" incr i if {$p == "/"} break } update tk_popup $m $x $y } } else { dsk_errbell } } proc dsk_copy_action {cmd} { global dsk_copy tkdesk tkdesk_anno dsk_copy_other dsk_debug "dsk_copy_action $cmd" set t .dsk_copy #cd [$tkdesk(active_viewer) curdir] set tlist "" if $dsk_copy(all) { if [winfo exists $t.eFile] { set tlist [list [$t.eFile get]] if {$dsk_copy(fcnt) < $dsk_copy(fmax)} { set tlist [concat $tlist [lrange $dsk_copy(flist) \ [expr $dsk_copy(fcnt) + 1] $dsk_copy(fmax)]] } } else { set tlist [lrange $dsk_copy(flist) $dsk_copy(fcnt) $dsk_copy(fmax)] } } if {($tlist == "" || !$dsk_copy(all)) && [winfo exists $t.eFile]} { set tlist [list [$t.eFile get]] set err [catch {set tlist [glob $tlist]}] if $err { set tlist [list [$t.eFile get]] } } if {$tlist == ""} { dsk_bell cb_info "Nothing to $cmd!" return } set anum [llength $tlist] dsk_debug " anum = $anum" set alist "" foreach f $tlist { catch {set f [cb_tilde $f expand]} if {[string index $f 0] != "/" && ![regexp {:/} $f]} { set f [dsk_active dir]$f } set err [catch {set fl [glob [_make_fname_safe $f]]}] if !$err { foreach fe $fl { lappend alist $fe } } elseif {[regexp {:/} $f]} { lappend alist $f } else { dsk_errbell cb_error "$f: no match!" } } if {$alist == ""} { return } set dest [$t.eDest get] if {$dest == "" && $cmd != "skip"} { cb_info "Please fill in the destination first!" return } catch {set dest [cb_tilde $dest expand]} if {[string index $dest 0] != "/"} { set dest [dsk_active dir]$dest } dsk_debug " alist = \{$alist\}" if [dsk_on_rofs $dest] { dsk_errbell cb_error "The target filesystem is mounted read-only." return } # check permissions: if {$dest != "/"} { set dest [string trimright $dest "/"] } if {![file writable $dest] && [file exists $dest]} { if {$cmd != "other"} { if [file isdirectory $dest] { cb_error "$dest:\ndirectory not writable." } else { cb_error "[file tail $dest]:\nfile not writable." } return } else { if [file isdirectory $dest] { set ret [cb_yesno "$dest:\ndirectory not writable.\nContinue anyway?"] } else { set ret [cb_yesno "[file tail $dest]:\nfile not writable.\nContinue anyway?"] } if {$ret == 1} return } } foreach file $alist { if {$cmd == "move"} { if ![dsk_check_perm $file] { cb_error "[file tail $file]:\nYou don't have permission to move this item." return } } elseif {$cmd == "copy"} { if {![file readable $file] && ![regexp {:/} $file]} { cb_error "[file tail $file]:\nnot readable." return } } } # check for existing files in destination dir: if {[file exists $dest] && !$tkdesk(overwrite_always) && $cmd != "skip" \ && $cmd != "other"} { if ![file isdirectory $dest] { if {[cb_dialog $t-ED "File exists" "$dest already exists!" \ questhead 0 "Overwrite" " Skip "] == 1} { set cmd skip } } elseif {[llength $alist] == 1} { if {$cmd == "move"} { if [dsk_on_rofs $alist] { # all files in $alist live on the same file system dsk_errbell cb_error "The source filesystem is mounted read-only." return } } set dfile [string trimright $dest /]/[file tail $alist] if [file exists $dfile] { if {[cb_dialog $t-ED "File exists" "$dfile already exists!" \ questhead 0 "Overwrite" " Skip "] == 1} { set cmd skip } } } else { # "all selected files" has been selected set tlist $alist set dir [string trimright $dest /] set rofs_checked 0 foreach file $tlist { if {$cmd == "move" && !$rofs_checked} { if [dsk_on_rofs $file] { # all files in $alist live on the same file system dsk_errbell cb_error "The source filesystem is mounted read-only." return } set rofs_checked 1 } set dfile $dir/[file tail $file] if [file exists $dfile] { set ret [cb_dialog $t-ED "File exists" \ "$dfile already exists!" questhead 0 \ "Overwrite" "Overwrite all" \ " Skip " " Cancel "] switch $ret { 1 { break } 2 { set i [lsearch $alist $file] if {$i > -1} { set alist [lreplace $alist $i $i] } else { cb_error "?? Couldn't find $file !?" } } 3 { catch {$t.bCancel.button invoke} return } } } } if {$alist == ""} { catch {$t.bCancel.button invoke} return } set anum [llength $alist] } } if $dsk_copy(all) { catch {$t.bCancel.button invoke} } if {$anum > 1} {set fll items} {set fll item} dsk_debug " Files: $anum" switch $cmd { skip { if $dsk_copy(all) return incr dsk_copy(fcnt) $t.lFile config -text "File ([expr $dsk_copy(fcnt) + 1] of [expr $dsk_copy(fmax) + 1]):" if {$dsk_copy(fcnt) > $dsk_copy(fmax)} { catch {$t.bCancel.button invoke} return } $t.eFile delete 0 end $t.eFile insert end [lindex $dsk_copy(flist) $dsk_copy(fcnt)] $t.eFile icursor end $t.eFile xview end return } copy { #dsk_busy dsk_sound dsk_copying set out [dsk_bgexec "$tkdesk(cmd,cp) $alist \"$dest\"" \ "Copying $anum $fll..."] #dsk_lazy } move { #dsk_busy dsk_sound dsk_moving if ![file isdirectory $alist] { set out [dsk_bgexec "$tkdesk(cmd,mv) $alist \"$dest\"" \ "Moving $anum $fll..."] } else { # try to hide different file systems from user: file stat [lindex $alist 0] s1 set s2(dev) 0 catch "file stat $dest s2" if !$s2(dev) { catch {file stat [file dirname $dest] s2} } if {$s1(dev) == $s2(dev) || !$s2(dev) \ || ([llength $alist] == 1 \ && ![file isdirectory [lindex $alist 0]])} { set out [dsk_bgexec "$tkdesk(cmd,mv) $alist \"$dest\"" \ "Moving $anum $fll..."] } else { set out [dsk_bgexec "$tkdesk(cmd,cp) $alist $dest" \ "Move: Copying $anum $fll..."] if {$out != "error" && $out != "break"} { set out [dsk_bgexec "$tkdesk(cmd,rm) $alist" \ "Move: Deleting $anum $fll..."] } } } #dsk_lazy } other { #dsk_busy set c "" foreach e $tkdesk(fileops,popup) { if {[lindex $e 0] == $dsk_copy_other} { set c [subst -nocommands [lindex $e 1]] break } } set err [catch {eval [_expand_pc $c \ [lindex $alist 0] $alist $dest]} errmsg] if $err { dsk_errbell cb_error $errmsg set out error } else { set out ok } #dsk_lazy } link { #dsk_busy dsk_sound dsk_linking set out [dsk_bgexec "$tkdesk(cmd,ln) $alist \"$dest\"" \ "Linking $anum $fll..."] #dsk_lazy } symlink { #dsk_busy dsk_sound dsk_symlinking set out [dsk_bgexec "$tkdesk(cmd,symln) $alist \"$dest\"" \ "Linking $anum $fll symbolically..."] #dsk_lazy } untar { #dsk_busy set f $alist ;# this can only be just one file if {[string match "*.tar.gz" $f] || \ [string match "*.tgz" $f] || \ [string match "*.taz" $f] || \ [string match "*.tar.Z" $f]} { dsk_sound dsk_untaring set out [dsk_bgexec "gzip -cd $f | tar xf - -C \"$dest\"" \ "Untaring [file tail $f]..."] } elseif {[string match "*.tar" $f]} { dsk_sound dsk_untaring set out [dsk_bgexec "tar xf $f -C \"$dest\"" \ "Untaring [file tail $f]..."] } else { dsk_errbell cb_error "This file doesn't look like a tar file." } #dsk_lazy } default { cb_error "dsk_copy_action: unknown cmd ($cmd)" return } } if {$out != "error"} { # copy annotations and adjust desk items: set dest [string trimright $dest "/"] foreach f $alist { if [info exists tkdesk_anno($f)] { if [file isdirectory $dest] { set tkdesk_anno($dest/[file tail $f]) $tkdesk_anno($f) } else { set tkdesk_anno($dest) $tkdesk_anno($f) } if {$cmd == "move"} { unset tkdesk_anno($f) } } if {$cmd == "move"} { if [file isdirectory $dest] { dsk_DeskItem :: move $f $dest/[file tail $f] } else { dsk_DeskItem :: move $f $dest } } } if {$cmd == "move"} { dsk_refresh "$alist $dest" } else { dsk_refresh "$dest" } if !$dsk_copy(all) { incr dsk_copy(fcnt) if {$dsk_copy(fcnt) > $dsk_copy(fmax)} { catch {$t.bCancel.button invoke} return } $t.eFile delete 0 end $t.eFile insert end [lindex $dsk_copy(flist) $dsk_copy(fcnt)] $t.eFile icursor end $t.eFile xview end } } } proc _dsk_hmenu {menu entry} { global tkdesk catch "$menu delete 0 last" if $tkdesk(sort_history) { set l [lsort [dir_history get]] } else { set l [dir_history get] } set ne 0 foreach dir $l { $menu add command -label $dir \ -command "$entry delete 0 end;\ $entry insert end \[list $dir\]" \ -font [cb_font $tkdesk(font,entries)] incr ne if {$ne > $tkdesk(_max_menu_entries)} { set om $menu $menu add cascade -label "More..." \ -menu [set menu $menu.c$ne] catch {destroy $menu}; menu $menu; set ne 0 foreach b [bind $om] {bind $menu $b [bind $om $b]} } } } proc _dsk_dmenu {menu entry} { global tkdesk _dsk_dmenu catch "$menu delete 0 last" set _dsk_dmenu(num_cas_dirs) 0 if [info exists tkdesk(directories)] { foreach mdir $tkdesk(directories) { if {$mdir == "-"} { $menu add separator } else { _dsk_add_dir_to_menu $menu $mdir $entry } } } } # # ----------------------------------------------------------------------------- # # Proc: dsk_ddcopy # Args: files - list of files # dest - destination path # Returns: "" # Desc: Is called when files have been dropped onto a file listbox. # If quick_dragndrop is set, the files are copied or moved # (in case the Control-Key was pressed) without asking. # Else dsk_copy gets called with the arguments of dsk_ddcopy. # Side-FX: none # proc dsk_ddcopy {files dest} { global tkdesk set anum [llength $files] if {$anum == 1} { set fll "Item" } else { set fll "Items" } if {[string first "$tkdesk(configdir)/.shelf/" $dest] > -1} { set out [dsk_bgexec "$tkdesk(cmd,symln) $files $dest" \ "Linking $anum $fll symbolically..."] dsk_refresh $dest return } if !$tkdesk(quick_dragndrop) { dsk_copy $files $dest return } else { if {[llength $files] == 1} { if {[file dirname $files] == [string trimright $dest /] || \ $files == [string trimright $dest /]} { # avoid copying of files to themselves return } } set dir [string trimright $dest /] set rofs_checked 0 foreach file $files { if {!$tkdesk(file_lb,control) && !$rofs_checked} { if [dsk_on_rofs $file] { # all files in $alist live on the same file system dsk_errbell cb_error "The source filesystem is mounted read-only." return } set rofs_checked 1 } set dfile $dir/[file tail $file] if [file exists $dfile] { set ret [cb_dialog .ddcopy-ED "File exists" \ "$dfile already exists!" questhead 0 \ "Overwrite" "Overwrite all" " Skip "] if {$ret == 1} { break } elseif {$ret == 2} { set i [lsearch $files $file] if {$i > -1} { set files [lreplace $files $i $i] } else { cb_error "?? Couldn't find $file !?" } } } } if {$files == ""} { return } if $tkdesk(file_lb,control) { # copy files dsk_sound dsk_copying set out [dsk_bgexec "$tkdesk(cmd,cp) $files $dest" \ "Copying $anum $fll..."] } else { # move files # try to hide different file systems from user: dsk_sound dsk_moving file stat [lindex $files 0] s1 set s2(dev) 0 catch "file stat $dest s2" if {$s1(dev) == $s2(dev) || !$s2(dev) \ || ([llength $files] == 1 \ && ![file isdirectory [lindex $files 0]]) } { set out [dsk_bgexec "$tkdesk(cmd,mv) $files $dest" \ "Moving $anum $fll..."] } else { set out [dsk_bgexec "$tkdesk(cmd,cp) $files $dest" \ "Move: Copying $anum $fll..."] if {$out != "error" && $out != "break"} { set out [dsk_bgexec "$tkdesk(cmd,rm) $files" \ "Move: Deleting $anum $fll..."] } } } if {$out != "error"} { if !$tkdesk(file_lb,control) { dsk_refresh "$files $dest" } else { dsk_refresh "$dest" } } } } tkdesk-2.0/tcldesk/cpanels.tcl0100644000175000007640000003766210037122617014524 0ustar jccjcc# ============================================================================= # # File: cpanels.tcl # Project: TkDesk # # Started: 30.04.97 # Changed: 30.04.97 # Author: cb # # Description: Implements the configuration panels for colors, fonts, # icons and sounds. # # Copyright (C) 1996, 1997 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_config_panel {which text_widget} #s proc dsk_config_panel_return {which insert {text_widget ""}} #s proc _dsk_cpanel_retrieveX {which offset maxBytes} #s proc _dsk_cpanel_color {frame default} #s proc _dsk_cpanel_color_set {w dummy} #s proc _dsk_cpanel_font {frame def} #s proc _dsk_cpanel_font_reread {entry lbox {def ""}} #s proc _dsk_cpanel_font_set {lbox sample} #s proc _dsk_cpanel_icon {f def} #s proc _dsk_cpanel_icon_list_popup {dlb x y} #s proc _dsk_cpanel_icon_path_popup {entry x y} #s proc _dsk_cpanel_icon_reread {frame} #s proc _dsk_cpanel_icon_createimg {dlb line file} # # ============================================================================= proc dsk_config_panel {which {text_widget ""}} { global tkdesk dsk_config_panel if {$text_widget != ""} { if {[$text_widget tag nextrange sel 1.0 end] != ""} { set def [subst [$text_widget get sel.first sel.last]] } else { set def "" } } else { set def "" } set t .cpanel-$which if [winfo exists $t] { wm deiconify $t raise $t return } # create panel toplevel toplevel $t wm withdraw $t frame $t.fTop -bd 1 -relief raised pack $t.fTop -fill both -expand yes # create lower part: frame $t.fBot -bd 1 -relief raised pack $t.fBot -fill x button $t.bOK -text "Insert" -width 7 -command \ "dsk_config_panel_return $which 1 $text_widget" button $t.bCopy -text "Select" -width 7 -command \ "dsk_config_panel_return $which 2 $text_widget" button $t.bCancel -text "Close" -width 7 -command \ "dsk_config_panel_return $which 0" pack $t.bOK $t.bCopy $t.bCancel -in $t.fBot -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 # jump to subroutines to build and handle the upper part: set dsk_config_panel($which,sel) $def set grid 0 set defgeom "" switch $which { colors { _dsk_cpanel_color $t.fTop $def wm resizable $t 0 0 set title "Choose a Color" } fonts { if ![dsk_auto_execok xlsfonts] { destroy $t cb_error "Couldn't execute xlsfonts!" return } _dsk_cpanel_font $t.fTop $def update idletasks pack propagate $t.fTop 0 set title "Choose a Font" set grid 1 set defgeom 60x10 } icons { _dsk_cpanel_iconsnd icons $t.fTop $def set title "Choose an Icon" set grid 1 set defgeom 20x20 } sounds { _dsk_cpanel_iconsnd sounds $t.fTop $def set title "Choose a Sound" set grid 1 set defgeom 20x15 } } # make panel visible update idletasks wm title $t $title wm protocol $t WM_DELETE_WINDOW "dsk_config_panel_return $which 0" dsk_place_window $t "cpanel-$which" $defgeom $grid wm deiconify $t } proc dsk_config_panel_return {which insert {text_widget ""}} { global dsk_config_panel if {$insert == 1} { if [winfo exists $text_widget] { set sel $dsk_config_panel($which,sel) if {$sel != ""} { if {[$text_widget tag nextrange sel 1.0 end] != ""} { cb_Text_change_callback $text_widget delete char $text_widget delete sel.first sel.last } cb_Text_change_callback $text_widget insert $sel $text_widget insert insert $sel $text_widget see insert } } else { dsk_errbell cb_error {The associated window has been deleted. Please copy and paste the chosen value using the "Select" button.} } } elseif {$insert == 2} { selection handle .cpanel-$which "_dsk_cpanel_retrieveX $which" selection own .cpanel-$which } else { destroy .cpanel-$which } } proc _dsk_cpanel_retrieveX {which offset maxBytes} { global dsk_config_panel return $dsk_config_panel($which,sel) } proc _dsk_cpanel_color {frame default} { global tkdesk if {$default == ""} { set default $tkdesk(color,background) } tk_chooseColor -parent $frame -initialcolor $default } proc _dsk_cpanel_color_set {w dummy} { global _dsk_cpanel_color set _dsk_cpanel_color(color) [format \#%02x%02x%02x \ $_dsk_cpanel_color(red) \ $_dsk_cpanel_color(green) \ $_dsk_cpanel_color(blue)] $w config -bg $_dsk_cpanel_color(color) } proc _dsk_cpanel_font {frame def} { global tkdesk _dsk_cpanel_font frame [set f $frame.fmask] pack $f -fill x label $f.lMask -text "Mask:" entry $f.eMask -bd 2 -relief sunken set entry $f.eMask $entry insert end "*" pack $f.lMask -side left -padx $tkdesk(pad) -pady $tkdesk(pad) pack $f.eMask -side left -fill x -padx $tkdesk(pad) -pady $tkdesk(pad) \ -expand yes -ipady 2 frame [set f $frame.flist] pack $f -fill both -expand yes listbox $f.lbox -bd 2 -relief sunken -selectmode browse \ -font [cb_font $tkdesk(font,text)] -width 40 -height 5 \ -exportselection false -yscroll "$f.sb set" \ -takefocus 0 -highlightthickness 0 -setgrid 1 set lbox $f.lbox bind $lbox "_dsk_cpanel_font_set $lbox $frame.lSample" scrollbar $f.sb -orient vertical -command "$f.lbox yview" pack $f.lbox -side left -fill both -expand yes -padx $tkdesk(pad) pack $f.sb -side left -fill y frame $f.frb -width $tkdesk(pad) pack $f.frb -side left label $frame.lSample -text "The quick brown fox\njumps over the lazy dog -\n1234567890 times." if {$def != ""} { catch {$frame.lSample config -font $def} } pack $frame.lSample -padx $tkdesk(pad) -pady $tkdesk(pad) bind $entry "_dsk_cpanel_font_reread $entry $lbox" _dsk_cpanel_font_reread $entry $lbox $def wm minsize [winfo toplevel $frame] 40 5 } proc _dsk_cpanel_font_reread {entry lbox {def ""}} { dsk_busy set flist "" catch {set flist [split [exec xlsfonts [$entry get]] \n]} set i 0 set seli -1 $lbox delete 0 end foreach f $flist { $lbox insert end $f if {$def != ""} { if [string match $def $f] { set seli $i } incr i } } if {$seli > -1} { $lbox selection set $seli $lbox see $seli } dsk_lazy } proc _dsk_cpanel_font_set {lbox sample} { global dsk_config_panel set cs [$lbox curselection] if {$cs == ""} { set dsk_config_panel(fonts,sel) "" return } set font [$lbox get $cs] $sample config -font $font update idletasks set dsk_config_panel(fonts,sel) $font } proc _dsk_cpanel_iconsnd {which f def} { global tkdesk _dsk_cpanel_iconsnd set _dsk_cpanel_iconsnd([winfo toplevel $f],which) $which frame $f.fPath pack $f.fPath -fill x label $f.lPath -text "Path:" -width 5 -anchor w entry $f.ePath -bd 2 -width 5 -relief sunken set entry $f.ePath set ip "" #if {$def != ""} { # catch {set ip [file dirname $def]} #} if {$ip == "" || $ip == "."} { if {$which == "icons"} { set plist [split $tkdesk(path,images) :] } else { set plist [split $tkdesk(path,sounds) :] } set ip [lindex $plist 0] } $entry insert end [cb_tilde $ip collapse] $entry xview end blt_drag&drop target $entry handler file "dd_handle_text $entry %v 1" bind $entry "_dsk_cpanel_iconsnd_reread $f" bind $entry <3> {_dsk_cpanel_iconsnd_path_popup %W %X %Y} cb_bindForCompletion $entry menubutton $f.mbDirs -bd 2 -relief raised \ -bitmap @$tkdesk(library)/cb_tools/bitmaps/combo.xbm \ -menu $f.mbDirs.menu menu [set m $f.mbDirs.menu] pack $f.lPath -in $f.fPath -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) pack $f.ePath -in $f.fPath -side left -fill x \ -padx $tkdesk(pad) -pady $tkdesk(pad) \ -expand yes -ipady 2 pack $f.mbDirs -in $f.fPath -side left -ipadx 2 -ipady 2 \ -padx $tkdesk(pad) -pady $tkdesk(pad) frame $f.fMask pack $f.fMask -fill x label $f.lMask -text "Mask:" -width 5 -anchor e entry $f.eMask -width 5 -bd 2 -relief sunken set mentry $f.eMask $mentry insert end "*" bind $mentry "_dsk_cpanel_iconsnd_reread $f" pack $f.lMask -in $f.fMask -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) pack $f.eMask -in $f.fMask -side left -fill x \ -padx $tkdesk(pad) -pady $tkdesk(pad) \ -expand yes -ipady 2 catch {$f.dlb delete} dsk_Listbox $f.dlb -width $tkdesk(file_lb,width) -height 10 \ -font [cb_font $tkdesk(font,file_lbs)] \ -bg [cb_col $tkdesk(color,filelb)] \ -fg $tkdesk(color,foreground) set fdlb [$f.dlb getFrame] if {$which == "icons"} { $fdlb.text config -tabs {56 left} } else { $fdlb.text config -tabs {24 left} } $f.dlb show_hsb 0 $fdlb config -relief flat pack $fdlb -fill both -expand yes bind $fdlb.text "+_dsk_cpanel_iconsnd_set $f.dlb" bind $fdlb.text <3> "\ [bind $fdlb.text <1>] ;\ update idletasks ;\ _dsk_cpanel_iconsnd_set $f.dlb ;\ _dsk_cpanel_iconsnd_list_popup $f.dlb %X %Y " if {$which == "sounds"} { bind $fdlb.text "_dsk_cpanel_iconsnd_play $f.dlb" } # bindtags for the icon widgets: bind cpanel-icon <1> " $f.dlb select clear $f.dlb select \[expr \[lindex \[split %W :\] 1\] - 1\] $f.dlb _sel_first \[lindex \[split %W :\] 1\] _dsk_cpanel_iconsnd_set $f.dlb " bind cpanel-icon [bind $fdlb.text ] bind cpanel-icon <3> "\ [bind cpanel-icon <1>] ;\ update idletasks ;\ _dsk_cpanel_iconsnd_set $f.dlb ;\ _dsk_cpanel_iconsnd_list_popup $f.dlb %X %Y " _dsk_cpanel_iconsnd_reread $f wm minsize [winfo toplevel $f] 16 2 } proc _dsk_cpanel_iconsnd_list_popup {dlb x y} { global _dsk_cpanel_iconsnd set si [$dlb select get] if {$si != ""} { set file [string trimleft [lindex [$dlb get] $si] \t] dsk_popup "" $_dsk_cpanel_iconsnd([winfo toplevel [$dlb getFrame]],curpath)/$file $x $y } } proc _dsk_cpanel_iconsnd_path_popup {entry x y} { global tkdesk set dest [$entry get] if [file isdirectory $dest] { set this [winfo toplevel $entry] set m $this-ppop catch {destroy $m} menu $m bind $m " set tkdesk(menu,control) 0 [bind Menu ]" bind $m " set tkdesk(menu,control) 1 [bind Menu ]" set p [cb_tilde [string trimright $dest "/"] collapse] if {$p != ""} { set op $p while {1} { set p [file dirname $p] $m add command -label $p -command \ "_dsk_cpanel_iconsnd_read_from $p [winfo parent $entry]" \ -font [cb_font $tkdesk(font,file_lbs)] if {$p == "/"} break } $m add separator set p $op set i 0 while {1} { set p [file dirname $p] set cm $m.cas$i $m add cascade -label $p -menu $cm -font [cb_font $tkdesk(font,file_lbs)] catch {destroy $cm} menu $cm -postcommand "dsk_casdirs $p $cm 1 \ \{_dsk_cpanel_iconsnd_read_from %d [winfo parent $entry]\}" incr i if {$p == "/"} break } update tk_popup $m $x $y } } } proc _dsk_cpanel_iconsnd_read_from {path frame} { $frame.ePath delete 0 end $frame.ePath insert end [cb_tilde $path collapse] $frame.ePath xview end _dsk_cpanel_iconsnd_reread $frame } proc _dsk_cpanel_iconsnd_reread {frame} { global tkdesk _dsk_cpanel_iconsnd dsk_busy set which $_dsk_cpanel_iconsnd([winfo toplevel $frame],which) set dir [string trimright [cb_tilde [$frame.ePath get] expand] "/"] set ndir $dir if {$dir == ""} {set dir "/"} if ![file isdirectory $dir] { dsk_lazy cb_error "$dir: not a directory." return } set _dsk_cpanel_iconsnd([winfo toplevel $frame],curpath) $dir set mask [$frame.eMask get] set flist [dskC_ls -f -s name $dir -M $mask] set list "" set dlist "" foreach f $flist { set f [string trimright $f] if ![file isdirectory $ndir/$f] { if {$which == "icons"} { lappend list \t$f } else { lappend list $f } } else { if {$f != "." && $f != ".."} { lappend dlist $f } } } $frame.dlb config -list $list set fdlb [$frame.dlb getFrame] set l 1 foreach f $list { set fn [list $ndir/[string trimleft $f \t]] if {$which == "icons"} { $fdlb.text window create $l.0 \ -create "_dsk_cpanel_iconsnd_createimg $frame.dlb $l $fn" \ -padx 4 -pady 4 } else { set ext [file extension $f] if {[lsearch {.au .voc .wav .aiff .raw .snd} $ext] > -1} { $fdlb.text window create $l.0 \ -create "_dsk_cpanel_iconsnd_createimg $frame.dlb $l ficons16/speaker.xpm" \ -padx 4 -pady 4 } } incr l } # update menu: $frame.mbDirs.menu delete 0 last if {[llength $dlist] > 0} { foreach d $dlist { $frame.mbDirs.menu add command \ -label [cb_tilde [list $ndir/$d] collapse] \ -command "_dsk_cpanel_iconsnd_read_from [list $ndir/$d] $frame" \ -font [cb_font $tkdesk(font,file_lbs)] } $frame.mbDirs.menu add separator } if {$which == "icons"} { set plist [split $tkdesk(path,images) :] } else { set plist [split $tkdesk(path,sounds) :] } foreach p $plist { $frame.mbDirs.menu add command \ -label [cb_tilde $p collapse] \ -command "_dsk_cpanel_iconsnd_read_from [list $p] $frame" \ -font [cb_font $tkdesk(font,file_lbs)] } dsk_lazy } proc _dsk_cpanel_iconsnd_createimg {dlb line file} { set dlb [$dlb getFrame] catch {label $dlb.text.l:$line} $dlb.text.l:$line config -image [dsk_image $file reread] -bd 0 bindtags $dlb.text.l:$line cpanel-icon return $dlb.text.l:$line } proc _dsk_cpanel_iconsnd_set {dlb} { global tkdesk dsk_config_panel _dsk_cpanel_iconsnd set fdlb [$dlb getFrame] set which $_dsk_cpanel_iconsnd([winfo toplevel $fdlb],which) set si [lindex [$dlb select get] 0] if {$si != ""} { set file [string trimleft [lindex [$dlb get] $si] \t] set curpath $_dsk_cpanel_iconsnd([winfo toplevel $fdlb],curpath) if {$curpath != "/"} { set file [cb_tilde $curpath/$file collapse] } # try to shorten path of file: set maxl 0 if {$which == "icons"} { set plist [split $tkdesk(path,images) :] } else { set plist [split $tkdesk(path,sounds) :] } foreach p $plist { set p [cb_tilde $p collapse] if {[string first $p $file] == 0} { set l [string length $p] if {$l > $maxl} { set maxl $l } } } if {$maxl > 0} { set file [string trimleft [string range $file $maxl 1000] /] } set dsk_config_panel($which,sel) $file } else { set dsk_config_panel($which,sel) "" } } proc _dsk_cpanel_iconsnd_play {dlb} { global tkdesk dsk_config_panel _dsk_cpanel_iconsnd set fdlb [$dlb getFrame] set which $_dsk_cpanel_iconsnd([winfo toplevel $fdlb],which) set si [lindex [$dlb select get] 0] if {$si != ""} { set file [string trimleft [lindex [$dlb get] $si] \t] set curpath $_dsk_cpanel_iconsnd([winfo toplevel $fdlb],curpath) if {$curpath != "/"} { set file [cb_tilde $curpath/$file collapse] } set us $tkdesk(use_sound) set tkdesk(use_sound) 1 dsk_sound $file set tkdesk(use_sound) $us } } tkdesk-2.0/tcldesk/delete.tcl0100644000175000007640000002440010020457430014317 0ustar jccjcc# ============================================================================= # # File: delete.tcl # Project: TkDesk # # Started: 22.10.94 # Changed: 22.10.94 # Author: cb # # Description: Implements classes and procs for file operations like # copy, move, delete, file info and disk usage (and others). # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_delete {{files ""}} #s proc dsk_delete_action {cmd} #s proc dsk_empty_trash {} # # ----------------------------------------------------------------------------- # # ----------------------------------------------------------------------------- # # Proc: dsk_delete # Args: files (opt.) list of files # Returns: "" # Desc: Deletes the selected files or $files. # Side-FX: none # if ![info exists tkdesk(geometry,dsk_delete)] { set tkdesk(geometry,dsk_delete) "" } global dsk_delete set dsk_delete(flist) "" ;# initial file list for dsk_copy set dsk_delete(fcnt) 0 ;# counter: that many files have been processed set dsk_delete(fmax) 0 ;# number of files to process - 1 set dsk_delete(really) 0 ;# REALLY delete? proc dsk_delete {{files ""}} { global tkdesk dsk_delete if {$files == ""} { set files [dsk_active sel] } set dsk_delete(fcnt) 0 set dsk_delete(fmax) [expr [llength $files] - 1] set dsk_delete(flist) $files set dsk_delete(all) $tkdesk(all_files) set dsk_delete(really) $tkdesk(really_delete) if {[string first "$tkdesk(trashdir)" [lindex $files 0]] > -1} { set dsk_delete(really) 1 } if !$tkdesk(ask_on_delete) { if {!$dsk_delete(really) && $dsk_delete(flist) != ""} { dsk_delete_action delete return } } set t .dsk_delete if [winfo exists $t] { destroy $t } toplevel $t # ---- File Box frame $t.ff -bd 1 -relief raised pack $t.ff -fill both -expand yes frame $t.fff pack $t.fff -in $t.ff -fill both -expand yes \ -padx $tkdesk(pad) -pady $tkdesk(pad) entry $t.eFile -width 40 -bd 2 -relief sunken pack $t.eFile -in $t.fff -side bottom -fill x -expand yes -ipady 2 $t.eFile insert end [lindex $dsk_delete(flist) 0] $t.eFile icursor end $t.eFile xview end bind $t.eFile "$t.bDelete invoke" bind $t.eFile <3> {dsk_popup %W [%W get] %X %Y} cb_bindForCompletion $t.eFile blt_drag&drop target $t.eFile handler file "dd_handle_text $t.eFile %v 1" label $t.lFile if {$files == ""} { $t.lFile config -text "File (no files selected):" } else { $t.lFile config \ -text "File ([expr $dsk_delete(fcnt) + 1] of [expr $dsk_delete(fmax) + 1]):" } pack $t.lFile -in $t.fff -side left if {$dsk_delete(fmax) > 0} { checkbutton $t.cbAll -text "all selected files" \ -padx $tkdesk(pad) -relief flat -variable dsk_delete(all) pack $t.cbAll -in $t.fff -side right } # ---- Buttons frame $t.fb -bd 1 -relief raised pack $t.fb -fill x button $t.bDelete -text " Delete " -command "dsk_delete_action delete" checkbutton $t.cbReally -text "Delete permanently" \ -padx $tkdesk(pad) -relief flat -variable dsk_delete(really) button $t.bSkip -text " Skip " -command "dsk_delete_action skip" if {$dsk_delete(fmax) < 1} { $t.bSkip config -state disabled } button $t.bCancel -text " Cancel " -command { set tkdesk(geometry,dsk_delete) [wm geometry .dsk_delete] destroy .dsk_delete } pack $t.bCancel $t.bSkip \ -in $t.fb -side right \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 pack $t.bDelete $t.cbReally \ -in $t.fb -side left \ -padx $tkdesk(pad) -pady $tkdesk(pad) -ipady 1 #bind $t "+focus $t.eFile" wm minsize $t 326 100 wm title $t "Delete Files" wm protocol $t WM_DELETE_WINDOW {.dsk_delete.bCancel invoke} dsk_place_window $t dsk_delete 470x98 focus -force $t.eFile } proc dsk_delete_action {cmd} { global dsk_delete tkdesk set t .dsk_delete #cd [$tkdesk(active_viewer) curdir] set alist "" if $dsk_delete(all) { if [winfo exists $t.eFile] { set alist [list [$t.eFile get]] if {$dsk_delete(fcnt) < $dsk_delete(fmax)} { set alist [concat $alist [lrange $dsk_delete(flist) \ [expr $dsk_delete(fcnt) + 1] $dsk_delete(fmax)]] } } else { set alist [lrange $dsk_delete(flist) \ $dsk_delete(fcnt) $dsk_delete(fmax)] } } if {($alist == "" || !$dsk_delete(all)) && [winfo exists $t.eFile]} { set alist [$t.eFile get] if {$alist != ""} { set alist [list $alist] set err [catch {set alist [glob $alist]}] if $err { set alist [list [$t.eFile get]] } } } dsk_debug "dsk_delete_action: alist: $alist" if {$alist == "" || $alist == "{}" } { dsk_bell cb_info "Nothing to $cmd!" return } set tmplist "" foreach f $alist { catch {set f [cb_tilde $f expand]} if {[string index $f 0] != "/"} { set f [dsk_active dir]$f } set err [catch {set fl [glob [_make_fname_safe $f]]}] if !$err { foreach fe $fl { lappend tmplist $fe } } else { # check for a broken symbolic link set err [catch {file readlink [_make_fname_safe $f]}] if $err { dsk_errbell cb_error "$f: no match!" } else { lappend tmplist $f } } dsk_debug "tmplist: $tmplist" } if {$tmplist == ""} { return } set alist $tmplist set anum [llength $alist] set dest $tkdesk(trashdir) # make sure trashdir really exists if ![file exists $tkdesk(trashdir)] { exec $tkdesk(cmd,mkdir) $tkdesk(trashdir) } if $dsk_delete(all) { catch {$t.bCancel invoke} } if {$anum > 1} {set fll items} {set fll item} switch $cmd { skip { if $dsk_delete(all) return incr dsk_delete(fcnt) $t.lFile config -text "File ([expr $dsk_delete(fcnt) + 1] of [expr $dsk_delete(fmax) + 1]):" if {$dsk_delete(fcnt) > $dsk_delete(fmax)} { catch {$t.bCancel invoke} return } $t.eFile delete 0 end $t.eFile insert end [lindex $dsk_delete(flist) $dsk_delete(fcnt)] $t.eFile icursor end $t.eFile xview end return } delete { #dsk_busy foreach file $alist { if ![dsk_check_perm $file] { dsk_errbell set rc [cb_dialog $t-ED "Permission denied" \ "[file tail $file]:\nYou don't have permission to delete this item." \ error 0 " OK " "Cancel"] if {$rc == 1} { return } else { set i [lsearch $alist $file] set alist [lreplace $alist $i $i] } } } if {$alist == ""} { return } if $dsk_delete(really) { dsk_sound dsk_really_deleting set out [dsk_bgexec "$tkdesk(cmd,rm) $alist" \ "Deleting $anum $fll..."] } else { set dir [string trimright $dest /] set rofs_checked 0 foreach file $alist { if !$rofs_checked { if [dsk_on_rofs $file] { # all files in $alist live on the same file system dsk_errbell cb_error "The filesystem is mounted read-only." return } set rofs_checked 1 } set dfile $dir/[file tail $file] if [file exists $dfile] { set ret [cb_dialog $t-ED "File exists" \ "There already is a file \"[file tail $file]\" in the trash can!" questhead 0 \ "Overwrite" "Overwrite all" " Skip "] if {$ret == 1} { break } elseif {$ret == 2} { set i [lsearch $alist $file] if {$i > -1} { set alist [lreplace $alist $i $i] } else { cb_error "?? Couldn't find $file !?" } } } } if {$alist == ""} { return } set anum [llength $alist] # try to hide different file systems from user: file stat $dest s2 set one 1 foreach file $alist { set s1(dev) $s2(dev) catch {file stat $file s1} set t file catch {set t [file type $file]} if {$s1(dev) != $s2(dev) && $t != "file"} { set one 0 break } } if $one { set out [dsk_bgexec "$tkdesk(cmd,mv) $alist $dest" \ "Moving $anum $fll to the trash can..."] } else { set out [dsk_bgexec "$tkdesk(cmd,cp) $alist $dest" \ "Copying $anum $fll to the trash can..."] if {$out != "error" && $out != "break"} { set out [dsk_bgexec "$tkdesk(cmd,rm) $alist" \ "Deleting $anum $fll..."] } } } #dsk_lazy } default { cb_error "dsk_delete_action: unknown cmd ($cmd)" return } } if {$out != "error"} { if $dsk_delete(really) { dsk_refresh "$alist" } else { dsk_refresh "$alist $dest" } foreach f $alist { dsk_DeskItem :: remove $f } if ![winfo exists $t] return if !$dsk_delete(all) { incr dsk_delete(fcnt) if {$dsk_delete(fcnt) > $dsk_delete(fmax)} { catch {$t.bCancel invoke} return } $t.eFile delete 0 end $t.eFile insert end [lindex $dsk_delete(flist) $dsk_delete(fcnt)] $t.eFile icursor end $t.eFile xview end } } } # ---------------------------------------------------------------------------- # dsk_empty_trash: # Empties the trash can. # proc dsk_empty_trash {} { global tkdesk set alist [dskC_ls -p -a $tkdesk(trashdir)] set anum [llength $alist] if {$anum == 0} { cb_info "The trash can is empty." } else { if {[cb_okcancel "Empty trash can?\nThis will delete ALL files in the trash can!"] == 0} { dsk_sound dsk_really_deleting if {$anum == 1} {set fll "File"} {set fll "Files"} set tlist "" foreach f $alist { lappend tlist [string trimright \ $tkdesk(trashdir)/$f " "] } dsk_bgexec "$tkdesk(cmd,rm) $tlist" "Deleting $anum $fll..." dsk_refresh $tkdesk(trashdir) } } } tkdesk-2.0/tcldesk/diary.tcl0100644000175000007640000002117710020457430014175 0ustar jccjcc# ============================================================================= # # File: diary.tcl # Project: TkDesk # # Started: 5.4.97 # Changed: 5.4.97 # Author: cb # # Description: Implements the diary. # # Copyright (C) 1996, 1997 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s proc dsk_diary_init {} #s proc dsk_diary_process_date {date} #s proc dsk_diary_process_time {time} #s proc dsk_diary_check {} #s proc dsk_diary_list {{date today} {show_none 1}} # # ============================================================================= global env set dsk_diary(filename) $env(HOME)/Diary.tcl set dsk_diary(list_after_startup) 1 set dsk_diary(list_at_midnight) 1 # fill weekday map: foreach d {Mon Tue Wed Thu Fri Sat Sun} \ fd {monday tuesday wednesday thursday friday saturday sunday} \ gd {mo di mi do fr sa so} \ gfd {montag dienstag mittwoch donnerstag freitag samstag sonntag} { set dsk_diary(map,$d) $d set dsk_diary(map,$fd) $d set dsk_diary(map,$gd) $d set dsk_diary(map,$gfd) $d } set dsk_diary(map,Sonnabend) Sat set dsk_diary(map,heute) today # ----------------------------------------------------------------------------- # dsk_diary_init: # Initializes the diary background procs. # proc dsk_diary_init {} { global tkdesk dsk_diary dsk_diary_entry # read Diary: set err [catch {source $dsk_diary(filename)} errmsg] if $err { cb_error $errmsg return } set dsk_diary(listwin) "" if [info exists dsk_diary(after_id)] { catch {after cancel $dsk_diary(after_id)} catch {unset dsk_diary(entries)} foreach ent [array names dsk_diary_entry] { unset dsk_diary_entry($ent) } } if [info exists tkdesk(diary)] { # process diary list: foreach app $tkdesk(diary) { ot_maplist $app date time message set date [string trim $date] set time [string tolower [string trim $time]] # process date: catch {unset wd} ot_maplist [dsk_diary_process_date $date] wd d m y if ![info exists wd] { cb_error "Unknown date format: $date" continue } # process time: ot_maplist [dsk_diary_process_time $time] H M # set internal diary entry: #puts "set dsk_diary_entry($wd-$d.$m.$y-$H:$M) $message" lappend dsk_diary(entries) $wd-$d.$m.$y-$H:$M set dsk_diary_entry($wd-$d.$m.$y-$H:$M) [list $date $time $message] } if ![info exists dsk_diary(after_id)] { if $dsk_diary(list_after_startup) { dsk_diary_list today 0 } } # normalize period: set tkdesk(diary,period) [expr 60 / \ round (60./$tkdesk(diary,period))] #puts "using period: $tkdesk(diary,period)" set dsk_diary(period) [expr $tkdesk(diary,period) * 60 * 1000] # determine time to first check: set min [clock format [clock seconds] -format %M] set min [string trimleft $min 0] if {$min == ""} {set min 0} set p $tkdesk(diary,period) set t [expr (($min/$p + 1) * $p - $min) * 60 * 1000] #puts "first check in [expr $t / 60000] minutes" set dsk_diary(after_id) [after $t dsk_diary_check] } } # ----------------------------------------------------------------------------- # dsk_diary_process_date date: # Normalizes $date. Returns a list of {weekday day month year}. # proc dsk_diary_process_date {date} { global dsk_diary set date [string tolower $date] set wd "*" if {[string first "." $date] > -1} { # european format ot_maplist [split $date .] d m y } elseif {[string first "/" $date] > -1} { # US-american format set y "" ot_maplist [split $date /] m d y } elseif {$date == "*"} { set d "*" set m "*" set y "*" } else { # maybe a single weekday? set err [catch {set wd $dsk_diary(map,$date)}] if $err { return "" } else { if {$wd == "today"} { ot_maplist [clock format [clock seconds] -format "%d %m %Y"] \ d m y set wd "*" } else { set d "*" set m "*" set y "*" } } } set $d [string trimleft $d 0] set $m [string trimleft $m 0] set cury [clock format [clock seconds] -format %Y] if {$y == ""} { set y $cury } else { set $y [string trimleft $y 0] if {$y == ""} {set y 0} } if {$y != "*"} { if {[string length $y] != 4} { set y [expr $y + ($cury / 100) * 100] } } return [list $wd $d $m $y] } # ----------------------------------------------------------------------------- # dsk_diary_process_time time: # Normalizes $time. Returns a list of {hour minute}. # proc dsk_diary_process_time {time} { global dsk_diary set add12 0 if {[string first "am" $time] > -1} { set time [string trimleft $time "am"] } elseif {[string first "pm" $time] > -1} { set time [string trimleft $time "pm"] set add12 1 } set M "" if {$time == "-"} { set H "" set M "" } elseif {$time == "*"} { set H "*" set M "*" } else { ot_maplist [split $time :] H M set $H [string trimleft $H 0] if {$H == ""} {set H 0} set $M [string trimleft $M 0] if {$M == ""} {set M 0} if $add12 { incr H 12 } } return [list $H $M] } # ----------------------------------------------------------------------------- # dsk_diary_check: # Periodically checks for any events. # proc dsk_diary_check {} { global tkdesk dsk_diary dsk_diary_entry #puts "diary-check at [clock format [clock seconds]]" ot_maplist [clock format [clock seconds] -format {%a %d %m %Y %H %M}] \ wday day month year hours minutes set day [string trimleft $day 0] set month [string trimleft $month 0] set hours [string trimleft $hours 0] if {$hours == ""} {set hours 0} set minutes [string trimleft $minutes 0] if {$minutes == ""} {set minutes 0} foreach ent $dsk_diary(entries) { #puts "string match $ent \"$wday-$day.$month.$year-$hours:$minutes\"" if [string match $ent "$wday-$day.$month.$year-$hours:$minutes"] { ot_maplist $dsk_diary_entry($ent) date time msg if {[string first "tcl:" $msg] > -1} { eval [string trimleft $msg "tcl:"] } else { dsk_bell cb_info [subst -nocommands $msg] } } } if {$hours == 0 && $minutes == 0 && $dsk_diary(list_at_midnight)} { dsk_diary_list today 0 } # determine time to next check: set min [clock format [clock seconds] -format %M] set min [string trimleft $min 0] if {$min == ""} {set min 0} set p $tkdesk(diary,period) set t [expr (($min/$p + 1) * $p - $min) * 60 * 1000] #puts "next check in [expr $t / 60000] minutes" set dsk_diary(after_id) [after $t dsk_diary_check] } # ----------------------------------------------------------------------------- # dsk_diary_list date: # Opens an editor window listing today's appointments. # TODO: Check for weekdays of $date other than "today". # proc dsk_diary_list {{date today} {show_none 1}} { global tkdesk dsk_diary dsk_diary_entry if {$date == "today"} { ot_maplist [clock format [clock seconds] -format {%a %A %d %m %B %Y}] \ wday fwday day month fmonth year set day [string trimleft $day 0] set month [string trimleft $month 0] set listing "Appointments for $fwday, $day. $fmonth $year:\n\n" } else { ot_maplist [dsk_diary_process_date $date] wday day month year set listing "Appointments for $day.$month.$year:\n\n" } set matched 0 foreach ent $dsk_diary(entries) { #puts "string match $ent \"$wday-$day.$month.$year-$hours:$minutes\"" ot_maplist [split $ent -] ewday edate etime if [string match "$ewday-$edate" "$wday-$day.$month.$year"] { ot_maplist $dsk_diary_entry($ent) date time msg #if {$time == ":"} {set time ""} append listing [format "%-8s%s\n" $time $msg] incr matched } } if {$matched == 0} { if $show_none { append listing "None.\n" } else { return } } if ![winfo exists dsk_diary(listwin)] { set ed [dsk_editor string $listing] set dsk_diary(listwin) $ed wm title $ed "Diary" wm iconname $ed "Diary" } else { set tw [$ed textWidget] $tw delete 1.0 end $tw insert end $listing } } tkdesk-2.0/tcldesk/dsk_Listbox.tcl0100644000175000007640000005356710035360575015373 0ustar jccjcc# ============================================================================= # # File: dsk_Listbox.tcl # Project: TkDesk # # Started: 07.10.94 # Changed: 09.10.94 # Author: cb # # Description: Implements a generic listbox widget, complete with scrollbar, # multiselection and tags. # # Copyright (C) 1996 Christian Bolik # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # See the file "COPYING" in the base directory of this distribution # for more. # # ----------------------------------------------------------------------------- # # Sections: #s itcl_class dsk_Listbox #s method config {config} { #s method textconfig {args} #s method sbconfig {args} #s method top {{line ""}} #s method get {{index ""}} #s method tag {cmd args} #s method select {cmd args} #s method _sel_entry {index} #s method _unsel_entry {index} #s method _sel_toggle {index} #s method _sel_toggle_all {} #s method _sel_first {index} #s method _sel_to {index {keep 0}} #s method _sel_get {tagname} #s method _sel_clear {} #s method _sel_drag {cmd {keep 0} {w ""} {x ""} {y ""}} #s method _sel_for_dd {index} #s method _dd_start {x y} #s method _dd_end {x y min} #s method _last_entry {index} #s method _yview {line} #s method _winunits {} #s method _resizeit {} #s method _pack_sb {{do_pack 0}} #s method show_hsb {show} #s proc autoscrollbar {{activate ""}} #s proc selcolor {color} #s proc modifier {use_old} # # ============================================================================= # # ============================================================================= # # Class: dsk_Listbox # Desc: Generic listbox metawidget. # # Methods: config ?options? for options see Publics # textconfig ?options? config for text widget (redundant) # sbconfig ?options? config for scrollbar (redundant) # top ?line? return or make line the first visible # get ?index? returns the current list # select select all entries in # select clear clear selection # select get return list of selected entries # tag