1 module dcc.gtkd.post; 2 3 private import std.conv; 4 private import std.string; 5 private import std.stdio; 6 private import std.regex : replaceAll, regex; 7 private import std.array : replace; 8 9 private import gtk.TextMark; 10 11 private import dcc.gtkd.main; 12 private import dcc.engine.tribune; 13 14 struct GtkPostSegmentContext { 15 bool bold = false, 16 italic = false, 17 underline = false, 18 strike = false, 19 fixed = false, 20 link = false, 21 totoz = false, 22 login = false, 23 mainClock = false; 24 25 Clock clock; 26 27 string link_target = ""; 28 } 29 30 class GtkPostSegment { 31 GtkPostSegmentContext context; 32 string text; 33 34 GtkPost post; 35 uint offset; 36 } 37 38 class GtkPost { 39 Post post; 40 GtkTribune tribune; 41 GtkPostSegment[] _segments; 42 GtkPostSegment[int] segmentIndices; 43 44 GtkPostSegment[] clockReferences; 45 46 bool answer = false; 47 GtkPost[] referencedPosts; 48 GtkPost[GtkPost] referencingPosts; 49 50 this(GtkTribune tribune, Post post) { 51 this.tribune = tribune; 52 this.post = post; 53 } 54 55 string id() { 56 return this.post.post_id ~ "@" ~ this.tribune.tribune.name; 57 } 58 59 override string toString() { 60 return this.id; 61 } 62 63 void checkIfAnswer() { 64 foreach (GtkPost post ; this.referencedPosts) { 65 if (post.post.mine) { 66 writeln("This post ", this.post, " answers ", post.post); 67 this.answer = true; 68 return; 69 } 70 } 71 } 72 73 GtkPostSegment getSegmentAt(int offset) { 74 foreach (GtkPostSegment segment; this._segments) { 75 if (segment.offset <= offset && segment.offset + segment.text.count > offset) { 76 return segment; 77 } 78 } 79 80 return null; 81 } 82 83 GtkPostSegment[] segments() { 84 if (this._segments.length > 0) { 85 return this._segments; 86 } 87 88 string[] tokens = this.tokenize(); 89 90 int offset = 0; 91 92 GtkPostSegmentContext context, previousContext; 93 foreach (int i, string sub; tokens) { 94 GtkPostSegment segment = new GtkPostSegment(); 95 segment.post = this; 96 segment.offset = offset; 97 98 bool is_clock = false; 99 100 previousContext = context; 101 102 foreach (Clock post_clock; this.post.clocks) { 103 if (sub.strip == post_clock.text) { 104 context.clock = post_clock; 105 } 106 } 107 108 switch (sub.strip()) { 109 case "<a>": 110 context.link = true; 111 break; 112 case "</a>": 113 context.link = false; 114 break; 115 case "<b>": 116 context.bold = true; 117 break; 118 case "</b>": 119 context.bold = false; 120 break; 121 case "<i>": 122 context.italic = true; 123 break; 124 case "</i>": 125 context.italic = false; 126 break; 127 case "<u>": 128 context.underline = true; 129 break; 130 case "</u>": 131 context.underline = false; 132 break; 133 case "<s>": 134 context.strike = true; 135 break; 136 case "</s>": 137 context.strike = false; 138 break; 139 default: 140 segment.text ~= sub.dup; 141 break; 142 } 143 144 if (context.link && segment.text) { 145 context.link_target ~= segment.text; 146 } 147 segment.context = context; 148 149 if (!context.link && context.link_target.length > 0) { 150 // The link has been completely parsed 151 segment.text = "[url]"; 152 segment.context.link = true; 153 context.link_target = ""; 154 } 155 156 if (segment.text && !context.link) { 157 // This is a regular segment 158 this._segments ~= segment; 159 offset += segment.text.count; 160 } 161 162 if (context.clock != Clock.init) { 163 context.clock = Clock.init; 164 } 165 } 166 167 return this._segments; 168 } 169 170 // This is the same function as for the curses interface, for now... 171 string[] tokenize() { 172 string line = this.post.message.replaceAll(regex(`\s+`, "g"), " "); 173 174 // Since I can't use backreferences here... 175 line = line.replaceAll(regex(`<a href="(.*?)".*?>(.*?)</a>`, "g"), "<a>$1</a>"); 176 line = line.replaceAll(regex(`<a href='(.*?)'.*?>(.*?)</a>`, "g"), "<a>$1</a>"); 177 178 line = replace(line, "<", "<"); 179 line = replace(line, ">", ">"); 180 line = replace(line, "&", "&"); 181 182 string[] tokens = [""]; 183 184 bool next = false; 185 foreach (char c; line) { 186 switch (c) { 187 case '<': 188 tokens ~= ""; 189 break; 190 case '{': 191 case '[': 192 case '(': 193 case ' ': 194 case ',': 195 tokens ~= ""; 196 next = true; 197 break; 198 case ']': 199 case '>': 200 next = true; 201 break; 202 default: 203 break; 204 } 205 206 tokens[$-1] ~= c; 207 208 if (next) { 209 tokens ~= ""; 210 next = false; 211 } 212 } 213 214 return tokens; 215 } 216 } 217