1 /////////////////////////////////////////////////////////////////////////////
2 // Author: Vaclav Slavik
5 // Copyright: (c) 2000 Vaclav Slavik
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
10 #pragma implementation "editor.h"
11 #pragma implementation "treedt.h"
14 // For compilers that support precompilation, includes "wx/wx.h".
15 #include "wx/wxprec.h"
22 #include "wx/xml/xml.h"
23 #include "wx/xrc/xmlres.h"
24 #include "wx/splitter.h"
25 #include "wx/config.h"
27 #include "wx/listctrl.h"
28 #include "wx/imaglist.h"
35 #include "propframe.h"
38 void wxXmlRcEditDocument::UpgradeNodeValue(wxXmlNode
*node
)
41 if (n
== NULL
) return;
46 if (n
->GetType() == wxXML_TEXT_NODE
||
47 n
->GetType() == wxXML_CDATA_SECTION_NODE
)
49 wxString str1
= n
->GetContent();
52 for (dt
= str1
.c_str(); *dt
; dt
++)
54 // Remap amp_char to &, map double amp_char to amp_char (for things
55 // like "&File..." -- this is illegal in XML, so we use "_File..."):
59 str1
[size_t(dt
-str1
.c_str()-1)] = '_';
68 void wxXmlRcEditDocument::UpgradeNode(wxXmlNode
*node
)
72 UpgradeNodeValue(node
);
73 UpgradeNode(node
->GetNext());
74 UpgradeNode(node
->GetChildren());
78 void wxXmlRcEditDocument::Upgrade()
82 wxXmlNode
*node
= GetRoot();
83 wxString verstr
= wxT("0.0.0.0");
84 node
->GetPropVal(wxT("version"),verstr
);
85 if (wxSscanf(verstr
.c_str(), wxT("%i.%i.%i.%i"),
86 &v1
, &v2
, &v3
, &v4
) == 4)
87 version
= v1
*256*256*256+v2
*256*256+v3
*256+v4
;
94 node
->DeleteProperty(wxT("version"));
95 node
->AddProperty(wxT("version"), WX_XMLRES_CURRENT_VERSION_STRING
);
99 class EditorTreeCtrl
: public wxTreeCtrl
102 EditorTreeCtrl(wxWindow
*parent
, int id
, EditorFrame
*frame
)
103 : wxTreeCtrl(parent
, id
), m_EdFrame(frame
) {}
106 EditorFrame
*m_EdFrame
;
108 void OnRightClick(wxMouseEvent
&event
)
111 m_EdFrame
->m_TreeCtrl
->HitTest(event
.GetPosition());
114 m_EdFrame
->m_TreeCtrl
->SelectItem(item
);
115 m_EdFrame
->OnRightClickTree(event
.GetPosition());
118 DECLARE_EVENT_TABLE()
121 BEGIN_EVENT_TABLE(EditorTreeCtrl
, wxTreeCtrl
)
122 EVT_RIGHT_DOWN(EditorTreeCtrl::OnRightClick
)
128 ID_PREVIEW
= wxID_HIGHEST
+ 100,
149 ID_NEWNODE
= wxID_HIGHEST
+ 10000, // safely out of XRCID range :)
150 ID_NEWSYBNODE
= ID_NEWNODE
+ 20000
157 BEGIN_EVENT_TABLE(EditorFrame
, wxFrame
)
158 EVT_TREE_SEL_CHANGED(ID_TREE
, EditorFrame::OnTreeSel
)
159 EVT_TOOL_RANGE(ID_PREVIEW
, ID_EXIT
, EditorFrame::OnToolbar
)
160 EVT_MENU_RANGE(ID_NEWDIALOG
, ID_NEWSYBNODE
+ 1000, EditorFrame::OnNewNode
)
161 EVT_MENU_RANGE(ID_CUT
, ID_COPY
, EditorFrame::OnClipboardAction
)
162 EVT_CLOSE(EditorFrame::OnCloseWindow
)
167 #if defined(__UNIX__)
168 #include "bitmaps/preview.xpm"
169 #include "bitmaps/close.xpm"
170 #include "bitmaps/save.xpm"
171 #include "bitmaps/open.xpm"
173 #include "bitmaps/control.xpm"
174 #include "bitmaps/vsizer.xpm"
175 #include "bitmaps/hsizer.xpm"
176 #include "bitmaps/panel.xpm"
177 #include "bitmaps/gsizer.xpm"
178 #include "bitmaps/resicon.xpm"
183 EditorFrame
*EditorFrame::ms_Instance
= NULL
;
185 EditorFrame::EditorFrame(wxFrame
*parent
, const wxString
& filename
)
186 : wxFrame(parent
, wxID_ANY
, filename
+ _("- wxWidgets resources editor"))
193 wxConfigBase
*cfg
= wxConfigBase::Get();
195 SetSize(wxRect(wxPoint(cfg
->Read(_T("editor_x"), wxDefaultCoord
), cfg
->Read(_T("editor_y"), wxDefaultCoord
)),
196 wxSize(cfg
->Read(_T("editor_w"), 400), cfg
->Read(_T("editor_h"), 400))));
198 m_SelectedNode
= NULL
;
200 m_FileName
= wxEmptyString
;
202 wxMenu
*menuFile
= new wxMenu
;
203 menuFile
->Append(ID_NEW
, _T("&New"));
204 menuFile
->Append(ID_OPEN
, _T("&Open\tCtrl-O"));
205 menuFile
->Append(ID_SAVE
, _T("&Save\tCtrl-S"));
206 menuFile
->Append(ID_SAVEAS
, _T("Save &as..."));
207 menuFile
->AppendSeparator();
208 menuFile
->Append(ID_EXIT
, _T("E&xit\tAlt-X"));
210 wxMenu
*menuEdit
= new wxMenu
;
211 menuEdit
->Append(ID_CUT
, _T("Cut\tCtrl-X"));
212 menuEdit
->Append(ID_COPY
, _T("Copy\tCtrl-C"));
213 menuEdit
->Append(ID_PASTE_SYBLING
, _T("Paste as sybling\tCtrl-V"));
214 menuEdit
->Append(ID_PASTE_CHILD
, _T("Paste as child"));
215 menuEdit
->AppendSeparator();
216 menuEdit
->Append(ID_DELETE_NODE
, _T("Delete"));
218 menuEdit
->Enable(ID_PASTE_SYBLING
, false);
219 menuEdit
->Enable(ID_PASTE_CHILD
, false);
221 wxMenuBar
*menuBar
= new wxMenuBar();
222 menuBar
->Append(menuFile
, _T("&File"));
223 menuBar
->Append(menuEdit
, _T("&Edit"));
227 wxToolBar
*toolBar
= CreateToolBar(wxNO_BORDER
| wxTB_HORIZONTAL
| wxTB_FLAT
);
228 toolBar
->SetMargins(2, 2);
229 toolBar
->SetToolBitmapSize(wxSize(24, 24));
230 toolBar
-> AddTool(ID_EXIT
, wxBITMAP(close
), wxNullBitmap
,
231 false, wxDefaultCoord
, wxDefaultCoord
, (wxObject
*) NULL
,
232 _("Quit the editor"));
233 toolBar
-> AddTool(ID_OPEN
, wxBITMAP(open
), wxNullBitmap
,
234 false, wxDefaultCoord
, wxDefaultCoord
, (wxObject
*) NULL
,
235 _("Open XML resource file"));
236 toolBar
-> AddTool(ID_SAVE
, wxBITMAP(save
), wxNullBitmap
,
237 false, wxDefaultCoord
, wxDefaultCoord
, (wxObject
*) NULL
,
239 toolBar
-> AddTool(ID_PREVIEW
, wxBITMAP(preview
), wxNullBitmap
,
240 false, wxDefaultCoord
, wxDefaultCoord
, (wxObject
*) NULL
,
242 toolBar
-> Realize();
244 wxSizer
*sizer
= new wxBoxSizer(wxHORIZONTAL
);
246 // Create tree control:
247 m_TreeCtrl
= new EditorTreeCtrl(this, ID_TREE
, this);
248 wxImageList
*imgList
= new wxImageList(16, 16);
249 imgList
->Add(wxICON(control
));
250 imgList
->Add(wxICON(panel
));
251 imgList
->Add(wxICON(vsizer
));
252 imgList
->Add(wxICON(hsizer
));
253 imgList
->Add(wxICON(gsizer
));
254 imgList
->Add(wxICON(resicon
));
255 m_TreeCtrl
->AssignImageList(imgList
);
256 sizer
->Add(m_TreeCtrl
, 1, wxEXPAND
);
269 EditorFrame::~EditorFrame()
271 PreviewFrame::Get()->Close();
272 PropertiesFrame::Get()->Close();
274 wxConfigBase
*cfg
= wxConfigBase::Get();
276 cfg
->Write(_T("editor_x"), (long)GetPosition().x
);
277 cfg
->Write(_T("editor_y"), (long)GetPosition().y
);
278 cfg
->Write(_T("editor_w"), (long)GetSize().x
);
279 cfg
->Write(_T("editor_h"), (long)GetSize().y
);
287 void EditorFrame::LoadFile(const wxString
& filename
)
289 if (!AskToSave()) return;
293 // create new resource in order to handle version differences properly
294 PreviewFrame::Get()->ResetResource();
296 m_FileName
= wxEmptyString
;
297 m_Resource
= new wxXmlRcEditDocument
;
300 if (!m_Resource
->Load(filename
, wxLocale::GetSystemEncodingName()))
305 wxLogError(_T("Error parsing ") + filename
);
309 m_FileName
= filename
;
311 // Upgrades old versions
312 m_Resource
->Upgrade();
320 void EditorFrame::SaveFile(const wxString
& filename
)
322 m_FileName
= filename
;
325 if (!m_Resource
->Save(filename
))
326 wxLogError(_("Error saving ") + filename
);
335 void EditorFrame::NewFile()
337 if (!AskToSave()) return;
341 m_FileName
= wxEmptyString
;
342 m_Resource
= new wxXmlRcEditDocument
;
343 m_Resource
->SetRoot(new wxXmlNode(wxXML_ELEMENT_NODE
, _("resource")));
345 m_Resource
->SetFileEncoding(_T("utf-8"));
347 m_Resource
->SetEncoding(wxLocale::GetSystemEncodingName());
350 m_Resource
->GetRoot()->AddProperty(_T("version"),
351 WX_XMLRES_CURRENT_VERSION_STRING
);
360 void EditorFrame::RefreshTitle()
363 if (m_Modified
) s
<< _T("* ");
365 if (m_FileName
!= wxEmptyString
)
366 s
<< _T(" - ") << wxFileNameFromPath(m_FileName
);
372 void EditorFrame::RefreshTree()
374 wxXmlNode
*sel
= m_SelectedNode
;
376 m_TreeCtrl
->DeleteAllItems();
378 wxTreeItemId root
= m_TreeCtrl
->AddRoot(_T("Resource: ") + wxFileNameFromPath(m_FileName
), 5, 5);
380 wxXmlNode
*n
= m_Resource
->GetRoot()->GetChildren();
383 if (n
->GetType() == wxXML_ELEMENT_NODE
)
384 CreateTreeNode(m_TreeCtrl
, root
, n
);
388 m_TreeCtrl
->Expand(root
);
395 static void RecursivelyExpand(wxTreeCtrl
*t
, wxTreeItemId item
)
399 wxTreeItemId id
= t
->GetFirstChild(item
, cookie
);
402 RecursivelyExpand(t
, id
);
403 id
= t
->GetNextChild(item
, cookie
);
407 bool EditorFrame::SelectNode(wxXmlNode
*node
, wxTreeItemId
*root
)
411 wxTreeItemId rootitem
= m_TreeCtrl
->GetRootItem();
412 return SelectNode(node
, &rootitem
);
420 item
= m_TreeCtrl
->GetFirstChild(*root
, cookie
);
423 dt
= (XmlTreeData
*)(m_TreeCtrl
->GetItemData(item
));
424 nd
= (dt
) ? dt
->Node
: NULL
;
427 RecursivelyExpand(m_TreeCtrl
, *root
);
428 m_TreeCtrl
->SelectItem(item
);
429 m_TreeCtrl
->EnsureVisible(item
);
432 if (m_TreeCtrl
->ItemHasChildren(item
) && SelectNode(node
, &item
))
434 item
= m_TreeCtrl
->GetNextChild(*root
, cookie
);
442 wxTreeItemId
EditorFrame::CreateTreeNode(wxTreeCtrl
*treectrl
, wxTreeItemId parent
, wxXmlNode
*node
)
446 wxTreeItemId invalid
;
450 return NodeHandler::Find(node
)->CreateTreeNode(treectrl
, parent
, node
);
455 void EditorFrame::NotifyChanged(int change_type
)
457 if (change_type
& CHANGED_TREE
)
460 if (change_type
& CHANGED_TREE_SELECTED
)
462 wxTreeItemId sel
= m_TreeCtrl
->GetSelection();
463 m_TreeCtrl
->SetItemText(sel
,
464 NodeHandler::Find(m_SelectedNode
)->GetTreeString(m_SelectedNode
));
467 if (change_type
& CHANGED_TREE_SELECTED_ICON
)
469 wxTreeItemId sel
= m_TreeCtrl
->GetSelection();
470 int icon
= NodeHandler::Find(m_SelectedNode
)->GetTreeIcon(m_SelectedNode
);
471 m_TreeCtrl
->SetItemImage(sel
, icon
);
480 PreviewFrame::Get()->MakeDirty();
485 void EditorFrame::OnTreeSel(wxTreeEvent
& event
)
487 XmlTreeData
*dt
= (XmlTreeData
*)(m_TreeCtrl
->GetItemData(event
.GetItem()));
488 wxXmlNode
*node
= (dt
) ? dt
->Node
: NULL
;
490 m_SelectedNode
= node
;
492 PropertiesFrame::Get()->ShowProps(node
);
494 if (m_TreeCtrl
->GetItemParent(event
.GetItem()) == m_TreeCtrl
->GetRootItem())
496 wxTreeItemId it
= event
.GetOldItem();
498 if (it
.IsOk() && m_TreeCtrl
->GetRootItem() != it
)
500 while (m_TreeCtrl
->GetItemParent(it
) != m_TreeCtrl
->GetRootItem())
501 it
= m_TreeCtrl
->GetItemParent(it
);
502 m_TreeCtrl
->Collapse(it
);
504 RecursivelyExpand(m_TreeCtrl
, event
.GetItem());
506 PreviewFrame::Get()->Preview(node
,m_Resource
);
512 void EditorFrame::OnToolbar(wxCommandEvent
& event
)
514 switch (event
.GetId())
518 XmlTreeData
* dt
= (XmlTreeData
*)m_TreeCtrl
->GetItemData(m_TreeCtrl
->GetSelection());;
519 if (dt
!= NULL
&& dt
->Node
!= NULL
)
520 PreviewFrame::Get()->Preview(dt
->Node
, m_Resource
);
534 wxString cwd
= wxGetCwd(); // workaround for 2.2
535 wxString name
= wxFileSelector(_("Open XML resource"), wxEmptyString
, wxEmptyString
, wxEmptyString
, _("XML resources (*.xrc)|*.xrc"), wxOPEN
| wxFILE_MUST_EXIST
);
536 wxSetWorkingDirectory(cwd
);
543 if (m_FileName
!= wxEmptyString
) { SaveFile(m_FileName
); break;}
548 wxString cwd
= wxGetCwd(); // workaround for 2.2
549 wxString name
= wxFileSelector(_("Save as"), wxEmptyString
, m_FileName
, wxEmptyString
, _("XML resources (*.xrc)|*.xrc"), wxSAVE
| wxOVERWRITE_PROMPT
);
550 wxSetWorkingDirectory(cwd
);
552 SaveFile((m_FileName
= name
));
556 case ID_DELETE_NODE
:
558 DeleteSelectedNode();
566 void EditorFrame::DeleteSelectedNode()
568 XmlTreeData
*dt
= (XmlTreeData
*)
569 (m_TreeCtrl
->GetItemData(m_TreeCtrl
->GetItemParent(m_TreeCtrl
->GetSelection())));
570 wxXmlNode
*n
= (dt
) ? dt
->Node
: NULL
;
572 m_SelectedNode
->GetParent()->RemoveChild(m_SelectedNode
);
573 NotifyChanged(CHANGED_TREE
);
579 void EditorFrame::OnNewNode(wxCommandEvent
& event
)
581 if (event
.GetId() >= ID_NEWSYBNODE
)
584 (XmlTreeData
*)(m_TreeCtrl
->GetItemData(
585 m_TreeCtrl
->GetItemParent(m_TreeCtrl
->GetSelection())));
587 if (pardt
&& pardt
->Node
&& pardt
->Node
!= m_Resource
->GetRoot())
589 wxXmlNode
*nd
= pardt
->Node
;
591 wxXmlNode
*realnode
= NodeHandler::Find(nd
)->GetRealNode(nd
);
592 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
593 wxString name
= hnd
->GetChildTypes()[event
.GetId()-ID_NEWSYBNODE
];
595 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, _T("object"));
596 node
->AddProperty(_T("class"), name
);
598 hnd
->InsertNode(realnode
, node
, m_SelectedNode
);
599 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
600 SelectNode(node
, &root
);
605 else if (event
.GetId() >= ID_NEWNODE
)
607 wxXmlNode
*realnode
= NodeHandler::Find(m_SelectedNode
)->GetRealNode(m_SelectedNode
);
608 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
609 wxString name
= hnd
->GetChildTypes()[event
.GetId()-ID_NEWNODE
];
611 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, _T("object"));
612 node
->AddProperty(_T("class"), name
);
614 hnd
->InsertNode(realnode
, node
);
615 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
616 SelectNode(node
, &root
);
622 switch (event
.GetId())
624 case ID_NEWDIALOG
: name
= _T("wxDialog"); break;
625 case ID_NEWFRAME
: name
= _T("wxFrame"); break;
626 case ID_NEWPANEL
: name
= _T("wxPanel"); break;
627 case ID_NEWMENU
: name
= _T("wxMenu"); break;
628 case ID_NEWMENUBAR
: name
= _T("wxMenuBar"); break;
629 case ID_NEWTOOLBAR
: name
= _T("wxToolBar"); break;
630 default : return; // never occurs
633 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, _T("object"));
634 node
->AddProperty(_T("class"), name
);
635 m_Resource
->GetRoot()->AddChild(node
);
636 NotifyChanged(CHANGED_TREE
);
643 void EditorFrame::OnRightClickTree(wxPoint pos
)
645 wxMenu
*popup
= new wxMenu
;
647 if (m_SelectedNode
== NULL
|| m_SelectedNode
== m_Resource
->GetRoot())
649 popup
->Append(ID_NEWDIALOG
, _("New wxDialog"));
650 popup
->Append(ID_NEWFRAME
, _("New wxFrame"));
651 popup
->Append(ID_NEWPANEL
, _("New wxPanel"));
652 popup
->Append(ID_NEWMENU
, _("New wxMenu"));
653 popup
->Append(ID_NEWMENUBAR
, _("New wxMenuBar"));
654 popup
->Append(ID_NEWTOOLBAR
, _("New wxToolBar"));
662 NodeHandler::Find(NodeHandler::Find(m_SelectedNode
)->GetRealNode(m_SelectedNode
))->
665 has_children
= !arr
.IsEmpty();
668 wxMenu
*news
= new wxMenu
;
669 wxMenu
*news2
= news
;
670 for (size_t i
= 0; i
< arr
.GetCount(); i
++)
672 news2
->Append(i
+ ID_NEWNODE
, arr
[i
]);
673 #ifdef __WXGTK__ // doesn't support Break
676 wxMenu
*m
= new wxMenu
;
677 news2
->Append(ID_NEWNODE
+arr
.GetCount(), _("More..."), m
);
681 if (i
% 16 == 15) news2
->Break();
684 popup
->Append(ID_NEWNODE
-1, _("New child"), news
);
690 (XmlTreeData
*)(m_TreeCtrl
->GetItemData(
691 m_TreeCtrl
->GetItemParent(m_TreeCtrl
->GetSelection())));
692 if (pardt
&& pardt
->Node
&& pardt
->Node
!= m_Resource
->GetRoot())
694 wxXmlNode
*nd
= pardt
->Node
;
696 NodeHandler::Find(NodeHandler::Find(nd
)->GetRealNode(nd
))->
701 wxMenu
*news
= new wxMenu
;
702 wxMenu
*news2
= news
;
703 for (size_t i
= 0; i
< arr
.GetCount(); i
++)
705 news2
->Append(i
+ ID_NEWSYBNODE
, arr
[i
]);
706 #ifdef __WXGTK__ // doesn't support Break
709 wxMenu
*m
= new wxMenu
;
710 news2
->Append(ID_NEWSYBNODE
+arr
.GetCount(), _("More..."), m
);
714 if (i
% 16 == 15) news2
->Break();
717 popup
->Append(ID_NEWSYBNODE
-1, _("New sybling"), news
);
722 popup
->AppendSeparator();
723 popup
->Append(ID_CUT
, _("Cut"));
724 popup
->Append(ID_COPY
, _("Copy"));
725 popup
->Append(ID_PASTE_SYBLING
, _("Paste as sybling"));
726 popup
->Append(ID_PASTE_CHILD
, _("Paste as child"));
727 popup
->AppendSeparator();
728 popup
->Append(ID_DELETE_NODE
, _("Delete"));
729 popup
->Enable(ID_PASTE_SYBLING
, m_Clipboard
!= NULL
);
730 popup
->Enable(ID_PASTE_CHILD
, has_children
&& m_Clipboard
!= NULL
);
733 m_TreeCtrl
->PopupMenu(popup
, pos
);
739 void EditorFrame::OnClipboardAction(wxCommandEvent
& event
)
741 switch (event
.GetId())
746 m_Clipboard
= new wxXmlNode(*m_SelectedNode
);
747 GetMenuBar()->Enable(ID_PASTE_SYBLING
, true);
748 GetMenuBar()->Enable(ID_PASTE_CHILD
, true);
749 if (event
.GetId() == ID_CUT
) DeleteSelectedNode();
752 case ID_PASTE_SYBLING
:
755 (XmlTreeData
*)(m_TreeCtrl
->GetItemData(
756 m_TreeCtrl
->GetItemParent(m_TreeCtrl
->GetSelection())));
758 if (pardt
&& pardt
->Node
&& pardt
->Node
!= m_Resource
->GetRoot())
760 wxXmlNode
*nd
= pardt
->Node
;
762 wxXmlNode
*realnode
= NodeHandler::Find(nd
)->GetRealNode(nd
);
763 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
764 wxXmlNode
*node
= new wxXmlNode(*m_Clipboard
);
765 hnd
->InsertNode(realnode
, node
, m_SelectedNode
);
766 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
767 SelectNode(node
, &root
);
773 wxXmlNode
*realnode
= NodeHandler::Find(m_SelectedNode
)->GetRealNode(m_SelectedNode
);
774 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
775 wxXmlNode
*node
= new wxXmlNode(*m_Clipboard
);
776 hnd
->InsertNode(realnode
, node
);
777 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
778 SelectNode(node
, &root
);
786 bool EditorFrame::AskToSave()
787 // asks the user to save current document (if modified)
788 // returns false if user cancelled the action, true of he choosed
791 if (!m_Modified
) return true;
793 int res
= wxMessageBox(_("File modified. Do you want to save changes?"), _("Save changes"),
794 wxYES_NO
| wxCANCEL
| wxCENTRE
| wxICON_QUESTION
);
796 SaveFile(m_FileName
);
797 return (res
!= wxCANCEL
);
802 void EditorFrame::OnCloseWindow(wxCloseEvent
&)
804 if (!AskToSave()) return;