Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCClassLibrary / Common / GUI / PlusGUI / Core / HistoryGui.sc
blob82401cb9f7716f6ff2daac15476cebe7e08953cb
1 HistoryGui {
3         classvar <w, <>docTitle = "History repeats", <>docHeight=120;
5         var <history, <w, <textV;
6         var <startBut, <filtBut, <filTextV, <filtBut, <keyPop, <topBut;
7         var <doc, <oldDocs, <docFlag = \sameDoc, <>stickMode=0;
9         var <filters, <filteredIndices, <filteredShorts, <filtering = false;
10         var lastLineSelected = 0, lastLinesShown;
12         *new { |history, where, numTextLines=12|
13                 ^super.new.init(history, where ? (0@0), numTextLines);
14         }
16         init { |inHist, where, numTextLines=12|
17                 var bounds;
18                 var closebut, listV, font, flow;
19                 bounds = where @ (where + (300@400));
21                 font = Font.monospace(9);       ////
22                 w = Window("History", bounds).front;    ////
23                 flow = w.addFlowLayout(2@2, 1@1);
24                 history = inHist;
26                 filters = [\all, ""];
28                 textV = TextView(w, Rect(0,0, 300 - 4, numTextLines * 12)).string_("")
29                         .enterInterpretsSelection_(false)
30                         .keyDownAction_({ |txvw, char, mod, uni, keycode|
31                                 // char.postcs;
32                                 if ([3, 13].includes(char.ascii)) {
33                                         this.rip(textV.string);
34                                 };
35                         })
36                         .resize_(2);
38                         // to do: disable if history is not current!
39                 startBut = Button(w, Rect(0, 0, 50, 20)) ////
40                         .states_([ ["start"], ["end"]])
41                         .canFocus_(false)
42                         .action_({ |btn|
43                                 switch(btn.value,
44                                         0, { if (history == History.current) { History.end } },
45                                         1, { if (history == History.current) { History.start } }
46                                 );
47                         });
49                 filtBut = Button(w, Rect(0, 0, 32, 20)) ////
50                         .canFocus_(false)
51                         .states_([["all"], ["filt"]]).action_({ |btn|
52                                 this.filtering_(btn.value > 0);
53                                 if (filtering) { this.filterLines };
54                                  history.hasMovedOn = true;
55                         });
57                 keyPop = PopUpMenu(w, Rect(0, 0, 40, 20))
58                         .items_([\all] ++ history.keys).value_(0)
59                         .action_({ |pop| this.setKeyFilter(pop.items[pop.value]) });
61                 filTextV = TextView(w, Rect(0,0, 88, 20)).string_("")
62                         .enterInterpretsSelection_(false)
63                         .resize_(2)
64                         .keyDownAction_({ |txvw, char, mod, uni, keycode|
65                                 this.setStrFilter(txvw.string);
66                                 if (this.filtering) { this.filterLines; }
67                         });
68                 topBut = Button(w, Rect(0, 0, 32, 20))
69                         .states_([["top"], ["keep"]]).value_(0)
70                         .resize_(3)
71                         .canFocus_(false)
72                         .action_({ |but| this.stickMode_(but.value) });
74                 Button(w, Rect(0, 0, 32, 20)) ////
75                         .states_([["rip"]])
76                         .resize_(3)
77                         .canFocus_(false)
78                         .action_({ |btn| this.rip(textV.string) });
80                 Button(w, Rect(0,0, 16, 20))
81                         .states_([["v"], ["^"]])
82                         .resize_(3)
83                         .action_ { |btn|
84                                 var views = w.view.children;
85                                 var resizes = [
86                                         [2, 1, 1, 1, 2, 3, 3, 3, 5],
87                                         [5, 7, 7, 7, 8, 9, 9, 9, 8]
88                                 ][btn.value.asInteger];
90                                 views.do { |v, i| v.resize_(resizes[i]) };
92                         };
94                 listV = ListView(w, bounds.copy.insetBy(2).height_(230))
95                         .font_(font)
96                         .items_([])
97                         .resize_(5)
98                         .background_(Color.grey(0.62))
99                         .action_({ |lview|
100                                 var index = lview.value;
101                                 if (lview.items.isEmpty) {
102                                         "no entries yet.".postln;
103                                 } {
104                                         lastLineSelected = listV.items[index];
105                                         if (filtering.not) {
106                                                 this.postInlined(index)
107                                         } {
108                                                 this.postInlined(filteredIndices[index])
109                                         }
110                                 }
111                         })
112                         .enterKeyAction_({ |lview|
113                                 var index = lview.value;
114                                 if (filtering) { index = filteredIndices[index] };
115                                 try {
116                                         history.lines[index][2].postln.interpret.postln;
117                                 //      "did execute.".postln;
118                                 } {
119                                         "execute line from history failed.".postln;
120                                 };
121                         });
122                 history.hasMovedOn = true;
124                 SkipJack({
125                         var newIndex, selectedLine, linesToShow, keys;
126                         var newStr = filTextV.string;
127                         if (filTextV.hasFocus and: (newStr != filters[1])) {
128                                 this.setStrFilter(newStr);
129                         }; // clumsy, but filTextV has no usable action...
131                         if (history.hasMovedOn) {
132                                 startBut.enabled_(history.isCurrent);
133                                 startBut.value_(History.started.binaryValue).refresh;
135                                 filtBut.value_(filtering.binaryValue).refresh;
136                                 if (filTextV.hasFocus.not) { filTextV.string_(filters[1]) };
137                                 keys = [\all] ++ history.keys.asArray.sort;
138                                 keyPop.items_(keys);
139                                 keyPop.value_(keys.indexOf(filters[0]) ? 0);
140                                 if (stickMode == 1) {
141                                                 // remember old selection
142                                         selectedLine = (lastLinesShown ? [])[listV.value];
143                                 } { };
145                                 linesToShow = if (filtering.not) {
146                                         history.lineShorts.array.copy
147                                 } {
148                                         this.filterLines;
149                                         filteredShorts;
150                                 } ? [];
152                                 if (linesToShow != lastLinesShown) {
153                                 //      "or updating listview here?".postln;
154                                         listV.items_(linesToShow);
155                                         lastLinesShown = linesToShow;
156                                 };
157                                 newIndex = if (selectedLine.isNil) { 0 }
158                                         { linesToShow.indexOf(selectedLine) };
159                                 listV.value_(newIndex ? 0);
160                                 if(stickMode == 0) { listV.action.value(listV) };
161                                 history.hasMovedOn = false;
162                         };
163                 }, 1, { w.isClosed }, "histwin");
164         }
165         setKeyFilter { |key| filters.put(0, key); this.filterLines; }
166         setStrFilter { |str| filters.put(1, str); this.filterLines; }
168         filtering_ { |flag=true|
169                  filtering = flag;
170                  history.hasMovedOn_(true);
171         }
172         filterOn { this.filtering_(true) }
173         filterOff { this.filtering_(false) }
175         filterLines {
176                 filteredIndices = history.indicesFor(*filters);
177                 filteredShorts = history.lineShorts[filteredIndices];
178                 defer {
179                         keyPop.value_(keyPop.items.indexOf(filters[0] ? 0));
180                         filTextV.string_(filters[1]);
181                 };
182                 if (filtering) { history.hasMovedOn = true; };
183         }
184         postInlined { |index|
185                 var line;
186                 if (history.lines.isNil) { "no history lines yet.".postln; ^this };
187                 line = history.lines[index];
188                 if (line.isNil) { "history: no line found!".inform; ^this };
189                 textV.string_(line[2]);
190         }
191         postDoc { |index|
192                 var line;
193                 if (history.lines.isNil) { "no history lines yet.".postln; ^this };
194                 line = history.lines[index];
195                 if (line.isNil) { "history: no line found!".inform; ^this };
196                 this.findDoc;
197                 doc.string_(line[2]).front;
198                 try { this.alignDoc };
199                 w.front;
200         }
201         alignDoc {
202                 var docbounds, winbounds;
203                 docbounds = doc.bounds;
204                 winbounds = w.bounds;
205                 doc.bounds_(
206                         Rect(
207                                 winbounds.left,
208                                 winbounds.top + winbounds.height + 24,
209                                 winbounds.width,
210                                 docHeight
211                         )
212                 )
213         }
215         rip {
216                 this.findDoc; doc.view.children.first.string_(textV.string); doc.front;
217         }
219         findDoc {
220                 if (docFlag == \newDoc) { oldDocs = oldDocs.add(doc) };
221                 if (docFlag == \newDoc or: doc.isNil or: { Window.allWindows.includes(doc).not }) {
222                         doc = Window(docTitle, Rect(300, 500, 300, 100));
223                         doc.addFlowLayout;
224                         TextView(doc, doc.bounds.resizeBy(-8, -8)).resize_(5);
225                 };
226                 oldDocs = oldDocs.select {|d| d.notNil and: { d.dataptr.notNil } };
227         }