CMake/Source/WXDialog/PropertyList.cpp
2005-06-30 15:54:14 -04:00

857 lines
22 KiB
C++

/*=========================================================================
Program: WXDialog - wxWidgets X-platform GUI Front-End for CMake
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Author: Jorgen Bodde
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
/* for compilers that support precompilation
includes "wx/wx.h" */
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "PropertyList.h"
#include "app_resources.h"
#include "../cmCacheManager.h"
#include "../cmSystemTools.h"
#include "../cmake.h"
BEGIN_EVENT_TABLE( wxPropertyList, wxGrid )
EVT_GRID_CELL_LEFT_CLICK( wxPropertyList::OnSelectCell )
EVT_GRID_CELL_CHANGE( wxPropertyList::OnCellChange )
EVT_GRID_CMD_CELL_RIGHT_CLICK( wxID_ANY, wxPropertyList::OnCellPopup )
EVT_MENU ( ID_CACHE_DELETE, wxPropertyList::OnDeleteCache )
EVT_MENU ( ID_CACHE_IGNORE, wxPropertyList::OnIgnoreCache )
EVT_MENU ( ID_CACHE_BROWSE, wxPropertyList::OnBrowseItem )
EVT_SIZE ( wxPropertyList::OnSizeGrid )
EVT_CHAR ( wxPropertyList::OnKeyPressed )
END_EVENT_TABLE()
#if 0
// ----------------------------------------------------------------------------
// wxGridCellPathEditor
// ----------------------------------------------------------------------------
wxGridCellPathEditor::wxGridCellPathEditor()
{
m_maxChars = 0;
}
void wxGridCellPathEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
{
m_control = new wxTextCtrl(parent, id, wxEmptyString,
wxDefaultPosition, wxDefaultSize
#if defined(__WXMSW__)
, wxTE_PROCESS_TAB | wxTE_AUTO_SCROLL
#endif
);
//
// m_button = new wxButton(parent, id+1, _("..."),
// wxDefaultPosition, wxDefaultSize);
//
// set max length allowed in the textctrl, if the parameter was set
if (m_maxChars != 0)
{
((wxTextCtrl*)m_control)->SetMaxLength(m_maxChars);
}
wxGridCellEditor::Create(parent, id, evtHandler);
}
void wxGridCellPathEditor::PaintBackground(const wxRect& WXUNUSED(rectCell),
wxGridCellAttr * WXUNUSED(attr))
{
// as we fill the entire client area, don't do anything here to minimize
// flicker
}
void wxGridCellPathEditor::SetSize(const wxRect& rectOrig)
{
wxRect rect(rectOrig);
// Make the edit control large enough to allow for internal
// margins
//
// TODO: remove this if the text ctrl sizing is improved esp. for
// unix
//
#if defined(__WXGTK__)
if (rect.x != 0)
{
rect.x += 1;
rect.y += 1;
rect.width -= 1;
rect.height -= 1;
}
#else // !GTK
int extra_x = ( rect.x > 2 )? 2 : 1;
// MB: treat MSW separately here otherwise the caret doesn't show
// when the editor is in the first row.
#if defined(__WXMSW__)
int extra_y = 2;
#else
int extra_y = ( rect.y > 2 )? 2 : 1;
#endif // MSW
#if defined(__WXMOTIF__)
extra_x *= 2;
extra_y *= 2;
#endif
rect.SetLeft( wxMax(0, rect.x - extra_x) );
rect.SetTop( wxMax(0, rect.y - extra_y) );
rect.SetRight( rect.GetRight() + 2*extra_x );
rect.SetBottom( rect.GetBottom() + 2*extra_y );
#endif // GTK/!GTK
wxGridCellEditor::SetSize(rect);
}
void wxGridCellPathEditor::BeginEdit(int row, int col, wxGrid* grid)
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be Created first!"));
m_startValue = grid->GetTable()->GetValue(row, col);
DoBeginEdit(m_startValue);
}
void wxGridCellPathEditor::DoBeginEdit(const wxString& startValue)
{
Text()->SetValue(startValue);
Text()->SetInsertionPointEnd();
Text()->SetSelection(-1,-1);
Text()->SetFocus();
}
bool wxGridCellPathEditor::EndEdit(int row, int col,
wxGrid* grid)
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be Created first!"));
bool changed = false;
wxString value = Text()->GetValue();
if (value != m_startValue)
changed = true;
if (changed)
grid->GetTable()->SetValue(row, col, value);
m_startValue = wxEmptyString;
// No point in setting the text of the hidden control
//Text()->SetValue(m_startValue);
return changed;
}
void wxGridCellPathEditor::Reset()
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be Created first!"));
DoReset(m_startValue);
}
void wxGridCellPathEditor::DoReset(const wxString& startValue)
{
Text()->SetValue(startValue);
Text()->SetInsertionPointEnd();
}
bool wxGridCellPathEditor::IsAcceptedKey(wxKeyEvent& event)
{
return wxGridCellEditor::IsAcceptedKey(event);
}
void wxGridCellPathEditor::StartingKey(wxKeyEvent& event)
{
// Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
// longer an appropriate way to get the character into the text control.
// Do it ourselves instead. We know that if we get this far that we have
// a valid character, so not a whole lot of testing needs to be done.
wxTextCtrl* tc = Text();
wxChar ch;
long pos;
#if wxUSE_UNICODE
ch = event.GetUnicodeKey();
if (ch <= 127)
ch = (wxChar)event.GetKeyCode();
#else
ch = (wxChar)event.GetKeyCode();
#endif
switch (ch)
{
case WXK_DELETE:
// delete the character at the cursor
pos = tc->GetInsertionPoint();
if (pos < tc->GetLastPosition())
tc->Remove(pos, pos+1);
break;
case WXK_BACK:
// delete the character before the cursor
pos = tc->GetInsertionPoint();
if (pos > 0)
tc->Remove(pos-1, pos);
break;
default:
tc->WriteText(ch);
break;
}
}
void wxGridCellPathEditor::HandleReturn( wxKeyEvent &event )
{
#if defined(__WXMOTIF__) || defined(__WXGTK__)
// wxMotif needs a little extra help...
size_t pos = (size_t)( Text()->GetInsertionPoint() );
wxString s( Text()->GetValue() );
s = s.Left(pos) + wxT("\n") + s.Mid(pos);
Text()->SetValue(s);
Text()->SetInsertionPoint( pos );
#else
// the other ports can handle a Return key press
//
event.Skip();
#endif
}
void wxGridCellPathEditor::SetParameters(const wxString& params)
{
if ( !params )
{
// reset to default
m_maxChars = 0;
}
else
{
long tmp;
if ( !params.ToLong(&tmp) )
{
wxLogDebug(_T("Invalid wxGridCellPathEditor parameter string '%s' ignored"), params.c_str());
}
else
{
m_maxChars = (size_t)tmp;
}
}
}
// return the value in the text control
wxString wxGridCellPathEditor::GetValue() const
{
return Text()->GetValue();
}
#endif
/////////////////////////////////////////////////////////////////////////////
// wxPropertyItem
// returns true when this property item is a filepath
bool wxPropertyItem::IsFilePath()
{
return m_nItemType == wxPropertyList::FILE;
}
// returns true when this property item is a dir path
bool wxPropertyItem::IsDirPath()
{
return m_nItemType == wxPropertyList::PATH;
}
/////////////////////////////////////////////////////////////////////////////
// wxPropertyList
wxPropertyList::~wxPropertyList()
{
WX_CLEAR_ARRAY(m_PropertyItems);
}
int wxPropertyList::AddItem(const wxString &txt)
{
// TODO: Add the item to the grid!
//int nIndex = AddString(txt);
//return nIndex;
return 0;
}
// order = 0 sorted (not supported yet)
// order = 1 add to top
// order = 2 add to bottom
int wxPropertyList::AddPropItem(wxPropertyItem* pItem, int order)
{
m_PropertyItems.Add(pItem);
if(pItem->GetAdvanced() && ! m_ShowAdvanced)
return 0;
// disable in progress editing
HideControls();
return AddPropertyToGrid(pItem, order);
}
int wxPropertyList::AddPropertyToGrid(wxPropertyItem *pItem, int order)
{
int row = 0;
if(order == 1)
InsertRows(0,1);
else
{
AppendRows(1);
row = GetNumberRows() - 1;
}
// initialise the type of renderer
if(pItem->GetItemType() == wxPropertyList::CHECKBOX)
{
SetCellRenderer(row, 1, new wxGridCellBoolRenderer);
SetCellEditor(row, 1, new wxGridCellBoolEditor);
}
#ifdef __LINUX__
// fix to make sure scrollbars are drawn properly
wxGrid::AdjustScrollbars();
#endif
// the property display is read only
UpdatePropertyItem(pItem, row);
return row;
}
void wxPropertyList::AddProperty(const char* name, const char* value, const char* helpString,
int type, const char* comboItems, bool reverseOrder, bool advanced)
{
wxPropertyItem* pItem = 0;
// add or update the property item
for(size_t i = 0; i < m_PropertyItems.Count(); i++)
{
if(m_PropertyItems[i]->GetPropName().IsSameAs(name))
{
pItem = m_PropertyItems[i];
if(!pItem->GetCurValue().IsSameAs(value))
{
pItem->SetCurValue(value);
pItem->SetHelpString(helpString);
pItem->SetAdvanced(advanced);
// update the property item
int row = FindProperty(pItem);
if(row != -1)
UpdatePropertyItem(pItem, row);
}
return;
}
}
// if it is not found, then create a new one
if(!pItem)
{
pItem = new wxPropertyItem(name, value, helpString, type, comboItems);
pItem->SetAdvanced(advanced);
AddPropItem(pItem, 1);
}
}
void wxPropertyList::UpdateGridView()
{
// make sure all items are shown, remove items that should not be shown
bool keepItem;
int row;
for(size_t i = 0; i < m_PropertyItems.Count(); i++)
{
// to begin with, does this item fit the query?
keepItem = m_strQuery.IsEmpty() || (m_PropertyItems[i]->GetPropName().Find(m_strQuery) != -1);
if(keepItem)
{
// when advanced items are allowed to be shown, keep when ok
if(!m_ShowAdvanced)
keepItem = !m_PropertyItems[i]->GetAdvanced();
}
// find the item, if not present but keep is true, add, if
// present but keep is false, remove
row = -1;
for(size_t j = 0; j < (size_t)GetNumberRows(); j++)
{
if(m_PropertyItems[i]->GetPropName().IsSameAs(GetCellValue(j, 0)))
{
row = j;
break;
}
}
if(row == -1 && keepItem)
AddPropertyToGrid(m_PropertyItems[i], (m_ShowAdvanced ? 2 : 0));
else if(row != -1 && !keepItem)
DeleteRows(row, 1);
}
#ifdef __LINUX__
// fix to make sure scrollbars are drawn properly
wxGrid::AdjustScrollbars();
#endif
}
void wxPropertyList::HideControls()
{
DisableCellEditControl();
}
void wxPropertyList::RemoveProperty(wxPropertyItem *pItem)
{
HideControls();
// look for property in grid, delete it when present
for(size_t j = 0; j < (size_t)GetNumberRows(); j++)
{
if(pItem->GetPropName().IsSameAs(GetCellValue(j, 0), false))
{
DeleteRows(j, 1);
#ifdef __LINUX__
// fix to make sure scrollbars are drawn properly
wxGrid::AdjustScrollbars();
#endif
break;
}
}
// delete the item from the list
m_PropertyItems.Remove(pItem);
delete pItem;
}
wxPropertyItem *wxPropertyList::FindPropertyByName(const wxString &name)
{
for(size_t i = 0; i < m_PropertyItems.Count(); i++)
{
// we have an advanced item, go through table and if not present, show it
if(m_PropertyItems[i]->GetPropName().IsSameAs(name, true))
return m_PropertyItems[i];
}
return 0;
}
/**
void wxPropertyList::OnIgnore()
{
if(m_curSel == -1 || this->GetCount() <= 0)
{
return;
}
wxPropertyItem* pItem = (wxPropertyItem*) GetItemDataPtr(m_curSel);
pItem->m_curValue = "IGNORE";
InvalidateList();
}
*/
/**
void wxPropertyList::OnDelete()
{
if(m_curSel == -1 || this->GetCount() <= 0)
{
return;
}
wxPropertyItem* pItem = (wxPropertyItem*) GetItemDataPtr(m_curSel);
m_CMakeSetupDialog->GetCMakeInstance()->GetCacheManager()->RemoveCacheEntry(pItem->m_propName);
m_PropertyItems.erase(pItem);
delete pItem;
this->DeleteString(m_curSel);
this->HideControls();
this->SetTopIndex(0);
InvalidateList();
}
*/
/**
void wxPropertyList::OnHelp()
{
if(m_curSel == -1 || this->GetCount() <= 0)
{
return;
}
wxPropertyItem* pItem = (wxPropertyItem*) GetItemDataPtr(m_curSel);
MessageBox(pItem->m_HelpString, pItem->m_propName, MB_OK|MB_ICONINFORMATION);
}
*/
void wxPropertyList::RemoveAll()
{
WX_CLEAR_ARRAY(m_PropertyItems);
m_generatedProjects = false;
if(GetNumberRows() > 0)
DeleteRows(0, GetNumberRows());
m_strQuery.Empty();
#ifdef __LINUX__
// fix to make sure scrollbars are drawn properly
wxGrid::AdjustScrollbars();
#endif
}
void wxPropertyList::ShowAdvanced()
{
// set flag in the control
m_ShowAdvanced = true;
UpdateGridView();
}
void wxPropertyList::HideAdvanced()
{
// set flag in the control
m_ShowAdvanced = false;
UpdateGridView();
}
int wxPropertyList::FindProperty(wxPropertyItem *pItem)
{
if(GetNumberRows() > 0 && pItem != 0)
{
// find the property the traditional way
for(size_t j = 0; j < (size_t)GetNumberRows(); j++)
{
if(pItem->GetPropName().IsSameAs(GetCellValue(j, 0)))
return j;
}
}
return -1;
}
wxPropertyItem *wxPropertyList::GetPropertyItemFromRow(int row)
{
if(row < GetNumberRows() && row >= 0)
{
wxString str = GetCellValue(row, 0);
// find the property the traditional way
for(size_t i = 0; i < (size_t)m_PropertyItems.Count(); i++)
{
if(m_PropertyItems[i]->GetPropName().IsSameAs(str))
return m_PropertyItems[i];
}
}
return 0;
}
bool wxPropertyList::UpdatePropertyItem(wxPropertyItem *pItem, int row)
{
wxCHECK(row < GetNumberRows(), false);
// reflect the property's state to match the grid row
SetReadOnly(row, 0);
// TODO: Make this a UpdatePropItem where ADVANCED, and new edit values are reflected
SetCellValue(row,0, pItem->GetPropName());
// boolean renderer
if(pItem->GetItemType() == wxPropertyList::CHECKBOX)
{
// translate ON or TRUE (case insensitive to a checkbox)
if(pItem->GetCurValue().IsSameAs(wxT("ON"), false) ||
pItem->GetCurValue().IsSameAs(wxT("TRUE"), false))
SetCellValue(row, 1, wxT("1"));
else
SetCellValue(row, 1, wxT("0"));
}
else
{
// for normal path values, give bold in cell when
// the NOTFOUND is present, for emphasis
wxString str = pItem->GetPropName() + wxT("-NOTFOUND");
if(pItem->GetCurValue().IsSameAs(str))
{
wxFont fnt = GetCellFont(row, 0);
fnt.SetWeight(wxFONTWEIGHT_BOLD);
SetCellFont(row, 1, fnt);
}
else
SetCellFont(row, 1, GetCellFont(row, 0));
SetCellValue(row,1, pItem->GetCurValue());
}
if(pItem->GetCurValue().IsSameAs("IGNORE"))
{
// ignored cell is completely dimmed
wxColour col(192,192,192);
SetCellTextColour(row, 1, col);
}
else
{
// we colour paths blue, filenames green, all else black
wxColour col;
if(pItem->IsDirPath())
col.Set(0,0,255);
else if(pItem->IsFilePath())
col.Set(0,128,0);
else
col = GetCellTextColour(row, 0);
SetCellTextColour(row, 1, col);
}
if(pItem->GetNewValue())
{
// new cell is red
wxColour col(255,100,100);
SetCellBackgroundColour(row, 0, col);
}
else
{
// old cell is grey
wxColour col(192, 192, 192);
SetCellBackgroundColour(row, 0, col);
}
return true;
}
void wxPropertyList::OnSelectCell( wxGridEvent& event )
{
this->SetFocus();
event.Skip();
}
void wxPropertyList::OnCellChange( wxGridEvent& event )
{
int row = event.GetRow();
wxPropertyItem *pItem = GetPropertyItemFromRow(row);
if(pItem && row != wxNOT_FOUND)
{
// write propery back, and set as new
pItem->SetNewValue(true);
// write back bool
if(pItem->GetItemType() == CHECKBOX)
{
if(GetCellValue(row, 1).IsSameAs("1"))
pItem->SetCurValue("ON");
else
pItem->SetCurValue("OFF");
}
else
pItem->SetCurValue(GetCellValue(row, 1));
UpdatePropertyItem(pItem, row);
event.Skip();
}
}
void wxPropertyList::OnCellPopup( wxGridEvent& event )
{
wxPoint pt;
int row = event.GetRow();
//pt = ::wxGetMousePosition();
//ScreenToClient(pt);
//row = YToRow(pt.y);
if(row != wxNOT_FOUND)
{
wxPropertyItem *pItem = GetPropertyItemFromRow(row);
if(pItem)
{
// select the row first if already in selection, don't
// this will clear the previous selection
if(!IsInSelection(row, 0))
SelectRow(row);
// show popup menu
wxMenu *menu = AppResources::CreatePopupMenu();
// enable when it is browsable, and selected one only
wxMenuItem *item = menu->FindItem(ID_CACHE_BROWSE);
if(item)
item->Enable(IsSelectedItemBrowsable());
PopupMenu(menu);
delete menu;
}
}
}
void wxPropertyList::OnIgnoreCache( wxCommandEvent& event )
{
HideControls();
// ignore all selected items
for(size_t i = 0; i < (size_t)GetNumberRows(); i++)
{
if(IsInSelection(i, 0))
{
wxPropertyItem *pItem = GetPropertyItemFromRow(i);
if(pItem)
{
pItem->SetCurValue("IGNORE");
UpdatePropertyItem(pItem, i);
}
}
}
}
void wxPropertyList::OnDeleteCache( wxCommandEvent& event )
{
HideControls();
// convert selections to prop items
wxArrayPtrVoid items;
for(size_t i = 0; i < (size_t)GetNumberRows(); i++)
{
// if selected, query for removal
if(IsInSelection(i, 0))
{
wxPropertyItem *pItem = GetPropertyItemFromRow(i);
if(pItem)
items.Add((void *)pItem);
}
}
// now delete all prop items in cells
for(size_t i = 0; i < items.Count(); i++)
RemoveProperty((wxPropertyItem *)items[i]);
}
void wxPropertyList::OnBrowseItem( wxCommandEvent& event )
{
BrowseSelectedItem();
}
bool wxPropertyList::IsSelectedItemBrowsable(int row)
{
// when there is only one selection, and our current item
// is browsable, make sure it can be selected.
wxPropertyItem *pItem = 0;
size_t count = 0;
for(size_t i = 0; i < (size_t)GetNumberRows() && (count < 2); i++)
{
if(IsInSelection(i, 0))
{
if(!pItem)
pItem = GetPropertyItemFromRow(i);
count ++;
}
}
// if we found nothing, take row (because the event EVT_GRID_CELL_SELECTED
// deselects the cells first before selecting the new one again
if(row != -1 && !pItem)
{
pItem = GetPropertyItemFromRow(row);
count ++; // needed because of next loop
}
// only one item allowed to select
if(pItem && count == 1)
{
if(pItem)
return pItem->IsDirPath() || pItem->IsFilePath();
}
return false;
}
void wxPropertyList::BrowseSelectedItem()
{
HideControls();
for(size_t i = 0; i < (size_t)GetNumberRows(); i++)
{
if(IsInSelection(i, 0))
{
// browse for file or directory
wxPropertyItem *pItem = GetPropertyItemFromRow(i);
if(pItem)
{
wxString title;
wxString str = pItem->GetPropName() + _("-NOTFOUND");
if(pItem->GetCurValue().IsSameAs(str, true))
str.Empty();
else
str = pItem->GetCurValue();
// browse the directory path
if(pItem->IsDirPath())
{
title = _("Select path for ") + pItem->GetPropName();
str = ::wxDirSelector(title, str, 0, wxDefaultPosition, this);
}
else if(pItem->IsFilePath())
{
title = _("Select file for ") + pItem->GetPropName();
str = ::wxFileSelector(title, str, _(""), _(""), _(MC_DEFAULT_WILDCARD), wxFILE_MUST_EXIST, this);
}
else
str.Empty();
if(!str.IsEmpty())
{
pItem->SetCurValue(str.c_str());
UpdatePropertyItem(pItem, i);
}
}
// only allow one item to browse
break;
}
}
}
void wxPropertyList::OnSizeGrid( wxSizeEvent &event )
{
int width, height;
// make sure the grid's cells are equally adjusted
GetClientSize(&width, &height);
SetDefaultColSize(width / 2, true);
wxGrid::AdjustScrollbars();
}
void wxPropertyList::OnKeyPressed( wxKeyEvent &event )
{
event.Skip();
}