33 char *ProgramName = "oobr";
37 int TreeContourWidth = 1;
39 int TreeBorderSize = BORDER_SIZE;
41 int TreeParentDistance = PARENT_DISTANCE;
43 DoubleBuffer *TreeDrawingAreaDB;
45 ContourOption TreeShowContourOption = NoContours;
47 DensityOption TreeLayoutDensity = Fixed;
49 char TreeShowSteps = FALSE;
51 char TreeAlignNodes = FALSE;
53 char PauseAfterStep = FALSE;
59 HWND hwndFrame, hwndClient;
63 static const char* SZ_FRAME_CLASS = "OObrFrameClass";
65 static const char* SZ_CLIENT_CLASS = "OObrClientClass";
67 static HWND hwndStatus;
69 static HINSTANCE hAppInst;
73 static BOOL FileModified = FALSE;
75 static char CurrentFile [MAX_PATH];
77 static char NextFile [MAX_PATH];
79 static char NewNodeName [MAX_NODE_NAME_LENGHT];
81 static char *FileTitle = NULL;
85 /* The semantics for these is different than in X.
87 Selected node is the one selected with left click.
89 Popup node is the tempoparily highlighted one, by a
91 right click, only while a popup menu is active */
93 static Tree *SelectedNode;
95 static Tree *PopupNode;
105 BOOL LoadFile (void);
107 BOOL SaveFileAs (void);
109 BOOL SaveFile (void);
111 void UpdateFrameTitle (void);
113 BOOL PromptFileName (BOOL bSave);
115 UINT FileMessage (UINT idstr, char* file, UINT flags);
121 * ------------------------------------
125 * ------------------------------------
145 SetWindowOrgEx (TreeDrawingAreaDB->hdc, 0, 0, NULL);
149 InvalidateRect (hwndClient, NULL, TRUE);
161 CurrentFile[0] = '\0';
175 TheTree = MakeNode();
177 SetNodeLabel(TheTree, strdup (NewNodeName));
201 /* Save file under the name in NextFile,
203 as set by PromptFileName */
205 newTree = ReadTreeFromFile (NextFile, &error);
209 if (error != ERR_NONE)
217 FileMessage (IDS_ERRORFIRST + error, NextFile,
219 MB_OK | MB_ICONEXCLAMATION);
227 strcpy (CurrentFile, NextFile);
231 FileModified = FALSE;
257 if (!PromptFileName (TRUE))
263 /* Save file under the name in NextFile,
265 as set by PromptFileName */
267 if (!SaveTreeToFile (TheTree, NextFile))
271 FileMessage (IDS_ERRORFIRST + ERR_OPENFAIL, NextFile,
273 MB_OK | MB_ICONEXCLAMATION);
281 strcpy (CurrentFile, NextFile);
285 FileModified = FALSE;
297 /* Always ask for filename if currently untitled */
299 if (CurrentFile[0] == 0)
301 return SaveFileAs ();
305 if (!SaveTreeToFile (TheTree, CurrentFile))
309 FileMessage (IDS_ERRORFIRST + ERR_OPENFAIL, CurrentFile,
311 MB_OK | MB_ICONEXCLAMATION);
319 FileModified = FALSE;
329 * Set frame title to either "FILE : OObrowser" or "OObrowser",
331 * depending on whether a file is open
337 UpdateFrameTitle (void)
341 char buf [MAX_PATH + 4], *p;
345 if (FileTitle == NULL || FileTitle[0] == '\0')
347 LoadString (hAppInst, IDS_UNTITLED, buf, sizeof(buf));
351 strcpy (buf, FileTitle);
357 p = buf + strlen (buf);
361 LoadString (hAppInst, IDS_APPTITLE, p,
363 sizeof (buf) - (p - buf));
365 SetWindowText (hwndFrame, buf);
379 FileMessage (UINT idstr, char* file, UINT flags)
383 char buf [MAX_PATH + 128], format [128], title[64];
387 LoadString (hAppInst, IDS_APPTITLE, title, sizeof(title));
389 LoadString (hAppInst, idstr, format, sizeof(format));
391 wsprintf (buf, format, file);
395 return MessageBox (hwndFrame, buf, title, flags);
403 * Propmt for file name for save or load. Return TRUE if user has
405 * chosen a name, FALSE if canceled. If name is chosen, it is
407 * stored into NextFile, and its title part into FileTitle
413 PromptFileName (BOOL bSave)
427 /* Default to current file name */
429 strcpy (NextFile, CurrentFile);
437 /* Start with empty file name */
445 ofn.lStructSize = sizeof (ofn);
447 ofn.hwndOwner = hwndFrame;
449 ofn.lpstrFilter = NULL; /* #### */
451 ofn.lpstrCustomFilter = NULL;
453 ofn.lpstrFile = NextFile;
455 ofn.nMaxFile = sizeof (NextFile);
457 ofn.lpstrFileTitle = NULL;
459 ofn.lpstrInitialDir = NULL;
461 ofn.lpstrTitle = NULL;
465 ? OFN_OVERWRITEPROMPT | OFN_NOTESTFILECREATE
467 : OFN_FILEMUSTEXIST);
469 ofn.lpstrDefExt = NULL;
475 ofn.lpTemplateName = NULL;
481 ? GetSaveFileName (&ofn)
483 : GetOpenFileName (&ofn));
487 FileTitle = NextFile + ofn.nFileOffset;
499 * Dialog procedure for the node name editing dialog
505 DlgNodeNameProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
515 /* lParam is TRUE for new root node when creating the tree,
517 FALSE for node name editing. We show different caption
519 based on this flag. */
525 LoadString (hAppInst, lParam ? IDS_ROOTNODE : IDS_MERENODE,
529 SetWindowText (hwnd, buf);
531 SetDlgItemText (hwnd, IDC_NAME, NewNodeName);
541 switch (LOWORD(wParam))
547 GetDlgItemText (hwnd, IDC_NAME, NewNodeName, sizeof(NewNodeName));
549 EndDialog (hwnd, TRUE);
557 EndDialog (hwnd, FALSE);
581 * Dialog procedure for the spacing dialog
587 DlgSpacingProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
597 SendDlgItemMessage (hwnd, IDC_LEVEL_SL, TBM_SETRANGE, FALSE, MAKELONG (10, 50));
599 SendDlgItemMessage (hwnd, IDC_LEVEL_SL, TBM_SETPOS, TRUE, TreeParentDistance);
601 SetDlgItemInt (hwnd, IDC_LEVEL_ED, TreeParentDistance, FALSE);
605 SendDlgItemMessage (hwnd, IDC_SIBLING_SL, TBM_SETRANGE, FALSE, MAKELONG (1, 15));
607 SendDlgItemMessage (hwnd, IDC_SIBLING_SL, TBM_SETPOS, TRUE, TreeBorderSize);
609 SetDlgItemInt (hwnd, IDC_SIBLING_ED, TreeBorderSize, FALSE);
617 switch (LOWORD(wParam))
625 int NewLevel = SendDlgItemMessage (hwnd, IDC_LEVEL_SL, TBM_GETPOS, 0, 0);
627 int NewSibling = SendDlgItemMessage (hwnd, IDC_SIBLING_SL, TBM_GETPOS, 0, 0);
629 if (NewLevel != TreeParentDistance
631 || NewSibling != TreeBorderSize)
635 TreeParentDistance = NewLevel;
637 TreeBorderSize = NewSibling;
643 DeleteTree(TheTree, TRUE);
647 ResetLabels(TheTree);
651 InvalidateRect (hwndClient, NULL, TRUE);
659 EndDialog (hwnd, TRUE);
667 EndDialog (hwnd, FALSE);
679 SetDlgItemInt (hwnd, IDC_LEVEL_ED,
681 SendDlgItemMessage (hwnd, IDC_LEVEL_SL, TBM_GETPOS, 0, 0), FALSE);
683 SetDlgItemInt (hwnd, IDC_SIBLING_ED,
685 SendDlgItemMessage (hwnd, IDC_SIBLING_SL, TBM_GETPOS, 0, 0), FALSE);
705 * Dialog procedure for the about dialog
711 DlgAboutProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
715 if (uMsg == WM_COMMAND && LOWORD(wParam) == IDOK)
733 * Edit node name given in NAME. Returns pointer to the
735 * new name static buffer, or NULL if canceled. The ROOT
737 * parameter affects dialog title only
743 DlgNodeName (const char* name, BOOL root)
749 strcpy (NewNodeName, name);
757 return (DialogBoxParam (hAppInst, MAKEINTRESOURCE (IDD_NODENAME),
759 hwndFrame, DlgNodeNameProc, root)
761 ? NewNodeName : NULL);
769 PromptRootNode (int u_n_u_s_e_d)
773 return DlgNodeName (NULL, TRUE) != 0;
781 PromptAndSave (BOOL (*proc)(int), int param)
785 if (proc && !proc (param))
801 /* Substitute "Untitled" if no file */
811 LoadString (hAppInst, IDS_UNTITLED, buf, sizeof(buf));
819 uRes = FileMessage (IDS_SAVECHANGES, p, MB_YESNOCANCEL | MB_ICONQUESTION);
821 if (uRes == IDCANCEL)
845 MaybeNewTreeNode (Tree* node, NodePosition pos)
849 char *newName = DlgNodeName (NULL, FALSE);
855 InsertNode (node, pos, strdup (newName));
857 InvalidateRect (hwndClient, NULL, TRUE);
869 SelectNode (Tree* node, BOOL edit)
873 /* Redraw old node in un-selected state */
875 if (SelectedNode != NULL)
879 SelectedNode->highlight = FALSE;
881 SelectedNode->focus = FALSE;
883 DrawNode (SelectedNode, New);
899 printf ("%s^^%s^^%s\n",
903 edit ? "br-edit" : "br-view",
905 (SelectedNode->value) ? SelectedNode->value : SelectedNode->label.text);
909 StatusMsg (SelectedNode->label.text, 1);
913 /* Redraw new node in selected state */
915 SelectedNode->highlight = TRUE;
917 SelectedNode->focus = TRUE;
919 DrawNode(SelectedNode, New);
933 ChangeFontMaybe (void)
943 cf.lStructSize = sizeof (cf);
945 cf.hwndOwner = hwndFrame;
949 cf.Flags = (CF_FORCEFONTEXIST | CF_NOSIMULATIONS | CF_NOVECTORFONTS
951 | CF_SELECTSCRIPT | CF_NOVERTFONTS | CF_SCREENFONTS);
955 if (ChooseFont (&cf) && SetNewFont (&lf))
959 DeleteTree(TheTree, TRUE);
961 ResetLabels(TheTree);
965 InvalidateRect (hwndClient, NULL, TRUE);
977 * Menu command handlers
983 HandleClientMenuCommand (WORD wId)
991 case ID_EDIT_ADDCHILD:
993 MaybeNewTreeNode (PopupNode, Child);
999 case ID_EDIT_ADDSIBLINGBEFORE:
1001 MaybeNewTreeNode (PopupNode, Before);
1007 case ID_EDIT_ADDSIBLIBNGAFTER:
1009 MaybeNewTreeNode (PopupNode, After);
1015 case ID_EDIT_DELETE:
1017 SelectNode(NULL, FALSE); /* deselect node before deleting it! */
1019 DeleteNode(PopupNode);
1021 InvalidateRect (hwndClient, NULL, TRUE);
1023 FileModified = TRUE;
1029 case ID_VIEW_COLLAPSE:
1031 case ID_VIEW_EXPAND:
1033 ExpandCollapseNode(PopupNode);
1035 InvalidateRect (hwndClient, NULL, TRUE);
1041 case ID_VIEW_SPACING:
1043 DialogBox (hAppInst, MAKEINTRESOURCE (IDD_SPACING), hwndFrame, DlgSpacingProc);
1057 case ID_VIEW_ALIGNLEVEL:
1059 TreeAlignNodes = !TreeAlignNodes;
1061 DeleteTree(TheTree, TRUE);
1063 ResetLabels(TheTree);
1067 InvalidateRect (hwndClient, NULL, TRUE);
1075 SelectNode (PopupNode, TRUE);
1083 SelectNode (PopupNode, FALSE);
1095 HandleFrameMenuCommand (WORD wId)
1105 if (PromptAndSave (PromptRootNode, 0))
1115 if (PromptAndSave (PromptFileName, FALSE))
1131 case ID_FILE_SAVEAS:
1141 if (PromptAndSave (NULL, 0))
1143 DestroyWindow (hwndFrame);
1149 case ID_HELP_CONTENTS:
1151 WinHelp (hwndFrame, "oobr.hlp", HELP_CONTENTS, 0);
1159 DialogBox (hAppInst, MAKEINTRESOURCE (IDD_ABOUT), hwndFrame, DlgAboutProc);
1179 HandleScroll (HWND hwnd, int dir, int op, int newpos)
1191 /* Get scroll metrics */
1193 si.cbSize = sizeof (si);
1197 GetScrollInfo (hwnd, dir, &si);
1201 /* Determine old position */
1203 GetWindowOrgEx (TreeDrawingAreaDB->hdc, &pt);
1205 oldpos = dir == SB_VERT ? pt.y : pt.x;
1207 maxpos = si.nMax - si.nPage - 1;
1229 newpos = oldpos - 3 * si.nPage / 4;
1235 newpos = oldpos + 3 * si.nPage / 4;
1241 newpos = oldpos - 16;
1247 newpos = oldpos + 16;
1259 newpos = max (newpos, 0);
1261 newpos = min (newpos, maxpos);
1265 if (newpos == oldpos)
1271 *(dir == SB_VERT ? &pt.y : &pt.x) = newpos;
1273 SetWindowOrgEx (TreeDrawingAreaDB->hdc, pt.x, pt.y, NULL);
1277 dir == SB_VERT ? 0 : oldpos - newpos,
1279 dir != SB_VERT ? 0 : oldpos - newpos,
1283 SetScrollPos (hwnd, dir, newpos, TRUE);
1291 ToLP (LPARAM lParam)
1297 pt.x = LOWORD (lParam);
1299 pt.y = HIWORD (lParam);
1301 DPtoLP (TreeDrawingAreaDB->hdc, &pt, 1);
1311 HandleLButton (POINT pt, BOOL edit)
1317 if (SearchTree(TheTree, pt.x, pt.y, &node))
1319 SelectNode (node, edit);
1323 SelectNode (NULL, edit);
1331 HandleRButton (POINT ptLP, POINT ptClick)
1337 if (SearchTree(TheTree, ptLP.x, ptLP.y, &node))
1341 HMENU hMenu, hPopup;
1349 if (SelectedNode != PopupNode)
1353 PopupNode->highlight = TRUE;
1355 DrawNode(PopupNode, New);
1361 SelectedNode->highlight = FALSE;
1363 DrawNode(SelectedNode, New);
1371 hMenu = LoadMenu (hAppInst, MAKEINTRESOURCE (IDR_NODEPOPUP));
1373 hPopup = GetSubMenu (hMenu, 0);
1377 TrackPopupMenu (hPopup, TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
1379 ptClick.x, ptClick.y, 0, hwndClient, NULL);
1383 DestroyMenu (hMenu);
1387 if (SelectedNode != PopupNode)
1391 PopupNode->highlight = FALSE;
1393 DrawNode(PopupNode, New);
1399 SelectedNode->highlight = TRUE;
1401 DrawNode(SelectedNode, New);
1415 HandleMenuPopup (HMENU hMenu)
1419 #define FROB(item,cond) EnableMenuItem (hMenu, (item), MF_BYCOMMAND | ((cond) ? MF_GRAYED : 0));
1423 FROB (ID_FILE_SAVE, TheTree == NULL);
1425 FROB (ID_FILE_SAVEAS, TheTree == NULL);
1427 FROB (ID_VIEW_EXPAND, PopupNode == NULL || !PopupNode->elision || IS_LEAF(PopupNode));
1429 FROB (ID_VIEW_COLLAPSE, PopupNode == NULL || PopupNode->elision || IS_LEAF(PopupNode));
1431 FROB (ID_FILE_SAVEAS, TheTree == NULL);
1433 FROB (ID_VIEW_ALIGNLEVEL, TheTree == NULL);
1441 CheckMenuItem (hMenu, ID_VIEW_ALIGNLEVEL, MF_BYCOMMAND | (TreeAlignNodes ? MF_CHECKED : 0));
1449 * Client window procedure
1453 static LRESULT CALLBACK
1455 ClientWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1465 /* We really need that very early. Scrollbar setup depends on this */
1469 TreeDrawingAreaDB = DBLcreate_double_buffer ();
1481 BeginPaint (hwnd, &ps);
1485 EndPaint (hwnd, &ps);
1495 if (TreeDrawingAreaDB)
1501 GetDrawingSize (&w, &h);
1503 SetDrawingSize (w, h);
1517 MenuHelp (uMsg, wParam, lParam, NULL, hAppInst, hwndStatus, &zero);
1525 case WM_INITMENUPOPUP:
1527 HandleMenuPopup ((HMENU) wParam);
1535 HandleScroll (hwnd, SB_VERT, LOWORD(wParam), HIWORD(wParam));
1543 HandleScroll (hwnd, SB_HORZ, LOWORD(wParam), HIWORD(wParam));
1549 case WM_LBUTTONDOWN:
1551 /* View node command */
1553 HandleLButton (ToLP (lParam), FALSE);
1559 case WM_LBUTTONDBLCLK:
1561 /* Edit node command */
1563 HandleLButton (ToLP (lParam), TRUE);
1569 case WM_RBUTTONDOWN:
1571 /* Pop up the menu */
1577 ptClick.x = LOWORD (lParam);
1579 ptClick.y = HIWORD (lParam);
1581 ClientToScreen (hwnd, &ptClick);
1583 HandleRButton (ToLP (lParam), ptClick);
1593 HandleClientMenuCommand (LOWORD(wParam));
1601 return DefWindowProc (hwnd, uMsg, wParam, lParam);
1613 * Frame window procedure
1617 static LRESULT CALLBACK
1619 FrameWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1629 /* Create status bar */
1631 hwndStatus = CreateStatusWindow (SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE,
1635 /* Create client window */
1637 hwndClient = CreateWindowEx (WS_EX_CLIENTEDGE, SZ_CLIENT_CLASS, NULL,
1639 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE,
1641 0, 0, 10, 10, /* Adjusted in WM_SIZE handler */
1643 hwnd, NULL, hAppInst, NULL);
1651 WinHelp (hwndFrame, "oobr.hlp", HELP_QUIT, 0);
1653 PostQuitMessage (0);
1661 HandleFrameMenuCommand (ID_FILE_EXIT);
1669 /* Adjust status bar */
1671 SendMessage (hwndStatus, uMsg, wParam, lParam);
1673 /* Adjust client rectangle */
1681 GetClientRect (hwndStatus, &rc);
1683 delta = rc.bottom - rc.top;
1685 GetClientRect (hwnd, &rc);
1689 MoveWindow (hwndClient, rc.left, rc.top,
1691 rc.right - rc.left, rc.bottom - rc.top, TRUE);
1705 MenuHelp (uMsg, wParam, lParam, NULL, hAppInst, hwndStatus, &zero);
1713 case WM_INITMENUPOPUP:
1715 HandleMenuPopup ((HMENU) wParam);
1723 /* Route unhandled command to the client */
1725 if (!HandleFrameMenuCommand (LOWORD(wParam)))
1727 SendMessage (hwndClient, uMsg, wParam, lParam);
1735 SetFocus (hwndClient);
1743 return DefWindowProc (hwnd, uMsg, wParam, lParam);
1753 /* ----------------------------------------------------------------------------
1757 * Status() displays the specified text in the status area.
1759 * 'urgent' overrides the value of TreeShowSteps.
1763 * ----------------------------------------------------------------------------
1771 StatusMsg(char *msg, int urgent)
1775 if (TreeShowSteps || urgent)
1777 SendMessage (hwndStatus, SB_SETTEXT, 0, (LPARAM)msg);
1783 /* ----------------------------------------------------------------------------
1787 * Pause is a trivial function, for the benefit of interface-dependent code
1789 * inside tree.c. This avoids having to include X11 stuff inside that file.
1791 * PauseTime is expected to contain an integer indicating 1/10ths of a sec.
1795 * ----------------------------------------------------------------------------
1815 WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
1819 hAppInst = hInstance;
1825 main (int argc, char* argv[])
1829 LPTSTR lpCmdLine = argc > 1 ? argv[1] : NULL;
1831 int nCmdShow = SW_SHOWNORMAL;
1833 hAppInst = GetModuleHandle (NULL);
1841 _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
1847 InitCommonControls ();
1851 /* Register window classes */
1859 /* Frame window class */
1863 wcl.lpfnWndProc = FrameWndProc;
1869 wcl.hInstance = hAppInst;
1871 wcl.hIcon = LoadIcon (hAppInst, MAKEINTRESOURCE (IDI_OOBR));
1873 wcl.hCursor = LoadCursor (NULL, IDC_ARROW);
1875 wcl.hbrBackground = NULL;
1877 wcl.lpszMenuName = MAKEINTRESOURCE (IDR_MAINMENU);
1879 wcl.lpszClassName = SZ_FRAME_CLASS;
1881 RegisterClass (&wcl);
1885 /* Client window class */
1887 wcl.style = CS_OWNDC | CS_DBLCLKS;
1889 wcl.lpfnWndProc = ClientWndProc;
1895 wcl.hInstance = hAppInst;
1899 wcl.hCursor = LoadCursor (NULL, IDC_ARROW);
1901 wcl.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1903 wcl.lpszMenuName = NULL;
1905 wcl.lpszClassName = SZ_CLIENT_CLASS;
1907 RegisterClass (&wcl);
1915 hwndFrame = CreateWindowEx (0, SZ_FRAME_CLASS, NULL, WS_OVERLAPPEDWINDOW,
1917 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1919 NULL, NULL, hAppInst, NULL);
1925 ShowWindow (hwndFrame, nCmdShow);
1929 if (lpCmdLine && lpCmdLine[0])
1933 strcpy (NextFile, lpCmdLine);
1937 FileTitle = strrchr (CurrentFile, '/');
1939 if (FileTitle == NULL)
1941 FileTitle = strrchr (CurrentFile, '\\');
1943 if (FileTitle == NULL)
1945 FileTitle = CurrentFile;
1951 UpdateFrameTitle ();
1961 while (GetMessage (&msg, 0, 0, 0))
1965 TranslateMessage (&msg);
1967 DispatchMessage (&msg);