1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37#define _CRT_SECURE_NO_DEPRECATE
38
39#include <windows.h>
40#include <windowsx.h>
41#include <direct.h>
42#include <stdio.h>
43
44#include "resource.h"
45
46extern HINSTANCE hInst;
47
48#define ITEM_BITMAPWIDTH     16
49#define ITEM_BITMAPHEIGHT    16
50#define ITEM_LEFTMARGIN       4
51#define ITEM_GAP              4
52
53static HWND hWndDirPicker;
54static HICON hIconDrives[5];
55static HICON hIconFolders[3];
56static LPSTR lpszStringToReturn;
57static char szUNCRoot[256] = "";
58
59UINT DriveType(UINT iType);
60
61static void fillComboBox(HWND hWnd)
62{
63  HWND hWndCB = GetDlgItem(hWnd, ID_COMBO_DIR);
64  HWND hWndTempLB = GetDlgItem(hWnd, ID_LISTTEMP_DIR);
65  if(hWndCB == NULL)
66    return;
67  ComboBox_ResetContent(hWndCB);
68  ListBox_ResetContent(hWndTempLB);
69  ListBox_Dir(hWndTempLB, DDL_DRIVES|DDL_EXCLUSIVE, (LPSTR)"*");
70
71  int iDriveCount = ListBox_GetCount(hWndTempLB);
72  int iCurDrive=_getdrive() - 1;
73
74  char szDrive[16];
75  char szItem[80];
76
77  for (int i = 0; i < iDriveCount;  i++)
78  {
79    ListBox_GetText(hWndTempLB, i, szDrive);
80    CharLower(szDrive);
81    int iDrive = szDrive[2] - 'a';
82    char szRoot[16];
83    sprintf(szRoot, "%c:\\", szDrive[2]);
84
85    int iType = DriveType(iDrive);
86
87    if(iType < 2)
88      continue;
89
90    //Start the item string with the drive letter, colon, and two spaces
91    sprintf(szItem, "%c%s", szDrive[2], ": ");
92
93    if((iType == DRIVE_FIXED) || (iType == DRIVE_RAMDISK))
94    { // get volume ID
95      char szVolumeID[80];
96      DWORD dwMaxLength;
97      DWORD dwSysFlags;
98      GetVolumeInformation(szRoot,             // address of root directory of the file system
99                           szVolumeID,         // address of name of the volume
100                           sizeof(szVolumeID), // length of lpVolumeNameBuffer
101                           NULL,               // address of volume serial number
102                           &dwMaxLength,       // address of system's maximum filename length
103                           &dwSysFlags,        // address of file system flags
104                           NULL,               // address of name of file system
105                           NULL);              // length of lpFileSystemNameBuffer
106
107      CharLower(szVolumeID);
108      lstrcat(szItem, szVolumeID);
109    }
110
111    //For network drives, go grab the \\server\share for it.
112    if(DRIVE_REMOTE == iType)
113    {
114      char szNet[64];
115      szNet[0] = '\0';
116      DWORD dwSizeOfszNet = sizeof(szNet);
117
118      sprintf(szDrive, "%c:", szDrive[2]);
119      CharUpper(szDrive);
120      WNetGetConnection(szDrive, szNet, &dwSizeOfszNet);
121      CharLower(szNet);
122      lstrcat(szItem, szNet);
123    }
124
125    int index = ComboBox_AddString(hWndCB, szItem);
126    ComboBox_SetItemData(hWndCB, index, MAKELONG(iDrive, iType));
127    if(iDrive == iCurDrive)
128      ComboBox_SetCurSel(hWndCB, index);
129    if(szUNCRoot[0] != '\0')
130      ComboBox_SetCurSel(hWndCB, -1);
131  }
132}
133
134static void fillTempLBWithDirs(HWND hWndTempLB, LPSTR lpszDir)
135{
136  BOOL bDone = FALSE;
137  WIN32_FIND_DATA ffdataStruct;
138
139  char szPath[_MAX_PATH];
140  char szFileName[_MAX_PATH];
141  lstrcpy(szPath, lpszDir);
142  if(szPath[lstrlen(szPath) - 1] == '\\')
143    szPath[lstrlen(szPath) - 1] = '\0';
144  lstrcpy(szFileName, szPath);
145  lstrcat(szFileName, "\\*");
146  HANDLE handle = FindFirstFile(szFileName, &ffdataStruct);
147  if(handle == INVALID_HANDLE_VALUE)
148  {
149    FindClose(handle);
150    return;
151  }
152  while(!bDone)
153  {
154    lstrcpy(szFileName, szPath);
155    lstrcat(szFileName, "\\");
156    lstrcat(szFileName, ffdataStruct.cFileName);
157    if(ffdataStruct. dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
158    {
159      char szStringToAdd[_MAX_PATH + 2];
160      lstrcpy(szStringToAdd, "[");
161      lstrcat(szStringToAdd, ffdataStruct.cFileName);
162      lstrcat(szStringToAdd, "]");
163      CharLower(szStringToAdd);
164      ListBox_AddString(hWndTempLB, szStringToAdd);
165    }
166    bDone = !FindNextFile(handle, &ffdataStruct);
167  }
168  FindClose(handle);
169}
170
171static void fillListBox(HWND hWnd, LPSTR lpszDir)
172{
173  HWND hWndLB = GetDlgItem(hWnd, ID_LIST_DIR);
174  HWND hWndTempLB = GetDlgItem(hWnd, ID_LISTTEMP_DIR);
175  HWND hWndEdit = GetDlgItem(hWnd, ID_EDIT_DIR);
176  if((hWndLB == NULL) || (lpszDir == NULL))
177    return;
178
179  int iLastChar = lstrlen(lpszDir);
180  if(lpszDir[iLastChar - 1] == '\\')
181    lpszDir[iLastChar - 1] = '\0';
182
183  SetWindowRedraw(hWndLB, FALSE);
184  ListBox_ResetContent(hWndLB);
185  ListBox_ResetContent(hWndTempLB);
186
187  LPSTR lpszLast;
188  lpszLast = CharLower(lpszDir);
189
190  SetWindowText(hWndLB, lpszDir);
191
192  char szDir[_MAX_DIR];
193  char szFullDir[_MAX_DIR];
194  sprintf(szFullDir, "%s", lpszDir);
195  sprintf(szDir, "%s\\*.*", lpszDir);
196
197  BOOL bFirst = TRUE;
198  char ch;
199  int index;
200  while (TRUE)
201  {
202    LPSTR lpsz;
203    if((lpszDir[0] == '\\') && (lpszDir[1] == '\\') && bFirst)
204      lpsz = strchr(lpszLast + lstrlen(szUNCRoot), '\\');
205    else
206      lpsz = strchr(lpszLast, '\\');
207    if(lpsz != NULL) {
208      if (bFirst)
209        ch = *(++lpsz);
210      else
211        ch = *lpsz;
212      *lpsz = 0;
213    }
214    else
215    {
216      //If we're looking at a drive only, then append a backslash
217      if (lpszLast == lpszDir && bFirst)
218        lstrcat(lpszLast, "\\");
219    }
220    //Add the drive string--includes the last one where lpsz == NULL
221    index = ListBox_AddString(hWndLB, lpszLast);
222
223    UINT i = (NULL != lpsz) ? ID_ICON_FOLDEROPEN : ID_ICON_OPENSELECT;
224    ListBox_SetItemData(hWndLB, index, MAKELONG(index, i));
225
226    if(NULL == lpsz)
227      break;
228
229      //Restore last character.
230    *lpsz = ch;
231    lpsz += (bFirst) ? 0 : 1;
232
233    bFirst=FALSE;
234    lpszLast = lpsz;
235  }
236  int indent = index + 1;
237
238  //Get available directories
239  fillTempLBWithDirs(hWndTempLB, lpszDir);
240
241  int itemCount = ListBox_GetCount(hWndTempLB);
242
243  int i=0;
244  for (i = 0; i < itemCount; i++) {
245    index = ListBox_GetText(hWndTempLB, i, lpszDir);
246    //Skip directories beginning with . (skipping . and ..)
247    if(lpszDir[1] == '.')
248      continue;
249    //Remove the ending ']'
250    iLastChar = lstrlen(lpszDir);
251    lpszDir[iLastChar - 1] = '\0';
252    //Add the string to the real directory list.
253    index = ListBox_AddString(hWndLB, lpszDir + 1);
254    ListBox_SetItemData(hWndLB, index, MAKELONG(indent, ID_ICON_FOLDERCLOSED));
255  }
256  //Force a listbox repaint.
257  SetWindowRedraw(hWndLB, TRUE);
258  InvalidateRect(hWndLB, NULL, TRUE);
259
260  if(szFullDir[lstrlen(szFullDir) - 1] == ':')
261    lstrcat(szFullDir, "\\");
262  Edit_SetText(hWndEdit, szFullDir);
263
264  GetScrollRange(hWndLB, SB_VERT, (LPINT)&i, (LPINT)&index);
265
266  if(!(i == 0 && index == 0))
267    ListBox_SetTopIndex(hWndLB, max((int)(index - 2), 0));
268
269  ListBox_SetCurSel(hWndLB, indent - 1);
270}
271
272static void onDrawItem(LPDRAWITEMSTRUCT lpdis, BOOL bDrive)
273{
274  if((int)lpdis->itemID < 0)
275    return;
276
277  char szItem[_MAX_DIR];
278  DWORD dwItemData;
279
280  if(bDrive)
281  {
282    dwItemData = ComboBox_GetItemData(lpdis->hwndItem, lpdis->itemID);
283    ComboBox_GetLBText(lpdis->hwndItem, lpdis->itemID, szItem);
284  }
285  else
286  {
287    dwItemData = ListBox_GetItemData(lpdis->hwndItem, lpdis->itemID);
288    ListBox_GetText(lpdis->hwndItem, lpdis->itemID, szItem);
289  }
290
291  if(lpdis->itemAction & (ODA_DRAWENTIRE | ODA_SELECT))
292  {
293    COLORREF colorText;
294    COLORREF colorBack;
295    if(lpdis->itemState & ODS_SELECTED)
296    {
297      colorText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
298      colorBack = SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT));
299    }
300    HICON hIcon;
301    int indent = 0;
302    if(bDrive)
303    {
304      int iType=(int)HIWORD(dwItemData);
305      switch (iType)
306      {
307        case DRIVE_REMOVABLE:
308          hIcon = hIconDrives[0];
309          break;
310        case DRIVE_FIXED:
311          hIcon = hIconDrives[1];
312          break;
313        case DRIVE_REMOTE:
314          hIcon = hIconDrives[2];
315          break;
316        case DRIVE_CDROM:
317          hIcon = hIconDrives[3];
318          break;
319        case DRIVE_RAMDISK:
320          hIcon = hIconDrives[4];
321          break;
322      }
323
324    }
325    else
326    {
327      int iconID = (int)HIWORD(lpdis->itemData);
328      switch (iconID)
329      {
330        case ID_ICON_FOLDERCLOSED:
331          hIcon = hIconFolders[0];
332          break;
333        case ID_ICON_FOLDEROPEN:
334          hIcon = hIconFolders[1];
335          break;
336        case ID_ICON_OPENSELECT:
337          hIcon = hIconFolders[2];
338          break;
339      }
340      indent = 4 * (1 + LOWORD(lpdis->itemData));
341    }
342
343    ExtTextOut(lpdis->hDC,
344               lpdis->rcItem.left + ITEM_LEFTMARGIN + ITEM_BITMAPWIDTH + ITEM_GAP + indent,
345               lpdis->rcItem.top,
346               ETO_OPAQUE | ETO_CLIPPED,
347               &lpdis->rcItem,
348               szItem,
349               lstrlen(szItem),
350               NULL);
351
352    BOOL res = DrawIcon(lpdis->hDC,
353                        lpdis->rcItem.left + ITEM_LEFTMARGIN + indent,
354                        lpdis->rcItem.top,
355                        hIcon);
356
357    if(lpdis->itemState & ODS_SELECTED)
358    {
359      SetTextColor(lpdis->hDC, colorText);
360      SetBkColor(lpdis->hDC, colorBack);
361    }
362  }
363  if((lpdis->itemAction & ODA_FOCUS) || (lpdis->itemState & ODS_FOCUS))
364    DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
365}
366
367static void fillUNCRootArray(LPSTR lpsz)
368{
369  char szCurDir[_MAX_PATH];
370  _getcwd(szCurDir, sizeof(szCurDir));
371  lstrcpy(szUNCRoot, lpsz);
372  if(szUNCRoot[lstrlen(szUNCRoot) - 1] == '\\')
373    szUNCRoot[lstrlen(szUNCRoot) - 1] = '\0';
374  for(;;)
375  {
376    LPSTR lptemp = strrchr(szUNCRoot, '\\');
377    if(lptemp == NULL)
378      break;
379    *lptemp = '\0';
380    if(_chdir(szUNCRoot) == -1)
381    {
382      *lptemp = '\\';
383      break;
384    }
385  }
386  _chdir(szCurDir);
387}
388
389static void onInitDialog(HWND hWnd, LPSTR lpsz)
390{
391  hWndDirPicker = hWnd;
392  lpszStringToReturn = lpsz;
393
394  hIconDrives[0] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVEFLOPPY));
395  hIconDrives[1] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVEHARD));
396  hIconDrives[2] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVENETWORK));
397  hIconDrives[3] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVECDROM));
398  hIconDrives[4] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVERAM));
399
400  hIconFolders[0] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_FOLDERCLOSED));
401  hIconFolders[1] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_FOLDEROPEN));
402  hIconFolders[2] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_OPENSELECT));
403
404  if(lpsz[0] == '\0')
405    _getcwd(lpsz, _MAX_PATH);
406  else if(lpsz[lstrlen(lpsz) - 1] == ':')
407    lstrcat(lpsz, "\\");
408
409  int ret = _chdir(lpsz);
410  if(ret == -1)
411  {
412    char szText[_MAX_PATH + 80];
413    sprintf(szText, "The specified directory %s\ncannot be found", lpsz);
414    MessageBox(GetParent(hWnd), szText, "Choose Directory", MB_ICONEXCLAMATION|MB_OK);
415    _getcwd(lpsz, _MAX_PATH);
416  }
417  if((lpsz[0] == '\\') && (lpsz[1] == '\\'))
418    fillUNCRootArray(lpsz);
419  fillListBox(hWnd, lpsz);
420  fillComboBox(hWnd);
421}
422
423static void shutDialog(HWND hWnd)
424{
425  szUNCRoot[0] = '\0';
426}
427
428static void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify)
429{
430  char szCurDir[_MAX_PATH];
431  switch(id)
432  {
433    case ID_LIST_DIR:
434      if(codeNotify == LBN_DBLCLK)
435      {
436        int index = ListBox_GetCurSel(hWndCtl);
437        DWORD dwItemData = ListBox_GetItemData(hWndCtl, index);
438
439        if(HIWORD(dwItemData) == ID_ICON_OPENSELECT)
440        {
441          shutDialog(hWnd);
442          char szString[_MAX_PATH];
443          Edit_GetText(GetDlgItem(hWndDirPicker, ID_EDIT_DIR), szString, sizeof(szString));
444          lstrcpy(lpszStringToReturn, szString);
445          EndDialog(hWnd, IDOK);
446          break;
447        }
448
449        ListBox_GetText(hWndCtl, index, szCurDir);
450
451        char szDir[_MAX_DIR];
452        LPSTR lpsz;
453        if((HIWORD(dwItemData) == ID_ICON_FOLDEROPEN) && (index != 0))
454        {
455          GetWindowText(hWndCtl, szDir, sizeof(szDir));
456          lpsz=_fstrstr(szDir, szCurDir);
457          *(lpsz + lstrlen(szCurDir)) = '\0';
458          lstrcpy(szCurDir, szDir);
459        }
460        if (_chdir(szCurDir) == 0)
461        {
462          _getcwd(szCurDir, _MAX_PATH);
463          fillListBox(hWndDirPicker, szCurDir);
464        }
465      }
466      break;
467    case ID_COMBO_DIR:
468      if(codeNotify == CBN_SELCHANGE)
469      {
470        char szDrive[80];
471        int index = ComboBox_GetCurSel(hWndCtl);
472        if(index == CB_ERR)
473          break;
474        ComboBox_GetLBText(hWndCtl, index, szDrive);
475
476        int iCurDrive = _getdrive();
477Retry:
478        HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
479        SetCapture(hWndDirPicker);
480        if((0 == _chdrive((int)(szDrive[0] - 'a' + 1))) && (NULL != _getcwd(szCurDir, _MAX_PATH)))
481        {
482          fillListBox(hWndDirPicker, szCurDir);
483          ListBox_SetTopIndex(GetDlgItem(hWndDirPicker, ID_LIST_DIR), 0);
484          SetCursor(hCursorOld);
485          ReleaseCapture();
486          break;
487        }
488        SetCursor(hCursorOld);
489        ReleaseCapture();
490
491        char szText[80];
492        sprintf(szText, "Cannot read drive %c:", szDrive[0]);
493        if(IDRETRY == MessageBox(hWndDirPicker, szText, "Choose Directory", MB_ICONEXCLAMATION|MB_RETRYCANCEL))
494          goto Retry;
495
496        //Changing drives failed so restore drive and selection
497        _chdrive(iCurDrive);
498
499        sprintf(szDrive, "%c:", (char)(iCurDrive + 'a' - 1));
500        index = ComboBox_SelectString(hWndCtl, -1, szDrive);
501      }
502      break;
503    case IDOK:
504      shutDialog(hWnd);
505      char szString[_MAX_PATH];
506      Edit_GetText(GetDlgItem(hWndDirPicker, ID_EDIT_DIR), szString, sizeof(szString));
507      lstrcpy(lpszStringToReturn, szString);
508      EndDialog(hWnd, IDOK);
509      break;
510    case IDCANCEL:
511      shutDialog(hWnd);
512      lpszStringToReturn[0] = '\0';
513      EndDialog(hWnd, IDCANCEL);
514      break;
515    default:
516      break;
517  }
518}
519
520static BOOL CALLBACK DirPickDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
521{
522  switch(msg) {
523    case WM_INITDIALOG:
524      onInitDialog(hWnd, (LPSTR)lParam);
525      break;
526    case WM_COMMAND:
527      HANDLE_WM_COMMAND(hWnd, wParam, lParam, onCommand);
528      break;
529    case WM_MEASUREITEM:
530    {
531      static int cyItem = -1;      //Height of a listbox item
532      LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
533      if(cyItem == -1)
534      {
535        HFONT hFont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0L);
536        if(hFont == NULL)
537          hFont = GetStockFont(SYSTEM_FONT);
538        HDC hDC = GetDC(hWnd);
539        HFONT hFontOld = SelectFont(hDC, hFont);
540        TEXTMETRIC tm;
541        GetTextMetrics(hDC, &tm);
542        cyItem = max(ITEM_BITMAPHEIGHT, tm.tmHeight);
543        SelectFont(hDC, hFontOld);
544        ReleaseDC(hWnd, hDC);
545      }
546
547      lpmis->itemHeight = cyItem;
548    }
549      break;
550    case WM_DRAWITEM:
551      onDrawItem((LPDRAWITEMSTRUCT)lParam, ((UINT)wParam == ID_COMBO_DIR));
552      return TRUE; // to prevent default action in listbox (drawing focus)
553    default:
554      return FALSE;
555  }
556  return TRUE;
557}
558
559/*
560 * DriveType
561 *
562 * Purpose:
563 *  Augments the Windows API GetDriveType with a call to the CD-ROM
564 *  extensions to determine if a drive is a floppy, hard disk, CD-ROM,
565 *  RAM-drive, or networked  drive.
566 *
567 * Parameters:
568 *  iDrive          UINT containing the zero-based drive index
569 *
570 * Return Value:
571 *  UINT            One of the following values describing the drive:
572 *                  DRIVE_FLOPPY, DRIVE_HARD, DRIVE_CDROM, DRIVE_RAMDISK,
573 *                  DRIVE_NETWORK.
574 *
575 * Copyright (c)1992 Kraig Brockschmidt, All Right Reserved
576 * Compuserve:  70750,2344
577 * Internet  :  kraigb@microsoft.com
578 *
579 */
580UINT DriveType(UINT iDrive)
581{
582  //Validate possible drive indices
583  if((0 > iDrive)  || (25 < iDrive))
584    return (UINT)-1;
585
586  static char path[] = "d:\\";
587  path[0] = 'a' + iDrive;
588  int iType = GetDriveType(path);
589
590  /*
591   * Under Windows NT, GetDriveType returns complete information
592   * not provided under Windows 3.x which we now get through other
593   * means.
594   */
595
596  return iType;
597}
598
599BOOL PickupDirectory(HWND hWndOwner, LPSTR lpszString)
600{
601  if(hWndOwner == NULL)
602    hWndOwner = GetDesktopWindow();
603  int ret = DialogBoxParam(hInst, MAKEINTRESOURCE(ID_DIALOG_CHOOSEDIR), hWndOwner, DirPickDlgProc, (LPARAM)lpszString);
604  return (ret == IDOK);
605}
606