patch proposal for xfer dialog desc quickfills

Andreas Köhler andi5.py at gmx.net
Wed Dec 7 10:56:43 EST 2005


Hi,


the attachment is a test patch, please do not apply it :)

It is about the description quickfill feature in transfer dialogs,
which was functioning in gnucash 1.8.x, but not in current trunk (at
least not for me). It does not affect the completion in the
register, will look into that afterwards.

If I entered text that was a prefix of the description of an
existing transfer, the dialog completed it and set the cursor to the
beginning of the entry widget.

The current patch only completes characters that are unique among
all possible completions and selects them. Pressing tab sets the
cursors at the end of the entry, pressing it again will tab to the
next entry with appropriate filling of other data fields if possible
(was working already).

Capitalization is adjusted only when there is exactly one possible,
longer word to complete to.

I know that this is not the way gnucash 1.8.x did the quickfilling,
I am just asking for comments. Nevertheless _some_ patch is needed
imho.


-- andi5
-------------- next part --------------
Index: src/gnome-utils/QuickFill.c
===================================================================
--- src/gnome-utils/QuickFill.c	(Revision 12135)
+++ src/gnome-utils/QuickFill.c	(Arbeitskopie)
@@ -124,7 +124,7 @@
   if (qf == NULL)
     return;
 
-  g_hash_table_foreach_remove (qf->matches, destroy_helper, NULL);
+  g_hash_table_foreach_remove (qf->matches, (GHRFunc)destroy_helper, NULL);
 
   if (qf->text)
     gnc_string_cache_remove (qf->text);
@@ -144,6 +144,12 @@
   return qf->text;
 }
 
+gboolean
+gnc_quickfill_is_full_string (QuickFill *qf)
+{
+  return (g_hash_table_size (qf->matches) == 0);
+}
+
 /********************************************************************\
 \********************************************************************/
 
Index: src/gnome-utils/QuickFill.h
===================================================================
--- src/gnome-utils/QuickFill.h	(Revision 12135)
+++ src/gnome-utils/QuickFill.h	(Arbeitskopie)
@@ -74,6 +74,11 @@
  */
 const char * gnc_quickfill_string (QuickFill *qf);
 
+/** Return whether the node 'qf' represents a full string, i.e.
+ *  there are no further matches.
+ */
+gboolean gnc_quickfill_is_full_string (QuickFill *qf);
+
 /** Return the subnode of the tree whose strings all hold 'wc' as
  *  the next letter.  That is, if 'qf' holds all strings starting 
  *  with the letter 'a', and we ask for the letter 'b', then this
Index: src/gnome-utils/dialog-transfer.c
===================================================================
--- src/gnome-utils/dialog-transfer.c	(Revision 12135)
+++ src/gnome-utils/dialog-transfer.c	(Arbeitskopie)
@@ -637,23 +637,22 @@
  * cursor position stored off from the insert_cb.
  */
 static void
-gnc_xfer_description_insert_cb(GtkEntry *entry,
-                               const gchar *insert_text,
-                               const gint insert_text_len,
-                               gint *start_pos,
-                               XferDialog *xferData)
+gnc_xfer_description_insert_cb (GtkEntry *entry,
+                                const gchar *insert_text,
+                                const gint insert_text_len,
+                                gint *start_pos,
+                                XferDialog *xferData)
 {
-  GString *change_text_gs, *new_text_gs;
+  GString *new_text_gs;
   glong old_text_chars, new_text_chars;
-  const char *old_text, *match_str = NULL;
+  gint unique_chars;
+  const gchar *old_text = NULL;
+  const gchar *fill_in_text = NULL;
   QuickFill *match;
-  int i;
-  const char *c;
-  gunichar uc;
 
   xferData->desc_didquickfill = FALSE;
 
-  if ( insert_text_len <= 0 )
+  if (insert_text_len <= 0)
     return;
 
   old_text = gtk_entry_get_text (entry);
@@ -662,44 +661,46 @@
 
   /* If we are inserting in the middle, do nothing */
   old_text_chars = g_utf8_strlen (old_text, -1);
-  if( *start_pos < old_text_chars )
+  if (*start_pos < old_text_chars)
     return;
 
-  change_text_gs = g_string_new_len (insert_text, insert_text_len);
+  new_text_gs = g_string_new (old_text);
+  g_string_append_len (new_text_gs, insert_text, insert_text_len);
+  new_text_chars = g_utf8_strlen (new_text_gs->str, -1);
 
-  /* Construct what the new value of the text entry will be */
-  new_text_gs = g_string_new ("");
-  
-  i = 0;
-  c = old_text;
-  //Copy old text up to insert position
-  while ( *c && ( i < *start_pos ) )
-  {
-    uc = g_utf8_get_char ( c );
-    g_string_append_unichar ( new_text_gs, uc );
-    c = g_utf8_next_char ( c );
-    i++;      
-  }
+  if ( /* search for new_text_gs */
+      (match = gnc_quickfill_get_string_len_match (xferData->qf,
+                                                   new_text_gs->str,
+                                                   new_text_chars)) &&
+      /* determine next unique characters */
+      (match = gnc_quickfill_get_unique_len_match (match, &unique_chars)) &&
+      /* test for a positive number of new unique chars */
+      (unique_chars > 0)) {
 
-  //Copy inserted text
-  g_string_append ( new_text_gs, change_text_gs->str );
+    if (gnc_quickfill_is_full_string (match)) {
+      /* use the matching string with capitalization */
+      fill_in_text = gnc_quickfill_string (match);
 
-  //Copy old text after insert position
-  while ( *c )
-  {
-    uc = g_utf8_get_char ( c );
-    g_string_append_unichar ( new_text_gs, uc );
-    c = g_utf8_next_char ( c );
-  }
+    } else {
+      /* append unique_chars characters from current position on to new_text_gs */
+      glong i = unique_chars;
+      gchar *c = g_utf8_offset_to_pointer (gnc_quickfill_string (match),
+                                           new_text_chars);
 
-  if( ( match = gnc_quickfill_get_string_match( xferData->qf, new_text_gs->str ) )
-   && ( match_str = gnc_quickfill_string( match ) ) 
-   && safe_strcmp( new_text_gs->str, old_text ) )
-  {
+      while (*c && ( i > 0)) {
+        g_string_append_unichar (new_text_gs, g_utf8_get_char (c));
+        i--;
+        c = g_utf8_next_char (c);
+      }
+
+      fill_in_text = new_text_gs->str;
+    }
+
+    /* block this callback while inserting our text */
     g_signal_handlers_block_matched (G_OBJECT (entry),
 				     G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xferData);
 
-    gtk_entry_set_text( entry, match_str );
+    gtk_entry_set_text (entry, fill_in_text);
 
     g_signal_handlers_unblock_matched (G_OBJECT (entry),
 				       G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xferData);
@@ -707,79 +708,17 @@
     /* stop the current insert */
     g_signal_stop_emission_by_name (G_OBJECT (entry), "insert_text");
 
-    /* This doesn't seem to fix the selection problems, why? */
-    gtk_editable_select_region (GTK_EDITABLE(entry), 0, 0);
-#if DRH_NEEDS_INVESTIGATION
-    gtk_old_editable_claim_selection (GTK_OLD_EDITABLE (entry), FALSE, GDK_CURRENT_TIME);
-#endif
-
     /* Store off data for the key_press_cb or
      * the button_release_cb to make use of. */
-    new_text_chars = g_utf8_strlen (new_text_gs->str, -1);
+    *start_pos = new_text_chars;
     xferData->desc_cursor_position = new_text_chars;
     xferData->desc_start_selection = new_text_chars;
     xferData->desc_end_selection = -1;
     xferData->desc_didquickfill = TRUE;
   }
 
-  g_string_free (change_text_gs, TRUE);
   g_string_free (new_text_gs, TRUE);
-  
-}
 
-/* This common post-key press and post-button release handler fixes
- * up the selection and cursor position changes that may be necessary
- * if a quickfill occurred in the insert_cb.
- */
-static gboolean
-common_post_quickfill_handler(guint32 time, XferDialog *xferData )
-{
-  GtkEntry *entry = GTK_ENTRY(xferData->description_entry);
-  gint current_pos;
-  gint current_start;
-  gint current_end;
-  gboolean did_something = FALSE;   /* was the selection or position changed? */
-
-  ENTER(" ");
-  current_pos = gtk_editable_get_position( GTK_EDITABLE(entry) );
-  gtk_editable_get_selection_bounds( GTK_EDITABLE(entry),
-				     &current_start,
-				     &current_end);
-  if( current_pos != xferData->desc_cursor_position )
-  {
-    gtk_entry_set_position( entry, xferData->desc_cursor_position );
-    did_something = TRUE;
-  }
-
-  if( ( current_start != xferData->desc_start_selection ||
-        current_end   != xferData->desc_end_selection      ) &&
-      ( xferData->desc_start_selection != xferData->desc_end_selection ||
-        xferData->desc_start_selection == 0 ) )
-  {
-    gtk_editable_select_region( GTK_EDITABLE(entry),
-				xferData->desc_start_selection,
-				xferData->desc_end_selection );
-#if DRH_NEEDS_INVESTIGATION
-    gtk_old_editable_claim_selection( GTK_OLD_EDITABLE(entry), TRUE, time );
-#endif
-    did_something = TRUE;
-  }
-
-  if( did_something ) 
-  {
-    /* Make sure we don't try to change things again based on these values. */
-    xferData->desc_start_selection = current_start;
-    xferData->desc_end_selection = current_end;
-    xferData->desc_cursor_position = current_pos;
-  }
-
-  /* Make sure a new quickfill must occur before coming back through here,
-   * whether or not we actually did anything in this function.
-   */
-  xferData->desc_didquickfill = FALSE;
-
-  LEAVE("did_something=%d", did_something);
-  return( did_something );
 }
 
 static gboolean
@@ -789,79 +728,47 @@
 {
   gboolean done_with_input = FALSE;
 
-  /* Most "special" keys are allowed to be handled directly by
-   * the entry's key press handler, but in some cases that doesn't
-   * seem to work right, so handle it here.
-   */
   ENTER(" ");
-  switch( event->keyval )
-  {
-    case GDK_Left:        /* right/left cause a focus change which is bad */
-    case GDK_KP_Left:
-    case GDK_Right:
-    case GDK_KP_Right:
-      done_with_input = TRUE;
-      break;
 
-    case GDK_Return:      /* On the first activate, need to
-                           * do the quickfill completion */
-    case GDK_KP_Enter:
-      if( gnc_xfer_dialog_quickfill( xferData ) )
-        done_with_input = TRUE;
-      /* Else if no updates were done, allow the keypress to go through,
-       * which will result in an activate signal for the dialog.
-       */
+  switch (event->keyval) {
+  case GDK_space:
+    /* inhibit further event processing */
+    done_with_input = TRUE;
+    break;
 
-      break;
-
-    case GDK_Tab:
-    case GDK_ISO_Left_Tab:
-      if( !( event->state & GDK_SHIFT_MASK) )    /* Complete on Tab,
-                                                  * but not Shift-Tab */
-      {
-        gnc_xfer_dialog_quickfill( xferData );
-        /* NOT done with input, though, since we need to focus to the next
-         * field.  Unselect the current field, though.
-         */
-        gtk_editable_select_region( GTK_EDITABLE(xferData->description_entry),
-				    0, 0 );
-#if DRH_NEEDS_INVESTIGATION
-        gtk_old_editable_claim_selection( GTK_OLD_EDITABLE(xferData->description_entry),
-                                          FALSE, event->time );
-#endif
+  case GDK_Tab:
+  case GDK_ISO_Left_Tab:
+    if (!(event->state & GDK_SHIFT_MASK)) {  /* Complete on Tab, but not Shift-Tab */
+      gint start_pos, end_pos;
+      gtk_editable_get_selection_bounds (GTK_EDITABLE (xferData->description_entry),
+                                         &start_pos, &end_pos);
+      if (start_pos != end_pos) {
+        gtk_editable_set_position (GTK_EDITABLE (xferData->description_entry), -1);
+        done_with_input = TRUE;
+      } else {
+        gnc_xfer_dialog_quickfill (xferData);
+        /* NOT done with input, though, since we need to focus to the next field. */
       }
-      break;
+    }
+    break;
   }
 
-  /* Common handling for both key presses and button releases
-   * to fix up the selection and cursor position at this point.
-   */
-  if( !done_with_input && xferData->desc_didquickfill )
-    done_with_input = common_post_quickfill_handler( event->time, xferData );
+  /* call default signal handler once */
+  (GTK_WIDGET_CLASS (GTK_ENTRY_GET_CLASS (entry)))->key_press_event (GTK_WIDGET (entry),
+                                                                     event);
+  g_signal_stop_emission_by_name (G_OBJECT (entry), "key_press_event");
 
-  if( done_with_input )
-    g_signal_stop_emission_by_name (G_OBJECT (entry), "key_press_event");
+  if (xferData->desc_didquickfill) {
+    gtk_editable_select_region (GTK_EDITABLE (entry),
+				xferData->desc_start_selection,
+				xferData->desc_end_selection);
+    xferData->desc_didquickfill = FALSE;
+  }
 
   LEAVE("done=%d", done_with_input);
-  return( done_with_input );
+  return done_with_input;
 }
 
-static gboolean
-gnc_xfer_description_button_release_cb( GtkEntry *entry,
-                                        GdkEventButton *event,
-                                        XferDialog *xferData )
-{
-  if( xferData->desc_didquickfill )
-  {
-    /* Common handling for both key presses and button presses
-     * to fix up the selection and cursor position at this point.
-     */
-    common_post_quickfill_handler( event->time, xferData );
-  }
-
-  return( FALSE );
-}
-
 /*** End of quickfill-specific callbacks ***/
 
 static void
@@ -1692,10 +1599,8 @@
     /* Get signals from the Description for quickfill. */
     g_signal_connect (G_OBJECT (entry), "insert_text",
 		      G_CALLBACK (gnc_xfer_description_insert_cb), xferData);
-    g_signal_connect (G_OBJECT (entry), "button_release_event",
-		      G_CALLBACK (gnc_xfer_description_button_release_cb), xferData);
-    g_signal_connect_after (G_OBJECT (entry), "key_press_event",
-			    G_CALLBACK (gnc_xfer_description_key_press_cb), xferData);
+    g_signal_connect (G_OBJECT (entry), "key_press_event",
+                      G_CALLBACK (gnc_xfer_description_key_press_cb), xferData);
 
     entry = glade_xml_get_widget (xml, "memo_entry");
     xferData->memo_entry = entry;


More information about the gnucash-devel mailing list