1 /*  shiftcolumn.c - a Geany plugin
2  *
3  *  Copyright 2009 Andrew L Janke <a.janke@gmail.com>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  *  MA 02110-1301, USA.
19  */
20 
21 
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 
26 #include <geanyplugin.h>
27 
28 #ifdef HAVE_LOCALE_H
29 # include <locale.h>
30 #endif
31 
32 #include <glib.h>
33 #include <glib/gprintf.h>
34 #include <gdk/gdkkeysyms.h>
35 
36 GeanyPlugin     *geany_plugin;
37 GeanyData       *geany_data;
38 
39 PLUGIN_VERSION_CHECK(224)
40 PLUGIN_SET_TRANSLATABLE_INFO(LOCALEDIR, GETTEXT_PACKAGE,
41     _("Shift Column"),
42     _("Shift a selection left and right."
43       "\nThis plugin currently has no maintainer. Would you like to help "
44       "by contributing to this plugin?"),
45     VERSION, "Andrew L Janke <a.janke@gmail.com>")
46 
47 
48 static GtkWidget *menu_item_shift_left = NULL;
49 static GtkWidget *menu_item_shift_right = NULL;
50 
51 /* Keybinding(s) */
52 enum{
53    KB_SHIFT_LEFT,
54    KB_SHIFT_RIGHT,
55    KB_COUNT
56    };
57 
58 
shift_left_cb(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)59 static void shift_left_cb(G_GNUC_UNUSED GtkMenuItem *menuitem,
60                           G_GNUC_UNUSED gpointer gdata){
61    gchar *txt;
62    gchar *txt_i;
63    gchar char_before;
64 
65    gint startpos;
66    gint endpos;
67 
68    gint startline;
69    gint endline;
70    gint line_iter;
71    gint linepos;
72    gint linelen;
73 
74    gint startcol;
75    gint endcol;
76 
77    gint i;
78 
79    gint n_spaces;
80    gchar *spaces;
81 
82    ScintillaObject *sci;
83 
84    /* get a pointer to the scintilla object */
85    sci = document_get_current()->editor->sci;
86 
87    if (sci_has_selection(sci)){
88 
89       startpos = sci_get_selection_start(sci);
90       endpos = sci_get_selection_end(sci);
91 
92       /* sanity check -- we dont care which way the block was selected */
93       if(startpos > endpos){
94          i = endpos;
95          endpos = startpos;
96          startpos = i;
97          }
98 
99       startline = sci_get_line_from_position(sci, startpos);
100       /* Setting also start point for 1st line */
101       linepos = sci_get_position_from_line(sci, startline);
102       endline = sci_get_line_from_position(sci, endpos);
103 
104       /* normal mode */
105       if(startline == endline){
106 
107          /* get the text in question */
108          txt_i = sci_get_selection_contents(sci);
109 
110          char_before = sci_get_char_at(sci, startpos - 1);
111 
112          /* set up new text buf */
113          txt = g_strdup_printf("%s%c", txt_i, char_before);
114 
115          /* start undo */
116          sci_start_undo_action(sci);
117 
118          /* put the new text in */
119          sci_set_selection_start(sci, startpos - 1);
120          sci_set_selection_end(sci, endpos);
121          sci_replace_sel(sci, txt);
122 
123          /* select the right bit again */
124          sci_set_selection_start(sci, startpos - 1);
125          sci_set_selection_end(sci, endpos - 1);
126 
127          /* end undo */
128          sci_end_undo_action(sci);
129 
130          g_free(txt);
131          g_free(txt_i);
132          }
133 
134       /* rectangle mode (we hope!) */
135       else{
136          startcol = sci_get_col_from_position(sci, startpos);
137          endcol = sci_get_col_from_position(sci, endpos);
138 
139          /* return early for the trivial case */
140          if(startcol == 0 || startcol == endcol){
141             return;
142             }
143 
144          /* start undo */
145          sci_start_undo_action(sci);
146 
147          for(line_iter = startline; line_iter <= endline; line_iter++){
148             linepos = sci_get_position_from_line(sci, line_iter);
149             linelen = sci_get_line_length(sci, line_iter);
150 
151             /* do we need to do something */
152             if(linelen >= startcol - 1 ){
153 
154                /* if between the two columns */
155                /* pad to the end first */
156                if(linelen <= endcol){
157 
158                   /* bung in some spaces -- sorry, I dont like tabs */
159                   n_spaces = endcol - linelen + 1;
160                   spaces = g_malloc(sizeof(gchar) * (n_spaces + 1));
161                   for(i = 0; i < n_spaces; i++){
162                      spaces[i] = ' ';
163                      }
164                   spaces[i] = '\0';
165 
166                   sci_insert_text(sci, linepos + linelen - 1, spaces);
167                   g_free(spaces);
168                   }
169 
170                /* now move the text itself */
171                sci_set_selection_mode(sci, 0);
172                sci_set_selection_start(sci, linepos + startcol);
173                sci_set_selection_end(sci, linepos + endcol);
174 
175                txt_i = sci_get_selection_contents(sci);
176                char_before = sci_get_char_at(sci, linepos + startcol - 1);
177 
178                /* set up new text buf */
179                txt = g_strdup_printf("%s%c", txt_i, char_before);
180 
181                /* put the new text in */
182                sci_set_selection_start(sci, linepos + startcol - 1);
183                sci_replace_sel(sci, txt);
184 
185                g_free(txt);
186                g_free(txt_i);
187                }
188             }
189 
190          /* put the selection box back */
191          /* here we rely upon the last result of linepos */
192          sci_set_selection_mode(sci, 1);
193          sci_set_selection_start(sci, startpos - 1);
194          sci_set_selection_end(sci, linepos + endcol - 1);
195 
196          /* end undo action */
197          sci_end_undo_action(sci);
198          }
199 
200       }
201    }
202 
shift_right_cb(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)203 static void shift_right_cb(G_GNUC_UNUSED GtkMenuItem *menuitem,
204                            G_GNUC_UNUSED gpointer gdata){
205    gchar *txt;
206    gchar *txt_i;
207    gchar char_after;
208 
209    gint startpos;
210    gint endpos;
211 
212    gint startline;
213    gint endline;
214    gint line_iter;
215    gint linepos;
216    gint linelen;
217 
218    gint startcol;
219    gint endcol;
220 
221    gint i;
222 
223    ScintillaObject *sci;
224 
225    /* get a pointer to the scintilla object */
226    sci = document_get_current()->editor->sci;
227 
228    if (sci_has_selection(sci)){
229 
230       startpos = sci_get_selection_start(sci);
231       endpos = sci_get_selection_end(sci);
232 
233       /* sanity check -- we dont care which way the block was selected */
234       if(startpos > endpos){
235          i = endpos;
236          endpos = startpos;
237          startpos = i;
238          }
239 
240       startline = sci_get_line_from_position(sci, startpos);
241       linepos = sci_get_position_from_line(sci, startline);
242       endline = sci_get_line_from_position(sci, endpos);
243 
244       /* normal mode */
245       if(startline == endline){
246 
247          /* get the text in question */
248          txt_i = sci_get_selection_contents(sci);
249 
250          char_after = sci_get_char_at(sci, endpos);
251 
252          /* set up new text buf */
253          txt = g_strdup_printf("%c%s", char_after, txt_i);
254 
255          /* start undo */
256          sci_start_undo_action(sci);
257 
258          /* put the new text in */
259          sci_set_selection_start(sci, startpos);
260          sci_set_selection_end(sci, endpos + 1);
261          sci_replace_sel(sci, txt);
262 
263          /* select the right bit again */
264          sci_set_selection_start(sci, startpos + 1);
265          sci_set_selection_end(sci, endpos + 1);
266 
267          /* end undo */
268          sci_end_undo_action(sci);
269 
270          g_free(txt);
271          g_free(txt_i);
272          }
273 
274       /* rectangle mode (we hope!) */
275       else{
276          startcol = sci_get_col_from_position(sci, startpos);
277          endcol = sci_get_col_from_position(sci, endpos);
278 
279          /* start undo */
280          sci_start_undo_action(sci);
281 
282          for(line_iter = startline; line_iter <= endline; line_iter++){
283             linepos = sci_get_position_from_line(sci, line_iter);
284             linelen = sci_get_line_length(sci, line_iter);
285 
286             /* do we need to do something */
287             if(linelen >= startcol - 1 ){
288 
289                /* if between the two columns or at the end */
290                /* add in a space */
291                if(linelen <= endcol || linelen - 1 == endcol){
292                   txt = g_malloc(sizeof(gchar) * 2);
293                   sprintf(txt, " ");
294 
295                   sci_insert_text(sci, linepos + startcol, txt);
296                   g_free(txt);
297                   }
298 
299                else{
300                   /* move the text itself */
301                   sci_set_selection_mode(sci, 0);
302                   sci_set_selection_start(sci, linepos + startcol);
303                   sci_set_selection_end(sci, linepos + endcol);
304 
305                   txt_i = sci_get_selection_contents(sci);
306                   char_after = sci_get_char_at(sci, linepos + endcol);
307 
308                   /* set up new text buf */
309                   txt = g_strdup_printf("%c%s", char_after, txt_i);
310 
311                   /* put the new text in */
312                   sci_set_selection_end(sci, linepos + endcol + 1);
313                   sci_replace_sel(sci, txt);
314 
315                   g_free(txt);
316                   g_free(txt_i);
317                   }
318                }
319             }
320 
321          /* put the selection box back */
322          /* here we rely upon the last result of linepos */
323          sci_set_selection_mode(sci, 1);
324          sci_set_selection_start(sci, startpos + 1);
325          sci_set_selection_end(sci, linepos + endcol + 1);
326 
327          /* end undo action */
328          sci_end_undo_action(sci);
329          }
330       }
331    }
332 
333 
kb_shift_left(G_GNUC_UNUSED guint key_id)334 static void kb_shift_left(G_GNUC_UNUSED guint key_id){
335 
336    /* sanity check */
337    if (document_get_current() == NULL){
338        return;
339        }
340 
341    shift_left_cb(NULL, NULL);
342    }
343 
kb_shift_right(G_GNUC_UNUSED guint key_id)344 static void kb_shift_right(G_GNUC_UNUSED guint key_id){
345 
346    /* sanity check */
347    if (document_get_current() == NULL){
348        return;
349        }
350 
351    shift_right_cb(NULL, NULL);
352    }
353 
plugin_init(G_GNUC_UNUSED GeanyData * data)354 void plugin_init(G_GNUC_UNUSED GeanyData *data){
355    GeanyKeyGroup *key_group;
356 
357    menu_item_shift_left = gtk_menu_item_new_with_mnemonic(_("Shift Left"));
358    gtk_widget_show(menu_item_shift_left);
359    gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu),
360        menu_item_shift_left);
361    g_signal_connect(menu_item_shift_left, "activate",
362        G_CALLBACK(shift_left_cb), NULL);
363 
364    menu_item_shift_right = gtk_menu_item_new_with_mnemonic(_("Shift Right"));
365    gtk_widget_show(menu_item_shift_right);
366    gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu),
367        menu_item_shift_right);
368    g_signal_connect(menu_item_shift_right, "activate",
369        G_CALLBACK(shift_right_cb), NULL);
370 
371    /* make sure our menu items aren't called when there is no doc open */
372    ui_add_document_sensitive(menu_item_shift_right);
373    ui_add_document_sensitive(menu_item_shift_left);
374 
375    /* setup keybindings */
376    key_group = plugin_set_key_group(geany_plugin, "shiftcolumn", KB_COUNT, NULL);
377    keybindings_set_item(key_group, KB_SHIFT_LEFT, kb_shift_left,
378       0, GDK_CONTROL_MASK, "shift_left", _("Shift Left"), menu_item_shift_left);
379    keybindings_set_item(key_group, KB_SHIFT_RIGHT, kb_shift_right,
380       0, GDK_CONTROL_MASK, "shift_right", _("Shift Right"), menu_item_shift_right);
381    }
382 
plugin_cleanup(void)383 void plugin_cleanup(void){
384    gtk_widget_destroy(menu_item_shift_left);
385    gtk_widget_destroy(menu_item_shift_right);
386    }
387