1# Copyright (c) 2002-2011 Tim Baker
2
3#
4# Demo: Outlook Express newsgroup messages
5#
6namespace eval DemoOutlookNewsgroup {}
7proc DemoOutlookNewsgroup::Init {T} {
8
9    variable Priv
10
11    InitPics outlook-*
12
13    set height [font metrics [$T cget -font] -linespace]
14    if {$height < 18} {
15	set height 18
16    }
17
18    #
19    # Configure the treectrl widget
20    #
21
22    $T configure -itemheight $height -selectmode browse \
23	-showroot no -showrootbutton no -showbuttons yes -showlines no \
24	-xscrollincrement 20 -xscrollsmoothing yes
25
26    switch -- [$T theme platform] {
27	visualstyles {
28	    $T theme setwindowtheme "Explorer"
29	}
30    }
31
32    #
33    # Create columns
34    #
35
36    $T column create -image outlook-clip -tags clip
37    $T column create -image outlook-arrow -tags arrow
38    $T column create -image outlook-watch -tags watch
39    $T column create -text Subject -width 250 -tags subject
40    $T column create -text From -width 150 -tags from
41    $T column create -text Sent -width 150 -tags sent
42    $T column create -text Size -width 60 -justify right -tags size
43
44#    $T column configure all -gridrightcolor #ebf4fe
45
46    # Would be nice if I could specify a column -tag too
47    # *blink* The amazing code Genie makes it so!!!
48    $T configure -treecolumn subject
49
50    # State for a read message
51    $T item state define read
52
53    # State for a message with unread descendants
54    $T item state define unread
55
56    # States for "open" rectangles.  This is an ugly hack to get the
57    # active outline to span multiple columns.
58    $T item state define openWE
59    $T item state define openE
60    $T item state define openW
61
62    #
63    # Create elements
64    #
65
66    $T element create elemImg image -image {
67	outlook-read-2Sel {selected read unread !open}
68	outlook-read-2 {read unread !open}
69	outlook-readSel {selected read}
70	outlook-read {read}
71	outlook-unreadSel {selected}
72	outlook-unread {}
73    }
74
75    $T element create elemTxt text -fill [list $::SystemHighlightText {selected focus}] \
76	-font [list DemoFontBold {read unread !open} DemoFontBold {!read}] -lines 1
77
78    $T element create sel rect \
79	-fill [list $::SystemHighlight {selected focus} gray {selected !focus}] \
80	-open {we openWE e openE w openW} -showfocus yes
81
82    #
83    # Create styles using the elements
84    #
85
86    # Image + text
87    set S [$T style create s1]
88    $T style elements $S {sel elemImg elemTxt}
89    $T style layout $S elemImg -expand ns
90    $T style layout $S elemTxt -padx {2 6} -squeeze x -expand ns
91    $T style layout $S sel -union [list elemTxt] -iexpand nes -ipadx {2 0}
92
93    # Text
94    set S [$T style create s2]
95    $T style elements $S {sel elemTxt}
96    $T style layout $S elemTxt -padx 6 -squeeze x -expand ns
97    $T style layout $S sel -detach yes -iexpand xy
98
99    # Set default item styles
100    $T column configure subject -itemstyle s1
101    $T column configure from -itemstyle s2
102    $T column configure sent -itemstyle s2
103    $T column configure size -itemstyle s2
104
105    #
106    # Create items and assign styles
107    #
108
109    set msgCnt 100
110
111    set thread 0
112    set Priv(count,0) 0
113    set items [$T item id root]
114    for {set i 1} {$i < $msgCnt} {incr i} {
115	set itemi [$T item create]
116	while 1 {
117	    set j [expr {int(rand() * $i)}]
118	    set itemj [lindex $items $j]
119	    if {$j == 0} break
120	    if {[$T depth $itemj] == 5} continue
121	    if {$Priv(count,$Priv(thread,$itemj)) == 15} continue
122	    break
123	}
124	$T item lastchild $itemj $itemi
125
126	set Priv(read,$itemi) [expr rand() * 2 > 1]
127	if {$j == 0} {
128	    set Priv(thread,$itemi) [incr thread]
129	    set Priv(seconds,$itemi) [expr {[clock seconds] - int(rand() * 500000)}]
130	    set Priv(seconds2,$itemi) $Priv(seconds,$itemi)
131	    set Priv(count,$thread) 1
132	} else {
133	    set Priv(thread,$itemi) $Priv(thread,$itemj)
134	    set Priv(seconds,$itemi) [expr {$Priv(seconds2,$itemj) + int(rand() * 10000)}]
135	    set Priv(seconds2,$itemi) $Priv(seconds,$itemi)
136	    set Priv(seconds2,$itemj) $Priv(seconds,$itemi)
137	    incr Priv(count,$Priv(thread,$itemj))
138	}
139	lappend items $itemi
140    }
141
142    for {set i 1} {$i < $msgCnt} {incr i} {
143	set itemi [lindex $items $i]
144	set subject "This is thread number $Priv(thread,$itemi)"
145	set from somebody@somewhere.net
146	set sent [clock format $Priv(seconds,$itemi) -format "%d/%m/%y %I:%M %p"]
147	set size [expr {1 + int(rand() * 10)}]KB
148
149	# This message has been read
150	if {$Priv(read,$itemi)} {
151	    $T item state set $itemi read
152	}
153
154	# This message has unread descendants
155	if {[AnyUnreadDescendants $T $itemi]} {
156	    $T item state set $itemi unread
157	}
158
159	if {[$T item numchildren $itemi]} {
160	    $T item configure $itemi -button yes
161
162	    # Collapse some messages
163	    if {rand() * 2 > 1} {
164		$T item collapse $itemi
165	    }
166	}
167
168#		$T item style set $i 3 s1 4 s2.we 5 s2.we 6 s2.w
169	$T item text $itemi subject $subject from $from sent $sent size $size
170
171	$T item state forcolumn $itemi subject openE
172	$T item state forcolumn $itemi from openWE
173	$T item state forcolumn $itemi sent openWE
174	$T item state forcolumn $itemi size openW
175    }
176
177    # Do something when the selection changes
178    $T notify bind $T <Selection> {
179	DemoOutlookNewsgroup::Selection %T
180    }
181
182    # Fix the display when a column is dragged
183    $T notify bind $T <ColumnDrag-receive> {
184	%T column move %C %b
185	DemoOutlookNewsgroup::FixItemStyles %T
186    }
187
188    # Fix the display when a column's visibility changes
189    $T notify bind $T <DemoColumnVisibility> {
190	DemoOutlookNewsgroup::FixItemStyles %T
191    }
192
193    return
194}
195
196proc DemoOutlookNewsgroup::Selection {T} {
197    variable Priv
198    # One item is selected
199    if {[$T selection count] == 1} {
200	if {[info exists Priv(afterId)]} {
201	    after cancel $Priv(afterId)
202	}
203	set Priv(afterId,item) [$T selection get 0]
204	set Priv(afterId) [after 500 DemoOutlookNewsgroup::MessageReadDelayed]
205    }
206    return
207}
208
209proc DemoOutlookNewsgroup::MessageReadDelayed {} {
210
211    variable Priv
212
213    set T [DemoList]
214
215    unset Priv(afterId)
216    set I $Priv(afterId,item)
217    if {![$T selection includes $I]} return
218
219    # This message is not read
220    if {!$Priv(read,$I)} {
221
222	# Read the message
223	$T item state set $I read
224	set Priv(read,$I) 1
225
226	# Check ancestors (except root)
227	foreach I2 [lrange [$T item ancestors $I] 0 end-1] {
228
229	    # This ancestor has no more unread descendants
230	    if {![AnyUnreadDescendants $T $I2]} {
231		$T item state set $I2 !unread
232	    }
233	}
234    }
235    return
236}
237
238# Alternate implementation that does not rely on run-time states
239proc DemoOutlookNewsgroup::Init_2 {T} {
240
241    global Message
242
243    InitPics outlook-*
244
245    set height [font metrics [$T cget -font] -linespace]
246    if {$height < 18} {
247	set height 18
248    }
249
250    #
251    # Configure the treectrl widget
252    #
253
254    $T configure -itemheight $height -selectmode browse \
255	-showroot no -showrootbutton no -showbuttons yes -showlines no
256
257    #
258    # Create columns
259    #
260
261    $T column create -image outlook-clip -tags clip
262    $T column create -image outlook-arrow -tags arrow
263    $T column create -image outlook-watch -tags watch
264    $T column create -text Subject -width 250 -tags subject
265    $T column create -text From -width 150 -tags from
266    $T column create -text Sent -width 150 -tags sent
267    $T column create -text Size -width 60 -justify right -tags size
268
269    $T configure -treecolumn 3
270
271    #
272    # Create elements
273    #
274
275    $T element create image.unread image -image outlook-unread
276    $T element create image.read image -image outlook-read
277    $T element create image.read2 image -image outlook-read-2
278    $T element create text.read text -fill [list $::SystemHighlightText {selected focus}] \
279	-lines 1
280    $T element create text.unread text -fill [list $::SystemHighlightText {selected focus}] \
281	-font [list DemoFontBold] -lines 1
282    $T element create sel.e rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open e -showfocus yes
283    $T element create sel.w rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open w -showfocus yes
284    $T element create sel.we rect -fill [list $::SystemHighlight {selected focus} gray {selected !focus}] -open we -showfocus yes
285
286    #
287    # Create styles using the elements
288    #
289
290    # Image + text
291    set S [$T style create unread]
292    $T style elements $S {sel.e image.unread text.unread}
293    $T style layout $S image.unread -expand ns
294    $T style layout $S text.unread -padx {2 6} -squeeze x -expand ns
295    $T style layout $S sel.e -union [list text.unread] -iexpand nes -ipadx {2 0}
296
297    # Image + text
298    set S [$T style create read]
299    $T style elements $S {sel.e image.read text.read}
300    $T style layout $S image.read -expand ns
301    $T style layout $S text.read -padx {2 6} -squeeze x -expand ns
302    $T style layout $S sel.e -union [list text.read] -iexpand nes -ipadx {2 0}
303
304    # Image + text
305    set S [$T style create read2]
306    $T style elements $S {sel.e image.read2 text.unread}
307    $T style layout $S image.read2 -expand ns
308    $T style layout $S text.unread -padx {2 6} -squeeze x -expand ns
309    $T style layout $S sel.e -union [list text.unread] -iexpand nes -ipadx {2 0}
310
311    # Text
312    set S [$T style create unread.we]
313    $T style elements $S {sel.we text.unread}
314    $T style layout $S text.unread -padx 6 -squeeze x -expand ns
315    $T style layout $S sel.we -detach yes -iexpand xy
316
317    # Text
318    set S [$T style create read.we]
319    $T style elements $S {sel.we text.read}
320    $T style layout $S text.read -padx 6 -squeeze x -expand ns
321    $T style layout $S sel.we -detach yes -iexpand xy
322
323    # Text
324    set S [$T style create unread.w]
325    $T style elements $S {sel.w text.unread}
326    $T style layout $S text.unread -padx 6 -squeeze x -expand ns
327    $T style layout $S sel.w -detach yes -iexpand xy
328
329    # Text
330    set S [$T style create read.w]
331    $T style elements $S {sel.w text.read}
332    $T style layout $S text.read -padx 6 -squeeze x -expand ns
333    $T style layout $S sel.w -detach yes -iexpand xy
334
335    #
336    # Create items and assign styles
337    #
338
339    set msgCnt 100
340
341    set thread 0
342    set Priv(count,0) 0
343    for {set i 1} {$i < $msgCnt} {incr i} {
344	$T item create
345	while 1 {
346	    set j [expr {int(rand() * $i)}]
347	    if {$j == 0} break
348	    if {[$T depth $j] == 5} continue
349	    if {$Priv(count,$Priv(thread,$j)) == 15} continue
350	    break
351	}
352	$T item lastchild $j $i
353
354	set Priv(read,$i) [expr rand() * 2 > 1]
355	if {$j == 0} {
356	    set Priv(thread,$i) [incr thread]
357	    set Priv(seconds,$i) [expr {[clock seconds] - int(rand() * 500000)}]
358	    set Priv(seconds2,$i) $Priv(seconds,$i)
359	    set Priv(count,$thread) 1
360	} else {
361	    set Priv(thread,$i) $Priv(thread,$j)
362	    set Priv(seconds,$i) [expr {$Priv(seconds2,$j) + int(rand() * 10000)}]
363	    set Priv(seconds2,$i) $Priv(seconds,$i)
364	    set Priv(seconds2,$j) $Priv(seconds,$i)
365	    incr Priv(count,$Priv(thread,$j))
366	}
367    }
368
369    for {set i 1} {$i < $msgCnt} {incr i} {
370	set subject "This is thread number $Priv(thread,$i)"
371	set from somebody@somewhere.net
372	set sent [clock format $Priv(seconds,$i) -format "%d/%m/%y %I:%M %p"]
373	set size [expr {1 + int(rand() * 10)}]KB
374	if {$Priv(read,$i)} {
375	    set style read
376	    set style2 read
377	} else {
378	    set style unread
379	    set style2 unread
380	}
381	$T item style set $i 3 $style 4 $style2.we 5 $style2.we 6 $style2.w
382	$T item text $i 3 $subject 4 $from 5 $sent 6 $size
383	if {[$T item numchildren $i]} {
384	    $T item configure $i -button yes
385	}
386    }
387
388    $T notify bind $T <Selection> {
389	if {[%T selection count] == 1} {
390	    set I [%T selection get 0]
391	    if {!$Priv(read,$I)} {
392		if {[%T item isopen $I] || ![AnyUnreadDescendants %T $I]} {
393		    # unread ->read
394		    %T item style map $I subject read {text.unread text.read}
395		    %T item style map $I from read.we {text.unread text.read}
396		    %T item style map $I sent read.we {text.unread text.read}
397		    %T item style map $I size read.w {text.unread text.read}
398		} else {
399		    # unread -> read2
400		    %T item style map $I subject read2 {text.unread text.unread}
401		}
402		set Priv(read,$I) 1
403		DisplayStylesInItem $I
404	    }
405	}
406    }
407
408    $T notify bind $T <Expand-after> {
409	if {$Priv(read,%I) && [AnyUnreadDescendants %T %I]} {
410	    # read2 -> read
411	    %T item style map %I subject read {text.unread text.read}
412	    # unread -> read
413	    %T item style map %I from read.we {text.unread text.read}
414	    %T item style map %I sent read.we {text.unread text.read}
415	    %T item style map %I size read.w {text.unread text.read}
416	}
417    }
418
419    $T notify bind $T <Collapse-after> {
420	if {$Priv(read,%I) && [AnyUnreadDescendants %T %I]} {
421	    # read -> read2
422	    %T item style map %I subject read2 {text.read text.unread}
423	    # read -> unread
424	    %T item style map %I from unread.we {text.read text.unread}
425	    %T item style map %I sent unread.we {text.read text.unread}
426	    %T item style map %I size unread.w {text.read text.unread}
427	}
428    }
429
430    for {set i 1} {$i < $msgCnt} {incr i} {
431	if {rand() * 2 > 1} {
432	    if {[$T item numchildren $i]} {
433		$T item collapse $i
434	    }
435	}
436    }
437
438    return
439}
440
441proc DemoOutlookNewsgroup::AnyUnreadDescendants {T I} {
442
443    variable Priv
444
445    foreach item [$T item descendants $I] {
446	if {!$Priv(read,$item)} {
447	    return 1
448	}
449    }
450    return 0
451}
452
453proc DemoOutlookNewsgroup::FixItemStyles {T} {
454
455    set columns1 [$T column id "visible tag clip||arrow||watch !tail"]
456    set columns2 [$T column id "visible tag !(clip||arrow||watch) !tail"]
457
458    foreach C [$T column id "visible !tail"] {
459
460	# The clip/arrow/watch columns only get a style when they are
461	# between the first and last text-containing columns.
462	if {[lsearch -exact $columns1 $C] != -1} {
463	    if {[$T column compare $C > [lindex $columns2 0]] &&
464		[$T column compare $C < [lindex $columns2 end]]} {
465		$T item style set all $C s2
466		$T item state forcolumn all $C {!openW !openE openWE}
467	    } else {
468		$T item style set all $C ""
469	    }
470	    continue
471	}
472
473	# The text-containing columns need their styles set such that the
474	# active outline of the selected item extends from left to right.
475	# Also, the left-most text-containing column is the tree column
476	# and displays the icon.
477	if {$C eq [lindex $columns2 0]} {
478	    $T configure -treecolumn $C
479	    set S s1
480	    set state openE
481	} elseif {$C eq [lindex $columns2 end]} {
482	    set S s2
483	    set state openW
484	} else {
485	    set S s2
486	    set state openWE
487	}
488	$T item state forcolumn all $C [list !openWE !openE !openW $state]
489
490	# Change the style, but keep the text so we don't have to reset it.
491	$T item style map all $C $S {elemTxt elemTxt}
492    }
493    return
494}
495