/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ // StringsCategoryViewDialog.cpp : implementation file // #include "stdafx.h" #include "leveledit.h" #include "stringscategoryviewdialog.h" #include "translateobj.h" #include "translatedb.h" #include "tdbcategory.h" #include "editstringdialog.h" #include "editstringtwiddlerdialog.h" #include "utils.h" #include "stringlibrarydialog.h" #include "stringtwiddler.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Local prototypes ///////////////////////////////////////////////////////////////////////////// static int CALLBACK StringEntrySortCompareFn (LPARAM param1, LPARAM param2, LPARAM sort_info); ///////////////////////////////////////////////////////////////////////////// // // StringsCategoryViewDialogClass // ///////////////////////////////////////////////////////////////////////////// StringsCategoryViewDialogClass::StringsCategoryViewDialogClass (CWnd *pParent) : SelectedObjectID (0), CategoryID (0), IsInitialized (false), ShouldUpdateVersionNumber (false), CurrentColSort (COL_ID), AscendingSort (true), CallbackObject (NULL), EditMode (EDIT_MODE_STRING), CDialog(StringsCategoryViewDialogClass::IDD, pParent) { //{{AFX_DATA_INIT(StringsCategoryViewDialogClass) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT Columns.Add (COL_ID); Columns.Add (COL_TEXT); ColumnSettings[COL_ID].id = COL_ID; ColumnSettings[COL_ID].name = "ID"; ColumnSettings[COL_ID].width = 0.33F; ColumnSettings[COL_TEXT].id = COL_TEXT; ColumnSettings[COL_TEXT].name = "English Test"; ColumnSettings[COL_TEXT].width = 0.66F; return ; } ///////////////////////////////////////////////////////////////////////////// // // DoDataExchange // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::DoDataExchange (CDataExchange *pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(StringsCategoryViewDialogClass) DDX_Control(pDX, IDC_STRING_LIST, m_ListCtrl); //}}AFX_DATA_MAP return ; } BEGIN_MESSAGE_MAP(StringsCategoryViewDialogClass, CDialog) //{{AFX_MSG_MAP(StringsCategoryViewDialogClass) ON_NOTIFY(LVN_COLUMNCLICK, IDC_STRING_LIST, OnColumnclickStringList) ON_NOTIFY(NM_DBLCLK, IDC_STRING_LIST, OnDblclkStringList) ON_NOTIFY(LVN_DELETEITEM, IDC_STRING_LIST, OnDeleteitemStringList) ON_NOTIFY(LVN_KEYDOWN, IDC_STRING_LIST, OnKeydownStringList) ON_WM_SIZE() ON_NOTIFY(LVN_ITEMCHANGED, IDC_STRING_LIST, OnItemchangedStringList) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // // Remove_Column // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Remove_Column (int col_id) { for (int index = 0; index < Columns.Count (); index ++) { // // If this is the column we were looking for, remove // it from list // if (Columns[index] == col_id) { Columns.Delete (index); break; } } return ; } ///////////////////////////////////////////////////////////////////////////// // // Set_Column_Width // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Set_Column_Width (int col_id, float width) { ColumnSettings[col_id].width = width; return ; } ///////////////////////////////////////////////////////////////////////////// // // Create // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Create (CWnd *parent_wnd) { CDialog::Create (StringsCategoryViewDialogClass::IDD, parent_wnd); return ; } ///////////////////////////////////////////////////////////////////////////// // // OnInitDialog // ///////////////////////////////////////////////////////////////////////////// BOOL StringsCategoryViewDialogClass::OnInitDialog (void) { CDialog::OnInitDialog (); // // Set the extended styles for the list control // m_ListCtrl.SetExtendedStyle (m_ListCtrl.GetExtendedStyle () | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT); // // Configure the columns // for (int index = 0; index < Columns.Count (); index ++) { m_ListCtrl.InsertColumn (index, ColumnSettings[index].name); } Update_Controls (); m_ListCtrl.GetClientRect (CurrListRect); // // Add an entry to the end that the user can double-click on to insert // new strings. // if (EditMode != EDIT_MODE_NONE) { m_ListCtrl.InsertItem (0, ""); } // // Get the list of strings from the database and insert them into the list control // for ( TDBObjClass *object = TranslateDBClass::Get_First_Object (CategoryID); object != NULL; object = TranslateDBClass::Get_Next_Object (CategoryID, object)) { // // Insert a copy of this object into the list control // Insert_New_Entry (object->Clone ()); } // // Sort the list // m_ListCtrl.SortItems (StringEntrySortCompareFn, MAKELONG (CurrentColSort, AscendingSort)); Resize_Controls (); // // Handle the default selection // if (m_ListCtrl.GetItemCount () > 0) { int item_index = -1; // // Try to find the object that is supposed to be selected // TDBObjClass *sel_object = TranslateDBClass::Find_Object (SelectedObjectID); if (sel_object != NULL) { item_index = Find_Entry (sel_object); } // // Select the object if possible, otherwise select the first entry // if (item_index >= 0) { m_ListCtrl.SetItemState (item_index, LVIS_SELECTED, LVIS_SELECTED); m_ListCtrl.EnsureVisible (item_index, FALSE); } else { m_ListCtrl.SetItemState (0, LVIS_SELECTED, LVIS_SELECTED); } } IsInitialized = true; return TRUE; } ///////////////////////////////////////////////////////////////////////////// // // Update_Controls // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Update_Controls (void) { // // Size the columns // CRect rect; m_ListCtrl.GetClientRect (&rect); int width = rect.Width () - ::GetSystemMetrics (SM_CXVSCROLL); // // Configure the columns // for (int index = 0; index < Columns.Count (); index ++) { m_ListCtrl.SetColumnWidth (index, ColumnSettings[index].width * width); } return ; } ///////////////////////////////////////////////////////////////////////////// // // Find_Entry // ///////////////////////////////////////////////////////////////////////////// int StringsCategoryViewDialogClass::Find_Entry (TDBObjClass *object) { int object_index = -1; // // Loop over all the objects in the list control // for (int index = 0; index < m_ListCtrl.GetItemCount (); index ++) { TDBObjClass *curr_object = (TDBObjClass *)m_ListCtrl.GetItemData (index); // // Is this the object we're looking for? // if (curr_object != NULL && curr_object->Get_ID () == object->Get_ID ()) { object_index = index; break; } } return object_index; } ///////////////////////////////////////////////////////////////////////////// // // OnDblclkStringList // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::OnDblclkStringList ( NMHDR * pNMHDR, LRESULT * pResult ) { (*pResult) = 0; // // Don't do anything if we aren't in edit mode // if (EditMode == EDIT_MODE_NONE) { return ; } TDBObjClass *object = NULL; // // Peek into the selected item to get the translation object associated with it // int selected_item = m_ListCtrl.GetNextItem (-1, LVNI_SELECTED | LVNI_ALL); if (selected_item >= 0) { object = (TDBObjClass *)m_ListCtrl.GetItemData (selected_item); } // // See if this is a string twiddler // StringTwiddlerClass *twiddler = NULL; if (object != NULL) { twiddler = object->As_StringTwiddlerClass (); } // // Determine which dialog to show // bool show_edit_string = true; if (EditMode == EDIT_MODE_TWIDDLER && object == NULL) { show_edit_string = false; } else if (twiddler != NULL) { show_edit_string = false; } // // Show a dialog to the user // if (show_edit_string) { // // Let the user edit this entry // EditStringDialogClass dialog (this); dialog.Set_Translate_Object (object); if (dialog.DoModal () == IDOK) { // // Either add a new entry to the list control, or update the // text of an existing entry // if (object == NULL) { TDBObjClass *new_object = dialog.Get_Translate_Object (); new_object->Set_Category_ID (CategoryID); TranslateDBClass::Add_Object (new_object); Insert_New_Entry (new_object->Clone ()); } else { Upate_Entry (selected_item); } } } else { EditStringTwiddlerDialogClass dialog (this); dialog.Set_Twiddler (twiddler); if (dialog.DoModal () == IDOK) { // // Either add a new entry to the list control, or update the // text of an existing entry // if (object == NULL) { StringTwiddlerClass *new_object = dialog.Get_Twiddler (); new_object->Set_Category_ID (CategoryID); TranslateDBClass::Add_Object (new_object); Insert_New_Entry (new_object->Clone ()); } else { Upate_Entry (selected_item); } } } // // Force a repaint // InvalidateRect (NULL, TRUE); UpdateWindow (); return ; } ///////////////////////////////////////////////////////////////////////////// // // OnColumnclickStringList // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::OnColumnclickStringList ( NMHDR * pNMHDR, LRESULT * pResult ) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; (*pResult) = 0; if (CurrentColSort == pNMListView->iSubItem) { AscendingSort = !AscendingSort; } else { CurrentColSort = pNMListView->iSubItem; AscendingSort = true; } m_ListCtrl.SortItems (StringEntrySortCompareFn, MAKELONG (CurrentColSort, AscendingSort)); return ; } ///////////////////////////////////////////////////////////////////////////// // // StringEntrySortCompareFn // ///////////////////////////////////////////////////////////////////////////// int CALLBACK StringEntrySortCompareFn (LPARAM param1, LPARAM param2, LPARAM sort_info) { int retval = 0; TDBObjClass *object1 = (TDBObjClass *)param1; TDBObjClass *object2 = (TDBObjClass *)param2; LONG column_id = LOWORD (sort_info); BOOL ascending = HIWORD (sort_info); if (object1 != NULL && object2 != NULL) { // // Determine the order based on which column the user clicked on // if (column_id == StringsCategoryViewDialogClass::COL_ID) { retval = ::lstrcmpi (object1->Get_ID_Desc (), object2->Get_ID_Desc ()); } else if (column_id == StringsCategoryViewDialogClass::COL_TEXT) { retval = ::lstrcmpi (object1->Get_English_String (), object2->Get_English_String ()); } } // // Invert the sort if necessary // if (ascending != TRUE) { retval = -retval; } return retval; } ///////////////////////////////////////////////////////////////////////////// // // Upate_Entry // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Upate_Entry (int index) { if (index < 0) { return ; } // // Update this list control entry // TDBObjClass *object = (TDBObjClass *)m_ListCtrl.GetItemData (index); if (object != NULL) { // // Update each column // for (int col_index = 0; col_index < Columns.Count (); col_index ++) { // // Determine what information to update // if (ColumnSettings[col_index].id == COL_ID) { m_ListCtrl.SetItemText (index, col_index, object->Get_ID_Desc ()); } else if (ColumnSettings[col_index].id == COL_TEXT) { m_ListCtrl.SetItemText (index, col_index, object->Get_English_String ()); } } } return ; } ///////////////////////////////////////////////////////////////////////////// // // Insert_New_Entry // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Insert_New_Entry (TDBObjClass *object) { if (object == NULL) { return ; } // // Ensure the new object has the right category // object->Set_Category_ID (CategoryID); // // Insert an item into the list control to represent this string // int last_index = max (m_ListCtrl.GetItemCount () - 1, 0); int item_index = m_ListCtrl.InsertItem (last_index, object->Get_ID_Desc ()); if (item_index >= 0) { // // Update each column // for (int col_index = 0; col_index < Columns.Count (); col_index ++) { // // Determine what information to update // if (ColumnSettings[col_index].id == COL_ID) { m_ListCtrl.SetItemText (item_index, col_index, object->Get_ID_Desc ()); } else if (ColumnSettings[col_index].id == COL_TEXT) { m_ListCtrl.SetItemText (item_index, col_index, object->Get_English_String ()); } } m_ListCtrl.SetItemData (item_index, (DWORD)object); } return ; } ///////////////////////////////////////////////////////////////////////////// // // OnDeleteitemStringList // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::OnDeleteitemStringList ( NMHDR * pNMHDR, LRESULT * pResult ) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; (*pResult) = 0; // // Free the associated translation object // TDBObjClass *object = (TDBObjClass *)m_ListCtrl.GetItemData (pNMListView->iItem); SAFE_DELETE (object); m_ListCtrl.SetItemData (pNMListView->iItem, 0L); return ; } ///////////////////////////////////////////////////////////////////////////// // // OnKeydownStringList // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::OnKeydownStringList ( NMHDR * pNMHDR, LRESULT * pResult ) { LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR; (*pResult) = 0; // // Don't do anything if we aren't in edit mode // if (EditMode == EDIT_MODE_NONE) { return ; } if (pLVKeyDow->wVKey == VK_DELETE) { // // Delete all the selected items (except for the last item) // int index = -1; while ((index = m_ListCtrl.GetNextItem (-1, LVNI_SELECTED | LVNI_ALL)) >= 0) { if (index < m_ListCtrl.GetItemCount () - 1) { m_ListCtrl.DeleteItem (index); ShouldUpdateVersionNumber = true; } else { break; } } } else if (::GetKeyState (VK_CONTROL) < 0) { if (CallbackObject != NULL) { // // Check to see if the user pressed the copy or paste keys // switch (pLVKeyDow->wVKey) { case 'x': case 'X': CallbackObject->On_Cut (); break; case 'v': case 'V': CallbackObject->On_Paste (); break; } } } return ; } ///////////////////////////////////////////////////////////////////////////// // // Resize_Controls // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Resize_Controls (void) { if (IsInitialized == false) { return ; } // // Get the current client rectangle of the list control // CRect new_list_rect; GetClientRect (&new_list_rect); int old_width = CurrListRect.Width (); // // Resize each column so its the same percentage size as it was before // for (int col_index = 0; col_index < Columns.Count (); col_index ++) { float col_per = (float)m_ListCtrl.GetColumnWidth (col_index) / (float)old_width; int new_width = int(new_list_rect.Width () * col_per); m_ListCtrl.SetColumnWidth (col_index, new_width); } CurrListRect = new_list_rect; // // Get the window bounding rectangle // CRect rect; GetClientRect (&rect); // // Resize the list control // m_ListCtrl.SetWindowPos (NULL, rect.left, rect.top, rect.Width (), rect.Height (), SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE); return ; } ///////////////////////////////////////////////////////////////////////////// // // Apply_Changes // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Apply_Changes (void) { // // Don't do anything if we aren't in edit mode // if (EditMode == EDIT_MODE_NONE) { return ; } // // Add all the objects from the list control into the database // int count = m_ListCtrl.GetItemCount (); for (int index = 0; index < count; index ++) { TDBObjClass *object = (TDBObjClass *)m_ListCtrl.GetItemData (index); if (object != NULL) { TranslateDBClass::Add_Object (object->Clone ()); } } return ; } ///////////////////////////////////////////////////////////////////////////// // // OnSize // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::OnSize (UINT nType, int cx, int cy) { CDialog::OnSize (nType, cx, cy); Resize_Controls (); return ; } ///////////////////////////////////////////////////////////////////////////// // // Cut // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Cut ( DynamicVectorClass & entry_list, bool sel_only ) { m_ListCtrl.SetRedraw (false); // // Enable the selected flag if we only want to cut the current selection set. // int flags = LVNI_ALL; if (sel_only) { flags |= LVNI_SELECTED; } // // Remove all selected entries from the list control and add them to the list // int index = -1; while ((index = m_ListCtrl.GetNextItem (-1, flags)) != -1) { TDBObjClass *object = (TDBObjClass *)m_ListCtrl.GetItemData (index); if (object != NULL) { entry_list.Add (object->Clone ()); m_ListCtrl.DeleteItem (index); } else if (m_ListCtrl.GetItemCount () == 1) { break; } } m_ListCtrl.SetRedraw (true); return ; } ///////////////////////////////////////////////////////////////////////////// // // Paste // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::Paste (DynamicVectorClass &entry_list) { m_ListCtrl.SetRedraw (false); // // Add a new entry for each object // for (int index = 0; index < entry_list.Count (); index ++) { TDBObjClass *object = entry_list[index]; TDBObjClass *our_copy = object->Clone (); our_copy->Set_Category_ID (CategoryID); Insert_New_Entry (our_copy); } // // Resort the list // m_ListCtrl.SortItems (StringEntrySortCompareFn, MAKELONG (CurrentColSort, AscendingSort)); m_ListCtrl.SetRedraw (true); return ; } ///////////////////////////////////////////////////////////////////////////// // // Get_Selection // ///////////////////////////////////////////////////////////////////////////// int StringsCategoryViewDialogClass::Get_Selection (void) { // // Check to see if we need to update the cached selection ID // if (::IsWindow (m_hWnd)) { // // Find the selected item // int index = m_ListCtrl.GetNextItem (-1, LVNI_SELECTED | LVNI_ALL); if (index != -1) { // // Store the selected item's object id // TDBObjClass *object = (TDBObjClass *)m_ListCtrl.GetItemData (index); if (object != NULL) { SelectedObjectID = object->Get_ID (); } } else { SelectedObjectID = 0; } } return SelectedObjectID; } ///////////////////////////////////////////////////////////////////////////// // // OnItemchangedStringList // ///////////////////////////////////////////////////////////////////////////// void StringsCategoryViewDialogClass::OnItemchangedStringList ( NMHDR * pNMHDR, LRESULT * pResult ) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; (*pResult) = 0; if (IsInitialized && (pNMListView->uChanged & LVIF_STATE)) { // // This will force the cached selection data to be updated // Get_Selection (); // // Notify the owner (if necessary) // if (CallbackObject != NULL) { CallbackObject->On_Selection_Changed (SelectedObjectID); } } return ; }