]> git.saurik.com Git - wxWidgets.git/blob - contrib/utils/wxrcedit/editor.cpp
c340043ff1a7420950ee2e8f009d6fcd0f695df7
[wxWidgets.git] / contrib / utils / wxrcedit / editor.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Author: Vaclav Slavik
3 // Created: 2000/05/05
4 // RCS-ID: $Id$
5 // Copyright: (c) 2000 Vaclav Slavik
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 #ifdef __GNUG__
10 #pragma implementation "editor.h"
11 #pragma implementation "treedt.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx/wx.h".
15 #include "wx/wxprec.h"
16
17 #ifdef __BORLANDC__
18 #pragma hdrstop
19 #endif
20
21 #include "wx/wx.h"
22 #include "wx/xml/xml.h"
23 #include "wx/xml/xmlres.h"
24 #include "wx/splitter.h"
25 #include "wx/config.h"
26 #include "wx/dir.h"
27 #include "wx/listctrl.h"
28 #include "wx/imaglist.h"
29
30 #include "treedt.h"
31 #include "editor.h"
32 #include "nodehnd.h"
33 #include "xmlhelpr.h"
34 #include "preview.h"
35 #include "propframe.h"
36
37
38
39 class EditorTreeCtrl : public wxTreeCtrl
40 {
41 public:
42 EditorTreeCtrl(wxWindow *parent, int id, EditorFrame *frame)
43 : wxTreeCtrl(parent, id), m_EdFrame(frame) {}
44
45 private:
46 EditorFrame *m_EdFrame;
47
48 void OnRightClick(wxMouseEvent &event)
49 {
50 wxTreeItemId item =
51 m_EdFrame->m_TreeCtrl->HitTest(event.GetPosition());
52 if (item.IsOk())
53 {
54 m_EdFrame->m_TreeCtrl->SelectItem(item);
55 m_EdFrame->OnRightClickTree(event.GetPosition());
56 }
57 }
58 DECLARE_EVENT_TABLE()
59 };
60
61 BEGIN_EVENT_TABLE(EditorTreeCtrl, wxTreeCtrl)
62 EVT_RIGHT_DOWN(EditorTreeCtrl::OnRightClick)
63 END_EVENT_TABLE()
64
65
66 enum
67 {
68 ID_PREVIEW = wxID_HIGHEST + 100,
69 ID_NEW,
70 ID_OPEN,
71 ID_CLOSE,
72 ID_SAVE,
73 ID_SAVEAS,
74 ID_DELETE_NODE,
75 ID_EXIT,
76 ID_TREE,
77
78 ID_CUT,
79 ID_PASTE_SYBLING,
80 ID_PASTE_CHILD,
81 ID_COPY,
82
83 ID_NEWDIALOG,
84 ID_NEWPANEL,
85 ID_NEWMENU,
86 ID_NEWMENUBAR,
87 ID_NEWTOOLBAR,
88 ID_NEWNODE = wxID_HIGHEST + 10000, // safely out of XMLID range :)
89 ID_NEWSYBNODE = ID_NEWNODE + 20000
90 };
91
92
93
94
95
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 END_EVENT_TABLE()
102
103
104
105 #if defined(__UNIX__)
106 #include "bitmaps/preview.xpm"
107 #include "bitmaps/close.xpm"
108 #include "bitmaps/save.xpm"
109 #include "bitmaps/open.xpm"
110
111 #include "bitmaps/control.xpm"
112 #include "bitmaps/vsizer.xpm"
113 #include "bitmaps/hsizer.xpm"
114 #include "bitmaps/panel.xpm"
115 #include "bitmaps/gsizer.xpm"
116 #include "bitmaps/resicon.xpm"
117 #endif
118
119
120
121 EditorFrame *EditorFrame::ms_Instance = NULL;
122
123 EditorFrame::EditorFrame(wxFrame *parent, const wxString& filename)
124 : wxFrame(parent, -1, filename + _("- wxWindows resources editor"))
125 {
126 ms_Instance = this;
127
128 m_Clipboard = NULL;
129
130 wxConfigBase *cfg = wxConfigBase::Get();
131
132 SetSize(wxRect(wxPoint(cfg->Read("editor_x", -1), cfg->Read("editor_y", -1)),
133 wxSize(cfg->Read("editor_w", 400), cfg->Read("editor_h", 400))));
134
135 m_SelectedNode = NULL;
136 m_Resource = NULL;
137 m_FileName = wxEmptyString;
138
139 wxMenu *menuFile = new wxMenu;
140 menuFile->Append(ID_NEW, "&New");
141 menuFile->Append(ID_OPEN, "&Open\tCtrl-O");
142 menuFile->Append(ID_SAVE, "&Save\tCtrl-S");
143 menuFile->Append(ID_SAVEAS, "Save &as...");
144 menuFile->AppendSeparator();
145 menuFile->Append(ID_EXIT, "E&xit\tAlt-X");
146
147 wxMenu *menuEdit = new wxMenu;
148 menuEdit->Append(ID_CUT, "Cut\tCtrl-X");
149 menuEdit->Append(ID_COPY, "Copy\tCtrl-C");
150 menuEdit->Append(ID_PASTE_SYBLING, "Paste as sybling\tCtrl-V");
151 menuEdit->Append(ID_PASTE_CHILD, "Paste as child");
152 menuEdit->AppendSeparator();
153 menuEdit->Append(ID_DELETE_NODE, "Delete");
154
155 menuEdit->Enable(ID_PASTE_SYBLING, FALSE);
156 menuEdit->Enable(ID_PASTE_CHILD, FALSE);
157
158 wxMenuBar *menuBar = new wxMenuBar();
159 menuBar->Append(menuFile, "&File");
160 menuBar->Append(menuEdit, "&Edit");
161 SetMenuBar(menuBar);
162
163 // Create toolbar:
164 wxToolBar *toolBar = CreateToolBar(wxNO_BORDER | wxTB_HORIZONTAL | wxTB_FLAT);
165 toolBar->SetMargins(2, 2);
166 toolBar->SetToolBitmapSize(wxSize(24, 24));
167 toolBar -> AddTool(ID_EXIT, wxBITMAP(close), wxNullBitmap,
168 FALSE, -1, -1, (wxObject *) NULL,
169 _("Quit the editor"));
170 toolBar -> AddTool(ID_OPEN, wxBITMAP(open), wxNullBitmap,
171 FALSE, -1, -1, (wxObject *) NULL,
172 _("Open XML resource file"));
173 toolBar -> AddTool(ID_SAVE, wxBITMAP(save), wxNullBitmap,
174 FALSE, -1, -1, (wxObject *) NULL,
175 _("Save XML file"));
176 toolBar -> AddTool(ID_PREVIEW, wxBITMAP(preview), wxNullBitmap,
177 FALSE, -1, -1, (wxObject *) NULL,
178 _("Preview"));
179 toolBar -> Realize();
180
181 wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
182
183 // Create tree control:
184 m_TreeCtrl = new EditorTreeCtrl(this, ID_TREE, this);
185 wxImageList *imgList = new wxImageList(16, 16);
186 imgList->Add(wxICON(control));
187 imgList->Add(wxICON(panel));
188 imgList->Add(wxICON(vsizer));
189 imgList->Add(wxICON(hsizer));
190 imgList->Add(wxICON(gsizer));
191 imgList->Add(wxICON(resicon));
192 m_TreeCtrl->AssignImageList(imgList);
193 sizer->Add(m_TreeCtrl, 1, wxEXPAND);
194
195 SetAutoLayout(TRUE);
196 SetSizer(sizer);
197
198 // Load file:
199 if (!filename)
200 NewFile();
201 else
202 LoadFile(filename);
203 }
204
205
206
207 EditorFrame::~EditorFrame()
208 {
209 PreviewFrame::Get()->Close();
210 PropertiesFrame::Get()->Close();
211
212 wxConfigBase *cfg = wxConfigBase::Get();
213
214 cfg->Write(_T("editor_x"), (long)GetPosition().x);
215 cfg->Write(_T("editor_y"), (long)GetPosition().y);
216 cfg->Write(_T("editor_w"), (long)GetSize().x);
217 cfg->Write(_T("editor_h"), (long)GetSize().y);
218
219 delete m_Clipboard;
220 }
221
222
223
224
225 void EditorFrame::LoadFile(const wxString& filename)
226 {
227 delete m_Resource;
228
229 m_FileName = "";
230 m_Resource = new wxXmlDocument;
231
232 if (!m_Resource->Load(filename))
233 {
234 delete m_Resource;
235 m_Resource = NULL;
236 NewFile();
237 wxLogError("Error parsing " + filename);
238 }
239 else
240 {
241 m_FileName = filename;
242 RefreshTree();
243 SetTitle("wxrcedit - " + wxFileNameFromPath(m_FileName));
244 }
245 }
246
247
248
249 void EditorFrame::SaveFile(const wxString& filename)
250 {
251 m_FileName = filename;
252 SetTitle("wxrcedit - " + wxFileNameFromPath(m_FileName));
253
254 if (!m_Resource->Save(filename, wxXML_IO_LIBXML))
255 wxLogError("Error saving " + filename);
256 }
257
258
259
260 void EditorFrame::NewFile()
261 {
262 delete m_Resource;
263
264 m_FileName = "";
265 m_Resource = new wxXmlDocument;
266 m_Resource->SetRoot(new wxXmlNode(wxXML_ELEMENT_NODE, "resource"));
267
268 RefreshTree();
269 SetTitle("unnamed");
270 }
271
272
273
274 void EditorFrame::RefreshTree()
275 {
276 wxXmlNode *sel = m_SelectedNode;
277
278 m_TreeCtrl->DeleteAllItems();
279 wxTreeItemId root = m_TreeCtrl->AddRoot("Resource: " + wxFileNameFromPath(m_FileName), 5, 5);
280
281 wxXmlNode *n = m_Resource->GetRoot()->GetChildren();
282 while (n)
283 {
284 if (n->GetType() == wxXML_ELEMENT_NODE)
285 CreateTreeNode(m_TreeCtrl, root, n);
286 n = n->GetNext();
287 }
288
289 m_TreeCtrl->Expand(root);
290 SelectNode(sel);
291 }
292
293
294
295 bool EditorFrame::SelectNode(wxXmlNode *node, wxTreeItemId *root)
296 {
297 if (root == NULL)
298 {
299 wxTreeItemId rootitem = m_TreeCtrl->GetRootItem();
300 return SelectNode(node, &rootitem);
301 }
302
303 wxTreeItemId item;
304 XmlTreeData *dt;
305 wxXmlNode *nd;
306 long cookie;
307
308 item = m_TreeCtrl->GetFirstChild(*root, cookie);
309 while (item.IsOk())
310 {
311 dt = (XmlTreeData*)(m_TreeCtrl->GetItemData(item));
312 nd = (dt) ? dt->Node : NULL;
313 if (nd == node)
314 {
315 m_TreeCtrl->SelectItem(item);
316 m_TreeCtrl->EnsureVisible(item);
317 return TRUE;
318 }
319 if (m_TreeCtrl->ItemHasChildren(item) && SelectNode(node, &item))
320 return TRUE;
321 item = m_TreeCtrl->GetNextChild(*root, cookie);
322 }
323 return FALSE;
324 }
325
326
327
328 wxTreeItemId EditorFrame::CreateTreeNode(wxTreeCtrl *treectrl, wxTreeItemId parent, wxXmlNode *node)
329 {
330 if (!node)
331 {
332 wxTreeItemId invalid;
333 return invalid;
334 }
335
336 return NodeHandler::Find(node)->CreateTreeNode(treectrl, parent, node);
337 }
338
339
340
341 void EditorFrame::NotifyChanged(int change_type)
342 {
343 if (change_type & CHANGED_TREE)
344 RefreshTree();
345
346 if (change_type & CHANGED_TREE_SELECTED)
347 {
348 wxTreeItemId sel = m_TreeCtrl->GetSelection();
349 m_TreeCtrl->SetItemText(sel,
350 NodeHandler::Find(m_SelectedNode)->GetTreeString(m_SelectedNode));
351 }
352
353 if (change_type & CHANGED_TREE_SELECTED_ICON)
354 {
355 wxTreeItemId sel = m_TreeCtrl->GetSelection();
356 int icon = NodeHandler::Find(m_SelectedNode)->GetTreeIcon(m_SelectedNode);
357 m_TreeCtrl->SetItemImage(sel, icon);
358 }
359 }
360
361
362
363 static void RecursivelyExpand(wxTreeCtrl *t, wxTreeItemId item)
364 {
365 t->Expand(item);
366 long cookie;
367 wxTreeItemId id = t->GetFirstChild(item, cookie);
368 while (id.IsOk())
369 {
370 RecursivelyExpand(t, id);
371 id = t->GetNextChild(item, cookie);
372 }
373 }
374
375 void EditorFrame::OnTreeSel(wxTreeEvent& event)
376 {
377 XmlTreeData *dt = (XmlTreeData*)(m_TreeCtrl->GetItemData(event.GetItem()));
378 wxXmlNode *node = (dt) ? dt->Node : NULL;
379
380 m_SelectedNode = node;
381 if (node)
382 PropertiesFrame::Get()->ShowProps(node);
383
384 if (m_TreeCtrl->GetParent(event.GetItem()) == m_TreeCtrl->GetRootItem())
385 {
386 wxTreeItemId it = event.GetOldItem();
387
388 if (it.IsOk() && m_TreeCtrl->GetRootItem() != it)
389 {
390 while (m_TreeCtrl->GetParent(it) != m_TreeCtrl->GetRootItem())
391 it = m_TreeCtrl->GetParent(it);
392 m_TreeCtrl->Collapse(it);
393 }
394 RecursivelyExpand(m_TreeCtrl, event.GetItem());
395
396 PreviewFrame::Get()->Preview(node);
397 }
398 }
399
400
401
402 void EditorFrame::OnToolbar(wxCommandEvent& event)
403 {
404 switch (event.GetId())
405 {
406 case ID_PREVIEW :
407 {
408 XmlTreeData* dt = (XmlTreeData*)m_TreeCtrl->GetItemData(m_TreeCtrl->GetSelection());;
409 if (dt != NULL && dt->Node != NULL)
410 PreviewFrame::Get()->Preview(dt->Node);
411 break;
412 }
413
414 case ID_EXIT :
415 Close(TRUE);
416 break;
417
418 case ID_NEW :
419 NewFile();
420 break;
421
422 case ID_OPEN :
423 {
424 wxString name = wxFileSelector(_("Open XML resource"), _T(""), _T(""), _T(""), _("XML resources (*.xrc)|*.xrc"), wxOPEN | wxFILE_MUST_EXIST);
425 if (!name.IsEmpty())
426 LoadFile(name);
427 break;
428 }
429
430 case ID_SAVE :
431 if (m_FileName != "") { SaveFile(m_FileName); break;}
432 // else go to SAVEAS
433
434 case ID_SAVEAS :
435 {
436 wxString name = wxFileSelector(_("Save as"), _T(""), m_FileName, _T(""), _("XML resources (*.xrc)|*.xrc"), wxSAVE | wxOVERWRITE_PROMPT);
437 if (!name.IsEmpty())
438 SaveFile((m_FileName = name));
439 break;
440 }
441
442 case ID_DELETE_NODE :
443 {
444 DeleteSelectedNode();
445 break;
446 }
447 }
448 }
449
450
451
452 void EditorFrame::DeleteSelectedNode()
453 {
454 XmlTreeData *dt = (XmlTreeData*)
455 (m_TreeCtrl->GetItemData(m_TreeCtrl->GetParent(m_TreeCtrl->GetSelection())));
456 wxXmlNode *n = (dt) ? dt->Node : NULL;
457
458 m_SelectedNode->GetParent()->RemoveChild(m_SelectedNode);
459 NotifyChanged(CHANGED_TREE);
460 SelectNode(n);
461 }
462
463
464
465 void EditorFrame::OnNewNode(wxCommandEvent& event)
466 {
467 if (event.GetId() >= ID_NEWSYBNODE)
468 {
469 XmlTreeData *pardt =
470 (XmlTreeData*)(m_TreeCtrl->GetItemData(
471 m_TreeCtrl->GetParent(m_TreeCtrl->GetSelection())));
472
473 if (pardt && pardt->Node && pardt->Node != m_Resource->GetRoot())
474 {
475 wxXmlNode *nd = pardt->Node;
476
477 wxXmlNode *realnode = NodeHandler::Find(nd)->GetRealNode(nd);
478 NodeHandler *hnd = NodeHandler::Find(realnode);
479 wxString name = hnd->GetChildTypes()[event.GetId()-ID_NEWSYBNODE];
480
481 wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, _T("object"));
482 node->AddProperty(_T("class"), name);
483
484 hnd->InsertNode(realnode, node, m_SelectedNode);
485 wxTreeItemId root = m_TreeCtrl->GetSelection();
486 SelectNode(node, &root);
487 }
488
489 }
490
491 else if (event.GetId() >= ID_NEWNODE)
492 {
493 wxXmlNode *realnode = NodeHandler::Find(m_SelectedNode)->GetRealNode(m_SelectedNode);
494 NodeHandler *hnd = NodeHandler::Find(realnode);
495 wxString name = hnd->GetChildTypes()[event.GetId()-ID_NEWNODE];
496
497 wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, _T("object"));
498 node->AddProperty(_T("class"), name);
499
500 hnd->InsertNode(realnode, node);
501 wxTreeItemId root = m_TreeCtrl->GetSelection();
502 SelectNode(node, &root);
503 }
504
505 else
506 {
507 wxString name;
508 switch (event.GetId())
509 {
510 case ID_NEWDIALOG : name = _T("wxDialog"); break;
511 case ID_NEWPANEL : name = _T("wxPanel"); break;
512 case ID_NEWMENU : name = _T("wxMenu"); break;
513 case ID_NEWMENUBAR : name = _T("wxMenuBar"); break;
514 case ID_NEWTOOLBAR : name = _T("wxToolBar"); break;
515 default : return; // never occurs
516 }
517
518 wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, _T("object"));
519 node->AddProperty(_T("class"), name);
520 m_Resource->GetRoot()->AddChild(node);
521 NotifyChanged(CHANGED_TREE);
522 SelectNode(node);
523 }
524 }
525
526
527
528 void EditorFrame::OnRightClickTree(wxPoint pos)
529 {
530 wxMenu *popup = new wxMenu;
531
532 if (m_SelectedNode == NULL || m_SelectedNode == m_Resource->GetRoot())
533 {
534 popup->Append(ID_NEWDIALOG, _("New wxDialog"));
535 popup->Append(ID_NEWPANEL, _("New wxPanel"));
536 popup->Append(ID_NEWMENU, _("New wxMenu"));
537 popup->Append(ID_NEWMENUBAR, _("New wxMenuBar"));
538 popup->Append(ID_NEWTOOLBAR, _("New wxToolBar"));
539 }
540
541 else
542 {
543 bool has_children;
544 {
545 wxArrayString& arr =
546 NodeHandler::Find(NodeHandler::Find(m_SelectedNode)->GetRealNode(m_SelectedNode))->
547 GetChildTypes();
548
549 has_children = !arr.IsEmpty();
550 if (!arr.IsEmpty())
551 {
552 wxMenu *news = new wxMenu;
553 wxMenu *news2 = news;
554 for (size_t i = 0; i < arr.GetCount(); i++)
555 {
556 news2->Append(i + ID_NEWNODE, arr[i]);
557 #ifdef __WXGTK__ // doesn't support Break
558 if (i % 20 == 19)
559 {
560 wxMenu *m = new wxMenu;
561 news2->Append(ID_NEWNODE+arr.GetCount(), _("More..."), m);
562 news2 = m;
563 }
564 #else
565 if (i % 16 == 15) news2->Break();
566 #endif
567 }
568 popup->Append(ID_NEWNODE-1, _("New child"), news);
569 }
570 }
571
572
573 XmlTreeData *pardt =
574 (XmlTreeData*)(m_TreeCtrl->GetItemData(
575 m_TreeCtrl->GetParent(m_TreeCtrl->GetSelection())));
576 if (pardt && pardt->Node && pardt->Node != m_Resource->GetRoot())
577 {
578 wxXmlNode *nd = pardt->Node;
579 wxArrayString& arr =
580 NodeHandler::Find(NodeHandler::Find(nd)->GetRealNode(nd))->
581 GetChildTypes();
582
583 if (!arr.IsEmpty())
584 {
585 wxMenu *news = new wxMenu;
586 wxMenu *news2 = news;
587 for (size_t i = 0; i < arr.GetCount(); i++)
588 {
589 news2->Append(i + ID_NEWSYBNODE, arr[i]);
590 #ifdef __WXGTK__ // doesn't support Break
591 if (i % 20 == 19)
592 {
593 wxMenu *m = new wxMenu;
594 news2->Append(ID_NEWSYBNODE+arr.GetCount(), _("More..."), m);
595 news2 = m;
596 }
597 #else
598 if (i % 16 == 15) news2->Break();
599 #endif
600 }
601 popup->Append(ID_NEWSYBNODE-1, _("New sybling"), news);
602 }
603 }
604
605
606 popup->AppendSeparator();
607 popup->Append(ID_CUT, _("Cut"));
608 popup->Append(ID_COPY, _("Copy"));
609 popup->Append(ID_PASTE_SYBLING, _("Paste as sybling"));
610 popup->Append(ID_PASTE_CHILD, _("Paste as child"));
611 popup->AppendSeparator();
612 popup->Append(ID_DELETE_NODE, _("Delete"));
613 popup->Enable(ID_PASTE_SYBLING, m_Clipboard != NULL);
614 popup->Enable(ID_PASTE_CHILD, has_children && m_Clipboard != NULL);
615 }
616
617 m_TreeCtrl->PopupMenu(popup, pos);
618 delete popup;
619 }
620
621
622
623 void EditorFrame::OnClipboardAction(wxCommandEvent& event)
624 {
625 switch (event.GetId())
626 {
627 case ID_COPY:
628 case ID_CUT:
629 delete m_Clipboard;
630 m_Clipboard = new wxXmlNode(*m_SelectedNode);
631 GetMenuBar()->Enable(ID_PASTE_SYBLING, TRUE);
632 GetMenuBar()->Enable(ID_PASTE_CHILD, TRUE);
633 if (event.GetId() == ID_CUT) DeleteSelectedNode();
634 break;
635
636 case ID_PASTE_SYBLING:
637 {
638 XmlTreeData *pardt =
639 (XmlTreeData*)(m_TreeCtrl->GetItemData(
640 m_TreeCtrl->GetParent(m_TreeCtrl->GetSelection())));
641
642 if (pardt && pardt->Node && pardt->Node != m_Resource->GetRoot())
643 {
644 wxXmlNode *nd = pardt->Node;
645
646 wxXmlNode *realnode = NodeHandler::Find(nd)->GetRealNode(nd);
647 NodeHandler *hnd = NodeHandler::Find(realnode);
648 wxXmlNode *node = new wxXmlNode(*m_Clipboard);
649 hnd->InsertNode(realnode, node, m_SelectedNode);
650 wxTreeItemId root = m_TreeCtrl->GetSelection();
651 SelectNode(node, &root);
652 }
653 }
654 break;
655
656 case ID_PASTE_CHILD:
657 wxXmlNode *realnode = NodeHandler::Find(m_SelectedNode)->GetRealNode(m_SelectedNode);
658 NodeHandler *hnd = NodeHandler::Find(realnode);
659 wxXmlNode *node = new wxXmlNode(*m_Clipboard);
660 hnd->InsertNode(realnode, node);
661 wxTreeItemId root = m_TreeCtrl->GetSelection();
662 SelectNode(node, &root);
663 break;
664 }
665 }
666