summaryrefslogblamecommitdiff
path: root/src/lib/elementary/elm_code_widget_selection.c
blob: c8c1dacde88a35c091f093fa4a928eb3622c6e6a (plain) (tree)
1
2
3
4
5
6
7
8
9

                               

      
                       
 
                                    
 
                                                       
 









                                                            





                                                                                           
                      






                                             
                                                                    
 

                        

 






                                                                    
                                                          
 
                                                             











                                                     
                                                                                             








                                                                  
                                                          
 
                                                             











                                                     
                                                                                               

 






                                                             
                                                          
































                                                                   




                                                    
                                                          
 

                      
 
                       
                        
                                                                                               

 
           
                                                                                           


                       
                                               
                 
                                             
 

                                                                        
                                                   

                                                                                                 
                                             
 















                                                  

                                                 
                   


           
                                                                                          


                            
                                                   
                 
                                             
 
                                                            

            

                                                                        
                                              
                                                                                                 
 
                                                                      
                                                     
                                                                                             
 













                                                                          
 
                                                         

                                                  
                                                                        

                                                 
                   

 

                                                                         

                            
                                             
 
                                                          


                      

                                                   
 

                                                                
                                                          
       
                                                         
                                                                                            


                        

                   
                                                                                               

 
         











                                                              




                                                                             
                                                          











                                                                

                  

                      




                                                











                                                                                               
                                                          


























                                                                                                             
           


                                                       

                                             
 
                                                          
 
                      

                       


                                                                

                                                                                                 


                   
 

           
                                                                              
 
                                 
 
                                          








                                                       


                                                  





                                                                                                   
 
                                                                                        






                                                   


                                                  








                                                                                                   
                           

                                    
 
                                                           









                                                                                                           














                                                                

                                                        






                                                    
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif

#include "Elementary.h"

#include "elm_code_widget_private.h"

static char _breaking_chars[] = " \t,.?!;:*&()[]{}'\"";

static Elm_Code_Widget_Selection_Data *
_elm_code_widget_selection_new()
{
   Elm_Code_Widget_Selection_Data *data;

   data = calloc(1, sizeof(Elm_Code_Widget_Selection_Data));

   return data;
}

static void
_elm_code_widget_selection_limit(Evas_Object *widget EINA_UNUSED, Elm_Code_Widget_Data *pd,
                                 unsigned int *row, unsigned int *col)
{
   Elm_Code_Line *line;
   Elm_Code_File *file;
   unsigned int width;

   file = pd->code->file;

   if (*row > elm_code_file_lines_get(file))
     *row = elm_code_file_lines_get(file);

   line = elm_code_file_line_get(file, *row);
   width = elm_code_widget_line_text_column_width_get(widget, line);

   if (*col > width + 1)
     *col = width + 1;
}

EAPI void
elm_code_widget_selection_start(Evas_Object *widget,
                                unsigned int line, unsigned int col)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Widget_Selection_Data *selection;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);

   _elm_code_widget_selection_limit(widget, pd, &line, &col);
   if (!pd->selection)
     {
        selection = _elm_code_widget_selection_new();

        selection->end_line = line;
        selection->end_col = col;

        pd->selection = selection;
     }

   pd->selection->start_line = line;
   pd->selection->start_col = col;
   efl_event_callback_legacy_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_START, widget);
}

EAPI void
elm_code_widget_selection_end(Evas_Object *widget,
                              unsigned int line, unsigned int col)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Widget_Selection_Data *selection;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);

   _elm_code_widget_selection_limit(widget, pd, &line, &col);
   if (!pd->selection)
     {
        selection = _elm_code_widget_selection_new();

        selection->start_line = line;
        selection->start_col = col;

        pd->selection = selection;
     }

   pd->selection->end_line = line;
   pd->selection->end_col = col;
   efl_event_callback_legacy_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CHANGED, widget);
}

EAPI Elm_Code_Widget_Selection_Data *
elm_code_widget_selection_normalized_get(Evas_Object *widget)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Widget_Selection_Data *selection;
   Eina_Bool reverse;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
   selection = _elm_code_widget_selection_new();

   if (!pd->selection)
     {
        selection->start_line = selection->end_line = 1;
        selection->start_col = selection->end_col = 1;

        return selection;
     }

   if (pd->selection->start_line == pd->selection->end_line)
     reverse = pd->selection->start_col > pd->selection->end_col;
   else
     reverse = pd->selection->start_line > pd->selection->end_line;

   if (reverse)
     {
        selection->start_line = pd->selection->end_line;
        selection->start_col = pd->selection->end_col;
        selection->end_line = pd->selection->start_line;
        selection->end_col = pd->selection->start_col;
     }
   else
     {
        selection->start_line = pd->selection->start_line;
        selection->start_col = pd->selection->start_col;
        selection->end_line = pd->selection->end_line;
        selection->end_col = pd->selection->end_col;
     }

   return selection;
}

EAPI void
elm_code_widget_selection_clear(Evas_Object *widget)
{
   Elm_Code_Widget_Data *pd;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);

   if (!pd->selection)
     return;

   free(pd->selection);
   pd->selection = NULL;
   efl_event_callback_legacy_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CLEARED, widget);
}

static void
_elm_code_widget_selection_delete_single(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
{
   Elm_Code_Line *line;
   const char *old;
   unsigned int old_length, start, end, length;
   char *content;
   Elm_Code_Widget_Selection_Data *selection;

   selection = elm_code_widget_selection_normalized_get(widget);
   line = elm_code_file_line_get(pd->code->file, selection->start_line);
   old = elm_code_line_text_get(line, &old_length);
   start = elm_code_widget_line_text_position_for_column_get(widget, line, selection->start_col);
   end = elm_code_widget_line_text_position_for_column_get(widget, line, selection->end_col);
   length = line->length - (end - start + 1);

   if (end == line->length)
     {
        length = line->length - (end - start);

        content = malloc(sizeof(char) * length);
        strncpy(content, old, start);
     }
   else
     {
        length = line->length - (end - start + 1);

        content = malloc(sizeof(char) * length);
        strncpy(content, old, start);
        strncpy(content + start, old + end + 1,
                old_length - (end + 1));
      }
   elm_code_line_text_set(line, content, length);
   free(content);
   free(selection);
}

static void
_elm_code_widget_selection_delete_multi(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
{
   Elm_Code_Line *line;
   const char *first, *last;
   unsigned int last_length, start, end, length, i;
   char *content;
   Elm_Code_Widget_Selection_Data *selection;

   if (pd->selection->end_line == pd->selection->start_line)
     return;

   selection = elm_code_widget_selection_normalized_get(widget);
   line = elm_code_file_line_get(pd->code->file, selection->start_line);
   first = elm_code_line_text_get(line, NULL);
   start = elm_code_widget_line_text_position_for_column_get(widget, line, selection->start_col);

   line = elm_code_file_line_get(pd->code->file, selection->end_line);
   last = elm_code_line_text_get(line, &last_length);
   end = elm_code_widget_line_text_position_for_column_get(widget, line, selection->end_col);

   if (last_length == end)
     {
        length = start + last_length - end;
        content = malloc(sizeof(char) * length);
        strncpy(content, first, start);
     }
   else
     {
        length = start + last_length - (end + 1);
        content = malloc(sizeof(char) * length);
        strncpy(content, first, start);

        strncpy(content + start, last + end + 1, last_length - (end + 1));
     }

   for (i = line->number; i > selection->start_line; i--)
     elm_code_file_line_remove(pd->code->file, i);

   line = elm_code_file_line_get(pd->code->file, selection->start_line);
   elm_code_line_text_set(line, content, length);
   free(content);
   free(selection);
}

void
_elm_code_widget_selection_delete_do(Evas_Object *widget, Eina_Bool undo)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Widget_Selection_Data *selection;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);

   if (!pd->selection)
     return;
   if (undo)
     _elm_code_widget_change_selection_add(widget);

   selection = elm_code_widget_selection_normalized_get(widget);
   if (selection->start_line == selection->end_line)
     _elm_code_widget_selection_delete_single(widget, pd);
   else
     _elm_code_widget_selection_delete_multi(widget, pd);
   elm_code_widget_cursor_position_set(widget, selection->start_line, selection->start_col);

   free(pd->selection);
   pd->selection = NULL;
   free(selection);

   efl_event_callback_legacy_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_SELECTION_CLEARED, widget);
}

EAPI void
elm_code_widget_selection_delete(Evas_Object *widget)
{
   _elm_code_widget_selection_delete_do(widget, EINA_TRUE);
}

void
_elm_code_widget_selection_delete_no_undo(Evas_Object *widget)
{
   _elm_code_widget_selection_delete_do(widget, EINA_FALSE);
}

EAPI void
elm_code_widget_selection_select_line(Evas_Object *widget, unsigned int line)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Line *lineobj;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
   lineobj = elm_code_file_line_get(pd->code->file, line);

   if (!lineobj)
     return;

   elm_code_widget_selection_start(widget, line, 1);
   elm_code_widget_selection_end(widget, line, lineobj->length);
}

static Eina_Bool
_elm_code_widget_selection_char_breaks(char chr)
{
   unsigned int i;

   if (chr == 0)
     return EINA_TRUE;

   for (i = 0; i < sizeof(_breaking_chars); i++)
     if (chr == _breaking_chars[i])
       return EINA_TRUE;


   return EINA_FALSE;
}

EAPI void
elm_code_widget_selection_select_word(Evas_Object *widget, unsigned int line, unsigned int col)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Line *lineobj;
   unsigned int colpos, length, pos;
   const char *content;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
   lineobj = elm_code_file_line_get(pd->code->file, line);
   content = elm_code_line_text_get(lineobj, &length);

   _elm_code_widget_selection_limit(widget, pd, &line, &col);
   colpos = elm_code_widget_line_text_position_for_column_get(widget, lineobj, col);

   pos = colpos;
   while (pos > 0)
     {
        if (_elm_code_widget_selection_char_breaks(content[pos - 1]))
          break;
        pos--;
     }
   elm_code_widget_selection_start(widget, line,
                                   elm_code_widget_line_text_column_width_to_position(widget, lineobj, pos));

   pos = colpos;
   while (pos < length - 1)
     {
        if (_elm_code_widget_selection_char_breaks(content[pos + 1]))
          break;
        pos++;
     }
   elm_code_widget_selection_end(widget, line,
                                 elm_code_widget_line_text_column_width_to_position(widget, lineobj, pos));
}

EAPI char *
elm_code_widget_selection_text_get(Evas_Object *widget)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Widget_Selection_Data *selection;
   char *text;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);

   if (!pd->selection)
     return strdup("");

   selection = elm_code_widget_selection_normalized_get(widget);

   text = elm_code_widget_text_between_positions_get(widget,
                                                     selection->start_line, selection->start_col,
                                                     selection->end_line, selection->end_col);

   free(selection);
   return text;
}

static void
_selection_loss_cb(void *data EINA_UNUSED, Elm_Sel_Type selection EINA_UNUSED)
{
   /* Elm_Code_Widget *widget; */

   /* widget = (Elm_Code_Widget *)data; */
// TODO we need to know whih selection we are clearing!
//   elm_code_widget_selection_clear(widget);
}

EAPI void
elm_code_widget_selection_cut(Evas_Object *widget)
{
   char *text;

   if (elm_code_widget_selection_is_empty(widget))
     return;

   text = elm_code_widget_selection_text_get(widget);
   elm_cnp_selection_set(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, text, strlen(text));
   elm_cnp_selection_loss_callback_set(widget, ELM_SEL_TYPE_CLIPBOARD, _selection_loss_cb, widget);
   free(text);

   elm_code_widget_selection_delete(widget);

   efl_event_callback_legacy_call(widget, ELM_OBJ_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
}

EAPI void
elm_code_widget_selection_copy(Evas_Object *widget)
{
   char *text;

   if (elm_code_widget_selection_is_empty(widget))
     return;

   text = elm_code_widget_selection_text_get(widget);
   elm_cnp_selection_set(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, text, strlen(text));
   elm_cnp_selection_loss_callback_set(widget, ELM_SEL_TYPE_CLIPBOARD, _selection_loss_cb, widget);
   free(text);
}

static Eina_Bool
_selection_paste_cb(void *data, Evas_Object *obj EINA_UNUSED, Elm_Selection_Data *ev)
{
   Elm_Code_Widget *widget;

   widget = (Elm_Code_Widget *)data;

   elm_code_widget_text_at_cursor_insert(widget, ev->data);
   return EINA_TRUE;
}

EAPI void
elm_code_widget_selection_paste(Evas_Object *widget)
{
   elm_code_widget_selection_delete(widget);

   elm_cnp_selection_get(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, _selection_paste_cb, widget);
}

EAPI Eina_Bool
elm_code_widget_selection_is_empty(Evas_Object *widget)
{
   Elm_Code_Widget_Data *pd;
   Elm_Code_Widget_Selection_Data *selection;
   Eina_Bool ret = EINA_FALSE;

   pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);

   if (!pd->selection)
     return EINA_TRUE;

   selection = elm_code_widget_selection_normalized_get(widget);

   // check there is no space between start and end
   if (selection->start_col == selection->end_col + 1 &&
       selection->start_line == selection->end_line)
     ret = EINA_TRUE;

   free(selection);

   return ret;
}