/ windows / pvdialog.cpp
pvdialog.cpp
  1  /*******************************************************************************
  2   * pvdialog.cpp
  3   *
  4   * This module implements dialog-box routines for the Windows build of POV.
  5   *
  6   * Author: Christopher J. Cason.
  7   *
  8   * ---------------------------------------------------------------------------
  9   * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
 10   * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
 11   *
 12   * POV-Ray is free software: you can redistribute it and/or modify
 13   * it under the terms of the GNU Affero General Public License as
 14   * published by the Free Software Foundation, either version 3 of the
 15   * License, or (at your option) any later version.
 16   *
 17   * POV-Ray is distributed in the hope that it will be useful,
 18   * but WITHOUT ANY WARRANTY; without even the implied warranty of
 19   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20   * GNU Affero General Public License for more details.
 21   *
 22   * You should have received a copy of the GNU Affero General Public License
 23   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 24   * ---------------------------------------------------------------------------
 25   * POV-Ray is based on the popular DKB raytracer version 2.12.
 26   * DKBTrace was originally written by David K. Buck.
 27   * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 28   * ---------------------------------------------------------------------------
 29   * $File: //depot/public/povray/3.x/windows/pvdialog.cpp $
 30   * $Revision: #1 $
 31   * $Change: 6069 $
 32   * $DateTime: 2013/11/06 11:59:40 $
 33   * $Author: chrisc $
 34   *******************************************************************************/
 35  
 36  #define POVWIN_FILE
 37  #define _WIN32_IE COMMONCTRL_VERSION
 38  
 39  #include <windows.h>
 40  #include <commdlg.h>
 41  #include <commctrl.h>
 42  #include <htmlhelp.h>
 43  #include <setjmp.h>
 44  #include <string.h>
 45  #include "pvengine.h"
 46  #include "resource.h"
 47  #include "pvedit.h"
 48  #include "pvguiext.h"
 49  #include "pvdisplay.h"
 50  #include "pvdialog.h"
 51  
 52  // this must be the last file included
 53  #include "syspovdebug.h"
 54  
 55  namespace povwin
 56  {
 57  
 58  bool                    otaChecked ;
 59  const char              *otaTitle ;
 60  const char              *otaText ;
 61  const char              *otaHelpString ;
 62  
 63  
 64  extern int              listbox_ychar ;
 65  extern int              tb_combo_sel ;
 66  extern char             engineHelpPath [_MAX_PATH] ;
 67  extern char             lastRenderName [_MAX_PATH] ;
 68  extern char             lastRenderPath [_MAX_PATH] ;
 69  extern char             lastBitmapPath [_MAX_PATH] ;
 70  extern char             lastQueuePath [_MAX_PATH] ;
 71  extern char             lastSecondaryIniFilePath [_MAX_PATH] ;
 72  extern char             DefaultRenderIniFileName [_MAX_PATH] ;
 73  extern char             SecondaryRenderIniFileName [_MAX_PATH] ;
 74  extern char             RegionStr [128] ;
 75  extern char             TempRegionStr [128] ;
 76  extern char             command_line [_MAX_PATH * 3] ;
 77  extern char             old_command_line [_MAX_PATH * 3] ;
 78  extern char             SecondaryRenderIniFileSection [64] ;
 79  extern char             source_file_name [_MAX_PATH] ;
 80  extern char             render_complete_sound [_MAX_PATH] ;
 81  extern char             parse_error_sound [_MAX_PATH] ;
 82  extern char             render_error_sound [_MAX_PATH] ;
 83  extern char             queued_files [MAX_QUEUE] [_MAX_PATH] ;
 84  extern bool             IsW95UserInterface ;
 85  extern bool             use_editors;
 86  extern bool             running_demo ;
 87  extern bool             render_complete_sound_enabled ;
 88  extern bool             parse_error_sound_enabled  ;
 89  extern bool             render_error_sound_enabled ;
 90  extern HWND             render_window ;
 91  extern HWND             toolbar_window ;
 92  extern HWND             aux_toolbar_window ;
 93  extern HWND             window_list [MAX_WINDOWS] ;
 94  extern HWND             toolbar_combobox ;
 95  extern HWND             rebar_window ;
 96  extern HWND             StatusWindow ;
 97  extern HWND             toolbar_cmdline ;
 98  extern HWND             tab_window ;
 99  extern HWND             main_window ;
100  extern HMENU            hMainMenu ;
101  extern unsigned         screen_width ;
102  extern unsigned         screen_height ;
103  extern unsigned         renderwin_8bits ;
104  extern unsigned         auto_render ;
105  extern unsigned         queued_file_count ;
106  extern unsigned         ThreadCount ;
107  extern HH_AKLINK        hh_aklink ;
108  
109  // include prototype here to avoid the need to include commdlg.h in all files
110  extern void init_ofn (OPENFILENAME *ofn, HWND hWnd, char *title, char *name, int maxlen, char *lastPath, char *defaultExt);
111  
112  char *GetINIFile (HWND hWnd, char *path)
113  {
114    int           result ;
115    OPENFILENAME  ofnTemp ;
116    static char   name [_MAX_PATH] ;
117  
118    validatePath (path) ;
119    init_ofn (&ofnTemp, hWnd, "Choose INI File", name, sizeof (name), path, "ini") ;
120    ofnTemp.lpstrFilter = "INI files (*.ini)\0*.ini\0Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0" ;
121    if ((result = GetOpenFileName (&ofnTemp)) != 0)
122    {
123      strcpy (path, name) ;
124      path [ofnTemp.nFileOffset - 1] = '\0' ;
125    }
126    return (result ? name : NULL) ;
127  }
128  
129  void AddQueue (HWND hWnd, HWND hlb)
130  {
131    int           queue_count ;
132    char          name [8192] ;
133    char          *s ;
134    char          str [_MAX_PATH] ;
135    OPENFILENAME  ofnTemp ;
136  
137    queue_count = SendMessage (hlb, LB_GETCOUNT, 0, 0) ;
138    if (queue_count >= MAX_QUEUE)
139    {
140      PovMessageBox ("File queue is full", "Cannot add any more files!") ;
141      return ;
142    }
143    strcpy (name, lastRenderName) ;
144    name [strlen (name) + 1] = '\0' ;
145    validatePath (lastQueuePath) ;
146    init_ofn (&ofnTemp, hWnd, "Add to Queue", name, sizeof (name), lastQueuePath, "pov") ;
147    ofnTemp.lpstrFilter = "POV source and INI (*.pov;*.ini)\0*.pov;*.ini\0POV files (*.pov)\0*.pov\0INI files (*.ini)\0*.ini\0All Files (*.*)\0*.*\0" ;
148    ofnTemp.Flags |= OFN_ALLOWMULTISELECT ;
149    if (GetOpenFileName (&ofnTemp) != false)
150    {
151      // convert spaces into NULL's if we're not using the new interface so it works with the below code
152      if (!IsW95UserInterface)
153        for (s = name ; *s ; s++)
154          if (*s == ' ')
155            *s = '\0' ;
156      if (ofnTemp.nFileOffset < strlen (name))
157      {
158        strcpy (lastQueuePath, name) ;
159        lastQueuePath [ofnTemp.nFileOffset - 1] = '\0' ;
160        SendMessage (hlb, LB_ADDSTRING, 0, (LPARAM) name) ;
161      }
162      else
163      {
164        s = name ;
165        strcpy (lastQueuePath, name) ;
166        for (s += strlen (s) + 1 ; *s ; s += strlen (s) + 1)
167        {
168          if (queue_count++ >= MAX_QUEUE)
169          {
170            PovMessageBox ("File queue is full", "Cannot add any more files!") ;
171            return ;
172          }
173          joinPath (str, lastQueuePath, s) ;
174          _strlwr (str) ;
175          SendMessage (hlb, LB_ADDSTRING, 0, (LPARAM) str) ;
176        }
177      }
178    }
179  }
180  
181  char *SelectSound (HWND hWnd, char *currentSound)
182  {
183    int           result ;
184    char          path [_MAX_PATH] ;
185    static char   name [_MAX_PATH] ;
186    OPENFILENAME  ofnTemp ;
187  
188    splitpath (currentSound, path, name) ;
189    validatePath (path) ;
190    init_ofn (&ofnTemp, hWnd, "Select Sound File", name, sizeof (name), path, "wav") ;
191    ofnTemp.Flags &= ~OFN_HIDEREADONLY ;
192    ofnTemp.lpstrFilter = "Sound Files (*.wav)\0*.wav\0All Files (*.*)\0*.*\0" ;
193    if ((result = GetOpenFileName (&ofnTemp)) != 0)
194      return (name) ;
195    return (NULL) ;
196  }
197  
198  SIZE *GetTextExtent (HWND hWnd, LPCSTR text)
199  {
200    int         result ;
201    HDC         hDC ;
202    HFONT       hFont = (HFONT) SendMessage (hWnd, WM_GETFONT, 0, 0) ;
203    static SIZE extent ;
204  
205    if (hFont == NULL)
206      return (0) ;
207    hDC = GetDC (hWnd) ;
208    HFONT hOldFont = (HFONT) SelectObject (hDC, hFont);
209    result = GetTextExtentPoint32 (hDC, text, (int) strlen(text), &extent) ;
210    SelectObject (hDC, hOldFont) ;
211    ReleaseDC (hWnd, hDC) ;
212    if (!result)
213      return (0) ;
214    return (&extent) ;
215  }
216  
217  int GetTextWidth (HWND hWnd, LPCSTR text)
218  {
219    int         width = 0;
220    int         result = 0;
221    SIZE        extent;
222    HDC         hDC ;
223    HFONT       hFont = (HFONT) SendMessage (hWnd, WM_GETFONT, 0, 0) ;
224    const char  *s;
225  
226    if (hFont == NULL)
227      return (0) ;
228    hDC = GetDC (hWnd) ;
229    HFONT hOldFont = (HFONT) SelectObject (hDC, hFont);
230    while (strlen(text) > 0)
231    {
232      if ((s = strchr(text, '\n')) == NULL)
233        s = text + strlen(text) - 1;
234      if ((result = GetTextExtentPoint32 (hDC, text, (int) (s - text), &extent)) == 0)
235        return -1;
236      text = s + 1;
237      if (extent.cx > width)
238        width = extent.cx;
239    }
240    SelectObject (hDC, hOldFont) ;
241    ReleaseDC (hWnd, hDC) ;
242    return width;
243  }
244  
245  void NudgeChildWindow (HWND hDlg, int idItem, int pixels)
246  {
247    RECT        rect ;
248    HWND        hWnd ;
249  
250    hWnd = GetDlgItem (hDlg, idItem) ;
251    GetWindowRect (hWnd, &rect) ;
252    ScreenToClient (hDlg, (LPPOINT) &rect) ;
253    SetWindowPos (hWnd, NULL, rect.left + pixels, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER) ;
254  }
255  
256  INT_PTR CALLBACK PovCommandLineDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
257  {
258    char        *s ;
259    char        str [_MAX_PATH * 2] ;
260    char        str1 [_MAX_PATH] ;
261    char        str2 [_MAX_PATH] ;
262    static char editINIname [_MAX_PATH] ;
263    static char editFilename [_MAX_PATH] ;
264    static char lastIniFilename [_MAX_PATH] ;
265    static bool had_region = false ;
266    static struct stat statbuf1 ;
267    static struct stat statbuf2 ;
268  
269    switch (message)
270    {
271      case WM_INITDIALOG :
272           had_region = false ;
273           hh_aklink.pszKeywords = "command-line options" ;
274           if (use_editors)
275           {
276             if ((EditGetFlags () & EDIT_MSG_SELECTED) == 0)
277             {
278               if ((s = EditGetFilename(false)) != NULL)
279               {
280                 EnableWindow (GetDlgItem (hDlg, IDC_PRESETSOURCEPATH), false) ;
281                 EnableWindow (GetDlgItem (hDlg, IDC_PRESETSOURCENAME), false) ;
282                 EnableWindow (GetDlgItem (hDlg, IDC_SOURCEDEFAULT), false) ;
283                 EnableWindow (GetDlgItem (hDlg, IDC_BROWSESOURCEFILE), false) ;
284                 EnableWindow (GetDlgItem (hDlg, IDC_EDITRENDER), false) ;
285                 splitpath (s, lastRenderPath, lastRenderName) ;
286                 EnableWindow (GetDlgItem (hDlg, IDC_EDITRENDER), false) ;
287               }
288             }
289             SetDlgItemText (hDlg, IDC_PRESETSOURCEPATH, lastRenderPath) ;
290             SetDlgItemText (hDlg, IDC_PRESETSOURCENAME, lastRenderName) ;
291           }
292           else
293           {
294             SetDlgItemText (hDlg, IDC_PRESETSOURCEPATH, lastRenderPath) ;
295             SetDlgItemText (hDlg, IDC_PRESETSOURCENAME, lastRenderName) ;
296             EnableWindow (GetDlgItem (hDlg, IDC_EDITRENDER), false) ;
297             EnableWindow (GetDlgItem (hDlg, IDC_EDITINI), false) ;
298           }
299           SendDlgItemMessage (hDlg, IDC_PRESETSOURCENAME, EM_LIMITTEXT, 64, 0L) ;
300           SendDlgItemMessage (hDlg, IDC_INIFILENAME, EM_LIMITTEXT, 64, 0L) ;
301           _strupr (SecondaryRenderIniFileName) ;
302           validatePath (lastRenderPath) ;
303           CenterWindowRelative ((HWND) lParam, hDlg, true, true) ;
304           FitWindowInWindow (NULL, hDlg) ;
305           if (strlen (TempRegionStr))
306           {
307             if (strlen (command_line))
308             {
309               strcpy (str, command_line) ;
310               strcat (str, TempRegionStr) ;
311             }
312             else
313               strcpy (str, TempRegionStr + 1) ;
314             strcpy (RegionStr, TempRegionStr) ;
315             TempRegionStr [0] = '\0' ;
316             SetDlgItemText (hDlg, IDC_COMMANDLINE, str) ;
317           }
318           else
319           {
320             SetDlgItemText (hDlg, IDC_COMMANDLINE, command_line) ;
321             if (RegionStr [0])
322               if (strstr (command_line, RegionStr + 1) != NULL)
323                 had_region = true ;
324           }
325           if (SecondaryRenderIniFileName [0] != '\0')
326           {
327             if (!hasTrailingPathSeparator(SecondaryRenderIniFileName))
328             {
329               splitpath (SecondaryRenderIniFileName, str1, str2) ;
330               validatePath (str1) ;
331               strcpy (editINIname, str2) ;
332               SetDlgItemText (hDlg, IDC_INIFILEPATH, str1) ;
333               SetDlgItemText (hDlg, IDC_INIFILENAME, str2) ;
334               extract_ini_sections (SecondaryRenderIniFileName, GetDlgItem (hDlg, IDC_INIFILESECTION)) ;
335               SendMessage (toolbar_combobox, CB_GETLBTEXT, SendMessage (toolbar_combobox, CB_GETCURSEL, 0, 0), (LPARAM) SecondaryRenderIniFileSection) ;
336               SendDlgItemMessage (hDlg, IDC_INIFILESECTION, CB_SELECTSTRING, -1, (LPARAM) SecondaryRenderIniFileSection) ;
337               strcpy (lastIniFilename, SecondaryRenderIniFileName) ;
338               stat (SecondaryRenderIniFileName, &statbuf1) ;
339             }
340             else
341               SetDlgItemText (hDlg, IDC_INIFILEPATH, SecondaryRenderIniFileName) ;
342           }
343           SetFocus (GetDlgItem (hDlg, IDC_COMMANDLINE)) ;
344           return (false) ;
345  
346      case WM_CTLCOLORBTN:
347      case WM_CTLCOLORDLG:
348      case WM_CTLCOLOREDIT:
349      case WM_CTLCOLORLISTBOX:
350      case WM_CTLCOLORSCROLLBAR:
351      case WM_CTLCOLORSTATIC:
352           return (DefWindowProc (hDlg, message, wParam, lParam)) ;
353  
354      case WM_COMMAND :
355           switch (LOWORD (wParam))
356           {
357             case IDC_INIFILESECTION :
358                  if (HIWORD (wParam) == CBN_SETFOCUS)
359                  {
360                    stat (lastIniFilename, &statbuf2) ;
361                    if (statbuf1.st_atime != statbuf2.st_atime)
362                    {
363                      statbuf1 = statbuf2 ;
364                      GetDlgItemText (hDlg, IDC_INIFILESECTION, str, sizeof (str)) ;
365                      extract_ini_sections (lastIniFilename, GetDlgItem (hDlg, IDC_INIFILESECTION)) ;
366                      SendDlgItemMessage (hDlg, IDC_INIFILESECTION, CB_SELECTSTRING, -1, (LPARAM) str) ;
367                    }
368                    return (true) ;
369                  }
370                  return (false) ;
371  
372             case IDC_EDITRENDER :
373                  GetDlgItemText (hDlg, IDC_PRESETSOURCEPATH, str1, sizeof (str1)) ;
374                  GetDlgItemText (hDlg, IDC_PRESETSOURCENAME, str2, sizeof (str2)) ;
375                  validatePath (str1) ;
376                  strcat (str1, "\\") ;
377                  strcat (str1, str2) ;
378                  if (EditOpenFile (str1))
379                  {
380                    EndDialog (hDlg, false) ;
381                    return (true) ;
382                  }
383                  return (true) ;
384  
385             case IDC_EDITINI :
386                  GetDlgItemText (hDlg, IDC_INIFILEPATH, str1, sizeof (str1)) ;
387                  GetDlgItemText (hDlg, IDC_INIFILENAME, str2, sizeof (str2)) ;
388                  validatePath (str1) ;
389                  strcat (str1, "\\") ;
390                  strcat (str1, str2) ;
391                  if (EditOpenFile (str1))
392                  {
393                    EndDialog (hDlg, false) ;
394                    return (true) ;
395                  }
396                  return (true) ;
397  
398             case IDC_BROWSEINIFILE :
399                  if ((s = GetINIFile (hDlg, lastSecondaryIniFilePath)) != NULL)
400                  {
401                    _strupr (s) ;
402                    splitpath (s, str1, str2) ;
403                    validatePath (str1) ;
404                    SetDlgItemText (hDlg, IDC_INIFILEPATH, str1) ;
405                    SetDlgItemText (hDlg, IDC_INIFILENAME, str2) ;
406                    if (strcmp (s, lastIniFilename))
407                    {
408                      extract_ini_sections (s, GetDlgItem (hDlg, IDC_INIFILESECTION)) ;
409                      strcpy (lastIniFilename, s) ;
410                      stat (lastIniFilename, &statbuf1) ;
411                    }
412                  }
413                  return (true) ;
414  
415             case IDC_INIFILENAME :
416                  if (HIWORD (wParam) == EN_KILLFOCUS)
417                  {
418                    GetDlgItemText (hDlg, IDC_INIFILEPATH, str1, sizeof (str1)) ;
419                    GetDlgItemText (hDlg, IDC_INIFILENAME, str2, sizeof (str2)) ;
420                    validatePath (str1) ;
421                    joinPath (str, str1, str2) ;
422                    if (_stricmp (str, lastIniFilename))
423                    {
424                      extract_ini_sections (str, GetDlgItem (hDlg, IDC_INIFILESECTION)) ;
425                      strcpy (lastIniFilename, str) ;
426                    }
427                    return (true) ;
428                  }
429                  if (HIWORD (wParam) == EN_UPDATE)
430                  {
431                    GetDlgItemText (hDlg, IDC_INIFILENAME, str, sizeof (str)) ;
432                    if (strchr (str, '\\') != NULL) // TODO FIXME - shouldn't we also reject '/' and strings starting drive letter?
433                      SetDlgItemText (hDlg, IDC_INIFILENAME, editINIname) ;
434                    else
435                      strcpy (editINIname, str) ;
436                  }
437                  return (true) ;
438  
439             case IDC_RESETINI :
440                  SetDlgItemText (hDlg, IDC_INIFILENAME, "") ;
441                  GetDlgItemText (hDlg, IDC_INIFILEPATH, lastIniFilename, sizeof (lastIniFilename)) ;
442                  SendMessage (GetDlgItem (hDlg, IDC_INIFILESECTION), CB_RESETCONTENT, 0, 0L) ;
443                  return (true) ;
444  
445             case IDC_INIDEFAULT :
446                  sprintf (str, "%sini", DocumentsPath) ;
447                  SetDlgItemText (hDlg, IDC_INIFILEPATH, str) ;
448                  SetDlgItemText (hDlg, IDC_INIFILENAME, "quickres.ini") ;
449                  SendMessage (hDlg, WM_COMMAND, (EN_KILLFOCUS << 16) | IDC_INIFILENAME, 0L) ;
450                  return (true) ;
451  
452             case IDC_PRESETSOURCENAME :
453                  if (HIWORD (wParam) == EN_UPDATE)
454                  {
455                    GetDlgItemText (hDlg, IDC_PRESETSOURCENAME, str, sizeof (str)) ;
456                    if (strchr (str, '\\') != NULL) // TODO FIXME - shouldn't we also reject '/' and strings starting drive letter?
457                      SetDlgItemText (hDlg, IDC_PRESETSOURCENAME, editFilename) ;
458                    else
459                      strcpy (editFilename, str) ;
460                  }
461                  return (true) ;
462  
463             case IDC_BROWSESOURCEFILE :
464                  if ((s = file_open (hDlg)) != NULL)
465                  {
466                    splitpath (s, str1, str2) ;
467                    validatePath (str1) ;
468                    SetDlgItemText (hDlg, IDC_PRESETSOURCEPATH, str1) ;
469                    SetDlgItemText (hDlg, IDC_PRESETSOURCENAME, str2) ;
470                  }
471                  return (true) ;
472  
473             case IDC_SOURCEDEFAULT :
474                  sprintf (str, "%sScenes\\advanced", DocumentsPath) ;
475                  SetDlgItemText (hDlg, IDC_PRESETSOURCEPATH, str) ;
476                  SetDlgItemText (hDlg, IDC_PRESETSOURCENAME, "Biscuit.pov") ;
477                  return (true) ;
478  
479             case IDC_SET :
480             case IDC_RENDER :
481             case IDC_RENDERa:    // IDC_RENDERa is defined because the render dialog
482                                  // has 2 render buttons on it, and VC++ complains
483                  if (!running_demo)
484                  {
485                    GetDlgItemText (hDlg, IDC_PRESETSOURCEPATH, lastRenderPath, sizeof (lastRenderPath)) ;
486                    GetDlgItemText (hDlg, IDC_PRESETSOURCENAME, lastRenderName, sizeof (lastRenderName)) ;
487                    validatePath (lastRenderPath) ;
488  //                _strupr (lastRenderPath) ;
489  //                _strupr (lastRenderName) ;
490                    joinPath (source_file_name, lastRenderPath, lastRenderName) ;
491                  }
492                  GetDlgItemText (hDlg, IDC_INIFILEPATH, str1, sizeof (str1)) ;
493                  GetDlgItemText (hDlg, IDC_INIFILENAME, str2, sizeof (str2)) ;
494                  validatePath (str1) ;
495                  strcpy (lastSecondaryIniFilePath, str1) ;
496                  joinPath (SecondaryRenderIniFileName, str1, str2) ;
497                  _strupr (SecondaryRenderIniFileName) ;
498                  GetDlgItemText (hDlg, IDC_INIFILESECTION, SecondaryRenderIniFileSection, sizeof (SecondaryRenderIniFileSection)) ;
499                  GetDlgItemText (hDlg, IDC_COMMANDLINE, command_line, sizeof (command_line)) ;
500                  SendMessage (toolbar_cmdline, WM_SETTEXT, 0, (LPARAM) command_line) ;
501                  extract_ini_sections_ex (SecondaryRenderIniFileName, toolbar_combobox) ;
502                  SendMessage (toolbar_combobox, CB_ADDSTRING, 0, (LPARAM) "More Resolutions ...") ;
503                  tb_combo_sel = select_combo_item_ex (toolbar_combobox, SecondaryRenderIniFileSection) ;
504                  if (tb_combo_sel == -1)
505                    tb_combo_sel = 0 ;
506  
507                  // was there a region string on the command line before it was edited ?
508                  if (had_region)
509                  {
510                    // if so, is it still there now, in an unchanged form ?
511                    // (we look at RegionStr + 1 since they always start with a space).
512                    if (strstr (command_line, RegionStr + 1) == NULL)
513                    {
514                      // it's not, so we remove the region string.
515                      RegionStr [0] = '\0' ;
516                    }
517                  }
518  
519                  if (LOWORD (wParam) == IDC_RENDER || LOWORD(wParam) == IDC_RENDERa)
520                  {
521                    if (EditSaveModified (NULL) == 0)
522                      return (true) ;
523                    EndDialog (hDlg, true) ;
524                  }
525                  else
526                    EndDialog (hDlg, false) ;
527                  return (true) ;
528  
529             case IDC_COMMANDHELP :
530                  hh_aklink.pszKeywords = "command-line dialog" ;
531                  HtmlHelp (NULL, engineHelpPath, HH_KEYWORD_LOOKUP, (DWORD_PTR) &hh_aklink) ;
532                  return (true) ;
533  
534             case IDCANCEL :
535                  EndDialog (hDlg, false) ;
536                  return (true) ;
537           }
538           break ;
539    }
540    return (false) ;
541  }
542  
543  INT_PTR CALLBACK PovShortCommandLineDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
544  {
545    static bool had_region = false ;
546  
547    switch (message)
548    {
549      case WM_INITDIALOG :
550           had_region = false ;
551           hh_aklink.pszKeywords = "command-line options" ;
552           CenterWindowRelative ((HWND) lParam, hDlg, true, true) ;
553           FitWindowInWindow (NULL, hDlg) ;
554           SetDlgItemText (hDlg, IDC_COMMANDLINE, command_line) ;
555           if (RegionStr [0])
556             if (strstr (command_line, RegionStr + 1) != NULL)
557               had_region = true ;
558           return (true) ;
559  
560      case WM_CTLCOLORBTN:
561      case WM_CTLCOLORDLG:
562      case WM_CTLCOLOREDIT:
563      case WM_CTLCOLORLISTBOX:
564      case WM_CTLCOLORSCROLLBAR:
565      case WM_CTLCOLORSTATIC:
566           return (DefWindowProc (hDlg, message, wParam, lParam));
567  
568      case WM_COMMAND :
569           switch (LOWORD (wParam))
570           {
571             case IDC_RENDER :
572             case IDC_RENDERa:
573                  GetDlgItemText (hDlg, IDC_COMMANDLINE, command_line, sizeof (command_line) - 1) ;
574                  SendMessage (toolbar_cmdline, WM_SETTEXT, 0, (LPARAM) command_line) ;
575  
576                  // was there a region string on the command line before it was edited ?
577                  if (had_region)
578                  {
579                    // if so, is it still there now, in an unchanged form ?
580                    // (we look at RegionStr + 1 since they always start with a space).
581                    if (strstr (command_line, RegionStr + 1) == NULL)
582                    {
583                      // it's not, so we remove the region string.
584                      RegionStr [0] = '\0' ;
585                    }
586                  }
587  
588                  if (LOWORD (wParam) == IDC_RENDER || LOWORD(wParam) == IDC_RENDERa)
589                  {
590                    if (EditSaveModified (NULL) == 0)
591                      return (true) ;
592                    EndDialog (hDlg, true) ;
593                  }
594                  else
595                    EndDialog (hDlg, false) ;
596  
597                  return (true) ;
598  
599             case IDCANCEL :
600                  EndDialog (hDlg, false) ;
601                  return (true) ;
602           }
603           break ;
604    }
605    return (false) ;
606  }
607  
608  INT_PTR CALLBACK PovFileQueueDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
609  {
610    int                   i ;
611    char                  str [64] ;
612    HWND                  hlb ;
613    DRAWITEMSTRUCT        *d ;
614    MEASUREITEMSTRUCT     *m ;
615    static HBRUSH         hbr ;
616  
617    switch (message)
618    {
619      case WM_INITDIALOG :
620           hh_aklink.pszKeywords = "File Queue" ;
621           CenterWindowRelative ((HWND) lParam, hDlg, true, true) ;
622           FitWindowInWindow (NULL, hDlg) ;
623           hlb = GetDlgItem (hDlg, IDC_FILEQUEUE) ;
624           hbr = CreateSolidBrush (GetSysColor (COLOR_BTNFACE)) ;
625           for (i = 0 ; i < queued_file_count ; i++)
626             SendMessage (hlb, LB_ADDSTRING, 0, (LPARAM) queued_files [i]) ;
627           sprintf (str, "Queue has %d entr%s", queued_file_count, queued_file_count != 1 ? "ies" : "y") ;
628           SetDlgItemText (hDlg, IDC_QUEUEENTRIES, str) ;
629           CheckDlgButton (hDlg, IDC_RELOADQUEUE, GetHKCU("FileQueue", "ReloadOnStartup", 0)) ;
630           CheckDlgButton (hDlg, IDC_AUTORENDER, auto_render) ;
631           return (true) ;
632  
633      case WM_CTLCOLORBTN:
634      case WM_CTLCOLORDLG:
635      case WM_CTLCOLOREDIT:
636      case WM_CTLCOLORLISTBOX:
637      case WM_CTLCOLORSCROLLBAR:
638      case WM_CTLCOLORSTATIC:
639           return(DefWindowProc(hDlg, message, wParam, lParam));
640  
641      case WM_COMMAND :
642           switch (LOWORD (wParam))
643           {
644             case IDOK :
645                  hlb = GetDlgItem (hDlg, IDC_FILEQUEUE) ;
646                  queued_file_count = SendMessage (hlb, LB_GETCOUNT, 0, 0) ;
647                  if (queued_file_count > MAX_QUEUE)
648                    queued_file_count = MAX_QUEUE ;
649                  for (i = 0 ; i < queued_file_count ; i++)
650                    SendMessage (hlb, LB_GETTEXT, i, (LPARAM) queued_files [i]) ;
651                  auto_render = IsDlgButtonChecked (hDlg, IDC_AUTORENDER) ;
652                  PVCheckMenuItem (CM_AUTORENDER, auto_render ? MF_CHECKED : MF_UNCHECKED) ;
653                  PutHKCU ("FileQueue", "ReloadOnStartup", IsDlgButtonChecked (hDlg, IDC_RELOADQUEUE)) ;
654                  update_queue_status (true) ;
655                  DeleteObject (hbr) ;
656                  EndDialog (hDlg, true) ;
657                  return (true) ;
658  
659             case IDCANCEL :
660                  DeleteObject (hbr) ;
661                  EndDialog (hDlg, false) ;
662                  return (true) ;
663  
664             case IDC_DELETEFILE :
665                  hlb = GetDlgItem (hDlg, IDC_FILEQUEUE) ;
666                  if ((i = SendMessage (hlb, LB_GETCURSEL, 0, 0)) != LB_ERR)
667                  {
668                    SendMessage (hlb, LB_DELETESTRING, i, 0) ;
669                    if (i)
670                      i-- ;
671                    SendMessage (hlb, LB_SETCURSEL, i, 0) ;
672                  }
673                  i = SendMessage (hlb, LB_GETCOUNT, 0, 0) ;
674                  sprintf (str, "Queue will have %d entr%s", i, i != 1 ? "ies" : "y") ;
675                  SetDlgItemText (hDlg, IDC_QUEUEENTRIES, str) ;
676                  return (true) ;
677  
678             case IDC_ADDFILE :
679                  hlb = GetDlgItem (hDlg, IDC_FILEQUEUE) ;
680                  AddQueue (hDlg, hlb) ;
681                  i = SendMessage (hlb, LB_GETCOUNT, 0, 0) ;
682                  sprintf (str, "Queue will have %d entr%s", i, i != 1 ? "ies" : "y") ;
683                  SetDlgItemText (hDlg, IDC_QUEUEENTRIES, str) ;
684                  return (true) ;
685  
686             case IDC_CONTEXTHELP :
687                  hh_aklink.pszKeywords = "File Queue" ;
688                  HtmlHelp (NULL, engineHelpPath, HH_KEYWORD_LOOKUP, (DWORD_PTR) &hh_aklink) ;
689                  return (true) ;
690           }
691           break ;
692  
693      case WM_MEASUREITEM :
694           if (wParam == IDC_FILEQUEUE)
695           {
696             m = (MEASUREITEMSTRUCT *) lParam ;
697             m->itemHeight = listbox_ychar ;
698             return (true) ;
699           }
700           else
701             return (false) ;
702  
703      case WM_DRAWITEM :
704           if (wParam == IDC_FILEQUEUE)
705           {
706             d = (DRAWITEMSTRUCT *) lParam ;
707             draw_ordinary_listbox (d, true) ;
708             return (true) ;
709           }
710           else
711             return (false) ;
712    }
713    return (false) ;
714  }
715  
716  INT_PTR CALLBACK PovFeatureAdviceDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
717  {
718    switch (message)
719    {
720      case WM_INITDIALOG :
721           hh_aklink.pszKeywords = otaHelpString ;
722           SetWindowText (hDlg, otaTitle) ;
723           SetDlgItemText (hDlg, IDC_ADVICETEXT, otaText) ;
724           CheckDlgButton (hDlg, IDC_DONTTELLMEAGAIN, otaChecked ? BST_CHECKED : BST_UNCHECKED) ;
725           if (lParam == 0)
726             lParam = (LPARAM) GetDesktopWindow () ;
727           CenterWindowRelative ((HWND) lParam, hDlg, true, true) ;
728           return (true) ;
729  
730      case WM_CTLCOLORBTN:
731      case WM_CTLCOLORDLG:
732      case WM_CTLCOLOREDIT:
733      case WM_CTLCOLORLISTBOX:
734      case WM_CTLCOLORSCROLLBAR:
735      case WM_CTLCOLORSTATIC:
736           return (DefWindowProc (hDlg, message, wParam, lParam));
737  
738      case WM_COMMAND :
739           switch (LOWORD (wParam))
740           {
741             case IDOK :
742                  otaChecked = IsDlgButtonChecked (hDlg, IDC_DONTTELLMEAGAIN) == BST_CHECKED ;
743                  EndDialog (hDlg, true) ;
744                  return (true) ;
745  
746             case IDCANCEL :
747                  otaChecked = false ;
748                  EndDialog (hDlg, false) ;
749                  return (true) ;
750  
751             case IDC_CONTEXTHELP :
752                  hh_aklink.pszKeywords = otaHelpString ;
753                  HtmlHelp (NULL, engineHelpPath, HH_KEYWORD_LOOKUP, (DWORD_PTR) &hh_aklink) ;
754                  return (true) ;
755           }
756           break ;
757  
758    }
759    return (false) ;
760  }
761  
762  INT_PTR CALLBACK PovSoundsDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
763  {
764    char        *s ;
765  
766    switch (message)
767    {
768      case WM_INITDIALOG :
769           hh_aklink.pszKeywords = "sounds" ;
770           CenterWindowRelative ((HWND) lParam, hDlg, true, true) ;
771           FitWindowInWindow (NULL, hDlg) ;
772           SetDlgItemText (hDlg, IDC_SOUND_RENDERCOMPLETE, render_complete_sound) ;
773           SetDlgItemText (hDlg, IDC_SOUND_PARSEERROR, parse_error_sound) ;
774           SetDlgItemText (hDlg, IDC_SOUND_RENDERERROR, render_error_sound) ;
775           CheckDlgButton (hDlg, IDC_ENABLE_RENDERCOMPLETESOUND, render_complete_sound_enabled ? BST_CHECKED : BST_UNCHECKED) ;
776           CheckDlgButton (hDlg, IDC_ENABLE_PARSEERRORSOUND, parse_error_sound_enabled ? BST_CHECKED : BST_UNCHECKED) ;
777           CheckDlgButton (hDlg, IDC_ENABLE_RENDERERRORSOUND, render_error_sound_enabled ? BST_CHECKED : BST_UNCHECKED) ;
778           return (true) ;
779  
780      case WM_CTLCOLORBTN:
781      case WM_CTLCOLORDLG:
782      case WM_CTLCOLOREDIT:
783      case WM_CTLCOLORLISTBOX:
784      case WM_CTLCOLORSCROLLBAR:
785      case WM_CTLCOLORSTATIC:
786           return(DefWindowProc(hDlg, message, wParam, lParam));
787  
788      case WM_COMMAND :
789           switch (LOWORD (wParam))
790           {
791             case IDC_BROWSE_RENDERCOMPLETESOUND:
792                  if ((s = SelectSound (hDlg, render_complete_sound)) != NULL)
793                    SetDlgItemText (hDlg, IDC_SOUND_RENDERCOMPLETE, s) ;
794                  return (true) ;
795  
796             case IDC_BROWSE_PARSEERRORSOUND:
797                  if ((s = SelectSound (hDlg, parse_error_sound)) != NULL)
798                    SetDlgItemText (hDlg, IDC_SOUND_PARSEERROR, s) ;
799                  return (true) ;
800  
801             case IDC_BROWSE_RENDERERRORSOUND:
802                  if ((s = SelectSound (hDlg, render_error_sound)) != NULL)
803                    SetDlgItemText (hDlg, IDC_SOUND_RENDERERROR, s) ;
804                  return (true) ;
805  
806             case IDOK :
807                  render_complete_sound_enabled = IsDlgButtonChecked (hDlg, IDC_ENABLE_RENDERCOMPLETESOUND) == BST_CHECKED ;
808                  parse_error_sound_enabled = IsDlgButtonChecked (hDlg, IDC_ENABLE_PARSEERRORSOUND) == BST_CHECKED ;
809                  render_error_sound_enabled = IsDlgButtonChecked (hDlg, IDC_ENABLE_RENDERERRORSOUND) == BST_CHECKED ;
810                  GetDlgItemText (hDlg, IDC_SOUND_RENDERCOMPLETE, render_complete_sound, _MAX_PATH) ;
811                  GetDlgItemText (hDlg, IDC_SOUND_PARSEERROR, parse_error_sound, _MAX_PATH) ;
812                  GetDlgItemText (hDlg, IDC_SOUND_RENDERERROR, render_error_sound, _MAX_PATH) ;
813                  EndDialog (hDlg, true) ;
814                  return (true) ;
815  
816             case IDCANCEL :
817                  EndDialog (hDlg, false) ;
818                  return (true) ;
819  
820             case IDC_CONTEXTHELP :
821                  hh_aklink.pszKeywords = "sounds" ;
822                  HtmlHelp (NULL, engineHelpPath, HH_KEYWORD_LOOKUP, (DWORD_PTR) &hh_aklink) ;
823                  return (true) ;
824           }
825           break ;
826    }
827    return (false) ;
828  }
829  
830  INT_PTR CALLBACK PovThreadCountDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
831  {
832    int         val;
833    BOOL        result;
834  
835    switch (message)
836    {
837      case WM_INITDIALOG :
838           hh_aklink.pszKeywords = "thread count" ; // TODO
839           SetDlgItemInt (hDlg, IDC_THREADCOUNT, ThreadCount, FALSE) ;
840           SendDlgItemMessage (hDlg, IDC_THREADCOUNT, EM_LIMITTEXT, 3, 0L) ;
841           SendDlgItemMessage (hDlg, IDC_THREADSPIN, UDM_SETRANGE, 0, MAKELONG(255, 1)) ;
842           CenterWindowRelative ((HWND) lParam, hDlg, true, true) ;
843           FitWindowInWindow (NULL, hDlg) ;
844           return (true) ;
845  
846      case WM_CTLCOLORBTN:
847      case WM_CTLCOLORDLG:
848      case WM_CTLCOLOREDIT:
849      case WM_CTLCOLORLISTBOX:
850      case WM_CTLCOLORSCROLLBAR:
851      case WM_CTLCOLORSTATIC:
852           return (DefWindowProc (hDlg, message, wParam, lParam));
853  
854      case WM_COMMAND :
855           switch (LOWORD (wParam))
856           {
857             case IDC_THREADCOUNT :
858                  if (HIWORD (wParam) == EN_CHANGE)
859                  {
860                    if (SendDlgItemMessage (hDlg, IDC_THREADCOUNT, EM_LINELENGTH, 0, 0) > 0)
861                    {
862                      val = GetDlgItemInt (hDlg, IDC_THREADCOUNT, &result, FALSE) ;
863                      if (result == FALSE || val < 1 || val > 255)
864                      {
865                        if (result == FALSE)
866                          SetDlgItemInt (hDlg, IDC_THREADCOUNT, ThreadCount, FALSE) ;
867                        else if (val < 1)
868                          SetDlgItemInt (hDlg, IDC_THREADCOUNT, 1, FALSE) ;
869                        else if (val > 255)
870                          SetDlgItemInt (hDlg, IDC_THREADCOUNT, 255, FALSE) ;
871                        SendDlgItemMessage (hDlg, IDC_THREADCOUNT, EM_SETSEL, 0, -1);
872                      }
873                      EnableWindow (GetDlgItem (hDlg, IDOK), TRUE);
874                    }
875                    else
876                      EnableWindow (GetDlgItem (hDlg, IDOK), FALSE);
877                    return (true);
878                  }
879                  return (false) ;
880  
881             case IDOK :
882                  val = GetDlgItemInt (hDlg, IDC_THREADCOUNT, &result, FALSE) ;
883                  if (result == TRUE && val > 0 && val < 256)
884                    ThreadCount = val ;
885                  EndDialog (hDlg, true) ;
886                  return (true) ;
887  
888             case IDCANCEL :
889                  EndDialog (hDlg, false) ;
890                  return (true) ;
891           }
892           break ;
893    }
894    return (false) ;
895  }
896  
897  INT_PTR CALLBACK RenderAlternativeFileDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
898  {
899    int                   width ;
900    int                   max = 0;
901    int                   diff ;
902    char                  str [8192] ;
903    char                  *s = str;
904    char                  *s2;
905    char                  srcname[_MAX_PATH];
906    char                  altname[_MAX_PATH];
907    RECT                  rect ;
908    HWND                  hText ;
909    const char            *src = source_file_name;
910    const char            *alt = (LPCSTR) lParam;
911  
912    switch (message)
913    {
914      case WM_INITDIALOG :
915           splitpath(src, NULL, srcname);
916           splitpath(alt, NULL, altname);
917           hText = GetDlgItem (hDlg, IDC_ALTERNATEFILETEXT) ;
918           s += sprintf(s, "You have requested to render '%s'.\n\n", src);
919           max = GetTextExtent(hText, str)->cx;
920           s += sprintf(s, "This is an include file that has been used by another render during this session. ");
921           s += sprintf(s, "The most recent use of %s was when rendering the below file:\n\n", srcname);
922           s += sprintf(s2 = s, "\t%s\n\n", alt);
923           if ((width = GetTextExtent(hText, s2)->cx) > max)
924             max = width;
925           s += sprintf(s, "If you prefer, POV-Ray will render %s instead of %s.\n\n", altname, srcname);
926           s += sprintf(s, "If you select \"Yes, For This Session\", POV will render %s and remember your choice for this session. ", altname);
927           s += sprintf(s, "If you select \"Yes, This Time\", you will be asked next time you render %s. ", srcname);
928           s += sprintf(s, "If you select \"No, Don't Ask Again\", POV will render %s and won't bug you about it for the remainder of this session.\n", srcname);
929           SetDlgItemText (hDlg, IDC_ALTERNATEFILETEXT, str) ;
930           if (max <= 0)
931             return (false) ;
932           GetClientRect (hText, &rect) ;
933           if (rect.right >= max + 10)
934             return (false) ;
935           diff = max - rect.right + 10 ;
936           SetWindowPos (hText, NULL, 0, 0, rect.right + diff, rect.bottom, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER) ;
937           GetWindowRect (hDlg, &rect) ;
938           rect.right -= rect.left ;
939           rect.bottom -= rect.top ;
940           rect.right += diff ;
941           SetWindowPos (hDlg, NULL, 0, 0, rect.right + 10, rect.bottom, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER) ;
942           diff /= 2 ;
943           NudgeChildWindow (hDlg, IDYES, diff) ;
944           NudgeChildWindow (hDlg, IDOK, diff) ;
945           NudgeChildWindow (hDlg, IDNO, diff) ;
946           NudgeChildWindow (hDlg, IDHELP, diff) ;
947           return (false) ;
948  
949      case WM_COMMAND :
950           switch (LOWORD (wParam))
951           {
952             case IDYES :
953             case IDOK :
954             case IDNO :
955             case IDCANCEL :
956                  EndDialog (hDlg, LOWORD(wParam)) ;
957                  return (true) ;
958           }
959           break ;
960    }
961    return (false) ;
962  }
963  
964  void FeatureNotify (const char *labelStr, const char *titleStr, const char *textStr, const char *helpStr, bool checked)
965  {
966    bool        result = false ;
967  
968    if (GetDontShowAgain (labelStr))
969      return ;
970    otaTitle = titleStr ;
971    otaText = textStr ;
972    hh_aklink.pszKeywords = otaHelpString = helpStr ;
973    otaChecked = checked ;
974    if (DialogBoxParam (hInstance, MAKEINTRESOURCE (IDD_FEATUREADVICE), main_window, (DLGPROC) PovFeatureAdviceDialogProc, (LPARAM) main_window) > 0)
975      if (otaChecked)
976        PutDontShowAgain (labelStr, true) ;
977  }
978  
979  }