| 1 | ///////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: propeditor.cpp |
| 3 | // Purpose: wxWindows Configuration Tool property editor |
| 4 | // Author: Julian Smart |
| 5 | // Modified by: |
| 6 | // Created: 2003-06-03 |
| 7 | // RCS-ID: $Id$ |
| 8 | // Copyright: (c) Julian Smart |
| 9 | // Licence: |
| 10 | ///////////////////////////////////////////////////////////////////////////// |
| 11 | |
| 12 | #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) |
| 13 | #pragma implementation "propeditor.h" |
| 14 | #endif |
| 15 | |
| 16 | #include <wx/wx.h> |
| 17 | |
| 18 | #ifdef __BORLANDC__ |
| 19 | #pragma hdrstop |
| 20 | #endif |
| 21 | |
| 22 | #include "wx/html/htmlwin.h" |
| 23 | #include "wx/grid.h" |
| 24 | #include "wx/filedlg.h" |
| 25 | #include "wx/tokenzr.h" |
| 26 | #include "wx/valgen.h" |
| 27 | |
| 28 | #include "propeditor.h" |
| 29 | #include "symbols.h" |
| 30 | #include "utils.h" |
| 31 | #include "configtooldoc.h" |
| 32 | #include "configitemselector.h" |
| 33 | #include "bitmaps/ellipsis.xpm" |
| 34 | |
| 35 | |
| 36 | /*! |
| 37 | * A container window for the property editor. |
| 38 | * and attribute editor. |
| 39 | */ |
| 40 | |
| 41 | IMPLEMENT_CLASS(ctPropertyEditor, wxPanel) |
| 42 | |
| 43 | BEGIN_EVENT_TABLE(ctPropertyEditor, wxPanel) |
| 44 | EVT_BUTTON(ctID_ATTRIBUTE_EDITOR_EDIT_DETAILS, ctPropertyEditor::OnEditDetails) |
| 45 | EVT_GRID_SELECT_CELL(ctPropertyEditor::OnSelectCell) |
| 46 | EVT_GRID_CELL_CHANGE(ctPropertyEditor::OnChangeCell) |
| 47 | EVT_GRID_CELL_LEFT_DCLICK(ctPropertyEditor::OnDClickCell) |
| 48 | EVT_UPDATE_UI(ctID_ATTRIBUTE_EDITOR_EDIT_DETAILS, ctPropertyEditor::OnUpdateEditDetails) |
| 49 | END_EVENT_TABLE() |
| 50 | |
| 51 | ctPropertyEditor::ctPropertyEditor(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style): |
| 52 | wxPanel(parent, id, pos, size, style) |
| 53 | { |
| 54 | m_splitterWindow = NULL; |
| 55 | m_attributeEditorGrid = NULL; |
| 56 | m_propertyDescriptionWindow = NULL; |
| 57 | m_elementTitleTextCtrl = NULL; |
| 58 | |
| 59 | CreateControls(this); |
| 60 | } |
| 61 | |
| 62 | ctPropertyEditor::~ctPropertyEditor() |
| 63 | { |
| 64 | } |
| 65 | |
| 66 | void ctPropertyEditor::CreateControls(wxWindow* parent) |
| 67 | { |
| 68 | m_elementTitleTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); |
| 69 | wxBitmap detailsIcon(ellipsis_xpm); |
| 70 | |
| 71 | wxBoxSizer *item0 = new wxBoxSizer( wxVERTICAL ); |
| 72 | |
| 73 | wxBoxSizer *item1 = new wxBoxSizer( wxHORIZONTAL ); |
| 74 | |
| 75 | wxTextCtrl *item2 = m_elementTitleTextCtrl; |
| 76 | item1->Add( item2, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
| 77 | |
| 78 | wxButton *item3a = new wxButton( parent, ctID_ATTRIBUTE_EDITOR_EDIT_DETAILS, wxT("Edit..."), wxDefaultPosition, wxSize(-1, -1)); |
| 79 | item1->Add( item3a, 0, wxALIGN_CENTRE|wxRIGHT|wxTOP|wxBOTTOM, 5 ); |
| 80 | |
| 81 | item0->Add( item1, 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5 ); |
| 82 | |
| 83 | // TODO: find sash pos from last time |
| 84 | int sashPos = 100; |
| 85 | |
| 86 | m_splitterWindow = new wxSplitterWindow(parent, ctID_PROPERTY_EDITOR_SPLITTER, wxDefaultPosition, wxSize(500, 400), wxSP_3DSASH|wxSP_FULLSASH/*|wxCLIP_CHILDREN*/ |wxBORDER_NONE|wxNO_FULL_REPAINT_ON_RESIZE); |
| 87 | m_splitterWindow->SetMinimumPaneSize(10); |
| 88 | |
| 89 | m_propertyDescriptionWindow = new wxHtmlWindow(m_splitterWindow, ctID_ATTRIBUTE_EDITOR_DESCRIPTION, wxDefaultPosition, wxSize(200, 60), wxSUNKEN_BORDER); |
| 90 | m_propertyDescriptionWindow->SetBackgroundColour(ctDESCRIPTION_BACKGROUND_COLOUR); |
| 91 | m_propertyDescriptionWindow->SetBorders(4); |
| 92 | m_attributeEditorGrid = new ctPropertyEditorGrid(m_splitterWindow, ctID_ATTRIBUTE_EDITOR_GRID , wxPoint(0, 0), wxSize(200, 100), wxBORDER_SUNKEN | wxWANTS_CHARS); |
| 93 | |
| 94 | m_splitterWindow->SplitHorizontally(m_propertyDescriptionWindow, m_attributeEditorGrid, sashPos); |
| 95 | |
| 96 | // TODO: show or hide description window |
| 97 | // if (some-setting) |
| 98 | // ShowDescriptionWindow(FALSE); |
| 99 | |
| 100 | item0->Add( m_splitterWindow, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxBOTTOM, 5 ); |
| 101 | |
| 102 | this->SetAutoLayout( TRUE ); |
| 103 | this->SetSizer( item0 ); |
| 104 | |
| 105 | /// Add help text |
| 106 | m_elementTitleTextCtrl->SetHelpText(_("The title of the property being edited.")); |
| 107 | FindWindow(ctID_ATTRIBUTE_EDITOR_EDIT_DETAILS)->SetHelpText(_("Click to use an appropriate editor for the selected property (if any).")); |
| 108 | FindWindow(ctID_ATTRIBUTE_EDITOR_GRID)->SetHelpText(_("Shows the properties of the selected item.")); |
| 109 | FindWindow(ctID_ATTRIBUTE_EDITOR_DESCRIPTION)->SetHelpText(_("Shows a description of the selected property, or a summary of the whole item.")); |
| 110 | |
| 111 | /// Set up the grid to display properties |
| 112 | m_attributeEditorGrid->RegisterDataType(ctGRID_VALUE_STRING, |
| 113 | new wxGridCellStringRenderer, |
| 114 | new ctGridCellTextEditor); |
| 115 | |
| 116 | m_attributeEditorGrid->CreateGrid( 0, 2, wxGrid::wxGridSelectRows ); |
| 117 | m_attributeEditorGrid->SetRowLabelSize(0); |
| 118 | m_attributeEditorGrid->SetColLabelSize(0 /* 18 */); |
| 119 | |
| 120 | wxArrayString columns; |
| 121 | columns.Add(_T("Name")); |
| 122 | columns.Add(_T("Value")); |
| 123 | |
| 124 | m_attributeEditorGrid->SetColSize(0, 140); |
| 125 | m_attributeEditorGrid->SetColSize(1, 80); |
| 126 | |
| 127 | m_attributeEditorGrid->SetColumnsToDisplay(columns); |
| 128 | m_attributeEditorGrid->DisplayLabels(); |
| 129 | |
| 130 | m_attributeEditorGrid->SetStretchableColumn(1); |
| 131 | |
| 132 | m_attributeEditorGrid->SetDefaultCellBackgroundColour(ctCELL_BACKGROUND_COLOUR); |
| 133 | |
| 134 | UpdateDescription(); |
| 135 | } |
| 136 | |
| 137 | /// Show/hide the description control |
| 138 | void ctPropertyEditor::ShowDescriptionWindow(bool show) |
| 139 | { |
| 140 | if (!show) |
| 141 | { |
| 142 | if (m_splitterWindow->IsSplit()) |
| 143 | m_splitterWindow->Unsplit(m_propertyDescriptionWindow); |
| 144 | } |
| 145 | else |
| 146 | { |
| 147 | // TODO |
| 148 | int pos = 100; |
| 149 | m_propertyDescriptionWindow->Show(TRUE); |
| 150 | if (!m_splitterWindow->IsSplit()) |
| 151 | { |
| 152 | m_splitterWindow->SplitHorizontally(m_propertyDescriptionWindow, m_attributeEditorGrid, pos); |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | /// Clear grid editor |
| 158 | void ctPropertyEditor::ClearEditor() |
| 159 | { |
| 160 | m_attributeEditorGrid->ClearAttributes(); |
| 161 | m_propertyDescriptionWindow->SetPage(WrapDescription(wxEmptyString)); |
| 162 | m_elementTitleTextCtrl->SetValue(_T("")); |
| 163 | } |
| 164 | |
| 165 | /// Handles detailed editing event. |
| 166 | void ctPropertyEditor::OnEditDetails(wxCommandEvent& WXUNUSED(event)) |
| 167 | { |
| 168 | wxWindow* parentFrame = this; |
| 169 | while (parentFrame && !parentFrame->IsKindOf(CLASSINFO(wxFrame))) |
| 170 | parentFrame = parentFrame->GetParent(); |
| 171 | |
| 172 | EditDetails(parentFrame); |
| 173 | } |
| 174 | |
| 175 | /// Handles detailed editing update event. |
| 176 | void ctPropertyEditor::OnUpdateEditDetails(wxUpdateUIEvent& event) |
| 177 | { |
| 178 | event.Enable(CanEditDetails()); |
| 179 | } |
| 180 | |
| 181 | /// Can we edit the details of the selected property? |
| 182 | bool ctPropertyEditor::CanEditDetails() |
| 183 | { |
| 184 | if (!m_item) |
| 185 | return FALSE; |
| 186 | |
| 187 | int row; |
| 188 | ctProperty* prop = FindSelectedProperty(row); |
| 189 | if (!prop || prop->GetEditorType().IsEmpty()) |
| 190 | return FALSE; |
| 191 | return TRUE; |
| 192 | } |
| 193 | |
| 194 | /// Shows the item |
| 195 | void ctPropertyEditor::ShowItem(ctConfigItem* item) |
| 196 | { |
| 197 | if (m_attributeEditorGrid->IsCellEditControlEnabled()) |
| 198 | m_attributeEditorGrid->SaveEditControlValue(); |
| 199 | |
| 200 | ClearEditor(); |
| 201 | if (item) |
| 202 | { |
| 203 | m_item = item; |
| 204 | |
| 205 | UpdateTitle(); |
| 206 | |
| 207 | m_attributeEditorGrid->AppendRows(m_item->GetProperties().GetCount()); |
| 208 | |
| 209 | wxNode* node = m_item->GetProperties().GetList().GetFirst(); |
| 210 | int i = 0; |
| 211 | while (node) |
| 212 | { |
| 213 | ctProperty* prop = (ctProperty*) node->GetData(); |
| 214 | DisplayProperty(i, prop); |
| 215 | |
| 216 | i ++; |
| 217 | node = node->GetNext(); |
| 218 | } |
| 219 | // Make sure scrollbars are up-to-date, etc. |
| 220 | wxSizeEvent event(m_attributeEditorGrid->GetSize(), m_attributeEditorGrid->GetId()); |
| 221 | m_attributeEditorGrid->GetEventHandler()->ProcessEvent(event); |
| 222 | |
| 223 | DisplayDefaultProperty(); |
| 224 | } |
| 225 | else |
| 226 | { |
| 227 | m_item = NULL; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | /// Determine background colour for this property. |
| 232 | void ctPropertyEditor::DeterminePropertyColour(ctProperty* prop, wxColour& colour) |
| 233 | { |
| 234 | if (prop->IsCustom()) |
| 235 | colour = ctCUSTOM_CELL_BACKGROUND_COLOUR; |
| 236 | else |
| 237 | colour = ctCELL_BACKGROUND_COLOUR; |
| 238 | } |
| 239 | |
| 240 | /// Update the title at the top of the property editor |
| 241 | void ctPropertyEditor::UpdateTitle() |
| 242 | { |
| 243 | if (m_item) |
| 244 | { |
| 245 | wxString name(m_item->GetTitle()); |
| 246 | m_elementTitleTextCtrl->SetValue(name); |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | /// Updates the item display, assuming it was already displayed. |
| 251 | void ctPropertyEditor::UpdateItem() |
| 252 | { |
| 253 | if (m_attributeEditorGrid->IsCellEditControlEnabled()) |
| 254 | m_attributeEditorGrid->SaveEditControlValue(); |
| 255 | if (m_item) |
| 256 | { |
| 257 | UpdateTitle(); |
| 258 | |
| 259 | wxNode* node = m_item->GetProperties().GetList().GetFirst(); |
| 260 | int i = 0; |
| 261 | while (node) |
| 262 | { |
| 263 | ctProperty* prop = (ctProperty*) node->GetData(); |
| 264 | DisplayProperty(i, prop, TRUE); |
| 265 | |
| 266 | i ++; |
| 267 | node = node->GetNext(); |
| 268 | } |
| 269 | // Make sure scrollbars are up-to-date, etc. |
| 270 | wxSizeEvent event(m_attributeEditorGrid->GetSize(), this->GetId()); |
| 271 | m_attributeEditorGrid->GetEventHandler()->ProcessEvent(event); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /// Display attribute at given row |
| 276 | bool ctPropertyEditor::DisplayProperty(int row, ctProperty* prop, bool valueOnly) |
| 277 | { |
| 278 | wxColour backgroundColour; |
| 279 | |
| 280 | DeterminePropertyColour(prop, backgroundColour); |
| 281 | |
| 282 | if (!valueOnly) |
| 283 | { |
| 284 | m_attributeEditorGrid->SetCellBackgroundColour(row, 0, backgroundColour); |
| 285 | m_attributeEditorGrid->SetCellBackgroundColour(row, 1, backgroundColour); |
| 286 | |
| 287 | m_attributeEditorGrid->SetCellValue(row, 0, prop->GetName()); |
| 288 | |
| 289 | m_attributeEditorGrid->SetReadOnly(row, 0); |
| 290 | // Set the alignment |
| 291 | m_attributeEditorGrid->SetCellAlignment(row, 1, wxALIGN_LEFT, wxALIGN_CENTER); |
| 292 | } |
| 293 | |
| 294 | if (!m_item->CanEditProperty(prop->GetName())) |
| 295 | { |
| 296 | m_attributeEditorGrid->SetReadOnly(row, 1); |
| 297 | |
| 298 | wxColour col(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); |
| 299 | m_attributeEditorGrid->SetCellTextColour(row, 1, col); |
| 300 | } |
| 301 | else |
| 302 | { |
| 303 | m_attributeEditorGrid->SetReadOnly(row, 1, FALSE); |
| 304 | m_attributeEditorGrid->SetCellTextColour(row, 1, * wxBLACK); |
| 305 | } |
| 306 | |
| 307 | // Set the value |
| 308 | m_attributeEditorGrid->SetCellValue(row, 1, ctConvertToSingleText(prop->GetValue())); |
| 309 | |
| 310 | if (valueOnly) |
| 311 | return TRUE; |
| 312 | |
| 313 | // Set the value type |
| 314 | if (prop->GetEditorType() == _T("choice")) |
| 315 | { |
| 316 | #if 0 |
| 317 | wxString* strArr = prop->GetChoices().GetStringArray(); |
| 318 | |
| 319 | m_attributeEditorGrid->SetCellEditor(row, 1, |
| 320 | new wxGridCellChoiceEditor(prop->GetChoices().GetCount(), strArr)); |
| 321 | |
| 322 | delete[] strArr; |
| 323 | #endif |
| 324 | m_attributeEditorGrid->SetCellEditor(row, 1, |
| 325 | new wxGridCellChoiceEditor(prop->GetChoices())); |
| 326 | } |
| 327 | else if (prop->GetEditorType() == _T("integer") || prop->GetVariant().GetType() == _T("long")) |
| 328 | { |
| 329 | m_attributeEditorGrid->SetCellEditor(row, 1, |
| 330 | new wxGridCellNumberEditor); |
| 331 | } |
| 332 | else if (prop->GetEditorType() == _T("float") || prop->GetVariant().GetType() == _T("double")) |
| 333 | { |
| 334 | m_attributeEditorGrid->SetCellEditor(row, 1, |
| 335 | new wxGridCellFloatEditor); |
| 336 | } |
| 337 | else if (prop->GetEditorType() == _T("bool") || prop->GetVariant().GetType() == _T("bool")) |
| 338 | { |
| 339 | m_attributeEditorGrid->SetCellValue(row, 1, prop->GetVariant().GetBool() ? _T("1") : _T("0")); |
| 340 | m_attributeEditorGrid->SetCellEditor(row, 1, |
| 341 | new wxGridCellBoolEditor); |
| 342 | m_attributeEditorGrid->SetCellRenderer(row, 1, new wxGridCellBoolRenderer); |
| 343 | } |
| 344 | else |
| 345 | { |
| 346 | m_attributeEditorGrid->SetCellEditor(row, 1, |
| 347 | new ctGridCellTextEditor); |
| 348 | } |
| 349 | |
| 350 | return TRUE; |
| 351 | } |
| 352 | |
| 353 | /// Display attribute value |
| 354 | bool ctPropertyEditor::DisplayProperty(ctProperty* prop) |
| 355 | { |
| 356 | if (!m_item) |
| 357 | return FALSE; |
| 358 | |
| 359 | int index = m_item->GetProperties().GetList().IndexOf(prop); |
| 360 | return DisplayProperty(index, prop, TRUE); |
| 361 | } |
| 362 | |
| 363 | /// Display the default property |
| 364 | bool ctPropertyEditor::DisplayDefaultProperty() |
| 365 | { |
| 366 | if (!m_item) |
| 367 | return FALSE; |
| 368 | |
| 369 | wxString str = m_item->GetDefaultProperty(); |
| 370 | |
| 371 | ctProperty* prop = m_item->GetProperties().FindProperty(str); |
| 372 | if (prop) |
| 373 | { |
| 374 | int index = m_item->GetProperties().GetList().IndexOf(prop); |
| 375 | this->m_attributeEditorGrid->SelectRow(index); |
| 376 | this->m_attributeEditorGrid->SetGridCursor(index, 1); |
| 377 | } |
| 378 | return TRUE; |
| 379 | } |
| 380 | |
| 381 | /// Edit the default property |
| 382 | bool ctPropertyEditor::EditDefaultProperty(ctConfigItem* item) |
| 383 | { |
| 384 | wxString defaultPropertyName = item->GetDefaultProperty(); |
| 385 | if (!defaultPropertyName.IsEmpty()) |
| 386 | { |
| 387 | ctProperty* prop = item->GetProperties().FindProperty(defaultPropertyName); |
| 388 | if (prop) |
| 389 | { |
| 390 | int index = item->GetProperties().GetList().IndexOf(prop); |
| 391 | if (index >= 0) |
| 392 | { |
| 393 | this->m_attributeEditorGrid->SelectRow(index); |
| 394 | this->m_attributeEditorGrid->SetGridCursor(index, 1); |
| 395 | EditDetails(wxTheApp->GetTopWindow()); |
| 396 | return TRUE; |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | } |
| 401 | return FALSE; |
| 402 | } |
| 403 | |
| 404 | /// Find the selected property |
| 405 | ctProperty* ctPropertyEditor::FindSelectedProperty(int& row) |
| 406 | { |
| 407 | if (!m_item) |
| 408 | return NULL; |
| 409 | |
| 410 | int selRow = m_attributeEditorGrid->GetCursorRow(); |
| 411 | if (selRow > -1) |
| 412 | { |
| 413 | row = selRow; |
| 414 | |
| 415 | if (selRow < (int) m_item->GetProperties().GetCount()) |
| 416 | { |
| 417 | ctProperty* prop = m_item->GetProperties().GetNth(selRow); |
| 418 | return prop; |
| 419 | } |
| 420 | } |
| 421 | return NULL; |
| 422 | } |
| 423 | |
| 424 | /// Find the property |
| 425 | ctProperty* ctPropertyEditor::FindProperty(int row) |
| 426 | { |
| 427 | if (!m_item) |
| 428 | return NULL; |
| 429 | |
| 430 | if (row > -1) |
| 431 | { |
| 432 | if (row < (int) m_item->GetProperties().GetCount()) |
| 433 | { |
| 434 | ctProperty* prop = m_item->GetProperties().GetNth(row); |
| 435 | return prop; |
| 436 | } |
| 437 | } |
| 438 | return NULL; |
| 439 | } |
| 440 | |
| 441 | /// Edit the details of this cell appropriately. |
| 442 | bool ctPropertyEditor::EditDetails(wxWindow* WXUNUSED(parent)) |
| 443 | { |
| 444 | if (CanEditDetails()) |
| 445 | { |
| 446 | int row; |
| 447 | ctProperty* prop = FindSelectedProperty(row); |
| 448 | if (!prop) |
| 449 | return FALSE; |
| 450 | |
| 451 | wxString type(prop->GetEditorType()); |
| 452 | wxString value = m_attributeEditorGrid->GetCellValue(row, 1); |
| 453 | |
| 454 | if (type == _T("multiline")) |
| 455 | { |
| 456 | value = ctConvertToMultilineText(value); |
| 457 | wxString msg; |
| 458 | msg.Printf(wxT("Edit %s:"), (const wxChar*) prop->GetName()); |
| 459 | ctMultiLineTextEditor dialog(wxTheApp->GetTopWindow(), |
| 460 | -1, wxT("Edit Text Property"), msg, value); |
| 461 | if (dialog.ShowModal() == wxID_OK) |
| 462 | { |
| 463 | value = ctConvertToSingleText(dialog.GetText()); |
| 464 | m_attributeEditorGrid->SetCellValue(row, 1, value); |
| 465 | ApplyCellValueToProperty(row, 1); |
| 466 | return TRUE; |
| 467 | } |
| 468 | else |
| 469 | return FALSE; |
| 470 | } |
| 471 | else if (type == _T("filename")) |
| 472 | { |
| 473 | wxString fullPath = value; |
| 474 | wxString defaultDir ; |
| 475 | wxString defaultFilename = wxFileNameFromPath(fullPath); |
| 476 | |
| 477 | defaultDir = wxPathOnly(value); |
| 478 | |
| 479 | wxString msg = wxT("Choose a filename"); |
| 480 | wxFileDialog dialog(wxTheApp->GetTopWindow(), |
| 481 | msg, defaultDir, defaultFilename, wxT("*.*")); |
| 482 | if (dialog.ShowModal() == wxID_OK) |
| 483 | { |
| 484 | fullPath = dialog.GetPath(); |
| 485 | value = fullPath; |
| 486 | |
| 487 | m_attributeEditorGrid->SetCellValue(row, 1, value); |
| 488 | ApplyCellValueToProperty(row, 1); |
| 489 | return TRUE; |
| 490 | } |
| 491 | else |
| 492 | return FALSE; |
| 493 | } |
| 494 | else if (type == _T("configitems")) |
| 495 | { |
| 496 | wxArrayString items; |
| 497 | ctConfigItem::StringToArray(value, items); |
| 498 | |
| 499 | ctConfigItemsSelector dialog(wxTheApp->GetTopWindow(), |
| 500 | -1, wxT("Select Configuration Items")); |
| 501 | dialog.SetConfigList(items); |
| 502 | if (dialog.ShowModal() == wxID_OK) |
| 503 | { |
| 504 | wxString newValue; |
| 505 | items = dialog.GetConfigList(); |
| 506 | ctConfigItem::ArrayToString(items, newValue); |
| 507 | |
| 508 | m_attributeEditorGrid->SetCellValue(row, 1, newValue); |
| 509 | ApplyCellValueToProperty(row, 1); |
| 510 | return TRUE; |
| 511 | } |
| 512 | else |
| 513 | return FALSE; |
| 514 | } |
| 515 | } |
| 516 | |
| 517 | return FALSE; |
| 518 | } |
| 519 | |
| 520 | /// Intercept selection event. |
| 521 | void ctPropertyEditor::OnSelectCell(wxGridEvent& event) |
| 522 | { |
| 523 | int row = event.GetRow(); |
| 524 | |
| 525 | UpdateDescription(row); |
| 526 | |
| 527 | event.Skip(); |
| 528 | } |
| 529 | |
| 530 | /// Update the description |
| 531 | void ctPropertyEditor::UpdateDescription(int row) |
| 532 | { |
| 533 | if (row == -1) |
| 534 | { |
| 535 | row = m_attributeEditorGrid->GetCursorRow(); |
| 536 | } |
| 537 | if (row == -1) |
| 538 | { |
| 539 | wxString str = WrapDescription(wxEmptyString); |
| 540 | m_propertyDescriptionWindow->SetPage(str); |
| 541 | } |
| 542 | else |
| 543 | { |
| 544 | ctProperty* prop = FindProperty(row); |
| 545 | if (prop) |
| 546 | { |
| 547 | wxString str = WrapDescription(m_item->GetDescription(prop)); |
| 548 | m_propertyDescriptionWindow->SetPage(str); |
| 549 | } |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | /// Wraps a description string in HTML |
| 554 | wxString ctPropertyEditor::WrapDescription(const wxString& s) |
| 555 | { |
| 556 | /// Convert a colour to a 6-digit hex string |
| 557 | wxColour col = ctDESCRIPTION_BACKGROUND_COLOUR; |
| 558 | wxString colStr = apColourToHexString(col); |
| 559 | colStr = wxT("#") + colStr; |
| 560 | |
| 561 | wxString str; |
| 562 | str << _T("<HTML><BODY BGCOLOR=\"") << colStr << wxT("\"><FONT SIZE=-1>") ; |
| 563 | str << s; |
| 564 | str << _T("</FONT></BODY></HTML>"); |
| 565 | |
| 566 | return str; |
| 567 | } |
| 568 | |
| 569 | /// Intercept cell data change event. |
| 570 | void ctPropertyEditor::OnChangeCell(wxGridEvent& event) |
| 571 | { |
| 572 | int row = event.GetRow(); |
| 573 | int col = event.GetCol(); |
| 574 | |
| 575 | ApplyCellValueToProperty(row, col); |
| 576 | } |
| 577 | |
| 578 | /// Double-click to show specialised editor. |
| 579 | void ctPropertyEditor::OnDClickCell(wxGridEvent& WXUNUSED(event)) |
| 580 | { |
| 581 | wxWindow* parentFrame = this; |
| 582 | while (parentFrame && !parentFrame->IsKindOf(CLASSINFO(wxFrame))) |
| 583 | parentFrame = parentFrame->GetParent(); |
| 584 | |
| 585 | EditDetails(parentFrame); |
| 586 | } |
| 587 | |
| 588 | /// Apply the cell value to the property, and notify the |
| 589 | /// item object. |
| 590 | void ctPropertyEditor::ApplyCellValueToProperty(int row, int col) |
| 591 | { |
| 592 | static bool s_Applying = FALSE; |
| 593 | |
| 594 | if (s_Applying) |
| 595 | return; |
| 596 | |
| 597 | s_Applying = TRUE; |
| 598 | if (col == 1 && m_item) |
| 599 | { |
| 600 | ctProperty* prop = m_item->GetProperties().GetNth(row); |
| 601 | |
| 602 | wxString value = m_attributeEditorGrid->GetCellValue(row, col); |
| 603 | if (prop->GetEditorType() == wxT("multiline")) |
| 604 | value = ctConvertToMultilineText(value); |
| 605 | |
| 606 | wxVariant variant = prop->GetVariant(); |
| 607 | |
| 608 | if (prop->GetVariant().GetType() == _T("bool")) |
| 609 | { |
| 610 | if (value == _T("1")) |
| 611 | variant = (bool) TRUE; |
| 612 | else |
| 613 | variant = (bool) FALSE; |
| 614 | } |
| 615 | else if (prop->GetVariant().GetType() == _T("long")) |
| 616 | { |
| 617 | long l; |
| 618 | value.ToLong(& l); |
| 619 | variant = l; |
| 620 | } |
| 621 | else if (prop->GetVariant().GetType() == _T("double")) |
| 622 | { |
| 623 | double d; |
| 624 | value.ToDouble(& d); |
| 625 | variant = d; |
| 626 | } |
| 627 | else |
| 628 | { |
| 629 | variant = value; |
| 630 | } |
| 631 | |
| 632 | ApplyPropertyValue(m_item, prop, variant); |
| 633 | |
| 634 | if (prop->GetName() == _T("description")) |
| 635 | UpdateDescription(row); |
| 636 | } |
| 637 | s_Applying = FALSE; |
| 638 | } |
| 639 | |
| 640 | /// Apply the cell value to the property, and notify the |
| 641 | /// item object. |
| 642 | void ctPropertyEditor::ApplyPropertyValue(ctConfigItem* item, ctProperty* property, const wxVariant& variant) |
| 643 | { |
| 644 | static bool s_Applying = FALSE; |
| 645 | |
| 646 | if (s_Applying) |
| 647 | return; |
| 648 | |
| 649 | s_Applying = TRUE; |
| 650 | |
| 651 | // Save the old values |
| 652 | ctProperties* oldProperties = new ctProperties(item->GetProperties()); |
| 653 | |
| 654 | wxVariant oldValue = property->GetVariant(); |
| 655 | |
| 656 | // Apply the new value |
| 657 | property->GetVariant() = variant; |
| 658 | item->ApplyProperty(property, oldValue); |
| 659 | item->Modify(); |
| 660 | |
| 661 | UpdateItem(); |
| 662 | |
| 663 | wxString menuLabel(_T("Change ") + property->GetName()); |
| 664 | |
| 665 | // This won't do anything first time Do is applied, |
| 666 | // since we've already done the action for this property. |
| 667 | // But when we Undo or Redo, the changed properties will be applied. |
| 668 | item->GetDocument()->GetCommandProcessor()->Submit( |
| 669 | new ctConfigCommand(menuLabel, ctCMD_APPLY_PROPERTY, |
| 670 | item, oldProperties, TRUE)); |
| 671 | |
| 672 | s_Applying = FALSE; |
| 673 | } |
| 674 | |
| 675 | /*! |
| 676 | * Attribute editor grid |
| 677 | */ |
| 678 | |
| 679 | IMPLEMENT_CLASS(ctPropertyEditorGrid, wxGrid) |
| 680 | |
| 681 | BEGIN_EVENT_TABLE(ctPropertyEditorGrid, wxGrid) |
| 682 | EVT_SIZE(ctPropertyEditorGrid::OnSize) |
| 683 | END_EVENT_TABLE() |
| 684 | |
| 685 | ctPropertyEditorGrid::ctPropertyEditorGrid(wxWindow* parent, wxWindowID id, |
| 686 | const wxPoint& pos, |
| 687 | const wxSize& sz, long style): |
| 688 | wxGrid(parent, id, pos, sz, style) |
| 689 | |
| 690 | { |
| 691 | m_stretchableColumn = -1; |
| 692 | } |
| 693 | |
| 694 | void ctPropertyEditorGrid::OnSize(wxSizeEvent& event) |
| 695 | { |
| 696 | if (m_stretchableColumn != -1) |
| 697 | { |
| 698 | // This window's client size = the internal window's |
| 699 | // client size if it has no borders |
| 700 | wxSize sz = GetClientSize(); |
| 701 | |
| 702 | int totalSize = 0; |
| 703 | int i; |
| 704 | for (i = 0; i < GetNumberCols(); i ++) |
| 705 | { |
| 706 | if (i != m_stretchableColumn) |
| 707 | { |
| 708 | totalSize += GetColSize(i); |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | // Allow for grid lines |
| 713 | totalSize += 1; |
| 714 | |
| 715 | int stretchSize = wxMax(5, sz.x - totalSize); |
| 716 | SetColSize(m_stretchableColumn, stretchSize); |
| 717 | } |
| 718 | |
| 719 | event.Skip(); |
| 720 | } |
| 721 | |
| 722 | /// Use m_columnsToDisplay to set the label strings |
| 723 | void ctPropertyEditorGrid::DisplayLabels() |
| 724 | { |
| 725 | size_t i; |
| 726 | for (i = 0; i < m_columnsToDisplay.GetCount(); i++) |
| 727 | { |
| 728 | SetColLabelValue(i, m_columnsToDisplay[i]); |
| 729 | } |
| 730 | } |
| 731 | |
| 732 | /// Clear attributes |
| 733 | bool ctPropertyEditorGrid::ClearAttributes() |
| 734 | { |
| 735 | if (GetNumberRows() > 0) |
| 736 | DeleteRows(0, GetNumberRows()); |
| 737 | return TRUE; |
| 738 | } |
| 739 | |
| 740 | /*! |
| 741 | * Use a single-line text control. |
| 742 | */ |
| 743 | |
| 744 | void ctGridCellTextEditor::Create(wxWindow* parent, wxWindowID id, |
| 745 | wxEvtHandler* evtHandler) |
| 746 | { |
| 747 | m_control = new wxTextCtrl(parent, id, wxEmptyString, |
| 748 | wxDefaultPosition, wxDefaultSize |
| 749 | ); |
| 750 | |
| 751 | wxGridCellEditor::Create(parent, id, evtHandler); |
| 752 | } |
| 753 | |
| 754 | |
| 755 | /// Translate the value to one which can be edited in a single-line |
| 756 | /// text control |
| 757 | wxString ctConvertToSingleText(const wxString& value) |
| 758 | { |
| 759 | wxString value1(value); |
| 760 | value1.Replace(wxT("\n"), wxT("\\n")); |
| 761 | value1.Replace(wxT("\t"), wxT("\\t")); |
| 762 | return value1; |
| 763 | } |
| 764 | |
| 765 | /// Translate back to 'real' characters, i.e. newlines are real |
| 766 | /// newlines. |
| 767 | wxString ctConvertToMultilineText(const wxString& value) |
| 768 | { |
| 769 | wxString value1(value); |
| 770 | value1.Replace(wxT("\\n"), wxT("\n")); |
| 771 | value1.Replace(wxT("\\t"), wxT("\t")); |
| 772 | return value1; |
| 773 | } |
| 774 | |
| 775 | //---------------------------------------------------------------------------- |
| 776 | // ctMultiLineTextEditor |
| 777 | //---------------------------------------------------------------------------- |
| 778 | |
| 779 | BEGIN_EVENT_TABLE(ctMultiLineTextEditor, wxDialog) |
| 780 | END_EVENT_TABLE() |
| 781 | |
| 782 | ctMultiLineTextEditor::ctMultiLineTextEditor( wxWindow *parent, wxWindowID id, const wxString &title, |
| 783 | const wxString& msg, |
| 784 | const wxString& value, |
| 785 | const wxPoint& pos, |
| 786 | const wxSize& size, |
| 787 | long style): |
| 788 | wxDialog(parent, id, title, pos, size, style) |
| 789 | { |
| 790 | m_text = value; |
| 791 | AddControls(this, msg); |
| 792 | Centre(); |
| 793 | } |
| 794 | |
| 795 | bool ctMultiLineTextEditor::AddControls(wxWindow* parent, const wxString& msg) |
| 796 | { |
| 797 | wxBoxSizer *item0 = new wxBoxSizer( wxVERTICAL ); |
| 798 | |
| 799 | wxBoxSizer *item1 = new wxBoxSizer( wxVERTICAL ); |
| 800 | |
| 801 | wxStaticText *item2 = new wxStaticText( parent, wxID_STATIC, msg, wxDefaultPosition, wxDefaultSize, 0 ); |
| 802 | item1->Add( item2, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT, 5 ); |
| 803 | |
| 804 | wxTextCtrl *item3 = new wxTextCtrl( parent, -1, wxT(""), wxDefaultPosition, wxSize(330,180), wxTE_MULTILINE|wxTE_RICH ); |
| 805 | item1->Add( item3, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
| 806 | |
| 807 | wxBoxSizer *item4 = new wxBoxSizer( wxHORIZONTAL ); |
| 808 | |
| 809 | item4->Add( 5, 5, 1, wxALIGN_CENTRE|wxALL, 5 ); |
| 810 | |
| 811 | wxButton *item5 = new wxButton( parent, wxID_OK, _("&OK"), wxDefaultPosition, wxDefaultSize, 0 ); |
| 812 | item5->SetDefault(); |
| 813 | item4->Add( item5, 0, wxALIGN_CENTRE|wxALL, 5 ); |
| 814 | |
| 815 | wxButton *item6 = new wxButton( parent, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxDefaultSize, 0 ); |
| 816 | item4->Add( item6, 0, wxALIGN_CENTRE|wxALL, 5 ); |
| 817 | |
| 818 | item1->Add( item4, 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5 ); |
| 819 | |
| 820 | item0->Add( item1, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
| 821 | |
| 822 | item3->SetValue(m_text); |
| 823 | item3->SetValidator(wxGenericValidator(& m_text)); |
| 824 | |
| 825 | |
| 826 | item3->SetFocus(); |
| 827 | ((wxButton*) FindWindow(wxID_OK))->SetDefault(); |
| 828 | |
| 829 | parent->SetAutoLayout( TRUE ); |
| 830 | parent->SetSizer(item0); |
| 831 | item0->Fit(parent); |
| 832 | |
| 833 | return TRUE; |
| 834 | } |
| 835 | |
| 836 | /* |
| 837 | * Special-purpose splitter window for changing sash look and |
| 838 | * also saving sash width |
| 839 | */ |
| 840 | |
| 841 | BEGIN_EVENT_TABLE(ctSplitterWindow, wxSplitterWindow) |
| 842 | EVT_SPLITTER_SASH_POS_CHANGED(-1, ctSplitterWindow::OnChangeSash) |
| 843 | END_EVENT_TABLE() |
| 844 | |
| 845 | ctSplitterWindow::ctSplitterWindow(wxWindow* parent, wxWindowID id, |
| 846 | const wxPoint& pos, const wxSize& size, long style): |
| 847 | wxSplitterWindow(parent, id, pos, size, style) |
| 848 | { |
| 849 | m_updateSettings = FALSE; |
| 850 | m_position = 0; |
| 851 | } |
| 852 | |
| 853 | void ctSplitterWindow::OnChangeSash(wxSplitterEvent& event) |
| 854 | { |
| 855 | if (!m_updateSettings) |
| 856 | return; |
| 857 | |
| 858 | m_position = event.GetSashPosition(); |
| 859 | } |