aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/SelectionManager.ts
blob: fcf705ac01397c03ff276de7e98cd72c1145fc2b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { action, observable } from 'mobx';
import { Doc, Opt } from '../../fields/Doc';
import { DocViews } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
import { Cast, DocCast } from '../../fields/Types';
import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
import { DocumentView } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { ScriptingGlobals } from './ScriptingGlobals';
import { UndoManager } from './UndoManager';

export namespace SelectionManager {
    class Manager {
        @observable SelectedViews: DocumentView[] = [];
        @observable IsDragging: boolean = false;
        @observable SelectedSchemaDocument: Doc | undefined;

        @action
        SelectSchemaViewDoc(doc: Opt<Doc>) {
            manager.SelectedSchemaDocument = doc;
        }
        @action
        SelectView(docView: DocumentView, ctrlPressed: boolean): void {
            // if doc is not in SelectedDocuments, add it
            if (!docView.SELECTED) {
                if (!ctrlPressed) this.DeselectAll();
                manager.SelectedViews.push(docView);
            } else if (!ctrlPressed && (manager.SelectedViews.length > 1 || manager.SelectedSchemaDocument)) {
                manager.SelectedViews.filter(dv => dv !== docView).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
                manager.SelectedSchemaDocument = undefined;
                manager.SelectedViews.length = 0;
            }
            docView.SELECTED = true;
            docView.props.whenChildContentsActiveChanged(true);
        }
        @action
        DeselectView(docView?: DocumentView): void {
            if (docView && manager.SelectedViews.includes(docView)) {
                docView.SELECTED = false;
                manager.SelectedViews.splice(manager.SelectedViews.indexOf(docView), 1);
                docView.props.whenChildContentsActiveChanged(false);
            }
        }
        @action
        DeselectAll(): void {
            LinkManager.currentLink = undefined;
            LinkManager.currentLinkAnchor = undefined;
            manager.SelectedSchemaDocument = undefined;
            manager.SelectedViews.forEach(dv => {
                dv.SELECTED = false;
                dv.props.whenChildContentsActiveChanged(false);
            });
            manager.SelectedViews.length = 0;
        }
    }

    const manager = new Manager();

    export function DeselectView(docView?: DocumentView): void {
        manager.DeselectView(docView);
    }
    export function SelectView(docView: DocumentView | undefined, ctrlPressed: boolean): void {
        if (!docView) DeselectAll();
        else manager.SelectView(docView, ctrlPressed);
    }
    export function SelectSchemaViewDoc(document: Opt<Doc>, deselectAllFirst?: boolean): void {
        if (deselectAllFirst) manager.DeselectAll();
        manager.SelectSchemaViewDoc(document);
    }

    // computed functions, such as used in IsSelected generate errors if they're called outside of a
    // reaction context.  Specifying the context with 'outsideReaction' allows an efficiency feature
    // to avoid unnecessary mobx invalidations when running inside a reaction.
    export function IsSelected(dv?: DocumentView | Doc): boolean {
        return (dv instanceof Doc ? Array.from(dv[DocViews]) : dv ? [dv] : []).some(dv => dv?.SELECTED);
    }

    export function DeselectAll(except?: Doc): void {
        const found = manager.SelectedViews.find(dv => dv.Document === except);
        manager.DeselectAll();
        if (found) manager.SelectView(found, false);
    }

    export function Views(): Array<DocumentView> {
        return manager.SelectedViews;
    }
    export function SelectedSchemaDoc(): Doc | undefined {
        return manager.SelectedSchemaDocument;
    }
    export function Docs(): Doc[] {
        return manager.SelectedViews.map(dv => dv.rootDoc).filter(doc => doc?._type_collection !== CollectionViewType.Docking);
    }
}
ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) {
    if (Doc.noviceMode && expertMode) return false;
    if (type === 'tab') {
        return SelectionManager.Views().lastElement()?.props.renderDepth === 0;
    }
    let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
    return selected?.type === type || selected?.type_collection === type || !type;
});
ScriptingGlobals.add(function deselectAll() {
    SelectionManager.DeselectAll();
});
ScriptingGlobals.add(function undo() {
    SelectionManager.DeselectAll();
    return UndoManager.Undo();
});

export function ShowUndoStack() {
    SelectionManager.DeselectAll();
    var buffer = '';
    UndoManager.undoStack.forEach((batch, i) => {
        buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n';
        ///batch.forEach(undo => (buffer += '   ' + undo.prop + '\n'));
    });
    alert(buffer);
}
ScriptingGlobals.add(function redo() {
    SelectionManager.DeselectAll();
    return UndoManager.Redo();
});
ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
    const docs = SelectionManager.Views()
        .map(dv => dv.props.Document)
        .filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
    return docs.length ? new List(docs) : prevValue;
});