rofi  1.5.1
combi.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2017 Qball Cow <qball@gmpclient.org>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
28 #define G_LOG_DOMAIN "Dialogs.Combi"
29 
30 #include <config.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <rofi.h>
34 #include "settings.h"
35 #include "helper.h"
36 
37 #include <dialogs/dialogs.h>
38 #include <pango/pango.h>
39 #include "mode-private.h"
40 #include <theme.h>
41 
45 typedef struct
46 {
48  gboolean disable;
49 } CombiMode;
50 
51 typedef struct
52 {
53  // List of (combined) entries.
54  unsigned int cmd_list_length;
55  // List to validate where each switcher starts.
56  unsigned int *starts;
57  unsigned int *lengths;
58  // List of switchers to combine.
59  unsigned int num_switchers;
62 
63 static void combi_mode_parse_switchers ( Mode *sw )
64 {
66  char *savept = NULL;
67  // Make a copy, as strtok will modify it.
68  char *switcher_str = g_strdup ( config.combi_modi );
69  const char * const sep = ",#";
70  // Split token on ','. This modifies switcher_str.
71  for ( char *token = strtok_r ( switcher_str, sep, &savept ); token != NULL;
72  token = strtok_r ( NULL, sep, &savept ) ) {
73  // Resize and add entry.
74  pd->switchers = (CombiMode *) g_realloc ( pd->switchers,
75  sizeof ( CombiMode ) * ( pd->num_switchers + 1 ) );
76 
77  Mode *mode = rofi_collect_modi_search ( token );
78  if ( mode ) {
79  pd->switchers[pd->num_switchers].disable = FALSE;
80  pd->switchers[pd->num_switchers++].mode = mode;
81  }
82  else {
83  // If not build in, use custom switchers.
84  Mode *sw = script_switcher_parse_setup ( token );
85  if ( sw != NULL ) {
86  pd->switchers[pd->num_switchers].disable = FALSE;
87  pd->switchers[pd->num_switchers++].mode = sw;
88  }
89  else {
90  // Report error, don't continue.
91  g_warning ( "Invalid script switcher: %s", token );
92  token = NULL;
93  }
94  }
95  }
96  // Free string that was modified by strtok_r
97  g_free ( switcher_str );
98 }
99 
100 static int combi_mode_init ( Mode *sw )
101 {
102  if ( mode_get_private_data ( sw ) == NULL ) {
103  CombiModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
104  mode_set_private_data ( sw, (void *) pd );
106  pd->starts = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
107  pd->lengths = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
108  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
109  if ( !mode_init ( pd->switchers[i].mode ) ) {
110  return FALSE;
111  }
112  }
113  if ( pd->cmd_list_length == 0 ) {
114  pd->cmd_list_length = 0;
115  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
116  unsigned int length = mode_get_num_entries ( pd->switchers[i].mode );
117  pd->starts[i] = pd->cmd_list_length;
118  pd->lengths[i] = length;
119  pd->cmd_list_length += length;
120  }
121  }
122  }
123  return TRUE;
124 }
125 static unsigned int combi_mode_get_num_entries ( const Mode *sw )
126 {
128  unsigned int length = 0;
129  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
130  unsigned int entries = mode_get_num_entries ( pd->switchers[i].mode );
131  pd->starts[i] = length;
132  pd->lengths[i] = entries;
133  length += entries;
134  }
135  return length;
136 }
137 static void combi_mode_destroy ( Mode *sw )
138 {
140  if ( pd != NULL ) {
141  g_free ( pd->starts );
142  g_free ( pd->lengths );
143  // Cleanup switchers.
144  for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
145  mode_destroy ( pd->switchers[i].mode );
146  }
147  g_free ( pd->switchers );
148  g_free ( pd );
149  mode_set_private_data ( sw, NULL );
150  }
151 }
152 static ModeMode combi_mode_result ( Mode *sw, int mretv, char **input, unsigned int selected_line )
153 {
155 
156  if ( input[0][0] == '!' ) {
157  int switcher = -1;
158  char *eob = strchrnul ( input[0], ' ' );
159  ssize_t bang_len = g_utf8_pointer_to_offset ( input[0], eob ) - 1;
160  if ( bang_len > 0 ) {
161  for ( unsigned i = 0; switcher == -1 && i < pd->num_switchers; i++ ) {
162  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
163  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
164  if ( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[0][1], mode_name, bang_len ) == 0 ) {
165  switcher = i;
166  }
167  }
168  }
169  if ( switcher >= 0 ) {
170  if ( eob[0] == ' ' ) {
171  char *n = eob + 1;
172  return mode_result ( pd->switchers[switcher].mode, mretv, &n,
173  selected_line - pd->starts[switcher] );
174  }
175  return MODE_EXIT;
176  }
177  }
178  if ( mretv & MENU_QUICK_SWITCH ) {
179  return mretv & MENU_LOWER_MASK;
180  }
181 
182  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
183  if ( selected_line >= pd->starts[i] &&
184  selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
185  return mode_result ( pd->switchers[i].mode, mretv, input, selected_line - pd->starts[i] );
186  }
187  }
188  return MODE_EXIT;
189 }
190 static int combi_mode_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
191 {
193  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
194  if ( pd->switchers[i].disable ) {
195  continue;
196  }
197  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
198  return mode_token_match ( pd->switchers[i].mode, tokens, index - pd->starts[i] );
199  }
200  }
201  return 0;
202 }
203 static char * combi_mgrv ( const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry )
204 {
206  if ( !get_entry ) {
207  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
208  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
209  mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, FALSE );
210  return NULL;
211  }
212  }
213  return NULL;
214  }
215  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
216  if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
217  char * retv;
218  char * str = retv = mode_get_display_value ( pd->switchers[i].mode, selected_line - pd->starts[i], state, attr_list, TRUE );
219  const char *dname = mode_get_display_name ( pd->switchers[i].mode );
221  retv = g_strdup_printf ( "%s %s", dname, str );
222  g_free ( str );
223  }
224 
225  if ( attr_list != NULL ) {
226  ThemeWidget *wid = rofi_theme_find_widget ( sw->name, NULL, TRUE );
227  Property *p = rofi_theme_find_property ( wid, P_COLOR, pd->switchers[i].mode->name, TRUE );
228  if ( p != NULL ) {
229  PangoAttribute *pa = pango_attr_foreground_new (
230  p->value.color.red * 65535,
231  p->value.color.green * 65535,
232  p->value.color.blue * 65535 );
233  pa->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
234  pa->end_index = strlen ( dname );
235  *attr_list = g_list_append ( *attr_list, pa );
236  }
237  }
238  return retv;
239  }
240  }
241 
242  return NULL;
243 }
244 static char * combi_get_completion ( const Mode *sw, unsigned int index )
245 {
247  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
248  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
249  char *comp = mode_get_completion ( pd->switchers[i].mode, index - pd->starts[i] );
250  char *mcomp = g_strdup_printf ( "!%s %s", mode_get_name ( pd->switchers[i].mode ), comp );
251  g_free ( comp );
252  return mcomp;
253  }
254  }
255  // Should never get here.
256  g_assert_not_reached ();
257  return NULL;
258 }
259 
260 static cairo_surface_t * combi_get_icon ( const Mode *sw, unsigned int index, int height )
261 {
263  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
264  if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) {
265  cairo_surface_t *icon = mode_get_icon ( pd->switchers[i].mode, index - pd->starts[i], height );
266  return icon;
267  }
268  }
269  return NULL;
270 }
271 
272 static char * combi_preprocess_input ( Mode *sw, const char *input )
273 {
275  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
276  pd->switchers[i].disable = FALSE;
277  }
278  if ( input != NULL && input[0] == '!' ) {
279  char *eob = strchrnul ( input, ' ' );
280  ssize_t bang_len = g_utf8_pointer_to_offset ( input, eob ) - 1;
281  if ( bang_len > 0 ) {
282  for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
283  const char *mode_name = mode_get_name ( pd->switchers[i].mode );
284  size_t mode_name_len = g_utf8_strlen ( mode_name, -1 );
285  if ( !( (size_t) bang_len <= mode_name_len && utf8_strncmp ( &input[1], mode_name, bang_len ) == 0 ) ) {
286  // No match.
287  pd->switchers[i].disable = TRUE;
288  }
289  }
290  if ( eob[0] == '\0' || eob[1] == '\0' ) {
291  return NULL;
292  }
293  return g_strdup ( eob + 1 );
294  }
295  }
296  return g_strdup ( input );
297 }
298 
300 {
301  .name = "combi",
302  .cfg_name_key = "display-combi",
303  ._init = combi_mode_init,
304  ._get_num_entries = combi_mode_get_num_entries,
305  ._result = combi_mode_result,
306  ._destroy = combi_mode_destroy,
307  ._token_match = combi_mode_match,
308  ._get_completion = combi_get_completion,
309  ._get_display_value = combi_mgrv,
310  ._get_icon = combi_get_icon,
311  ._preprocess_input = combi_preprocess_input,
312  .private_data = NULL,
313  .free = NULL
314 };
static char * combi_preprocess_input(Mode *sw, const char *input)
Definition: combi.c:272
Mode * mode
Definition: combi.c:47
CombiMode * switchers
Definition: combi.c:60
int mode_token_match(const Mode *mode, rofi_int_matcher **tokens, unsigned int selected_line)
Definition: mode.c:105
static cairo_surface_t * combi_get_icon(const Mode *sw, unsigned int index, int height)
Definition: combi.c:260
int utf8_strncmp(const char *a, const char *b, size_t n)
Definition: helper.c:963
double blue
Definition: rofi-types.h:121
ThemeWidget * rofi_theme_find_widget(const char *name, const char *state, gboolean exact)
Definition: theme.c:507
static int combi_mode_init(Mode *sw)
Definition: combi.c:100
cairo_surface_t * mode_get_icon(const Mode *mode, unsigned int selected_line, int height)
Definition: mode.c:72
ThemeColor color
Definition: rofi-types.h:190
Property * rofi_theme_find_property(ThemeWidget *widget, PropertyType type, const char *property, gboolean exact)
Definition: theme.c:469
double green
Definition: rofi-types.h:119
double red
Definition: rofi-types.h:117
char * combi_modi
Definition: settings.h:128
gboolean combi_hide_mode_prefix
Definition: settings.h:167
PropertyValue value
Definition: rofi-types.h:217
static ModeMode combi_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
Definition: combi.c:152
ModeMode
Definition: mode.h:49
Definition: mode.h:52
unsigned int * starts
Definition: combi.c:56
static void combi_mode_destroy(Mode *sw)
Definition: combi.c:137
void * mode_get_private_data(const Mode *mode)
Definition: mode.c:128
unsigned int num_switchers
Definition: combi.c:59
Mode * rofi_collect_modi_search(const char *name)
Definition: rofi.c:456
void mode_destroy(Mode *mode)
Definition: mode.c:49
static unsigned int combi_mode_get_num_entries(const Mode *sw)
Definition: combi.c:125
unsigned int * lengths
Definition: combi.c:57
unsigned int cmd_list_length
Definition: combi.c:54
gboolean disable
Definition: combi.c:48
static void combi_mode_parse_switchers(Mode *sw)
Definition: combi.c:63
unsigned int mode_get_num_entries(const Mode *mode)
Definition: mode.c:56
char * mode_get_display_value(const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry)
Definition: mode.c:63
const char * mode_get_name(const Mode *mode)
Definition: mode.c:112
void mode_set_private_data(Mode *mode, void *pd)
Definition: mode.c:134
Mode * script_switcher_parse_setup(const char *str)
Definition: script.c:283
ModeMode mode_result(Mode *mode, int menu_retv, char **input, unsigned int selected_line)
Definition: mode.c:97
static char * combi_get_completion(const Mode *sw, unsigned int index)
Definition: combi.c:244
Mode combi_mode
Definition: combi.c:299
const char * mode_get_display_name(const Mode *mode)
Definition: mode.c:143
Settings config
static char * combi_mgrv(const Mode *sw, unsigned int selected_line, int *state, GList **attr_list, int get_entry)
Definition: combi.c:203
static int combi_mode_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition: combi.c:190
char * name
Definition: mode-private.h:156
char * mode_get_completion(const Mode *mode, unsigned int selected_line)
Definition: mode.c:84
int mode_init(Mode *mode)
Definition: mode.c:42