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