don't draw contact list scrollbar if it is not required
[bioacid.git] / tklog.d
blob78e617e232ae784795b3724127d00020be1ba9ab
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program 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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module tklog is aliced;
19 import std.datetime;
21 import arsd.color;
22 import arsd.image;
23 import arsd.simpledisplay;
25 import iv.cmdcon;
26 import iv.cmdcon.gl;
27 import iv.nanovega;
28 import iv.nanovega.blendish;
29 import iv.nanovega.textlayouter;
30 import iv.strex;
31 import iv.tox;
32 import iv.unarray;
33 import iv.utfutil;
34 import iv.vfs.io;
36 import accdb;
37 import accobj;
38 import fonts;
39 import popups;
40 import icondata;
41 import notifyicon;
42 import toxproto;
44 import tkmain;
47 // ////////////////////////////////////////////////////////////////////////// //
48 alias LayTextClass = LayTextD;
51 // ////////////////////////////////////////////////////////////////////////// //
52 __gshared LayTextClass lay;
53 __gshared int layWinHeight = 0;
54 __gshared int layOffset = 0; // from bottom
56 static struct LayUrl {
57 string url;
58 uint wordidx; // first word
61 __gshared LayUrl[uint] layUrlList; // for each word
64 // ////////////////////////////////////////////////////////////////////////// //
65 class MessageStart : LayObject {
66 long msgid; // >0: outgoing, unacked yet
68 this (long aid=-1) nothrow @safe @nogc { msgid = aid; }
70 override int width () => 0;
71 override int spacewidth () => 0;
72 override int height () => 0;
73 override int ascent () => 0;
74 override int descent () => 0;
75 override bool canbreak () => true;
76 override bool spaced () => false;
77 // y is at baseline
78 override void draw (NVGContext ctx, float x, float y) {}
82 class MessageOutMark : LayObject {
83 long msgid; // >0: outgoing, unacked yet
85 this (long aid=-1) nothrow @safe @nogc { msgid = aid; }
87 override int width () => kittyOut.width;
88 override int spacewidth () => 4;
89 override int height () => kittyOut.height;
90 override int ascent () => height;
91 override int descent () => 0;
92 override bool canbreak () => true;
93 override bool spaced () => false;
94 // y is at baseline
95 override void draw (NVGContext ctx, float x, float y) {
96 if (msgid > 0) {
97 nvg.save();
98 scope(exit) nvg.restore;
99 nvg.newPath();
100 nvg.rect(x+0.5, y+0.5, width, height);
101 nvg.fillPaint(nvg.imagePattern(0, 0, width, height, 0, kittyOut));
102 nvg.fill();
108 // ////////////////////////////////////////////////////////////////////////// //
109 void wipeLog () {
110 lay.wipeAll(true); // clear log, but delete objects
111 layUrlList.clear();
112 layOffset = 0;
116 void addDividerLine (bool doflushgui=false) {
117 if (glconCtlWindow is null || glconCtlWindow.closed) return;
120 bool inFrame = nvg.inFrame;
121 if (!inFrame) {
122 glconCtlWindow.setAsCurrentOpenGlContext(); // make this window active
123 nvg.beginFrame(glconCtlWindow.width, glconCtlWindow.height);
125 scope(exit) {
126 if (!inFrame) {
127 nvg.endFrame();
128 if (doflushgui) flushGui();
129 glconCtlWindow.releaseCurrentOpenGlContext();
133 lay.fontStyle.fontsize = 2;
134 lay.fontStyle.color = NVGColor.k8orange.asUint;
135 lay.fontStyle.bgcolor = NVGColor("#aa0").asUint;
136 lay.fontStyle.monospace = true;
138 lay.putExpander();
139 lay.endPara();
140 lay.finalize();
142 // redraw
143 glconPostScreenRepaint();
147 // `ct` can be `null` for "my message" or "system message"
148 void addTextToLog (Account acc, Contact ct, LogFile.Msg.Kind kind, bool action, const(char)[] msg, SysTime time, long msgid=-1, bool doflushgui=false) {
149 if (glconCtlWindow is null || glconCtlWindow.closed) return;
152 bool inFrame = nvg.inFrame;
153 if (!inFrame) {
154 glconCtlWindow.setAsCurrentOpenGlContext(); // make this window active
155 nvg.beginFrame(glconCtlWindow.width, glconCtlWindow.height);
157 scope(exit) {
158 if (!inFrame) {
159 nvg.endFrame();
160 if (doflushgui) flushGui();
161 glconCtlWindow.releaseCurrentOpenGlContext();
165 // add "message start" mark
166 lay.putObject(new MessageStart(msgid));
168 lay.fontStyle.fontsize = 16;
169 lay.fontStyle.color = NVGColor.k8orange.asUint;
170 lay.fontStyle.bgcolor = NVGColor("#222").asUint;
171 lay.fontStyle.monospace = true;
173 NVGColor textColor;
175 final switch (kind) {
176 case LogFile.Msg.Kind.Outgoing: textColor = NVGColor.k8orange; lay.fontStyle.color = NVGColor("#c40").asUint; lay.put(acc.info.nick); break;
177 case LogFile.Msg.Kind.Incoming: textColor = NVGColor("#ccc"); lay.fontStyle.color = NVGColor("#666").asUint; lay.put(ct.info.nick); break;
178 case LogFile.Msg.Kind.Notification: textColor = NVGColor("#0c0"); lay.fontStyle.color = textColor.asUint; lay.put("*system*"); break;
181 lay.fontStyle.monospace = false;
182 //lay.putHardSpace(64);
183 lay.putExpander();
184 // add "message outgoing" mark
185 if (kind == LogFile.Msg.Kind.Outgoing && msgid > 0) {
186 //lay.put("!");
187 //conwriteln("msgoutmark");
188 lay.putObject(new MessageOutMark(msgid));
189 lay.putExpander();
193 import std.datetime;
194 import std.format : format;
195 auto dt = cast(DateTime)time;
196 string tstr = "%04u/%02u/%02u".format(dt.year, dt.month, dt.day);
197 lay.put(tstr);
198 lay.putNBSP();
201 lay.fontStyle.color = NVGColor("#aaa").asUint;
202 lay.fontStyle.bgcolor = NVGColor("#006").asUint;
204 import std.datetime;
205 import std.format : format;
206 auto dt = cast(DateTime)time;
207 string tstr = "%02u:%02u:%02u".format(dt.hour, dt.minute, dt.second);
208 lay.put(tstr);
209 lay.putNBSP();
211 lay.endPara();
213 lay.fontStyle.bgcolor = NVGColor.transparent.asUint;
214 lay.fontStyle.fontsize = 20;
216 if (action) {
217 lay.fontStyle.color = NVGColor("#fff").asUint;
218 lay.put("/me ");
221 void xput (const(char)[] str) {
222 while (str.length) {
223 auto nl = str.indexOf('\n');
224 if (nl < 0) { lay.put(str); break; }
225 if (nl > 0) lay.put(str[0..nl]);
226 lay.endPara();
227 str = str[nl+1..$];
231 msg = msg.xstripright;
233 lay.fontStyle.color = textColor.asUint;
234 while (msg.length) {
235 auto nfo = urlDetect(msg);
236 if (!nfo.valid) {
237 xput(msg);
238 break;
240 // url found
241 xput(msg[0..nfo.pos]);
242 string url = msg[nfo.pos..nfo.end].idup;
243 msg = msg[nfo.end..$];
244 auto stword = lay.nextWordIndex;
245 lay.pushStyles();
246 auto c = lay.fontStyle.color;
247 scope(exit) { lay.popStyles; lay.fontStyle.color = c; }
248 lay.fontStyle.href = true;
249 lay.fontStyle.underline = true;
250 lay.fontStyle.color = NVGColor("#06f").asUint;
251 lay.put(url);
252 while (stword < lay.nextWordIndex) {
253 layUrlList[stword] = LayUrl(url, stword);
254 ++stword;
258 lay.endPara();
259 lay.finalize();
261 // redraw
262 glconPostScreenRepaint();
266 void addTextToLog (Account acc, Contact ct, in ref LogFile.Msg msg, long msgid=-1, bool doflushgui=false) {
267 import iv.utfutil : utf8Encode;
268 char[] text;
269 text.reserve(4096);
270 scope(exit) delete text;
271 // decode text
272 foreach (dchar dc; msg.byDChar) {
273 char[4] buf = void;
274 auto len = utf8Encode(buf[], dc);
275 text ~= buf[0..len];
277 addTextToLog(acc, ct, msg.kind, msg.isMe, text, msg.time, msgid, doflushgui);
281 void ackLogMessage (long msgid) {
282 if (msgid <= 0) return;
283 foreach (immutable uint widx; 0..lay.wordCount) {
284 auto w = lay.wordByIndex(widx);
285 int oidx = w.objectIdx;
286 if (oidx >= 0) {
287 if (auto maw = cast(MessageOutMark)lay.objectAtIndex(oidx)) {
288 if (maw.msgid == msgid) {
289 maw.msgid = -1; // reset mark
290 glconPostScreenRepaint(); // redraw