Fix flip action.
[kaya.git] / lib / mainwindow.rb
blobc19c79d2f5caafd3854eedee1427be60f2a5f6c7
1 # Copyright (c) 2009 Paolo Capriotti <p.capriotti@gmail.com>
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 require 'qtutils'
9 require 'board/board'
10 require 'board/pool'
11 require 'board/table'
12 require 'board/scene'
13 require 'interaction/history'
14 require 'controller'
15 require 'dummy_player'
17 require 'interaction/match'
19 require 'console'
21 require 'filewriter'
22 require 'newgame'
23 require 'engine_prefs'
24 require 'theme_prefs'
25 require 'view'
26 require 'multiview'
28 class MainWindow < KDE::XmlGuiWindow
29   include ActionHandler
30   include FileWriter
31   
32   attr_reader :console
34   def initialize(loader, game)
35     super nil
36     
37     @loader = loader
38     @theme_loader = @loader.get_matching(:theme_loader).new
39     
40     startup(game)
41     setup_actions
42     load_action_providers
43     setupGUI
44     new_game(Match.new(game), :new_tab => false)
45   end
46   
47   def closeEvent(event)
48     if controller.match
49       controller.match.close
50     end
51     event.accept
52   end
54   def controller
55     @view.current.controller
56   end
58 private
60   def setup_actions
61     @actions = { }
62     std_action(:open_new) { create_game }
63     std_action(:open) { load_game }
64     std_action :quit, :slot => :close
65     std_action(:save) { save_game }
66     std_action(:saveAs) { save_game_as }
67     
68     @actions[:back] = regular_action :back, :icon => 'go-previous', 
69                           :text => KDE.i18n("B&ack") do
70       controller.back
71     end
72     @actions[:forward] = regular_action :forward, :icon => 'go-next', 
73                              :text => KDE.i18n("&Forward") do
74       controller.forward
75     end
76     
77     regular_action :flip, :icon => 'object-rotate-left',
78                           :text => KDE.i18n("F&lip") do
79       table = @view.current.table
80       table.flip(!table.flipped?)
81     end
82     
83     regular_action :configure_engines,
84                    :icon => 'help-hint',
85                    :text => KDE.i18n("Configure &Engines...") do
86       dialog = EnginePrefs.new(@engine_loader, self)
87       dialog.show
88     end
89     
90     regular_action :configure_themes,
91                    :icon => 'games-config-theme',
92                    :text => KDE.i18n("Configure &Themes...") do
93       dialog = ThemePrefs.new(@loader, @theme_loader, self)
94       dialog.show
95     end
96     
97     @actions[:undo] = std_action(:undo) do
98       controller.undo!
99     end
101     @actions[:redo] = std_action(:redo) do
102       controller.redo!
103     end
104   end
105   
106   def load_action_providers
107     @loader.get_all_matching(:action_provider).each do |provider_klass|
108       provider = provider_klass.new
109       ActionProviderClient.new(self, provider)
110     end
111   end
112   
113   def create_view(opts = { })
114     scene = Scene.new
115     table = Table.new(scene, @loader, @theme_loader, @view)
116     contr = Controller.new(table, @field)
117     movelist = @loader.get_matching(:movelist).new(contr)
118     v = View.new(table, contr, movelist)
119     @view.add(v, opts)
120   end
121   
122   def startup(game)
123     @field = AnimationField.new(20)
126     movelist_stack = Qt::StackedWidget.new(self)
127     movelist_dock = Qt::DockWidget.new(self)
128     movelist_dock.widget = movelist_stack
129     movelist_dock.window_title = KDE.i18n("History")
130     movelist_dock.object_name = "movelist"
131     add_dock_widget(Qt::LeftDockWidgetArea, movelist_dock, Qt::Vertical)
132     movelist_dock.show
133     action_collection.add_action('toggle_history', 
134       movelist_dock.toggle_view_action)
136     @view = MultiView.new(self, movelist_stack)
137     create_view(:name => game.class.plugin_name)
138     
139     @engine_loader = @loader.get_matching(:engine_loader).new
140     @engine_loader.reload
141     
142     @console = Console.new(nil)
143     console_dock = Qt::DockWidget.new(self)                                                      
144     console_dock.widget = @console                                                             
145     console_dock.focus_proxy = @console                                                        
146     console_dock.window_title = KDE.i18n("Console")                                              
147     console_dock.object_name = "console"                                                         
148     add_dock_widget(Qt::BottomDockWidgetArea, console_dock, Qt::Horizontal)                      
149     console_dock.window_flags = console_dock.window_flags & ~Qt::WindowStaysOnTopHint            
150     console_dock.show
151     action_collection.add_action('toggle_console', 
152       console_dock.toggle_view_action)
153     
154     self.central_widget = @view
155   end
156   
157   def new_game(match, opts = { })
158     setup_single_player(match)
159     controller.reset(match)
160   end
161   
162   def setup_single_player(match)
163     controller.color = match.game.players.first
164     controller.premove = false
165     opponents = match.game.players[1..-1].map do |color|
166       DummyPlayer.new(color)
167     end
168     opponents.each do |p| 
169       controller.add_controlled_player(p)
170     end
172     controller.controlled.values.each do |p|
173       match.register(p)
174     end
175     controller.controlled.values.each do |p|
176       match.start(p)
177     end
178   end
180   def create_game(opts = { })
181     current_game = if controller.match 
182       controller.match.game
183     end
184     diag = NewGame.new(self, @engine_loader, current_game)
185     diag.observe(:ok) do |data|
186       game = data[:game]
187       match = Match.new(game, :editable => data[:engines].empty?)
188       if data[:new_tab]
189         create_view(:activate => true,
190                     :name => game.class.plugin_name)
191       else
192         @view.set_tab_text(@view.index, game.class.plugin_name)
193       end
194       contr = controller
195       
196       
197       match.observe(:started) do
198         contr.reset(match)
199       end
200       
201       # set up engine players
202       players = game.players
203       data[:engines].each do |player, engine|
204         e = engine.new(player, match)
205         e.start
206       end
207       
208       # set up human players
209       if data[:humans].empty?
210         contr.color = nil
211       else
212         contr.color = data[:humans].first
213         contr.premove = data[:humans].size == 1
214         match.register(contr)
215         
216         data[:humans][1..-1].each do |player|
217           p = DummyPlayer.new(player)
218           contr.add_controlled_player(p)
219           match.register(p)
220         end
221       end
222       contr.controlled.values.each {|p| match.start(p) }
223     end
224     diag.show
225   end
227   def load_game
228     url = KDE::FileDialog.get_open_url(KDE::Url.new, '*.*', self,
229       KDE.i18n("Open game"))
230     unless url.is_empty
231       # find readers
232       ext = File.extname(url.path)[1..-1]
233       return unless ext
234       readers = Game.to_enum.find_all do |_, game|
235         game.respond_to?(:game_extensions) and
236         game.game_extensions.include?(ext)
237       end.map do |_, game|
238         [game, game.game_reader]
239       end
240       
241       if readers.empty?
242         warn "Unknown file extension #{ext}"
243         return
244       end
245       
246       tmp_file = ""
247       return unless KIO::NetAccess.download(url, tmp_file, self)
249       history = nil
250       game = nil
251       info = nil
252       
253       readers.each do |g, reader|
254         begin
255           data = File.open(tmp_file) do |f|
256             f.read
257           end
258           i = {}
259           history = reader.read(data, i)
260           game = g
261           info = i
262           break
263         rescue ParseException
264         end
265       end
266       
267       unless history
268         warn "Could not load file #{url.path}"
269         return
270       end
271       
272       # create game
273       match = Match.new(game)
274       create_view(:activate => true,
275                   :name => game.class.plugin_name)
276       setup_single_player(match)
277       match.history = history
278       match.add_info(info)
279       match.url = url
280       controller.reset(match)
281     end
282   end
283   
284   def save_game_as
285     match = controller.match
286     if match
287       pattern = if match.game.respond_to?(:game_extensions)
288         match.game.game_extensions.map{|ext| "*.#{ext}"}.join(' ')
289       else
290         '*.*'
291       end
292       url = KDE::FileDialog.get_save_url(
293         KDE::Url.new, pattern, self, KDE.i18n("Save game"))
294       match.url = write_game(url)
295     end
296   end
297   
298   def save_game
299     match = controller.match
300     if match
301       if match.url
302         write_game
303       else
304         save_game_as
305       end
306     end
307   end
308   
309   def write_game(url = nil)
310     match = controller.match
311     if match
312       url ||= match.url
313       writer = match.game.game_writer
314       info = match.info
315       info[:players] = info[:players].inject({}) do |res, pl|
316         res[pl.color] = pl.name
317         res
318       end
319       result = writer.write(info, match.history)
320       write_file(url, result)
321     end
322   end
323   
324   def update_game_actions(match)
325     unplug_action_list('game_actions')
326     actions = if match.game.respond_to?(:actions)
327       match.game.actions(self, action_collection, controller.policy)
328     else
329       []
330     end
331     plug_action_list('game_actions', actions)
332   end