StringLibraryDialog.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. // StringLibraryDialog.cpp : implementation file
  19. //
  20. #include "stdafx.h"
  21. #include "leveledit.h"
  22. #include "stringlibrarydialog.h"
  23. #include "translatedb.h"
  24. #include "translateobj.h"
  25. #include "editstringdialog.h"
  26. #include "utils.h"
  27. #include "stringscategoryviewdialog.h"
  28. #include "stringscategorynamedialog.h"
  29. #ifdef _DEBUG
  30. #define new DEBUG_NEW
  31. #undef THIS_FILE
  32. static char THIS_FILE[] = __FILE__;
  33. #endif
  34. /////////////////////////////////////////////////////////////////////////////
  35. // Contants
  36. /////////////////////////////////////////////////////////////////////////////
  37. static const int BORDER_X = 10;
  38. static const int BORDER_Y = 10;
  39. static const int SPACING_X = 10;
  40. static const int SPACING_Y = 10;
  41. /////////////////////////////////////////////////////////////////////////////
  42. //
  43. // StringLibraryDialogClass
  44. //
  45. /////////////////////////////////////////////////////////////////////////////
  46. StringLibraryDialogClass::StringLibraryDialogClass (CWnd *pParent) :
  47. IsInitialized (false),
  48. CurrentTab (0),
  49. Mode (MODE_STRING),
  50. CDialog(StringLibraryDialogClass::IDD, pParent)
  51. {
  52. //{{AFX_DATA_INIT(StringLibraryDialogClass)
  53. //}}AFX_DATA_INIT
  54. return ;
  55. }
  56. /////////////////////////////////////////////////////////////////////////////
  57. //
  58. // ~StringLibraryDialogClass
  59. //
  60. /////////////////////////////////////////////////////////////////////////////
  61. StringLibraryDialogClass::~StringLibraryDialogClass (void)
  62. {
  63. Clear_Clipboard ();
  64. //
  65. // Free the child dialog objects
  66. //
  67. for (int index = 0; index < CategoryPages.Count (); index ++) {
  68. delete CategoryPages[index];
  69. }
  70. CategoryPages.Delete_All ();
  71. return ;
  72. }
  73. /////////////////////////////////////////////////////////////////////////////
  74. //
  75. // DoDataExchange
  76. //
  77. /////////////////////////////////////////////////////////////////////////////
  78. void
  79. StringLibraryDialogClass::DoDataExchange (CDataExchange* pDX)
  80. {
  81. CDialog::DoDataExchange(pDX);
  82. //{{AFX_DATA_MAP(StringLibraryDialogClass)
  83. DDX_Control(pDX, IDC_TAB_CTRL, m_TabCtrl);
  84. //}}AFX_DATA_MAP
  85. return ;
  86. }
  87. BEGIN_MESSAGE_MAP(StringLibraryDialogClass, CDialog)
  88. //{{AFX_MSG_MAP(StringLibraryDialogClass)
  89. ON_WM_SIZE()
  90. ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_CTRL, OnSelchangeTabCtrl)
  91. ON_BN_CLICKED(IDC_ADD, OnAdd)
  92. ON_BN_CLICKED(IDC_REMOVE, OnRemove)
  93. ON_BN_CLICKED(IDC_RENAME, OnRename)
  94. ON_COMMAND(IDM_MODE_TWIDDLER, OnModeTwiddler)
  95. ON_COMMAND(IDM_MODE_STRING, OnModeString)
  96. //}}AFX_MSG_MAP
  97. END_MESSAGE_MAP()
  98. /////////////////////////////////////////////////////////////////////////////
  99. //
  100. // OnInitDialog
  101. //
  102. /////////////////////////////////////////////////////////////////////////////
  103. BOOL
  104. StringLibraryDialogClass::OnInitDialog (void)
  105. {
  106. CDialog::OnInitDialog ();
  107. //
  108. // Loop over all the categories in the database
  109. //
  110. int count = TranslateDBClass::Get_Category_Count ();
  111. for (int index = 0; index < count; index ++) {
  112. //
  113. // Lookup this category
  114. //
  115. TDBCategoryClass *category = TranslateDBClass::Get_Category (index);
  116. if (category != NULL) {
  117. Add_Category_Page (category);
  118. }
  119. }
  120. //
  121. // Display the first category page
  122. //
  123. if (CategoryPages.Count () > 0) {
  124. CategoryPages[0]->ShowWindow (SW_SHOW);
  125. }
  126. Resize_Controls ();
  127. Enable_Buttons ();
  128. Update_Mode ();
  129. IsInitialized = true;
  130. return TRUE;
  131. }
  132. /////////////////////////////////////////////////////////////////////////////
  133. //
  134. // OnSize
  135. //
  136. /////////////////////////////////////////////////////////////////////////////
  137. void
  138. StringLibraryDialogClass::OnSize
  139. (
  140. UINT nType,
  141. int cx,
  142. int cy
  143. )
  144. {
  145. CDialog::OnSize (nType, cx, cy);
  146. if (IsInitialized) {
  147. Resize_Controls ();
  148. }
  149. return ;
  150. }
  151. /////////////////////////////////////////////////////////////////////////////
  152. //
  153. // Resize_Controls
  154. //
  155. /////////////////////////////////////////////////////////////////////////////
  156. void
  157. StringLibraryDialogClass::Resize_Controls (void)
  158. {
  159. //
  160. // Get the window and button bounding rectangles
  161. //
  162. CRect rect;
  163. CRect button_rect;
  164. GetClientRect (&rect);
  165. ::GetWindowRect (::GetDlgItem (m_hWnd, IDOK), &button_rect);
  166. //
  167. // Calculate some positions and widths
  168. //
  169. int width = rect.Width ();
  170. int height = rect.Height ();
  171. int button_width = button_rect.Width ();
  172. int button_height = button_rect.Height ();
  173. int tab_width = width - (BORDER_X * 2);
  174. int tab_height = height - ((BORDER_Y * 2) + (SPACING_Y) + button_height);
  175. int button_y_pos = BORDER_Y + tab_height + SPACING_Y;
  176. int button_x_pos = (width / 2) - (((button_width * 2) + (SPACING_X * 1)) / 2);
  177. //
  178. // Resize the tab control
  179. //
  180. m_TabCtrl.SetWindowPos (NULL, BORDER_X, BORDER_Y, tab_width, tab_height, SWP_NOZORDER | SWP_NOCOPYBITS);
  181. //
  182. // Get the display rectangle of the tab control
  183. //
  184. CRect tab_rect;
  185. m_TabCtrl.GetWindowRect (&tab_rect);
  186. m_TabCtrl.AdjustRect (FALSE, &tab_rect);
  187. ScreenToClient (&tab_rect);
  188. //
  189. // Resize all the category page controls
  190. //
  191. for (int index = 0; index < CategoryPages.Count (); index ++) {
  192. CategoryPages[index]->SetWindowPos (NULL, tab_rect.left + BORDER_X, tab_rect.top + BORDER_Y,
  193. tab_rect.Width () - BORDER_X * 2, tab_rect.Height () - BORDER_Y * 2, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE);
  194. }
  195. //
  196. // Reposition the buttons
  197. //
  198. ::SetWindowPos (::GetDlgItem (m_hWnd, IDOK), NULL, button_x_pos, button_y_pos,
  199. 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOCOPYBITS | SWP_NOACTIVATE);
  200. button_x_pos += button_width + SPACING_X;
  201. ::SetWindowPos (::GetDlgItem (m_hWnd, IDCANCEL), NULL, button_x_pos, button_y_pos,
  202. 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOCOPYBITS | SWP_NOACTIVATE);
  203. return ;
  204. }
  205. /////////////////////////////////////////////////////////////////////////////
  206. //
  207. // OnOK
  208. //
  209. /////////////////////////////////////////////////////////////////////////////
  210. void
  211. StringLibraryDialogClass::OnOK (void)
  212. {
  213. //
  214. // Reset the database
  215. //
  216. TranslateDBClass::Remove_All ();
  217. //
  218. // Ask each category page to save its contents into the database
  219. //
  220. int count = CategoryPages.Count ();
  221. bool update_version_number = false;
  222. for (int index = 0; index < count; index ++) {
  223. update_version_number |= CategoryPages[index]->Is_Version_Number_Dirty ();
  224. CategoryPages[index]->Apply_Changes ();
  225. }
  226. //
  227. // Increment the version number
  228. //
  229. if (update_version_number) {
  230. TranslateDBClass::Update_Version ();
  231. }
  232. CDialog::OnOK ();
  233. return ;
  234. }
  235. /////////////////////////////////////////////////////////////////////////////
  236. //
  237. // OnSelchangeTabCtrl
  238. //
  239. /////////////////////////////////////////////////////////////////////////////
  240. void
  241. StringLibraryDialogClass::OnSelchangeTabCtrl
  242. (
  243. NMHDR * pNMHDR,
  244. LRESULT * pResult
  245. )
  246. {
  247. (*pResult) = 0;
  248. Update_Page_Visibility ();
  249. return ;
  250. }
  251. /////////////////////////////////////////////////////////////////////////////
  252. //
  253. // Update_Page_Visibility
  254. //
  255. /////////////////////////////////////////////////////////////////////////////
  256. void
  257. StringLibraryDialogClass::Update_Page_Visibility (void)
  258. {
  259. //
  260. // Check to see if the user has selected a new tab
  261. //
  262. int newtab = m_TabCtrl.GetCurSel ();
  263. if (CurrentTab != newtab) {
  264. //
  265. // Hide the old tab
  266. //
  267. if (CurrentTab < CategoryPages.Count () && CategoryPages[CurrentTab] != NULL) {
  268. CategoryPages[CurrentTab]->ShowWindow (SW_HIDE);
  269. }
  270. //
  271. // Show the new tab
  272. //
  273. if (CategoryPages[newtab] != NULL) {
  274. CategoryPages[newtab]->ShowWindow (SW_SHOW);
  275. }
  276. //
  277. // Remember what our new current tab is
  278. //
  279. CurrentTab = newtab;
  280. }
  281. Enable_Buttons ();
  282. return ;
  283. }
  284. /////////////////////////////////////////////////////////////////////////////
  285. //
  286. // Enable_Buttons
  287. //
  288. /////////////////////////////////////////////////////////////////////////////
  289. void
  290. StringLibraryDialogClass::Enable_Buttons (void)
  291. {
  292. CMenu *menu = GetMenu ();
  293. if (menu != NULL) {
  294. CMenu *sub_menu = menu->GetSubMenu (0);
  295. if (sub_menu != NULL) {
  296. //
  297. // Update the enable state of these menu entries
  298. //
  299. int value = MF_BYCOMMAND | ((CurrentTab != 0) ? MF_ENABLED : MF_GRAYED);
  300. sub_menu->EnableMenuItem (IDC_REMOVE, value);
  301. sub_menu->EnableMenuItem (IDC_RENAME, value);
  302. }
  303. }
  304. return ;
  305. }
  306. /////////////////////////////////////////////////////////////////////////////
  307. //
  308. // OnAdd
  309. //
  310. /////////////////////////////////////////////////////////////////////////////
  311. void
  312. StringLibraryDialogClass::OnAdd (void)
  313. {
  314. StringsCategoryNameDialogClass dialog (this);
  315. if (dialog.DoModal () == IDOK) {
  316. //
  317. // Create the new category
  318. //
  319. TDBCategoryClass *category = TranslateDBClass::Add_Category (dialog.Get_Name ());
  320. if (category != NULL) {
  321. //
  322. // Add some UI for this new category
  323. //
  324. Add_Category_Page (category);
  325. //
  326. // Ensure the new category page is the right size
  327. //
  328. Resize_Controls ();
  329. }
  330. }
  331. return ;
  332. }
  333. /////////////////////////////////////////////////////////////////////////////
  334. //
  335. // OnRemove
  336. //
  337. /////////////////////////////////////////////////////////////////////////////
  338. void
  339. StringLibraryDialogClass::OnRemove (void)
  340. {
  341. //
  342. // The user can't remove the first (default) category
  343. //
  344. int index = CurrentTab;
  345. if (index > 0) {
  346. TDBCategoryClass *category = TranslateDBClass::Get_Category (index);
  347. if (category != NULL) {
  348. //
  349. // Prompt the user to ensure they really want to remove the category
  350. //
  351. CString message;
  352. message.Format ("Are you sure you wish to remove the %s category?", (LPCTSTR)category->Get_Name ());
  353. if (::MessageBox (m_hWnd, message, "Remove Category", MB_ICONQUESTION | MB_YESNO) == IDYES) {
  354. //
  355. // Transfer the data from the old category to the default category
  356. //
  357. Clear_Clipboard ();
  358. CategoryPages[index]->Cut (LocalClipboard, false);
  359. CategoryPages[0]->Paste (LocalClipboard);
  360. Clear_Clipboard ();
  361. //
  362. // Remove the category from the database
  363. //
  364. TranslateDBClass::Remove_Category (index);
  365. //
  366. // Remove the category's tab
  367. //
  368. m_TabCtrl.DeleteItem (index);
  369. //
  370. // Free the UI object and remove it from our list
  371. //
  372. CategoryPages[index]->DestroyWindow ();
  373. delete CategoryPages[index];
  374. CategoryPages.Delete (index);
  375. //
  376. // Change focus back to the first page
  377. //
  378. m_TabCtrl.SetCurSel (0);
  379. Update_Page_Visibility ();
  380. }
  381. }
  382. }
  383. return ;
  384. }
  385. /////////////////////////////////////////////////////////////////////////////
  386. //
  387. // Add_Category_Page
  388. //
  389. /////////////////////////////////////////////////////////////////////////////
  390. void
  391. StringLibraryDialogClass::Add_Category_Page (TDBCategoryClass *category)
  392. {
  393. //
  394. // Add a tab to the dialog for this category
  395. //
  396. TC_ITEM tab_info = { 0 };
  397. tab_info.mask = TCIF_TEXT;
  398. tab_info.pszText = (char *)(LPCTSTR)category->Get_Name ();
  399. m_TabCtrl.InsertItem (0xFF, &tab_info);
  400. //
  401. // Create a page for this new category
  402. //
  403. StringsCategoryViewDialogClass *child_wnd = new StringsCategoryViewDialogClass;
  404. child_wnd->Set_Category_ID (category->Get_ID ());
  405. child_wnd->Set_Callback (this);
  406. child_wnd->Create (this);
  407. CategoryPages.Add (child_wnd);
  408. return ;
  409. }
  410. /////////////////////////////////////////////////////////////////////////////
  411. //
  412. // On_Cut
  413. //
  414. /////////////////////////////////////////////////////////////////////////////
  415. void
  416. StringLibraryDialogClass::On_Cut (void)
  417. {
  418. Clear_Clipboard ();
  419. CategoryPages[CurrentTab]->Cut (LocalClipboard);
  420. return ;
  421. }
  422. /////////////////////////////////////////////////////////////////////////////
  423. //
  424. // On_Paste
  425. //
  426. /////////////////////////////////////////////////////////////////////////////
  427. void
  428. StringLibraryDialogClass::On_Paste (void)
  429. {
  430. CategoryPages[CurrentTab]->Paste (LocalClipboard);
  431. Clear_Clipboard ();
  432. return ;
  433. }
  434. /////////////////////////////////////////////////////////////////////////////
  435. //
  436. // Clear_Clipboard
  437. //
  438. /////////////////////////////////////////////////////////////////////////////
  439. void
  440. StringLibraryDialogClass::Clear_Clipboard (void)
  441. {
  442. for (int index = 0; index < LocalClipboard.Count (); index ++) {
  443. SAFE_DELETE (LocalClipboard[index]);
  444. }
  445. LocalClipboard.Delete_All ();
  446. return ;
  447. }
  448. /////////////////////////////////////////////////////////////////////////////
  449. //
  450. // OnRename
  451. //
  452. /////////////////////////////////////////////////////////////////////////////
  453. void
  454. StringLibraryDialogClass::OnRename (void)
  455. {
  456. //
  457. // The user can't rename the first (default) category
  458. //
  459. int index = CurrentTab;
  460. if (index > 0) {
  461. TDBCategoryClass *category = TranslateDBClass::Get_Category (index);
  462. if (category != NULL) {
  463. //
  464. // Show a dialog to the user where they can enter a new name
  465. //
  466. StringsCategoryNameDialogClass dialog (this);
  467. dialog.Set_Name (category->Get_Name ());
  468. if (dialog.DoModal () == IDOK) {
  469. //
  470. // Rename the category and tab UI
  471. //
  472. const char *new_name = dialog.Get_Name ();
  473. category->Set_Name (new_name);
  474. //
  475. // Update the tab's text
  476. //
  477. TC_ITEM tab_info = { 0 };
  478. tab_info.mask = TCIF_TEXT;
  479. tab_info.pszText = (char *)new_name;
  480. m_TabCtrl.SetItem (index, &tab_info);
  481. //
  482. // HACK - Not really sure why, but the current page disappears
  483. // when the tab ctrl is renamed (and it has nothing to do with repainting).
  484. //
  485. CategoryPages[index]->ShowWindow (SW_HIDE);
  486. CategoryPages[index]->ShowWindow (SW_SHOW);
  487. }
  488. }
  489. }
  490. return ;
  491. }
  492. /////////////////////////////////////////////////////////////////////////////
  493. //
  494. // OnModeTwiddler
  495. //
  496. /////////////////////////////////////////////////////////////////////////////
  497. void
  498. StringLibraryDialogClass::OnModeTwiddler (void)
  499. {
  500. Mode = MODE_TWIDDLER;
  501. Update_Mode ();
  502. //
  503. // Let each category know the new editing mode
  504. //
  505. for (int index = 0; index < CategoryPages.Count (); index ++) {
  506. CategoryPages[index]->Set_Edit_Mode (StringsCategoryViewDialogClass::EDIT_MODE_TWIDDLER);
  507. }
  508. return ;
  509. }
  510. /////////////////////////////////////////////////////////////////////////////
  511. //
  512. // OnModeString
  513. //
  514. /////////////////////////////////////////////////////////////////////////////
  515. void
  516. StringLibraryDialogClass::OnModeString (void)
  517. {
  518. Mode = MODE_STRING;
  519. Update_Mode ();
  520. //
  521. // Let each category know the new editing mode
  522. //
  523. for (int index = 0; index < CategoryPages.Count (); index ++) {
  524. CategoryPages[index]->Set_Edit_Mode (StringsCategoryViewDialogClass::EDIT_MODE_STRING);
  525. }
  526. return ;
  527. }
  528. /////////////////////////////////////////////////////////////////////////////
  529. //
  530. // Update_Mode
  531. //
  532. /////////////////////////////////////////////////////////////////////////////
  533. void
  534. StringLibraryDialogClass::Update_Mode (void)
  535. {
  536. CMenu *menu = GetMenu ();
  537. if (menu != NULL) {
  538. CMenu *sub_menu = menu->GetSubMenu (1);
  539. if (sub_menu != NULL) {
  540. //
  541. // Determine which menu entry should get the check
  542. //
  543. int id = 0;
  544. if (Mode == MODE_STRING) {
  545. id = IDM_MODE_STRING;
  546. } else {
  547. id = IDM_MODE_TWIDDLER;
  548. }
  549. //
  550. // Check the appropriate radio button
  551. //
  552. sub_menu->CheckMenuRadioItem (0, 1, Mode, MF_BYPOSITION);
  553. }
  554. }
  555. return ;
  556. }