dtwm: basic multihead(xinerama only) support
[oweals/cde.git] / cde / programs / dtfile / ChangeDirP.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: ChangeDirP.c /main/8 1998/04/01 15:05:07 rafi $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  *
27  *   FILE:           ChangeDirP.c
28  *
29  *   COMPONENT_NAME: Desktop File Manager (dtfile)
30  *
31  *   Description:    Processing functions for the change directory display
32  *                   line and the current directory dialog.
33  *
34  *   FUNCTIONS: ABS
35  *              CheckCurrentDirectorySelect
36  *              CurrentDirChange
37  *              CurrentDirClose
38  *              CurrentDirDropCallback
39  *              CurrentDirExposed
40  *              CurrentDirIconCallback
41  *              CurrentDirSelected
42  *              CurrentDirectoryIconMotion
43  *              DrawCurrentDirectory
44  *              GetStatusMsg
45  *              ResizeFastText
46  *              ShowChangeDirDialog
47  *              ShowFastChangeDir
48  *              TimerEvent
49  *              draw_imagestring
50  *              get_text_pieces
51  *              get_textwidth
52  *
53  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
54  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
55  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
56  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
57  *
58  ****************************************************************************
59  ************************************<+>*************************************/
60
61 #include <limits.h>
62 #include <Xm/Xm.h>
63 #include <Xm/XmP.h>
64 #include <Xm/DrawP.h>
65 #include <Xm/TextFP.h>
66 #include <Xm/PushBG.h>
67 #include <Xm/DragDrop.h>
68 #include <Xm/RowColumn.h>
69 #include <Dt/Icon.h>
70 #include <Dt/IconP.h>
71 #include <Dt/DtNlUtils.h>
72 #include <Dt/Connect.h>
73 #include <Dt/FileM.h>
74 #include "Encaps.h"
75 #include "SharedProcs.h"
76
77 #include "Desktop.h"
78 #include "FileMgr.h"
79 #include "Main.h"
80 #include "ChangeDir.h"
81 #include "Prefs.h"
82
83
84 /********    Static Function Declarations    ********/
85
86 static void CurrentDirChange(
87                         XtPointer client_data,
88                         DialogData *old_dialog_data,
89                         DialogData *new_dialog_data,
90                         XtPointer call_data) ;
91 static void CurrentDirClose(
92                         XtPointer client_data,
93                         DialogData *old_dialog_data,
94                         DialogData *new_dialog_data) ;
95 static void CheckCurrentDirectorySelect(
96                         FileMgrData *file_mgr_data ) ;
97 static void TimerEvent(
98                         XtPointer client_data,
99                         XtIntervalId *id );
100 static void ResizeFastText(
101                         FileMgrRec *file_mgr_rec,
102                         FileMgrData *file_mgr_data,
103                         short columns) ;
104 static int get_textwidth(
105                         FileMgrData *fmd,
106                         char *str,
107                         int len);
108 static void draw_imagestring(
109                         Display *display,
110                         Drawable d,
111                         FileMgrData *fmd,
112                         GC gc,
113                         int x, int y,
114                         char *text,
115                         int bytes);
116
117 /********    End Static Function Declarations    ********/
118
119 /* absolute value macro */
120 #ifndef ABS
121 #define ABS(x) (((x) > 0) ? (x) : (-(x)))
122 #endif
123
124 /* layout constants */
125 #define CUR_DIR_SPACING 5
126
127
128 /* local global varible to determine whether the user has double clicked
129    or not */
130 static Boolean doubleClick = False;
131
132 /*--------------------------------------------------------------------
133  * get_text_pieces:
134  *
135  *   Given a width available for the current directory text,
136  *   determines the text to be drawn in one of three formats:
137  *
138  *     /path         (non-restricted directory)
139  *     /.../path     (restricted directory)
140  *     .../subpath   (if the full path wouldn't fit)
141  *
142  *   Returns the length (in chars) and width (in pixels) of each of
143  *   the two components of the text: 
144  *   (1) prefix "/..." or "...", and (2) path or subpath.
145  *   Returns True if the path was chopped because it wouldn't fit.
146  *
147  *------------------------------------------------------------------*/
148
149 static Boolean
150 get_text_pieces(
151         FileMgrData *file_mgr_data,
152         int width,
153         char buf[],
154         int *host_len_p,
155         int *host_pixels_p,
156         int *prefix_len_p,
157         int *prefix_pixels_p,
158         int *path_len_p,
159         int *path_pixels_p)
160 {
161    Boolean chopped = False;
162    int prefix_len;
163    int prefix_pixels;
164    int path_len;
165    int path_pixels;
166    char *path_begin;
167    char *next_part = NULL;
168
169    *host_len_p = *host_pixels_p = 0;
170
171    /* if restricted directory, path prefix is "/..." */
172    if (file_mgr_data->restricted_directory)
173    {
174       strcpy(buf, "/...");
175       prefix_len = strlen("/...");
176       prefix_pixels = get_textwidth (file_mgr_data, "/...", prefix_len);
177       path_begin = file_mgr_data->current_directory +
178                        strlen(file_mgr_data->restricted_directory);
179       if (*path_begin == '\0')
180          path_begin = "/";
181       else if (*path_begin != '/')
182           -- path_begin;
183
184    }
185    else
186    {
187       prefix_len = 0;
188       prefix_pixels = 0;
189       path_begin = file_mgr_data->current_directory;
190    }
191
192    /* calculate path length & width */
193    path_len = strlen(path_begin);
194    path_pixels = get_textwidth (file_mgr_data, path_begin, path_len);
195
196    /* if whole path doesn't fit, we need to chop off pieces until it does */
197    if (prefix_pixels + path_pixels > width)
198    {
199       chopped = True;
200
201       /* change the path prefix */
202       strcpy(buf, "...");
203       prefix_len = strlen("...");
204       prefix_pixels = get_textwidth (file_mgr_data, "...", prefix_len);
205
206       do
207       {
208          /* chop off the next piece (everything up to the next '/') */
209          next_part = NULL;
210          next_part = DtStrchr(path_begin + 1, '/');
211          if (next_part == NULL)
212          {
213            /* Got here only when the last directory is still too
214               long to display
215            */
216            break;
217          }
218
219          /* calculate new path length */
220          path_begin = next_part;
221          path_len = strlen(path_begin);
222          path_pixels = get_textwidth (file_mgr_data, path_begin, path_len);
223
224          /* keep going until it fits */
225       } while (prefix_pixels + path_pixels > width);
226    }
227
228    /* add the path to the buffer */
229    if( NULL == next_part )
230    {
231      /* Got here only when the last directory is still too
232         long to display
233       */
234      int len = path_len;
235      char saved_char;
236
237      while( 0 != len )
238      {
239        /* Going back one character at a time to see if it fit
240        */
241        path_pixels = get_textwidth( file_mgr_data, path_begin, len );
242        if( prefix_pixels + path_pixels < width )
243        {
244          break;
245        }
246        --len;
247      }
248
249      if( 0 == len )
250      {
251        buf[prefix_len] = 0x0;
252        *path_len_p = 0;
253        *path_pixels_p = 0;
254      }
255      else
256      {
257        strncpy (buf + prefix_len, path_begin, len);
258        *path_len_p      = len;
259        *path_pixels_p   = path_pixels;
260      }
261    }
262    else
263    {
264      strcpy (buf + prefix_len, path_begin);
265      *path_len_p      = path_len;
266      *path_pixels_p   = path_pixels;
267    }
268
269    /* return values */
270    *prefix_len_p    = prefix_len;
271    *prefix_pixels_p = prefix_pixels;
272    return chopped;
273 }
274
275
276 /************************************************************************
277  *
278  *  ShowChangeDirDialog
279  *      Callback functions invoked from the Change Directory... menu
280  *      item.  This function displays the change directory dialog.
281  *
282  ************************************************************************/
283
284 /*ARGSUSED*/
285 void
286 ShowChangeDirDialog(
287         Widget w,
288         XtPointer client_data,
289         XtPointer callback )
290 {
291    FileMgrRec  * file_mgr_rec;
292    DialogData  * dialog_data;
293    FileMgrData * file_mgr_data;
294    ChangeDirData * change_dir_data;
295    ChangeDirRec * change_dir_rec;
296    Arg args[1];
297    Widget mbar;
298    char *tempStr, *tmpStr;
299
300
301    /*  Set the menu item to insensitive to prevent multiple  */
302    /*  dialogs from being posted and get the area under the  */
303    /*  menu pane redrawn.                                    */
304
305    if (w)
306    {
307       if((XtArgVal)client_data == FM_POPUP)
308          mbar = XtParent(w);
309       else
310          mbar = (Widget) XmGetPostedFromWidget(XtParent(w));
311
312       XmUpdateDisplay (w);
313       XtSetArg(args[0], XmNuserData, &file_mgr_rec);
314       XtGetValues(mbar, args, 1);
315
316       /* Ignore accelerators when we're insensitive */
317       if ((file_mgr_rec->menuStates & CHANGEDIR) == 0)
318       {
319         XSetInputFocus(XtDisplay(w),
320                        XtWindow(file_mgr_rec->change_directoryBtn_child),
321                        RevertToParent, CurrentTime);
322         return;
323       }
324    }
325    else
326    {
327       /* Done only during a restore session */
328       file_mgr_rec = (FileMgrRec *)client_data;
329    }
330
331    /* Got an accelerator after we were unposted */
332    if ((dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec)) == NULL)
333       return;
334
335    file_mgr_data = (FileMgrData *) dialog_data->data;
336    change_dir_data = (ChangeDirData *) file_mgr_data->change_dir->data;
337    change_dir_data->file_mgr_rec = (XtPointer) file_mgr_rec;
338
339    file_mgr_rec->menuStates &= ~CHANGEDIR;
340
341    _DtShowDialog (file_mgr_rec->shell, (Widget)NULL, (XtPointer)file_mgr_rec,
342                file_mgr_data->change_dir,
343                CurrentDirChange, (XtPointer)file_mgr_rec,
344                CurrentDirClose, (XtPointer)file_mgr_rec, (char *)NULL,
345                False, False, (char *)NULL, (XClassHint *)NULL);
346
347    /* Save a ptr to file_mgr_rec in the find dialogs structure */
348    change_dir_rec = (ChangeDirRec *)_DtGetDialogInstance(
349                                                    file_mgr_data->change_dir);
350
351    if(file_mgr_data->title != NULL &&
352                strcmp(file_mgr_data->helpVol, DTFILE_HELP_NAME) != 0)
353    {
354       tmpStr = GETMESSAGE(2,15, "Go To");
355       tempStr = (char *)XtMalloc(strlen(tmpStr) +
356                                  strlen(file_mgr_data->title) + 5);
357       sprintf(tempStr, "%s - %s", file_mgr_data->title, tmpStr);
358    }
359    else
360    {
361       tmpStr = (GETMESSAGE(2,17, "File Manager - Go To"));
362       tempStr = XtNewString(tmpStr);
363    }
364    XtSetArg (args[0], XmNtitle, tempStr);
365    XtSetValues (XtParent (change_dir_rec->change_dir), args, 1);
366    XtFree(tempStr);
367
368    file_mgr_rec->change_directoryBtn_child=XtParent (change_dir_rec->change_dir);
369 }
370
371
372
373
374 /************************************************************************
375  *
376  *  CurrentDirSelected
377  *      When a Button1 selection occurs on the current directory line,
378  *      see if it occurred within the directory path, highlight the
379  *      selected sub-path, or if the sub-path was already highlighted,
380  *      set the current directory to the path and dehighlight.
381  *
382  ************************************************************************/
383
384 void
385 CurrentDirSelected(
386         Widget w,
387         XtPointer client_data,
388         XtPointer call_data )
389 {
390    FileMgrRec  *file_mgr_rec = (FileMgrRec *) client_data;
391    DialogData  *dialog_data;
392    FileMgrData *file_mgr_data;
393    char buf[2*MAX_PATH];
394    char host_name[MAX_PATH];
395    Boolean chopped;
396    int host_len;
397    int host_pixels;
398    int prefix_len;
399    int prefix_pixels;
400    int path_len;
401    int path_pixels;
402
403    XmDrawnButtonCallbackStruct *button_data;
404    XButtonEvent *event;
405    Dimension width, highlight, shadow, margin;
406    Arg args[4];
407
408    int left_margin;
409    int begin_x;
410    int end_x;
411    int len;
412    int i;
413    char *ptr;
414    char *new_select;
415    int swidth;
416    static XtIntervalId TimerId;
417
418    /* if doubleClick is true than we have a double click so we want
419     * to change to the new directory that the user double clicked on
420     */
421    if (doubleClick)
422       XtRemoveTimeOut(TimerId);
423
424    button_data = (XmDrawnButtonCallbackStruct *) call_data;
425    event = (XButtonEvent *) button_data->event;
426
427    dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec);
428    file_mgr_data = (FileMgrData *) dialog_data->data;
429
430    /* Get layout values */
431    XtSetArg (args[0], XmNwidth, &width);
432    XtSetArg (args[1], XmNhighlightThickness, &highlight);
433    XtGetValues (w, args, 2);
434    XtSetArg (args[0], XmNshadowThickness, &shadow);
435    XtSetArg (args[1], XmNmarginWidth, &margin);
436    XtGetValues (file_mgr_rec->current_directory_text, args, 2);
437    left_margin = highlight + shadow + margin;
438
439    /*  Get the starting and ending locations of the current  */
440    /*  directory text.                                       */
441    chopped = get_text_pieces(file_mgr_data, width - 2*left_margin,
442                buf, &host_len, &host_pixels, &prefix_len, &prefix_pixels,
443                &path_len, &path_pixels);
444
445    begin_x = left_margin;
446    end_x = begin_x + host_pixels + prefix_pixels + path_pixels;
447
448    /*  Get the selected path  */
449    if (event->x < begin_x || event->x >= end_x)
450    {
451       /* click outside the directory text: nothing selected */
452       new_select = NULL;
453    }
454    else if (event->x < begin_x + host_pixels ||
455             event->x < begin_x + host_pixels + prefix_pixels && !chopped)
456    {
457       /* click on host name or "/..." prefix: root selected */
458       if (file_mgr_data->restricted_directory)
459          new_select = XtNewString(file_mgr_data->restricted_directory);
460       else
461          new_select = XtNewString("/");
462    }
463    else if (event->x < begin_x + host_pixels + prefix_pixels && chopped)
464    {
465       /* click on "..." prefix: directory above the visible piece selected */
466       len = strlen(file_mgr_data->current_directory) - path_len;
467       new_select = (char *) XtMalloc(len + 1);
468       memcpy(new_select, file_mgr_data->current_directory, len);
469       new_select[len] = 0;
470    }
471    else /* event->x >= begin_x + host_pixels + prefix_pixels */
472    {
473       /* click on the path: determine which subdirectory selected */
474       begin_x += host_pixels + prefix_pixels;
475       i = host_len + prefix_len;
476       swidth = get_textwidth(file_mgr_data, "/", strlen("/")) / 2;
477
478       while (begin_x - swidth < event->x)
479       {
480          /* find next '/' in path */
481          ptr = DtStrchr(buf + i, '/');
482          if (ptr == NULL)
483          {
484            i = host_len + prefix_len + path_len + 1;
485            break;
486          }
487
488          /* get x-position of next path component */
489          len = ((ptr + 1) - buf) - i;
490          begin_x += get_textwidth(file_mgr_data, buf + i, len);
491          i += len;
492       }
493
494          /* if we have a restricted diretory and i == 5 ("/.../") then we want
495             the len to be the restricted directory */
496       if (file_mgr_data->restricted_directory &&  i == 5)
497          len = strlen(file_mgr_data->restricted_directory);
498       else
499          len = strlen(file_mgr_data->current_directory)
500              - (host_len + prefix_len + path_len - i) - 1;
501       new_select = (char *) XtMalloc(len + 1);
502       if (len == 0)
503          strcpy(new_select, "/");
504       else
505       {
506          memcpy(new_select, file_mgr_data->current_directory, len);
507          new_select[len] = 0;
508       }
509    }
510
511    /* in restricted mode, don't allow going above the user's home dir */
512    if (new_select != NULL && restrictMode)
513    {
514       /* check if new_select is the same as or a subdirectory of $HOME */
515       len = strlen(users_home_dir);
516       if (strncmp(new_select, users_home_dir, len) != 0
517            || new_select[len] != '\0' && new_select[len] != '/')
518       {
519          /* change new_select to $HOME */
520          XtFree(new_select);
521          new_select = XtNewString(users_home_dir);
522       }
523    }
524
525
526    /*  If the path is the same as what is already selected,  */
527    /*  free cd_select, set the directory to the selected     */
528    /*  directory, redraw the directory display.              */
529
530    /*  If the path was different, set cd_select to the       */
531    /*  selected directory and redraw the directory.          */
532
533    if (new_select != NULL && file_mgr_data->cd_select != NULL &&
534        strcmp(new_select, file_mgr_data->cd_select) == 0)
535    {
536       XtFree (file_mgr_data->cd_select);
537       file_mgr_data->cd_select = NULL;
538
539       strcpy(buf, new_select);
540       strcpy(host_name, file_mgr_data->host);
541
542       if (strcmp (buf, file_mgr_data->current_directory) == 0)
543          FileMgrReread (file_mgr_rec);
544       else
545          ShowNewDirectory (file_mgr_data, host_name, buf);
546
547       XtFree (new_select);
548    }
549    else
550    {
551       XtFree (file_mgr_data->cd_select);
552       file_mgr_data->cd_select = new_select;
553    }
554
555    if (doubleClick)
556    {
557       doubleClick = False;
558       DrawCurrentDirectory (w, file_mgr_rec, file_mgr_data);
559    }
560    else
561    {
562       doubleClick = True;
563       TimerId = XtAppAddTimeOut (XtWidgetToApplicationContext (w), 
564                                  XtGetMultiClickTime(XtDisplay(w)),
565                                  (XtTimerCallbackProc) TimerEvent, 
566                                  (XtPointer) file_mgr_rec);
567    }
568 }
569
570
571
572 /************************************************************************
573  *
574  *  CurrentDirDropCallback
575  *      Callback function invoked upon an action on the change view drop.
576  *
577  ************************************************************************/
578
579 void
580 CurrentDirDropCallback(
581         Widget w,
582         XtPointer client_data,
583         XtPointer call_data )
584 {
585    FileMgrRec  * file_mgr_rec = (FileMgrRec *) client_data;
586    XmAnyCallbackStruct * callback;
587    DialogData  * dialog_data;
588    FileMgrData * file_mgr_data;
589    char host_name[MAX_PATH];
590
591    callback = (XmAnyCallbackStruct *) call_data;
592    dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec);
593    file_mgr_data = (FileMgrData *) dialog_data->data;
594
595    if (callback->reason == XmCR_DEFAULT_ACTION)
596    {
597       strcpy(host_name, file_mgr_data->host);
598       ShowNewDirectory (file_mgr_data, host_name,
599                         _DtPName (file_mgr_data->current_directory));
600    }
601 }
602
603
604 /************************************************************************
605  *
606  *  CurrentDirIconCallback
607  *      Callback function invoked upon an action occuring on an icon.
608  *
609  ************************************************************************/
610
611 void
612 CurrentDirIconCallback(
613         Widget w,
614         XtPointer client_data,
615         XtPointer call_data )
616 {
617    FileMgrRec  * file_mgr_rec = (FileMgrRec *) client_data;
618    XmAnyCallbackStruct * callback;
619    XButtonEvent        * event;
620    DialogData  * dialog_data;
621    FileMgrData * file_mgr_data;
622
623    callback = (XmAnyCallbackStruct *) call_data;
624    event = (XButtonEvent *) callback->event;
625    dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec);
626    file_mgr_data = (FileMgrData *) dialog_data->data;
627
628    if (callback->reason == XmCR_DRAG)
629    {
630       /* Do nothing if a Button 1 drag is already ramping up */
631       if (B1DragPossible)
632          return;
633
634       /* Save starting X and Y, for threshold detection */
635       initialDragX = event->x;
636       initialDragY = event->y;
637
638       /* Flag that a Button 2 drag is ramping up */
639       B2DragPossible = True;
640
641       UnpostTextField(file_mgr_data);
642    }
643    else if (callback->reason == XmCR_ARM)
644    {
645       /* Do nothing if a Button 2 drag is already ramping up */
646       if (B2DragPossible)
647          return;
648
649       /* Save starting X and Y, for threshold detection */
650       initialDragX = event->x;
651       initialDragY = event->y;
652
653       /* Flag that a Button 1 drag is ramping up */
654       B1DragPossible = True;
655       
656       /* but since we're in the current directory icon we don't want to
657          process on the button up */
658       ProcessBtnUpCD = False;
659
660       UnpostTextField(file_mgr_data);
661    }
662    else if (callback->reason == XmCR_DEFAULT_ACTION)
663    {
664       /* We now know that a drag operation won't be starting up */
665       B1DragPossible = False;
666       B2DragPossible = False;
667
668       UnpostTextField(file_mgr_data);
669
670       /* Default action is to reread the directory */
671       FileMgrReread (file_mgr_rec);
672    }
673    else if ((callback->reason == XmCR_SELECT) || 
674             (callback->reason == XmCR_DISARM) ||
675             (callback->reason == XmCR_DROP))
676    {
677       /* We now know that a drag operation won't be starting up */
678       B1DragPossible = False;
679       B2DragPossible = False;
680    }
681 }
682
683
684 /*
685  * This function processes motion events anytime a B1 or B2 drag operation
686  * has the potential of starting.  When the drag threshold is surpassed,
687  * a drag operation will be started.
688  */
689
690 void
691 CurrentDirectoryIconMotion(
692                         Widget w,
693                         XtPointer client_data,
694                         XEvent *event)
695 {
696
697    FileMgrRec  * file_mgr_rec = (FileMgrRec *) client_data;
698    DialogData  * dialog_data;
699    FileMgrData * file_mgr_data;
700    Pixmap drag_pixmap;
701    char * type_set;
702    char * file_set;
703    int diffX, diffY;
704    Widget dirIcon;
705    DtIconGadget iconG;
706
707
708    if ((B1DragPossible && (event->xmotion.state & Button1Mask)) ||
709        (B2DragPossible && (event->xmotion.state & Button2Mask)))
710    {
711       /* Have we passed the drag threshold? */
712       diffX = initialDragX - event->xmotion.x;
713       diffY = initialDragY - event->xmotion.y;
714   
715       if ((ABS(diffX) >= dragThreshold) || (ABS(diffY) >= dragThreshold))
716       {
717          dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec);
718          file_mgr_data = (FileMgrData *) dialog_data->data;
719
720          initiating_view = (XtPointer) file_mgr_data;
721          dirIcon = file_mgr_rec->current_directory_icon;
722          StartDrag(dirIcon, NULL, event);
723       }
724    }
725 }
726
727
728 /************************************************************************
729  *
730  *  CurrentDirExposed
731  *      Callback functions invoked from the current directory display
732  *      drawn button.  This function extracts some structures and calls
733  *      the directory display function.
734  *
735  ************************************************************************/
736
737 void
738 CurrentDirExposed(
739         Widget w,
740         XtPointer client_data,
741         XtPointer call_data )
742 {
743    FileMgrRec  * file_mgr_rec = (FileMgrRec *) client_data;
744    DialogData  * dialog_data;
745    FileMgrData * file_mgr_data;
746
747    dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec);
748
749    /*  Check to see if the view has been closed  */
750
751    if (dialog_data == NULL) return;
752
753    file_mgr_data = (FileMgrData *) dialog_data->data;
754
755    if (file_mgr_data->cd_normal_gc != 0)
756       DrawCurrentDirectory (w, file_mgr_rec, file_mgr_data);
757 }
758
759
760
761
762 /************************************************************************
763  *
764  * GetStatusMsg
765  *   Construct the status message (normally "x Items, y Hidden").
766  *   Returns True if the message is deemed "important".
767  *
768  *   @@@ Note 2/17/95: messages below containing "Item(s)" should really
769  *   be just "Item", but the message catalog is frozen at this time, so
770  *   we can't fix this now.  (In practice, it doesn't matter, because
771  *   the item count should always be > 1, since every directory shoule
772  *   contain at least two files "." and "..")
773  *
774  ************************************************************************/
775
776 Boolean
777 GetStatusMsg(
778         FileMgrData *file_mgr_data,
779         char *buf )
780 {
781    int n_files;
782    int n_hidden;
783    int i, j;
784    TreeShow ts;
785    FileViewData **file_view_data;
786
787    /*
788     * If we are currently busy reading a directory, display a progress
789     * message instead of the normal "x Items, y Hidden" message.
790     */
791    if (file_mgr_data->busy_status == initiating_readdir ||
792        file_mgr_data->busy_status == busy_readdir)
793    {
794       if (file_mgr_data->busy_detail == 0)
795          sprintf (buf, "%s", (GETMESSAGE(3,5, "Reading ...")));
796       else if (file_mgr_data->busy_detail == 1)
797         sprintf (buf, (GETMESSAGE(3,11, "%d Item(s)...")),
798                  file_mgr_data->busy_detail);
799       else
800         sprintf (buf, (GETMESSAGE(3,9, "%3d Items ...")),
801                  file_mgr_data->busy_detail);
802
803       return True;  /* this message deemed important! */
804    }
805
806    else if (file_mgr_data->show_type == MULTIPLE_DIRECTORY)
807    {
808       /*
809        * In tree mode, we only show a count of the hidden files
810        * in branches that are currently expanded.
811        * The idea is we want to show how many additional files would
812        * show up if the user turned on the "Show Hidden" option.
813        */
814       n_hidden = 0;
815       for (i = 0; i < file_mgr_data->directory_count; i++)
816       {
817          file_view_data = file_mgr_data->directory_set[i]->file_view_data;
818          if (file_view_data == NULL)
819             continue;
820
821          ts = file_mgr_data->directory_set[i]->sub_root->ts;
822          if (ts < tsDirs)
823             continue;   /* this branch is not expanded */
824
825          for (j = 0; j < file_mgr_data->directory_set[i]->file_count; j++)
826          {
827             if (file_view_data[j]->filtered &&
828                 (ts == tsAll || file_view_data[j]->file_data->is_subdir))
829             {
830                n_hidden++;
831             }
832          }
833       }
834       sprintf (buf, (GETMESSAGE(3,6, "%d Hidden")), n_hidden);
835
836       return False;
837    }
838
839    else
840    {
841       /*
842        * In flat mode, we only show a total count of all files
843        * and a count of hidden files.
844        */
845       n_files = file_mgr_data->directory_set[0]->file_count;
846       if( n_files == 0 )
847         sprintf( buf, (GETMESSAGE(11,31, "Error while reading %s")), file_mgr_data->current_directory );
848       else if( file_mgr_data == trashFileMgrData )
849       {
850         n_hidden = file_mgr_data->directory_set[0]->filtered_file_count;
851         sprintf (buf, (GETMESSAGE(3,10, "%d Item(s)")),
852                  n_files - n_hidden);
853       }
854       else
855       {
856         n_files -= file_mgr_data->directory_set[0]->invisible_file_count;
857         n_hidden = file_mgr_data->directory_set[0]->filtered_file_count -
858           file_mgr_data->directory_set[0]->invisible_file_count;
859         if (n_files == 1)
860           sprintf (buf, (GETMESSAGE(3,12, "%d Item(s) %d Hidden")),
861                    n_files, n_hidden);
862         else
863           sprintf (buf, (GETMESSAGE(3,7, "%d Items %d Hidden")),
864                  n_files, n_hidden);
865       }
866
867       return False;
868    }
869 }
870
871
872 /************************************************************************
873  *
874  *  DrawCurrentDirectory
875  *      Draw the current directory display area, including the hostname,
876  *      the current directory, any highlighted sub-path of the directory,
877  *      the file count and the number of selected files.
878  *
879  ************************************************************************/
880
881 void
882 DrawCurrentDirectory(
883         Widget w,
884         FileMgrRec *file_mgr_rec,
885         FileMgrData *file_mgr_data )
886 {
887    Arg args[8];
888    Dimension width, height, highlight, shadow, margin, twidth;
889    XFontSetExtents *extents;
890    int font_height;
891    int font_yoffset;
892    int top_margin;
893    int left_margin;
894    char buf[2*MAX_PATH];
895    char msg[21+MAX_PATH];
896    Boolean chopped;
897    int host_len;
898    int host_pixels;
899    int prefix_len;
900    int prefix_pixels;
901    int path_len;
902    int path_pixels;
903    int draw_x;
904    int draw_y;
905    int dir_width;
906    int msg_width;
907    short columns;
908    Boolean msg_drawn;
909
910    /* Get layout values */
911    XtSetArg (args[0], XmNwidth, &width);
912    XtSetArg (args[1], XmNheight, &height);
913    XtSetArg (args[2], XmNhighlightThickness, &highlight);
914    XtGetValues (w, args, 3);
915    XtSetArg (args[0], XmNshadowThickness, &shadow);
916    XtSetArg (args[1], XmNmarginWidth, &margin);
917    XtGetValues (file_mgr_rec->current_directory_text, args, 2);
918
919    if(file_mgr_data->cd_fonttype == XmFONT_IS_FONTSET) {
920        extents = XExtentsOfFontSet(file_mgr_data->cd_fontset);
921        font_yoffset = -(extents->max_logical_extent.y);
922        font_height = extents->max_logical_extent.height;
923    }
924    else
925    {
926        font_yoffset = file_mgr_data->cd_font->ascent;
927        font_height = file_mgr_data->cd_font->ascent +
928                        file_mgr_data->cd_font->descent;
929    }
930    top_margin = (height > (Dimension)font_height)? (Dimension)(height - font_height + 1)/(Dimension)2: 0;
931    left_margin = highlight + shadow + margin;
932
933    /* Ensure the area is cleared out. */
934    XClearArea (XtDisplay (w), XtWindow (w),
935                highlight, highlight,
936                width - 2*highlight, height - 2*highlight,
937                False);
938
939    /*
940     * If there is no status line and no iconic path,
941     * we will want yo draw the "x Files y Hidden" message here.
942     */
943    if (!file_mgr_data->show_iconic_path && !file_mgr_data->show_status_line)
944    {
945       /*
946        * GetStatusMsg() returns True if the status msg is "important".
947        * In this case, make sure we leave room for it
948        */
949       msg_drawn = GetStatusMsg(file_mgr_data, msg);
950       msg_width = get_textwidth (file_mgr_data, msg, strlen(msg));
951    }
952    else
953       msg_drawn = False;
954
955    draw_x = left_margin;
956    if( draw_x < 0 || (Dimension) draw_x > width ) /* Make sure that it's in bound */
957      draw_x = 0;
958
959    draw_y = top_margin + font_yoffset;
960    if( draw_y < 0 || (Dimension) draw_y > height ) /* Make sure that it's in bound */
961    {
962      draw_y = height-5;
963    }
964
965    /* get the text pieces */
966    dir_width = width - 2*left_margin;
967    if (msg_drawn)
968       dir_width -= CUR_DIR_SPACING + msg_width;
969    chopped = get_text_pieces(file_mgr_data, dir_width,
970                buf, &host_len, &host_pixels, &prefix_len, &prefix_pixels,
971                &path_len, &path_pixels);
972
973    /*
974     * go check and change the file_mgr_data->cd_select, make sure its not
975     * longer than the file_mgr_data->current_directory.
976     */
977    CheckCurrentDirectorySelect(file_mgr_data);
978
979    /* draw the host and paths */
980    if (!file_mgr_data->fast_cd_enabled)
981    {
982       draw_imagestring (XtDisplay (w), XtWindow (w), file_mgr_data,
983                         file_mgr_data->cd_normal_gc, draw_x, draw_y,
984                         buf, host_len + prefix_len + path_len);
985    }
986    else if (file_mgr_data->restricted_directory)
987    {
988       draw_imagestring (XtDisplay (w), XtWindow (w), file_mgr_data,
989                         file_mgr_data->cd_normal_gc, draw_x, draw_y,
990                         buf, host_len + prefix_len);
991    }
992
993    /*
994     * If there is no status line and no iconic path, and we have
995     * room left, draw the "x Files y Hidden" message here.
996     */
997    if (!file_mgr_data->show_iconic_path && !file_mgr_data->show_status_line)
998    {
999       /* determine where the message could begin */
1000       if (!file_mgr_data->fast_cd_enabled)
1001          draw_x += host_pixels + prefix_pixels + path_pixels;
1002       else
1003       {
1004          XtSetArg(args[0], XmNwidth, &twidth);
1005          XtGetValues(file_mgr_rec->current_directory_text, args, 1);
1006          if (file_mgr_data->restricted_directory && !chopped)
1007             draw_x += host_pixels + prefix_pixels + twidth;
1008          else
1009             draw_x += host_pixels + twidth;
1010       }
1011
1012       /* if there is enough space left, draw the message */
1013       if ((Dimension)(draw_x + CUR_DIR_SPACING + msg_width + left_margin) <= width)
1014       {
1015          draw_x = width - left_margin - msg_width;
1016          draw_imagestring (XtDisplay (w), XtWindow (w), file_mgr_data,
1017                            file_mgr_data->cd_normal_gc, draw_x, draw_y,
1018                            msg, strlen(msg));
1019          draw_x -= shadow + CUR_DIR_SPACING;  /* right edge of shadow */
1020          msg_drawn = True;
1021       }
1022       else
1023          msg_drawn = False;
1024    }
1025
1026    if (!msg_drawn)
1027       draw_x = width - (highlight + shadow);
1028
1029    /* draw the shadow */
1030    if (!file_mgr_data->fast_cd_enabled)
1031    {
1032       int shadow_width = draw_x - highlight;
1033       int shadow_height = height - 2*highlight;
1034       XmTextFieldWidget tf =
1035              (XmTextFieldWidget)file_mgr_rec->current_directory_text;
1036
1037       XmeDrawShadows(XtDisplay(w), XtWindow(w),
1038                      tf->primitive.top_shadow_GC,
1039                      tf->primitive.bottom_shadow_GC,
1040                      highlight, highlight, shadow_width, shadow_height,
1041                      shadow, XmSHADOW_IN);
1042    }
1043 }
1044
1045
1046
1047 /************************************************************************
1048  *
1049  *  CurrentDirChange
1050  *      Callback functions invoked from the current directory dialog's
1051  *      apply button being pressed.  This function updates and redisplays
1052  *      the current directory information.
1053  *
1054  ************************************************************************/
1055
1056 static void
1057 CurrentDirChange(
1058         XtPointer client_data,
1059         DialogData *old_dialog_data,
1060         DialogData *new_dialog_data,
1061         XtPointer call_data )
1062 {
1063    FileMgrRec  * file_mgr_rec = (FileMgrRec *) client_data;
1064    DialogData  * dialog_data;
1065    FileMgrData * file_mgr_data;
1066    ChangeDirData * new_change_dir_data;
1067    ChangeDirData * old_change_dir_data;
1068    char path[MAX_PATH];
1069    char host_name[MAX_PATH];
1070    char * ptr;
1071
1072
1073    /*  Get a pointer file manager's data structure, free up the   */
1074    /*  old current directory and copy in a new one.  Free up the  */
1075    /*  old selected sub-path, update the file manager's internal  */
1076    /*  data, and redraw the directory.                            */
1077
1078    dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec);
1079    file_mgr_data = (FileMgrData *) dialog_data->data;
1080
1081    if (file_mgr_data->cd_select != NULL)
1082    {
1083       XtFree (file_mgr_data->cd_select);
1084       file_mgr_data->cd_select = NULL;
1085    }
1086
1087    old_change_dir_data = (ChangeDirData *) file_mgr_data->change_dir->data;
1088    new_change_dir_data = (ChangeDirData *) new_dialog_data->data;
1089    
1090    new_change_dir_data->host_name = XtNewString(old_change_dir_data->host_name);
1091    new_change_dir_data->file_mgr_rec = old_change_dir_data->file_mgr_rec;
1092
1093    _DtHideDialog(old_dialog_data, False);
1094
1095    new_change_dir_data->displayed = False;
1096
1097    file_mgr_data->change_dir->data = (XtPointer) new_change_dir_data;
1098    new_dialog_data->data = (XtPointer) old_change_dir_data;
1099    _DtFreeDialogData (new_dialog_data);
1100    file_mgr_rec->menuStates |= CHANGEDIR;
1101
1102
1103    /*  Process call_data into a hostname and directory name.  */
1104
1105    ShowNewDirectory (file_mgr_data,
1106                      ((ChangeDirData *)file_mgr_data->change_dir->data)->host_name,
1107                      call_data);
1108 }
1109
1110
1111
1112
1113 /************************************************************************
1114  *
1115  *  CurrentDirClose
1116  *      Callback functions invoked from the current directory dialog's
1117  *      close button being pressed.  This function resensitizes the
1118  *      Change Directory... menu item.
1119  *
1120  ************************************************************************/
1121
1122 /*ARGSUSED*/
1123 static void
1124 CurrentDirClose(
1125         XtPointer client_data,
1126         DialogData *old_dialog_data,
1127         DialogData *new_dialog_data )
1128 {
1129    FileMgrRec * file_mgr_rec = (FileMgrRec *) client_data;
1130
1131    _DtFreeDialogData (new_dialog_data);
1132    file_mgr_rec->menuStates |= CHANGEDIR;
1133 }
1134
1135 /************************************************************************
1136  *
1137  *  CheckCurrentDirectorySelect
1138  *           Before calling DrawCurrentDirectorySelect() this function
1139  *           makes sure that the fm->cd_select isn't longer than the
1140  *           fm->current_directory.  If it is it reconfigures fm->cd_select
1141  *           to hold no more than what fm->current_directory is.
1142  *
1143  ************************************************************************/
1144 static void
1145 CheckCurrentDirectorySelect( 
1146          FileMgrData *file_mgr_data )
1147 {
1148    int length_cd, length_cd_s;
1149    char *str, *ptr;
1150
1151    if (file_mgr_data            == NULL ||
1152        file_mgr_data->cd_select == NULL ||
1153        file_mgr_data->current_directory == NULL)
1154        return;
1155
1156    /* get the true lengths of current_directory and current_directory_select */
1157    length_cd = strlen(file_mgr_data->current_directory);
1158    length_cd_s = strlen(file_mgr_data->cd_select);
1159    
1160    /* if cd is larger than cd_select than we have now problem */
1161    if(length_cd >= length_cd_s)
1162       return;
1163
1164    /* we need to recalculate the cd_select */
1165    str = XtNewString(file_mgr_data->cd_select);
1166    while(1)
1167    {
1168       ptr = strrchr(str, '/');
1169       *ptr = '\0';
1170       length_cd_s = strlen(str);
1171       if(length_cd > length_cd_s)
1172       {
1173          XtFree(file_mgr_data->cd_select);
1174          file_mgr_data->cd_select = (char *)XtMalloc(strlen(str) + 1);
1175          strcpy(file_mgr_data->cd_select, str);
1176          XtFree(str);
1177          return;
1178       }
1179    }
1180 }
1181
1182 /************************************************************************
1183  *
1184  *  ShowFastChangeDir
1185  *      Post the fast change to text widget.
1186  *
1187  ***************************************************************************/
1188 void
1189 ShowFastChangeDir(
1190         FileMgrRec *file_mgr_rec,
1191         FileMgrData *file_mgr_data )
1192 {
1193    char *textString;
1194    Arg args[16];
1195    Dimension width, height;
1196    Dimension shadow, highlight, margin;
1197    char buf[2*MAX_PATH];
1198    Boolean chopped;
1199    int host_len;
1200    int host_pixels;
1201    int prefix_len;
1202    int prefix_pixels;
1203    int path_len;
1204    int path_pixels;
1205    int begin_x;
1206    int left_margin;
1207
1208    doubleClick = False;
1209
1210    XtRemoveAllCallbacks (file_mgr_rec->current_directory, XmNexposeCallback);
1211
1212    XtFree (file_mgr_data->cd_select);
1213    file_mgr_data->cd_select = NULL;
1214
1215    file_mgr_data->fast_cd_enabled = True;
1216
1217    /* if not a toolbox, just put the current directory in text widget */
1218    if (file_mgr_data->restricted_directory == NULL)
1219    {
1220       if (strcmp(file_mgr_data->host, home_host_name) == 0)
1221          textString = XtNewString(file_mgr_data->current_directory);
1222       else
1223          textString = DtCreateContextString(file_mgr_data->host,
1224                                             file_mgr_data->current_directory,
1225                                             NULL);
1226    }
1227    else  /* is a toolbox, so put the subset of what the toolbox is in the text*/
1228    {
1229       char *ptr;
1230
1231       ptr = file_mgr_data->current_directory +
1232                         strlen(file_mgr_data->restricted_directory);
1233       if (strcmp(ptr, "") == 0)
1234          textString = XtNewString("/");
1235       else
1236          textString = XtNewString(ptr);
1237    }
1238    begin_x = get_textwidth (file_mgr_data, textString, strlen (textString));
1239
1240    /* Get layout values */
1241    XtSetArg (args[0], XmNwidth, &width);
1242    XtSetArg (args[1], XmNhighlightThickness, &highlight);
1243    XtGetValues (file_mgr_rec->current_directory, args, 2);
1244    XtSetArg (args[0], XmNshadowThickness, &shadow);
1245    XtSetArg (args[1], XmNmarginWidth, &margin);
1246    XtGetValues (file_mgr_rec->current_directory_text, args, 2);
1247    left_margin = highlight + shadow + margin;
1248
1249    if(file_mgr_data->restricted_directory == NULL)
1250       XtSetArg (args[0], XmNleftOffset, 0);
1251    else
1252    {
1253       XtSetArg (args[0], XmNshadowThickness, &shadow);
1254       XtSetArg (args[1], XmNhighlightThickness, &highlight);
1255       XtGetValues(file_mgr_rec->current_directory_text, args, 2);
1256       chopped =
1257         get_text_pieces(file_mgr_data, width - 2*left_margin,
1258                   buf, &host_len, &host_pixels, &prefix_len, &prefix_pixels,
1259                   &path_len, &path_pixels);
1260       begin_x = left_margin + host_pixels - shadow - highlight;
1261       if (!chopped)
1262         begin_x += prefix_pixels;
1263       XtSetArg (args[0], XmNleftOffset, begin_x);
1264    }
1265    XtSetArg (args[1], XmNvalue, textString);
1266    XtSetValues(file_mgr_rec->current_directory_text, args, 2);
1267    XtSetArg (args[0], XmNcursorPosition, strlen(textString));
1268    XtSetValues(file_mgr_rec->current_directory_text, args, 1);
1269
1270    XtSetArg (args[0], XmNallowShellResize, False);
1271    XtSetValues(file_mgr_rec->shell, args, 1);
1272    XtManageChild(file_mgr_rec->current_directory_text);
1273    XtSetArg (args[0], XmNallowShellResize, True);
1274    XtSetValues(file_mgr_rec->shell, args, 1);
1275    XRaiseWindow(XtDisplay(file_mgr_rec->current_directory_text),
1276                 XtWindow(file_mgr_rec->current_directory_text));
1277    XmUpdateDisplay(file_mgr_rec->current_directory_text);
1278    XmProcessTraversal(file_mgr_rec->current_directory_text,
1279                          XmTRAVERSE_CURRENT);
1280    XtFree(textString);
1281    XtAddCallback (file_mgr_rec->current_directory, XmNexposeCallback,
1282                                           CurrentDirExposed, file_mgr_rec);
1283 }
1284
1285 /************************************************************************
1286  *
1287  *  TimerEvent - timeout for double click on current Directory line.  If
1288  *      we get here we know it was a single click so lets post the
1289  *      fast change to text widget.
1290  *
1291  ***************************************************************************/
1292 static void
1293 TimerEvent(
1294         XtPointer client_data,
1295         XtIntervalId *id )
1296 {
1297    FileMgrRec *file_mgr_rec = (FileMgrRec *)client_data;
1298    DialogData  * dialog_data;
1299    FileMgrData *file_mgr_data;
1300
1301    doubleClick = False;
1302
1303    /* Got an accelerator after we were unposted */
1304    if ((dialog_data = _DtGetInstanceData ((XtPointer)file_mgr_rec)) == NULL)
1305       return;
1306    file_mgr_data = (FileMgrData *) dialog_data->data;
1307
1308    ShowFastChangeDir(file_mgr_rec, file_mgr_data);
1309 }
1310
1311 /************************************************************************
1312  *
1313  *  ResizeFastText - resizes the fast change text widget due to changes
1314  *       in the size of the FileManager window.
1315  *
1316  *************************************************************************/
1317 static void
1318 ResizeFastText(
1319         FileMgrRec *file_mgr_rec,
1320         FileMgrData *file_mgr_data,
1321         short columns)
1322 {
1323    Arg args[2];
1324    Dimension width;
1325    int left_offset;
1326
1327    /* nothing to do if not managed */
1328    if (!XtIsManaged(file_mgr_rec->current_directory_text))
1329       return;
1330
1331    /* get width of current directory line */
1332    XtSetArg (args[0], XmNwidth, &width);
1333    XtGetValues (file_mgr_rec->current_directory, args, 1);
1334
1335    /* get offset of the text widget */
1336    XtSetArg(args[0], XmNleftOffset, &left_offset);
1337    XtGetValues(file_mgr_rec->current_directory_text, args, 1);
1338
1339    /* set text widget width = current_directory width minus left offset */
1340    XtSetArg (args[0], XmNwidth, width - left_offset);
1341    XtSetValues (file_mgr_rec->current_directory_text, args, 1);
1342 }
1343
1344
1345 /*--------------------------------------------------------------------
1346  * get_textwidth
1347  *------------------------------------------------------------------*/
1348
1349 /* use Xmb functions if XFontSet is used. */
1350 static int
1351 get_textwidth( FileMgrData *fmd,
1352                char *str,
1353                int len)
1354 {
1355     int w = 0;
1356
1357     switch(fmd->cd_fonttype)
1358     {
1359         case XmFONT_IS_FONTSET:
1360             w = XmbTextEscapement(fmd->cd_fontset, str, len);
1361             break;
1362         case XmFONT_IS_FONT:
1363             w = XTextWidth(fmd->cd_font, str, len);
1364         default:
1365             break;
1366     }
1367     return(w);
1368 }
1369
1370 static void
1371 draw_imagestring( Display *display,
1372                   Drawable d,
1373                   FileMgrData *fmd,
1374                   GC gc,
1375                   int x, int y,
1376                   char *text,
1377                   int bytes)
1378 {
1379     switch(fmd->cd_fonttype)
1380     {
1381         case XmFONT_IS_FONTSET:
1382             XmbDrawImageString(display, d, fmd->cd_fontset, gc, x, y, text,
1383                                bytes);
1384             break;
1385         case XmFONT_IS_FONT:
1386             XDrawImageString(display, d, gc, x, y, text, bytes);
1387         default:
1388             break;
1389     }
1390 }
1391