Passed
Pull Request — master (#291)
by Marek
01:57
created

StarMapWidget.onObjectSelected()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 4
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
#
3
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
4
#
5
#  This file is part of Outer Space.
6
#
7
#  Outer Space is free software; you can redistribute it and/or modify
8
#  it under the terms of the GNU General Public License as published by
9
#  the Free Software Foundation; either version 2 of the License, or
10
#  (at your option) any later version.
11
#
12
#  Outer Space is distributed in the hope that it will be useful,
13
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
#  GNU General Public License for more details.
16
#
17
#  You should have received a copy of the GNU General Public License
18
#  along with Outer Space; if not, write to the Free Software
19
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
#
21
import bisect
22
23
from pygameui.Widget import Widget, registerWidget
24
import pygameui as ui
25
from pygameui import Fonts
26
import ige.ospace.Const as Const
27
import pygame, pygame.draw, pygame.key, pygame.image
28
from dialog.ShowBuoyDlg import ShowBuoyDlg
29
from dialog.KeyModHelp import KeyModHelp
30
import gdata, client, res
31
from ige import log
32
from osci.dialog.SearchDlg import SearchDlg
33
from osci.MiniMap import MiniMap
34
from osci.StarMap import StarMap
35
36
37
class StarMapWidget(Widget):
38
39
    def __init__(self, parent, **kwargs):
40
        Widget.__init__(self, parent)
41
        self.searchDlg = SearchDlg(self.app)
42
        self.searchDlg.mapWidget = self
43
        # data
44
        self.control_modes = {}  # mutable, thus updating here will update StarMap
45
        self.updateConfigModes()
46
        self.control_modes['systems'] = 1
47
        self.control_modes['planets'] = 1
48
        self.control_modes['fleets'] = 1
49
        self.control_modes['civilian_fleets'] = 1
50
        self.control_modes['pirate_areas'] = 1
51
        self.control_modes['hotbuttons'] = 1
52
        self.control_modes['minimap'] = 1
53
        self.control_modes['redirects'] = 1
54
        self.control_modes['map_grid_coords'] = 1
55
        self.control_modes['map_grid'] = 1
56
        self.control_modes['scanners'] = 1
57
        self.control_modes['fleet_lines'] = 1
58
        self.control_modes['gate_systems'] = 1
59
        self.control_modes['alternative_view'] = 1
60
        self.control_modes['control_areas'] = 0
61
        self.control_modes['pirate_dialogs'] = 0  # only for pirate, obv.
62
        # more data
63
        self.highlightPos = None
64
        self.alwaysShowRangeFor = None
65
        self.activeObjID = Const.OID_NONE
66
        self.activeObjIDs = []
67
        self.pressedObjIDs = []
68
        self._newCurrXY = 0
69
        self.activePos = (0, 0)
70
71
        # the map itself!
72
        self.star_map = StarMap(self.control_modes)
73
        self.action = None
74
        self.callEventHandler = None
75
        self.showBuoyDlg = ShowBuoyDlg(self.app)
76
        self.KeyModHelp = KeyModHelp(self.app)
77
        self._miniMapRect = pygame.Rect(0, 20, 175, 175)
78
        self._hotbuttonsZone = pygame.Rect(0,0,0,0)
79
        self.initHotbuttons()
80
        self.miniMap = MiniMap(self._miniMapRect.width, self._miniMapRect.height)
81
        # flags
82
        self.processKWArguments(kwargs)
83
        parent.registerWidget(self)
84
        # popup menu
85
        self.popup = ui.Menu(self.app, title = _("Select object"))
86
        self.popup.subscribeAction("*", self)
87
        # overlay system
88
        self._overlayZone = False
89
        # key setting system
90
        self.selectobject = False
91
        self.setKey = False
92
        # commands
93
        self.keyPress = False
94
        # map - produced by StarMap draw method
95
        self._mapSurf = None
96
        self._actAreas = {}
97
        self._actBuoyAreas = {}
98
        # dirty flag
99
        self.repaint_map = True
100
101
102
    def updateConfigModes(self):
103
        self.control_modes['redirects'] = gdata.config.defaults.showredirects is not 'no'
104
        self.control_modes['coords'] = gdata.config.defaults.showcoords is not 'no'
105
        self.control_modes['map_grid'] = gdata.config.defaults.showmapgrid is not 'no'
106
        self.control_modes['scanners'] = gdata.config.defaults.showmapscanners is not 'no'
107
        self.control_modes['fleet_lines'] = gdata.config.defaults.showfleetlines is not 'no'
108
        self.control_modes['gate_systems'] = gdata.config.defaults.showgatesystems is not 'no'
109
        self.control_modes['alternative_mode'] = gdata.config.defaults.alternateviewmode is not 'no'
110
        self.control_modes['control_areas'] = gdata.config.defaults.showplayerzones is not 'no'
111
        self.control_modes['minimap'] = gdata.config.defaults.showminimap is not 'yes'
112
113
    def precompute(self):
114
        self.star_map.rect = self.rect
115
        self.star_map.precompute()
116
        player = client.getPlayer()
117
        if (player.type == Const.T_PIRPLAYER or\
118
            player.type == Const.T_AIPIRPLAYER) and not self.control_modes['pirate_dialogs']:
119
            self.control_modes['pirate_dialogs'] = True
120
            if self.control_modes['hotbuttons']:
121
                self.initHotbuttons() #reinit to add the pirate button
122
        self.miniMap.precompute()
123
        # self dirty flag
124
        self.repaint_map = 1
125
126
    def save(self, append='', chronicle_shot=False):
127
        name = ("%s.png" % append)
128
        if chronicle_shot:
129
            # print whole galaxy, centered over black hole
130
            # star_map has much more information about the galaxy, thus handling this request
131
            new_surf = self.star_map.chronicle_draw()
132
            pygame.image.save(new_surf, name)
133
        else:
134
            # print current player view
135
            new_surf, empty, empty = self.star_map.draw(pygame.Surface((self.star_map.rect.width,
136
                                                                        self.star_map.rect.height)))
137
            pygame.image.save(new_surf, name)
138
        return name
139
140
141
    def draw(self, surface):
142
        self._miniMapRect.left = self.rect.width - self._miniMapRect.width
143
        self._miniMapRect.top = self.rect.top
144
        if not self._mapSurf:
145
            mapSurf = pygame.Surface(self.rect.size, pygame.SWSURFACE, surface)
146
        else:
147
            mapSurf = self._mapSurf
148
149
        if self.repaint_map:
150
            mapSurf, self._actAreas, self._actBuoyAreas  = self.star_map.draw(mapSurf)
151
            # For some reason, this is not just optimization, it's mandatory for proper
152
            # function. BUG?!
153
            self.repaint_map = 0
154
            self.repaintHotbuttons = 1
155
        if self.repaintHotbuttons and self.control_modes['hotbuttons']:
156
            self.drawHotbuttons(mapSurf)
157
            self.repaintHotbuttons = 0
158
        # blit cached map
159
        surface.blit(mapSurf, self.rect)
160
        self._mapSurf = mapSurf
161
162
        if self.control_modes['minimap']:
163
            self.miniMap.draw(surface, self._miniMapRect.left, self._miniMapRect.top)
164
            if self.miniMap.needRect():
165
                self.processMiniMapRect()
166
                self.miniMap.draw(surface, self._miniMapRect.left, self._miniMapRect.top)
167
        # additional information (ranges, fleet lines, selected system sign)
168
        self.drawAdditions(surface)
169
170
        self.drawPopups(surface)
171
        return self.rect
172
173
    def drawHotbuttons(self, mapSurf):
174
        rect = mapSurf.get_rect()
175
        bottom = rect.bottom
176
        right = rect.right
177
        dx = 137
178
        dy = 46
179
        top = bottom - dy - 1
180
        left = right - dx - 1
181
        self._hotbuttonsZone.top = top + self.rect.top
182
        self._hotbuttonsZone.left = left
183
        self._hotbuttonsZone.width = dx
184
        self._hotbuttonsZone.height = dy
185
186
        pygame.draw.rect(mapSurf,(0x00, 0x00, 0x90),(left-1,top-1,dx+2,dy+2))
187
        pygame.draw.rect(mapSurf,(0x33, 0x33, 0x66),(left,top,dx,dy))
188
189
        for buttonkey in self._hotbuttons:
190
            button = self._hotbuttons[buttonkey]
191
            self._hotbuttonRects[button[0]] = [button[0],pygame.Rect(button[2]+self._hotbuttonsZone.left,button[3]+self._hotbuttonsZone.top+15,button[4],button[5])]
192
            img = res.getButton(button[0],button[1])
193
            if (button[1] and not (self._tempOverlayHotbutton and self._tempOverlayHotbutton == button[0])) or (not button[1] and self._tempOverlayHotbutton and self._tempOverlayHotbutton == button[0]):
194
                pygame.draw.rect(mapSurf,(0x90, 0x90, 0x90),(left+button[2]-1,top+15+button[3]-1,button[4]+2,button[5]+2),1)
195
            mapSurf.blit(img,(left+button[2],top+15+button[3]))
196
        if self._tempOverlayHotbutton:
197
            text = self._hotbuttons[self._tempOverlayHotbutton][7]
198
            textSrfc = Fonts.renderText(self.star_map.textSize, text, 1, (0xEF, 0xEF, 0xEF))
199
            mapSurf.blit(textSrfc, (left+2,top+1))
200
201
    def drawPopups(self, surface):
202
        # draw popups
203
        moreIDs = len(self.activeObjIDs) > 1
204
        if not moreIDs:
205
            x, y = self.activePos
206
            x += 20
207
        else:
208
            x = self.rect.left + 2
209
            y = self.rect.top
210
        if not pygame.key.get_mods() & pygame.KMOD_SHIFT:
211
            for activeObjID in self.activeObjIDs:
212
                index = 0
213
                if self.star_map._popupInfo.has_key(activeObjID):
214
                    # put pop up info on the screen
215
                    info = self.star_map._popupInfo[activeObjID]
216
                    # x1, y1 = self._actAreas[self.activeObjID].center
217
                    fg = self.theme.themeForeground #(0x30, 0xe0, 0x30, 0xff)
218
                    bg = self.theme.themeBackground #(0x20, 0x40, 0x20, 0x99)
219
                    width = 0
220
                    height = 0
221
                    # pygame.draw.line(surface, fg, (x1, y1), (x, y), 1)
222
                    for item in info:
223
                        w, h = Fonts.getTextSize('normal', item)
224
                        width = max(width, w)
225
                        height += h
226
                    if not moreIDs:
227
                        if x + width >= self.rect.width:
228
                            x -= width + 40
229
                        if y + 1 + height >= self.rect.height:
230
                            y -= height
231
                    surface.fill(bg, (x, y, width + 2, height + 2))
232
                    x += 1
233
                    tmpY = y + 1
234
                    for item in info:
235
                        textSrfc = Fonts.renderText('normal', item, 1, fg)
236
                        surface.blit(textSrfc, (x, tmpY))
237
                        tmpY += textSrfc.get_height()
238
                    x += width + 2
239
240
    def _drawApproachingFleetLine(self, surface, activeObjID):
241
        maxY = self._mapSurf.get_rect().height
242
        centerX, centerY = self._mapSurf.get_rect().center
243
        x, y, x1, y1 = self.star_map._fleetTarget[activeObjID]
244
        sx = int((x - self.star_map.currX) * self.star_map.scale) + centerX + self.rect.left
245
        sy = maxY - (int((y - self.star_map.currY) * self.star_map.scale) + centerY) + self.rect.top
246
        dx = int((x1 - self.star_map.currX) * self.star_map.scale) + centerX + self.rect.left
247
        dy = maxY - (int((y1 - self.star_map.currY) * self.star_map.scale) + centerY) + self.rect.top
248
        pygame.draw.line(surface, (0xff, 0xff, 0x00), (sx, sy), (dx, dy), 2)
249
250
    def _drawThickeningFleetOrderLines(self, surface, activeObjID):
251
        maxY = self._mapSurf.get_rect().height
252
        centerX, centerY = self._mapSurf.get_rect().center
253
        for x, y, x1, y1, color in self.star_map._fordersTarget[activeObjID]:
254
            sx = int((x - self.star_map.currX) * self.star_map.scale) + centerX + self.rect.left
255
            sy = maxY - (int((y - self.star_map.currY) * self.star_map.scale) + centerY) + self.rect.top
256
            dx = int((x1 - self.star_map.currX) * self.star_map.scale) + centerX + self.rect.left
257
            dy = maxY - (int((y1 - self.star_map.currY) * self.star_map.scale) + centerY) + self.rect.top
258
            pygame.draw.line(surface, color, (sx, sy), (dx, dy), 2)
259
260
    def _drawFleetRangesTime(self, surface, activeObjID):
261
        maxY = self._mapSurf.get_rect().height
262
        centerX, centerY = self._mapSurf.get_rect().center
263
        x, y, maxRange, operRange, halfRange, speed, turns = self.star_map._fleetRanges[activeObjID]
264
        sx = int((x - self.star_map.currX) * self.star_map.scale) + centerX + self.rect.left
265
        sy = maxY - (int((y - self.star_map.currY) * self.star_map.scale) + centerY) + self.rect.top
266
267
        for i in xrange(1, turns / 6):
268
            rng = int(i * speed * self.star_map.scale)
269
            if rng > 1:
270
                pygame.draw.circle(surface, (0x70, 0x70, 0x80), (sx, sy), rng, 1)
271
                textSrfc = Fonts.renderText(self.star_map.textSize, res.formatTime(i * 6), 1, (0x70, 0x70, 0x80), (0x00, 0x00, 0x00))
272
                surface.blit(textSrfc, (sx - rng, sy - textSrfc.get_height() / 2))
273
                surface.blit(textSrfc, (sx + rng, sy - textSrfc.get_height() / 2))
274
                surface.blit(textSrfc, (sx - textSrfc.get_width() / 2, sy - rng))
275
                surface.blit(textSrfc, (sx - textSrfc.get_width() / 2, sy + rng - textSrfc.get_height()))
276
        rng = int(max(maxRange * self.star_map.scale, 0.2 * self.star_map.scale))
277
        if rng > 1:
278
            pygame.draw.circle(surface, (0xc0, 0x20, 0x20), (sx, sy), rng, 1)
279
280
    def _drawFleetRangesFuel(self, surface, activeObjID):
281
        maxY = self._mapSurf.get_rect().height
282
        centerX, centerY = self._mapSurf.get_rect().center
283
        x, y, maxRange, operRange, halfRange, speed, turns = self.star_map._fleetRanges[activeObjID]
284
        sx = int((x - self.star_map.currX) * self.star_map.scale) + centerX + self.rect.left
285
        sy = maxY - (int((y - self.star_map.currY) * self.star_map.scale) + centerY) + self.rect.top
286
287
        # fleet range based on fuel
288
        rng = int(max(maxRange * self.star_map.scale, 0.2 * self.star_map.scale))
289
        if rng > 1:
290
            pygame.draw.circle(surface, (0xc0, 0x20, 0x20), (sx, sy), rng, 1)
291
        rng = int(operRange * self.star_map.scale)
292
        if rng > 1:
293
            pygame.draw.circle(surface, (0x20, 0x80, 0x20), (sx, sy), rng, 1)
294
        rng = int(halfRange * self.star_map.scale)
295
        if rng > 1:
296
            pygame.draw.circle(surface, (0x20, 0x20, 0x80), (sx, sy), rng, 1)
297
298
    def _drawFleetRanges(self, surface, activeObjID):
299
        if pygame.key.get_mods() & pygame.KMOD_SHIFT:
300
            self._drawFleetRangesTime(surface, activeObjID)
301
        else:
302
            self._drawFleetRangesFuel(surface, activeObjID)
303
304
    def drawAdditions(self, surface):
305
        oldClip = surface.get_clip()
306
        surface.set_clip(self.rect)
307
        centerX, centerY = self._mapSurf.get_rect().center
308
        maxY = self._mapSurf.get_rect().height
309
        # highlight position circle
310
        if self.highlightPos:
311
            sx = int((self.highlightPos[0] - self.star_map.currX) * self.star_map.scale) + centerX + self.rect.left
312
            sy = maxY - (int((self.highlightPos[1] - self.star_map.currY) * self.star_map.scale) + centerY) + self.rect.top
313
            pygame.draw.circle(surface, (0xff, 0xff, 0xff), (sx, sy), 13, 2)
314
        # fleet range in case of selecting fleet orders
315
        if self.alwaysShowRangeFor and self.star_map._fleetRanges.has_key(self.alwaysShowRangeFor):
316
            self._drawFleetRangesFuel(surface, self.alwaysShowRangeFor)
317
        for activeObjID in self.activeObjIDs:
318
            if activeObjID and activeObjID in self.star_map._fleetTarget:
319
                self._drawApproachingFleetLine(surface, activeObjID)
320
            if activeObjID and activeObjID in self.star_map._fordersTarget:
321
                self._drawThickeningFleetOrderLines(surface, activeObjID)
322
            if activeObjID and activeObjID in self.star_map._fleetRanges:
323
                self._drawFleetRanges(surface, activeObjID)
324
        # restore clipping
325
        surface.set_clip(oldClip)
326
327
    def initHotbuttons(self):
328
        # key : [ key , state , x , y , dx, dy, value, tooltip ]
329
        # 'value' is "active state' gdata value or true
330
        self._hotbuttons = {
331
            'pzone': ['pzone',self.control_modes['control_areas'],2,2,17,13, 1,_('Player Zones (CTRL-P)')],
332
            'civ': ['civ',self.control_modes['civilian_fleets'],21,2,18,13, 1,_('Civilian Ships (CTRL-H)')],
333
            'lines': ['lines',self.control_modes['fleet_lines'],41,2,18,13, 1,_('Fleet Lines (CTRL-L)')],
334
            'redir': ['redir',self.control_modes['redirects'],61,2,18,13, 1,_('Redirect Arrows (CTRL-R)')],
335
            'scanner': ['scanner',self.control_modes['scanners'],81,2,17,13, 1,_('Scanners (CTRL-S)')],
336
            'grid': ['grid',self.control_modes['map_grid'],100,2,17,13, 1,_('Grid (CTRL-G)')],
337
            'alternate': ['alternate',self.control_modes['alternative_mode'],119,2,17,13, 2,_('Alternate View (CTRL-A)')],
338
            'ov_diplo': ['ov_diplo',False,2,17,13,13, gdata.OVERLAY_DIPLO,_('Overlay: Diplomacy')],
339
            'ov_min': ['ov_min',False,17,17,13,13, gdata.OVERLAY_MIN,_('Overlay: Minerals')],
340
            'ov_env': ['ov_env',False,32,17,13,13, gdata.OVERLAY_BIO,_('Overlay: Environment')],
341
            'ov_slot': ['ov_slot',False,47,17,13,13, gdata.OVERLAY_SLOT,_('Overlay: Slots')],
342
            'ov_morale': ['ov_morale',False,62,17,13,13, gdata.OVERLAY_MORALE,_('Overlay: Morale')],
343
            'ov_fuel': ['ov_fuel',False,77,17,13,13, gdata.OVERLAY_DOCK,_('Overlay: Fuel and Repair')],
344
            'ov_gate': ['ov_gate',False,92,17,13,13, gdata.OVERLAY_STARGATE,_('Overlay: Star Gate Speed')],
345
            'ov_pirate': ['ov_pirate',False,107,17,13,13, gdata.OVERLAY_FAME,_('Overlay: Pirate Fame')],
346
        }
347
        if self.control_modes['pirate_dialogs']:
348
            self._hotbuttons['ov_piratecolony'] = ['ov_piratecolony',False,122,17,13,13, gdata.OVERLAY_PIRATECOLONYCOST,'Overlay: Pirate Colony Cost']
349
        self._oldOverlayHotbutton = False;
350
        self._tempOverlayHotbutton = False;
351
        self._hotbuttonRects = {}
352
353
    def toggleHotButtons(self,button):
354
        self.toggleTempButton(False)
355
        if (button[:3] == 'ov_'): #overlay
356
            if self._oldOverlayHotbutton == button:
357
                self.star_map.overlayMode = gdata.OVERLAY_OWNER
358
                self._hotbuttons[button][1] = False
359
                self._oldOverlayHotbutton = False
360
            else:
361
                if self._oldOverlayHotbutton:
362
                    self._hotbuttons[self._oldOverlayHotbutton][1] = False
363
                self._hotbuttons[button][1] = True
364
                self.star_map.overlayMode = self._hotbuttons[button][6]
365
                self._oldOverlayHotbutton = button
366
        else: #normal toggle
367
            if self._hotbuttons[button][1]:
368
                self._hotbuttons[button][1] = 0
369
            else:
370
                self._hotbuttons[button][1] = self._hotbuttons[button][6] # set standard value
371
            if button == 'pzone':
372
                self.control_modes['control_areas'] = self._hotbuttons[button][1]
373
            elif button == 'civ':
374
                self.control_modes['civilian_fleets'] = self._hotbuttons[button][1]
375
            elif button == 'lines':
376
                self.control_modes['fleet_lines'] = self._hotbuttons[button][1]
377
            elif button == 'redir':
378
                self.control_modes['redirects'] = self._hotbuttons[button][1]
379
            elif button == 'scanner':
380
                self.control_modes['scanners'] = self._hotbuttons[button][1]
381
            elif button == 'grid':
382
                self.control_modes['map_grid'] = self._hotbuttons[button][1]
383
            elif button == 'alternate':
384
                self.control_modes['alternative_mode'] = self._hotbuttons[button][1]
385
        self.repaintHotbuttons = 1
386
        self.repaint_map = 1
387
388
    def toggleTempButton(self,pos=False):
389
        if pos: # true unless we are no longer in the box, in which case we are resetting
390
            currentButton = self.detectButtonOverpass(pos)
391
            if currentButton == self._tempOverlayHotbutton: return
392
            if self._tempOverlayHotbutton:
393
                self._hotbuttons[self._tempOverlayHotbutton][1] = not self._hotbuttons[self._tempOverlayHotbutton][1]
394
            if not currentButton:
395
                self.repaintHotbuttons = 1
396
                self._tempOverlayHotbutton = False
397
                return
398
            self._hotbuttons[currentButton][1] = not self._hotbuttons[currentButton][1]
399
            self._tempOverlayHotbutton = currentButton
400
        elif self._tempOverlayHotbutton:
401
            self._hotbuttons[self._tempOverlayHotbutton][1] = not self._hotbuttons[self._tempOverlayHotbutton][1]
402
            self._tempOverlayHotbutton = False
403
        self.repaintHotbuttons = 1
404
405
    def detectButtonOverpass(self,pos):
406
        for buttonkey in self._hotbuttonRects:
407
            #log.debug(self._hotbuttonRects[buttonkey][1],pos)
408
            if self._hotbuttonRects[buttonkey][1].collidepoint(pos): return buttonkey
409
        return False
410
411
    def processMB1Down(self, evt):
412
        # handle SHIFT click as MB3
413
        mods = pygame.key.get_mods()
414
        if mods & pygame.KMOD_SHIFT:
415
            return self.processMB3Down(evt)
416
        pos = evt.pos
417
        # show current position for debugging
418
        # log.debug(pos)
419
        if self.control_modes['minimap']:
420
            if self._miniMapRect.collidepoint(pos):
421
                return ui.NoEvent
422
        if self.control_modes['hotbuttons'] and self._hotbuttonsZone.collidepoint(pos):
423
            return ui.NoEvent
424
        self.pressedObjIDs = []
425
        for objID in self._actAreas.keys():
426
            rect = self._actAreas[objID]
427
            if rect.collidepoint(pos):
428
                self.pressedObjIDs.append(objID)
429
430
        self.pressedBuoyObjIDs = []
431
        for objID in self._actBuoyAreas.keys():
432
            rect = self._actBuoyAreas[objID]
433
            if rect.collidepoint(pos):
434
                self.pressedBuoyObjIDs.append(objID)
435
436
        if self.pressedObjIDs or self.pressedBuoyObjIDs:
437
            return ui.NoEvent
438
        else:
439
            self.activeObjID = Const.OID_NONE
440
            return ui.NoEvent
441
442
    def processMB1Up(self, evt):
443
        # handle SHIFT click as MB3
444
        mods = pygame.key.get_mods()
445
        if mods & pygame.KMOD_SHIFT:
446
            return self.processMB3Up(evt)
447
        pos = evt.pos
448
        if self.control_modes['minimap']:
449
            if self._miniMapRect.collidepoint(pos):
450
                self.star_map.currX, self.star_map.currY = self.miniMap.processMB1Up((pos[0] - self._miniMapRect.left, self._miniMapRect.height - pos[1] + self._miniMapRect.top))
451
                self.processMiniMapRect()
452
                self.repaint_map = 1
453
                return ui.NoEvent
454
        if self.control_modes['hotbuttons'] and self._hotbuttonsZone.collidepoint(pos):
455
            button = self.detectButtonOverpass(pos)
456
            if button:
457
                self.toggleHotButtons(button)
458
            return ui.NoEvent
459
        objIDs = []
460
        for objID in self._actAreas.keys():
461
            rect = self._actAreas[objID]
462
            if rect.collidepoint(pos):
463
                objIDs.append(objID)
464
465
        bObjIDs = []
466
        for objID in self._actBuoyAreas.keys():
467
            rect = self._actBuoyAreas[objID]
468
            if rect.collidepoint(pos):
469
                bObjIDs.append(objID)
470
471
        if (objIDs or bObjIDs) and (self.pressedObjIDs == objIDs or self.pressedBuoyObjIDs == bObjIDs) and self.action:
472
            if self.selectobject:
473
                self.setKeyObject(objIDs,bObjIDs)
474
                return ui.NoEvent
475
            self.gotoObject(objIDs,bObjIDs)
476
            return ui.NoEvent
477
        else:
478
            self.activeObjID = Const.OID_NONE
479
            return ui.NoEvent
480
481
    def gotoObject(self,objIDs,bObjIDs):
482
        if len(objIDs) + len(bObjIDs) == 1:
483
            if len(objIDs) == 1:
484
                if self.selectobject:
485
                    return objIDs[0]
486
                self.processAction(self.action, objIDs[0])
487
                self.pressedObjIDs = []
488
            else:
489
                if self.selectobject:
490
                    return Const.OID_NONE
491
                self.showBuoyDlg.display(bObjIDs[0])
492
                self.pressedBuoyObjIDs = []
493
        else:
494
            # multiple objects -> post pop-up menu
495
            items = []
496
            for objID in objIDs:
497
                obj = client.get(objID)
498
                if obj.type == Const.T_SYSTEM:
499
                    name = getattr(obj, "name", None)
500
                    name = _("System: %s [ID: %d]") % (name or res.getUnknownName(), obj.oid)
501
                elif obj.type == Const.T_WORMHOLE:
502
                    name = getattr(obj, "name", None)
503
                    name = _("Worm hole: %s [ID: %d]") % (name or res.getUnknownName(), obj.oid)
504
                elif obj.type == Const.T_PLANET:
505
                    name = getattr(obj, "name", None)
506
                    name = _("Planet: %s [ID: %d]") % (name or res.getUnknownName(), obj.oid)
507
                elif obj.type == Const.T_FLEET:
508
                    if hasattr(obj,'customname') and obj.customname:
509
                        name = obj.customname
510
                    else:
511
                        name = getattr(obj, "name", None)
512
                    name = _("Fleet: %s [ID: %d]") % (name or res.getUnknownName(), obj.oid)
513
                else:
514
                    name = _("Unknown object [ID: %d]") % obj.oid
515
                item = ui.Item(name, action = "onObjectSelected", data = objID)
516
                items.append(item)
517
            for objID in bObjIDs:
518
                obj = client.get(objID)
519
                if obj.type == Const.T_SYSTEM:
520
                    name = getattr(obj, "name", None)
521
                    name = _("Buoy on system: %s [ID: %d]") % (name or res.getUnknownName(), obj.oid)
522
                elif obj.type == Const.T_WORMHOLE:
523
                    name = getattr(obj, "name", None)
524
                    name = _("Buoy on worm hole: %s [ID: %d]") % (name or res.getUnknownName(), obj.oid)
525
                else:
526
                    name = _("Buoy on unknown object [ID: %d]") % obj.oid
527
                item = ui.Item(name, action = "onBuoySelected", data = objID)
528
                items.append(item)
529
            self.popup.items = items
530
            self.popup.show()
531
        if self.selectobject:
532
            return Const.OID_NONE
533
534
    def onObjectSelected(self, widget, action, data):
535
        self.processAction(self.action, data)
536
537
    def onBuoySelected(self, widget, action, data):
538
        self.showBuoyDlg.display(data)
539
540
    def processMB3Down(self, evt):
541
        if self.control_modes['minimap']:
542
            if self._miniMapRect.collidepoint(evt.pos):
543
                return ui.NoEvent
544
        self._newCurrXY = 1
545
        return ui.NoEvent
546
547
    def processMB3Up(self, evt):
548
        if self._newCurrXY:
549
            x, y = evt.pos
550
            centerX, centerY = self._mapSurf.get_rect().center
551
            self.star_map.currX -= float(centerX - x) / self.star_map.scale
552
            self.star_map.currY += float(centerY - y) / self.star_map.scale
553
            self.processMiniMapRect()
554
            self.repaint_map = 1
555
            self._newCurrXY = 0
556
        return ui.NoEvent
557
558
    def processMiniMapRect(self):
559
        if self.control_modes['minimap']:
560
            rect = self._mapSurf.get_rect()
561
            self.miniMap.moveRect(self.star_map.currX, self.star_map.currY, rect.width / self.star_map.scale, rect.height / self.star_map.scale)
562
563
    def _rescaleMap(self, evt, delta):
564
        if not 10 < self.star_map.scale + delta < 80:
565
            return ui.NoEvent
566
        try:
567
            x, y = evt.pos
568
        except AttributeError:
569
            # keyboard rescale
570
            x, y = pygame.mouse.get_pos()
571
        centerX, centerY = self._mapSurf.get_rect().center
572
        sign = cmp(delta, 0)
573
        self.star_map.currX -= sign * float(centerX - x) * (1/ self.star_map.scale - 1 / (self.star_map.scale+delta))
574
        self.star_map.currY += sign * float(centerY - y) * (1/ self.star_map.scale - 1 / (self.star_map.scale+delta))
575
        self.star_map.scale += delta
576
        self.star_map.textSize = ['small', 'normal', 'large'][bisect.bisect([40, 60], self.star_map.scale)]
577
        self.repaint_map = 1
578
        self.processMiniMapRect()
579
580
    def processMWUp(self, evt):
581
        return self._rescaleMap(evt, 5)
582
583
    def processMWDown(self, evt):
584
        return self._rescaleMap(evt, -5)
585
586
    def processMMotion(self, evt):
587
        pos = evt.pos
588
        if self.control_modes['minimap']:
589
            if self._miniMapRect.collidepoint(pos):
590
                #log.debug('Minimap Rect Position');
591
                return ui.NoEvent
592
        if self.control_modes['hotbuttons'] and self._hotbuttonsZone.collidepoint(pos):
593
            #should give hotkey tooltips for this eventually
594
            self.toggleTempButton(pos)
595
            return ui.NoEvent
596
        elif self._tempOverlayHotbutton: # cleanup if cursor not in zone
597
            self.toggleTempButton(False)
598
        self.activeObjID = Const.OID_NONE
599
        self.activeObjIDs = []
600
        for objID in self._actAreas.keys():
601
            rect = self._actAreas[objID]
602
            if rect.collidepoint(pos):
603
                self.activeObjID = objID
604
                self.activeObjIDs.append(objID)
605
                self.activePos = pos
606
        return ui.NoEvent
607
608
    # put actually processing of key in "processKeyUp" using key pressed during "processKeyDown" to prevent erroneous double press detection when holding down CTRL, SHIFT, or ALT keys
609
    def processKeyDown(self, evt):
610
        self.keyPress = evt
611
        if self.callEventHandler:
612
            self.callEventHandler.processKeyDown(evt)
613
        return ui.NoEvent
614
615
    def _processObjectHotkeys(self, evt):
616
        if pygame.key.get_mods() & pygame.KMOD_CTRL:
617
            log.debug('Set Key:',evt.key)
618
            if gdata.config.defaults.displayhelp != 'no':
619
                self.KeyModHelp.show()
620
            self.selectobject = True
621
            self.setKey = evt.key
622
            self.app.setStatus(_("Select object to hotkey. ESC to cancel."))
623
        elif pygame.key.get_mods() & pygame.KMOD_SHIFT:
624
            log.debug('Focus Key:',evt.key)
625
            self.focusOnKeyObject(evt.key)
626
        else:
627
            log.debug('Goto Key:',evt.key)
628
            self.gotoKeyObject(evt.key)
629
        return ui.NoEvent
630
631
    def processKeyUp(self, evt2):
632
        if self.callEventHandler:
633
            self.callEventHandler.processKeyUp(evt2)
634
        evt = self.keyPress
635
        if not self.keyPress: return ui.NoEvent
636
        self.keyPress = False
637
        # ==== Object Hotkeys ====
638
        #I have not found unicode escape characters for Ctrl-0 through Ctrl-9, so using direct key reference (less preferred due to international keyboards)
639
        if evt.key in [49,50,51,52,53,54,55,56,57,48]:
640
            self._processObjectHotkeys(evt)
641
        # ==== Map and Dialog Hotkeys ====
642
        elif evt.key == pygame.K_ESCAPE and self.selectobject:
643
            log.debug('Canceled Key')
644
            if self.selectobject:
645
                self.app.setStatus(_("Ready."))
646
                self.selectobject = False
647
            return ui.NoEvent
648
        if not evt.unicode:
649
            # force update
650
            self.star_map.scale += 1
651
            self.star_map.scale -= 1
652
            return ui.NoEvent
653
        if evt.unicode in u'+=':
654
            self._rescaleMap(evt, 5)
655
        elif evt.unicode == u'-':
656
            self._rescaleMap(evt, -5)
657
        # Space Bar - Recenter
658
        elif evt.unicode == u' ':
659
            x, y = pygame.mouse.get_pos()
660
            centerX, centerY = self._mapSurf.get_rect().center
661
            self.star_map.currX -= float(centerX - x) / self.star_map.scale
662
            self.star_map.currY += float(centerY - y) / self.star_map.scale
663
            self.repaint_map = 1
664
            self._newCurrXY = 0
665
        # ==== Standard Hotkeys ====
666
        # Reserve CTRL-C for copy (future editor support)
667
        # Ctrl+F
668
        toggleMapping = {u'\x01': 'alternate',  # Alternative system info
669
                         u'\x07': 'grid',       # Grid
670
                         u'\x08': 'civ',        # Civilian ships
671
                         u'\x0C': 'lines',      # Fleet lines
672
                         u'\x10': 'pzone',      # Control areas
673
                         u'\x12': 'redir',      # Redirections
674
                         u'\x13': 'scanner'}    # Scanner circles
675
        if evt.unicode in toggleMapping and pygame.key.get_mods() & pygame.KMOD_CTRL:
676
            self.toggleHotButtons(toggleMapping[evt.unicode])
677
        # Ctrl+F to open the search (find) dialog
678
        elif evt.unicode == u'\x06' and pygame.key.get_mods() & pygame.KMOD_CTRL:
679
            self.searchDlg.display()
680
        # Reserve CTRL-V,X,and Z for paste, cut, and undo (future editor support)
681
        # ==== Else ====
682
        else:
683
            # force update
684
            self.star_map.scale += 1
685
            self.star_map.scale -= 1
686
        return ui.NoEvent
687
688
    def setKeyObject(self,objIDs,bObjIDs):
689
        objID = self.gotoObject(objIDs,bObjIDs)
690
        log.debug('Setting Key Object To:',objID)
691
        self.app.setStatus(_("Ready."))
692
        self.selectobject = False
693
        if (objID == Const.OID_NONE):
694
            return
695
        obj = client.get(objID)
696
        if obj.type in (Const.T_SYSTEM, Const.T_PLANET, Const.T_FLEET):
697
            gdata.objectFocus[self.setKey]=objID
698
699
    def gotoKeyObject(self,evtkey):
700
        if evtkey in gdata.objectFocus:
701
            objID = gdata.objectFocus[evtkey]
702
            self.processAction(self.action, objID)
703
            self.pressedObjIDs = []
704
705
    def focusOnKeyObject(self,evtkey):
706
        if evtkey in gdata.objectFocus:
707
            objID = gdata.objectFocus[evtkey]
708
            obj = client.get(objID, noUpdate = 1)
709
            if hasattr(obj, "x"):
710
                gdata.mainGameDlg.win.vStarMap.highlightPos = (obj.x, obj.y)
711
                gdata.mainGameDlg.win.vStarMap.setPos(obj.x, obj.y)
712
713
    def onMouseOver(self):
714
        self.mouseOver = 1
715
        try:
716
            self.parent.parent.setFocus(self)
717
        except:
718
            log.debug('Cannot refocus on starmap')
719
720
    def setPos(self, x, y):
721
        self.star_map.currX = x
722
        self.star_map.currY = y
723
        self.repaint_map = 1
724
        self.processMiniMapRect()
725
726
727
registerWidget(StarMapWidget, 'starmapwidget')
728