Fix order of shogi players.
[kaya.git] / lib / mainwindow.rb
blobaa68c9a639d6383c13f3e94e9a622da5f2fd48f4
1 require 'qtutils'
2 require 'board/board'
3 require 'board/pool'
4 require 'board/table'
5 require 'board/scene'
6 require 'history'
7 require 'controller'
9 require 'interaction/match'
11 require 'ics/protocol'
12 require 'ics/match_handler'
13 require 'ics/connection'
14 require 'console'
16 require 'filewriter'
17 require 'newgame'
19 class MainWindow < KDE::XmlGuiWindow
20   include ActionHandler
21   include FileWriter
23   def initialize(loader, game)
24     super nil
25     
26     @loader = loader
27     @default_game = game
28     
29     startup
30     setup_actions
31     setupGUI
32     new_game(Match.new(game))
33   end
35 private
37   def setup_actions
38     std_action(:open_new) { create_game }
39     std_action(:open) { load_game }
40     std_action :quit, :slot => :close
41     std_action(:save) { save_game }
42     std_action(:saveAs) { save_game_as }
43     
44     regular_action :back, :icon => 'go-previous', 
45                           :text => KDE.i18n("B&ack") do
46       @controller.back
47     end
48     regular_action :forward, :icon => 'go-next', 
49                              :text => KDE.i18n("&Forward") do
50       @controller.forward
51     end
52     regular_action :connect, :icon => 'network-connect',
53                              :text => KDE.i18n("&Connect to ICS") do
54       connect_to_ics
55     end
56     regular_action :disconnect, :icon => 'network-disconnect',
57                                 :text => KDE.i18n("&Disconnect from ICS") do
58       if @connection
59         @connection.close
60         @connection = nil
61       end
62     end
63     
64     regular_action :flip, :icon => 'object-rotate-left',
65                           :text => KDE.i18n("F&lip") do
66       @table.flip(! @table.flipped?)
67     end
68   end
69   
70   def startup
71     scene = Scene.new
72     @table = Table.new scene, @loader, self
73     @controller = Controller.new(@table)
74     @table.observe(:reset) do |match|
75       update_game_actions(match)
76     end
77     @engine_loader = @loader.get_matching(:engine_loader).new(@loader)
79     movelist = @loader.get_matching(:movelist).new(@controller)
80     movelist_dock = Qt::DockWidget.new(self)
81     movelist_dock.widget = movelist
82     movelist_dock.window_title = KDE.i18n("History")
83     movelist_dock.object_name = "movelist"
84     add_dock_widget(Qt::LeftDockWidgetArea, movelist_dock, Qt::Vertical)
85     movelist_dock.show
87     @console = Console.new(nil)
88     console_dock = Qt::DockWidget.new(self)                                                      
89     console_dock.widget = @console                                                             
90     console_dock.focus_proxy = @console                                                        
91     console_dock.window_title = KDE.i18n("Console")                                              
92     console_dock.object_name = "console"                                                         
93     add_dock_widget(Qt::BottomDockWidgetArea, console_dock, Qt::Horizontal)                      
94     console_dock.window_flags = console_dock.window_flags & ~Qt::WindowStaysOnTopHint            
95     console_dock.show
96     
97     self.central_widget = @table
98   end
99   
100   def connect_to_ics
101     protocol = ICS::Protocol.new(:debug)
102     @connection = ICS::Connection.new('freechess.org', 23)
103     config = KDE::Global.config.group("ICS")
104     protocol.add_observer ICS::AuthModule.new(@connection, 
105       config.read_entry('username', 'guest'), 
106       config.read_entry('password', ''))
107     protocol.add_observer ICS::StartupModule.new(@connection)
108     protocol.link_to @connection
110     protocol.observe :text do |text|
111       @console.append(text)
112     end
114     @console.observe :input do |text|
115       @connection.send_text text
116     end
118     handler = ICS::MatchHandler.new(@controller, protocol)
120     @connection.start
121   end
122   
123   def new_game(match)
124     setup_single_player(match)
125     @controller.reset(match)
126   end
127   
128   def setup_single_player(match)
129     @controller.color = match.game.players.first
130     opponents = match.game.players[1..-1].map do |color|
131       DummyPlayer.new(color)
132     end
133     opponents.each do |p| 
134       @controller.add_controlled_player(p)
135     end
137     @controller.controlled.values.each do |p|
138       match.register(p)
139     end
140     @controller.controlled.values.each do |p|
141       match.start(p)
142     end
143   end
145   def create_game
146     current_game = if @controller.match 
147       @controller.match.game
148     end
149     diag = NewGame.new(self, @engine_loader, current_game)
150     diag.observe(:ok) do |data|
151       game = data[:game]
152       match = Match.new(game)
153       
154       match.observe(:started) { @controller.reset(match) }
155       
156       # set up engine players
157       players = game.players
158       data[:engines].each do |player, engine|
159         e = engine.new(player, match)
160         e.start
161       end
162       
163       # set up human players
164       if data[:humans].empty?
165         @controller.color = nil
166       else
167         @controller.color = data[:humans].first
168         match.register(@controller)
169         
170         data[:humans][1..-1].each do |player|
171           p = DummyPlayer.new(player)
172           @controller.add_controlled_player(p)
173           match.register(p)
174         end
175       end
176       @controller.controlled.values.each {|p| match.start(p) }
177     end
178     diag.show
179   end
181   def load_game
182     url = KDE::FileDialog.get_open_url(KDE::Url.new, '*.*', self,
183       KDE.i18n("Open game"))
184     unless url.is_empty
185       # find readers
186       ext = File.extname(url.path)[1..-1]
187       return unless ext
188       readers = Game.to_enum.find_all do |_, game|
189         game.respond_to?(:game_extensions) and
190         game.game_extensions.include?(ext)
191       end.map do |_, game|
192         [game, game.game_reader]
193       end
194       
195       if readers.empty?
196         warn "Unknown file extension #{ext}"
197         return
198       end
199       
200       tmp_file = ""
201       return unless KIO::NetAccess.download(url, tmp_file, self)
203       history = nil
204       game = nil
205       info = nil
206       
207       readers.each do |g, reader|
208         begin
209           data = File.open(tmp_file) do |f|
210             f.read
211           end
212           i = {}
213           history = reader.read(data, i)
214           game = g
215           info = i
216           break
217         rescue ParseException
218         end
219       end
220       
221       unless history
222         warn "Could not load file #{url.path}"
223         return
224       end
225       
226       # create game
227       match = Match.new(game)
228       setup_single_player(match)
229       match.history = history
230       match.add_info(info)
231       match.url = url
232       @controller.reset(match)
233     end
234   end
235   
236   def save_game_as
237     match = @controller.match
238     if match
239       pattern = if match.game.respond_to?(:game_extensions)
240         match.game.game_extensions.map{|ext| "*.#{ext}"}.join(' ')
241       else
242         '*.*'
243       end
244       url = KDE::FileDialog.get_save_url(
245         KDE::Url.new, pattern, self, KDE.i18n("Save game"))
246       match.url = write_game(url)
247     end
248   end
249   
250   def save_game
251     match = @controller.match
252     if match
253       if match.url
254         write_game
255       else
256         save_game_as
257       end
258     end
259   end
260   
261   def write_game(url = nil)
262     match = @controller.match
263     if match
264       url ||= match.url
265       writer = match.game.game_writer
266       info = match.info
267       info[:players] = info[:players].inject({}) do |res, pl|
268         res[pl.color] = pl.name
269         res
270       end
271       result = writer.write(info, match.history)
272       write_file(url, result)
273     end
274   end
275   
276   def update_game_actions(match)
277     unplug_action_list('game_actions')
278     actions = if match.game.respond_to?(:actions)
279       match.game.actions(self, action_collection, @controller.policy)
280     else
281       []
282     end
283     plug_action_list('game_actions', actions)
284   end
287 class DummyPlayer
288   include Observer
289   include Player
290   
291   attr_reader :color
292   attr_accessor :name
293   
294   def initialize(color)
295     @color = color
296   end