4 // Lukas Lipka <lukas@pmad.net>
5 // Raphael Slinckx <rslinckx@gmail.com>
7 // Copyright (C) 2005 Novell, Inc.
11 using System
.Collections
;
15 using System
.Threading
;
19 namespace ImLogViewer
{
21 public class ImLogWindow
{
22 [Widget
] Window imviewer
;
23 [Widget
] TreeView timelinetree
;
24 [Widget
] Label time_title
;
25 [Widget
] Entry search_entry
;
26 [Widget
] Button search_button
;
27 [Widget
] Button clear_button
;
28 [Widget
] TextView conversation
;
30 private string selected_log
;
32 private string speaking_to
;
33 private string log_path
;
34 private string highlight_text
;
35 private string search_text
;
37 private TreeStore tree_store
;
38 private ThreadNotify index_thread_notify
;
39 private Timeline timeline
= new Timeline ();
41 private string client
;
43 public ImLogWindow (string path
, string search
, string highlight
)
45 // FIXME: This is a bunch of crap, ImLog should be more dynamic.
46 if (path
.IndexOf (".gaim/logs") != -1)
48 else if (path
.IndexOf ("apps/kopete/logs") != -1)
51 if (Directory
.Exists (path
)) {
53 } else if (File
.Exists (path
)) {
54 log_path
= Path
.GetDirectoryName (path
);
57 Console
.WriteLine ("ERROR: Log path doesn't exist - {0}", path
);
61 highlight_text
= highlight
;
68 private void SetStatusTitle (DateTime dt
)
70 time_title
.Markup
= String
.Format ("<b>{0}</b>", StringFu
.DateTimeToPrettyString (dt
));
73 private void SetWindowTitle (string speaker
)
75 if (speaker
== null || speaker
== "")
82 buddy
= new GaimBuddyListReader ().Search (speaker
);
83 else if (client
== "kopete")
84 buddy
= new KopeteBuddyListReader ().Search (speaker
);
86 if (speaker
.EndsWith (".chat")) {
87 imviewer
.Title
= String
.Format (Catalog
.GetString ("Conversations in {0}"), speaker
.Replace (".chat", ""));
89 string nick
= speaker
;
91 if (buddy
!= null && buddy
.Alias
!= "")
94 imviewer
.Title
= String
.Format (Catalog
.GetString ("Conversations with {0}"), nick
);
97 speaking_to
= speaker
;
100 private void ShowWindow ()
104 Glade
.XML gxml
= new Glade
.XML (null, "ImLogViewer.glade", "imviewer", null);
105 gxml
.Autoconnect (this);
107 conversation
.PixelsAboveLines
= 3;
108 conversation
.LeftMargin
= 4;
109 conversation
.RightMargin
= 4;
111 TextTag boldtag
= new TextTag ("bold");
112 boldtag
.Weight
= Pango
.Weight
.Bold
;
113 conversation
.Buffer
.TagTable
.Add (boldtag
);
115 TextTag highlight
= new TextTag ("highlight");
116 highlight
.Background
= "yellow";
117 conversation
.Buffer
.TagTable
.Add (highlight
);
119 tree_store
= new TreeStore (new Type
[] {typeof (string), typeof (string), typeof (object)}
);
121 timelinetree
.Model
= tree_store
;
122 timelinetree
.AppendColumn ("Date", new CellRendererText(), "markup", 0);
123 timelinetree
.AppendColumn ("Snippet", new CellRendererText(), "text", 1);
124 timelinetree
.Selection
.Changed
+= OnConversationSelected
;
126 if (highlight_text
!= null)
127 search_entry
.Text
= highlight_text
;
129 if (search_text
!= null)
130 Search (search_text
);
132 search_entry
.Activated
+= OnSearchClicked
;
133 search_button
.Clicked
+= OnSearchClicked
;
134 clear_button
.Clicked
+= OnClearClicked
;
135 imviewer
.DeleteEvent
+= new DeleteEventHandler (OnWindowDelete
);
137 AccelGroup accel_group
= new AccelGroup ();
138 GlobalKeybinder global_keys
= new GlobalKeybinder (accel_group
);
139 global_keys
.AddAccelerator (OnWindowClose
, (uint) Gdk
.Key
.Escape
, 0, Gtk
.AccelFlags
.Visible
);
140 imviewer
.AddAccelGroup (accel_group
);
143 index_thread_notify
= new ThreadNotify (new ReadyEvent (RepopulateTimeline
));
144 Thread t
= new Thread (new ThreadStart (IndexLogs
));
150 private void IndexLogs ()
152 foreach (string file
in Directory
.GetFiles (log_path
)) {
153 ICollection logs
= null;
155 if (client
== "gaim")
156 logs
= GaimLog
.ScanLog (new FileInfo (file
));
157 else if (client
== "kopete")
158 logs
= KopeteLog
.ScanLog (new FileInfo (file
));
163 foreach (ImLog log
in logs
) {
164 if (speaking_to
== null)
165 SetWindowTitle (log
.SpeakingTo
);
167 timeline
.Add (log
, log
.StartTime
);
171 index_thread_notify
.WakeupMain ();
174 private bool LogContainsString (ImLog log
, string text
)
176 string [] words
= text
.Split (null);
178 //FIXME: This is very crude and EXPENSIVE!
179 foreach (string word
in words
) {
182 foreach (ImLog
.Utterance utt
in log
.Utterances
) {
183 if (utt
.Text
.ToLower ().IndexOf (word
.ToLower ()) != -1) {
189 if (!match
) return false;
195 private class ImLogPreview
197 public string Snippet
;
200 public ImLogPreview (ImLog log
)
202 Snippet
= log
.EllipsizedSnippet
;
207 private void AddCategory (ArrayList list
, string name
, string date_format
)
209 if (list
.Count
> 0) {
210 ArrayList previews
= GetPreviews (list
);
211 if (previews
.Count
> 0) {
212 TreeIter parent
= tree_store
.AppendValues (String
.Format ("<b>{0}</b>", Catalog
.GetString (name
)), "", null);
213 AddPreviews (parent
, previews
, Catalog
.GetString (date_format
));
218 private void RepopulateTimeline ()
222 AddCategory (timeline
.Today
, "Today", "HH:mm");
223 AddCategory (timeline
.Yesterday
, "Yesterday", "HH:mm");
224 AddCategory (timeline
.ThisWeek
, "This Week", "dddd");
225 AddCategory (timeline
.LastWeek
, "Last Week", "dddd");
226 AddCategory (timeline
.ThisMonth
, "This Month", "MMM d");
227 AddCategory (timeline
.ThisYear
, "This Year", "MMM d");
228 AddCategory (timeline
.Older
, "Older", "yyy MMM d");
230 timelinetree
.ExpandAll();
233 private void AddPreviews (TreeIter parent
, ArrayList previews
, string date_format
)
235 foreach (ImLogPreview preview
in previews
) {
236 string date
= preview
.Log
.StartTime
.ToString (date_format
);
237 tree_store
.AppendValues (parent
, date
, preview
.Snippet
, preview
.Log
);
239 if (selected_log
== null || selected_log
== preview
.Log
.LogFile
) {
240 selected_log
= preview
.Log
.LogFile
;
241 RenderConversation (preview
.Log
);
242 ScrollToLog (preview
.Log
.LogFile
);
247 private ArrayList
GetPreviews (ArrayList list
)
249 ArrayList logs
= new ArrayList ();
251 foreach (ImLog log
in list
) {
252 if (search_text
!= null && search_text
!= "")
253 if (! LogContainsString (log
, search_text
))
256 ImLogPreview preview
= new ImLogPreview (log
);
263 private void RenderConversation (ImLog im_log
)
265 TextBuffer buffer
= conversation
.Buffer
;
266 buffer
.Delete (buffer
.StartIter
, buffer
.EndIter
);
268 if (im_log
== null) {
269 //SetStatusTitle (new DateTime ());
273 SetStatusTitle (im_log
.StartTime
);
275 TextTag bold
= buffer
.TagTable
.Lookup ("bold");
277 foreach (ImLog
.Utterance utt
in im_log
.Utterances
) {
278 buffer
.InsertWithTags (buffer
.EndIter
, utt
.Who
+ ":", new TextTag
[] {bold}
);
279 buffer
.Insert (buffer
.EndIter
, String
.Format(" {0}\n", utt
.Text
));
282 if (highlight_text
!= null)
283 HighlightSearchTerms (highlight_text
);
285 if (search_text
!= null && search_text
!= "")
286 HighlightSearchTerms (search_text
);
289 private void HighlightSearchTerms (string highlight
)
291 TextBuffer buffer
= conversation
.Buffer
;
292 string text
= buffer
.GetText (buffer
.StartIter
, buffer
.EndIter
, false).ToLower ();
293 string [] words
= highlight
.Split (' ');
295 foreach (string word
in words
) {
298 if (word
== String
.Empty
)
301 while ((idx
= text
.IndexOf (word
.ToLower (), idx
)) != -1) {
302 Gtk
.TextIter start
= buffer
.GetIterAtOffset (idx
);
303 Gtk
.TextIter end
= start
;
304 end
.ForwardChars (word
.Length
);
306 buffer
.ApplyTag ("highlight", start
, end
);
313 private void Search (string text
)
315 search_entry
.Text
= text
;
316 search_button
.Visible
= false;
317 clear_button
.Visible
= true;
318 search_entry
.Sensitive
= false;
321 highlight_text
= null;
325 private void OnConversationSelected (object o
, EventArgs args
)
330 if (((TreeSelection
)o
).GetSelected (out model
, out iter
)) {
331 ImLog log
= model
.GetValue (iter
, 2) as ImLog
;
336 selected_log
= log
.LogFile
;
337 RenderConversation (log
);
341 private void OnWindowClose (object o
, EventArgs args
)
346 private void OnWindowDelete (object o
, DeleteEventArgs args
)
351 private void OnSearchClicked (object o
, EventArgs args
)
353 if (search_entry
.Text
== null || search_entry
.Text
== "")
356 Search (search_entry
.Text
);
357 RepopulateTimeline ();
360 private void ScrollToLog (string scroll_log
)
363 tree_store
.GetIterFirst (out root_iter
);
366 if (tree_store
.IterHasChild (root_iter
)) {
368 tree_store
.IterNthChild (out child
, root_iter
, 0);
371 ImLog log
= tree_store
.GetValue (child
, 2) as ImLog
;
373 if (log
.LogFile
== scroll_log
) {
374 TreePath path
= tree_store
.GetPath (child
);
375 timelinetree
.ExpandToPath (path
);
376 timelinetree
.Selection
.SelectPath (path
);
377 timelinetree
.ScrollToCell (path
, null, true, 0.5f
, 0.0f
);
380 } while (tree_store
.IterNext (ref child
));
382 } while (tree_store
.IterNext (ref root_iter
));
385 private void OnClearClicked (object o
, EventArgs args
)
387 highlight_text
= search_text
= null;
388 search_button
.Visible
= true;
389 clear_button
.Visible
= false;
390 search_entry
.Sensitive
= true;
392 RepopulateTimeline ();
394 ScrollToLog (selected_log
);