Commits

Steve Borho committed 729dddf Merge

merge with TK

Comments (0)

Files changed (4)

         buttons = (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
         super(EmailDialog, self).__init__(flags=gtk.DIALOG_MODAL, 
                                            buttons=buttons)
-        #shlib.set_tortoise_icon(self, 'menucheckout.ico')
+        shlib.set_tortoise_icon(self, 'hg.ico')
         self.root = root
         self.revargs = revargs
         
-        self._button_conf = gtk.Button('Preferences', gtk.STOCK_PREFERENCES)
-        self._button_conf.connect('clicked', self._on_conf_clicked)
-        self.action_area.pack_end(self._button_conf)
-
-        self._button_send = gtk.Button('Send', gtk.STOCK_OK)
-        self._button_send.connect('clicked', self._on_send_clicked)
-        self.action_area.pack_end(self._button_send)
+        self.tbar = gtk.Toolbar()
+        tbuttons = [
+                self._toolbutton(gtk.STOCK_GOTO_LAST, 'Send',
+                                 self._on_send_clicked),
+                gtk.SeparatorToolItem(),
+                self._toolbutton(gtk.STOCK_PREFERENCES, 'configure',
+                                 self._on_conf_clicked),
+                gtk.SeparatorToolItem(),
+            ]
+        for btn in tbuttons:
+            self.tbar.insert(btn, -1)
+        self.vbox.pack_start(self.tbar, False, False, 2)
 
         # set dialog title
         title = "Email Mercurial Patches"
         vbox = gtk.VBox()
         flagframe.add(vbox)
 
+        self.tooltips = gtk.Tooltips()
         self._git = gtk.CheckButton("Use extended (git) patch format")
         vbox.pack_start(self._git, True, True, 4)
+        self.tooltips.set_tip(self._git, 
+                'Git patches can describe binary files, copies, and'
+                ' permission changes, but recipients may not be able to'
+                ' use them if they are not using git or Mercurial.')
 
         self._plain = gtk.CheckButton("Plain, do not prepend HG header")
         vbox.pack_start(self._plain, True, True, 4)
+        self.tooltips.set_tip(self._plain, 
+                'Stripping Mercurial header removes username and parent'
+                ' information.  Only useful if recipient is not using'
+                ' Mercurial (and does not like to see the headers).')
 
         self._bundle = gtk.CheckButton("Send single binary bundle, not patches")
         vbox.pack_start(self._bundle, True, True, 4)
+        self.tooltips.set_tip(self._bundle, 
+                'Bundles store complete changesets in binary form.'
+                ' Upstream users can pull from them. This is the safest'
+                ' way to send changes to recipient Mercurial users.')
 
         self.descview = gtk.TextView(buffer=None)
         self.descview.set_editable(True)
         scrolledwindow = gtk.ScrolledWindow()
         scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
         scrolledwindow.add(self.descview)
-        frame = gtk.Frame('Patch Series Description')
+        frame = gtk.Frame('Patch Series (Bundle) Description')
         frame.set_border_width(4)
-        frame.add(scrolledwindow)
+        hbox = gtk.HBox()
+        hbox.pack_start(scrolledwindow, True, True, 4)
+        frame.add(hbox)
         self.vbox.pack_start(frame, True, True, 4)
+        self.tooltips.set_tip(frame, 
+                'Patch series description is sent in initial summary'
+                ' email with [PATCH 0 of N] header.  It should describe'
+                ' the effects of the entire patch series.  When emailing'
+                ' a bundle, this description makes up the message body.'
+                ' This field is _unused_ when sending a single patch')
+
         self.connect('map_event', self._on_window_map_event)
 
+    def _toolbutton(self, stock, label, handler, menu=None, userdata=None):
+        if menu:
+            tbutton = gtk.MenuToolButton(stock)
+            tbutton.set_menu(menu)
+        else:
+            tbutton = gtk.ToolButton(stock)
+            
+        tbutton.set_label(label)
+        tbutton.connect('clicked', handler, userdata)
+        return tbutton
+        
     def _on_window_map_event(self, event, param):
         self._refresh()
 
         fill_history(history, self._fromlist, 'email.from')
 
         # See if user has set flags in defaults.email
+        self._git.set_sensitive(True)
+        self._bundle.set_sensitive(True)
+        self._plain.set_sensitive(True)
         defaults = repo.ui.config('defaults', 'email', '').split()
-        if '-g' in defaults:      self._git.set_active(True)
-        if '-b' in defaults:      self._bundle.set_active(True)
-        if '--plain' in defaults: self._plain.set_active(True)
+        for flag in defaults:
+            if flag in ('-g', '--git'):
+                self._git.set_active(True)
+                self._git.set_sensitive(False)
+            if flag in ('-b', '--bundle'):
+                self._bundle.set_active(True)
+                self._bundle.set_sensitive(False)
+            if flag in ('--plain'):
+                self._plain.set_active(True)
+                self._plain.set_sensitive(False)
 
-    def _on_conf_clicked(self, button):
+    def _on_conf_clicked(self, button, userdata):
         dlg = ConfigDialog(self.root, False, 'email.from')
         dlg.show_all()
         dlg.run()
         dlg.hide()
         self._refresh()
 
-    def _on_send_clicked(self, button):
+    def _on_send_clicked(self, button, userdata):
         def record_new_value(cpath, history, newvalue):
             if cpath not in history:
                 history[cpath] = []
                 self._refresh()
                 return
 
-        '''
-        editor = self.repo.ui.geteditor()
-        if editor in ('vi', 'vim'):
-            info_dialog('Info required', 'Please configure a visual editor')
-            dlg = ConfigDialog(self.root, False, 'ui.editor')
-            dlg.show_all()
-            dlg.run()
-            dlg.hide()
-            self._refresh()
-            return
-        '''
-
         history = shlib.read_history()
         record_new_value('email.to', history, totext)
         record_new_value('email.cc', history, cctext)
                                  self._push_menu()),
                 self._toolbutton(gtk.STOCK_GOTO_LAST,
                                  'email',
-                                 self._email_clicked,
-                                 self._email_menu()),
+                                 self._email_clicked),
                 gtk.SeparatorToolItem(),
                 self._toolbutton(gtk.STOCK_PREFERENCES,
                                  'configure',
-                                 self._conf_clicked,
-                                 self._conf_menu()),
+                                 self._conf_clicked),
                 gtk.SeparatorToolItem(),
             ]
         for btn in tbuttons:
         menu.show_all()
         return menu
         
-    def _email_menu(self):
-        return None
-
-    def _conf_menu(self):
-        return None
-
     def _get_paths(self):
         """ retrieve repo revisions """
         try:

hggtk/thgconfig.py

                 error_dialog('No repository found', 'no repo at ' + root)
                 self.response(gtk.RESPONSE_CANCEL)
 
+        # Catch close events
+        self.connect('delete-event', self._delete)
+        self.connect('response', self._response)
+
         if configrepo:
             self.ui = repo.ui
             self.rcpath = [os.sep.join([repo.root, '.hg', 'hgrc'])]
             self.rcpath = util.user_rcpath()
             self.set_title('TortoiseHg Configure User-Global Settings')
 
-        #shlib.set_tortoise_icon(self, 'menurepobrowse.ico')
+        shlib.set_tortoise_icon(self, 'hg.ico')
         self.ini = self.load_config(self.rcpath)
 
         # Create a new notebook, place the position of the tabs
         self._btn_apply.connect('clicked', self._apply_clicked)
         self.action_area.pack_end(self._btn_apply)
 
+        self.dirty = False
         self.pages = []
+        self.tooltips = gtk.Tooltips()
         self.history = shlib.read_history()
 
         # create pages for each section of configuration file
 
         self._paths_info = (
                 ('default', 'paths.default', [],
-'''Directory or URL to use when pulling if no source is specified.
-Default is set to repository from which the current repository was cloned.'''),
+'Directory or URL to use when pulling if no source is specified.'
+' Default is set to repository from which the current repository was cloned.'),
                 ('default-push', 'paths.default-push', [],
-'''Optional. Directory or URL to use when pushing if no
-destination is specified.'''))
+'Optional. Directory or URL to use when pushing if no'
+' destination is specified.'''))
         self.paths_frame = self.add_page(notebook, 'Paths')
         vbox = self.fill_frame(self.paths_frame, self._paths_info)
 
         column = gtk.TreeViewColumn('Peer Repository Paths',
                 gtk.CellRendererText(), text=2)
         self.pathtree.append_column(column) 
+        scrolledwindow = gtk.ScrolledWindow()
+        scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolledwindow.add(self.pathtree)
+        vbox.add(scrolledwindow)
 
         self.pathlist = []
         if 'paths' in list(self.ini):
             for name in self.ini['paths']:
                 if name in ('default', 'default-push'): continue
                 self.pathlist.append((name, self.ini['paths'][name]))
-        vbox.add(self.pathtree)
         self.curpathrow = 0
 
-        # TODO add scrollable window to tree view
         buttonbox = gtk.HBox()
         self.addButton = gtk.Button("Add")
         self.addButton.connect('clicked', self._add_path)
         self._delpathbutton.connect('clicked', self._remove_path)
         buttonbox.pack_start(self._delpathbutton)
 
+        self._refreshpathbutton = gtk.Button("Refresh")
+        self._refreshpathbutton.connect('clicked', self._refresh_path)
+        buttonbox.pack_start(self._refreshpathbutton)
+
         self._testpathbutton = gtk.Button("Test")
         self._testpathbutton.connect('clicked', self._test_path)
         buttonbox.pack_start(self._testpathbutton)
 
-        self._refreshpathbutton = gtk.Button("Refresh")
-        self._refreshpathbutton.connect('clicked', self._refresh_path)
-        buttonbox.pack_start(self._refreshpathbutton)
-
         hbox = gtk.HBox()
         self._pathnameedit = gtk.Entry()
         hbox.pack_start(gtk.Label('Name:'), False, False, 4)
                 ('Max Changes', 'web.maxfiles', ['10'],
                     'Maximum number of changes to list on the changelog.'),
                 ('Allow Push', 'ui.allow_push', ['*'],
-'''Whether to allow pushing to the repository. If empty or not
-set, push is not allowed. If the special value "*", any remote
-user can push, including unauthenticated users. Otherwise, the
-remote user must have been authenticated, and the authenticated
-user name must be present in this list (separated by whitespace
-or ","). The contents of the allow_push list are examined after
-the deny_push list.'''),
+'Whether to allow pushing to the repository. If empty or not'
+' set, push is not allowed. If the special value "*", any remote'
+' user can push, including unauthenticated users. Otherwise, the'
+' remote user must have been authenticated, and the authenticated'
+' user name must be present in this list (separated by whitespace'
+' or ","). The contents of the allow_push list are examined after'
+' the deny_push list.'),
                 ('Deny Push', 'ui.deny_push', ['*'],
-'''Whether to deny pushing to the repository. If empty or not set,
-push is not denied. If the special value "*", all remote users
-are denied push. Otherwise, unauthenticated users are all
-denied, and any authenticated user name present in this list
-(separated by whitespace or ",") is also denied. The contents
-of the deny_push list are examined before the allow_push list.'''),
+'Whether to deny pushing to the repository. If empty or not set,'
+' push is not denied. If the special value "*", all remote users'
+' are denied push. Otherwise, unauthenticated users are all'
+' denied, and any authenticated user name present in this list'
+' (separated by whitespace or ",") is also denied. The contents'
+' of the deny_push list are examined before the allow_push list.'),
                 ('Encoding', 'web.encoding', ['UTF-8'],
                     'Character encoding name'))
         self.web_frame = self.add_page(notebook, 'Web')
                     'Comma-separated list of blind carbon copy recipients'' ' +
                     'email addresses'),
                 ('method:', 'email.method', ['smtp'],
-'''Optional. Method to use to send email messages. If value is "smtp" (default),
-use SMTP (configured below).  Otherwise, use as name of program to run that
-acts like sendmail (takes "-f" option for sender, list of recipients on command
-line, message on stdin). Normally, setting this to "sendmail" or
-"/usr/sbin/sendmail" is enough to use sendmail to send messages.'''),
+'Optional. Method to use to send email messages. If value is "smtp" (default),'
+' use SMTP (configured below).  Otherwise, use as name of program to run that'
+' acts like sendmail (takes "-f" option for sender, list of recipients on'
+' command line, message on stdin). Normally, setting this to "sendmail" or'
+' "/usr/sbin/sendmail" is enough to use sendmail to send messages.'),
                 ('SMTP Host:', 'smtp.host', [], 'Host name of mail server'),
                 ('SMTP Port:', 'smtp.port', ['25'],
                     'Port to connect to on mail server. Default: 25'),
                     ['gpyfm', 'kdiff3', 'tortoisemerge', 'p4merge',
                         'meld', 'tkdiff', 'filemerge', 'ecmerge',
                         'xxdiff', 'guiffy', 'diffmerge'],
-'''Textual merge program for resolving merge conflicts.  If left
-unspecified, the hgmerge wrapper will use the first applicable
-tool it finds on your system'''),)
+'Textual merge program for resolving merge conflicts.  If left'
+' unspecified, the hgmerge wrapper will use the first applicable'
+' tool it finds on your system'),)
         self.hgmerge_frame = self.add_page(notebook, 'Merge')
         self.fill_frame(self.hgmerge_frame, self._hgmerge_info)
         # TODO add ability to specify file extension based merge tools
                         self.notebook.set_current_page(page_num)
                         widgets[w].grab_focus()
 
+        # Force dialog into clean state in the beginning
+        self._btn_apply.set_sensitive(False)
+        self.dirty = False
+
+    def _delete(self, widget, event):
+        return True
+
+    def _response(self, widget, response_id):
+        if self.dirty:
+            if question_dialog('Quit without saving?',
+                'Yes to lose changes, No to continue') != gtk.RESPONSE_YES:
+                widget.emit_stop_by_name('response')
+
+    def dirty_event(self, *args):
+        if not self.dirty:
+            self._btn_apply.set_sensitive(True)
+            self.dirty = True
+
     def _add_path(self, *args):
         if len(self.pathlist):
             self.pathlist.append(self.pathlist[self.curpathrow])
         self.curpathrow = len(self.pathlist)-1
         self.refresh_path_list()
         self._pathnameedit.grab_focus()
+        self.dirty_event()
 
     def _remove_path(self, *args):
         del self.pathlist[self.curpathrow]
         if self.curpathrow > len(self.pathlist)-1:
             self.curpathrow = len(self.pathlist)-1
         self.refresh_path_list()
+        self.dirty_event()
 
     def _test_path(self, *args):
         testpath = self._pathpathedit.get_text()
         self.pathlist[self.curpathrow] = (self._pathnameedit.get_text(),
                 self._pathpathedit.get_text())
         self.refresh_path_list()
+        self.dirty_event()
 
     def _pathlist_rowchanged(self, sel):
         model, iter = sel.get_selected()
              self.pathsel.select_path(self.curpathrow)
 
     def fill_frame(self, frame, info):
-        tooltips = gtk.Tooltips()
         widgets = []
         vbox = gtk.VBox()
         frame.add(vbox)
         for label, cpath, values, tooltip in info:
             vlist = gtk.ListStore(str)
             combo = gtk.ComboBoxEntry(vlist, 0)
+            combo.connect("changed", self.dirty_event)
             widgets.append(combo)
 
             # Get currently configured value from this config file
             if curvalue is None:
                 combo.set_active(0)
             elif currow is None:
-                combo.get_child().set_text(curvalue)
+                combo.child.set_text(curvalue)
             else:
                 combo.set_active(currow)
 
             lbl = gtk.Label(label)
             hbox = gtk.HBox()
-            tooltips.set_tip(combo, tooltip)
-            tooltips.set_tip(lbl, tooltip)
+            self.tooltips.set_tip(combo, tooltip)
+            self.tooltips.set_tip(lbl, tooltip)
             hbox.pack_start(lbl, False, False, 4)
             hbox.pack_start(combo, True, True, 4)
             vbox.pack_start(hbox, False, False, 4)
         # Flush changes on all pages
         for vbox, info, widgets in self.pages:
             for w, (label, cpath, values, tip) in enumerate(info):
-                newvalue = widgets[w].get_child().get_text()
+                newvalue = widgets[w].child.get_text()
                 self.record_new_value(cpath, newvalue)
 
         shlib.save_history(self.history)
             f.close()
         except IOError, e:
             error_dialog('Unable to write back configuration file', str(e))
+
+        self._btn_apply.set_sensitive(False)
+        self.dirty = False
         return 0
 
 def run(root='', cmdline=[], **opts):

hggtk/tracelog.py

 import threading
 import Queue
 import win32trace
+import shlib
 
 class TraceLog():
     def __init__(self):
         self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
         self.window.set_title("Python Trace Collector")
+        shlib.set_tortoise_icon(self.window, 'hg.ico')
         
         # construct window
         self.window.set_default_size(700, 400)