| Trees | Indices | Help |
|
|---|
|
|
1 """
2 Container widgets. Each has a name and knows if it's showing on screen. L{ToolsFace} contains other L{Face}s.
3 Uses super (http://www.phyast.pitt.edu/~micheles/python/super.pdf).
4
5 Copyright 2008-2013 Research Foundation State University of New York
6 This file is part of QUB Express.
7
8 QUB Express is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 QUB Express is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License,
19 named LICENSE.txt, in the QUB Express program directory. If not, see
20 <http://www.gnu.org/licenses/>.
21
22 """
23
24 import gtk
25 import gobject
26 import traceback
27 import sys
28 import os
29
30 import qubx.pyenv
31 import qubx.tree
32 from gtk import gdk
33 from gtk import keysyms
34 from itertools import izip, count
35 from math import *
36 from qubx.util_types import *
37 from qubx.GTK import TextViewAppender, gtk_literally
38 from qubx.GTK import pack_item, pack_space, pack_hsep, pack_vsep, pack_label, pack_button, pack_check, pack_radio, pack_scrolled, build_menuitem
39 from qubx.toolspace import ToolSpace, Layer, SubLayer_Label, SubLayer_DropDown, SubLayer_Popup, SubLayer_Popup_PNG, ColorInfo, COLOR_CLEAR
40
41 FACE_POP_INSIDE, FACE_POP_WINDOW, FACE_POP_MINIMIZED, FACE_POP_MAXIMIZED = (0, 1, 2, 3)
42 COLOR_POPPED_TEXT = ('faces.popped.empty.text', (.1,0,0,1))
43 ColorInfo[COLOR_POPPED_TEXT[0]].label = 'Window popped-out placeholder text'
44 COLOR_POPPED_BTN = ('faces.popped.btn', (.7,0,.1,1))
45 ColorInfo[COLOR_POPPED_BTN[0]].label = 'Window popped-out placeholder button'
46 FILEMENU_WIDTH = 32
47 RECENT_FILES_COUNT = 20
48
49 POS_DROPDOWN = 'dropdown'
50
51 REQUEST_SHOW_AS_WINDOW = False
52
53 RESPONSE_NO_TO_ALL = 12345
54
55
57 """Base container class.
58
59 @ivar showing: (bool) whether this Face is on screen
60 @ivar icon: widget next to name in ToolsFace tabs
61 @ivar tab_sublayers: extra controls for ToolToggle sidebar
62 @ivar menubar: optional gtk.MenuBar for windowed state only
63 """
64 __explore_featured = ['face_name', 'global_name', 'showing', 'icon', 'parent_window', 'pop_info', 'pop_prefs', 'tab_sublayers', 'menubar', 'pack_start', 'pack_end', 'remove', 'show', 'onShow', 'request_show', 'set_size_request']
65
67 super(Face, self).__init__()
68 self.face_name = name
69 self.global_name = global_name
70 self._showing = False
71 self.icon = None
72 self.__parent_window = None
73 self.__pop_prefs = None
74 self.pop_info = None
75 self.tab_sublayers = []
76 self.menubar = None
77 showing = property(lambda s: s._showing, lambda s,x: s.set_showing(x))
86 if self.pop_info:
87 if self.pop_info.state:
88 self.pop_info.window.present()
89 elif REQUEST_SHOW_AS_WINDOW:
90 self.pop_info.state = FACE_POP_WINDOW
91 self.pop_info.window.present()
92 else:
93 self.pop_info.container.show_face(self.face_name)
94 self.pop_info.container.request_show()
95 parent_window = property(lambda s: s.__parent_window, lambda s, x: s.set_parent_window(x))
97 self.__parent_window = parent_window
98 pop_prefs = property(lambda s: s.__pop_prefs, lambda s, x: s.set_pop_prefs(x))
100 self.__pop_prefs = pop_prefs
101
102
104 """Represents the embedded or windowed state of a Face inside ToolsFace or ToolsToggleFace."""
105
107 self.container = container
108 self.face = face
109 self.window = None
110 self.OnWindow = WeakEvent() # (Face)
111 self.__pop_prefs = None
112 self.__state = FACE_POP_INSIDE
113 self.__position = (0, 0, 0, 0)
115 self.__pop_prefs = prefs
116 if prefs is None: return
117 if self.face.global_name:
118 self.__position = prefs.get_position(self.face.global_name)
119 gobject.idle_add(self.set_state, prefs.get_state(self.face.global_name))
120 pop_prefs = property(lambda self: self.__pop_prefs, lambda self, x: self.set_pop_prefs(x))
122 if self.__state == state: return
123 self.face.pop_prefs.update_state(self.face.global_name, state)
124 if self.__state == FACE_POP_INSIDE:
125 self.__state = state
126 if self.__position == (0, 0, 0, 0):
127 x, y, w, h = self.face.get_allocation()
128 try:
129 x, y = self.face.translate_coordinates(self.face.get_toplevel(), 0, 0)
130 except:
131 pass
132 if self.face.parent_window:
133 px, py = self.face.parent_window.get_position()
134 x += px
135 y += py
136 self.__position = (x, y, w, h)
137 self.face.pop_prefs.update_position(self.face.global_name, x, y, w, h)
138 else:
139 x, y, w, h = self.__position
140 self.container.pop_out(self.face)
141 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
142 self.window.set_title(self.face.global_name)
143 self.window.connect('destroy', self.__onWinDestroy)
144 self.window.connect('configure_event', self.__onWinConfigure)
145 self.window.connect('window_state_event', self.__onWinState)
146 self.window.resize(w, h)
147 gobject.idle_add(self.window.move, x, y)
148 self.face.parent_window = self.window
149 self.toplevel = gtk.VBox()
150 self.toplevel.show()
151 self.window.add(self.toplevel)
152 if self.face.menubar:
153 self.toplevel.pack_start(self.face.menubar, False, True)
154 self.face.menubar.show()
155 self.toplevel.pack_start(self.face, True, True)
156 if state == FACE_POP_MINIMIZED:
157 self.window.iconify()
158 elif state == FACE_POP_MAXIMIZED:
159 self.window.maximize()
160 self.window.show()
161 self.face.showing = state != FACE_POP_MINIMIZED
162 self.OnWindow(self.face)
163 return
164 elif state == FACE_POP_INSIDE:
165 self.face.parent_window = self.container.parent_window
166 self.toplevel.remove(self.face)
167 if self.face.menubar:
168 self.toplevel.remove(self.face.menubar)
169 self.window.destroy()
170 self.window = self.toplevel = None
171 self.__state = state
172 self.container.pop_in(self.face, show=True)
173 self.OnWindow(self.face)
174 return
175 if self.__state == FACE_POP_MINIMIZED:
176 self.window.deiconify()
177 elif self.__state == FACE_POP_MAXIMIZED:
178 self.window.unmaximize()
179 self.__state = state
180 if state == FACE_POP_MINIMIZED:
181 self.window.iconify()
182 elif state == FACE_POP_MAXIMIZED:
183 self.window.maximize()
184 self.face.showing = state != FACE_POP_MINIMIZED
185 state = property(lambda self: self.__state, lambda self, x: self.set_state(x))
187 if self.__position == (x, y, w, h): return
188 self.__position = x, y, w, h
189 self.face.pop_prefs.update_position(self.face.global_name, x, y, w, h)
190 if self.state == FACE_POP_WINDOW:
191 gobject.idle_add(self.window.move, x, y)
192 self.window.resize(w, h)
193 position = property(lambda self: self.__position)
195 self.toplevel.remove(self.face)
196 if self.face.menubar:
197 self.toplevel.remove(self.face.menubar)
198 self.__state = FACE_POP_INSIDE
199 self.face.pop_prefs.update_state(self.face.global_name, FACE_POP_INSIDE)
200 self.window = None
201 self.container.pop_in(self.face)
203 x, y = win.get_position()
204 self.__position = (x, y, alloc.width, alloc.height)
205 self.face.pop_prefs.update_position(self.face.global_name, x, y, alloc.width, alloc.height)
207 state = self.state
208 if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
209 if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
210 state = FACE_POP_MINIMIZED
211 else:
212 state = FACE_POP_WINDOW
213 if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED:
214 if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED:
215 state = FACE_POP_MAXIMIZED
216 else:
217 state = FACE_POP_WINDOW
218 self.__state = state
219 self.face.showing = state != FACE_POP_MINIMIZED
220 self.face.pop_prefs.update_state(self.face.global_name, state)
221
222
224 """Manages global, persistent state for all containers' pop-out states."""
226 self.profile = profile
227 self.nodes = dict((node.name, node) for node in qubx.tree.children(profile))
228 self.freeze = False
230 if global_name in self.nodes:
231 node = self.nodes[global_name]
232 else:
233 node = self.profile[global_name]
234 self.nodes[global_name] = node
235 if not node['state'].data:
236 node['state'].data = FACE_POP_INSIDE
237 if len(node['position'].data) != 4:
238 node['position'].data = (0, 0, 0, 0)
239 return node
251 if self.freeze: return False
252 multi_window = False
253 for node in qubx.tree.children(updates):
254 if node.name in self.nodes:
255 try:
256 face = qubx.pyenv.env.eval_str(node.name, eat_exceptions=False)
257 if node['state'].data:
258 state = node['state'].data[0]
259 self.update_state(node.name, state)
260 face.pop_info.set_state(state)
261 multi_window = True
262 if len(node['position'].data) == 4:
263 x, y, w, h = node['position'].data[:]
264 self.update_position(node.name, x, y, w, h)
265 face.pop_info.set_position(x, y, w, h)
266 except AttributeError:
267 pass
268 except:
269 traceback.print_exc()
270 return multi_window
271
272
274 """Toggle graphic for pop-out/-in of sub-Face; shown in tabs of ToolsFace, ToolsToggleFace."""
276 self.__ref = Reffer()
277 if 'rgba' in kw:
278 self.rgba = kw['rgba']
279 del kw['rgba']
280 else:
281 self.rgba = (.7, .35, 0, .8)
282 kw['enter'] = self.__ref(self.__enter)
283 kw['exit'] = self.__ref(self.__exit)
284 qubx.toolspace.SubLayer_Icon.__init__(self, *args, **kw)
285 self.popped = False
286 self.__highlight = False
288 self.__popped = x
289 if self.layer and self.layer.space and (len(self.layer.subs) == 1):
290 self.layer.space.set_tooltip_text(x and 'Return panel to this layout' or 'Pop out panel as a separate window')
291 self.__exit()
292 self.invalidate()
293 popped = property(lambda self: self.__popped, lambda self, x: self.set_popped(x))
301 qubx.toolspace.SubLayer_Icon.draw(self, context, w, h, appearance)
302 context.save()
303 #context.set_source_rgba(.5, .5, .5, 1)
304 #context.rectangle(.05*self.w, .05*self.h, .9*self.w, .9*self.h)
305 #context.fill()
306
307 r,g,b,a = self.rgba
308 if self.__popped:
309 context.translate(self.w, self.h)
310 context.rotate(pi)
311 if self.__highlight:
312 h,s,v = RGBtoHSV(r, g, b)
313 r, g, b = HSVtoRGB(h, s, (v+1)/2)
314 context.set_line_width((0.25 + 0.1*self.__highlight) * appearance.emsize)
315 context.set_source_rgba(r, g, b, a)
316 for y0 in (.15, .35):
317 context.move_to(.25*self.w, (y0+.15)*self.h)
318 context.line_to(.5*self.w, y0*self.h)
319 context.line_to(.75*self.w, (y0+.15)*self.h)
320 context.stroke()
321 context.restore()
322
323
325 """Shows one of a collection of L{Face}s, chosen by tab. Child faces are accessible
326 by natural naming; e.g. to get the child face with name "Whatever", type `myToolsFace.Whatever`
327 """
328
329 __explore_featured = ['OnSwitchFace', 'book', 'mnu_line', 'mnu', 'faces', 'data',
330 'face', 'index', 'insert_face', 'append_face', 'remove_face', 'show_face',
331 'pop_out', 'pop_in']
333 """@param pos: e.g. gtk.POS_LEFT, for gtk.Notebook; or POS_DROPDOWN for a dropdown at top"""
334 super(ToolsFace, self).__init__(name, global_name)
335 self.__ref = Reffer()
336 self.__explore_featured = self.__explore_featured[:] # modify local copy with child face names
337 self.set_size_request(100, 100)
338 self.OnSwitchFace = WeakEvent() # (ToolsFace, face_name)
339 self.book = pack_item(gtk.Notebook(), self, expand=True, at_end=True)
340 self.book.connect('switch_page', self._onSwitchPage)
341 self.book.set_show_border(True)
342 if pos == POS_DROPDOWN:
343 self.book.set_show_tabs(False)
344 self.mnu_line = pack_item(gtk.HBox(), self)
345 pack_label(dropdown_label, self.mnu_line)
346 self.mnu = pack_item(gtk.combo_box_new_text(), self.mnu_line, expand=True)
347 self.mnu.connect('changed', self._onMnuChanged)
348 else:
349 self.mnu = None
350 self.mnu_line = None
351 self.book.set_tab_pos(pos)
352 self._face = None
353 self.faces = []
354 self.data = {}
355 self.__draw_placeholder = self.__ref(lambda ctx, w, h: ctx.set_source_rgba(1,1,1,1) or ctx.paint())
357 super(ToolsFace, self).set_parent_window(parent_window)
358 for face in self.faces:
359 if not face.pop_info.state:
360 face.parent_window = parent_window
362 super(ToolsFace, self).set_pop_prefs(pop_prefs)
363 for face in self.faces:
364 face.pop_prefs = pop_prefs
365 face.pop_info.pop_prefs = pop_prefs
370 """Returns the index of a face name."""
371 for i, f in enumerate(self.faces):
372 if f.face_name == name:
373 return i
374 raise KeyError(name)
376 tab = gtk.HBox()
377 if face.icon:
378 tab.pack_start(face.icon, False, True)
379 tab.pack_start(gtk.Label(face.face_name))
380 sub = None
381 if face.pop_prefs:
382 pop = ToolSpace()
383 pop.set_size_request(14, 14)
384 pop.OnDraw += self.__ref(self.__onDrawPopBG)
385 name = face.face_name
386 sub = pop.add_layer(Layer(x=.2,y=.1, w=-.2, h=-.1, cBG=COLOR_CLEAR)).add_sublayer(SubLayer_PopIcon(w=-.01, h=-.01, rgba=(.66, .25, 0, 1), action=self.__ref(lambda x,y,e: self.__onTogglePopped(name))))
387 sub.popped = False
388 tab.pack_start(pop, False, False)
389 tab.show_all()
390 return tab, sub
395 face = self.faces[self.index(face_name)]
396 if face.pop_info.state:
397 face.pop_info.state = 0
398 self.show_face(face.face_name)
399 else:
400 face.pop_info.state = 1
402 """Inserts a child face at index i."""
403 self.faces.insert(i, face)
404 safename = SafeName(face.face_name)
405 self.data[face.face_name] = Anon(face=face)
406 try:
407 if isinstance(self.__dict__[safename], Face):
408 raise Exception('replaceable')
409 except:
410 self.__dict__[safename] = face
411 if not face.global_name:
412 face.global_name = "%s.%s" % (self.global_name, face.face_name)
413 face.parent_window = self.parent_window
414 face.pop_prefs = self.pop_prefs
415 face.pop_info = PopInfo(self, face)
416 face.pop_info.pop_prefs = self.pop_prefs
417 tab, pop = self.build_tab_label(face)
418 self.data[face.face_name].pop = pop
419 self.__explore_featured.append(safename)
420 face.show()
421 self.book.insert_page(face, tab, position=i)
422 if self.mnu:
423 self.mnu.insert_text(i, face.face_name)
424 if i == 0:
425 self.mnu.set_active(0)
427 """Inserts a child face at the end."""
428 if face.face_name in self.data:
429 self.remove_face(self.index(face.face_name))
430 self.insert_face(len(self.faces), face)
432 """Removes the child face at index ix."""
433 face = self.faces[ix]
434 data = self.data[face.face_name]
435 del self.faces[ix]
436 del self.data[face.face_name]
437 self.__explore_featured.remove(SafeName(face.face_name))
438 self.book.remove_page(ix)
439 if self.mnu:
440 self.mnu.remove_text(ix)
441 if face == self.__dict__[SafeName(face.face_name)]:
442 del self.__dict__[SafeName(face.face_name)]
443 if not self.faces:
444 self._face = None
445 self.OnSwitchFace(self, None)
446 face.parent_window = None
447 face.pop_prefs = None
448 face.pop_info = None
450 """Brings the named face to the front."""
451 self.book.set_current_page(self.faces.index(self.data[name].face))
456 if self._face and self._face.pop_info and (not self._face.pop_info.state) and self.showing:
457 self._face.showing = False
458 self._face = self.faces[page_num]
459 if self._face.pop_info and not self._face.pop_info.state:
460 self._face.showing = self.showing
461 if self.mnu:
462 self.mnu.set_active(page_num)
463 self.OnSwitchFace(self, self._face.face_name)
465 i = mnu.get_active()
466 if not (0 <= i < len(self.faces)): return
467 self.book.set_current_page(i)
469 ix = self.faces.index(face)
470 placeholder = ToolSpace()
471 placeholder.show()
472 placeholder.OnDraw += self.__draw_placeholder
473 layer = qubx.toolspace.Layer(x=0, y=0, w=50, h=9, cBG=COLOR_CLEAR)
474 placeholder.add_layer(layer)
475 layer.add_sublayer(qubx.toolspace.SubLayer_Label("This panel has been popped out as a separate window.",
476 -1, 1, x=6, y=2, w=50, h=1.5,
477 color=COLOR_POPPED_TEXT, hover_color=COLOR_POPPED_TEXT))
478 layer.add_sublayer(qubx.toolspace.SubLayer_Label("Return to Layout", 0, 1, x=12, y=5, w=15, h=1.5, color=COLOR_POPPED_BTN,
479 border=1, action=self.__ref(bind(self.__return_to_layout, face.face_name))))
480 layer.add_sublayer(qubx.toolspace.SubLayer_Label("Show Window", 0, 1, x=30, y=5, w=15, h=1.5, color=COLOR_POPPED_BTN,
481 border=1, action=self.__ref(bind(self.__show_window, face.face_name))))
482 self.book.remove_page(ix)
483 tab, pop = self.build_tab_label(face)
484 self.data[face.face_name].pop = pop
485 pop.popped = True
486 self.book.insert_page(placeholder, tab, position=ix)
487 ### maybe with a message / buttons: bring to front, put back in this frame
492 face = self.faces[self.index(face_name)]
493 face.pop_info.set_state(0) or face.pop_info.container.show_face(face_name)
495 ix = self.faces.index(face)
496 self.book.remove_page(ix)
497 tab, pop = self.build_tab_label(face)
498 self.data[face.face_name].pop = pop
499 self.book.insert_page(face, tab, position=ix)
500 face.showing = self.showing and (self.book.get_current_page() == ix)
501
502
503 TOOLTOGGLE_WIDTH_EM = 2.0
504 TOOLTOGGLE_MENUHEIGHT_EM = 3.0
505 TOOLTOGGLE_TABHEIGHT_EM = 10.0
506 TOOLTOGGLE_NAME_EM = 16.0
507 TOOLTOGGLE_TAB_BORDER = 1.5
508 #TOOLTOGGLE_MIN_REPOS_PIX = 16
509
510 COLOR_TOOLTAB_BG = ('faces.tooltab.bg', (0,0,0,1))
511 ColorInfo[COLOR_TOOLTAB_BG[0]].label = 'Window side button background'
512 COLOR_TOOLTAB = lambda showing: showing and ('faces.tooltab.showing', (.2, 1, .2, 1)) or ('faces.tooltab.hidden', (.2, 1, .2, .5))
513 COLOR_TOOLTAB_CLOSE = ('faces.tooltoggle.close', (.9, 0, 0, 1))
514 ColorInfo[COLOR_TOOLTAB_CLOSE].label = 'Window sidebar tab close button'
515 COLOR_TOOLTAB_CLOSE_HOVER = ('faces.tooltoggle.close.hover', (1, .2, .2, 1))
516 ColorInfo[COLOR_TOOLTAB_CLOSE_HOVER].label = 'Window sidebar tab close button mouseover'
517 ColorInfo['faces.tooltab.showing'].label = 'Window side button foreground'
518 ColorInfo['faces.tooltab.hidden'].label = 'Window side button foreground hidden'
519 COLOR_TOOLTOGGLE_MENU = ('faces.tooltoggle.menu', (.2, 1, .2, 1))
520 ColorInfo[COLOR_TOOLTOGGLE_MENU[0]].label = 'Window main menu'
521 COLOR_TOOLTOGGLE_INFO = ('faces.tooltoggle.info', (1, 1, 1, .76))
522 ColorInfo[COLOR_TOOLTOGGLE_INFO[0]].label = 'Window instructions'
523
525 """Shows one or more of a collection of L{Face}s, toggled visible by left-hand tabs. Child faces are accessible
526 by natural naming; e.g. to get the child face with name "Whatever", type `myToolsFace.Whatever`
527 """
528 __explore_featured = ['OnToggleFace', 'tabspace', 'tabLayer', 'toolBox', 'toolRoot', 'faces', 'data',
529 'tabs', 'pops', 'widgets', 'splits', 'splits_pos', 'data_showing',
530 'face', 'index', 'insert_face', 'append_face', 'remove_face', 'show_face',
531 'updateProfile', 'update_from_fractions', 'pop_out', 'pop_in', 'unpop_all']
532
534 super(ToolsToggleFace, self).__init__(name, global_name)
535 self.connect('size_allocate', self.__on_size_allocate)
536 if not (profile is None):
537 self.profile = profile
538 else:
539 self.profile = qubx.tree.Node('')
540 self.__explore_featured = self.__explore_featured[:] # modify local copy with child face names
541 self.ref = Reffer()
542 self.set_size_request(100, 100)
543 self.OnToggleFace = WeakEvent() # (ToolsToggleFace, face_name, showing)
544 self.hbox = pack_item(gtk.HBox(), self, expand=True)
545 self.tabspace = pack_item(ToolSpace(), self.hbox)
546 self.tabspace.OnDraw += self.ref(self.__onDrawTabsBG)
547 self.tabspace.set_size_request(int(2*self.tabspace.appearance.emsize*TOOLTOGGLE_WIDTH_EM), -1)
548 self.tabspace.appearance.OnSetFontSize += self.ref(self.__onSetFontSize)
549 self.tabLayer = Layer(y=0.2*TOOLTOGGLE_WIDTH_EM, w=-.01, h=-TOOLTOGGLE_NAME_EM)
550 self.tabspace.add_layer(self.tabLayer)
551 self.tabLayer.add_sublayer(SubLayer_Label('panels:', x=0, y=-TOOLTOGGLE_TABHEIGHT_EM-2,
552 w=TOOLTOGGLE_WIDTH_EM, h=TOOLTOGGLE_TABHEIGHT_EM,
553 color=COLOR_TOOLTOGGLE_INFO, rotate=pi/2))
554 self.nameLayer = Layer(y=-TOOLTOGGLE_NAME_EM-1, w=-.01, h=TOOLTOGGLE_NAME_EM)
555 self.tabspace.add_layer(self.nameLayer)
556
557 self.toolBox = pack_item(gtk.VBox(), self.hbox, expand=True)
558 self.toolRoot = None
559 self.faces = []
560 self.data = {}
561 self.tabs = []
562 self.pops = []
563 self.widgets = []
564 self.splits = []
565 self.splits_pos = []
566 self.data_showing = []
567 self.__repositioning = False
568 self.__wh = (0,0)
569 if pop_prefs is None:
570 self.pop_prefs = PopPrefs(self.profile['PopPrefs'])
571 else:
572 self.pop_prefs = pop_prefs
573 # this is maybe obnoxious, preventing background work:
575 super(ToolsToggleFace, self).request_show()
576 if self.parent_window and qubx.GTK.is_front_app():
577 self.parent_window.present()
579 super(ToolsToggleFace, self).set_parent_window(parent_window)
580 for face in self.faces:
581 face.parent_window = parent_window
583 super(ToolsToggleFace, self).set_pop_prefs(pop_prefs)
584 for face in self.faces:
585 face.pop_prefs = pop_prefs
586 face.pop_info.pop_prefs = pop_prefs
588 for node in qubx.tree.children(updates['Showing']):
589 if node.data and (node.name in self.data):
590 self.show_face(node.name, bool(node.data[0]))
591 if self.showing:
592 changed_frac = False
593 for node in qubx.tree.children(updates['Fraction']):
594 if node.data and (node.name in self.data):
595 self.data[node.name].fraction = node.data[0]
596 self.profile['Fraction'][node.name].data = node.data[0]
597 changed_frac = True
598 if changed_frac:
599 self.__relayout(renest=False)
600 multi_window = self.pop_prefs.updateProfile(updates.find('PopPrefs'))
601 # needs twice?
602 if not second_time:
603 gobject.idle_add(self.updateProfile2, settings, updates)
604 return multi_window
611 """Returns the index of a face name."""
612 for i, f in enumerate(self.faces):
613 if f.face_name == name:
614 return i
615 raise KeyError(name)
617 """Inserts a child face at index i."""
618 if showing is None:
619 if self.profile['Showing'][face.face_name].data:
620 show = bool(self.profile['Showing'][face.face_name].data[0])
621 else:
622 show = True
623 else:
624 show = showing
625 frac = self.profile['Fraction'][face.face_name].data and self.profile['Fraction'][face.face_name].data[0] or 0
626 self.faces.insert(i, face)
627 self.data[face.face_name] = Anon(face=face, showing=show, fraction=frac)
628 self.__dict__[SafeName(face.face_name)] = face
629 self.__explore_featured.append(SafeName(face.face_name))
630 face.parent_window = self.parent_window
631 face.pop_prefs = self.pop_prefs
632 face.pop_info = PopInfo(self, face)
633 face.pop_info.pop_prefs = self.pop_prefs
634 if show:
635 face.showing = self.showing
636 self.__relayout(retab=True, add_name=face.face_name)
637 self.OnToggleFace(self, face, show)
639 """Inserts a child face at the end."""
640 if face.face_name in self.data:
641 self.remove_face(self.index(face.face_name))
642 self.insert_face(len(self.faces), face, showing=showing)
644 """Removes the child face at index ix."""
645 face = self.faces[ix]
646 data = self.data[face.face_name]
647 self.__explore_featured.remove(SafeName(face.face_name))
648 if face.pop_info.state:
649 face.pop_info.state = FACE_POP_INSIDE
650 if face.showing:
651 self.OnToggleFace(self, face, False)
652 face.showing = False
653 del self.faces[ix]
654 del self.data[face.face_name]
655 self.__relayout(retab=True)
656 face.parent_window = None
657 face.pop_prefs = None
658 face.pop_info = None
660 """Brings the named face onscreen (or hides)."""
661 data = self.data[name]
662 if data.showing != showing:
663 data.showing = showing
664 add_name = rem_name = None
665 if data.face.pop_info.state:
666 if showing:
667 data.face.pop_info.state = FACE_POP_WINDOW
668 data.face.pop_info.window.present()
669 else:
670 data.face.pop_info.state = FACE_POP_MINIMIZED
671 else:
672 if self.showing:
673 self.data[name].face.showing = showing
674 if showing:
675 add_name = name
676 else:
677 rem_name = name
678 self.__relayout(add_name=add_name, rem_name=rem_name)
679 self.tabs[self.index(name)].color = COLOR_TOOLTAB(showing)
680 self.profile['Showing'][name].data = int(showing)
681 self.OnToggleFace(self, self.data[name].face, showing)
683 for info in self.data.itervalues():
684 if not (info.face.pop_info.state) and info.showing:
685 info.face.showing = showing
690 for data in self.data_showing:
691 self.profile['Fraction'][data.face.face_name].data = data.fraction
692 self.__relayout(renest=False)
694 self.__repositioning = True
695 faces_showing = [face for face in self.faces if ((not face.pop_info.state) and self.data[face.face_name].showing)]
696 # redo the sidebar of tabs
697 if retab:
698 for sub in self.tabs:
699 self.tabLayer.remove_sublayer(sub)
700 for sub in self.pops:
701 self.tabLayer.remove_sublayer(sub)
702 for sub in self.widgets:
703 self.tabLayer.remove_sublayer(sub)
704 y = 0
705 self.tabs = []
706 self.pops = []
707 self.widgets = []
708 for face in self.faces:
709 sub = SubLayer_Label(face.face_name, x=0, y=y, w=TOOLTOGGLE_WIDTH_EM, h=TOOLTOGGLE_TABHEIGHT_EM,
710 color=COLOR_TOOLTAB(face in faces_showing), rotate=pi/2, border=TOOLTOGGLE_TAB_BORDER,
711 action=self.ref(bind(self.__onClickTab, face.face_name)))
712 self.tabLayer.add_sublayer(sub)
713 self.tabs.append(sub)
714 pop = SubLayer_PopIcon(x=TOOLTOGGLE_WIDTH_EM, y=y, w=TOOLTOGGLE_WIDTH_EM, h=TOOLTOGGLE_WIDTH_EM,
715 border=TOOLTOGGLE_TAB_BORDER,
716 tooltip="Show %s panel in its own window" % face.face_name,
717 action=self.ref(bind(self.__onTogglePopped, face)))
718 self.tabLayer.add_sublayer(pop)
719 self.pops.append(pop)
720 closer = SubLayer_Label('-', 0, 1, x=TOOLTOGGLE_WIDTH_EM, y=y+TOOLTOGGLE_WIDTH_EM, w=TOOLTOGGLE_WIDTH_EM, h=TOOLTOGGLE_WIDTH_EM,
721 color=COLOR_TOOLTAB_CLOSE, hover_color=COLOR_TOOLTAB_CLOSE_HOVER,
722 border=TOOLTOGGLE_TAB_BORDER,
723 tooltip="Hide %s panel" % face.face_name,
724 action=self.ref(bind(self.__onClickClose, face.face_name)))
725 self.tabLayer.add_sublayer(closer)
726 self.widgets.append(closer)
727 widget_y = y + 2*TOOLTOGGLE_WIDTH_EM
728 for widget in face.tab_sublayers:
729 widget.x = TOOLTOGGLE_WIDTH_EM
730 widget.y = widget_y
731 self.tabLayer.add_sublayer(widget)
732 self.widgets.append(widget)
733 widget_y += TOOLTOGGLE_WIDTH_EM
734 y += TOOLTOGGLE_TABHEIGHT_EM
735 self.tabLayer.h_min = y + TOOLTOGGLE_TABHEIGHT_EM # extra tab for 'show/hide:'
736 if renest:
737 # add/rem face: distribute delta fraction among showing faces
738 if add_name:
739 if len(faces_showing) == 1:
740 self.data[add_name].fraction = add_frac = 1.0
741 else:
742 add_frac = self.data[add_name].fraction or (1.0 / max(1, len(faces_showing)))
743 self.data[add_name].fraction = add_frac
744 self.profile['Fraction'][add_name].data = add_frac
745 for data in self.data_showing:
746 data.fraction *= (1 - add_frac)
747 if rem_name and faces_showing:
748 rescale = 1.0 / (1 - self.data[rem_name].fraction)
749 for data in self.data_showing:
750 if data.face.face_name != rem_name:
751 data.fraction *= rescale
752 if add_name or (rem_name and faces_showing):
753 for data in self.data_showing:
754 self.profile['Fraction'][data.face.face_name].data = data.fraction
755
756 # remove prior nested layout
757 if self.toolRoot:
758 for split in reversed(self.splits):
759 split.remove(split.get_child1())
760 split.remove(split.get_child2())
761 self.toolBox.remove(self.toolRoot)
762 self.splits = []
763 # and re-establish layout
764 self.toolRoot = make_nested_layout(faces_showing, self.splits, self.__on_notify_position)
765 self.splits_pos = [split.get_position() for split in self.splits]
766 if self.toolRoot:
767 self.toolBox.pack_start(self.toolRoot, True, True)
768 self.data_showing = [self.data[face.face_name] for face in faces_showing]
769 set_nested_proportions([data.fraction for data in self.data_showing], self.splits)
770 gobject.idle_add(self.__reenable_repos)
778 if face.pop_info.state:
779 face.pop_info.state = 0
780 self.show_face(face.face_name)
781 else:
782 face.pop_info.state = 1
784 if self.__repositioning: return
785 frac = [0.0] * len(self.data_showing)
786 read_nested_proportions(frac, self.splits)
787 for i, f in enumerate(frac):
788 self.data_showing[i].fraction = f
789 self.profile['Fraction'][self.data_showing[i].face.face_name].data = f
790 #ix = self.splits.index(paned) + 1
791 #if abs(self.splits_pos[ix-1] - self.splits[ix-1].get_position()) < TOOLTOGGLE_MIN_REPOS_PIX:
792 # return
793 ## distribute delta evenly among all faces above/below
794 #frac_pre = [data.fraction for data in self.data_showing]
795 #frac_post = frac_pre[:] # will overwrite; just need same shape
796 #read_nested_proportions(frac_post, self.splits)
797 #self.splits_pos = [split.get_position() for split in self.splits]
798 #print frac_pre
799 #print frac_post
800 #print
801 #scale = sum(frac_post[:ix]) / sum(frac_pre[:ix])
802 #for i in xrange(ix):
803 # self.data_showing[i].fraction = frac_pre[i] * scale
804 #scale = sum(frac_post[ix:]) / sum(frac_pre[ix:])
805 #for i in xrange(ix,len(frac_post)):
806 # self.data_showing[i].fraction = frac_pre[i] * scale
807 #self.__repositioning = True
808 #gobject.idle_add(self.__relayout_frac_only)
809 # def __relayout_frac_only(self):
810 # self.__relayout(renest=False)
812 if not self.window:
813 return
814 alloc = self.get_allocation()
815 wh = (alloc.width, alloc.height)
816 if wh != self.__wh:
817 self.__wh = wh
818 self.__relayout(renest=False)
821 # resize?
823 self.pops[self.index(face.face_name)].popped = True
824 if self.data[face.face_name].showing:
825 self.__relayout(rem_name=face.face_name)
827 self.pops[self.index(face.face_name)].popped = False
828 if self.data[face.face_name].showing:
829 self.__relayout(add_name=face.face_name)
836
837
838
840 if len(faces) == 0:
841 return None
842 elif i == (len(faces)-1):
843 return faces[i]
844 else:
845 split = gtk.VPaned()
846 split.connect("notify::position", notify_position)
847 splits.append(split)
848 split.pack1(faces[i], True, True)
849 split.pack2(make_nested_layout(faces, splits, notify_position, i+1), True, True)
850 split.show()
851 return split
852
854 if i == len(splits):
855 fractions[i] = k
856 else:
857 frac_local = splits[i].get_position() * 1.0 / splits[i].get_allocation()[3]
858 fractions[i] = max(1e-5, frac_local * k)
859 read_nested_proportions(fractions, splits, i+1, k*(1-frac_local))
860
862 if i == len(splits):
863 return
864 frac_local = k * fractions[i]
865 splits[i].set_position(int(frac_local * splits[i].get_allocation()[3]))
866 set_nested_proportions(fractions, splits, i+1, frac_local and k/((1 - frac_local) or 1) or k)
867
868 # hbox[
869 # vs0[
870 # panel0
871 # vs1[
872 # panel1
873 # panel2
874 # ]
875 # ]
876 # ]
877
879 """Shows a scrolling text area(s).
880
881 @ivar textView: gtk.TextView
882 @ivar textViews: list of gtk.TextView
883 @ivar textOut: L{qubx.GTK.TextViewAppender}
884 @ivar textOuts: list of textOut
885 """
886
887 __explore_featured = ['textViews', 'textOuts', 'textView', 'textOut',
888 'clear', 'write', 'set_text', 'get_text']
889
891 super(TextFace, self).__init__(name, global_name)
892 self.textViews = []
893 self.textOuts = []
894 for i in xrange(len(add_panes)+1):
895 self.textView = qubx.GTK.HyperTextView()
896 self.scroll = pack_scrolled(self.textView, self, size_request=(40, 40), expand=True, at_end=True)
897 self.textOut = TextViewAppender(self.textView)
898 self.textViews.insert(0, self.textView) # self.textView == self.textViews[0]
899 self.textOuts.insert(0, self.textOut)
900 if i < len(add_panes):
901 line = pack_item(gtk.HBox(), self, at_end=True)
902 pack_label(add_panes[i], line)
910 """Replaces all the text with str(x).
911 @param scroll_top: if True, moves the insertion point before the first char
912 """
913 self.clear(pane=pane)
914 self.write(str(x), pane=pane)
915 if scroll_top:
916 b = self.textViews[pane].get_buffer()
917 b.place_cursor(b.get_start_iter())
918 self.textViews[pane].scroll_to_mark(b.get_insert(), 0)
920 b = self.textViews[pane].get_buffer()
921 return b.get_text(b.get_start_iter(), b.get_end_iter())
922
923
924 COLOR_MENU_TEXT = ('faces.menu.text', (0,1,0,.75))
925 ColorInfo[COLOR_MENU_TEXT[0]].label = 'Default files menu text'
926 COLOR_MENU_FILE = ('faces.menu.file', (0,1,0,1))
927 ColorInfo[COLOR_MENU_FILE[0]].label = 'Default file menu triangle'
928
930 """Base class for a widget that manages multiple open files; e.g Data, Model.
931 Provides a menu of open files, file switching, a file menu, open and save dialogs,
932
933 @ivar file: object with fields "path", "save(path)", "saveAs(path)", "revert_to_saved()"
934 @ivar index: index of currently showing file
935 @ivar view: L{ToolSpace} for editing file; must have .controls as primary LayerSet
936 @ivar table: L{qubx.ObjectTable} containing files
937 @ivar views: list of each file's view
938 @ivar names: list of each file's name
939 @ivar lbls: list of each file's tab's gtk.Label
940 @ivar page0: a blank page shown when no file is open, constructed by ViewClass()
941 @ivar OnSwitch: L{WeakEvent}(FilesFace, file) called when index changes
942 @ivar OnChange: L{WeakEvent}(FilesFace, file) called when index changes or file is edited
943 @ivar OnSave: L{WeakEvent}(FilesFace, file, path) called after successful save
944 @ivar OnClosing: L{WeakEvent}(index) before close
945 """
946
947 __explore_featured = ['file', 'index', 'view', 'table', 'views', 'names', 'page_list', 'path', 'lbls', 'page0',
948 'OnSwitch', 'OnChange', 'OnSave', 'OnClosing', 'ViewClass',
949 'mnuFiles', 'layFiles', 'layMenu', 'make_view', 'make_page',
950 'saveAs', 'close', 'close_one', 'on_pre_close', 'save', 'on_pre_save',
951 'promptSave', 'close_down', 'show_file', 'add_recent', 'new', 'open',
952 'is_changed', 'can_save', 'can_revert', 'init_file', 'done_file',
953 'get_open_filters', 'get_save_filters', 'mnuFile', 'mnuRecent']
954
955 - def __init__(self, name, table, ViewClass, menu_width=32, popup_color=COLOR_MENU_FILE, global_name=""):
956 """
957 @param name: the name of this face; e.g. Data
958 @param table: a L{qubx.ObjectTable} of files
959 @param ViewClass: class or function(files_table) which instantiates a ToolSpace, subsequently assigned .file
960 @param menu_width: the file switcher is this many 'M's wide, at lower-left
961 @param popup_color: COLORREF of the inscribed-triangle File menu
962 """
963 Face.__init__(self, name, global_name)
964 self.__ref = Reffer()
965 self.ViewClass = ViewClass
966 self.table = table
967 self.table.OnInsert += self.__ref(self.__onInsert)
968 self.table.OnRemoved += self.__ref(self.__onRemoved)
969 self.table.OnSelect += self.__ref(self.__onSelect)
970 self.__buildMenu()
971 self.__recent = qubx.settings.SettingsMgr["%s.Recent" % name].active
972
973 self.path = qubx.pyenv.env.globals['documents_path']
974 self._file = None
975 self._index = -1
976 self._view = None
977 self.views = []
978 self.names = []
979 self.lbls = []
980 self.page_list = []
981 self._no_to_all = False
982
983 self.OnSwitch = WeakEvent() # (self, file) when switch
984 self.OnChange = WeakEvent() # (self, file) when switch or edit
985 self.OnSave = WeakEvent()
986 self.OnClosing = WeakEvent()
987
988 self.layFiles = Layer( 1, -4, menu_width, 3)
989 self.mnuFiles = SubLayer_DropDown('<no file>', 0, 1, COLOR_MENU_TEXT,
990 x=1, y=.5, w=menu_width-2, h=2)
991 self.layFiles.add_sublayer(self.mnuFiles)
992
993 self.layMenu = Layer( menu_width+2, -4, 2, 3, COLOR_CLEAR)
994 self.layMenu.add_sublayer(SubLayer_Popup_PNG(self.mnuFile, os.path.join(qubx.global_namespace.app_path, 'icons', 'folder.png'), y=.5, w=2, h=2, tooltip="File menu",
995 on_popup=self.__ref(self.on_popup_file)))
996 self.tab_sublayers.append(SubLayer_Popup_PNG(self.mnuFile, os.path.join(qubx.global_namespace.app_path, 'icons', 'folder.png'),
997 w=TOOLTOGGLE_WIDTH_EM, h=TOOLTOGGLE_WIDTH_EM, border=TOOLTOGGLE_TAB_BORDER,
998 on_popup=self.__ref(self.on_popup_file),
999 tooltip="%s: File menu" % name))
1000
1001 self.pages = pack_item(gtk.Notebook(), self, expand=True)
1002 self.pages.connect('switch_page', self._onSwitchPage)
1003 self.pages.set_show_border(True)
1004 self.pages.set_show_tabs(False)
1005
1006 self.page0 = ViewClass(None)
1007 self.page0.tool = None
1008 self.page0.controls.add_layer(self.layFiles)
1009 self.page0.controls.add_layer(self.layMenu)
1010 self.pages.insert_page(self.page0, None, 0)
1011 self.page0.show()
1012 self.page0.set_size_request(70, 70)
1013
1014 self._sensitize()
1015
1016 file = property(lambda self: self._file, lambda self, x: self.set_file(x))
1017 index = property(lambda self: self._index, lambda self, x: self.set_index(x))
1018 view = property(lambda self: self._view)
1019
1021 self.mnuFile = gtk.Menu()
1022 self.itemNew = build_menuitem("New", self.__ref(self._onItemNew), menu=self.mnuFile)
1023 self.itemOpen = build_menuitem("Open...", self.__ref(self._onItemOpen), menu=self.mnuFile)
1024 self.mnuRecent = gtk.Menu()
1025 self.itemOpenRecent = build_menuitem("Open recent", submenu=self.mnuRecent, menu=self.mnuFile)
1026 self.itemSave = build_menuitem("Save", self.__ref(self._onItemSave), menu=self.mnuFile)
1027 self.itemSaveAs = build_menuitem("Save as...", self.__ref(self._onItemSaveAs), menu=self.mnuFile)
1028 self.itemRevert = build_menuitem('Revert to saved', self.__ref(self._onItemRevert), menu=self.mnuFile)
1029 self.itemClose = build_menuitem("Close", self.__ref(self._onItemClose), menu=self.mnuFile)
1030 self.itemCloseAll = build_menuitem("Close all", self.__ref(self._onItemCloseAll), menu=self.mnuFile)
1031 # self.itemPrint = build_menuitem("Print...", self.__ref(self._onItemPrint), menu=self.mnuFile)
1032
1034 if self.index >= 0:
1035 self._view = self.views[self.index]
1036 else:
1037 self._view = self.page0
1038 self.itemSave.set_sensitive(self.can_save())
1039 self.itemRevert.set_sensitive(self.can_revert())
1040 self.itemSaveAs.set_sensitive(not (self._file is None))
1041 self.itemClose.set_sensitive(not (self._file is None))
1042 self.itemCloseAll.set_sensitive(not (self._file is None))
1043 self.layMenu.space = self._view
1044 self.layFiles.space = self._view
1045 # self.itemPrint.set_sensitive(not (self._file is None))
1047 self.mnuRecent.foreach(lambda item: self.mnuRecent.remove(item))
1048 for node in qubx.tree.children(self.__recent):
1049 if os.path.exists(str(node.data)):
1050 build_menuitem(gtk_literally(str(node.data)), self.__ref(bind(self.open, str(node.data))), menu=self.mnuRecent)
1051 elif os.path.exists(node.name):
1052 build_menuitem(gtk_literally(node.name), self.__ref(bind(self.open, node.name)), menu=self.mnuRecent)
1053 return True
1057 if (x == self._index) and not force: return
1058 if self._file:
1059 self.done_file(self._file, self._view)
1060 if x >= 0:
1061 self._file = self.table.entries[x]
1062 self._index = x
1063 self.pages.set_current_page(x+1)
1064 self.mnuFiles.label = self.names[x]
1065 self.init_file(self._file, self.views[x])
1066 else:
1067 self._file = self.page0.file
1068 self._index = -1
1069 self.pages.set_current_page(0)
1070 self.mnuFiles.label = '<no file>'
1071 self.init_file(self.page0.file, self.page0)
1072 self._sensitize()
1073 self.layMenu.space = self.view or self.page0
1074 self.layFiles.space = self.view or self.page0
1075 self.OnSwitch(self, self.file)
1076 self.OnChange(self, self.file)
1078 file = self.table.entries[i]
1079 self.views.insert(i, self.make_view(self.table))
1080 self.names.insert(i, os.path.split(self.table.entries[i].path or 'Untitled')[1])
1081 self.lbls.insert(i, gtk.Label(self.names[i]))
1082 self.mnuFiles.menu.insert(i, (self.names[i], lambda: self.set_file(file)))
1083 self.views[i].file = file
1084 self.views[i].mnuFile = self.mnuFile
1085 self.views[i].mnuFiles = self.mnuFiles
1086 self.views[i].controls.add_layer(self.layMenu)
1087 self.views[i].controls.add_layer(self.layFiles)
1088 self.page_list.insert(i, self.make_page(self.views[i]))
1089 self.pages.insert_page(self.page_list[i], self.lbls[i], i+1)
1090 self._sensitize()
1091 if os.path.exists(file.path):
1092 self.add_recent(file.path)
1094 return self.ViewClass(table)
1099 j = None
1100 if i == self.index:
1101 j = i - 1
1102 if j < 0 and self.table.size > 1:
1103 j = 0
1104 self.pages.remove_page(i+1)
1105 self.views[i].controls.remove_layer(self.layMenu)
1106 self.views[i].controls.remove_layer(self.layFiles)
1107 del self.mnuFiles.menu[i]
1108 del self.names[i]
1109 del self.lbls[i]
1110 del self.views[i]
1111 del self.page_list[i]
1112 self.set_index(j, force=True) # in case index stays same but refers to new file
1113 self._sensitize()
1119 self.index = page_num-1
1120 # disabled because obnoxious:
1121 #if self.index >= 0:
1122 # self.table.select(page_num-1, None, self)
1124 self.new()
1126 fname = qubx.GTK.Open('Open...', self.path, self.open, self.get_open_filters(), allow_all_files=True)
1127 if fname:
1128 self.path = os.path.split(fname)[0]
1130 try:
1131 self.save()
1132 #self.file.saveAs(self.file.path)
1133 self.OnSave(self, file, self.file.path)
1134 except IOError, e:
1135 qubx.GTK.ShowMessage("The file couldn't be saved. Try a different name or location.\n\n%s" % str(e), title="Error", parent=self.parent_window)
1137 self.saveAs()
1139 if fname:
1140 self.save(fname)
1141 else:
1142 name = os.path.split(self.file.path)[1]
1143 name = name.replace('<', '').replace('>', '')
1144 fname = qubx.GTK.SaveAs('Save as...', self.path, name, self.save, self.get_save_filters())
1146 dlg = gtk.MessageDialog(None, buttons=gtk.BUTTONS_NONE,
1147 flags=gtk.DIALOG_MODAL,
1148 message_format='Revert: discard all changes to %s?' % os.path.split(self.file.path)[1])
1149 dlg.add_buttons('Yes',gtk.RESPONSE_YES,
1150 'No',gtk.RESPONSE_NO)
1151 response = dlg.run()
1152 dlg.destroy()
1153 if response == gtk.RESPONSE_YES:
1154 if self.global_name:
1155 qubx.pyenv.env.OnScriptable('%s.file.revert_to_saved()' % self.global_name)
1156 self.file.revert_to_saved()
1158 self.close()
1160 node = qubx.settings.SettingsMgr['%s.FilesFace'%self.face_name.strip()].active['ask_close_all']
1161 if not node.data:
1162 node.data = 1
1163 if node.data[0]:
1164 dlg = qubx.GTK.AskOnceDialog('Close All %s?' % self.face_name.strip(), qubx.GTK.get_active_window(),
1165 """Really close all %s files?""" % self.face_name.strip(),
1166 (("No", gtk.RESPONSE_REJECT),
1167 ("Yes", gtk.RESPONSE_ACCEPT)),
1168 dont_show_again=False)
1169 response, dont_show = dlg.run()
1170 dlg.destroy()
1171 if response != gtk.RESPONSE_ACCEPT:
1172 return
1173 node.data = int(not dont_show)
1174 self.close_down()
1176 self.do_print()
1181 if self.promptSave():
1182 file, view = self.file, self.view
1183 self.OnClosing(self.index)
1184 self.on_pre_close()
1185 file.pre_close()
1186 self.table.remove(self.index)
1187 file.dispose()
1188 view.dispose()
1189 qubx.pyenv.env.gc_collect_on_idle()
1190 return True
1191 return False
1195 fname = self.file.path if (path is None) else path
1196 if (not fname) or (fname.lower() == 'untitled'):
1197 return self.saveAs(None) # prompt for destination
1198 for other_view in self.views:
1199 if other_view.file != self.file:
1200 if other_view.file.path == fname:
1201 raise Exception('The file at that location is in use. Please close it first.')
1202 self.on_pre_save(fname)
1203 self.file.saveAs(fname)
1204 self.path = os.path.split(fname)[0] or self.path
1205 self.OnSave(self, self.file, fname)
1206 self.add_recent(self.file.path)
1210 """Returns True if no changes, or asks "save changes" then returns True unless they chose cancel."""
1211 if self.is_changed() and not self._no_to_all:
1212 dlg = gtk.MessageDialog(qubx.GTK.get_active_window(), buttons=gtk.BUTTONS_NONE,
1213 flags=gtk.DIALOG_MODAL,
1214 message_format='Save changes to %s?' % (self.file.path and os.path.split(self.file.path)[1] or 'Untitled'))
1215 dlg.add_buttons('Yes',gtk.RESPONSE_YES,
1216 'No',gtk.RESPONSE_NO,
1217 'No to all',RESPONSE_NO_TO_ALL,
1218 'Cancel',gtk.RESPONSE_CANCEL)
1219 response = dlg.run()
1220 dlg.destroy()
1221 if response == gtk.RESPONSE_YES:
1222 try:
1223 if self.file.path:
1224 self.save()
1225 else:
1226 self.saveAs()
1227 except:
1228 traceback.print_exc()
1229 return self.promptSave() # ask again on failure
1230 elif response == RESPONSE_NO_TO_ALL:
1231 self._no_to_all = True
1232 elif response == gtk.RESPONSE_CANCEL:
1233 return False
1234 return True
1236 """Attempts to close all files; returns True if successful."""
1237 self._no_to_all = False
1238 for i in reversed(xrange(self.table.size)):
1239 self.index = i
1240 if not self.close_one():
1241 return False
1242 return True
1244 for i, view in enumerate(self.views):
1245 if (name == view.file.path) or (name == os.path.split(view.file.path)[1]):
1246 self.index = i
1247 return view.file
1248 return None
1250 node = self.__recent.insert("")
1251 node.data = path
1252 node = node.sibling
1253 i = 1
1254 while not node.isNull:
1255 if (i >= RECENT_FILES_COUNT) or (str(node.data) == path) or (node.name == path):
1256 to_remove, node = node, node.sibling
1257 self.__recent.remove(to_remove)
1258 else:
1259 node = node.sibling
1260 i += 1
1261
1265 #pr = gtk.PrintOperation()
1266 #pr.connect('draw_page', qubx.fitsGTK.PageDrawer(self.fitting.hires))
1267 #pr.set_n_pages(1)
1268 #pr.set_unit(gtk.UNIT_POINTS)
1269 #res = pr.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, self)
1270 #if res == gtk.PRINT_OPERATION_RESULT_APPLY: settings = pr.get_print_settings()
1275 """Override this method to open the file at path and add it to table."""
1276 if path is None:
1277 self._onItemOpen(None)
1288 """Override this method to do things when the file comes onscreen (e.g. subscribe to its events)."""
1289 pass
1294 """Override this method to return a list of (".extension", gtk.FileFilter) to add to the open file dialog."""
1295 return []
1297 """Override this method to return a list of (".extension", gtk.FileFilter) to add to the save file dialog; By default, uses the open_filters."""
1298 return self.get_open_filters()
1299
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Dec 15 19:07:38 2017 | http://epydoc.sourceforge.net |