1# -*- coding: UTF-8 -*-
2
3from math import pi, cos, sin
4
5import cairo
6from gi.repository import GLib, Gtk, Gdk, Pango, PangoCairo, GObject
7
8from pychess.System import conf
9from pychess.Utils import formatTime
10from pychess.Utils.const import BLACK, WHITE, LOCAL, UNFINISHED_STATES, DRAW, WHITEWON, BLACKWON, UNKNOWN_STATE
11from . import preferencesDialog
12
13
14class ChessClock(Gtk.DrawingArea):
15    def __init__(self):
16        GObject.GObject.__init__(self)
17        self.connect("draw", self.expose)
18        self.names = [_("White"), _("Black")]
19
20        self.model = None
21        self.short_on_time = [False, False]
22        self.alarm_spin = conf.get("alarm_spin")
23
24        conf.notify_add("alarm_spin", self.on_alarm_spin)
25
26    def on_alarm_spin(self, *args):
27        self.alarm_spin = conf.get("alarm_spin")
28
29    def expose(self, widget, ctx):
30        context = widget.get_window().cairo_create()
31        clip_ext = context.clip_extents()
32
33        if clip_ext[0] > 0 or clip_ext[2] < self.get_allocated_width():
34            self.redraw_canvas()
35            return False
36
37        rec = Gdk.Rectangle()
38        rec.x, rec.y, rec.width, rec.height = clip_ext[0], clip_ext[1], \
39            clip_ext[2] - clip_ext[0], clip_ext[3] - clip_ext[1]
40        context.rectangle(rec.x, rec.y, rec.width, rec.height)
41        context.clip()
42        self.draw(context)
43        return False
44
45    def draw(self, context):
46
47        style_ctxt = self.get_style_context()
48        self.light = style_ctxt.lookup_color("p_light_color")[1]
49        self.dark = style_ctxt.lookup_color("p_dark_color")[1]
50        if not self.model:
51            return
52
53        # Draw graphical Clock. Should this be moved to preferences?
54        drawClock = True
55
56        rect = Gdk.Rectangle()
57        clip_ext = context.clip_extents()
58        rect.x, rect.y, rect.width, rect.height = clip_ext[0], clip_ext[1], \
59            clip_ext[2] - clip_ext[0], clip_ext[3] - clip_ext[1]
60        context.rectangle(rect.width / 2. * self.model.movingColor, 0,
61                          rect.width / 2., rect.height)
62
63        context.set_source_rgba(self.dark.red, self.dark.green, self.dark.blue,
64                                self.dark.alpha)
65        context.fill_preserve()
66        context.new_path()
67
68        time0 = self.names[0], self.formatedCache[WHITE]
69        layout0 = self.create_pango_layout(" %s: %s " % (time0))
70        layout0.set_font_description(Pango.FontDescription("Sans Serif 17"))
71
72        time1 = self.names[1], self.formatedCache[BLACK]
73        layout1 = self.create_pango_layout(" %s: %s " % (time1))
74        layout1.set_font_description(Pango.FontDescription("Sans Serif 17"))
75
76        dbl_max = max(layout1.get_pixel_size()[0], layout0.get_pixel_size()[0]) * 2
77        self.set_size_request(dbl_max + rect.height + 7, -1)
78
79        pangoScale = float(Pango.SCALE)
80
81        # Analog clock code.
82        def paintClock(player):
83            clock_y = rect.height / 2.
84            clock_x = clock_y + rect.width / 2. * player + 1
85            rec = rect.height / 2. - 3.5
86
87            context.arc(clock_x, clock_y, rec - 1, 0, 2 * pi)
88            linear = cairo.LinearGradient(clock_x - rec * 2, clock_y - rec * 2,
89                                          clock_x + rec * 2, clock_y + rec * 2)
90            linear.add_color_stop_rgba(0, 1, 1, 1, 0.3)
91            linear.add_color_stop_rgba(1, 0, 0, 0, 0.3)
92            # context.set_source_rgba( 0, 0, 0, .3)
93            context.set_source(linear)
94            context.fill()
95
96            linear = cairo.LinearGradient(clock_x - rec, clock_y - rec,
97                                          clock_x + rec, clock_y + rec)
98            linear.add_color_stop_rgba(0, 0, 0, 0, 0.5)
99            linear.add_color_stop_rgba(1, 1, 1, 1, 0.5)
100            context.arc(clock_x, clock_y, rec, 0, 2 * pi)
101            context.set_source(linear)
102            context.set_line_width(2.5)
103            context.stroke()
104
105            starttime = float(self.model.getInitialTime()) or 1
106            used = self.model.getPlayerTime(player) / starttime
107            if used > 0:
108                if used > 0:
109                    context.arc(clock_x, clock_y, rec - .8, -(used + 0.25) * 2 * pi, -0.5 *
110                                pi)
111                    context.line_to(clock_x, clock_y)
112                    context.close_path()
113                elif used == 0:
114                    context.arc(clock_x, clock_y, rec - .8, -0.5 * pi, 1.5 * pi)
115                    context.line_to(clock_x, clock_y)
116
117                radial = cairo.RadialGradient(clock_x, clock_y, 3, clock_x, clock_y, rec)
118                if player == 0:
119                    # radial.add_color_stop_rgb(0, .73, .74, .71)
120                    radial.add_color_stop_rgb(0, .93, .93, .92)
121                    radial.add_color_stop_rgb(1, 1, 1, 1)
122                else:
123                    # radial.add_color_stop_rgb(0, .53, .54, .52)
124                    radial.add_color_stop_rgb(0, .18, .20, .21)
125                    radial.add_color_stop_rgb(1, 0, 0, 0)
126                context.set_source(radial)
127                context.fill()
128
129                x_loc = clock_x - cos((used - 0.25) * 2 * pi) * (rec - 1)
130                y_loc = clock_y + sin((used - 0.25) * 2 * pi) * (rec - 1)
131                context.move_to(clock_x, clock_y - rec + 1)
132                context.line_to(clock_x, clock_y)
133                context.line_to(x_loc, y_loc)
134                context.set_line_width(0.2)
135                if player == 0:
136                    context.set_source_rgb(0, 0, 0)
137                else:
138                    context.set_source_rgb(1, 1, 1)
139                context.stroke()
140
141        if drawClock:
142            paintClock(WHITE)
143        if (self.model.movingColor or WHITE) == WHITE:
144            context.set_source_rgba(self.light.red, self.light.green,
145                                    self.light.blue, self.light.alpha)
146        else:
147            context.set_source_rgba(self.dark.red, self.dark.green,
148                                    self.dark.blue, self.dark.alpha)
149        y_loc = rect.height / 2. - layout0.get_extents()[0].height / pangoScale / 2 \
150            - layout0.get_extents()[0].y / pangoScale
151        context.move_to(rect.height - 7, y_loc)
152        PangoCairo.show_layout(context, layout0)
153
154        if drawClock:
155            paintClock(BLACK)
156        if self.model.movingColor == BLACK:
157            context.set_source_rgba(self.light.red, self.light.green,
158                                    self.light.blue, self.light.alpha)
159        else:
160            context.set_source_rgba(self.dark.red, self.dark.green,
161                                    self.dark.blue, self.dark.alpha)
162        y_loc = rect.height / 2. - layout0.get_extents()[0].height / pangoScale / 2 \
163            - layout0.get_extents()[0].y / pangoScale
164        context.move_to(rect.width / 2. + rect.height - 7, y_loc)
165        PangoCairo.show_layout(context, layout1)
166
167    def redraw_canvas(self):
168        def do_redraw_canvas():
169            if self.get_window():
170                allocation = self.get_allocation()
171                rect = Gdk.Rectangle()
172                rect.x, rect.y, rect.width, rect.height = (0, 0, allocation.width,
173                                                           allocation.height)
174                self.get_window().invalidate_rect(rect, True)
175                self.get_window().process_updates(True)
176
177        GLib.idle_add(do_redraw_canvas)
178
179    def setModel(self, model):
180        self.model = model
181        self.model.connect("time_changed", self.time_changed)
182        self.model.connect("player_changed", self.player_changed)
183        self.formatedCache = [formatTime(self.model.getPlayerTime(
184            self.model.movingColor or WHITE))] * 2
185        if model.secs != 0 or model.gain != 0:
186            GLib.timeout_add(100, self.update)
187
188    def time_changed(self, model):
189        self.update()
190
191    def player_changed(self, model):
192        self.redraw_canvas()
193
194    def update(self, wmovecount=-1, bmovecount=-1):
195        if self.model.ended:
196            return False
197        if len(self.model.gamemodel.players) < 2:
198            return not self.model.ended
199        alarm_time = int(self.alarm_spin)
200        if self.model.getPlayerTime(self.model.movingColor) <= alarm_time and \
201            self.model.gamemodel.players[self.model.movingColor].__type__ == LOCAL and \
202            self.model.gamemodel.status in UNFINISHED_STATES and \
203            self.model.secs != 0 and \
204            self.model.gamemodel.endstatus not in (DRAW, WHITEWON, BLACKWON, UNKNOWN_STATE) and \
205                not self.short_on_time[self.model.movingColor]:
206            self.short_on_time[self.model.movingColor] = True
207            preferencesDialog.SoundTab.playAction("shortOnTime")
208
209        if self.model.paused and wmovecount == -1 and bmovecount == -1:
210            return not self.model.ended
211        white_time = formatTime(self.model.getPlayerTime(WHITE, wmovecount))
212        black_time = formatTime(self.model.getPlayerTime(BLACK, bmovecount))
213        if self.formatedCache != [white_time, black_time]:
214            self.formatedCache = [white_time, black_time]
215            self.redraw_canvas()
216        return not self.model.ended
217