1 /*
2 FXiTe - The Free eXtensIble Text Editor
3 Copyright (c) 2009-2011 Jeffrey Pohlmeyer <yetanothergeek@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License version 3 as
7 published by the Free Software Foundation.
8
9 This software is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19
20 #include <cerrno>
21
22 #include <fxkeys.h>
23 #include <fx.h>
24
25 #include "shmenu.h"
26 #include "compat.h"
27
28 #include "intl.h"
29 #include "tooltree.h"
30
31 #define TREEOPTS TREELIST_BROWSESELECT|TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|LAYOUT_FILL
32
33
34
ToolsTree(FXComposite * p,FXObject * tgt,FXSelector sel,UserMenu ** menus)35 ToolsTree::ToolsTree(FXComposite*p, FXObject*tgt, FXSelector sel, UserMenu**menus):FXTreeList(p,tgt,sel,TREEOPTS) {
36 menu_list=menus;
37 saved_path="";
38 prev_item=NULL;
39 dummy_item=NULL;
40 scan(false);
41 }
42
43
44
getFilePath(FXTreeItem * item)45 const char*ToolsTree::getFilePath(FXTreeItem*item) {
46 if ( item->getParent() ) {
47 if ( item->getParent()->getParent()) {
48 if (item->hasItems()) {
49 return (const char*)((((FXMenuCascade*)(item->getData()))->getMenu())->getUserData());
50 } else {
51 return (const char*)(((FXMenuCommand*)(item->getData()))->getUserData());
52 }
53 } else {
54 return ((UserMenu*)(item->getData()))->getTopDir();
55 }
56 } else {
57 return NULL;
58 }
59 }
60
61
62
build_tree(FXTreeItem * item,const FXPopup * menu)63 void ToolsTree::build_tree(FXTreeItem*item, const FXPopup*menu)
64 {
65 item->setHasItems(true);
66 if (menu) {
67 for (FXWindow*w=menu->getFirst(); w; w=w->getNext()) {
68 FXTreeItem*node=new FXTreeItem(((FXMenuCaption*)w)->getText());
69 if (!saved_path.empty()) {
70 const char*this_path;
71 this_path=(const char*)((FXMenuCommand*)w)->getUserData();
72 if (!this_path) {
73 this_path=(const char*)((((FXMenuCascade*)w)->getMenu())->getUserData());
74 }
75 if ( this_path && strcmp(saved_path.text(),this_path) == 0 ) {
76 restored_item=node;
77 }
78 }
79 insertItem(NULL,item,node);
80 node->setData(w);
81 node->setHasItems(false);
82 if (!w->getUserData()) { /* Cascading menus don't carry the path, their popup does */
83 build_tree(node,((FXMenuCascade*)w)->getMenu());
84 }
85 }
86 }
87 }
88
89
90
scan(bool rebuild)91 void ToolsTree::scan(bool rebuild)
92 {
93 FXString item_name="";
94 if (prev_item) {
95 if ((prev_item==tools)||(prev_item->getParent()==tools)) {
96 item_name=prev_item->getText();
97 } else {
98 if (saved_path.empty()) { saved_path=getFilePath(prev_item); }
99 }
100 }
101 if (dummy_item) {
102 if (dummy_item->hasItems()) {
103 FXMenuCascade*casc=(FXMenuCascade*)(dummy_item->getData());
104 FXMenuPane*pane=(FXMenuPane*)casc->getMenu();
105 free(pane->getUserData());
106 delete pane;
107 delete casc;
108 dummy_item->setData(NULL);
109 } else {
110 FXMenuCommand*cmd=(FXMenuCommand*)(dummy_item->getData());
111 free(cmd->getUserData());
112 delete cmd;
113 dummy_item->setData(NULL);
114 }
115 dummy_item=NULL;
116 }
117 clearItems();
118 prev_item=NULL;
119 restored_item=NULL;
120 tools = new FXTreeItem(_("Tools"));
121 tools->setHasItems(true);
122 appendItem(NULL,tools);
123 for (UserMenu**um=menu_list; *um; um++) {
124 if (rebuild) { (*um)->rescan(); }
125 FXTreeItem *item=new FXTreeItem((*um)->getText());
126 item->setHasItems(true);
127 item->setData(*um);
128 insertItem(NULL,tools,item);
129 build_tree(item,(*um)->menu());
130 }
131 saved_path="";
132 if (item_name.empty()) {
133 for (FXTreeItem *ti=restored_item; ti; ti=ti->getParent()) { expandTree(ti); }
134 } else {
135 if (compare(tools->getText(),item_name)==0) {
136 restored_item=tools;
137 } else {
138 for (FXTreeItem *ti=tools->getFirst(); ti; ti=ti->getNext()) {
139 if (compare(ti->getText(),item_name)==0) {
140 restored_item=ti;
141 break;
142 }
143 }
144 }
145 }
146 expandTree(tools);
147 if (!restored_item) { restored_item=tools; }
148 expandTree(restored_item);
149 setCurrentItem(restored_item);
150 setFocus();
151 if (target&&message) {
152 target->handle(this,FXSEL(SEL_CHANGED,message),restored_item);
153 }
154 }
155
156
157
GetUserMenu(FXTreeItem * item)158 UserMenu*ToolsTree::GetUserMenu(FXTreeItem*item) {
159 for (FXTreeItem*i=item; i; i=i->getParent()) {
160 if (i->getParent()==tools) {
161 return (UserMenu*)(i->getData());
162 }
163 }
164 return NULL;
165 }
166
167
MakeDummyMenu(FXTreeItem * parent_item)168 void ToolsTree::MakeDummyMenu(FXTreeItem*parent_item)
169 {
170 FXString path=getFilePath(parent_item);
171 path.append(PATHSEP);
172 FXint index=0;
173 FXString tmp;
174 while (index<100) {
175 tmp.format("%s%02d.new-group",path.text(),index);
176 if (!FXStat::exists(tmp)) { break; }
177 index++;
178 }
179 if (FXStat::exists(tmp)) {
180 FXMessageBox::error(getShell(), MBOX_OK, _("File error"), "%s:\n%s\n\n%s",
181 _("Failed to create temporary directory"),
182 tmp.text(), _("too many \"New Group\" folders")
183 );
184 dummy_item=NULL;
185 return;
186 }
187 if (!FXDir::create(tmp)) {
188 FXMessageBox::error(getShell(), MBOX_OK, _("File error"), "%s:\n%s\n\n%s",
189 _("Failed to create temporary directory"),
190 tmp.text(), SystemErrorStr()
191 );
192 dummy_item=NULL;
193 return;
194 }
195 dummy_item=new FXTreeItem(_("New Group"));
196 dummy_item->setHasItems(true);
197 appendItem(parent_item,dummy_item);
198 FXMenuPane*pane=new FXMenuPane(this);
199 pane->setUserData(strdup(tmp.text()));
200 FXMenuCascade*casc=new FXMenuCascade(this,"",NULL,pane,0);
201 dummy_item->setData(casc);
202 setCurrentItem(dummy_item);
203 selectItem(dummy_item);
204 }
205
206
207
MakeDummyTool(FXTreeItem * parent_item,FXuint perm)208 void ToolsTree::MakeDummyTool(FXTreeItem*parent_item, FXuint perm)
209 {
210 FXString path=getFilePath(parent_item);
211 const char *ext;
212 UserMenu*um=GetUserMenu(parent_item);
213 switch (um?um->Tag():0) { // Suggest a file extension
214 case 'C':
215 case 'F': { // Commands and Filters use shell script extension.
216 #ifdef WIN32
217 ext="bat";
218 #else
219 ext="sh";
220 #endif
221 break;
222 }
223 case 'M': { // Macros use lua extension
224 ext="lua";
225 break;
226 }
227 default: { // Snippets (maybe just a text file)
228 ext=(perm & FXIO::OwnerExec)?"exec.txt":"read.txt";
229 }
230 }
231 path.append(PATHSEP);
232 FXint index=0;
233 FXString tmp;
234 while (index<100) {
235 tmp.format("%s%02d.new-tool.%s",path.text(),index, ext);
236 if (!FXStat::exists(tmp)) { break; }
237 index++;
238 }
239 if (FXStat::exists(tmp)) {
240 FXMessageBox::error(getShell(), MBOX_OK, _("File error"), "%s:\n%s\n\n%s",
241 _("Failed to create temporary file"),
242 tmp.text(), _("too many \"New Tool\" files")
243 );
244 dummy_item=NULL;
245 return;
246 }
247 if (!FXFile::create(tmp,perm)) {
248 FXMessageBox::error(getShell(), MBOX_OK, _("File error"), "%s:\n%s\n\n%s",
249 _("Failed to create temporary file"),
250 tmp.text(), SystemErrorStr()
251 );
252 dummy_item=NULL;
253 return;
254 }
255 dummy_item=new FXTreeItem(_("New Tool"));
256 dummy_item->setHasItems(false);
257 appendItem(parent_item,dummy_item);
258 FXMenuCommand*cmd=new FXMenuCommand(this,_("New Tool"));
259 cmd->setUserData(strdup(tmp.text()));
260 dummy_item->setData(cmd);
261 setCurrentItem(dummy_item);
262 selectItem(dummy_item);
263 }
264
265
266
267 /* An OK button that is only enabled when the selected tree item is a tool (i.e. a leaf node) */
268 class OkBtn: public FXButton {
269 FXDECLARE(OkBtn);
OkBtn()270 OkBtn(){}
271 public:
onCheckEnable(FXObject * o,FXSelector sel,void * p)272 long onCheckEnable(FXObject*o, FXSelector sel, void*p)
273 {
274 FXTreeItem*item=(FXTreeItem*)p;
275 if (item && !item->hasItems()) { enable(); } else { disable(); }
276 return 1;
277 }
278 enum { ID_CHECK_ENABLE=FXButton::ID_LAST };
OkBtn(FXComposite * p)279 OkBtn(FXComposite*p):FXButton(p,_("OK"),NULL,p,FXDialogBox::ID_ACCEPT,BUTTON_NORMAL|BUTTON_INITIAL|LAYOUT_CENTER_X){}
280 };
281
282
283
284 FXDEFMAP(OkBtn)OkBtnMap[]={ FXMAPFUNC(SEL_CHANGED,OkBtn::ID_CHECK_ENABLE,OkBtn::onCheckEnable) };
285
286 FXIMPLEMENT(OkBtn,FXButton,OkBtnMap,ARRAYNUMBER(OkBtnMap));
287
288
289
HasTool(FXTreeItem * item)290 static bool HasTool(FXTreeItem*item)
291 {
292 for(FXTreeItem*i=item; i; i=i->getNext()) {
293 if ((!i->hasItems())||HasTool(i->getFirst())) { return true; }
294 }
295 return false;
296 }
297
298
299
300 /* Display a dialog box to let the user select a tool item */
SelectTool(FXWindow * owner,UserMenu ** menus,FXMenuCommand * & mc)301 bool ToolsTree::SelectTool(FXWindow* owner, UserMenu** menus, FXMenuCommand*&mc)
302 {
303 FXDialogBox dlg(owner,"Select tool",DECOR_TITLE|DECOR_BORDER,0,0,240,320,1,1,1,1,0,0);
304 FXVerticalFrame*vbox=new FXVerticalFrame(&dlg,FRAME_RAISED|LAYOUT_FILL);
305 OkBtn*ok=new OkBtn(&dlg);
306 ToolsTree*tree=new ToolsTree(vbox, ok, OkBtn::ID_CHECK_ENABLE, menus);
307 FXHorizontalFrame*btns=new FXHorizontalFrame(vbox,FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_CENTER_X|PACK_UNIFORM_WIDTH);
308 ok->reparent(btns,NULL);
309 new FXButton(btns,_("Cancel"),NULL,&dlg,FXDialogBox::ID_CANCEL,BUTTON_NORMAL|LAYOUT_CENTER_X);
310 if (!HasTool(tree->getFirstItem())) {
311 FXMessageBox::information(owner, MBOX_OK,_("No tools defined"), "%s\n%s",
312 _("You have not defined any custom tools. To create a custom tool,"),
313 _("use the \"Customize menu\" item from the \"Tools\" menu.")
314 );
315 return false;
316 }
317 if (dlg.execute(PLACEMENT_OWNER)) {
318 mc=((FXMenuCommand*)(tree->getCurrentItem()->getData()));
319 return true;
320 } else {
321 return false;
322 }
323 }
324
325