New game dialog allows selecting engines.
[kaya.git] / lib / mainwindow.rb
blobb512dfeda06598a4770365a62bcdb86fface8682
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     diag = NewGame.new(self, @engine_loader)
147     diag.observe(:ok) do |data|
148       game = data[:game]
149       match = Match.new(game)
150       
151       match.observe(:started) { @controller.reset(match) }
152       
153       # set up engine players
154       players = game.players
155       data[:engines].each do |player, engine|
156         e = engine.new(player, match)
157         e.start
158       end
159       
160       # set up human players
161       if data[:humans].empty?
162         @controller.color = nil
163       else
164         @controller.color = data[:humans].first
165         match.register(@controller)
166         
167         data[:humans][1..-1].each do |player|
168           p = DummyPlayer.new(player)
169           @controller.add_controlled_player(p)
170           match.register(p)
171         end
172       end
173       @controller.controlled.values.each {|p| match.start(p) }
174     end
175     diag.show
176   end
178   def load_game
179     url = KDE::FileDialog.get_open_url(KDE::Url.new, '*.*', self,
180       KDE.i18n("Open game"))
181     unless url.is_empty
182       # find readers
183       ext = File.extname(url.path)[1..-1]
184       return unless ext
185       readers = Game.to_enum.find_all do |_, game|
186         game.respond_to?(:game_extensions) and
187         game.game_extensions.include?(ext)
188       end.map do |_, game|
189         [game, game.game_reader]
190       end
191       
192       if readers.empty?
193         warn "Unknown file extension #{ext}"
194         return
195       end
196       
197       tmp_file = ""
198       return unless KIO::NetAccess.download(url, tmp_file, self)
200       history = nil
201       game = nil
202       info = nil
203       
204       readers.each do |g, reader|
205         begin
206           data = File.open(tmp_file) do |f|
207             f.read
208           end
209           i = {}
210           history = reader.read(data, i)
211           game = g
212           info = i
213           break
214         rescue ParseException
215         end
216       end
217       
218       unless history
219         warn "Could not load file #{url.path}"
220         return
221       end
222       
223       # create game
224       match = Match.new(game)
225       setup_single_player(match)
226       match.history = history
227       match.add_info(info)
228       match.url = url
229       @controller.reset(match)
230     end
231   end
232   
233   def save_game_as
234     match = @controller.match
235     if match
236       pattern = if match.game.respond_to?(:game_extensions)
237         match.game.game_extensions.map{|ext| "*.#{ext}"}.join(' ')
238       else
239         '*.*'
240       end
241       url = KDE::FileDialog.get_save_url(
242         KDE::Url.new, pattern, self, KDE.i18n("Save game"))
243       match.url = write_game(url)
244     end
245   end
246   
247   def save_game
248     match = @controller.match
249     if match
250       if match.url
251         write_game
252       else
253         save_game_as
254       end
255     end
256   end
257   
258   def write_game(url = nil)
259     match = @controller.match
260     if match
261       url ||= match.url
262       writer = match.game.game_writer
263       info = match.info
264       info[:players] = info[:players].inject({}) do |res, pl|
265         res[pl.color] = pl.name
266         res
267       end
268       result = writer.write(info, match.history)
269       write_file(url, result)
270     end
271   end
272   
273   def update_game_actions(match)
274     unplug_action_list('game_actions')
275     actions = if match.game.respond_to?(:actions)
276       match.game.actions(self, action_collection, @controller.policy)
277     else
278       []
279     end
280     plug_action_list('game_actions', actions)
281   end
284 class DummyPlayer
285   include Observer
286   include Player
287   
288   attr_reader :color
289   attr_accessor :name
290   
291   def initialize(color)
292     @color = color
293   end