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/xml/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"
39 class EditorTreeCtrl
: public wxTreeCtrl
42 EditorTreeCtrl(wxWindow
*parent
, int id
, EditorFrame
*frame
)
43 : wxTreeCtrl(parent
, id
), m_EdFrame(frame
) {}
46 EditorFrame
*m_EdFrame
;
48 void OnRightClick(wxMouseEvent
&event
)
51 m_EdFrame
->m_TreeCtrl
->HitTest(event
.GetPosition());
54 m_EdFrame
->m_TreeCtrl
->SelectItem(item
);
55 m_EdFrame
->OnRightClickTree(event
.GetPosition());
61 BEGIN_EVENT_TABLE(EditorTreeCtrl
, wxTreeCtrl
)
62 EVT_RIGHT_DOWN(EditorTreeCtrl::OnRightClick
)
68 ID_PREVIEW
= wxID_HIGHEST
+ 100,
88 ID_NEWNODE
= wxID_HIGHEST
+ 10000, // safely out of XMLID range :)
89 ID_NEWSYBNODE
= ID_NEWNODE
+ 20000
96 BEGIN_EVENT_TABLE(EditorFrame
, wxFrame
)
97 EVT_TREE_SEL_CHANGED(ID_TREE
, EditorFrame::OnTreeSel
)
98 EVT_TOOL_RANGE(ID_PREVIEW
, ID_EXIT
, EditorFrame::OnToolbar
)
99 EVT_MENU_RANGE(ID_NEWDIALOG
, ID_NEWSYBNODE
+ 1000, EditorFrame::OnNewNode
)
100 EVT_MENU_RANGE(ID_CUT
, ID_COPY
, EditorFrame::OnClipboardAction
)
101 EVT_CLOSE(EditorFrame::OnCloseWindow
)
106 #if defined(__UNIX__)
107 #include "bitmaps/preview.xpm"
108 #include "bitmaps/close.xpm"
109 #include "bitmaps/save.xpm"
110 #include "bitmaps/open.xpm"
112 #include "bitmaps/control.xpm"
113 #include "bitmaps/vsizer.xpm"
114 #include "bitmaps/hsizer.xpm"
115 #include "bitmaps/panel.xpm"
116 #include "bitmaps/gsizer.xpm"
117 #include "bitmaps/resicon.xpm"
122 EditorFrame
*EditorFrame::ms_Instance
= NULL
;
124 EditorFrame::EditorFrame(wxFrame
*parent
, const wxString
& filename
)
125 : wxFrame(parent
, -1, filename
+ _("- wxWindows resources editor"))
132 wxConfigBase
*cfg
= wxConfigBase::Get();
134 SetSize(wxRect(wxPoint(cfg
->Read("editor_x", -1), cfg
->Read("editor_y", -1)),
135 wxSize(cfg
->Read("editor_w", 400), cfg
->Read("editor_h", 400))));
137 m_SelectedNode
= NULL
;
139 m_FileName
= wxEmptyString
;
141 wxMenu
*menuFile
= new wxMenu
;
142 menuFile
->Append(ID_NEW
, "&New");
143 menuFile
->Append(ID_OPEN
, "&Open\tCtrl-O");
144 menuFile
->Append(ID_SAVE
, "&Save\tCtrl-S");
145 menuFile
->Append(ID_SAVEAS
, "Save &as...");
146 menuFile
->AppendSeparator();
147 menuFile
->Append(ID_EXIT
, "E&xit\tAlt-X");
149 wxMenu
*menuEdit
= new wxMenu
;
150 menuEdit
->Append(ID_CUT
, "Cut\tCtrl-X");
151 menuEdit
->Append(ID_COPY
, "Copy\tCtrl-C");
152 menuEdit
->Append(ID_PASTE_SYBLING
, "Paste as sybling\tCtrl-V");
153 menuEdit
->Append(ID_PASTE_CHILD
, "Paste as child");
154 menuEdit
->AppendSeparator();
155 menuEdit
->Append(ID_DELETE_NODE
, "Delete");
157 menuEdit
->Enable(ID_PASTE_SYBLING
, FALSE
);
158 menuEdit
->Enable(ID_PASTE_CHILD
, FALSE
);
160 wxMenuBar
*menuBar
= new wxMenuBar();
161 menuBar
->Append(menuFile
, "&File");
162 menuBar
->Append(menuEdit
, "&Edit");
166 wxToolBar
*toolBar
= CreateToolBar(wxNO_BORDER
| wxTB_HORIZONTAL
| wxTB_FLAT
);
167 toolBar
->SetMargins(2, 2);
168 toolBar
->SetToolBitmapSize(wxSize(24, 24));
169 toolBar
-> AddTool(ID_EXIT
, wxBITMAP(close
), wxNullBitmap
,
170 FALSE
, -1, -1, (wxObject
*) NULL
,
171 _("Quit the editor"));
172 toolBar
-> AddTool(ID_OPEN
, wxBITMAP(open
), wxNullBitmap
,
173 FALSE
, -1, -1, (wxObject
*) NULL
,
174 _("Open XML resource file"));
175 toolBar
-> AddTool(ID_SAVE
, wxBITMAP(save
), wxNullBitmap
,
176 FALSE
, -1, -1, (wxObject
*) NULL
,
178 toolBar
-> AddTool(ID_PREVIEW
, wxBITMAP(preview
), wxNullBitmap
,
179 FALSE
, -1, -1, (wxObject
*) NULL
,
181 toolBar
-> Realize();
183 wxSizer
*sizer
= new wxBoxSizer(wxHORIZONTAL
);
185 // Create tree control:
186 m_TreeCtrl
= new EditorTreeCtrl(this, ID_TREE
, this);
187 wxImageList
*imgList
= new wxImageList(16, 16);
188 imgList
->Add(wxICON(control
));
189 imgList
->Add(wxICON(panel
));
190 imgList
->Add(wxICON(vsizer
));
191 imgList
->Add(wxICON(hsizer
));
192 imgList
->Add(wxICON(gsizer
));
193 imgList
->Add(wxICON(resicon
));
194 m_TreeCtrl
->AssignImageList(imgList
);
195 sizer
->Add(m_TreeCtrl
, 1, wxEXPAND
);
209 EditorFrame::~EditorFrame()
211 PreviewFrame::Get()->Close();
212 PropertiesFrame::Get()->Close();
214 wxConfigBase
*cfg
= wxConfigBase::Get();
216 cfg
->Write(_T("editor_x"), (long)GetPosition().x
);
217 cfg
->Write(_T("editor_y"), (long)GetPosition().y
);
218 cfg
->Write(_T("editor_w"), (long)GetSize().x
);
219 cfg
->Write(_T("editor_h"), (long)GetSize().y
);
227 void EditorFrame::LoadFile(const wxString
& filename
)
229 if (!AskToSave()) return;
234 m_Resource
= new wxXmlDocument
;
237 if (!m_Resource
->Load(filename
))
242 wxLogError("Error parsing " + filename
);
246 m_FileName
= filename
;
254 void EditorFrame::SaveFile(const wxString
& filename
)
256 m_FileName
= filename
;
259 wxXmlNode
*root
= m_Resource
->GetRoot();
260 root
->DeleteProperty(wxT("version"));
261 root
->AddProperty(wxT("version"), wxT(WX_XMLRES_CURRENT_VERSION_STRING
));
264 if (!m_Resource
->Save(filename
))
265 wxLogError(_("Error saving ") + filename
);
274 void EditorFrame::NewFile()
276 if (!AskToSave()) return;
281 m_Resource
= new wxXmlDocument
;
282 m_Resource
->SetRoot(new wxXmlNode(wxXML_ELEMENT_NODE
, _("resource")));
291 void EditorFrame::RefreshTitle()
294 if (m_Modified
) s
<< _T("* ");
297 s
<< _T(" - ") << wxFileNameFromPath(m_FileName
);
303 void EditorFrame::RefreshTree()
305 wxXmlNode
*sel
= m_SelectedNode
;
307 m_TreeCtrl
->DeleteAllItems();
308 wxTreeItemId root
= m_TreeCtrl
->AddRoot("Resource: " + wxFileNameFromPath(m_FileName
), 5, 5);
310 wxXmlNode
*n
= m_Resource
->GetRoot()->GetChildren();
313 if (n
->GetType() == wxXML_ELEMENT_NODE
)
314 CreateTreeNode(m_TreeCtrl
, root
, n
);
318 m_TreeCtrl
->Expand(root
);
325 static void RecursivelyExpand(wxTreeCtrl
*t
, wxTreeItemId item
)
329 wxTreeItemId id
= t
->GetFirstChild(item
, cookie
);
332 RecursivelyExpand(t
, id
);
333 id
= t
->GetNextChild(item
, cookie
);
337 bool EditorFrame::SelectNode(wxXmlNode
*node
, wxTreeItemId
*root
)
341 wxTreeItemId rootitem
= m_TreeCtrl
->GetRootItem();
342 return SelectNode(node
, &rootitem
);
350 item
= m_TreeCtrl
->GetFirstChild(*root
, cookie
);
353 dt
= (XmlTreeData
*)(m_TreeCtrl
->GetItemData(item
));
354 nd
= (dt
) ? dt
->Node
: NULL
;
357 RecursivelyExpand(m_TreeCtrl
, *root
);
358 m_TreeCtrl
->SelectItem(item
);
359 m_TreeCtrl
->EnsureVisible(item
);
362 if (m_TreeCtrl
->ItemHasChildren(item
) && SelectNode(node
, &item
))
364 item
= m_TreeCtrl
->GetNextChild(*root
, cookie
);
372 wxTreeItemId
EditorFrame::CreateTreeNode(wxTreeCtrl
*treectrl
, wxTreeItemId parent
, wxXmlNode
*node
)
376 wxTreeItemId invalid
;
380 return NodeHandler::Find(node
)->CreateTreeNode(treectrl
, parent
, node
);
385 void EditorFrame::NotifyChanged(int change_type
)
387 if (change_type
& CHANGED_TREE
)
390 if (change_type
& CHANGED_TREE_SELECTED
)
392 wxTreeItemId sel
= m_TreeCtrl
->GetSelection();
393 m_TreeCtrl
->SetItemText(sel
,
394 NodeHandler::Find(m_SelectedNode
)->GetTreeString(m_SelectedNode
));
397 if (change_type
& CHANGED_TREE_SELECTED_ICON
)
399 wxTreeItemId sel
= m_TreeCtrl
->GetSelection();
400 int icon
= NodeHandler::Find(m_SelectedNode
)->GetTreeIcon(m_SelectedNode
);
401 m_TreeCtrl
->SetItemImage(sel
, icon
);
410 PreviewFrame::Get()->MakeDirty();
415 void EditorFrame::OnTreeSel(wxTreeEvent
& event
)
417 XmlTreeData
*dt
= (XmlTreeData
*)(m_TreeCtrl
->GetItemData(event
.GetItem()));
418 wxXmlNode
*node
= (dt
) ? dt
->Node
: NULL
;
420 m_SelectedNode
= node
;
422 PropertiesFrame::Get()->ShowProps(node
);
424 if (m_TreeCtrl
->GetParent(event
.GetItem()) == m_TreeCtrl
->GetRootItem())
426 wxTreeItemId it
= event
.GetOldItem();
428 if (it
.IsOk() && m_TreeCtrl
->GetRootItem() != it
)
430 while (m_TreeCtrl
->GetParent(it
) != m_TreeCtrl
->GetRootItem())
431 it
= m_TreeCtrl
->GetParent(it
);
432 m_TreeCtrl
->Collapse(it
);
434 RecursivelyExpand(m_TreeCtrl
, event
.GetItem());
436 PreviewFrame::Get()->Preview(node
);
442 void EditorFrame::OnToolbar(wxCommandEvent
& event
)
444 switch (event
.GetId())
448 XmlTreeData
* dt
= (XmlTreeData
*)m_TreeCtrl
->GetItemData(m_TreeCtrl
->GetSelection());;
449 if (dt
!= NULL
&& dt
->Node
!= NULL
)
450 PreviewFrame::Get()->Preview(dt
->Node
);
464 wxString cwd
= wxGetCwd(); // workaround for 2.2
465 wxString name
= wxFileSelector(_("Open XML resource"), _T(""), _T(""), _T(""), _("XML resources (*.xrc)|*.xrc"), wxOPEN
| wxFILE_MUST_EXIST
);
466 wxSetWorkingDirectory(cwd
);
473 if (m_FileName
!= "") { SaveFile(m_FileName
); break;}
478 wxString cwd
= wxGetCwd(); // workaround for 2.2
479 wxString name
= wxFileSelector(_("Save as"), _T(""), m_FileName
, _T(""), _("XML resources (*.xrc)|*.xrc"), wxSAVE
| wxOVERWRITE_PROMPT
);
480 wxSetWorkingDirectory(cwd
);
482 SaveFile((m_FileName
= name
));
486 case ID_DELETE_NODE
:
488 DeleteSelectedNode();
496 void EditorFrame::DeleteSelectedNode()
498 XmlTreeData
*dt
= (XmlTreeData
*)
499 (m_TreeCtrl
->GetItemData(m_TreeCtrl
->GetParent(m_TreeCtrl
->GetSelection())));
500 wxXmlNode
*n
= (dt
) ? dt
->Node
: NULL
;
502 m_SelectedNode
->GetParent()->RemoveChild(m_SelectedNode
);
503 NotifyChanged(CHANGED_TREE
);
509 void EditorFrame::OnNewNode(wxCommandEvent
& event
)
511 if (event
.GetId() >= ID_NEWSYBNODE
)
514 (XmlTreeData
*)(m_TreeCtrl
->GetItemData(
515 m_TreeCtrl
->GetParent(m_TreeCtrl
->GetSelection())));
517 if (pardt
&& pardt
->Node
&& pardt
->Node
!= m_Resource
->GetRoot())
519 wxXmlNode
*nd
= pardt
->Node
;
521 wxXmlNode
*realnode
= NodeHandler::Find(nd
)->GetRealNode(nd
);
522 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
523 wxString name
= hnd
->GetChildTypes()[event
.GetId()-ID_NEWSYBNODE
];
525 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, _T("object"));
526 node
->AddProperty(_T("class"), name
);
528 hnd
->InsertNode(realnode
, node
, m_SelectedNode
);
529 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
530 SelectNode(node
, &root
);
535 else if (event
.GetId() >= ID_NEWNODE
)
537 wxXmlNode
*realnode
= NodeHandler::Find(m_SelectedNode
)->GetRealNode(m_SelectedNode
);
538 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
539 wxString name
= hnd
->GetChildTypes()[event
.GetId()-ID_NEWNODE
];
541 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, _T("object"));
542 node
->AddProperty(_T("class"), name
);
544 hnd
->InsertNode(realnode
, node
);
545 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
546 SelectNode(node
, &root
);
552 switch (event
.GetId())
554 case ID_NEWDIALOG
: name
= _T("wxDialog"); break;
555 case ID_NEWPANEL
: name
= _T("wxPanel"); break;
556 case ID_NEWMENU
: name
= _T("wxMenu"); break;
557 case ID_NEWMENUBAR
: name
= _T("wxMenuBar"); break;
558 case ID_NEWTOOLBAR
: name
= _T("wxToolBar"); break;
559 default : return; // never occurs
562 wxXmlNode
*node
= new wxXmlNode(wxXML_ELEMENT_NODE
, _T("object"));
563 node
->AddProperty(_T("class"), name
);
564 m_Resource
->GetRoot()->AddChild(node
);
565 NotifyChanged(CHANGED_TREE
);
572 void EditorFrame::OnRightClickTree(wxPoint pos
)
574 wxMenu
*popup
= new wxMenu
;
576 if (m_SelectedNode
== NULL
|| m_SelectedNode
== m_Resource
->GetRoot())
578 popup
->Append(ID_NEWDIALOG
, _("New wxDialog"));
579 popup
->Append(ID_NEWPANEL
, _("New wxPanel"));
580 popup
->Append(ID_NEWMENU
, _("New wxMenu"));
581 popup
->Append(ID_NEWMENUBAR
, _("New wxMenuBar"));
582 popup
->Append(ID_NEWTOOLBAR
, _("New wxToolBar"));
590 NodeHandler::Find(NodeHandler::Find(m_SelectedNode
)->GetRealNode(m_SelectedNode
))->
593 has_children
= !arr
.IsEmpty();
596 wxMenu
*news
= new wxMenu
;
597 wxMenu
*news2
= news
;
598 for (size_t i
= 0; i
< arr
.GetCount(); i
++)
600 news2
->Append(i
+ ID_NEWNODE
, arr
[i
]);
601 #ifdef __WXGTK__ // doesn't support Break
604 wxMenu
*m
= new wxMenu
;
605 news2
->Append(ID_NEWNODE
+arr
.GetCount(), _("More..."), m
);
609 if (i
% 16 == 15) news2
->Break();
612 popup
->Append(ID_NEWNODE
-1, _("New child"), news
);
618 (XmlTreeData
*)(m_TreeCtrl
->GetItemData(
619 m_TreeCtrl
->GetParent(m_TreeCtrl
->GetSelection())));
620 if (pardt
&& pardt
->Node
&& pardt
->Node
!= m_Resource
->GetRoot())
622 wxXmlNode
*nd
= pardt
->Node
;
624 NodeHandler::Find(NodeHandler::Find(nd
)->GetRealNode(nd
))->
629 wxMenu
*news
= new wxMenu
;
630 wxMenu
*news2
= news
;
631 for (size_t i
= 0; i
< arr
.GetCount(); i
++)
633 news2
->Append(i
+ ID_NEWSYBNODE
, arr
[i
]);
634 #ifdef __WXGTK__ // doesn't support Break
637 wxMenu
*m
= new wxMenu
;
638 news2
->Append(ID_NEWSYBNODE
+arr
.GetCount(), _("More..."), m
);
642 if (i
% 16 == 15) news2
->Break();
645 popup
->Append(ID_NEWSYBNODE
-1, _("New sybling"), news
);
650 popup
->AppendSeparator();
651 popup
->Append(ID_CUT
, _("Cut"));
652 popup
->Append(ID_COPY
, _("Copy"));
653 popup
->Append(ID_PASTE_SYBLING
, _("Paste as sybling"));
654 popup
->Append(ID_PASTE_CHILD
, _("Paste as child"));
655 popup
->AppendSeparator();
656 popup
->Append(ID_DELETE_NODE
, _("Delete"));
657 popup
->Enable(ID_PASTE_SYBLING
, m_Clipboard
!= NULL
);
658 popup
->Enable(ID_PASTE_CHILD
, has_children
&& m_Clipboard
!= NULL
);
661 m_TreeCtrl
->PopupMenu(popup
, pos
);
667 void EditorFrame::OnClipboardAction(wxCommandEvent
& event
)
669 switch (event
.GetId())
674 m_Clipboard
= new wxXmlNode(*m_SelectedNode
);
675 GetMenuBar()->Enable(ID_PASTE_SYBLING
, TRUE
);
676 GetMenuBar()->Enable(ID_PASTE_CHILD
, TRUE
);
677 if (event
.GetId() == ID_CUT
) DeleteSelectedNode();
680 case ID_PASTE_SYBLING
:
683 (XmlTreeData
*)(m_TreeCtrl
->GetItemData(
684 m_TreeCtrl
->GetParent(m_TreeCtrl
->GetSelection())));
686 if (pardt
&& pardt
->Node
&& pardt
->Node
!= m_Resource
->GetRoot())
688 wxXmlNode
*nd
= pardt
->Node
;
690 wxXmlNode
*realnode
= NodeHandler::Find(nd
)->GetRealNode(nd
);
691 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
692 wxXmlNode
*node
= new wxXmlNode(*m_Clipboard
);
693 hnd
->InsertNode(realnode
, node
, m_SelectedNode
);
694 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
695 SelectNode(node
, &root
);
701 wxXmlNode
*realnode
= NodeHandler::Find(m_SelectedNode
)->GetRealNode(m_SelectedNode
);
702 NodeHandler
*hnd
= NodeHandler::Find(realnode
);
703 wxXmlNode
*node
= new wxXmlNode(*m_Clipboard
);
704 hnd
->InsertNode(realnode
, node
);
705 wxTreeItemId root
= m_TreeCtrl
->GetSelection();
706 SelectNode(node
, &root
);
714 bool EditorFrame::AskToSave()
715 // asks the user to save current document (if modified)
716 // returns FALSE if user cancelled the action, TRUE of he choosed
719 if (!m_Modified
) return TRUE
;
721 int res
= wxMessageBox(_("File modified. Do you want to save changes?"), _("Save changes"),
722 wxYES_NO
| wxCANCEL
| wxCENTRE
| wxICON_QUESTION
);
724 SaveFile(m_FileName
);
725 return (res
!= wxCANCEL
);
730 void EditorFrame::OnCloseWindow(wxCloseEvent
&)
732 if (!AskToSave()) return;