]> git.neil.brown.name Git - edlib.git/blob - doc-docs.c
menubar: refresh bar whenever size changes.
[edlib.git] / doc-docs.c
1 /*
2  * Copyright Neil Brown ©2016-2017-2019 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * Document management is eased by having a well defined collection
6  * of documents.  This module provides a pane and a document to manage
7  * that collection.
8  *
9  * The document presents as a list of documents, called "*Documents*",
10  * providing a "line-format" to guide display of each line.
11  * The auxiliary pane becomes the parent of all attached documents, so
12  * that the list of children is exactly the content of the document.
13  * This pane receives doc:revisit notification  from the
14  * individual documents, and also requests notification of
15  * doc:status-changed.
16  *
17  * Supported global operations include:
18  * docs:byname - report pane with given (str)name
19  * docs:byfd - find a document given a path and file-descriptor.
20  * Each document is asked whether it matches the path and/or fd.
21  * docs:choose - choose and return a document which is not currently displayed
22  * somewhere.
23  * docs:save-all - each each document to save itself
24  * docs:show-modified - display a pane, in given window, listing just the
25  * documents that are modified and might need saving.
26  * Pane auto-closes when empty.
27  *
28  * After a document is created and bound to a pane "doc:appeared-*" is called
29  * which adds that pane to the list if it isn't already attached somewhere else.
30  * If docs sees two documents with the same name, it changes one to keep them
31  * all unique.
32  *
33  */
34
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdio.h>
39
40 #include "safe.h"
41 #define PRIVATE_DOC_REF
42 struct doc_ref {
43         struct pane     *p;
44         unsigned int    ignore;
45 };
46 #define DOC_SHARESREF
47 #define DOC_DATA_TYPE struct docs
48 #define DOC_NEXT(d,m,r,b) docs_next(d,r,b)
49 #define DOC_PREV(d,m,r,b) docs_prev(d,r,b)
50
51 #define PANE_DATA_PTR_TYPE struct pane *
52 #define PANE_DATA_VOID_2
53 #include "core.h"
54
55 static struct map *docs_map, *docs_aux_map, *docs_modified_map,
56         *docs_callback_map;
57 DEF_LOOKUP_CMD(docs_handle, docs_map);
58 DEF_LOOKUP_CMD(docs_aux, docs_aux_map);
59 DEF_LOOKUP_CMD(docs_modified_handle, docs_modified_map);
60 DEF_LOOKUP_CMD(docs_callback_handle, docs_callback_map);
61
62 struct docs {
63         struct doc              doc;
64         struct command          callback;
65         struct pane             *collection safe;
66 };
67 #include "core-pane.h"
68
69 static void docs_demark(struct pane *d safe, struct pane *p safe)
70 {
71         /* This document (p) is about to be moved in the list (d->collection).
72          * Any mark pointing at it is moved forward
73          */
74         struct docs *doc = d->doc_data;
75         struct mark *m, *first = NULL;
76         struct pane *next;
77         struct pane *col = doc->collection;
78
79         if (list_empty(&p->siblings) ||
80             p == list_last_entry(&col->children, struct pane, siblings))
81                 next = NULL;
82         else
83                 next = list_next_entry(p, siblings);
84
85         for (m = mark_first(&doc->doc);
86              m;
87              m = mark_next(m))
88                 if (m->ref.p == p) {
89                         if (!first) {
90                                 first = mark_prev(m);
91                                 if (!first)
92                                         first = m;
93                         }
94                         m->ref.p = next;
95                 } else if (first)
96                         break;
97         if (first)
98                 pane_notify("doc:replaced", d, 1, first);
99 }
100
101 static void docs_enmark(struct pane *d safe, struct pane *p safe)
102 {
103         /* This document has just been added to the list.
104          * any mark pointing just past it is moved back.
105          */
106         struct docs *doc = d->doc_data;
107         struct mark *m, *first = NULL;
108         struct pane *next;
109         struct pane *col = doc->collection;
110
111         if (p == list_last_entry(&col->children, struct pane, siblings))
112                 next = NULL;
113         else
114                 next = list_next_entry(p, siblings);
115
116         for (m = mark_first(&doc->doc);
117              m;
118              m = mark_next(m))
119                 if (m->ref.p == next) {
120                         if (!first)
121                                 first = m;
122                         m->ref.p = p;
123                 } else if (first)
124                         break;
125         if (first)
126                 pane_notify("doc:replaced", d, 1, first);
127 }
128
129 static bool doc_save(struct pane *p safe, struct pane *focus safe, int test)
130 {
131         char *fn = pane_attr_get(p, "filename");
132         char *mod = pane_attr_get(p, "doc-modified");
133         if (!fn || !*fn)
134                 call("Message", focus, 0, NULL,
135                      "File has no filename - cannot be saved.");
136         else if (!mod || strcmp(mod, "yes") != 0)
137                 call("Message", focus, 0, NULL,
138                      "File not modified - no need to save.");
139         else if (test)
140                 return True;
141         else
142                 home_call(p, "doc:save-file", focus);
143         return False;
144 }
145
146 static void check_name(struct docs *docs safe, struct pane *pane safe)
147 {
148         struct doc *d = &pane->doc;
149         char *nname;
150         int unique = 1;
151         int conflict = 1;
152
153         if (!d->name)
154                 d->name = strdup("*unknown*");
155
156         nname = malloc(strlen(d->name) + sizeof("<xxx>"));
157         while (conflict && unique < 1000) {
158                 struct pane *p;
159                 conflict = 0;
160                 if (unique > 1)
161                         sprintf(nname, "%s<%d>", d->name, unique);
162                 else
163                         strcpy(nname, d->name);
164                 list_for_each_entry(p, &docs->collection->children, siblings) {
165                         struct doc *d2 = &p->doc;
166                         if (d != d2 && d2->name &&
167                             strcmp(nname, d2->name) == 0) {
168                                 conflict = 1;
169                                 unique += 1;
170                                 break;
171                         }
172                 }
173         }
174         if (unique > 1) {
175                 free(d->name);
176                 d->name = nname;
177         } else
178                 free(nname);
179 }
180
181 static void doc_checkname(struct pane *p safe, struct pane *d safe, int n)
182 {
183         struct docs *ds = d->doc_data;
184         ASSERT(p->parent->handle == &docs_aux.c);
185         check_name(ds, p);
186         if (n) {
187                 docs_demark(d, p);
188                 if (n > 0)
189                         list_move(&p->siblings, &ds->collection->children);
190                 else
191                         list_move_tail(&p->siblings, &ds->collection->children);
192                 docs_enmark(d, p);
193         }
194 }
195
196 /*
197  * Interactive saving of files, particularly as happens when the editor
198  * is exiting, pops up a document-list window which only display
199  * documents which need saving.  They can be saved or killed, both of which
200  * actions removes them from the list.  When the list is empty an event can be
201  * sent back to the pane that requested the popup.
202  */
203
204 static int docs_open(struct pane *home safe, struct pane *focus safe,
205                      struct mark *m, bool other);
206
207 DEF_CMD(docs_mod_next)
208 {
209         struct mark *m;
210
211         /* If this is the last, then quit */
212         if (!ci->mark)
213                 return Enoarg;
214         m = mark_dup(ci->mark);
215         call("doc:EOL", ci->home->parent, 1, m, NULL, 1);
216         /* Passing '0' is deliberate.  We don't want to render
217          * anything, just see if there is anything tha could be rendered.
218          */
219         if (call("doc:render-line", ci->focus, 0, m) < 0 ||
220             m->ref.p == NULL) {
221                 mark_free(m);
222                 return call("popup:close", ci->focus);
223         }
224         mark_free(m);
225         /* Ask viewer to move forward */
226         return 2;
227 }
228
229 DEF_CMD(docs_mod_quit)
230 {
231         return call("popup:close", ci->home);
232 }
233
234 DEF_CMD(docs_mod_other)
235 {
236         /* abort the current action, and open this in another window */
237         docs_open(ci->home, ci->focus, ci->mark, True);
238         call("Abort", ci->home);
239         return 1;
240 }
241
242 DEF_CMD(docs_mod_empty)
243 {
244         call("popup:close", ci->focus);
245         return 1;
246 }
247
248 DEF_CMD(docs_mod_noop)
249 {
250         /* Don't want anything else to fall through to default */
251         return 1;
252 }
253
254 DEF_CMD(docs_callback_complete)
255 {
256         struct pane *p;
257
258         p = home_call_ret(pane, ci->home, "doc:attach-view", ci->focus,
259                           0, NULL, "simple");
260         if (p) {
261                 attr_set_str(&p->attrs, "line-format", "%doc-name");
262                 attr_set_str(&p->attrs, "heading", "");
263                 attr_set_str(&p->attrs, "done-key", "Replace");
264                 p = call_ret(pane, "attach-render-complete", p);
265         }
266         if (p)
267                 return comm_call(ci->comm2, "callback:doc", p);
268         return Efail;
269 }
270
271 DEF_CMD(docs_callback_byname)
272 {
273         struct docs *doc = ci->home->doc_data;
274         struct pane *p;
275
276         if (ci->str == NULL || strcmp(ci->str, "*Documents*") == 0)
277                 return comm_call(ci->comm2, "callback:doc",
278                                  ci->home);
279         list_for_each_entry(p, &doc->collection->children, siblings) {
280                 struct doc *dc = &p->doc;
281                 char *n = dc->name;
282                 if (n && strcmp(ci->str, n) == 0)
283                         return comm_call(ci->comm2, "callback:doc", p);
284         }
285         return Efail;
286 }
287
288 DEF_CMD(docs_callback_byfd)
289 {
290         struct docs *doc = ci->home->doc_data;
291         struct pane *p;
292
293         list_for_each_entry(p, &doc->collection->children, siblings) {
294                 if (call("doc:same-file", p, 0, NULL, ci->str,
295                          ci->num2) > 0)
296                         return comm_call(ci->comm2, "callback:doc", p);
297         }
298         return Efail;
299 }
300
301 DEF_CMD(docs_callback_byeach)
302 {
303         struct docs *doc = ci->home->doc_data;
304         struct pane *p;
305
306         list_for_each_entry(p, &doc->collection->children, siblings) {
307                 int r;
308                 r = comm_call(ci->comm2, "callback:doc", p);
309                 if (r)
310                         return r;
311         }
312         return 1;
313 }
314
315 DEF_CMD(docs_callback_choose)
316 {
317         struct docs *doc = ci->home->doc_data;
318         struct pane *choice = NULL, *last = NULL;
319         struct pane *p;
320
321         /* Choose a document with no notifiees or no pointer,
322          * but ignore 'CLOSED'
323          */
324
325         list_for_each_entry(p, &doc->collection->children, siblings) {
326                 struct doc *d = &p->doc;
327
328                 if (p->damaged & DAMAGED_CLOSED)
329                         continue;
330                 last = p;
331                 if (list_empty(&p->notifiees)) {
332                         choice = p;
333                         break;
334                 }
335                 if (tlist_empty(&d->points)) {
336                         choice = p;
337                         break;
338                 }
339         }
340         if (!choice)
341                 choice = last;
342         if (!choice)
343                 choice = ci->home;
344         return comm_call(ci->comm2, "callback:doc", choice);
345 }
346
347 DEF_CMD(docs_callback_saveall)
348 {
349         struct docs *doc = ci->home->doc_data;
350         struct pane *p;
351         int dirlen = ci->str ? (int)strlen(ci->str) : -1;
352
353         list_for_each_entry(p, &doc->collection->children, siblings) {
354                 if (dirlen > 0) {
355                         char *fn = pane_attr_get(p, "dirname");
356                         if (!fn || strncmp(ci->str, fn, dirlen) != 0)
357                                 continue;
358                 }
359                 if (doc_save(p, p, ci->num2))
360                         /* Something needs to be saved, we were only asked
361                          * to test.
362                          */
363                         return 2;
364         }
365         return 1;
366 }
367
368 DEF_CMD(docs_callback_modified)
369 {
370         struct pane *p;
371
372         p = home_call_ret(pane, ci->home, "doc:attach-view", ci->focus,
373                           0, NULL, "simple");
374         if (!p)
375                 return Efail;
376         p = call_ret(pane, "attach-linefilter", p);
377         if (!p)
378                 return Efail;
379         attr_set_str(&p->attrs, "filter:attr", "doc-can-save");
380         attr_set_str(&p->attrs, "filter:match", "yes");
381         p = pane_register_2(p, 0, &docs_modified_handle.c);
382         if (!p)
383                 return Efail;
384         attr_set_str(&p->attrs, "doc-name", "*Modified Documents*");
385         attr_set_str(&p->attrs, "line-format", "%doc-name:20 %filename");
386         attr_set_str(&p->attrs, "heading",
387                      "<bold>Document             File</>\n"
388                      "<bold,underline>[s]ave [y]es [n]o [q]uit</>");
389         /* Don't want to inherit position from some earlier instance,
390          * always move to the start.
391          */
392         call("doc:file", p, -1);
393         return 1;
394 }
395
396 DEF_CMD(docs_callback_appeared)
397 {
398         struct docs *doc = ci->home->doc_data;
399         struct pane *p;
400
401         /* Always return Efallthrough so other handlers get a chance */
402         p = ci->focus;
403         if (!p)
404                 return Efallthrough;
405         if (p->parent != p->parent->parent)
406                 /* This has a parent which is not the root,
407                  * so we shouldn't interfere.
408                  */
409                 return Efallthrough;
410         if (p == ci->home)
411                 /* The docs doc is attached separately */
412                 return Efallthrough;
413         pane_reparent(p, doc->collection);
414         home_call(p, "doc:request:doc:revisit", doc->collection);
415         home_call(p, "doc:request:doc:status-changed",
416                   doc->collection);
417         doc_checkname(p, ci->home, ci->num ?: -1);
418
419         return Efallthrough;
420 }
421
422 DEF_CMD(doc_damage)
423 {
424         struct pane *dp = ci->home->data;
425         struct mark *m = mark_new(dp);
426         struct pane *child = ci->focus;
427
428         if (!child || !m)
429                 return Enoarg;
430         do {
431                 if (m->ref.p == child) {
432                         pane_notify("doc:replaced", dp, 1, m);
433                         break;
434                 }
435         } while (doc_next(dp, m) != WEOF);
436         mark_free(m);
437         return 1;
438 }
439
440 DEF_CMD(doc_revisit)
441 {
442         struct pane *p = ci->focus;
443         struct pane *dp = ci->home->data;
444         struct docs *docs = dp->doc_data;
445
446         if (!p)
447                 return Einval;
448         if (p->parent != docs->collection)
449                 return Efallthrough;
450         if (p == ci->home)
451                 return 1;
452         doc_checkname(p, dp, ci->num);
453         return 1;
454 }
455
456 static inline wint_t docs_next(struct pane *home safe, struct doc_ref *r safe, bool bytes)
457 {
458         struct docs *d = home->doc_data;
459         struct pane *p = r->p;
460
461         if (p == NULL)
462                 return WEOF;
463
464         if (p == list_last_entry(&d->collection->children,
465                                  struct pane, siblings))
466                 r->p = NULL;
467         else
468                 r->p = list_next_entry(p, siblings);
469         return '\n';
470 }
471 static inline wint_t docs_prev(struct pane *home safe, struct doc_ref *r safe, bool bytes)
472 {
473         struct docs *d = home->doc_data;
474         struct pane *p = r->p;
475
476         if (list_empty(&d->collection->children))
477                 return WEOF;
478         else if (!p)
479                 p = list_last_entry(&d->collection->children,
480                                     struct pane, siblings);
481         else if (p != list_first_entry(&d->collection->children,
482                                        struct pane, siblings))
483                 p = list_prev_entry(p, siblings);
484         else
485                 return WEOF;
486         r->p = p;
487         return '\n';
488 }
489
490 DEF_CMD(docs_char)
491 {
492         return do_char_byte(ci);
493 }
494
495 DEF_CMD(docs_set_ref)
496 {
497         struct docs *d = ci->home->doc_data;
498         struct mark *m = ci->mark;
499
500         if (!m)
501                 return Enoarg;
502
503         mark_to_end(ci->home, m, ci->num != 1);
504         if (ci->num == 1 && !list_empty(&d->collection->children))
505                 m->ref.p = list_first_entry(&d->collection->children,
506                                             struct pane, siblings);
507         else
508                 m->ref.p = NULL;
509
510         m->ref.ignore = 0;
511         return 1;
512 }
513
514 DEF_CMD(docs_doc_get_attr)
515 {
516         struct mark *m = ci->mark;
517         const char *attr = ci->str;
518         char *val;
519
520         if (!m || !attr)
521                 return Enoarg;
522
523         if (!m->ref.p)
524                 return Efallthrough;
525
526         val = pane_attr_get(m->ref.p, attr);
527         /* use 'while' instead of 'if' to allow 'break' */
528         while (!val && strcmp(attr, "doc-can-save") == 0) {
529                 char *mod, *fl, *dir;
530                 val = "no";
531                 mod = pane_attr_get(m->ref.p, "doc-modified");
532                 if (!mod || strcmp(mod, "yes") != 0)
533                         break;
534                 fl = pane_attr_get(m->ref.p, "filename");
535                 if (!fl || !*fl)
536                         break;
537                 dir = pane_attr_get(ci->focus, "only-here");
538                 if (!dir || strncmp(dir, fl, strlen(dir)) == 0)
539                         val = "yes";
540         }
541
542         if (!val)
543                 return Efallthrough;
544         comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, m, val,
545                   0, NULL, attr);
546         return 1;
547 }
548
549 DEF_CMD(docs_get_attr)
550 {
551         const char *attr = ci->str;
552         char *val;
553
554         if (!attr)
555                 return Enoarg;
556
557         if ((val = attr_find(ci->home->attrs, attr)) != NULL)
558                 ;
559         else if (strcmp(attr, "heading") == 0)
560                 val = "<bold,underline> Mod Document             File</>";
561         else if (strcmp(attr, "line-format") == 0)
562                 val = " %doc-modified:3 %doc-name:20 %filename";
563         else if (strcmp(attr, "render-default") == 0)
564                 val = "format";
565         else if (strcmp(attr, "render-simple") == 0)
566                 val = "format";
567         else if (strcmp(attr, "view-default") == 0)
568                 val = "viewer";
569         else if (strcmp(attr, "doc-type") == 0)
570                 val = "docs";
571         else
572                 return Efallthrough;
573
574         comm_call(ci->comm2, "callback:get_attr", ci->focus,
575                   0, NULL, val);
576         return 1;
577 }
578
579 static int docs_open(struct pane *home safe, struct pane *focus safe,
580                      struct mark *m, bool other)
581 {
582         struct pane *p = NULL;
583         struct pane *dp;
584         struct pane *par;
585
586         if (!m)
587                 return Enoarg;
588         dp = m->ref.p;
589         /* close this pane, open the given document. */
590         if (dp == NULL)
591                 return Efail;
592
593         if (other) {
594                 par = home_call_ret(pane, focus, "DocPane", dp);
595                 if (par) {
596                         pane_take_focus(par);
597                         return 1;
598                 }
599                 par = call_ret(pane, "OtherPane", focus);
600         } else
601                 par = call_ret(pane, "ThisPane", focus);
602         if (par)
603                 p = home_call_ret(pane, dp, "doc:attach-view", par, 1);
604         if (p) {
605                 pane_take_focus(p);
606                 return 1;
607         } else {
608                 return Efail;
609         }
610 }
611
612 static int docs_open_alt(struct pane *home safe, struct pane *focus safe,
613                          struct mark *m, char cmd)
614 {
615         struct pane *p;
616         struct pane *dp;
617         char *renderer = NULL;
618         char *viewer = NULL;
619         struct pane *par;
620         char buf[100];
621
622         if (!m)
623                 return Enoarg;
624         dp = m->ref.p;
625         /* close this pane, open the given document. */
626         if (dp == NULL)
627                 return Efail;
628
629         snprintf(buf, sizeof(buf), "render-cmd-%c", cmd);
630         renderer = pane_attr_get(dp, buf);
631         snprintf(buf, sizeof(buf), "view-cmd-%c", cmd);
632         viewer = pane_attr_get(dp, buf);
633         if (!renderer && !viewer)
634                 return Efail;
635
636         par = call_ret(pane, "ThisPane", focus);
637         if (!par)
638                 return Efail;
639         p = home_call_ret(pane, dp, "doc:attach-view", par, 1, NULL, buf+5);
640         if (p) {
641                 pane_take_focus(p);
642                 return 1;
643         } else {
644                 return Efail;
645         }
646 }
647
648 static int docs_bury(struct pane *focus safe)
649 {
650         /* If the docs list is in a tile, put something else there. */
651         /* FIXME should this be a function of the pane manager? */
652         struct pane *tile, *doc;
653         tile = call_ret(pane, "ThisPane", focus);
654         if (!tile)
655                 return 1;
656         /* Discourage this doc from being chosen again */
657         call("doc:notify:doc:revisit", focus, -1);
658         doc = call_ret(pane, "docs:choose", focus);
659         if (doc)
660                 home_call(doc, "doc:attach-view", tile);
661         return 1;
662 }
663
664 static int docs_save(struct pane *focus safe, struct mark *m)
665 {
666         struct pane *dp;
667
668         if (!m)
669                 return Enoarg;
670         dp = m->ref.p;
671         if (!dp)
672                 return Efail;
673         doc_save(dp, focus, 0);
674         return 1;
675 }
676
677 static int docs_kill(struct pane *focus safe, struct mark *m, int num)
678 {
679         struct pane *dp;
680         char *mod;
681
682         if (!m)
683                 return Enoarg;
684         dp = m->ref.p;
685         if (!dp)
686                 return Efail;
687         mod = pane_attr_get(dp, "doc-modified");
688         if (mod && strcmp(mod, "yes") == 0 &&
689             num == NO_NUMERIC) {
690                 call("Message", focus, 0, NULL,
691                      "File modified, cannot kill.");
692                 return 1;
693         }
694         call("doc:destroy", dp);
695         return 1;
696 }
697
698 DEF_CMD(docs_destroy)
699 {
700         /* Not allowed to destroy this document
701          * So handle command here, so we don't get
702          * to the default handler
703          */
704         return 1;
705 }
706
707 DEF_CMD(docs_child_closed)
708 {
709         struct pane *pd = ci->home->data;
710
711         if (ci->num < 0)
712             docs_demark(pd, ci->focus);
713         return 1;
714 }
715
716 DEF_CMD(docs_do_open)
717 {
718         return docs_open(ci->home, ci->focus, ci->mark, False);
719 }
720
721 DEF_CMD(docs_do_open_other)
722 {
723         return docs_open(ci->home, ci->focus, ci->mark, True);
724 }
725
726 DEF_CMD(docs_do_open_alt)
727 {
728         const char *c = ksuffix(ci, "doc:cmd-");
729
730         return docs_open_alt(ci->home,
731                              ci->focus, ci->mark, c[0]);
732 }
733
734 DEF_CMD(docs_do_quit)
735 {
736         return docs_bury(ci->focus);
737 }
738
739 DEF_CMD(docs_do_save)
740 {
741         return docs_save(ci->focus, ci->mark);
742 }
743
744 DEF_CMD(docs_do_kill)
745 {
746         return docs_kill(ci->focus, ci->mark, ci->num);
747 }
748
749 DEF_CMD(docs_shares_ref)
750 {
751         return 1;
752 }
753
754 DEF_CMD(docs_val_marks)
755 {
756         struct docs *d = ci->home->doc_data;
757         struct pane *p;
758         int found;
759
760         if (!ci->mark || !ci->mark2)
761                 return Enoarg;
762
763         if (ci->mark->ref.p == ci->mark2->ref.p) {
764                 if (ci->mark->ref.ignore < ci->mark2->ref.ignore)
765                         return 1;
766                 LOG("docs_val_marks: same buf, bad offset: %u, %u",
767                     ci->mark->ref.ignore, ci->mark2->ref.ignore);
768                 return Efalse;
769         }
770         if (ci->mark->ref.p == NULL) {
771                 LOG("docs_val_marks: mark.p is NULL");
772                 return Efalse;
773         }
774         found = 0;
775         list_for_each_entry(p, &d->collection->children, siblings) {
776                 if (ci->mark->ref.p == p)
777                         found = 1;
778                 if (ci->mark2->ref.p == p) {
779                         if (found == 1)
780                                 return 1;
781                         LOG("docs_val_marks: mark2.p found before mark1");
782                         return Efalse;
783                 }
784         }
785         if (ci->mark2->ref.p == NULL) {
786                 if (found == 1)
787                         return 1;
788                 LOG("docs_val_marks: mark2.p (NULL) found before mark1");
789                 return Efalse;
790         }
791         if (found == 0)
792                 LOG("docs_val_marks: Neither mark found in pane list");
793         if (found == 1)
794                 LOG("docs_val_marks: mark2 not found in pane list");
795         return Efalse;
796 }
797
798 DEF_CMD_CLOSED(docs_close)
799 {
800         struct docs *docs = ci->home->doc_data;
801
802         call_comm("global-set-command-prefix", ci->home, &edlib_noop,
803                   0, NULL, "docs:");
804         call_comm("global-set-command", ci->home, &edlib_noop,
805                   0, NULL, "doc:appeared-docs-register");
806         pane_close(docs->collection);
807         return Efallthrough;
808 }
809
810 static void docs_init_map(void)
811 {
812         if (docs_map)
813                 return;
814         docs_map = key_alloc();
815         docs_aux_map = key_alloc();
816         docs_modified_map = key_alloc();
817         docs_callback_map = key_alloc();
818         /* A "docs" document provides services to children and also behaves as
819          * a document which lists those children
820          */
821         key_add_chain(docs_map, doc_default_cmd);
822         key_add(docs_map, "doc:set-ref", &docs_set_ref);
823         key_add(docs_map, "doc:get-attr", &docs_doc_get_attr);
824         key_add(docs_map, "doc:char", &docs_char);
825         key_add(docs_map, "doc:destroy", &docs_destroy);
826         key_add(docs_map, "doc:cmd-f", &docs_do_open);
827         key_add(docs_map, "doc:cmd-\n", &docs_do_open);
828         key_add(docs_map, "doc:cmd:Enter", &docs_do_open);
829         key_add(docs_map, "doc:cmd-o", &docs_do_open_other);
830         key_add(docs_map, "doc:cmd-q", &docs_do_quit);
831         key_add(docs_map, "doc:cmd-s", &docs_do_save);
832         key_add(docs_map, "doc:cmd-k", &docs_do_kill);
833         key_add_range(docs_map, "doc:cmd-A", "doc:cmd-Z", &docs_do_open_alt);
834         key_add(docs_map, "doc:shares-ref", &docs_shares_ref);
835         if(0)key_add(docs_map, "debug:validate-marks", &docs_val_marks);
836
837         key_add(docs_map, "get-attr", &docs_get_attr);
838         key_add(docs_map, "Close", &docs_close);
839
840         key_add(docs_aux_map, "doc:revisit", &doc_revisit);
841         key_add(docs_aux_map, "doc:status-changed", &doc_damage);
842         key_add(docs_aux_map, "Child-Notify", &docs_child_closed);
843
844         key_add_prefix(docs_modified_map, "doc:cmd-", &docs_mod_noop);
845         key_add_prefix(docs_modified_map, "doc:cmd:", &docs_mod_noop);
846         key_add(docs_modified_map, "doc:cmd-s", &docs_do_save);
847         key_add(docs_modified_map, "doc:cmd-y", &docs_do_save);
848         key_add(docs_modified_map, "doc:cmd-n", &docs_mod_next);
849         key_add(docs_modified_map, "doc:cmd-q", &docs_mod_quit);
850         key_add(docs_modified_map, "doc:cmd-o", &docs_mod_other);
851
852         key_add(docs_modified_map, "Notify:filter:empty", &docs_mod_empty);
853
854         key_add(docs_callback_map, "docs:complete", &docs_callback_complete);
855         key_add(docs_callback_map, "docs:byname", &docs_callback_byname);
856         key_add(docs_callback_map, "docs:byfd", &docs_callback_byfd);
857         key_add(docs_callback_map, "docs:byeach", &docs_callback_byeach);
858         key_add(docs_callback_map, "docs:choose", &docs_callback_choose);
859         key_add(docs_callback_map, "docs:save-all", &docs_callback_saveall);
860         key_add(docs_callback_map, "docs:show-modified",
861                 &docs_callback_modified);
862         key_add(docs_callback_map, "doc:appeared-docs-register",
863                 &docs_callback_appeared);
864 }
865
866 DEF_CB(docs_callback_lookup)
867 {
868         struct docs *docs = container_of(ci->comm, struct docs, callback);
869         struct pane *home = docs->collection->data;
870
871         return do_call_val(TYPE_comm, home, &docs_callback_handle.c,
872                            ci->key, ci->focus,
873                            ci->num, ci->mark, ci->str,
874                            ci->num2, ci->mark2, ci->str2,
875                            ci->x, ci->y, ci->comm2);
876 }
877
878 DEF_CMD(attach_docs)
879 {
880         /* Attach a docs handler.  We register some commands with the editor
881          * so we can be found
882          */
883         struct docs *doc;
884         struct pane *pd, *paux;
885
886         docs_init_map();
887
888         pd = doc_register(ci->home, &docs_handle.c);
889         if (!pd)
890                 return Efail;
891         doc = pd->doc_data;
892         doc->doc.name = strdup("*Documents*");
893         paux = pane_register(ci->home, 0, &docs_aux.c, pd);
894         if (!paux) {
895                 pane_close(pd);
896                 return Efail;
897         }
898         doc->collection = paux;
899
900         doc->callback = docs_callback_lookup;
901         call_comm("global-set-command-prefix", ci->home, &doc->callback,
902                   0, NULL, "docs:");
903         call_comm("global-set-command", ci->home, &doc->callback,
904                   0, NULL, "doc:appeared-docs-register");
905
906         pane_reparent(pd, doc->collection);
907
908         return comm_call(ci->comm2, "callback:doc", pd);
909 }
910
911 void edlib_init(struct pane *ed safe)
912 {
913         call_comm("global-set-command", ed, &attach_docs, 0, NULL,
914                   "attach-doc-docs");
915 }